Merge branch 'develop' of https://github.com/sleuthkit/autopsy into live_autopsy_warning

This commit is contained in:
rishwanth1995 2018-08-10 09:50:15 -04:00
commit 1932d4381b
96 changed files with 2638 additions and 1472 deletions

View File

@ -86,7 +86,7 @@
<target name="getTestDataFiles"> <target name="getTestDataFiles">
<mkdir dir="${basedir}/test/qa-functional/data"/> <mkdir dir="${basedir}/test/qa-functional/data"/>
<get src="https://drive.google.com/uc?id=1dLYGctuvRQMmnzfXPppTM_9gB49eLc_g" dest="${test-input}/EmbeddedIM_img1_v1.vhd" skipexisting="true"/> <get src="https://drive.google.com/uc?id=1FkinvA7EFqP4nOSOyTAOli5KefM67ufA" dest="${test-input}/EmbeddedIM_img1_v2.vhd" skipexisting="true"/>
<get src="https://drive.google.com/uc?id=1JACMDyH4y54ypGzFWl82ZzMQf3qbrioP" dest="${test-input}/BitlockerDetection_img1_v1.vhd" skipexisting="true"/> <get src="https://drive.google.com/uc?id=1JACMDyH4y54ypGzFWl82ZzMQf3qbrioP" dest="${test-input}/BitlockerDetection_img1_v1.vhd" skipexisting="true"/>
<get src="https://drive.google.com/uc?id=17sGybvmBGsWWJYo1IWKmO04oG9hKpPi3" dest="${test-input}/SqlCipherDetection_img1_v1.vhd" skipexisting="true"/> <get src="https://drive.google.com/uc?id=17sGybvmBGsWWJYo1IWKmO04oG9hKpPi3" dest="${test-input}/SqlCipherDetection_img1_v1.vhd" skipexisting="true"/>
<get src="https://drive.google.com/uc?id=0BxdBkzm5VKGNT0dGY0dqcHVsU3M" dest="${test-input}/IngestFilters_img1_v1.img" skipexisting="true"/> <get src="https://drive.google.com/uc?id=0BxdBkzm5VKGNT0dGY0dqcHVsU3M" dest="${test-input}/IngestFilters_img1_v1.img" skipexisting="true"/>

View File

@ -2,7 +2,7 @@ Manifest-Version: 1.0
OpenIDE-Module: org.sleuthkit.autopsy.core/10 OpenIDE-Module: org.sleuthkit.autopsy.core/10
OpenIDE-Module-Localizing-Bundle: org/sleuthkit/autopsy/core/Bundle.properties OpenIDE-Module-Localizing-Bundle: org/sleuthkit/autopsy/core/Bundle.properties
OpenIDE-Module-Layer: org/sleuthkit/autopsy/core/layer.xml OpenIDE-Module-Layer: org/sleuthkit/autopsy/core/layer.xml
OpenIDE-Module-Implementation-Version: 23 OpenIDE-Module-Implementation-Version: 24
OpenIDE-Module-Requires: org.openide.windows.WindowManager OpenIDE-Module-Requires: org.openide.windows.WindowManager
AutoUpdate-Show-In-Client: true AutoUpdate-Show-In-Client: true
AutoUpdate-Essential-Module: true AutoUpdate-Essential-Module: true

View File

@ -17,7 +17,6 @@ file.reference.sevenzipjbinding-AllPlatforms.jar=release/modules/ext/sevenzipjbi
file.reference.sevenzipjbinding.jar=release/modules/ext/sevenzipjbinding.jar file.reference.sevenzipjbinding.jar=release/modules/ext/sevenzipjbinding.jar
file.reference.sqlite-jdbc-3.8.11.jar=release/modules/ext/sqlite-jdbc-3.8.11.jar file.reference.sqlite-jdbc-3.8.11.jar=release/modules/ext/sqlite-jdbc-3.8.11.jar
file.reference.StixLib.jar=release/modules/ext/StixLib.jar file.reference.StixLib.jar=release/modules/ext/StixLib.jar
file.reference.sleuthkit-postgresql-4.6.1.jar=release/modules/ext/sleuthkit-postgresql-4.6.1.jar
file.reference.bcprov-jdk15on-1.54.jar=release/modules/ext/bcprov-jdk15on-1.54.jar file.reference.bcprov-jdk15on-1.54.jar=release/modules/ext/bcprov-jdk15on-1.54.jar
file.reference.jackcess-2.1.8.jar=release/modules/ext/jackcess-2.1.8.jar file.reference.jackcess-2.1.8.jar=release/modules/ext/jackcess-2.1.8.jar
file.reference.jackcess-encrypt-2.1.2.jar=release/modules/ext/jackcess-encrypt-2.1.2.jar file.reference.jackcess-encrypt-2.1.2.jar=release/modules/ext/jackcess-encrypt-2.1.2.jar
@ -30,7 +29,7 @@ file.reference.cxf-rt-transports-http-3.0.16.jar=release/modules/ext/cxf-rt-tran
file.reference.fontbox-2.0.8.jar=release/modules/ext/fontbox-2.0.8.jar file.reference.fontbox-2.0.8.jar=release/modules/ext/fontbox-2.0.8.jar
file.reference.pdfbox-2.0.8.jar=release/modules/ext/pdfbox-2.0.8.jar file.reference.pdfbox-2.0.8.jar=release/modules/ext/pdfbox-2.0.8.jar
file.reference.pdfbox-tools-2.0.8.jar=release/modules/ext/pdfbox-tools-2.0.8.jar file.reference.pdfbox-tools-2.0.8.jar=release/modules/ext/pdfbox-tools-2.0.8.jar
file.reference.sleuthkit-postgresql-4.6.1.jar=release/modules/ext/sleuthkit-postgresql-4.6.1.jar file.reference.sleuthkit-postgresql-4.6.2.jar=release/modules/ext/sleuthkit-postgresql-4.6.2.jar
file.reference.tika-core-1.17.jar=release/modules/ext/tika-core-1.17.jar file.reference.tika-core-1.17.jar=release/modules/ext/tika-core-1.17.jar
file.reference.tika-parsers-1.17.jar=release/modules/ext/tika-parsers-1.17.jar file.reference.tika-parsers-1.17.jar=release/modules/ext/tika-parsers-1.17.jar
file.reference.curator-client-2.8.0.jar=release/modules/ext/curator-client-2.8.0.jar file.reference.curator-client-2.8.0.jar=release/modules/ext/curator-client-2.8.0.jar
@ -47,5 +46,5 @@ nbm.homepage=http://www.sleuthkit.org/
nbm.module.author=Brian Carrier nbm.module.author=Brian Carrier
nbm.needs.restart=true nbm.needs.restart=true
source.reference.curator-recipes-2.8.0.jar=release/modules/ext/curator-recipes-2.8.0-sources.jar source.reference.curator-recipes-2.8.0.jar=release/modules/ext/curator-recipes-2.8.0-sources.jar
spec.version.base=10.11 spec.version.base=10.12

View File

@ -251,7 +251,7 @@
<compile-dependency/> <compile-dependency/>
<run-dependency> <run-dependency>
<release-version>3</release-version> <release-version>3</release-version>
<specification-version>1.1</specification-version> <specification-version>1.2</specification-version>
</run-dependency> </run-dependency>
</dependency> </dependency>
</module-dependencies> </module-dependencies>
@ -412,8 +412,8 @@
<binary-origin>release/modules/ext/metadata-extractor-2.10.1.jar</binary-origin> <binary-origin>release/modules/ext/metadata-extractor-2.10.1.jar</binary-origin>
</class-path-extension> </class-path-extension>
<class-path-extension> <class-path-extension>
<runtime-relative-path>ext/sleuthkit-postgresql-4.6.1.jar</runtime-relative-path> <runtime-relative-path>ext/sleuthkit-postgresql-4.6.2.jar</runtime-relative-path>
<binary-origin>release/modules/ext/sleuthkit-postgresql-4.6.1.jar</binary-origin> <binary-origin>release/modules/ext/sleuthkit-postgresql-4.6.2.jar</binary-origin>
</class-path-extension> </class-path-extension>
<class-path-extension> <class-path-extension>
<runtime-relative-path>ext/tika-core-1.17.jar</runtime-relative-path> <runtime-relative-path>ext/tika-core-1.17.jar</runtime-relative-path>

View File

@ -24,10 +24,8 @@ import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent; import java.awt.event.KeyEvent;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.TreeMap; import java.util.TreeMap;
import javax.swing.AbstractAction; import javax.swing.AbstractAction;
import javax.swing.ActionMap; import javax.swing.ActionMap;
@ -105,7 +103,16 @@ public class GetTagNameAndCommentDialog extends JDialog {
public static TagNameAndComment doDialog(Window owner) { public static TagNameAndComment doDialog(Window owner) {
GetTagNameAndCommentDialog dialog = new GetTagNameAndCommentDialog(owner); GetTagNameAndCommentDialog dialog = new GetTagNameAndCommentDialog(owner);
dialog.display(); dialog.display();
return dialog.tagNameAndComment; return dialog.getTagNameAndComment();
}
/**
* Get the TagNameAndComment.
*
* @return the tagNameAndComment
*/
private TagNameAndComment getTagNameAndComment() {
return tagNameAndComment;
} }
private GetTagNameAndCommentDialog(Window owner) { private GetTagNameAndCommentDialog(Window owner) {
@ -114,14 +121,14 @@ public class GetTagNameAndCommentDialog extends JDialog {
ModalityType.APPLICATION_MODAL); ModalityType.APPLICATION_MODAL);
} }
private void display() { private void display() {
initComponents(); initComponents();
tagCombo.setRenderer(new DefaultListCellRenderer() { tagCombo.setRenderer(new DefaultListCellRenderer() {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@Override @Override
public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) { public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
String status = ((TagName) value).getKnownStatus() == TskData.FileKnown.BAD ?TagsManager.getNotableTagLabel() : ""; String status = ((TagName) value).getKnownStatus() == TskData.FileKnown.BAD ? TagsManager.getNotableTagLabel() : "";
String newValue = ((TagName) value).getDisplayName() + status; String newValue = ((TagName) value).getDisplayName() + status;
return super.getListCellRendererComponent(list, newValue, index, isSelected, cellHasFocus); return super.getListCellRendererComponent(list, newValue, index, isSelected, cellHasFocus);
} }
@ -160,7 +167,6 @@ public class GetTagNameAndCommentDialog extends JDialog {
} }
}); });
} catch (TskCoreException | NoCurrentCaseException ex) { } catch (TskCoreException | NoCurrentCaseException ex) {
Logger.getLogger(GetTagNameAndCommentDialog.class Logger.getLogger(GetTagNameAndCommentDialog.class
.getName()).log(Level.SEVERE, "Failed to get tag names", ex); //NON-NLS .getName()).log(Level.SEVERE, "Failed to get tag names", ex); //NON-NLS
@ -320,4 +326,5 @@ public class GetTagNameAndCommentDialog extends JDialog {
private javax.swing.JComboBox<TagName> tagCombo; private javax.swing.JComboBox<TagName> tagCombo;
private javax.swing.JLabel tagLabel; private javax.swing.JLabel tagLabel;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
} }

View File

@ -114,7 +114,6 @@ LocalFilesDSProcessor.toString.text=Logical Files
LocalFilesPanel.contentType.text=LOCAL LocalFilesPanel.contentType.text=LOCAL
LocalFilesPanel.moduleErr=Module Error LocalFilesPanel.moduleErr=Module Error
LocalFilesPanel.moduleErr.msg=A module caused an error listening to LocalFilesPanel updates. See log to determine which module. Some data could be incomplete. LocalFilesPanel.moduleErr.msg=A module caused an error listening to LocalFilesPanel updates. See log to determine which module. Some data could be incomplete.
MissingImageDialog.allDesc.text=All Supported Types
MissingImageDialog.display.title=Search for Missing Image MissingImageDialog.display.title=Search for Missing Image
MissingImageDialog.confDlg.noFileSel.msg=No image file has been selected. Are you sure you\nwould like to exit without finding the image? MissingImageDialog.confDlg.noFileSel.msg=No image file has been selected. Are you sure you\nwould like to exit without finding the image?
MissingImageDialog.confDlg.noFileSel.title=Missing Image MissingImageDialog.confDlg.noFileSel.title=Missing Image

View File

@ -101,7 +101,6 @@ LocalFilesDSProcessor.toString.text=\u30ed\u30b8\u30ab\u30eb\u30d5\u30a1\u30a4\u
LocalFilesPanel.contentType.text=\u30ed\u30fc\u30ab\u30eb LocalFilesPanel.contentType.text=\u30ed\u30fc\u30ab\u30eb
LocalFilesPanel.moduleErr=\u30e2\u30b8\u30e5\u30fc\u30eb\u30a8\u30e9\u30fc LocalFilesPanel.moduleErr=\u30e2\u30b8\u30e5\u30fc\u30eb\u30a8\u30e9\u30fc
LocalFilesPanel.moduleErr.msg=LocalFilesPanel\u30a2\u30c3\u30d7\u30c7\u30fc\u30c8\u3092\u78ba\u8a8d\u4e2d\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u3092\u8d77\u3053\u3057\u307e\u3057\u305f\u3002\u3069\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304b\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u4e0b\u3055\u3044\u3002\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u4e0d\u5b8c\u5168\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002 LocalFilesPanel.moduleErr.msg=LocalFilesPanel\u30a2\u30c3\u30d7\u30c7\u30fc\u30c8\u3092\u78ba\u8a8d\u4e2d\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u3092\u8d77\u3053\u3057\u307e\u3057\u305f\u3002\u3069\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304b\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u4e0b\u3055\u3044\u3002\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u4e0d\u5b8c\u5168\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002
MissingImageDialog.allDesc.text=\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u308b\u5168\u3066\u306e\u30bf\u30a4\u30d7
MissingImageDialog.display.title=\u6b20\u843d\u30a4\u30e1\u30fc\u30b8\u3092\u691c\u7d22 MissingImageDialog.display.title=\u6b20\u843d\u30a4\u30e1\u30fc\u30b8\u3092\u691c\u7d22
MissingImageDialog.confDlg.noFileSel.msg=\u30a4\u30e1\u30fc\u30b8\u30d5\u30a1\u30a4\u30eb\u304c\u9078\u629e\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u30a4\u30e1\u30fc\u30b8\u3092\u898b\u3064\u3051\u308b\n\u524d\u306b\u672c\u5f53\u306b\u7d42\u4e86\u3057\u307e\u3059\u304b\uff1f MissingImageDialog.confDlg.noFileSel.msg=\u30a4\u30e1\u30fc\u30b8\u30d5\u30a1\u30a4\u30eb\u304c\u9078\u629e\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u30a4\u30e1\u30fc\u30b8\u3092\u898b\u3064\u3051\u308b\n\u524d\u306b\u672c\u5f53\u306b\u7d42\u4e86\u3057\u307e\u3059\u304b\uff1f
MissingImageDialog.confDlg.noFileSel.title=\u6b20\u843d\u30a4\u30e1\u30fc\u30b8 MissingImageDialog.confDlg.noFileSel.title=\u6b20\u843d\u30a4\u30e1\u30fc\u30b8

View File

@ -90,6 +90,15 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
configPanel = ImageFilePanel.createInstance(ImageDSProcessor.class.getName(), filtersList); configPanel = ImageFilePanel.createInstance(ImageDSProcessor.class.getName(), filtersList);
} }
/**
* Get the list of file filters supported by this DSP.
*
* @return A list of all supported file filters.
*/
static List<FileFilter> getFileFiltersList() {
return filtersList;
}
/** /**
* Gets a string that describes the type of data sources this processor is * Gets a string that describes the type of data sources this processor is
* able to add to the case database. The string is suitable for display in a * able to add to the case database. The string is suitable for display in a

View File

@ -28,6 +28,7 @@ import java.io.File;
import javax.swing.JFileChooser; import javax.swing.JFileChooser;
import javax.swing.JFrame; import javax.swing.JFrame;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import javax.swing.filechooser.FileFilter;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.windows.WindowManager; import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.coreutils.DriveUtils; import org.sleuthkit.autopsy.coreutils.DriveUtils;
@ -44,17 +45,8 @@ class MissingImageDialog extends javax.swing.JDialog {
private static final Logger logger = Logger.getLogger(MissingImageDialog.class.getName()); private static final Logger logger = Logger.getLogger(MissingImageDialog.class.getName());
long obj_id; long obj_id;
SleuthkitCase db; SleuthkitCase db;
static final GeneralFilter rawFilter = new GeneralFilter(GeneralFilter.RAW_IMAGE_EXTS, GeneralFilter.RAW_IMAGE_DESC);
static final GeneralFilter encaseFilter = new GeneralFilter(GeneralFilter.ENCASE_IMAGE_EXTS, GeneralFilter.ENCASE_IMAGE_DESC);
static final List<String> allExt = new ArrayList<String>();
static { private final JFileChooser fileChooser = new JFileChooser();
allExt.addAll(GeneralFilter.RAW_IMAGE_EXTS);
allExt.addAll(GeneralFilter.ENCASE_IMAGE_EXTS);
}
static final String allDesc = NbBundle.getMessage(MissingImageDialog.class, "MissingImageDialog.allDesc.text");
static final GeneralFilter allFilter = new GeneralFilter(allExt, allDesc);
private final JFileChooser fc = new JFileChooser();
/** /**
* Instantiate a MissingImageDialog. * Instantiate a MissingImageDialog.
@ -68,13 +60,15 @@ class MissingImageDialog extends javax.swing.JDialog {
this.db = db; this.db = db;
initComponents(); initComponents();
fc.setDragEnabled(false); fileChooser.setDragEnabled(false);
fc.setFileSelectionMode(JFileChooser.FILES_ONLY); fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
fc.setMultiSelectionEnabled(false); fileChooser.setMultiSelectionEnabled(false);
fc.addChoosableFileFilter(rawFilter); List<FileFilter> fileFiltersList = ImageDSProcessor.getFileFiltersList();
fc.addChoosableFileFilter(encaseFilter); for (FileFilter fileFilter : fileFiltersList) {
fc.setFileFilter(allFilter); fileChooser.addChoosableFileFilter(fileFilter);
}
fileChooser.setFileFilter(fileFiltersList.get(0));
selectButton.setEnabled(false); selectButton.setEnabled(false);
} }
@ -286,12 +280,12 @@ class MissingImageDialog extends javax.swing.JDialog {
// set the current directory of the FileChooser if the ImagePath Field is valid // set the current directory of the FileChooser if the ImagePath Field is valid
File currentDir = new File(oldText); File currentDir = new File(oldText);
if (currentDir.exists()) { if (currentDir.exists()) {
fc.setCurrentDirectory(currentDir); fileChooser.setCurrentDirectory(currentDir);
} }
int retval = fc.showOpenDialog(this); int retval = fileChooser.showOpenDialog(this);
if (retval == JFileChooser.APPROVE_OPTION) { if (retval == JFileChooser.APPROVE_OPTION) {
String path = fc.getSelectedFile().getPath(); String path = fileChooser.getSelectedFile().getPath();
pathNameTextField.setText(path); pathNameTextField.setText(path);
} }
//pcs.firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.FOCUS_NEXT.toString(), false, true); //pcs.firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.FOCUS_NEXT.toString(), false, true);

View File

@ -859,13 +859,14 @@ public class SingleUserCaseConverter {
if (value > biggestPK) { if (value > biggestPK) {
biggestPK = value; biggestPK = value;
} }
outputStatement.executeUpdate("INSERT INTO content_tags (tag_id, obj_id, tag_name_id, comment, begin_byte_offset, end_byte_offset) VALUES (" //NON-NLS outputStatement.executeUpdate("INSERT INTO content_tags (tag_id, obj_id, tag_name_id, comment, begin_byte_offset, end_byte_offset, user_name) VALUES (" //NON-NLS
+ value + "," + value + ","
+ inputResultSet.getLong(2) + "," + inputResultSet.getLong(2) + ","
+ inputResultSet.getLong(3) + ",'" + inputResultSet.getLong(3) + ",'"
+ inputResultSet.getString(4) + "'," + inputResultSet.getString(4) + "',"
+ inputResultSet.getLong(5) + "," + inputResultSet.getLong(5) + ","
+ inputResultSet.getLong(6) + ")"); //NON-NLS + inputResultSet.getLong(6) + ",'"
+ inputResultSet.getString(7)+ "')"); //NON-NLS
} catch (SQLException ex) { } catch (SQLException ex) {
if (ex.getErrorCode() != 0) { // 0 if the entry already exists if (ex.getErrorCode() != 0) { // 0 if the entry already exists
@ -892,7 +893,8 @@ public class SingleUserCaseConverter {
+ value + "," + value + ","
+ inputResultSet.getLong(2) + "," + inputResultSet.getLong(2) + ","
+ inputResultSet.getLong(3) + ",'" + inputResultSet.getLong(3) + ",'"
+ inputResultSet.getString(4) + "')"); //NON-NLS + inputResultSet.getString(4) + "','"
+ inputResultSet.getString(5) + "')"); //NON-NLS
} catch (SQLException ex) { } catch (SQLException ex) {
if (ex.getErrorCode() != 0) { // 0 if the entry already exists if (ex.getErrorCode() != 0) { // 0 if the entry already exists

View File

@ -21,7 +21,6 @@ package org.sleuthkit.autopsy.casemodule.services;
import java.io.Closeable; import java.io.Closeable;
import java.io.IOException; import java.io.IOException;
import org.openide.util.Lookup; import org.openide.util.Lookup;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchService; import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchService;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
@ -80,8 +79,8 @@ public final class Blackboard implements Closeable {
* *
* @return A type object representing the artifact type. * @return A type object representing the artifact type.
* *
* @throws BlackboardBlackboardException If there is a problem getting or * @throws BlackboardException If there is a problem getting or adding the
* adding the artifact type. * artifact type.
*/ */
public synchronized BlackboardArtifact.Type getOrAddArtifactType(String typeName, String displayName) throws BlackboardException { public synchronized BlackboardArtifact.Type getOrAddArtifactType(String typeName, String displayName) throws BlackboardException {
if (null == caseDb) { if (null == caseDb) {
@ -110,8 +109,8 @@ public final class Blackboard implements Closeable {
* *
* @return A type object representing the attribute type. * @return A type object representing the attribute type.
* *
* @throws BlackboardBlackboardException If there is a problem getting or * @throws BlackboardException If there is a problem getting or adding the
* adding the attribute type. * attribute type.
*/ */
public synchronized BlackboardAttribute.Type getOrAddAttributeType(String typeName, BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE valueType, String displayName) throws BlackboardException { public synchronized BlackboardAttribute.Type getOrAddAttributeType(String typeName, BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE valueType, String displayName) throws BlackboardException {
if (null == caseDb) { if (null == caseDb) {
@ -140,7 +139,6 @@ public final class Blackboard implements Closeable {
caseDb = null; caseDb = null;
} }
/** /**
* A blackboard exception. * A blackboard exception.
*/ */

View File

@ -47,7 +47,6 @@ import org.sleuthkit.datamodel.TskData;
public class TagsManager implements Closeable { public class TagsManager implements Closeable {
private static final Logger LOGGER = Logger.getLogger(TagsManager.class.getName()); private static final Logger LOGGER = Logger.getLogger(TagsManager.class.getName());
private final SleuthkitCase caseDb; private final SleuthkitCase caseDb;
/** /**
@ -71,13 +70,14 @@ public class TagsManager implements Closeable {
|| tagDisplayName.contains(";")); || tagDisplayName.contains(";"));
} }
@NbBundle.Messages({"TagsManager.notableTagEnding.text= (Notable)"}) @NbBundle.Messages({"TagsManager.notableTagEnding.text= (Notable)"})
/** /**
* Get String of text which is used to label tags as notable to the user. * Get String of text which is used to label tags as notable to the user.
* *
* @return Bundle message TagsManager.notableTagEnding.text * @return Bundle message TagsManager.notableTagEnding.text
*/ */
public static String getNotableTagLabel(){ public static String getNotableTagLabel() {
return Bundle.TagsManager_notableTagEnding_text(); return Bundle.TagsManager_notableTagEnding_text();
} }
@ -166,6 +166,33 @@ public class TagsManager implements Closeable {
return caseDb.getTagNamesInUse(); return caseDb.getTagNamesInUse();
} }
/**
* Gets a list of all tag names currently in use in the case database for
* tagging content or artifacts by the specified user.
*
* @param userName - the user name that you want to get tags for
*
* @return A list, possibly empty, of TagName objects.
*
* @throws TskCoreException If there is an error querying the case database.
*/
public List<TagName> getTagNamesInUseForUser(String userName) throws TskCoreException {
Set<TagName> tagNameSet = new HashSet<>();
List<BlackboardArtifactTag> artifactTags = caseDb.getAllBlackboardArtifactTags();
for (BlackboardArtifactTag tag : artifactTags) {
if (tag.getUserName().equals(userName)) {
tagNameSet.add(tag.getName());
}
}
List<ContentTag> contentTags = caseDb.getAllContentTags();
for (ContentTag tag : contentTags) {
if (tag.getUserName().equals(userName)) {
tagNameSet.add(tag.getName());
}
}
return new ArrayList<>(tagNameSet);
}
/** /**
* Selects all of the rows from the tag_names table in the case database for * Selects all of the rows from the tag_names table in the case database for
* which there is at least one matching row in the content_tags or * which there is at least one matching row in the content_tags or
@ -181,6 +208,37 @@ public class TagsManager implements Closeable {
public List<TagName> getTagNamesInUse(long dsObjId) throws TskCoreException { public List<TagName> getTagNamesInUse(long dsObjId) throws TskCoreException {
return caseDb.getTagNamesInUse(dsObjId); return caseDb.getTagNamesInUse(dsObjId);
} }
/**
* Selects all of the rows from the tag_names table in the case database for
* which there is at least one matching row in the content_tags or
* blackboard_artifact_tags tables, for the given data source object id and user.
*
* @param dsObjId data source object id
* @param userName - the user name that you want to get tags for
*
* @return A list, possibly empty, of TagName data transfer objects (DTOs)
* for the rows.
*
* @throws TskCoreException
*/
public List<TagName> getTagNamesInUseForUser(long dsObjId, String userName) throws TskCoreException {
Set<TagName> tagNameSet = new HashSet<>();
List<BlackboardArtifactTag> artifactTags = caseDb.getAllBlackboardArtifactTags();
for (BlackboardArtifactTag tag : artifactTags) {
if (tag.getUserName().equals(userName) && tag.getArtifact().getDataSource().getId() == dsObjId) {
tagNameSet.add(tag.getName());
}
}
List<ContentTag> contentTags = caseDb.getAllContentTags();
for (ContentTag tag : contentTags) {
if (tag.getUserName().equals(userName) && tag.getContent().getDataSource().getId() == dsObjId) {
tagNameSet.add(tag.getName());
}
}
return new ArrayList<>(tagNameSet);
}
/** /**
* Gets a map of tag display names to tag name entries in the case database. * Gets a map of tag display names to tag name entries in the case database.
* It has keys for the display names of the standard tag types, the current * It has keys for the display names of the standard tag types, the current
@ -416,11 +474,37 @@ public class TagsManager implements Closeable {
return caseDb.getContentTagsCountByTagName(tagName); return caseDb.getContentTagsCountByTagName(tagName);
} }
/**
* Gets content tags count by tag name for the specified user.
*
* @param tagName The representation of the desired tag type in the case
* database, which can be obtained by calling getTagNames
* and/or addTagName.
* @param userName - the user name that you want to get tags for
*
* @return A count of the content tags with the specified tag name for the
* specified user.
*
* @throws TskCoreException If there is an error getting the tags count from
* the case database.
*/
public long getContentTagsCountByTagNameForUser(TagName tagName, String userName) throws TskCoreException {
long count = 0;
List<ContentTag> contentTags = getContentTagsByTagName(tagName);
for (ContentTag tag : contentTags) {
if (userName.equals(tag.getUserName())) {
count++;
}
}
return count;
}
/** /**
* Gets content tags count by tag name, for the given data source * Gets content tags count by tag name, for the given data source
* *
* @param tagName The representation of the desired tag type in the case * @param tagName The representation of the desired tag type in the case
* database, which can be obtained by calling getTagNames and/or addTagName. * database, which can be obtained by calling getTagNames
* and/or addTagName.
* *
* @param dsObjId data source object id * @param dsObjId data source object id
* *
@ -434,6 +518,33 @@ public class TagsManager implements Closeable {
return caseDb.getContentTagsCountByTagName(tagName, dsObjId); return caseDb.getContentTagsCountByTagName(tagName, dsObjId);
} }
/**
* Gets content tags count by tag name, for the given data source and user
*
* @param tagName The representation of the desired tag type in the case
* database, which can be obtained by calling getTagNames
* and/or addTagName.
*
* @param dsObjId data source object id
* @param userName - the user name that you want to get tags for
*
* @return A count of the content tags with the specified tag name, and for
* the given data source and user
*
* @throws TskCoreException If there is an error getting the tags count from
* the case database.
*/
public long getContentTagsCountByTagNameForUser(TagName tagName, long dsObjId, String userName) throws TskCoreException {
long count = 0;
List<ContentTag> contentTags = getContentTagsByTagName(tagName, dsObjId);
for (ContentTag tag : contentTags) {
if (userName.equals(tag.getUserName())) {
count++;
}
}
return count;
}
/** /**
* Gets a content tag by tag id. * Gets a content tag by tag id.
* *
@ -581,6 +692,31 @@ public class TagsManager implements Closeable {
return caseDb.getBlackboardArtifactTagsCountByTagName(tagName); return caseDb.getBlackboardArtifactTagsCountByTagName(tagName);
} }
/**
* Gets an artifact tags count by tag name for a specific user.
*
* @param tagName The representation of the desired tag type in the case
* database, which can be obtained by calling getTagNames
* and/or addTagName.
* @param userName - the user name that you want to get tags for
*
* @return A count of the artifact tags with the specified tag name for the
* specified user.
*
* @throws TskCoreException If there is an error getting the tags count from
* the case database.
*/
public long getBlackboardArtifactTagsCountByTagNameForUser(TagName tagName, String userName) throws TskCoreException {
long count = 0;
List<BlackboardArtifactTag> artifactTags = getBlackboardArtifactTagsByTagName(tagName);
for (BlackboardArtifactTag tag : artifactTags) {
if (userName.equals(tag.getUserName())) {
count++;
}
}
return count;
}
/** /**
* Gets an artifact tags count by tag name, for the given data source. * Gets an artifact tags count by tag name, for the given data source.
* *
@ -589,8 +725,8 @@ public class TagsManager implements Closeable {
* and/or addTagName. * and/or addTagName.
* @param dsObjId data source object id * @param dsObjId data source object id
* *
* @return A count of the artifact tags with the specified tag name, * @return A count of the artifact tags with the specified tag name, for the
* for the given data source. * given data source.
* *
* @throws TskCoreException If there is an error getting the tags count from * @throws TskCoreException If there is an error getting the tags count from
* the case database. * the case database.
@ -599,6 +735,33 @@ public class TagsManager implements Closeable {
return caseDb.getBlackboardArtifactTagsCountByTagName(tagName, dsObjId); return caseDb.getBlackboardArtifactTagsCountByTagName(tagName, dsObjId);
} }
/**
* Gets an artifact tags count by tag name, for the given data source and
* user.
*
* @param tagName The representation of the desired tag type in the case
* database, which can be obtained by calling getTagNames
* and/or addTagName.
* @param dsObjId data source object id
* @param userName - the user name that you want to get tags for
*
* @return A count of the artifact tags with the specified tag name, for the
* given data source and user.
*
* @throws TskCoreException If there is an error getting the tags count from
* the case database.
*/
public long getBlackboardArtifactTagsCountByTagNameForUser(TagName tagName, long dsObjId, String userName) throws TskCoreException {
long count = 0;
List<BlackboardArtifactTag> artifactTags = getBlackboardArtifactTagsByTagName(tagName, dsObjId);
for (BlackboardArtifactTag tag : artifactTags) {
if (userName.equals(tag.getUserName())) {
count++;
}
}
return count;
}
/** /**
* Gets an artifact tag by tag id. * Gets an artifact tag by tag id.
* *

View File

@ -21,51 +21,48 @@ package org.sleuthkit.autopsy.centralrepository;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.AbstractAction; import javax.swing.AbstractAction;
import org.openide.DialogDisplayer;
import org.openide.NotifyDescriptor;
import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamArtifactUtil; import org.sleuthkit.autopsy.centralrepository.datamodel.EamArtifactUtil;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.TskCoreException;
/** /**
* An AbstractAction to manage adding and modifying a Central Repository file * An AbstractAction to manage adding and modifying a Central Repository file
* instance comment. * instance comment.
*/ */
@Messages({"AddEditCentralRepoCommentAction.menuItemText.addEditCentralRepoComment=Add/Edit Central Repository Comment"})
public final class AddEditCentralRepoCommentAction extends AbstractAction { public final class AddEditCentralRepoCommentAction extends AbstractAction {
private static final Logger logger = Logger.getLogger(AddEditCentralRepoCommentAction.class.getName()); private static final Logger logger = Logger.getLogger(AddEditCentralRepoCommentAction.class.getName());
private boolean addToDatabase; private boolean addToDatabase;
private CorrelationAttribute correlationAttribute; private CorrelationAttribute correlationAttribute;
String title; private String comment;
/** /**
* Private constructor to create an instance given a CorrelationAttribute. * Constructor to create an instance given a CorrelationAttribute.
* *
* @param correlationAttribute The correlation attribute to modify. * @param correlationAttribute The correlation attribute to modify.
* @param title The text for the menu item.
*/ */
private AddEditCentralRepoCommentAction(CorrelationAttribute correlationAttribute, String title) { public AddEditCentralRepoCommentAction(CorrelationAttribute correlationAttribute) {
super(title); super(Bundle.AddEditCentralRepoCommentAction_menuItemText_addEditCentralRepoComment());
this.title = title;
this.correlationAttribute = correlationAttribute; this.correlationAttribute = correlationAttribute;
} }
/** /**
* Private constructor to create an instance given an AbstractFile. * Constructor to create an instance given an AbstractFile.
* *
* @param file The file from which a correlation attribute to modify is * @param file The file from which a correlation attribute to modify is
* derived. * derived.
* @param title The text for the menu item.
*/ */
private AddEditCentralRepoCommentAction(AbstractFile file, String title) { public AddEditCentralRepoCommentAction(AbstractFile file) {
super(Bundle.AddEditCentralRepoCommentAction_menuItemText_addEditCentralRepoComment());
super(title);
this.title = title;
correlationAttribute = EamArtifactUtil.getCorrelationAttributeFromContent(file); correlationAttribute = EamArtifactUtil.getCorrelationAttributeFromContent(file);
if (correlationAttribute == null) { if (correlationAttribute == null) {
addToDatabase = true; addToDatabase = true;
@ -73,26 +70,24 @@ public final class AddEditCentralRepoCommentAction extends AbstractAction {
} }
} }
@Override
public void actionPerformed(ActionEvent event) {
addEditCentralRepoComment();
}
/** /**
* Create a Add/Edit dialog for the correlation attribute file instance * Create a Add/Edit dialog for the correlation attribute file instance
* comment. The comment will be updated in the database if the file instance * comment. The comment will be updated in the database if the file instance
* exists there, or a new file instance will be added to the database with * exists there, or a new file instance will be added to the database with
* the comment attached otherwise. * the comment attached otherwise.
* *
* The current comment for this instance is returned in case it is needed to * The current comment for this instance is saved in case it is needed to
* update the display. * update the display. If the comment was not changed either due to the
* * action being canceled or the occurrence of an error, the comment will be
* @return the current comment for this instance * null.
*/ */
public String addEditCentralRepoComment() { @Override
CentralRepoCommentDialog centralRepoCommentDialog = new CentralRepoCommentDialog(correlationAttribute, title); public void actionPerformed(ActionEvent event) {
CentralRepoCommentDialog centralRepoCommentDialog = new CentralRepoCommentDialog(correlationAttribute);
centralRepoCommentDialog.display(); centralRepoCommentDialog.display();
comment = null;
if (centralRepoCommentDialog.isCommentUpdated()) { if (centralRepoCommentDialog.isCommentUpdated()) {
EamDb dbManager; EamDb dbManager;
@ -104,45 +99,35 @@ public final class AddEditCentralRepoCommentAction extends AbstractAction {
} else { } else {
dbManager.updateAttributeInstanceComment(correlationAttribute); dbManager.updateAttributeInstanceComment(correlationAttribute);
} }
comment = centralRepoCommentDialog.getComment();
} catch (EamDbException ex) { } catch (EamDbException ex) {
logger.log(Level.SEVERE, "Error adding comment", ex); logger.log(Level.SEVERE, "Error adding comment", ex);
NotifyDescriptor notifyDescriptor = new NotifyDescriptor.Message(
"An error occurred while trying to save the comment to the central repository.",
NotifyDescriptor.ERROR_MESSAGE);
DialogDisplayer.getDefault().notify(notifyDescriptor);
} }
} }
return centralRepoCommentDialog.getComment();
} }
/** /**
* Create an instance labeled "Add/Edit Central Repository Comment" given an * Retrieve the comment that was last saved. If a comment update was
* AbstractFile. This is intended for the result view. * canceled or an error occurred while attempting to save the comment, the
* comment will be null.
* *
* @param file The file from which a correlation attribute to modify is * @return The comment.
* derived.
*
* @return The instance.
*
* @throws EamDbException
* @throws NoCurrentCaseException
* @throws TskCoreException
*/ */
@Messages({"AddEditCentralRepoCommentAction.menuItemText.addEditCentralRepoComment=Add/Edit Central Repository Comment"}) public String getComment() {
public static AddEditCentralRepoCommentAction createAddEditCentralRepoCommentAction(AbstractFile file) { return comment;
return new AddEditCentralRepoCommentAction(file,
Bundle.AddEditCentralRepoCommentAction_menuItemText_addEditCentralRepoComment());
} }
/** /**
* Create an instance labeled "Add/Edit Comment" given a * Retrieve the associated correlation attribute.
* CorrelationAttribute. This is intended for the content view.
* *
* @param correlationAttribute The correlation attribute to modify. * @return The correlation attribute.
*
* @return The instance.
*/ */
@Messages({"AddEditCentralRepoCommentAction.menuItemText.addEditComment=Add/Edit Comment"}) public CorrelationAttribute getCorrelationAttribute() {
public static AddEditCentralRepoCommentAction createAddEditCommentAction(CorrelationAttribute correlationAttribute) { return correlationAttribute;
return new AddEditCentralRepoCommentAction(correlationAttribute,
Bundle.AddEditCentralRepoCommentAction_menuItemText_addEditComment());
} }
} }

View File

@ -5,8 +5,6 @@ OpenIDE-Module-Long-Description=\
Correlation Engine ingest module and central database. \n\n\ Correlation Engine ingest module and central database. \n\n\
The Correlation Engine ingest module stores attributes of artifacts matching selected correlation types into a central database.\n\ The Correlation Engine ingest module stores attributes of artifacts matching selected correlation types into a central database.\n\
Stored attributes are used in future cases to correlate and analyzes files and artifacts during ingest. Stored attributes are used in future cases to correlate and analyzes files and artifacts during ingest.
CentralRepoCommentDialog.fileLabel.text=File:
CentralRepoCommentDialog.commentLabel.text=Comment: CentralRepoCommentDialog.commentLabel.text=Comment:
CentralRepoCommentDialog.pathLabel.text=
CentralRepoCommentDialog.okButton.text=&OK CentralRepoCommentDialog.okButton.text=&OK
CentralRepoCommentDialog.cancelButton.text=C&ancel CentralRepoCommentDialog.cancelButton.text=C&ancel

View File

@ -31,14 +31,7 @@
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Component id="jScrollPane1" pref="500" max="32767" attributes="0"/> <Component id="jScrollPane1" pref="500" max="32767" attributes="0"/>
<Group type="102" attributes="0"> <Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0"> <Component id="commentLabel" min="-2" max="-2" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Component id="fileLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="pathLabel" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="commentLabel" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="0" pref="451" max="32767" attributes="0"/> <EmptySpace min="0" pref="451" max="32767" attributes="0"/>
</Group> </Group>
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0">
@ -55,21 +48,16 @@
<DimensionLayout dim="1"> <DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/> <EmptySpace min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="fileLabel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="pathLabel" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="19" max="-2" attributes="0"/>
<Component id="commentLabel" min="-2" max="-2" attributes="0"/> <Component id="commentLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="jScrollPane1" max="32767" attributes="0"/> <Component id="jScrollPane1" max="32767" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/> <EmptySpace type="unrelated" min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="3" attributes="0">
<Component id="okButton" alignment="0" min="-2" max="-2" attributes="0"/> <Component id="cancelButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="cancelButton" min="-2" max="-2" attributes="0"/> <Component id="okButton" alignment="3" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace min="-2" max="-2" attributes="0"/>
</Group> </Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
@ -113,20 +101,6 @@
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cancelButtonActionPerformed"/> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cancelButtonActionPerformed"/>
</Events> </Events>
</Component> </Component>
<Component class="javax.swing.JLabel" name="fileLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/Bundle.properties" key="CentralRepoCommentDialog.fileLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="pathLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/Bundle.properties" key="CentralRepoCommentDialog.pathLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="commentLabel"> <Component class="javax.swing.JLabel" name="commentLabel">
<Properties> <Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">

View File

@ -18,6 +18,7 @@
*/ */
package org.sleuthkit.autopsy.centralrepository; package org.sleuthkit.autopsy.centralrepository;
import org.openide.util.NbBundle.Messages;
import org.openide.windows.WindowManager; import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
@ -26,6 +27,7 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeIns
* Dialog to allow Central Repository file instance comments to be added and * Dialog to allow Central Repository file instance comments to be added and
* modified. * modified.
*/ */
@Messages({"CentralRepoCommentDialog.title.addEditCentralRepoComment=Add/Edit Central Repository Comment"})
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
final class CentralRepoCommentDialog extends javax.swing.JDialog { final class CentralRepoCommentDialog extends javax.swing.JDialog {
@ -37,10 +39,9 @@ final class CentralRepoCommentDialog extends javax.swing.JDialog {
* Create an instance. * Create an instance.
* *
* @param correlationAttribute The correlation attribute to be modified. * @param correlationAttribute The correlation attribute to be modified.
* @param title The title to assign the dialog.
*/ */
CentralRepoCommentDialog(CorrelationAttribute correlationAttribute, String title) { CentralRepoCommentDialog(CorrelationAttribute correlationAttribute) {
super(WindowManager.getDefault().getMainWindow(), title); super(WindowManager.getDefault().getMainWindow(), Bundle.CentralRepoCommentDialog_title_addEditCentralRepoComment());
initComponents(); initComponents();
@ -51,7 +52,6 @@ final class CentralRepoCommentDialog extends javax.swing.JDialog {
currentComment = instance.getComment(); currentComment = instance.getComment();
} }
pathLabel.setText(instance.getFilePath());
commentTextArea.setText(instance.getComment()); commentTextArea.setText(instance.getComment());
this.correlationAttribute = correlationAttribute; this.correlationAttribute = correlationAttribute;
@ -82,6 +82,7 @@ final class CentralRepoCommentDialog extends javax.swing.JDialog {
* Get the current comment. * Get the current comment.
* If the user hit OK, this will be the new comment. * If the user hit OK, this will be the new comment.
* If the user canceled, this will be the original comment. * If the user canceled, this will be the original comment.
*
* @return the comment * @return the comment
*/ */
String getComment() { String getComment() {
@ -101,8 +102,6 @@ final class CentralRepoCommentDialog extends javax.swing.JDialog {
commentTextArea = new javax.swing.JTextArea(); commentTextArea = new javax.swing.JTextArea();
okButton = new javax.swing.JButton(); okButton = new javax.swing.JButton();
cancelButton = new javax.swing.JButton(); cancelButton = new javax.swing.JButton();
fileLabel = new javax.swing.JLabel();
pathLabel = new javax.swing.JLabel();
commentLabel = new javax.swing.JLabel(); commentLabel = new javax.swing.JLabel();
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
@ -129,10 +128,6 @@ final class CentralRepoCommentDialog extends javax.swing.JDialog {
} }
}); });
org.openide.awt.Mnemonics.setLocalizedText(fileLabel, org.openide.util.NbBundle.getMessage(CentralRepoCommentDialog.class, "CentralRepoCommentDialog.fileLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(pathLabel, org.openide.util.NbBundle.getMessage(CentralRepoCommentDialog.class, "CentralRepoCommentDialog.pathLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(commentLabel, org.openide.util.NbBundle.getMessage(CentralRepoCommentDialog.class, "CentralRepoCommentDialog.commentLabel.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(commentLabel, org.openide.util.NbBundle.getMessage(CentralRepoCommentDialog.class, "CentralRepoCommentDialog.commentLabel.text")); // NOI18N
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
@ -144,12 +139,7 @@ final class CentralRepoCommentDialog extends javax.swing.JDialog {
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 500, Short.MAX_VALUE) .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 500, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(commentLabel)
.addGroup(layout.createSequentialGroup()
.addComponent(fileLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(pathLabel))
.addComponent(commentLabel))
.addGap(0, 451, Short.MAX_VALUE)) .addGap(0, 451, Short.MAX_VALUE))
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addGap(0, 0, Short.MAX_VALUE) .addGap(0, 0, Short.MAX_VALUE)
@ -162,17 +152,13 @@ final class CentralRepoCommentDialog extends javax.swing.JDialog {
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addContainerGap() .addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(fileLabel)
.addComponent(pathLabel))
.addGap(19, 19, 19)
.addComponent(commentLabel) .addComponent(commentLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jScrollPane1) .addComponent(jScrollPane1)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(okButton) .addComponent(cancelButton)
.addComponent(cancelButton)) .addComponent(okButton))
.addContainerGap()) .addContainerGap())
); );
@ -195,9 +181,7 @@ final class CentralRepoCommentDialog extends javax.swing.JDialog {
private javax.swing.JButton cancelButton; private javax.swing.JButton cancelButton;
private javax.swing.JLabel commentLabel; private javax.swing.JLabel commentLabel;
private javax.swing.JTextArea commentTextArea; private javax.swing.JTextArea commentTextArea;
private javax.swing.JLabel fileLabel;
private javax.swing.JScrollPane jScrollPane1; private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JButton okButton; private javax.swing.JButton okButton;
private javax.swing.JLabel pathLabel;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
} }

View File

@ -38,19 +38,23 @@ public class CentralRepoContextMenuActionsProvider implements ContextMenuActions
@Override @Override
public List<Action> getActions() { public List<Action> getActions() {
ArrayList<Action> actions = new ArrayList<>(); ArrayList<Action> actionsList = new ArrayList<>();
Collection<? extends AbstractFile> selectedFiles = Utilities.actionsGlobalContext().lookupAll(AbstractFile.class); Collection<? extends AbstractFile> selectedFiles = Utilities.actionsGlobalContext().lookupAll(AbstractFile.class);
if (selectedFiles.size() != 1) { if (selectedFiles.size() != 1) {
return actions; return actionsList;
} }
for (AbstractFile file : selectedFiles) { for (AbstractFile file : selectedFiles) {
if (EamDbUtil.useCentralRepo() && EamArtifactUtil.isSupportedAbstractFileType(file) && file.isFile()) { if (EamDbUtil.useCentralRepo() && EamArtifactUtil.isSupportedAbstractFileType(file) && file.isFile()) {
actions.add(AddEditCentralRepoCommentAction.createAddEditCentralRepoCommentAction(file)); AddEditCentralRepoCommentAction action = new AddEditCentralRepoCommentAction(file);
if (action.getCorrelationAttribute() == null) {
action.setEnabled(false);
}
actionsList.add(action);
} }
} }
return actions; return actionsList;
} }
} }

View File

@ -3,7 +3,7 @@ DataContentViewerOtherCases.showCaseDetailsMenuItem.text=Show Case Details
DataContentViewerOtherCases.table.toolTip.text=Click column name to sort. Right-click on the table for more options. DataContentViewerOtherCases.table.toolTip.text=Click column name to sort. Right-click on the table for more options.
DataContentViewerOtherCases.exportToCSVMenuItem.text=Export Selected Rows to CSV DataContentViewerOtherCases.exportToCSVMenuItem.text=Export Selected Rows to CSV
DataContentViewerOtherCases.showCommonalityMenuItem.text=Show Frequency DataContentViewerOtherCases.showCommonalityMenuItem.text=Show Frequency
DataContentViewerOtherCases.addCommentMenuItem.text=Add/Edit Comment DataContentViewerOtherCases.addCommentMenuItem.text=Add/Edit Central Repository Comment
DataContentViewerOtherCases.earliestCaseDate.text=Earliest Case Date DataContentViewerOtherCases.earliestCaseDate.text=Earliest Case Date
DataContentViewerOtherCases.earliestCaseLabel.toolTipText= DataContentViewerOtherCases.earliestCaseLabel.toolTipText=
DataContentViewerOtherCases.earliestCaseLabel.text=Central Repository Starting Date: DataContentViewerOtherCases.earliestCaseLabel.text=Central Repository Starting Date:

View File

@ -80,7 +80,7 @@
</DimensionLayout> </DimensionLayout>
<DimensionLayout dim="1"> <DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Component id="otherCasesPanel" pref="483" max="32767" attributes="0"/> <Component id="otherCasesPanel" pref="59" max="32767" attributes="0"/>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
</Layout> </Layout>
@ -106,7 +106,7 @@
<EmptySpace min="0" pref="483" max="32767" attributes="0"/> <EmptySpace min="0" pref="483" max="32767" attributes="0"/>
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0"> <Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0">
<Component id="tableContainerPanel" pref="483" max="32767" attributes="0"/> <Component id="tableContainerPanel" pref="59" max="32767" attributes="0"/>
<EmptySpace min="-2" pref="0" max="-2" attributes="0"/> <EmptySpace min="-2" pref="0" max="-2" attributes="0"/>
</Group> </Group>
</Group> </Group>
@ -133,24 +133,19 @@
<Component id="earliestCaseLabel" min="-2" max="-2" attributes="0"/> <Component id="earliestCaseLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="earliestCaseDate" min="-2" max="-2" attributes="0"/> <Component id="earliestCaseDate" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/> <EmptySpace max="32767" attributes="0"/>
<Component id="tableStatusPanelLabel" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group> </Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
<DimensionLayout dim="1"> <DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0"> <Group type="102" alignment="1" attributes="0">
<Component id="tableScrollPane" pref="176" max="32767" attributes="0"/> <Component id="tableScrollPane" pref="27" max="32767" attributes="0"/>
<EmptySpace min="0" pref="0" max="-2" attributes="0"/> <EmptySpace min="-2" pref="2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="1" attributes="0">
<Group type="103" groupAlignment="3" attributes="0"> <Group type="103" groupAlignment="3" attributes="0">
<Component id="earliestCaseLabel" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="earliestCaseLabel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="earliestCaseDate" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="earliestCaseDate" alignment="3" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<Component id="tableStatusPanelLabel" min="-2" pref="16" max="-2" attributes="0"/>
</Group>
<EmptySpace min="0" pref="0" max="-2" attributes="0"/> <EmptySpace min="0" pref="0" max="-2" attributes="0"/>
<Component id="tableStatusPanel" min="-2" max="-2" attributes="0"/> <Component id="tableStatusPanel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="-2" attributes="0"/> <EmptySpace min="0" pref="0" max="-2" attributes="0"/>
@ -230,13 +225,6 @@
</DimensionLayout> </DimensionLayout>
</Layout> </Layout>
</Container> </Container>
<Component class="javax.swing.JLabel" name="tableStatusPanelLabel">
<Properties>
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="33" green="0" red="ff" type="rgb"/>
</Property>
</Properties>
</Component>
</SubComponents> </SubComponents>
</Container> </Container>
</SubComponents> </SubComponents>

View File

@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.centralrepository.contentviewer; package org.sleuthkit.autopsy.centralrepository.contentviewer;
import java.awt.Component; import java.awt.Component;
import java.awt.FontMetrics;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.io.BufferedWriter; import java.io.BufferedWriter;
@ -38,7 +39,6 @@ import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.logging.Level; import java.util.logging.Level;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import java.util.stream.Collectors;
import javax.swing.JFileChooser; import javax.swing.JFileChooser;
import javax.swing.JMenuItem; import javax.swing.JMenuItem;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
@ -86,7 +86,10 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
private static final long serialVersionUID = -1L; private static final long serialVersionUID = -1L;
private final static Logger logger = Logger.getLogger(DataContentViewerOtherCases.class.getName()); private static final Logger logger = Logger.getLogger(DataContentViewerOtherCases.class.getName());
private static final int DEFAULT_MIN_CELL_WIDTH = 15;
private static final int CELL_TEXT_WIDTH_PADDING = 5;
private final DataContentViewerOtherCasesTableModel tableModel; private final DataContentViewerOtherCasesTableModel tableModel;
private final Collection<CorrelationAttribute> correlationAttributes; private final Collection<CorrelationAttribute> correlationAttributes;
@ -126,13 +129,16 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
showCommonalityDetails(); showCommonalityDetails();
} else if (jmi.equals(addCommentMenuItem)) { } else if (jmi.equals(addCommentMenuItem)) {
try { try {
OtherOccurrenceNodeData selectedNode = (OtherOccurrenceNodeData) tableModel.getRow(otherCasesTable.getSelectedRow()); OtherOccurrenceNodeInstanceData selectedNode = (OtherOccurrenceNodeInstanceData) tableModel.getRow(otherCasesTable.getSelectedRow());
AddEditCentralRepoCommentAction action = AddEditCentralRepoCommentAction.createAddEditCommentAction(selectedNode.createCorrelationAttribute()); AddEditCentralRepoCommentAction action = new AddEditCentralRepoCommentAction(selectedNode.createCorrelationAttribute());
String currentComment = action.addEditCentralRepoComment(); action.actionPerformed(null);
selectedNode.updateComment(currentComment); String currentComment = action.getComment();
if (currentComment != null) {
selectedNode.updateComment(action.getComment());
otherCasesTable.repaint(); otherCasesTable.repaint();
}
} catch (EamDbException ex) { } catch (EamDbException ex) {
logger.log(Level.SEVERE, "Error performing Add/Edit Comment action", ex); logger.log(Level.SEVERE, "Error performing Add/Edit Central Repository Comment action", ex);
} }
} }
} }
@ -147,7 +153,6 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
// Set background of every nth row as light grey. // Set background of every nth row as light grey.
TableCellRenderer renderer = new DataContentViewerOtherCasesTableCellRenderer(); TableCellRenderer renderer = new DataContentViewerOtherCasesTableCellRenderer();
otherCasesTable.setDefaultRenderer(Object.class, renderer); otherCasesTable.setDefaultRenderer(Object.class, renderer);
tableStatusPanelLabel.setVisible(false);
} }
@ -205,7 +210,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
if (-1 != selectedRowViewIdx) { if (-1 != selectedRowViewIdx) {
EamDb dbManager = EamDb.getInstance(); EamDb dbManager = EamDb.getInstance();
int selectedRowModelIdx = otherCasesTable.convertRowIndexToModel(selectedRowViewIdx); int selectedRowModelIdx = otherCasesTable.convertRowIndexToModel(selectedRowViewIdx);
OtherOccurrenceNodeData nodeData = (OtherOccurrenceNodeData) tableModel.getRow(selectedRowModelIdx); OtherOccurrenceNodeInstanceData nodeData = (OtherOccurrenceNodeInstanceData) tableModel.getRow(selectedRowModelIdx);
CorrelationCase eamCasePartial = nodeData.getCorrelationAttributeInstance().getCorrelationCase(); CorrelationCase eamCasePartial = nodeData.getCorrelationAttributeInstance().getCorrelationCase();
if (eamCasePartial == null) { if (eamCasePartial == null) {
JOptionPane.showConfirmDialog(showCaseDetailsMenuItem, JOptionPane.showConfirmDialog(showCaseDetailsMenuItem,
@ -460,8 +465,8 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
@Messages({"DataContentViewerOtherCases.earliestCaseNotAvailable= Not Enabled."}) @Messages({"DataContentViewerOtherCases.earliestCaseNotAvailable= Not Enabled."})
/** /**
* Gets the list of Eam Cases and determines the earliest case creation date. * Gets the list of Eam Cases and determines the earliest case creation
* Sets the label to display the earliest date string to the user. * date. Sets the label to display the earliest date string to the user.
*/ */
private void setEarliestCaseDate() { private void setEarliestCaseDate() {
String dateStringDisplay = Bundle.DataContentViewerOtherCases_earliestCaseNotAvailable(); String dateStringDisplay = Bundle.DataContentViewerOtherCases_earliestCaseNotAvailable();
@ -493,10 +498,10 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
} }
/** /**
* Query the central repo database (if enabled) and the case database to find all * Query the central repo database (if enabled) and the case database to
* artifact instances correlated to the given central repository artifact. If the * find all artifact instances correlated to the given central repository
* central repo is not enabled, this will only return files from the current case * artifact. If the central repo is not enabled, this will only return files
* with matching MD5 hashes. * from the current case with matching MD5 hashes.
* *
* @param corAttr CorrelationAttribute to query for * @param corAttr CorrelationAttribute to query for
* @param dataSourceName Data source to filter results * @param dataSourceName Data source to filter results
@ -504,18 +509,18 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
* *
* @return A collection of correlated artifact instances * @return A collection of correlated artifact instances
*/ */
private Map<UniquePathKey,OtherOccurrenceNodeData> getCorrelatedInstances(CorrelationAttribute corAttr, String dataSourceName, String deviceId) { private Map<UniquePathKey, OtherOccurrenceNodeInstanceData> getCorrelatedInstances(CorrelationAttribute corAttr, String dataSourceName, String deviceId) {
// @@@ Check exception // @@@ Check exception
try { try {
final Case openCase = Case.getCurrentCase(); final Case openCase = Case.getCurrentCase();
String caseUUID = openCase.getName(); String caseUUID = openCase.getName();
HashMap<UniquePathKey,OtherOccurrenceNodeData> nodeDataMap = new HashMap<>(); HashMap<UniquePathKey, OtherOccurrenceNodeInstanceData> nodeDataMap = new HashMap<>();
if (EamDb.isEnabled()) { if (EamDb.isEnabled()) {
List<CorrelationAttributeInstance> instances = EamDb.getInstance().getArtifactInstancesByTypeValue(corAttr.getCorrelationType(), corAttr.getCorrelationValue()); List<CorrelationAttributeInstance> instances = EamDb.getInstance().getArtifactInstancesByTypeValue(corAttr.getCorrelationType(), corAttr.getCorrelationValue());
for (CorrelationAttributeInstance artifactInstance:instances) { for (CorrelationAttributeInstance artifactInstance : instances) {
// Only add the attribute if it isn't the object the user selected. // Only add the attribute if it isn't the object the user selected.
// We consider it to be a different object if at least one of the following is true: // We consider it to be a different object if at least one of the following is true:
@ -528,7 +533,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|| !artifactInstance.getCorrelationDataSource().getDeviceID().equals(deviceId) || !artifactInstance.getCorrelationDataSource().getDeviceID().equals(deviceId)
|| !artifactInstance.getFilePath().equalsIgnoreCase(file.getParentPath() + file.getName())) { || !artifactInstance.getFilePath().equalsIgnoreCase(file.getParentPath() + file.getName())) {
OtherOccurrenceNodeData newNode = new OtherOccurrenceNodeData(artifactInstance, corAttr.getCorrelationType(), corAttr.getCorrelationValue()); OtherOccurrenceNodeInstanceData newNode = new OtherOccurrenceNodeInstanceData(artifactInstance, corAttr.getCorrelationType(), corAttr.getCorrelationValue());
UniquePathKey uniquePathKey = new UniquePathKey(newNode); UniquePathKey uniquePathKey = new UniquePathKey(newNode);
nodeDataMap.put(uniquePathKey, newNode); nodeDataMap.put(uniquePathKey, newNode);
} }
@ -558,10 +563,14 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
} }
/** /**
* Get all other abstract files in the current case with the same MD5 as the selected node. * Get all other abstract files in the current case with the same MD5 as the
* selected node.
*
* @param corAttr The CorrelationAttribute containing the MD5 to search for * @param corAttr The CorrelationAttribute containing the MD5 to search for
* @param openCase The current case * @param openCase The current case
*
* @return List of matching AbstractFile objects * @return List of matching AbstractFile objects
*
* @throws NoCurrentCaseException * @throws NoCurrentCaseException
* @throws TskCoreException * @throws TskCoreException
* @throws EamDbException * @throws EamDbException
@ -592,9 +601,9 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
* @throws TskCoreException * @throws TskCoreException
* @throws EamDbException * @throws EamDbException
*/ */
private void addOrUpdateNodeData(final Case autopsyCase, Map<UniquePathKey,OtherOccurrenceNodeData> nodeDataMap, AbstractFile newFile) throws TskCoreException, EamDbException { private void addOrUpdateNodeData(final Case autopsyCase, Map<UniquePathKey, OtherOccurrenceNodeInstanceData> nodeDataMap, AbstractFile newFile) throws TskCoreException, EamDbException {
OtherOccurrenceNodeData newNode = new OtherOccurrenceNodeData(newFile, autopsyCase); OtherOccurrenceNodeInstanceData newNode = new OtherOccurrenceNodeInstanceData(newFile, autopsyCase);
// If the caseDB object has a notable tag associated with it, update // If the caseDB object has a notable tag associated with it, update
// the known status to BAD // the known status to BAD
@ -617,7 +626,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
// Otherwise this is a new node so add the new node to the map. // Otherwise this is a new node so add the new node to the map.
if (nodeDataMap.containsKey(uniquePathKey)) { if (nodeDataMap.containsKey(uniquePathKey)) {
if (newNode.getKnown() == TskData.FileKnown.BAD) { if (newNode.getKnown() == TskData.FileKnown.BAD) {
OtherOccurrenceNodeData prevInstance = nodeDataMap.get(uniquePathKey); OtherOccurrenceNodeInstanceData prevInstance = nodeDataMap.get(uniquePathKey);
prevInstance.updateKnown(newNode.getKnown()); prevInstance.updateKnown(newNode.getKnown());
} }
} else { } else {
@ -640,7 +649,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
} else { } else {
return this.file != null return this.file != null
&& this.file.getSize() > 0 && this.file.getSize() > 0
&& ((this.file.getMd5Hash() != null) && ( ! this.file.getMd5Hash().isEmpty())); && ((this.file.getMd5Hash() != null) && (!this.file.getMd5Hash().isEmpty()));
} }
} }
@ -663,8 +672,10 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
* *
* @param node The node being viewed. * @param node The node being viewed.
*/ */
@Messages({"DataContentViewerOtherCases.table.isempty=There are no associated artifacts or files from other occurrences to display.", @Messages({
"DataContentViewerOtherCases.table.noArtifacts=Correlation cannot be performed on the selected file."}) "DataContentViewerOtherCases.table.noArtifacts=Item has no attributes with which to search.",
"DataContentViewerOtherCases.table.noResultsFound=No results found."
})
private void populateTable(Node node) { private void populateTable(Node node) {
String dataSourceName = ""; String dataSourceName = "";
String deviceId = ""; String deviceId = "";
@ -682,7 +693,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
// get the attributes we can correlate on // get the attributes we can correlate on
correlationAttributes.addAll(getCorrelationAttributesFromNode(node)); correlationAttributes.addAll(getCorrelationAttributesFromNode(node));
for (CorrelationAttribute corAttr : correlationAttributes) { for (CorrelationAttribute corAttr : correlationAttributes) {
Map<UniquePathKey,OtherOccurrenceNodeData> correlatedNodeDataMap = new HashMap<>(0); Map<UniquePathKey, OtherOccurrenceNodeInstanceData> correlatedNodeDataMap = new HashMap<>(0);
// get correlation and reference set instances from DB // get correlation and reference set instances from DB
correlatedNodeDataMap.putAll(getCorrelatedInstances(corAttr, dataSourceName, deviceId)); correlatedNodeDataMap.putAll(getCorrelatedInstances(corAttr, dataSourceName, deviceId));
@ -694,36 +705,45 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
} }
if (correlationAttributes.isEmpty()) { if (correlationAttributes.isEmpty()) {
// @@@ BC: We should have a more descriptive message than this. Mention that the file didn't have a MD5, etc. tableModel.addNodeData(new OtherOccurrenceNodeMessageData(Bundle.DataContentViewerOtherCases_table_noArtifacts()));
displayMessageOnTableStatusPanel(Bundle.DataContentViewerOtherCases_table_noArtifacts()); setColumnWidthToText(0, Bundle.DataContentViewerOtherCases_table_noArtifacts());
} else if (0 == tableModel.getRowCount()) { } else if (0 == tableModel.getRowCount()) {
displayMessageOnTableStatusPanel(Bundle.DataContentViewerOtherCases_table_isempty()); tableModel.addNodeData(new OtherOccurrenceNodeMessageData(Bundle.DataContentViewerOtherCases_table_noResultsFound()));
setColumnWidthToText(0, Bundle.DataContentViewerOtherCases_table_noResultsFound());
} else { } else {
clearMessageOnTableStatusPanel();
setColumnWidths(); setColumnWidths();
} }
setEarliestCaseDate(); setEarliestCaseDate();
} }
/**
* Adjust a given column for the text provided.
*
* @param columnIndex The index of the column to adjust.
* @param text The text whose length will be used to adjust the
* column width.
*/
private void setColumnWidthToText(int columnIndex, String text) {
TableColumn column = otherCasesTable.getColumnModel().getColumn(columnIndex);
FontMetrics fontMetrics = otherCasesTable.getFontMetrics(otherCasesTable.getFont());
int stringWidth = fontMetrics.stringWidth(text);
column.setMinWidth(stringWidth + CELL_TEXT_WIDTH_PADDING);
}
/**
* Adjust column widths to their preferred values.
*/
private void setColumnWidths() { private void setColumnWidths() {
for (int idx = 0; idx < tableModel.getColumnCount(); idx++) { for (int idx = 0; idx < tableModel.getColumnCount(); idx++) {
TableColumn column = otherCasesTable.getColumnModel().getColumn(idx); TableColumn column = otherCasesTable.getColumnModel().getColumn(idx);
int colWidth = tableModel.getColumnPreferredWidth(idx); column.setMinWidth(DEFAULT_MIN_CELL_WIDTH);
if (0 < colWidth) { int columnWidth = tableModel.getColumnPreferredWidth(idx);
column.setPreferredWidth(colWidth); if (columnWidth > 0) {
column.setPreferredWidth(columnWidth);
} }
} }
} }
private void displayMessageOnTableStatusPanel(String message) {
tableStatusPanelLabel.setText(message);
tableStatusPanelLabel.setVisible(true);
}
private void clearMessageOnTableStatusPanel() {
tableStatusPanelLabel.setVisible(false);
}
/** /**
* This method is called from within the constructor to initialize the form. * 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 * WARNING: Do NOT modify this code. The content of this method is always
@ -747,7 +767,6 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
earliestCaseLabel = new javax.swing.JLabel(); earliestCaseLabel = new javax.swing.JLabel();
earliestCaseDate = new javax.swing.JLabel(); earliestCaseDate = new javax.swing.JLabel();
tableStatusPanel = new javax.swing.JPanel(); tableStatusPanel = new javax.swing.JPanel();
tableStatusPanelLabel = new javax.swing.JLabel();
rightClickPopupMenu.addPopupMenuListener(new javax.swing.event.PopupMenuListener() { rightClickPopupMenu.addPopupMenuListener(new javax.swing.event.PopupMenuListener() {
public void popupMenuCanceled(javax.swing.event.PopupMenuEvent evt) { public void popupMenuCanceled(javax.swing.event.PopupMenuEvent evt) {
@ -809,8 +828,6 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
.addGap(0, 16, Short.MAX_VALUE) .addGap(0, 16, Short.MAX_VALUE)
); );
tableStatusPanelLabel.setForeground(new java.awt.Color(255, 0, 51));
javax.swing.GroupLayout tableContainerPanelLayout = new javax.swing.GroupLayout(tableContainerPanel); javax.swing.GroupLayout tableContainerPanelLayout = new javax.swing.GroupLayout(tableContainerPanel);
tableContainerPanel.setLayout(tableContainerPanelLayout); tableContainerPanel.setLayout(tableContainerPanelLayout);
tableContainerPanelLayout.setHorizontalGroup( tableContainerPanelLayout.setHorizontalGroup(
@ -823,20 +840,16 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
.addComponent(earliestCaseLabel) .addComponent(earliestCaseLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(earliestCaseDate) .addComponent(earliestCaseDate)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addComponent(tableStatusPanelLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addContainerGap())
); );
tableContainerPanelLayout.setVerticalGroup( tableContainerPanelLayout.setVerticalGroup(
tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, tableContainerPanelLayout.createSequentialGroup() .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, tableContainerPanelLayout.createSequentialGroup()
.addComponent(tableScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 176, Short.MAX_VALUE) .addComponent(tableScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 27, Short.MAX_VALUE)
.addGap(0, 0, 0) .addGap(2, 2, 2)
.addGroup(tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addGroup(tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addGroup(tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(earliestCaseLabel) .addComponent(earliestCaseLabel)
.addComponent(earliestCaseDate)) .addComponent(earliestCaseDate))
.addComponent(tableStatusPanelLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGap(0, 0, 0) .addGap(0, 0, 0)
.addComponent(tableStatusPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(tableStatusPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 0, 0)) .addGap(0, 0, 0))
@ -855,7 +868,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
.addGap(0, 483, Short.MAX_VALUE) .addGap(0, 483, Short.MAX_VALUE)
.addGroup(otherCasesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(otherCasesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(otherCasesPanelLayout.createSequentialGroup() .addGroup(otherCasesPanelLayout.createSequentialGroup()
.addComponent(tableContainerPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 483, Short.MAX_VALUE) .addComponent(tableContainerPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 59, Short.MAX_VALUE)
.addGap(0, 0, 0))) .addGap(0, 0, 0)))
); );
@ -867,7 +880,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
); );
layout.setVerticalGroup( layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(otherCasesPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 483, Short.MAX_VALUE) .addComponent(otherCasesPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 59, Short.MAX_VALUE)
); );
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
@ -877,8 +890,9 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
if (EamDbUtil.useCentralRepo() && otherCasesTable.getSelectedRowCount() == 1) { if (EamDbUtil.useCentralRepo() && otherCasesTable.getSelectedRowCount() == 1) {
int rowIndex = otherCasesTable.getSelectedRow(); int rowIndex = otherCasesTable.getSelectedRow();
OtherOccurrenceNodeData selectedNode = (OtherOccurrenceNodeData) tableModel.getRow(rowIndex); OtherOccurrenceNodeData selectedNode = (OtherOccurrenceNodeData) tableModel.getRow(rowIndex);
if (selectedNode.isCentralRepoNode()) { if (selectedNode instanceof OtherOccurrenceNodeInstanceData) {
enableCentralRepoActions = true; OtherOccurrenceNodeInstanceData instanceData = (OtherOccurrenceNodeInstanceData) selectedNode;
enableCentralRepoActions = instanceData.isCentralRepoNode();
} }
} }
@ -902,20 +916,19 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
private javax.swing.JPanel tableContainerPanel; private javax.swing.JPanel tableContainerPanel;
private javax.swing.JScrollPane tableScrollPane; private javax.swing.JScrollPane tableScrollPane;
private javax.swing.JPanel tableStatusPanel; private javax.swing.JPanel tableStatusPanel;
private javax.swing.JLabel tableStatusPanelLabel;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
/** /**
* Used as a key to ensure we eliminate duplicates from the result set by * Used as a key to ensure we eliminate duplicates from the result set by
* not overwriting CR correlation instances. * not overwriting CR correlation instances.
*/ */
static final class UniquePathKey { private static final class UniquePathKey {
private final String dataSourceID; private final String dataSourceID;
private final String filePath; private final String filePath;
private final String type; private final String type;
UniquePathKey(OtherOccurrenceNodeData nodeData) { UniquePathKey(OtherOccurrenceNodeInstanceData nodeData) {
super(); super();
dataSourceID = nodeData.getDeviceID(); dataSourceID = nodeData.getDeviceID();
if (nodeData.getFilePath() != null) { if (nodeData.getFilePath() != null) {
@ -929,10 +942,10 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
@Override @Override
public boolean equals(Object other) { public boolean equals(Object other) {
if (other instanceof UniquePathKey) { if (other instanceof UniquePathKey) {
UniquePathKey otherKey = (UniquePathKey)(other); UniquePathKey otherKey = (UniquePathKey) (other);
return ( Objects.equals(otherKey.dataSourceID, this.dataSourceID) return (Objects.equals(otherKey.getDataSourceID(), this.getDataSourceID())
&& Objects.equals(otherKey.filePath, this.filePath) && Objects.equals(otherKey.getFilePath(), this.getFilePath())
&& Objects.equals(otherKey.type, this.type)); && Objects.equals(otherKey.getType(), this.getType()));
} }
return false; return false;
} }
@ -942,7 +955,34 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
//int hash = 7; //int hash = 7;
//hash = 67 * hash + this.dataSourceID.hashCode(); //hash = 67 * hash + this.dataSourceID.hashCode();
//hash = 67 * hash + this.filePath.hashCode(); //hash = 67 * hash + this.filePath.hashCode();
return Objects.hash(dataSourceID, filePath, type); return Objects.hash(getDataSourceID(), getFilePath(), getType());
}
/**
* Get the type of this UniquePathKey.
*
* @return the type
*/
String getType() {
return type;
}
/**
* Get the file path for the UniquePathKey.
*
* @return the filePath
*/
String getFilePath() {
return filePath;
}
/**
* Get the data source id for the UniquePathKey.
*
* @return the dataSourceID
*/
String getDataSourceID() {
return dataSourceID;
} }
} }

View File

@ -1,7 +1,7 @@
/* /*
* Central Repository * Central Repository
* *
* Copyright 2015-2017 Basis Technology Corp. * Copyright 2015-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -22,20 +22,20 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.swing.table.AbstractTableModel; import javax.swing.table.AbstractTableModel;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
/** /**
* Model for cells in data content viewer table * Model for cells in data content viewer table
*/ */
public class DataContentViewerOtherCasesTableModel extends AbstractTableModel { public class DataContentViewerOtherCasesTableModel extends AbstractTableModel {
private static final long serialVersionUID = 1L;
@Messages({"DataContentViewerOtherCasesTableModel.case=Case", @Messages({"DataContentViewerOtherCasesTableModel.case=Case",
"DataContentViewerOtherCasesTableModel.device=Device", "DataContentViewerOtherCasesTableModel.device=Device",
"DataContentViewerOtherCasesTableModel.dataSource=Data Source", "DataContentViewerOtherCasesTableModel.dataSource=Data Source",
"DataContentViewerOtherCasesTableModel.path=Path", "DataContentViewerOtherCasesTableModel.path=Path",
"DataContentViewerOtherCasesTableModel.type=Correlation Type", "DataContentViewerOtherCasesTableModel.attribute=Matched Attribute",
"DataContentViewerOtherCasesTableModel.value=Correlation Value", "DataContentViewerOtherCasesTableModel.value=Attribute Value",
"DataContentViewerOtherCasesTableModel.known=Known", "DataContentViewerOtherCasesTableModel.known=Known",
"DataContentViewerOtherCasesTableModel.comment=Comment", "DataContentViewerOtherCasesTableModel.comment=Comment",
"DataContentViewerOtherCasesTableModel.noData=No Data.",}) "DataContentViewerOtherCasesTableModel.noData=No Data.",})
@ -44,7 +44,7 @@ public class DataContentViewerOtherCasesTableModel extends AbstractTableModel {
// If order is changed, update the CellRenderer to ensure correct row coloring. // If order is changed, update the CellRenderer to ensure correct row coloring.
CASE_NAME(Bundle.DataContentViewerOtherCasesTableModel_case(), 100), CASE_NAME(Bundle.DataContentViewerOtherCasesTableModel_case(), 100),
DATA_SOURCE(Bundle.DataContentViewerOtherCasesTableModel_dataSource(), 100), DATA_SOURCE(Bundle.DataContentViewerOtherCasesTableModel_dataSource(), 100),
TYPE(Bundle.DataContentViewerOtherCasesTableModel_type(), 100), ATTRIBUTE(Bundle.DataContentViewerOtherCasesTableModel_attribute(), 125),
VALUE(Bundle.DataContentViewerOtherCasesTableModel_value(), 200), VALUE(Bundle.DataContentViewerOtherCasesTableModel_value(), 200),
KNOWN(Bundle.DataContentViewerOtherCasesTableModel_known(), 50), KNOWN(Bundle.DataContentViewerOtherCasesTableModel_known(), 50),
FILE_PATH(Bundle.DataContentViewerOtherCasesTableModel_path(), 450), FILE_PATH(Bundle.DataContentViewerOtherCasesTableModel_path(), 450),
@ -68,7 +68,7 @@ public class DataContentViewerOtherCasesTableModel extends AbstractTableModel {
} }
}; };
List<OtherOccurrenceNodeData> nodeDataList; private final List<OtherOccurrenceNodeData> nodeDataList;
DataContentViewerOtherCasesTableModel() { DataContentViewerOtherCasesTableModel() {
nodeDataList = new ArrayList<>(); nodeDataList = new ArrayList<>();
@ -109,26 +109,41 @@ public class DataContentViewerOtherCasesTableModel extends AbstractTableModel {
return Bundle.DataContentViewerOtherCasesTableModel_noData(); return Bundle.DataContentViewerOtherCasesTableModel_noData();
} }
return mapValueById(rowIdx, TableColumns.values()[colIdx]); OtherOccurrenceNodeData nodeData = nodeDataList.get(rowIdx);
TableColumns columnId = TableColumns.values()[colIdx];
if (nodeData instanceof OtherOccurrenceNodeMessageData) {
return mapNodeMessageData((OtherOccurrenceNodeMessageData) nodeData, columnId);
} }
return mapNodeInstanceData((OtherOccurrenceNodeInstanceData) nodeData, columnId);
Object getRow(int rowIdx) {
return nodeDataList.get(rowIdx);
} }
/** /**
* Map a rowIdx and colId to the value in that cell. * Map a column ID to the value in that cell for node message data.
* *
* @param rowIdx Index of row to search * @param nodeData The node message data.
* @param colId ID of column to search * @param columnId The ID of the cell column.
* *
* @return value in the cell * @return The value in the cell.
*/ */
private Object mapValueById(int rowIdx, TableColumns colId) { private Object mapNodeMessageData(OtherOccurrenceNodeMessageData nodeData, TableColumns columnId) {
OtherOccurrenceNodeData nodeData = nodeDataList.get(rowIdx); if (columnId == TableColumns.CASE_NAME) {
return nodeData.getDisplayMessage();
}
return "";
}
/**
* Map a column ID to the value in that cell for node instance data.
*
* @param nodeData The node instance data.
* @param columnId The ID of the cell column.
*
* @return The value in the cell.
*/
private Object mapNodeInstanceData(OtherOccurrenceNodeInstanceData nodeData, TableColumns columnId) {
String value = Bundle.DataContentViewerOtherCasesTableModel_noData(); String value = Bundle.DataContentViewerOtherCasesTableModel_noData();
switch (colId) { switch (columnId) {
case CASE_NAME: case CASE_NAME:
if (null != nodeData.getCaseName()) { if (null != nodeData.getCaseName()) {
value = nodeData.getCaseName(); value = nodeData.getCaseName();
@ -147,7 +162,7 @@ public class DataContentViewerOtherCasesTableModel extends AbstractTableModel {
case FILE_PATH: case FILE_PATH:
value = nodeData.getFilePath(); value = nodeData.getFilePath();
break; break;
case TYPE: case ATTRIBUTE:
value = nodeData.getType(); value = nodeData.getType();
break; break;
case VALUE: case VALUE:
@ -159,10 +174,16 @@ public class DataContentViewerOtherCasesTableModel extends AbstractTableModel {
case COMMENT: case COMMENT:
value = nodeData.getComment(); value = nodeData.getComment();
break; break;
default: // This shouldn't occur! Use default "No data" value.
break;
} }
return value; return value;
} }
Object getRow(int rowIdx) {
return nodeDataList.get(rowIdx);
}
@Override @Override
public Class<String> getColumnClass(int colIdx) { public Class<String> getColumnClass(int colIdx) {
return String.class; return String.class;
@ -178,6 +199,9 @@ public class DataContentViewerOtherCasesTableModel extends AbstractTableModel {
fireTableDataChanged(); fireTableDataChanged();
} }
/**
* Clear the node data table.
*/
void clearTable() { void clearTable() {
nodeDataList.clear(); nodeDataList.clear();
fireTableDataChanged(); fireTableDataChanged();

View File

@ -1,5 +1,5 @@
/* /*
* Central Repository * Autopsy Forensic Browser
* *
* Copyright 2018 Basis Technology Corp. * Copyright 2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
@ -18,216 +18,9 @@
*/ */
package org.sleuthkit.autopsy.centralrepository.contentviewer; package org.sleuthkit.autopsy.centralrepository.contentviewer;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.datamodel.TskDataException;
/** /**
* Class for populating the Other Occurrences tab * Marker interface for Other Occurrences nodes.
*/ */
class OtherOccurrenceNodeData { interface OtherOccurrenceNodeData {
// For now hard code the string for the central repo files type, since
// getting it dynamically can fail.
private static final String FILE_TYPE_STR = "Files";
private final String caseName;
private String deviceID;
private String dataSourceName;
private final String filePath;
private final String typeStr;
private final CorrelationAttribute.Type type;
private final String value;
private TskData.FileKnown known;
private String comment;
private AbstractFile originalAbstractFile = null;
private CorrelationAttributeInstance originalCorrelationInstance = null;
/**
* Create a node from a central repo instance.
* @param instance The central repo instance
* @param type The type of the instance
* @param value The value of the instance
*/
OtherOccurrenceNodeData(CorrelationAttributeInstance instance, CorrelationAttribute.Type type, String value) {
caseName = instance.getCorrelationCase().getDisplayName();
deviceID = instance.getCorrelationDataSource().getDeviceID();
dataSourceName = instance.getCorrelationDataSource().getName();
filePath = instance.getFilePath();
this.typeStr = type.getDisplayName();
this.type = type;
this.value = value;
known = instance.getKnownStatus();
comment = instance.getComment();
originalCorrelationInstance = instance;
}
/**
* Create a node from an abstract file.
* @param newFile The abstract file
* @param autopsyCase The current case
* @throws EamDbException
*/
OtherOccurrenceNodeData(AbstractFile newFile, Case autopsyCase) throws EamDbException {
caseName = autopsyCase.getDisplayName();
try {
DataSource dataSource = autopsyCase.getSleuthkitCase().getDataSource(newFile.getDataSource().getId());
deviceID = dataSource.getDeviceId();
dataSourceName = dataSource.getName();
} catch (TskDataException | TskCoreException ex) {
throw new EamDbException("Error loading data source for abstract file ID " + newFile.getId(), ex);
}
filePath = newFile.getParentPath() + newFile.getName();
typeStr = FILE_TYPE_STR;
this.type = null;
value = newFile.getMd5Hash();
known = newFile.getKnown();
comment = "";
originalAbstractFile = newFile;
}
/**
* Check if this node is a "file" type
* @return true if it is a file type
*/
boolean isFileType() {
return FILE_TYPE_STR.equals(typeStr);
}
/**
* Update the known status for this node
* @param newKnownStatus The new known status
*/
void updateKnown(TskData.FileKnown newKnownStatus) {
known = newKnownStatus;
}
/**
* Update the comment for this node
* @param newComment The new comment
*/
void updateComment(String newComment) {
comment = newComment;
}
/**
* Check if this is a central repo node.
* @return true if this node was created from a central repo instance, false otherwise
*/
boolean isCentralRepoNode() {
return (originalCorrelationInstance != null);
}
/**
* Uses the saved instance plus type and value to make a new CorrelationAttribute.
* Should only be called if isCentralRepoNode() is true.
* @return the newly created CorrelationAttribute
*/
CorrelationAttribute createCorrelationAttribute() throws EamDbException {
if (! isCentralRepoNode() ) {
throw new EamDbException("Can not create CorrelationAttribute for non central repo node");
}
CorrelationAttribute attr = new CorrelationAttribute(type, value);
attr.addInstance(originalCorrelationInstance);
return attr;
}
/**
* Get the case name
* @return the case name
*/
String getCaseName() {
return caseName;
}
/**
* Get the device ID
* @return the device ID
*/
String getDeviceID() {
return deviceID;
}
/**
* Get the data source name
* @return the data source name
*/
String getDataSourceName() {
return dataSourceName;
}
/**
* Get the file path
* @return the file path
*/
String getFilePath() {
return filePath;
}
/**
* Get the type (as a string)
* @return the type
*/
String getType() {
return typeStr;
}
/**
* Get the value (MD5 hash for files)
* @return the value
*/
String getValue() {
return value;
}
/**
* Get the known status
* @return the known status
*/
TskData.FileKnown getKnown() {
return known;
}
/**
* Get the comment
* @return the comment
*/
String getComment() {
return comment;
}
/**
* Get the backing abstract file.
* Should only be called if isCentralRepoNode() is false
* @return the original abstract file
*/
AbstractFile getAbstractFile() throws EamDbException {
if (originalCorrelationInstance == null) {
throw new EamDbException("AbstractFile is null");
}
return originalAbstractFile;
}
/**
* Get the backing CorrelationAttributeInstance.
* Should only be called if isCentralRepoNode() is true
* @return the original CorrelationAttributeInstance
* @throws EamDbException
*/
CorrelationAttributeInstance getCorrelationAttributeInstance() throws EamDbException {
if (originalCorrelationInstance == null) {
throw new EamDbException("CorrelationAttributeInstance is null");
}
return originalCorrelationInstance;
}
} }

View File

@ -0,0 +1,233 @@
/*
* Central Repository
*
* Copyright 2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.centralrepository.contentviewer;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.datamodel.TskDataException;
/**
* Class for populating the Other Occurrences tab
*/
class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData {
// For now hard code the string for the central repo files type, since
// getting it dynamically can fail.
private static final String FILE_TYPE_STR = "Files";
private final String caseName;
private String deviceID;
private String dataSourceName;
private final String filePath;
private final String typeStr;
private final CorrelationAttribute.Type type;
private final String value;
private TskData.FileKnown known;
private String comment;
private AbstractFile originalAbstractFile = null;
private CorrelationAttributeInstance originalCorrelationInstance = null;
/**
* Create a node from a central repo instance.
* @param instance The central repo instance
* @param type The type of the instance
* @param value The value of the instance
*/
OtherOccurrenceNodeInstanceData(CorrelationAttributeInstance instance, CorrelationAttribute.Type type, String value) {
caseName = instance.getCorrelationCase().getDisplayName();
deviceID = instance.getCorrelationDataSource().getDeviceID();
dataSourceName = instance.getCorrelationDataSource().getName();
filePath = instance.getFilePath();
this.typeStr = type.getDisplayName();
this.type = type;
this.value = value;
known = instance.getKnownStatus();
comment = instance.getComment();
originalCorrelationInstance = instance;
}
/**
* Create a node from an abstract file.
* @param newFile The abstract file
* @param autopsyCase The current case
* @throws EamDbException
*/
OtherOccurrenceNodeInstanceData(AbstractFile newFile, Case autopsyCase) throws EamDbException {
caseName = autopsyCase.getDisplayName();
try {
DataSource dataSource = autopsyCase.getSleuthkitCase().getDataSource(newFile.getDataSource().getId());
deviceID = dataSource.getDeviceId();
dataSourceName = dataSource.getName();
} catch (TskDataException | TskCoreException ex) {
throw new EamDbException("Error loading data source for abstract file ID " + newFile.getId(), ex);
}
filePath = newFile.getParentPath() + newFile.getName();
typeStr = FILE_TYPE_STR;
this.type = null;
value = newFile.getMd5Hash();
known = newFile.getKnown();
comment = "";
originalAbstractFile = newFile;
}
/**
* Check if this node is a "file" type
* @return true if it is a file type
*/
boolean isFileType() {
return FILE_TYPE_STR.equals(typeStr);
}
/**
* Update the known status for this node
* @param newKnownStatus The new known status
*/
void updateKnown(TskData.FileKnown newKnownStatus) {
known = newKnownStatus;
}
/**
* Update the comment for this node
* @param newComment The new comment
*/
void updateComment(String newComment) {
comment = newComment;
}
/**
* Check if this is a central repo node.
* @return true if this node was created from a central repo instance, false otherwise
*/
boolean isCentralRepoNode() {
return (originalCorrelationInstance != null);
}
/**
* Uses the saved instance plus type and value to make a new CorrelationAttribute.
* Should only be called if isCentralRepoNode() is true.
* @return the newly created CorrelationAttribute
*/
CorrelationAttribute createCorrelationAttribute() throws EamDbException {
if (! isCentralRepoNode() ) {
throw new EamDbException("Can not create CorrelationAttribute for non central repo node");
}
CorrelationAttribute attr = new CorrelationAttribute(type, value);
attr.addInstance(originalCorrelationInstance);
return attr;
}
/**
* Get the case name
* @return the case name
*/
String getCaseName() {
return caseName;
}
/**
* Get the device ID
* @return the device ID
*/
String getDeviceID() {
return deviceID;
}
/**
* Get the data source name
* @return the data source name
*/
String getDataSourceName() {
return dataSourceName;
}
/**
* Get the file path
* @return the file path
*/
String getFilePath() {
return filePath;
}
/**
* Get the type (as a string)
* @return the type
*/
String getType() {
return typeStr;
}
/**
* Get the value (MD5 hash for files)
* @return the value
*/
String getValue() {
return value;
}
/**
* Get the known status
* @return the known status
*/
TskData.FileKnown getKnown() {
return known;
}
/**
* Get the comment
* @return the comment
*/
String getComment() {
return comment;
}
/**
* Get the backing abstract file.
* Should only be called if isCentralRepoNode() is false
* @return the original abstract file
*/
AbstractFile getAbstractFile() throws EamDbException {
if (originalCorrelationInstance == null) {
throw new EamDbException("AbstractFile is null");
}
return originalAbstractFile;
}
/**
* Get the backing CorrelationAttributeInstance.
* Should only be called if isCentralRepoNode() is true
* @return the original CorrelationAttributeInstance
* @throws EamDbException
*/
CorrelationAttributeInstance getCorrelationAttributeInstance() throws EamDbException {
if (originalCorrelationInstance == null) {
throw new EamDbException("CorrelationAttributeInstance is null");
}
return originalCorrelationInstance;
}
}

View File

@ -0,0 +1,34 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.centralrepository.contentviewer;
/**
* Class for populating the Other Occurrences tab with a single message.
*/
final class OtherOccurrenceNodeMessageData implements OtherOccurrenceNodeData {
private final String displayMessage;
OtherOccurrenceNodeMessageData(String displayMessage) {
this.displayMessage = displayMessage;
}
String getDisplayMessage() {
return displayMessage;
}
}

View File

@ -235,21 +235,37 @@ public class EamArtifactUtil {
return null; return null;
} }
CorrelationAttribute correlationAttribute = null; CorrelationAttribute correlationAttribute;
CorrelationAttribute.Type type;
CorrelationCase correlationCase;
CorrelationDataSource correlationDataSource;
String value;
String filePath;
try { try {
CorrelationAttribute.Type type = EamDb.getInstance().getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID); type = EamDb.getInstance().getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID);
CorrelationCase correlationCase = EamDb.getInstance().getCase(Case.getCurrentCaseThrows()); correlationCase = EamDb.getInstance().getCase(Case.getCurrentCaseThrows());
if (null == correlationCase) { if (null == correlationCase) {
correlationCase = EamDb.getInstance().newCase(Case.getCurrentCaseThrows()); correlationCase = EamDb.getInstance().newCase(Case.getCurrentCaseThrows());
} }
CorrelationDataSource correlationDataSource = CorrelationDataSource.fromTSKDataSource(correlationCase, file.getDataSource()); correlationDataSource = CorrelationDataSource.fromTSKDataSource(correlationCase, file.getDataSource());
String value = file.getMd5Hash(); value = file.getMd5Hash();
String filePath = (file.getParentPath() + file.getName()).toLowerCase(); filePath = (file.getParentPath() + file.getName()).toLowerCase();
} catch (TskCoreException | EamDbException ex) {
correlationAttribute = EamDb.getInstance().getCorrelationAttribute(type, correlationCase, correlationDataSource, value, filePath);
} catch (TskCoreException | EamDbException | NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "Error retrieving correlation attribute.", ex); logger.log(Level.SEVERE, "Error retrieving correlation attribute.", ex);
return null;
} catch (NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "Case is closed.", ex);
return null;
}
try {
correlationAttribute = EamDb.getInstance().getCorrelationAttribute(type, correlationCase, correlationDataSource, value, filePath);
} catch (EamDbException ex) {
logger.log(Level.WARNING, String.format(
"Correlation attribute could not be retrieved for '%s' (id=%d): %s",
content.getName(), content.getId(), ex.getMessage()));
return null;
} }
return correlationAttribute; return correlationAttribute;
@ -300,9 +316,12 @@ public class EamArtifactUtil {
af.getParentPath() + af.getName()); af.getParentPath() + af.getName());
eamArtifact.addInstance(cei); eamArtifact.addInstance(cei);
return eamArtifact; return eamArtifact;
} catch (TskCoreException | EamDbException | NoCurrentCaseException ex) { } catch (TskCoreException | EamDbException ex) {
logger.log(Level.SEVERE, "Error making correlation attribute.", ex); logger.log(Level.SEVERE, "Error making correlation attribute.", ex);
return null; return null;
} catch (NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "Case is closed.", ex);
return null;
} }
} }

View File

@ -77,7 +77,7 @@ public class FileInstanceNode extends FileNode {
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), NO_DESCR, this.getContent().getName())); sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), NO_DESCR, this.getContent().getName()));
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), NO_DESCR, this.getContent().getParentPath())); sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), NO_DESCR, this.getContent().getParentPath()));
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), NO_DESCR, getHashSetHitsForFile(this.getContent()))); sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), NO_DESCR, getHashSetHitsCsvList(this.getContent())));
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), NO_DESCR, this.getDataSource())); sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), NO_DESCR, this.getDataSource()));
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_mimeTypeColLbl(), Bundle.CommonFilesSearchResultsViewerTable_mimeTypeColLbl(), NO_DESCR, StringUtils.defaultString(this.getContent().getMIMEType()))); sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_mimeTypeColLbl(), Bundle.CommonFilesSearchResultsViewerTable_mimeTypeColLbl(), NO_DESCR, StringUtils.defaultString(this.getContent().getMIMEType())));

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2017-18 Basis Technology Corp. * Copyright 2017-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");

View File

@ -117,8 +117,8 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont
drp.open(); drp.open();
drpExplorerManager = drp.getExplorerManager(); drpExplorerManager = drp.getExplorerManager();
drpExplorerManager.addPropertyChangeListener(evt -> drpExplorerManager.addPropertyChangeListener(evt
viewInNewWindowButton.setEnabled(drpExplorerManager.getSelectedNodes().length == 1)); -> viewInNewWindowButton.setEnabled(drpExplorerManager.getSelectedNodes().length == 1));
} }
/** /**

View File

@ -22,23 +22,17 @@ import java.awt.BorderLayout;
import java.awt.Component; import java.awt.Component;
import java.awt.Cursor; import java.awt.Cursor;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.TreeMap;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.stream.Collectors;
import javax.swing.JComboBox; import javax.swing.JComboBox;
import javax.swing.JFileChooser; import javax.swing.JFileChooser;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
@ -48,14 +42,11 @@ import org.openide.util.NbBundle;
import org.openide.windows.WindowManager; import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.casemodule.services.FileManager;
import org.sleuthkit.autopsy.casemodule.services.Services;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.sqlitereader.SQLiteReader;
/** /**
* A file content viewer for SQLite database files. * A file content viewer for SQLite database files.
@ -70,7 +61,7 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
private final SQLiteTableView selectedTableView = new SQLiteTableView(); private final SQLiteTableView selectedTableView = new SQLiteTableView();
private AbstractFile sqliteDbFile; private AbstractFile sqliteDbFile;
private File tmpDbFile; private File tmpDbFile;
private Connection connection; private SQLiteReader sqliteReader;
private int numRows; // num of rows in the selected table private int numRows; // num of rows in the selected table
private int currPage = 0; // curr page of rows being displayed private int currPage = 0; // curr page of rows being displayed
@ -347,10 +338,10 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
numEntriesField.setText(""); numEntriesField.setText("");
// close DB connection to file // close DB connection to file
if (null != connection) { if (null != sqliteReader) {
try { try {
connection.close(); sqliteReader.close();
connection = null; sqliteReader = null;
} catch (SQLException ex) { } catch (SQLException ex) {
logger.log(Level.SEVERE, "Failed to close DB connection to file.", ex); //NON-NLS logger.log(Level.SEVERE, "Failed to close DB connection to file.", ex); //NON-NLS
} }
@ -371,40 +362,14 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
"SQLiteViewer.errorMessage.failedToinitJDBCDriver=The JDBC driver for SQLite could not be loaded.", "SQLiteViewer.errorMessage.failedToinitJDBCDriver=The JDBC driver for SQLite could not be loaded.",
"# {0} - exception message", "SQLiteViewer.errorMessage.unexpectedError=An unexpected error occurred:\n{0).",}) "# {0} - exception message", "SQLiteViewer.errorMessage.unexpectedError=An unexpected error occurred:\n{0).",})
private void processSQLiteFile() { private void processSQLiteFile() {
tablesDropdownList.removeAllItems(); tablesDropdownList.removeAllItems();
// Copy the file to temp folder
String tmpDBPathName;
try { try {
tmpDBPathName = Case.getCurrentCaseThrows().getTempDirectory() + File.separator + sqliteDbFile.getName(); String localDiskPath = Case.getCurrentCaseThrows().getTempDirectory() +
} catch (NoCurrentCaseException ex) { File.separator + sqliteDbFile.getName();
logger.log(Level.SEVERE, "Current case has been closed", ex); //NON-NLS sqliteReader = new SQLiteReader(sqliteDbFile, localDiskPath);
MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_errorMessage_noCurrentCase());
return;
}
tmpDbFile = new File(tmpDBPathName); Map<String, String> dbTablesMap = sqliteReader.getTableSchemas();
if (! tmpDbFile.exists()) {
try {
ContentUtils.writeToFile(sqliteDbFile, tmpDbFile);
// Look for any meta files associated with this DB - WAL, SHM, etc.
findAndCopySQLiteMetaFile(sqliteDbFile, sqliteDbFile.getName() + "-wal");
findAndCopySQLiteMetaFile(sqliteDbFile, sqliteDbFile.getName() + "-shm");
} catch (IOException | NoCurrentCaseException | TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Failed to create temp copy of DB file '%s' (objId=%d)", sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS
MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_errorMessage_failedToExtractFile());
return;
}
}
try {
// Load the SQLite JDBC driver, if necessary.
Class.forName("org.sqlite.JDBC"); //NON-NLS
connection = DriverManager.getConnection("jdbc:sqlite:" + tmpDBPathName); //NON-NLS
Map<String, String> dbTablesMap = getTables();
if (dbTablesMap.isEmpty()) { if (dbTablesMap.isEmpty()) {
tablesDropdownList.addItem(Bundle.SQLiteViewer_comboBox_noTableEntry()); tablesDropdownList.addItem(Bundle.SQLiteViewer_comboBox_noTableEntry());
tablesDropdownList.setEnabled(false); tablesDropdownList.setEnabled(false);
@ -413,78 +378,36 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
tablesDropdownList.addItem(tableName); tablesDropdownList.addItem(tableName);
}); });
} }
} catch (NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "Current case has been closed", ex); //NON-NLS
MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_errorMessage_noCurrentCase());
} catch (IOException | TskCoreException ex) {
logger.log(Level.SEVERE, String.format(
"Failed to create temp copy of DB file '%s' (objId=%d)", //NON-NLS
sqliteDbFile.getName(), sqliteDbFile.getId()), ex);
MessageNotifyUtil.Message.error(
Bundle.SQLiteViewer_errorMessage_failedToExtractFile());
} catch (ClassNotFoundException ex) { } catch (ClassNotFoundException ex) {
logger.log(Level.SEVERE, String.format("Failed to initialize JDBC SQLite '%s' (objId=%d)", sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS logger.log(Level.SEVERE, String.format(
MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_errorMessage_failedToinitJDBCDriver()); "Failed to initialize JDBC SQLite '%s' (objId=%d)", //NON-NLS
sqliteDbFile.getName(), sqliteDbFile.getId()), ex);
MessageNotifyUtil.Message.error(
Bundle.SQLiteViewer_errorMessage_failedToinitJDBCDriver());
} catch (SQLException ex) { } catch (SQLException ex) {
logger.log(Level.SEVERE, String.format("Failed to get tables from DB file '%s' (objId=%d)", sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS logger.log(Level.SEVERE, String.format(
MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_errorMessage_failedToQueryDatabase()); "Failed to get tables from DB file '%s' (objId=%d)", //NON-NLS
sqliteDbFile.getName(), sqliteDbFile.getId()), ex);
MessageNotifyUtil.Message.error(
Bundle.SQLiteViewer_errorMessage_failedToQueryDatabase());
} }
} }
/**
* Searches for a meta file associated with the give SQLite db If found,
* copies the file to the temp folder
*
* @param sqliteFile - SQLIte db file being processed
* @param metaFileName name of meta file to look for
*/
private void findAndCopySQLiteMetaFile(AbstractFile sqliteFile, String metaFileName) throws NoCurrentCaseException, TskCoreException, IOException {
Case openCase = Case.getCurrentCaseThrows();
SleuthkitCase sleuthkitCase = openCase.getSleuthkitCase();
Services services = new Services(sleuthkitCase);
FileManager fileManager = services.getFileManager();
List<AbstractFile> metaFiles = fileManager.findFiles(sqliteFile.getDataSource(), metaFileName, sqliteFile.getParent().getName());
if (metaFiles != null) {
for (AbstractFile metaFile : metaFiles) {
String tmpMetafilePathName = openCase.getTempDirectory() + File.separator + metaFile.getName();
File tmpMetafile = new File(tmpMetafilePathName);
ContentUtils.writeToFile(metaFile, tmpMetafile);
}
}
}
/**
* Gets the table names and schemas from the SQLite database file.
*
* @return A mapping of table names to SQL CREATE TABLE statements.
*/
private Map<String, String> getTables() throws SQLException {
Map<String, String> dbTablesMap = new TreeMap<>();
Statement statement = null;
ResultSet resultSet = null;
try {
statement = connection.createStatement();
resultSet = statement.executeQuery(
"SELECT name, sql FROM sqlite_master "
+ " WHERE type= 'table' "
+ " ORDER BY name;"); //NON-NLS
while (resultSet.next()) {
String tableName = resultSet.getString("name"); //NON-NLS
String tableSQL = resultSet.getString("sql"); //NON-NLS
dbTablesMap.put(tableName, tableSQL);
}
} finally {
if (null != resultSet) {
resultSet.close();
}
if (null != statement) {
statement.close();
}
}
return dbTablesMap;
}
@NbBundle.Messages({"# {0} - tableName", @NbBundle.Messages({"# {0} - tableName",
"SQLiteViewer.selectTable.errorText=Error getting row count for table: {0}" "SQLiteViewer.selectTable.errorText=Error getting row count for table: {0}"
}) })
private void selectTable(String tableName) { private void selectTable(String tableName) {
try {
try (Statement statement = connection.createStatement(); numRows = sqliteReader.getTableRowCount(tableName);
ResultSet resultSet = statement.executeQuery(
"SELECT count (*) as count FROM " + tableName)) { //NON-NLS{
numRows = resultSet.getInt("count");
numEntriesField.setText(numRows + " entries"); numEntriesField.setText(numRows + " entries");
currPage = 1; currPage = 1;
@ -504,8 +427,11 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
} }
} catch (SQLException ex) { } catch (SQLException ex) {
logger.log(Level.SEVERE, String.format("Failed to load table %s from DB file '%s' (objId=%d)", tableName, sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS logger.log(Level.SEVERE, String.format(
MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_selectTable_errorText(tableName)); "Failed to load table %s from DB file '%s' (objId=%d)", tableName, //NON-NLS
sqliteDbFile.getName(), sqliteDbFile.getId()), ex);
MessageNotifyUtil.Message.error(
Bundle.SQLiteViewer_selectTable_errorText(tableName));
} }
} }
@ -513,63 +439,40 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
"SQLiteViewer.readTable.errorText=Error getting rows for table: {0}"}) "SQLiteViewer.readTable.errorText=Error getting rows for table: {0}"})
private void readTable(String tableName, int startRow, int numRowsToRead) { private void readTable(String tableName, int startRow, int numRowsToRead) {
try ( try {
Statement statement = connection.createStatement(); List<Map<String, Object>> rows = sqliteReader.getRowsFromTable(
ResultSet resultSet = statement.executeQuery( tableName, startRow, numRowsToRead);
"SELECT * FROM " + tableName
+ " LIMIT " + Integer.toString(numRowsToRead)
+ " OFFSET " + Integer.toString(startRow - 1))) {
ArrayList<Map<String, Object>> rows = resultSetToArrayList(resultSet);
if (Objects.nonNull(rows)) { if (Objects.nonNull(rows)) {
selectedTableView.setupTable(rows); selectedTableView.setupTable(rows);
} else { } else {
selectedTableView.setupTable(Collections.emptyList()); selectedTableView.setupTable(Collections.emptyList());
} }
} catch (SQLException ex) { } catch (SQLException ex) {
logger.log(Level.SEVERE, String.format("Failed to read table %s from DB file '%s' (objId=%d)", tableName, sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS logger.log(Level.SEVERE, String.format(
MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_readTable_errorText(tableName)); "Failed to read table %s from DB file '%s' (objId=%d)", tableName, //NON-NLS
sqliteDbFile.getName(), sqliteDbFile.getId()), ex);
MessageNotifyUtil.Message.error(
Bundle.SQLiteViewer_readTable_errorText(tableName));
} }
} }
@NbBundle.Messages("SQLiteViewer.BlobNotShown.message=BLOB Data not shown") /**
private ArrayList<Map<String, Object>> resultSetToArrayList(ResultSet rs) throws SQLException { * Converts a sqlite table into a CSV file.
ResultSetMetaData metaData = rs.getMetaData(); *
int columns = metaData.getColumnCount(); * @param file
ArrayList<Map<String, Object>> rowlist = new ArrayList<>(); * @param tableName
while (rs.next()) { * @param rowMap -- A list of rows in the table, where each row is represented as a column-value
Map<String, Object> row = new LinkedHashMap<>(columns); * map.
for (int i = 1; i <= columns; ++i) { * @throws FileNotFoundException
if (rs.getObject(i) == null) { * @throws IOException
row.put(metaData.getColumnName(i), ""); */
} else { @NbBundle.Messages({
if (metaData.getColumnTypeName(i).compareToIgnoreCase("blob") == 0) {
row.put(metaData.getColumnName(i), Bundle.SQLiteViewer_BlobNotShown_message());
} else {
row.put(metaData.getColumnName(i), rs.getObject(i));
}
}
}
rowlist.add(row);
}
return rowlist;
}
@NbBundle.Messages({"SQLiteViewer.exportTableToCsv.write.errText=Failed to export table content to csv file.",
"SQLiteViewer.exportTableToCsv.FileName=File name: ", "SQLiteViewer.exportTableToCsv.FileName=File name: ",
"SQLiteViewer.exportTableToCsv.TableName=Table name: " "SQLiteViewer.exportTableToCsv.TableName=Table name: "
}) })
private void exportTableToCsv(File file) { public void exportTableToCSV(File file, String tableName,
String tableName = (String) this.tablesDropdownList.getSelectedItem(); List<Map<String, Object>> rowMap) throws FileNotFoundException, IOException{
try (
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("SELECT * FROM " + tableName)) {
List<Map<String, Object>> currentTableRows = resultSetToArrayList(resultSet);
if (Objects.isNull(currentTableRows) || currentTableRows.isEmpty()) {
logger.log(Level.INFO, String.format("The table %s is empty. (objId=%d)", tableName, sqliteDbFile.getId())); //NON-NLS
} else {
File csvFile; File csvFile;
String fileName = file.getName(); String fileName = file.getName();
if (FilenameUtils.getExtension(fileName).equalsIgnoreCase("csv")) { if (FilenameUtils.getExtension(fileName).equalsIgnoreCase("csv")) {
@ -582,40 +485,62 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
out.write((Bundle.SQLiteViewer_exportTableToCsv_FileName() + csvFile.getName() + "\n").getBytes()); out.write((Bundle.SQLiteViewer_exportTableToCsv_FileName() + csvFile.getName() + "\n").getBytes());
out.write((Bundle.SQLiteViewer_exportTableToCsv_TableName() + tableName + "\n").getBytes()); out.write((Bundle.SQLiteViewer_exportTableToCsv_TableName() + tableName + "\n").getBytes());
// Set up the column names
Map<String, Object> row = currentTableRows.get(0);
StringBuffer header = new StringBuffer();
for (Map.Entry<String, Object> col : row.entrySet()) {
String colName = col.getKey();
if (header.length() > 0) {
header.append(',').append(colName);
} else {
header.append(colName);
}
}
out.write(header.append('\n').toString().getBytes());
for (Map<String, Object> maps : currentTableRows) { String header = createColumnHeader(rowMap.get(0)).concat("\n");
StringBuffer valueLine = new StringBuffer(); out.write(header.getBytes());
maps.values().forEach((value) -> {
if (valueLine.length() > 0) { for (Map<String, Object> maps : rowMap) {
valueLine.append(',').append(value.toString()); String row = maps.values()
.stream()
.map(Object::toString)
.collect(Collectors.joining(","))
.concat("\n");
out.write(row.getBytes());
}
}
}
@NbBundle.Messages({
"SQLiteViewer.exportTableToCsv.write.errText=Failed to export table content to csv file.",
})
private void exportTableToCsv(File file) {
String tableName = (String) this.tablesDropdownList.getSelectedItem();
try {
List<Map<String, Object>> currentTableRows =
sqliteReader.getRowsFromTable(tableName);
if (Objects.isNull(currentTableRows) || currentTableRows.isEmpty()) {
logger.log(Level.INFO, String.format(
"The table %s is empty. (objId=%d)", tableName, //NON-NLS
sqliteDbFile.getId()));
} else { } else {
valueLine.append(value.toString()); exportTableToCSV(file, tableName, currentTableRows);
}
});
out.write(valueLine.append('\n').toString().getBytes());
}
}
} }
} catch (SQLException ex) { } catch (SQLException ex) {
logger.log(Level.SEVERE, String.format("Failed to read table %s from DB file '%s' (objId=%d)", tableName, sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS logger.log(Level.SEVERE, String.format(
MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_readTable_errorText(tableName)); "Failed to read table %s from DB file '%s' (objId=%d)", //NON-NLS
tableName, sqliteDbFile.getName(), sqliteDbFile.getId()), ex);
MessageNotifyUtil.Message.error(
Bundle.SQLiteViewer_readTable_errorText(tableName));
} catch (IOException ex) { } catch (IOException ex) {
logger.log(Level.SEVERE, String.format("Failed to export table %s to file '%s'", tableName, file.getName()), ex); //NON-NLS logger.log(Level.SEVERE, String.format(
MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_exportTableToCsv_write_errText()); "Failed to export table %s to file '%s'", tableName, file.getName()), ex); //NON-NLS
MessageNotifyUtil.Message.error(
Bundle.SQLiteViewer_exportTableToCsv_write_errText());
} }
} }
/**
* Returns a comma seperated header string from the keys of the column
* row map.
*
* @param row -- column header row map
* @return -- comma seperated header string
*/
private String createColumnHeader(Map<String, Object> row) {
return row.entrySet()
.stream()
.map(Map.Entry::getKey)
.collect(Collectors.joining(","));
}
} }

View File

@ -70,6 +70,7 @@ public final class UserPreferences {
private static final String MAX_NUM_OF_LOG_FILE = "MaximumNumberOfLogFiles"; private static final String MAX_NUM_OF_LOG_FILE = "MaximumNumberOfLogFiles";
private static final int LOG_FILE_NUM_INT = 10; private static final int LOG_FILE_NUM_INT = 10;
public static final String GROUP_ITEMS_IN_TREE_BY_DATASOURCE = "GroupItemsInTreeByDataSource"; //NON-NLS public static final String GROUP_ITEMS_IN_TREE_BY_DATASOURCE = "GroupItemsInTreeByDataSource"; //NON-NLS
public static final String SHOW_ONLY_CURRENT_USER_TAGS = "ShowOnlyCurrentUserTags";
// Prevent instantiation. // Prevent instantiation.
private UserPreferences() { private UserPreferences() {
@ -196,6 +197,27 @@ public final class UserPreferences {
preferences.putBoolean(GROUP_ITEMS_IN_TREE_BY_DATASOURCE, value); preferences.putBoolean(GROUP_ITEMS_IN_TREE_BY_DATASOURCE, value);
} }
/**
* Get the user preference which identifies whether tags should be shown for
* only the current user or all users.
*
* @return true for just the current user, false for all users
*/
public static boolean showOnlyCurrentUserTags() {
return preferences.getBoolean(SHOW_ONLY_CURRENT_USER_TAGS, false);
}
/**
* Set the user preference which identifies whether tags should be shown for
* only the current user or all users.
*
* @param value - true for just the current user, false for all users
*/
public static void setShowOnlyCurrentUserTags(boolean value) {
preferences.putBoolean(SHOW_ONLY_CURRENT_USER_TAGS, value);
}
/** /**
* Reads persisted case database connection info. * Reads persisted case database connection info.
* *
@ -379,6 +401,7 @@ public final class UserPreferences {
/** /**
* get the maximum number of log files to save * get the maximum number of log files to save
*
* @return Number of log files * @return Number of log files
*/ */
public static int getLogFileCount() { public static int getLogFileCount() {
@ -387,13 +410,16 @@ public final class UserPreferences {
/** /**
* get the default number of log files to save * get the default number of log files to save
*
* @return LOG_FILE_COUNT * @return LOG_FILE_COUNT
*/ */
public static int getDefaultLogFileCount() { public static int getDefaultLogFileCount() {
return LOG_FILE_NUM_INT; return LOG_FILE_NUM_INT;
} }
/** /**
* Set the maximum number of log files to save * Set the maximum number of log files to save
*
* @param count number of log files * @param count number of log files
*/ */
public static void setLogFileCount(int count) { public static void setLogFileCount(int count) {

View File

@ -26,7 +26,7 @@ import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import javax.swing.JTabbedPane; import javax.swing.JTabbedPane;
import javax.swing.SwingWorker; import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener; import javax.swing.event.ChangeListener;
import org.openide.explorer.ExplorerManager; import org.openide.explorer.ExplorerManager;
@ -579,29 +579,6 @@ public class DataResultPanel extends javax.swing.JPanel implements DataResult, C
} }
} }
/**
* Worker for RootNodeListener childrenAdded.
*/
class SetupTabsChildrenWorker extends SwingWorker<Void, Void> {
private final Node childNode;
SetupTabsChildrenWorker(Node aChildNode) {
childNode = aChildNode;
}
@Override
protected Void doInBackground() throws Exception {
setupTabs(childNode);
return null;
}
@Override
protected void done() {
setupTabs(childNode);
}
}
/** /**
* Responds to changes in the root node due to asynchronous child node * Responds to changes in the root node due to asynchronous child node
* creation. * creation.
@ -628,8 +605,13 @@ public class DataResultPanel extends javax.swing.JPanel implements DataResult, C
*/ */
if (waitingForData && containsReal(delta)) { if (waitingForData && containsReal(delta)) {
waitingForData = false; waitingForData = false;
Node childNode = nme.getNode(); if (SwingUtilities.isEventDispatchThread()) {
new SetupTabsChildrenWorker(childNode).execute(); setupTabs(nme.getNode());
} else {
SwingUtilities.invokeLater(() -> {
setupTabs(nme.getNode());
});
}
} }
} }

View File

@ -208,6 +208,11 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
@Override @Override
@ThreadConfined(type = ThreadConfined.ThreadType.AWT) @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
public void setNode(Node rootNode) { public void setNode(Node rootNode) {
if (! SwingUtilities.isEventDispatchThread()) {
LOGGER.log(Level.SEVERE, "Attempting to run setNode() from non-EDT thread");
return;
}
/* /*
* The quick filter must be reset because when determining column width, * The quick filter must be reset because when determining column width,
* ETable.getRowCount is called, and the documentation states that quick * ETable.getRowCount is called, and the documentation states that quick

View File

@ -169,7 +169,7 @@ public class FileUtil {
public static String escapeFileName(String fileName) { public static String escapeFileName(String fileName) {
//for now escaping /:"*?<>| (not valid in file name, at least on Windows) //for now escaping /:"*?<>| (not valid in file name, at least on Windows)
//with underscores. We are only keeping \ as it could be part of the path. //with underscores. We are only keeping \ as it could be part of the path.
return fileName.replaceAll("[/:\"*?<>|]+", "_"); return fileName.replaceAll("[\\p{Cntrl}/:\"*?<>|]+", "_");
} }
/** /**

View File

@ -21,14 +21,12 @@ package org.sleuthkit.autopsy.datamodel;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.swing.Action;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.openide.nodes.Children; import org.openide.nodes.Children;
import org.openide.nodes.Sheet; import org.openide.nodes.Sheet;
@ -38,9 +36,6 @@ import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent; import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent;
import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent; import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent;
import org.sleuthkit.autopsy.centralrepository.AddEditCentralRepoCommentAction;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamArtifactUtil;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbUtil;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import static org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode.AbstractFilePropertyType.*; import static org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode.AbstractFilePropertyType.*;
import static org.sleuthkit.autopsy.datamodel.Bundle.*; import static org.sleuthkit.autopsy.datamodel.Bundle.*;
@ -251,7 +246,7 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
map.put(TYPE_DIR.toString(), content.getDirType().getLabel()); map.put(TYPE_DIR.toString(), content.getDirType().getLabel());
map.put(TYPE_META.toString(), content.getMetaType().toString()); map.put(TYPE_META.toString(), content.getMetaType().toString());
map.put(KNOWN.toString(), content.getKnown().getName()); map.put(KNOWN.toString(), content.getKnown().getName());
map.put(HASHSETS.toString(), getHashSetHitsForFile(content)); map.put(HASHSETS.toString(), getHashSetHitsCsvList(content));
map.put(MD5HASH.toString(), StringUtils.defaultString(content.getMd5Hash())); map.put(MD5HASH.toString(), StringUtils.defaultString(content.getMd5Hash()));
map.put(ObjectID.toString(), content.getId()); map.put(ObjectID.toString(), content.getId());
map.put(MIMETYPE.toString(), StringUtils.defaultString(content.getMIMEType())); map.put(MIMETYPE.toString(), StringUtils.defaultString(content.getMIMEType()));
@ -301,7 +296,15 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
} }
} }
public static String getHashSetHitsForFile(AbstractFile file) { /**
* Gets a comma-separated values list of the names of the hash sets
* currently identified as including a given file.
*
* @param file The file.
*
* @return The CSV list of hash set names.
*/
protected static String getHashSetHitsCsvList(AbstractFile file) {
try { try {
return StringUtils.join(file.getHashSetNames(), ", "); return StringUtils.join(file.getHashSetNames(), ", ");
} catch (TskCoreException tskCoreException) { } catch (TskCoreException tskCoreException) {

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011 Basis Technology Corp. * Copyright 2011-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -29,6 +29,7 @@ import org.sleuthkit.datamodel.AbstractFile;
* Abstract class that implements the commonality between File and Directory * Abstract class that implements the commonality between File and Directory
* Nodes (same properties). * Nodes (same properties).
* *
* @param <T> extends AbstractFile
*/ */
public abstract class AbstractFsContentNode<T extends AbstractFile> extends AbstractAbstractFileNode<T> { public abstract class AbstractFsContentNode<T extends AbstractFile> extends AbstractAbstractFileNode<T> {

View File

@ -41,9 +41,9 @@ import org.sleuthkit.datamodel.TskCoreException;
* Child factory to create the top level children of the autopsy tree * Child factory to create the top level children of the autopsy tree
* *
*/ */
public class AutopsyTreeChildrenFactory extends ChildFactory.Detachable<Object> { public final class AutopsyTreeChildFactory extends ChildFactory.Detachable<Object> {
private static final Logger logger = Logger.getLogger(AutopsyTreeChildrenFactory.class.getName()); private static final Logger logger = Logger.getLogger(AutopsyTreeChildFactory.class.getName());
/** /**
* Listener for handling DATA_SOURCE_ADDED events. * Listener for handling DATA_SOURCE_ADDED events.

View File

@ -48,8 +48,6 @@ import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagAddedEvent;
import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagDeletedEvent; import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagDeletedEvent;
import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent; import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent;
import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent; import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent;
import org.sleuthkit.autopsy.centralrepository.AddEditCentralRepoCommentAction;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbUtil;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import static org.sleuthkit.autopsy.datamodel.DisplayableItemNode.findLinked; import static org.sleuthkit.autopsy.datamodel.DisplayableItemNode.findLinked;

View File

@ -27,6 +27,7 @@ import javax.swing.Action;
import org.openide.nodes.Children; import org.openide.nodes.Children;
import org.openide.nodes.Sheet; import org.openide.nodes.Sheet;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
import org.openide.util.lookup.Lookups; import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
@ -59,6 +60,7 @@ public class BlackboardArtifactTagNode extends DisplayableItemNode {
this.tag = tag; this.tag = tag;
} }
@Messages({"BlackboardArtifactTagNode.createSheet.userName.text=User Name"})
@Override @Override
protected Sheet createSheet() { protected Sheet createSheet() {
Sheet propertySheet = super.createSheet(); Sheet propertySheet = super.createSheet();
@ -80,6 +82,7 @@ public class BlackboardArtifactTagNode extends DisplayableItemNode {
Logger.getLogger(ContentTagNode.class.getName()).log(Level.SEVERE, "Failed to get path for content (id = " + tag.getContent().getId() + ")", ex); //NON-NLS Logger.getLogger(ContentTagNode.class.getName()).log(Level.SEVERE, "Failed to get path for content (id = " + tag.getContent().getId() + ")", ex); //NON-NLS
contentPath = NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.unavail.text"); contentPath = NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.unavail.text");
} }
properties.put(new NodeProperty<>( properties.put(new NodeProperty<>(
NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.srcFilePath.text"), NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.srcFilePath.text"),
NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.srcFilePath.text"), NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.srcFilePath.text"),
@ -95,7 +98,11 @@ public class BlackboardArtifactTagNode extends DisplayableItemNode {
NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.comment.text"), NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.comment.text"),
"", "",
tag.getComment())); tag.getComment()));
properties.put(new NodeProperty<>(
NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.userName.text"),
NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.userName.text"),
"",
tag.getUserName()));
return propertySheet; return propertySheet;
} }

View File

@ -59,8 +59,8 @@ class ContentTagNode extends DisplayableItemNode {
@Messages({ @Messages({
"ContentTagNode.createSheet.artifactMD5.displayName=MD5 Hash", "ContentTagNode.createSheet.artifactMD5.displayName=MD5 Hash",
"ContentTagNode.createSheet.artifactMD5.name=MD5 Hash" "ContentTagNode.createSheet.artifactMD5.name=MD5 Hash",
}) "ContentTagNode.createSheet.userName.text=User Name"})
@Override @Override
protected Sheet createSheet() { protected Sheet createSheet() {
Content content = tag.getContent(); Content content = tag.getContent();
@ -115,6 +115,11 @@ class ContentTagNode extends DisplayableItemNode {
Bundle.ContentTagNode_createSheet_artifactMD5_displayName(), Bundle.ContentTagNode_createSheet_artifactMD5_displayName(),
"", "",
file != null ? StringUtils.defaultString(file.getMd5Hash()) : "")); file != null ? StringUtils.defaultString(file.getMd5Hash()) : ""));
properties.put(new NodeProperty<>(
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.userName.text"),
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.userName.text"),
"",
tag.getUserName()));
return propertySheet; return propertySheet;
} }
@ -123,7 +128,8 @@ class ContentTagNode extends DisplayableItemNode {
List<Action> actions = new ArrayList<>(); List<Action> actions = new ArrayList<>();
actions.addAll(Arrays.asList(super.getActions(context))); actions.addAll(Arrays.asList(super.getActions(context)));
AbstractFile file = getLookup().lookup(AbstractFile.class); AbstractFile file = getLookup().lookup(AbstractFile.class
);
if (file != null) { if (file != null) {
actions.add(ViewFileInTimelineAction.createViewFileAction(file)); actions.add(ViewFileInTimelineAction.createViewFileAction(file));
} }

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2017 Basis Technology Corp. * Copyright 2011-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.datamodel; package org.sleuthkit.autopsy.datamodel;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
@ -115,9 +116,7 @@ public class LayoutFileNode extends AbstractAbstractFileNode<LayoutFile> {
@Override @Override
public Action[] getActions(boolean context) { public Action[] getActions(boolean context) {
List<Action> actionsList = new ArrayList<>(); List<Action> actionsList = new ArrayList<>();
for (Action a : super.getActions(true)) { actionsList.addAll(Arrays.asList(super.getActions(true)));
actionsList.add(a);
}
actionsList.add(new NewWindowViewAction( actionsList.add(new NewWindowViewAction(
NbBundle.getMessage(this.getClass(), "LayoutFileNode.getActions.viewInNewWin.text"), this)); NbBundle.getMessage(this.getClass(), "LayoutFileNode.getActions.viewInNewWin.text"), this));
actionsList.add(new ExternalViewerAction( actionsList.add(new ExternalViewerAction(
@ -127,9 +126,9 @@ public class LayoutFileNode extends AbstractAbstractFileNode<LayoutFile> {
actionsList.add(null); // creates a menu separator actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance()); actionsList.add(AddContentTagAction.getInstance());
final Collection<AbstractFile> selectedFilesList = final Collection<AbstractFile> selectedFilesList
new HashSet<>(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class)); = new HashSet<>(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class));
if(selectedFilesList.size() == 1) { if (selectedFilesList.size() == 1) {
actionsList.add(DeleteFileContentTagAction.getInstance()); actionsList.add(DeleteFileContentTagAction.getInstance());
} }
@ -137,7 +136,6 @@ public class LayoutFileNode extends AbstractAbstractFileNode<LayoutFile> {
return actionsList.toArray(new Action[actionsList.size()]); return actionsList.toArray(new Action[actionsList.size()]);
} }
void fillPropertyMap(Map<String, Object> map) { void fillPropertyMap(Map<String, Object> map) {
AbstractAbstractFileNode.fillPropertyMap(map, getContent()); AbstractAbstractFileNode.fillPropertyMap(map, getContent());
map.put(LayoutContentPropertyType.PARTS.toString(), content.getNumParts()); map.put(LayoutContentPropertyType.PARTS.toString(), content.getNumParts());

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2017 Basis Technology Corp. * Copyright 2011-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");

View File

@ -125,7 +125,7 @@ public class LocalFileNode extends AbstractAbstractFileNode<AbstractFile> {
logger.log(Level.WARNING, "Unable to add unzip with password action to context menus", ex); logger.log(Level.WARNING, "Unable to add unzip with password action to context menus", ex);
} }
} }
return actionsList.toArray(new Action[0]); return actionsList.toArray(new Action[actionsList.size()]);
} }
@Override @Override

View File

@ -55,7 +55,8 @@ public class Tags implements AutopsyVisitableItem {
// override of Children.Keys<T>.createNodes(). // override of Children.Keys<T>.createNodes().
private final TagResults tagResults = new TagResults(); private final TagResults tagResults = new TagResults();
private final String DISPLAY_NAME = NbBundle.getMessage(RootNode.class, "TagsNode.displayName.text"); private final static String DISPLAY_NAME = NbBundle.getMessage(RootNode.class, "TagsNode.displayName.text");
private static final String USER_NAME_PROPERTY = "user.name"; //NON-NLS
private final String ICON_PATH = "org/sleuthkit/autopsy/images/tag-folder-blue-icon-16.png"; //NON-NLS private final String ICON_PATH = "org/sleuthkit/autopsy/images/tag-folder-blue-icon-16.png"; //NON-NLS
private final long datasourceObjId; private final long datasourceObjId;
@ -68,6 +69,15 @@ public class Tags implements AutopsyVisitableItem {
this.datasourceObjId = dsObjId; this.datasourceObjId = dsObjId;
} }
/**
* Return the display name used by the tags node in the tree.
*
* @return - DISPLAY_NAME
*/
public static String getTagsDisplayName() {
return DISPLAY_NAME;
}
long filteringDataSourceObjId() { long filteringDataSourceObjId() {
return this.datasourceObjId; return this.datasourceObjId;
} }
@ -98,13 +108,11 @@ public class Tags implements AutopsyVisitableItem {
*/ */
public class RootNode extends DisplayableItemNode { public class RootNode extends DisplayableItemNode {
public RootNode(long objId) { public RootNode(long objId) {
super(Children.create(new TagNameNodeFactory(objId), true), Lookups.singleton(DISPLAY_NAME)); super(Children.create(new TagNameNodeFactory(objId), true), Lookups.singleton(DISPLAY_NAME));
super.setName(DISPLAY_NAME); super.setName(DISPLAY_NAME);
super.setDisplayName(DISPLAY_NAME); super.setDisplayName(DISPLAY_NAME);
this.setIconBaseWithExtension(ICON_PATH); this.setIconBaseWithExtension(ICON_PATH);
} }
@Override @Override
@ -134,6 +142,14 @@ public class Tags implements AutopsyVisitableItem {
public String getItemType() { public String getItemType() {
return getClass().getName(); return getClass().getName();
} }
/**
* Cause the contents of the RootNode and its children to be updated.
*/
public void refresh() {
tagResults.update();
}
} }
private class TagNameNodeFactory extends ChildFactory.Detachable<TagName> implements Observer { private class TagNameNodeFactory extends ChildFactory.Detachable<TagName> implements Observer {
@ -197,6 +213,7 @@ public class Tags implements AutopsyVisitableItem {
/** /**
* Constructor * Constructor
*
* @param objId data source object id * @param objId data source object id
*/ */
TagNameNodeFactory(long objId) { TagNameNodeFactory(long objId) {
@ -224,11 +241,17 @@ public class Tags implements AutopsyVisitableItem {
@Override @Override
protected boolean createKeys(List<TagName> keys) { protected boolean createKeys(List<TagName> keys) {
try { try {
List<TagName> tagNamesInUse;
List<TagName> tagNamesInUse = UserPreferences.groupItemsInTreeByDatasource() ? if (UserPreferences.showOnlyCurrentUserTags()) {
Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUse(datasourceObjId) : String userName = System.getProperty(USER_NAME_PROPERTY);
Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUse() tagNamesInUse = UserPreferences.groupItemsInTreeByDatasource()
; ? Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUseForUser(datasourceObjId, userName)
: Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUseForUser(userName);
} else {
tagNamesInUse = UserPreferences.groupItemsInTreeByDatasource()
? Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUse(datasourceObjId)
: Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUse();
}
Collections.sort(tagNamesInUse); Collections.sort(tagNamesInUse);
keys.addAll(tagNamesInUse); keys.addAll(tagNamesInUse);
} catch (TskCoreException | NoCurrentCaseException ex) { } catch (TskCoreException | NoCurrentCaseException ex) {
@ -276,15 +299,24 @@ public class Tags implements AutopsyVisitableItem {
long tagsCount = 0; long tagsCount = 0;
try { try {
TagsManager tm = Case.getCurrentCaseThrows().getServices().getTagsManager(); TagsManager tm = Case.getCurrentCaseThrows().getServices().getTagsManager();
if (UserPreferences.showOnlyCurrentUserTags()) {
String userName = System.getProperty(USER_NAME_PROPERTY);
if (UserPreferences.groupItemsInTreeByDatasource()) {
tagsCount = tm.getContentTagsCountByTagNameForUser(tagName, datasourceObjId, userName);
tagsCount += tm.getBlackboardArtifactTagsCountByTagNameForUser(tagName, datasourceObjId, userName);
} else {
tagsCount = tm.getContentTagsCountByTagNameForUser(tagName, userName);
tagsCount += tm.getBlackboardArtifactTagsCountByTagNameForUser(tagName, userName);
}
} else {
if (UserPreferences.groupItemsInTreeByDatasource()) { if (UserPreferences.groupItemsInTreeByDatasource()) {
tagsCount = tm.getContentTagsCountByTagName(tagName, datasourceObjId); tagsCount = tm.getContentTagsCountByTagName(tagName, datasourceObjId);
tagsCount += tm.getBlackboardArtifactTagsCountByTagName(tagName, datasourceObjId); tagsCount += tm.getBlackboardArtifactTagsCountByTagName(tagName, datasourceObjId);
} } else {
else {
tagsCount = tm.getContentTagsCountByTagName(tagName); tagsCount = tm.getContentTagsCountByTagName(tagName);
tagsCount += tm.getBlackboardArtifactTagsCountByTagName(tagName); tagsCount += tm.getBlackboardArtifactTagsCountByTagName(tagName);
} }
}
} catch (TskCoreException | NoCurrentCaseException ex) { } catch (TskCoreException | NoCurrentCaseException ex) {
Logger.getLogger(TagNameNode.class.getName()).log(Level.SEVERE, "Failed to get tags count for " + tagName.getDisplayName() + " tag name", ex); //NON-NLS Logger.getLogger(TagNameNode.class.getName()).log(Level.SEVERE, "Failed to get tags count for " + tagName.getDisplayName() + " tag name", ex); //NON-NLS
} }
@ -387,9 +419,17 @@ public class Tags implements AutopsyVisitableItem {
private void updateDisplayName() { private void updateDisplayName() {
long tagsCount = 0; long tagsCount = 0;
try { try {
tagsCount = UserPreferences.groupItemsInTreeByDatasource() ?
Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsCountByTagName(tagName, datasourceObjId) : if (UserPreferences.showOnlyCurrentUserTags()) {
Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsCountByTagName(tagName); String userName = System.getProperty(USER_NAME_PROPERTY);
tagsCount = UserPreferences.groupItemsInTreeByDatasource()
? Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsCountByTagNameForUser(tagName, datasourceObjId, userName)
: Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsCountByTagNameForUser(tagName, userName);
} else {
tagsCount = UserPreferences.groupItemsInTreeByDatasource()
? Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsCountByTagName(tagName, datasourceObjId)
: Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsCountByTagName(tagName);
}
} catch (TskCoreException | NoCurrentCaseException ex) { } catch (TskCoreException | NoCurrentCaseException ex) {
Logger.getLogger(ContentTagTypeNode.class.getName()).log(Level.SEVERE, "Failed to get content tags count for " + tagName.getDisplayName() + " tag name", ex); //NON-NLS Logger.getLogger(ContentTagTypeNode.class.getName()).log(Level.SEVERE, "Failed to get content tags count for " + tagName.getDisplayName() + " tag name", ex); //NON-NLS
} }
@ -444,11 +484,19 @@ public class Tags implements AutopsyVisitableItem {
protected boolean createKeys(List<ContentTag> keys) { protected boolean createKeys(List<ContentTag> keys) {
// Use the content tags bearing the specified tag name as the keys. // Use the content tags bearing the specified tag name as the keys.
try { try {
List<ContentTag> contentTags = UserPreferences.groupItemsInTreeByDatasource() ? List<ContentTag> contentTags = UserPreferences.groupItemsInTreeByDatasource()
Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByTagName(tagName, datasourceObjId) : ? Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByTagName(tagName, datasourceObjId)
Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByTagName(tagName); : Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByTagName(tagName);
if (UserPreferences.showOnlyCurrentUserTags()) {
String userName = System.getProperty(USER_NAME_PROPERTY);
for (ContentTag tag : contentTags) {
if (userName.equals(tag.getUserName())) {
keys.add(tag);
}
}
} else {
keys.addAll(contentTags); keys.addAll(contentTags);
}
} catch (TskCoreException | NoCurrentCaseException ex) { } catch (TskCoreException | NoCurrentCaseException ex) {
Logger.getLogger(ContentTagNodeFactory.class.getName()).log(Level.SEVERE, "Failed to get tag names", ex); //NON-NLS Logger.getLogger(ContentTagNodeFactory.class.getName()).log(Level.SEVERE, "Failed to get tag names", ex); //NON-NLS
} }
@ -492,9 +540,16 @@ public class Tags implements AutopsyVisitableItem {
private void updateDisplayName() { private void updateDisplayName() {
long tagsCount = 0; long tagsCount = 0;
try { try {
tagsCount = UserPreferences.groupItemsInTreeByDatasource() ? if (UserPreferences.showOnlyCurrentUserTags()) {
Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagName(tagName, datasourceObjId) : String userName = System.getProperty(USER_NAME_PROPERTY);
Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagName(tagName); tagsCount = UserPreferences.groupItemsInTreeByDatasource()
? Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagNameForUser(tagName, datasourceObjId, userName)
: Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagNameForUser(tagName, userName);
} else {
tagsCount = UserPreferences.groupItemsInTreeByDatasource()
? Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagName(tagName, datasourceObjId)
: Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagName(tagName);
}
} catch (TskCoreException | NoCurrentCaseException ex) { } catch (TskCoreException | NoCurrentCaseException ex) {
Logger.getLogger(BlackboardArtifactTagTypeNode.class.getName()).log(Level.SEVERE, "Failed to get blackboard artifact tags count for " + tagName.getDisplayName() + " tag name", ex); //NON-NLS Logger.getLogger(BlackboardArtifactTagTypeNode.class.getName()).log(Level.SEVERE, "Failed to get blackboard artifact tags count for " + tagName.getDisplayName() + " tag name", ex); //NON-NLS
} }
@ -549,10 +604,19 @@ public class Tags implements AutopsyVisitableItem {
protected boolean createKeys(List<BlackboardArtifactTag> keys) { protected boolean createKeys(List<BlackboardArtifactTag> keys) {
try { try {
// Use the blackboard artifact tags bearing the specified tag name as the keys. // Use the blackboard artifact tags bearing the specified tag name as the keys.
List<BlackboardArtifactTag> artifactTags = UserPreferences.groupItemsInTreeByDatasource() ? List<BlackboardArtifactTag> artifactTags = UserPreferences.groupItemsInTreeByDatasource()
Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsByTagName(tagName, datasourceObjId) : ? Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsByTagName(tagName, datasourceObjId)
Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsByTagName(tagName); : Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsByTagName(tagName);
if (UserPreferences.showOnlyCurrentUserTags()) {
String userName = System.getProperty(USER_NAME_PROPERTY);
for (BlackboardArtifactTag tag : artifactTags) {
if (userName.equals(tag.getUserName())) {
keys.add(tag);
}
}
} else {
keys.addAll(artifactTags); keys.addAll(artifactTags);
}
} catch (TskCoreException | NoCurrentCaseException ex) { } catch (TskCoreException | NoCurrentCaseException ex) {
Logger.getLogger(BlackboardArtifactTagNodeFactory.class.getName()).log(Level.SEVERE, "Failed to get tag names", ex); //NON-NLS Logger.getLogger(BlackboardArtifactTagNodeFactory.class.getName()).log(Level.SEVERE, "Failed to get tag names", ex); //NON-NLS
} }

View File

@ -50,8 +50,6 @@ public class VirtualDirectoryNode extends SpecialDirectoryNode {
this.setDisplayName(nameForVirtualDirectory(ld)); this.setDisplayName(nameForVirtualDirectory(ld));
String name = ld.getName();
//set icon for name, special case for logical file set //set icon for name, special case for logical file set
if (ld.isDataSource()) { if (ld.isDataSource()) {
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/fileset-icon-16.png"); //NON-NLS this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/fileset-icon-16.png"); //NON-NLS

View File

@ -125,3 +125,4 @@ GroupDataSourcesDialog.queryLabel.text=Would you like to group by data source fo
GroupDataSourcesDialog.yesButton.text=Yes GroupDataSourcesDialog.yesButton.text=Yes
GroupDataSourcesDialog.noButton.text=No GroupDataSourcesDialog.noButton.text=No
GroupDataSourcesDialog.title=Group by Data Source? GroupDataSourcesDialog.title=Group by Data Source?
DirectoryTreeTopComponent.showOnlyCurrentUserTagsCheckbox.text=Hide Other User's Tags

View File

@ -21,7 +21,9 @@
<Component id="backButton" min="-2" max="-2" attributes="0"/> <Component id="backButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="forwardButton" min="-2" max="-2" attributes="0"/> <Component id="forwardButton" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="51" max="32767" attributes="0"/> <EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="showOnlyCurrentUserTagsCheckbox" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Component id="showRejectedCheckBox" min="-2" max="-2" attributes="0"/> <Component id="showRejectedCheckBox" min="-2" max="-2" attributes="0"/>
<Component id="groupByDatasourceCheckBox" min="-2" max="-2" attributes="0"/> <Component id="groupByDatasourceCheckBox" min="-2" max="-2" attributes="0"/>
@ -36,7 +38,10 @@
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0"> <Group type="102" attributes="0">
<EmptySpace min="-2" pref="5" max="-2" attributes="0"/> <EmptySpace min="-2" pref="5" max="-2" attributes="0"/>
<Component id="showRejectedCheckBox" min="-2" max="-2" attributes="0"/> <Group type="103" groupAlignment="3" attributes="0">
<Component id="showRejectedCheckBox" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="showOnlyCurrentUserTagsCheckbox" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="groupByDatasourceCheckBox" min="-2" max="-2" attributes="0"/> <Component id="groupByDatasourceCheckBox" min="-2" max="-2" attributes="0"/>
</Group> </Group>
@ -151,5 +156,15 @@
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="groupByDatasourceCheckBoxActionPerformed"/> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="groupByDatasourceCheckBoxActionPerformed"/>
</Events> </Events>
</Component> </Component>
<Component class="javax.swing.JCheckBox" name="showOnlyCurrentUserTagsCheckbox">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/directorytree/Bundle.properties" key="DirectoryTreeTopComponent.showOnlyCurrentUserTagsCheckbox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="showOnlyCurrentUserTagsCheckboxActionPerformed"/>
</Events>
</Component>
</SubComponents> </SubComponents>
</Form> </Form>

View File

@ -80,7 +80,8 @@ import org.sleuthkit.autopsy.datamodel.FileTypesByMimeType;
import org.sleuthkit.autopsy.datamodel.InterestingHits; import org.sleuthkit.autopsy.datamodel.InterestingHits;
import org.sleuthkit.autopsy.datamodel.KeywordHits; import org.sleuthkit.autopsy.datamodel.KeywordHits;
import org.sleuthkit.autopsy.datamodel.ResultsNode; import org.sleuthkit.autopsy.datamodel.ResultsNode;
import org.sleuthkit.autopsy.datamodel.AutopsyTreeChildrenFactory; import org.sleuthkit.autopsy.datamodel.AutopsyTreeChildFactory;
import org.sleuthkit.autopsy.datamodel.Tags;
import org.sleuthkit.autopsy.datamodel.ViewsNode; import org.sleuthkit.autopsy.datamodel.ViewsNode;
import org.sleuthkit.autopsy.datamodel.accounts.Accounts; import org.sleuthkit.autopsy.datamodel.accounts.Accounts;
import org.sleuthkit.autopsy.datamodel.accounts.BINRange; import org.sleuthkit.autopsy.datamodel.accounts.BINRange;
@ -108,7 +109,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
private final LinkedList<String[]> forwardList; private final LinkedList<String[]> forwardList;
private static final String PREFERRED_ID = "DirectoryTreeTopComponent"; //NON-NLS private static final String PREFERRED_ID = "DirectoryTreeTopComponent"; //NON-NLS
private static final Logger LOGGER = Logger.getLogger(DirectoryTreeTopComponent.class.getName()); private static final Logger LOGGER = Logger.getLogger(DirectoryTreeTopComponent.class.getName());
private AutopsyTreeChildrenFactory autopsyTreeChildrenFactory; private AutopsyTreeChildFactory autopsyTreeChildFactory;
private Children autopsyTreeChildren; private Children autopsyTreeChildren;
private static final long DEFAULT_DATASOURCE_GROUPING_THRESHOLD = 5; // Threshold for prompting the user about grouping by data source private static final long DEFAULT_DATASOURCE_GROUPING_THRESHOLD = 5; // Threshold for prompting the user about grouping by data source
private static final String GROUPING_THRESHOLD_NAME = "GroupDataSourceThreshold"; private static final String GROUPING_THRESHOLD_NAME = "GroupDataSourceThreshold";
@ -137,6 +138,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
forwardButton.setEnabled(false); forwardButton.setEnabled(false);
groupByDatasourceCheckBox.setSelected(UserPreferences.groupItemsInTreeByDatasource()); groupByDatasourceCheckBox.setSelected(UserPreferences.groupItemsInTreeByDatasource());
showOnlyCurrentUserTagsCheckbox.setSelected(UserPreferences.showOnlyCurrentUserTags());
} }
/** /**
@ -152,6 +154,9 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
case UserPreferences.GROUP_ITEMS_IN_TREE_BY_DATASOURCE: case UserPreferences.GROUP_ITEMS_IN_TREE_BY_DATASOURCE:
refreshContentTreeSafe(); refreshContentTreeSafe();
break; break;
case UserPreferences.SHOW_ONLY_CURRENT_USER_TAGS:
refreshTagsTree();
break;
case UserPreferences.HIDE_KNOWN_FILES_IN_VIEWS_TREE: case UserPreferences.HIDE_KNOWN_FILES_IN_VIEWS_TREE:
case UserPreferences.HIDE_SLACK_FILES_IN_VIEWS_TREE: case UserPreferences.HIDE_SLACK_FILES_IN_VIEWS_TREE:
// TODO: Need a way to refresh the Views subtree // TODO: Need a way to refresh the Views subtree
@ -191,6 +196,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
forwardButton = new javax.swing.JButton(); forwardButton = new javax.swing.JButton();
showRejectedCheckBox = new javax.swing.JCheckBox(); showRejectedCheckBox = new javax.swing.JCheckBox();
groupByDatasourceCheckBox = new javax.swing.JCheckBox(); groupByDatasourceCheckBox = new javax.swing.JCheckBox();
showOnlyCurrentUserTagsCheckbox = new javax.swing.JCheckBox();
treeView.setBorder(null); treeView.setBorder(null);
@ -235,6 +241,13 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
} }
}); });
org.openide.awt.Mnemonics.setLocalizedText(showOnlyCurrentUserTagsCheckbox, org.openide.util.NbBundle.getMessage(DirectoryTreeTopComponent.class, "DirectoryTreeTopComponent.showOnlyCurrentUserTagsCheckbox.text")); // NOI18N
showOnlyCurrentUserTagsCheckbox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
showOnlyCurrentUserTagsCheckboxActionPerformed(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout); this.setLayout(layout);
layout.setHorizontalGroup( layout.setHorizontalGroup(
@ -244,7 +257,9 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
.addComponent(backButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(backButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(forwardButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(forwardButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 51, Short.MAX_VALUE) .addGap(18, 18, 18)
.addComponent(showOnlyCurrentUserTagsCheckbox, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(showRejectedCheckBox) .addComponent(showRejectedCheckBox)
.addComponent(groupByDatasourceCheckBox)) .addComponent(groupByDatasourceCheckBox))
@ -256,7 +271,9 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addGap(5, 5, 5) .addGap(5, 5, 5)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(showRejectedCheckBox) .addComponent(showRejectedCheckBox)
.addComponent(showOnlyCurrentUserTagsCheckbox))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(groupByDatasourceCheckBox)) .addComponent(groupByDatasourceCheckBox))
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
@ -323,10 +340,15 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
UserPreferences.setGroupItemsInTreeByDatasource(this.groupByDatasourceCheckBox.isSelected()); UserPreferences.setGroupItemsInTreeByDatasource(this.groupByDatasourceCheckBox.isSelected());
}//GEN-LAST:event_groupByDatasourceCheckBoxActionPerformed }//GEN-LAST:event_groupByDatasourceCheckBoxActionPerformed
private void showOnlyCurrentUserTagsCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_showOnlyCurrentUserTagsCheckboxActionPerformed
UserPreferences.setShowOnlyCurrentUserTags(this.showOnlyCurrentUserTagsCheckbox.isSelected());
}//GEN-LAST:event_showOnlyCurrentUserTagsCheckboxActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton backButton; private javax.swing.JButton backButton;
private javax.swing.JButton forwardButton; private javax.swing.JButton forwardButton;
private javax.swing.JCheckBox groupByDatasourceCheckBox; private javax.swing.JCheckBox groupByDatasourceCheckBox;
private javax.swing.JCheckBox showOnlyCurrentUserTagsCheckbox;
private javax.swing.JCheckBox showRejectedCheckBox; private javax.swing.JCheckBox showRejectedCheckBox;
private javax.swing.JScrollPane treeView; private javax.swing.JScrollPane treeView;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
@ -476,8 +498,8 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
} }
// if there's at least one image, load the image and open the top componen // if there's at least one image, load the image and open the top componen
autopsyTreeChildrenFactory = new AutopsyTreeChildrenFactory(); autopsyTreeChildFactory = new AutopsyTreeChildFactory();
autopsyTreeChildren = Children.create(autopsyTreeChildrenFactory, true); autopsyTreeChildren = Children.create(autopsyTreeChildFactory, true);
Node root = new AbstractNode(autopsyTreeChildren) { Node root = new AbstractNode(autopsyTreeChildren) {
//JIRA-2807: What is the point of these overrides? //JIRA-2807: What is the point of these overrides?
/** /**
@ -735,9 +757,11 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
* responsible for opening core windows. Consider moving * responsible for opening core windows. Consider moving
* this elsewhere. * this elsewhere.
*/ */
if (!this.isOpened()) { SwingUtilities.invokeLater(() -> {
SwingUtilities.invokeLater(CoreComponentControl::openCoreWindows); if (! DirectoryTreeTopComponent.this.isOpened()) {
CoreComponentControl.openCoreWindows();
} }
});
} catch (NoCurrentCaseException notUsed) { } catch (NoCurrentCaseException notUsed) {
/** /**
* Case is closed, do nothing. * Case is closed, do nothing.
@ -888,6 +912,29 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
SwingUtilities.invokeLater(this::rebuildTree); SwingUtilities.invokeLater(this::rebuildTree);
} }
/**
* Refresh only the tags subtree(s) of the tree view.
*/
private void refreshTagsTree() {
SwingUtilities.invokeLater(() -> {
// if no open case or has no data then there is no tree to rebuild
if (UserPreferences.groupItemsInTreeByDatasource()) {
for (Node dataSource : autopsyTreeChildren.getNodes()) {
Node tagsNode = dataSource.getChildren().findChild(Tags.getTagsDisplayName());
if (tagsNode != null) {
//Reports is at the same level as the data sources so we want to ignore it
((Tags.RootNode)tagsNode).refresh();
}
}
} else {
Node tagsNode = autopsyTreeChildren.findChild(Tags.getTagsDisplayName());
if (tagsNode != null) {
((Tags.RootNode)tagsNode).refresh();
}
}
});
}
/** /**
* Rebuilds the autopsy tree. * Rebuilds the autopsy tree.
* *
@ -907,7 +954,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
} }
// refresh all children of the root. // refresh all children of the root.
autopsyTreeChildrenFactory.refreshChildren(); autopsyTreeChildFactory.refreshChildren();
// Select the first node and reset the selection history // Select the first node and reset the selection history
// This should happen on the EDT once the tree has been rebuilt. // This should happen on the EDT once the tree has been rebuilt.

View File

@ -25,7 +25,9 @@ import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.AbstractAction; import javax.swing.AbstractAction;
import javax.swing.JFileChooser; import javax.swing.JFileChooser;
@ -33,7 +35,6 @@ import javax.swing.JOptionPane;
import javax.swing.SwingWorker; import javax.swing.SwingWorker;
import org.netbeans.api.progress.ProgressHandle; import org.netbeans.api.progress.ProgressHandle;
import org.openide.util.Cancellable; import org.openide.util.Cancellable;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.Utilities; import org.openide.util.Utilities;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
@ -44,8 +45,6 @@ import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.datamodel.ContentUtils; import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.autopsy.datamodel.ContentUtils.ExtractFscContentVisitor; import org.sleuthkit.autopsy.datamodel.ContentUtils.ExtractFscContentVisitor;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskCoreException;
/** /**
* Extracts AbstractFiles to a location selected by the user. * Extracts AbstractFiles to a location selected by the user.
@ -54,6 +53,8 @@ public final class ExtractAction extends AbstractAction {
private Logger logger = Logger.getLogger(ExtractAction.class.getName()); private Logger logger = Logger.getLogger(ExtractAction.class.getName());
private String userDefinedExportPath;
// This class is a singleton to support multi-selection of nodes, since // This class is a singleton to support multi-selection of nodes, since
// org.openide.nodes.NodeOp.findActions(Node[] nodes) will only pick up an Action if every // org.openide.nodes.NodeOp.findActions(Node[] nodes) will only pick up an Action if every
// node in the array returns a reference to the same action object from Node.getActions(boolean). // node in the array returns a reference to the same action object from Node.getActions(boolean).
@ -66,6 +67,9 @@ public final class ExtractAction extends AbstractAction {
return instance; return instance;
} }
/**
* Private constructor for the action.
*/
private ExtractAction() { private ExtractAction() {
super(NbBundle.getMessage(ExtractAction.class, "ExtractAction.title.extractFiles.text")); super(NbBundle.getMessage(ExtractAction.class, "ExtractAction.title.extractFiles.text"));
} }
@ -94,64 +98,70 @@ public final class ExtractAction extends AbstractAction {
/** /**
* Called when user has selected a single file to extract * Called when user has selected a single file to extract
* *
* @param e * @param event
* @param selectedFile Selected file * @param selectedFile Selected file
*/ */
@NbBundle.Messages ({"ExtractAction.noOpenCase.errMsg=No open case available."}) @NbBundle.Messages({"ExtractAction.noOpenCase.errMsg=No open case available."})
private void extractFile(ActionEvent e, AbstractFile selectedFile) { private void extractFile(ActionEvent event, AbstractFile selectedFile) {
Case openCase; Case openCase;
try { try {
openCase = Case.getCurrentCaseThrows(); openCase = Case.getCurrentCaseThrows();
} catch (NoCurrentCaseException ex) { } catch (NoCurrentCaseException ex) {
JOptionPane.showMessageDialog((Component) e.getSource(), Bundle.ExtractAction_noOpenCase_errMsg()); JOptionPane.showMessageDialog((Component) event.getSource(), Bundle.ExtractAction_noOpenCase_errMsg());
logger.log(Level.INFO, "Exception while getting open case.", ex); //NON-NLS logger.log(Level.INFO, "Exception while getting open case.", ex); //NON-NLS
return; return;
} }
JFileChooser fileChooser = new JFileChooser(); JFileChooser fileChooser = new JFileChooser();
fileChooser.setCurrentDirectory(new File(openCase.getExportDirectory())); fileChooser.setCurrentDirectory(new File(getExportDirectory(openCase)));
// If there is an attribute name, change the ":". Otherwise the extracted file will be hidden // If there is an attribute name, change the ":". Otherwise the extracted file will be hidden
fileChooser.setSelectedFile(new File(FileUtil.escapeFileName(selectedFile.getName()))); fileChooser.setSelectedFile(new File(FileUtil.escapeFileName(selectedFile.getName())));
if (fileChooser.showSaveDialog((Component) e.getSource()) == JFileChooser.APPROVE_OPTION) { if (fileChooser.showSaveDialog((Component) event.getSource()) == JFileChooser.APPROVE_OPTION) {
updateExportDirectory(fileChooser.getSelectedFile().getParent(), openCase);
ArrayList<FileExtractionTask> fileExtractionTasks = new ArrayList<>(); ArrayList<FileExtractionTask> fileExtractionTasks = new ArrayList<>();
fileExtractionTasks.add(new FileExtractionTask(selectedFile, fileChooser.getSelectedFile())); fileExtractionTasks.add(new FileExtractionTask(selectedFile, fileChooser.getSelectedFile()));
runExtractionTasks(e, fileExtractionTasks); runExtractionTasks(event, fileExtractionTasks);
} }
} }
/** /**
* Called when a user has selected multiple files to extract * Called when a user has selected multiple files to extract
* *
* @param e * @param event
* @param selectedFiles Selected files * @param selectedFiles Selected files
*/ */
private void extractFiles(ActionEvent e, Collection<? extends AbstractFile> selectedFiles) { private void extractFiles(ActionEvent event, Collection<? extends AbstractFile> selectedFiles) {
Case openCase; Case openCase;
try { try {
openCase = Case.getCurrentCaseThrows(); openCase = Case.getCurrentCaseThrows();
} catch (NoCurrentCaseException ex) { } catch (NoCurrentCaseException ex) {
JOptionPane.showMessageDialog((Component) e.getSource(), Bundle.ExtractAction_noOpenCase_errMsg()); JOptionPane.showMessageDialog((Component) event.getSource(), Bundle.ExtractAction_noOpenCase_errMsg());
logger.log(Level.INFO, "Exception while getting open case.", ex); //NON-NLS logger.log(Level.INFO, "Exception while getting open case.", ex); //NON-NLS
return; return;
} }
JFileChooser folderChooser = new JFileChooser(); JFileChooser folderChooser = new JFileChooser();
folderChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); folderChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
folderChooser.setCurrentDirectory(new File(openCase.getExportDirectory())); folderChooser.setCurrentDirectory(new File(getExportDirectory(openCase)));
if (folderChooser.showSaveDialog((Component) e.getSource()) == JFileChooser.APPROVE_OPTION) { if (folderChooser.showSaveDialog((Component) event.getSource()) == JFileChooser.APPROVE_OPTION) {
File destinationFolder = folderChooser.getSelectedFile(); File destinationFolder = folderChooser.getSelectedFile();
if (!destinationFolder.exists()) { if (!destinationFolder.exists()) {
try { try {
destinationFolder.mkdirs(); destinationFolder.mkdirs();
} catch (Exception ex) { } catch (Exception ex) {
JOptionPane.showMessageDialog((Component) e.getSource(), NbBundle.getMessage(this.getClass(), JOptionPane.showMessageDialog((Component) event.getSource(), NbBundle.getMessage(this.getClass(),
"ExtractAction.extractFiles.cantCreateFolderErr.msg")); "ExtractAction.extractFiles.cantCreateFolderErr.msg"));
logger.log(Level.INFO, "Unable to create folder(s) for user " + destinationFolder.getAbsolutePath(), ex); //NON-NLS logger.log(Level.INFO, "Unable to create folder(s) for user " + destinationFolder.getAbsolutePath(), ex); //NON-NLS
return; return;
} }
} }
updateExportDirectory(destinationFolder.getPath(), openCase);
/* get the unique set of files from the list. A user once reported extraction taking /*
* days because it was extracting the same PST file 20k times. They selected 20k * get the unique set of files from the list. A user once reported
* email messages in the tree and chose to extract them. */ * extraction taking days because it was extracting the same PST
* file 20k times. They selected 20k email messages in the tree and
* chose to extract them.
*/
Set<AbstractFile> uniqueFiles = new HashSet<>(selectedFiles); Set<AbstractFile> uniqueFiles = new HashSet<>(selectedFiles);
// make a task for each file // make a task for each file
@ -160,11 +170,57 @@ public final class ExtractAction extends AbstractAction {
// If there is an attribute name, change the ":". Otherwise the extracted file will be hidden // If there is an attribute name, change the ":". Otherwise the extracted file will be hidden
fileExtractionTasks.add(new FileExtractionTask(source, new File(destinationFolder, source.getId() + "-" + FileUtil.escapeFileName(source.getName())))); fileExtractionTasks.add(new FileExtractionTask(source, new File(destinationFolder, source.getId() + "-" + FileUtil.escapeFileName(source.getName()))));
} }
runExtractionTasks(e, fileExtractionTasks); runExtractionTasks(event, fileExtractionTasks);
} }
} }
private void runExtractionTasks(ActionEvent e, ArrayList<FileExtractionTask> fileExtractionTasks) { /**
* Get the export directory path.
*
* @param openCase The current case.
*
* @return The export directory path.
*/
private String getExportDirectory(Case openCase) {
String caseExportPath = openCase.getExportDirectory();
if (userDefinedExportPath == null) {
return caseExportPath;
}
File file = new File(userDefinedExportPath);
if (file.exists() == false || file.isDirectory() == false) {
return caseExportPath;
}
return userDefinedExportPath;
}
/**
* Update the default export directory. If the directory path matches the
* case export directory, then the directory used will always match the
* export directory of any given case. Otherwise, the path last used will be
* saved.
*
* @param exportPath The export path.
* @param openCase The current case.
*/
private void updateExportDirectory(String exportPath, Case openCase) {
if (exportPath.equalsIgnoreCase(openCase.getExportDirectory())) {
userDefinedExportPath = null;
} else {
userDefinedExportPath = exportPath;
}
}
/**
* Execute a series of file extraction tasks.
*
* @param event ActionEvent whose source will be used for
* centering popup dialogs.
* @param fileExtractionTasks List of file extraction tasks.
*/
private void runExtractionTasks(ActionEvent event, List<FileExtractionTask> fileExtractionTasks) {
// verify all of the sources and destinations are OK // verify all of the sources and destinations are OK
for (Iterator<FileExtractionTask> it = fileExtractionTasks.iterator(); it.hasNext();) { for (Iterator<FileExtractionTask> it = fileExtractionTasks.iterator(); it.hasNext();) {
@ -177,16 +233,16 @@ public final class ExtractAction extends AbstractAction {
} }
/* /*
* This code assumes that each destination is unique. We previously satisfied * This code assumes that each destination is unique. We previously
* that by adding the unique ID. * satisfied that by adding the unique ID.
*/ */
if (task.destination.exists()) { if (task.destination.exists()) {
if (JOptionPane.showConfirmDialog((Component) e.getSource(), if (JOptionPane.showConfirmDialog((Component) event.getSource(),
NbBundle.getMessage(this.getClass(), "ExtractAction.confDlg.destFileExist.msg", task.destination.getAbsolutePath()), NbBundle.getMessage(this.getClass(), "ExtractAction.confDlg.destFileExist.msg", task.destination.getAbsolutePath()),
NbBundle.getMessage(this.getClass(), "ExtractAction.confDlg.destFileExist.title"), NbBundle.getMessage(this.getClass(), "ExtractAction.confDlg.destFileExist.title"),
JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
if (!FileUtil.deleteFileDir(task.destination)) { if (!FileUtil.deleteFileDir(task.destination)) {
JOptionPane.showMessageDialog((Component) e.getSource(), JOptionPane.showMessageDialog((Component) event.getSource(),
NbBundle.getMessage(this.getClass(), "ExtractAction.msgDlg.cantOverwriteFile.msg", task.destination.getAbsolutePath())); NbBundle.getMessage(this.getClass(), "ExtractAction.msgDlg.cantOverwriteFile.msg", task.destination.getAbsolutePath()));
it.remove(); it.remove();
} }
@ -210,11 +266,20 @@ public final class ExtractAction extends AbstractAction {
} }
} }
/**
* Stores source and destination for file extraction.
*/
private class FileExtractionTask { private class FileExtractionTask {
AbstractFile source; AbstractFile source;
File destination; File destination;
/**
* Create an instance of the FileExtractionTask.
*
* @param source The file to be extracted.
* @param destination The destination for the extraction.
*/
FileExtractionTask(AbstractFile source, File destination) { FileExtractionTask(AbstractFile source, File destination) {
this.source = source; this.source = source;
this.destination = destination; this.destination = destination;
@ -226,11 +291,16 @@ public final class ExtractAction extends AbstractAction {
*/ */
private class FileExtracter extends SwingWorker<Object, Void> { private class FileExtracter extends SwingWorker<Object, Void> {
private Logger logger = Logger.getLogger(FileExtracter.class.getName()); private final Logger logger = Logger.getLogger(FileExtracter.class.getName());
private ProgressHandle progress; private ProgressHandle progress;
private ArrayList<FileExtractionTask> extractionTasks; private final List<FileExtractionTask> extractionTasks;
FileExtracter(ArrayList<FileExtractionTask> extractionTasks) { /**
* Create an instance of the FileExtracter.
*
* @param extractionTasks List of file extraction tasks.
*/
FileExtracter(List<FileExtractionTask> extractionTasks) {
this.extractionTasks = extractionTasks; this.extractionTasks = extractionTasks;
} }
@ -275,7 +345,7 @@ public final class ExtractAction extends AbstractAction {
boolean msgDisplayed = false; boolean msgDisplayed = false;
try { try {
super.get(); super.get();
} catch (Exception ex) { } catch (InterruptedException | ExecutionException ex) {
logger.log(Level.SEVERE, "Fatal error during file extraction", ex); //NON-NLS logger.log(Level.SEVERE, "Fatal error during file extraction", ex); //NON-NLS
MessageNotifyUtil.Message.info( MessageNotifyUtil.Message.info(
NbBundle.getMessage(this.getClass(), "ExtractAction.done.notifyMsg.extractErr", ex.getMessage())); NbBundle.getMessage(this.getClass(), "ExtractAction.done.notifyMsg.extractErr", ex.getMessage()));
@ -289,22 +359,23 @@ public final class ExtractAction extends AbstractAction {
} }
} }
private int calculateProgressBarWorkUnits(AbstractFile file) { /**
int workUnits = 0; * Calculate the number of work units for the progress bar.
if (file.isFile()) { *
workUnits += file.getSize(); * @param file File whose children will be reviewed to get the number of
} else { * work units.
try { *
for (Content child : file.getChildren()) { * @return The number of work units.
if (child instanceof AbstractFile) { */
workUnits += calculateProgressBarWorkUnits((AbstractFile) child); /*
} * private int calculateProgressBarWorkUnits(AbstractFile file) { int
} * workUnits = 0; if (file.isFile()) { workUnits += file.getSize(); }
} catch (TskCoreException ex) { * else { try { for (Content child : file.getChildren()) { if (child
logger.log(Level.SEVERE, "Could not get children of content", ex); //NON-NLS * instanceof AbstractFile) { workUnits +=
} * calculateProgressBarWorkUnits((AbstractFile) child); } } } catch
} * (TskCoreException ex) { logger.log(Level.SEVERE, "Could not get
return workUnits; * children of content", ex); //NON-NLS } } return workUnits;
} }
*/
} }
} }

View File

@ -61,15 +61,23 @@ import org.sleuthkit.datamodel.VolumeSystem;
* Extracts all the unallocated space as a single file * Extracts all the unallocated space as a single file
*/ */
final class ExtractUnallocAction extends AbstractAction { final class ExtractUnallocAction extends AbstractAction {
private static final Logger logger = Logger.getLogger(ExtractUnallocAction.class.getName()); private static final Logger logger = Logger.getLogger(ExtractUnallocAction.class.getName());
private final List<OutputFileData> filesToExtract = new ArrayList<>(); private final List<OutputFileData> filesToExtract = new ArrayList<>();
private static final Set<String> volumesInProgress = new HashSet<>(); private static final Set<String> volumesInProgress = new HashSet<>();
private static final Set<Long> imagesInProgress = new HashSet<>(); private static final Set<Long> imagesInProgress = new HashSet<>();
private static String userDefinedExportPath;
private long currentImage = 0L; private long currentImage = 0L;
private final boolean isImage; private final boolean isImage;
public ExtractUnallocAction(String title, Volume volume){ /**
* Create an instance of ExtractUnallocAction with a volume.
*
* @param title The title
* @param volume The volume set for extraction.
*/
public ExtractUnallocAction(String title, Volume volume) {
super(title); super(title);
isImage = false; isImage = false;
try { try {
@ -82,6 +90,13 @@ final class ExtractUnallocAction extends AbstractAction {
} }
/**
* Create an instance of ExtractUnallocAction with an image.
*
* @param title The title.
* @param image The image set for extraction.
* @throws NoCurrentCaseException If no case is open.
*/
public ExtractUnallocAction(String title, Image image) throws NoCurrentCaseException { public ExtractUnallocAction(String title, Image image) throws NoCurrentCaseException {
super(title); super(title);
isImage = true; isImage = true;
@ -101,7 +116,7 @@ final class ExtractUnallocAction extends AbstractAction {
* Writes the unallocated files to * Writes the unallocated files to
* $CaseDir/Export/ImgName-Unalloc-ImgObjectID-VolumeID.dat * $CaseDir/Export/ImgName-Unalloc-ImgObjectID-VolumeID.dat
* *
* @param e * @param event
*/ */
@NbBundle.Messages({"# {0} - fileName", @NbBundle.Messages({"# {0} - fileName",
"ExtractUnallocAction.volumeInProgress=Already extracting unallocated space into {0} - will skip this volume", "ExtractUnallocAction.volumeInProgress=Already extracting unallocated space into {0} - will skip this volume",
@ -110,8 +125,8 @@ final class ExtractUnallocAction extends AbstractAction {
"ExtractUnallocAction.imageError=Error extracting unallocated space from image", "ExtractUnallocAction.imageError=Error extracting unallocated space from image",
"ExtractUnallocAction.noOpenCase.errMsg=No open case available."}) "ExtractUnallocAction.noOpenCase.errMsg=No open case available."})
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent event) {
if (filesToExtract != null && filesToExtract.size() > 0) { if (filesToExtract != null && filesToExtract.isEmpty() == false) {
// This check doesn't absolutely guarantee that the image won't be in progress when we make the worker, // This check doesn't absolutely guarantee that the image won't be in progress when we make the worker,
// but in general it will suffice. // but in general it will suffice.
if (isImage && isImageInProgress(currentImage)) { if (isImage && isImageInProgress(currentImage)) {
@ -145,18 +160,21 @@ final class ExtractUnallocAction extends AbstractAction {
} }
}; };
fileChooser.setCurrentDirectory(new File(openCase.getExportDirectory())); fileChooser.setCurrentDirectory(new File(getExportDirectory(openCase)));
fileChooser.setDialogTitle( fileChooser.setDialogTitle(
NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.dlgTitle.selectDirToSaveTo.msg")); NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.dlgTitle.selectDirToSaveTo.msg"));
fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
fileChooser.setAcceptAllFileFilterUsed(false); fileChooser.setAcceptAllFileFilterUsed(false);
int returnValue = fileChooser.showSaveDialog((Component) e.getSource()); int returnValue = fileChooser.showSaveDialog((Component) event.getSource());
if (returnValue == JFileChooser.APPROVE_OPTION) { if (returnValue == JFileChooser.APPROVE_OPTION) {
String destination = fileChooser.getSelectedFile().getPath(); String destination = fileChooser.getSelectedFile().getPath();
updateExportDirectory(destination, openCase);
for (OutputFileData outputFileData : filesToExtract) { for (OutputFileData outputFileData : filesToExtract) {
outputFileData.setPath(destination); outputFileData.setPath(destination);
if (outputFileData.layoutFiles != null && outputFileData.layoutFiles.size() > 0 && (! isVolumeInProgress(outputFileData.getFileName()))) { if (outputFileData.layoutFiles != null && outputFileData.layoutFiles.size() > 0 && (!isVolumeInProgress(outputFileData.getFileName()))) {
//Format for single Unalloc File is ImgName-Unalloc-ImgObjectID-VolumeID.dat //Format for single Unalloc File is ImgName-Unalloc-ImgObjectID-VolumeID.dat
// Check if there is already a file with this name // Check if there is already a file with this name
@ -174,20 +192,20 @@ final class ExtractUnallocAction extends AbstractAction {
} }
if (!isImage & !copyList.isEmpty()) { if (!isImage & !copyList.isEmpty()) {
try{ try {
ExtractUnallocWorker worker = new ExtractUnallocWorker(outputFileData); ExtractUnallocWorker worker = new ExtractUnallocWorker(outputFileData);
worker.execute(); worker.execute();
} catch (Exception ex){ } catch (TskCoreException ex) {
logger.log(Level.WARNING, "Already extracting unallocated space into " + outputFileData.getFileName()); logger.log(Level.WARNING, "Already extracting unallocated space into {0}", outputFileData.getFileName());
MessageNotifyUtil.Message.info(NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.volumeInProgress", outputFileData.getFileName())); MessageNotifyUtil.Message.info(NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.volumeInProgress", outputFileData.getFileName()));
} }
} }
} else { } else {
// The output file for this volume could not be created for one of the following reasons // The output file for this volume could not be created for one of the following reasons
if (outputFileData.layoutFiles == null){ if (outputFileData.layoutFiles == null) {
MessageNotifyUtil.Message.info(NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.volumeError")); MessageNotifyUtil.Message.info(NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.volumeError"));
logger.log(Level.SEVERE, "Tried to get unallocated content but the list of unallocated files was null"); //NON-NLS logger.log(Level.SEVERE, "Tried to get unallocated content but the list of unallocated files was null"); //NON-NLS
} else if (outputFileData.layoutFiles.isEmpty()){ } else if (outputFileData.layoutFiles.isEmpty()) {
MessageNotifyUtil.Message.info(NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.noFiles")); MessageNotifyUtil.Message.info(NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.noFiles"));
logger.log(Level.WARNING, "No unallocated files found in volume"); //NON-NLS logger.log(Level.WARNING, "No unallocated files found in volume"); //NON-NLS
copyList.remove(outputFileData); copyList.remove(outputFileData);
@ -204,32 +222,70 @@ final class ExtractUnallocAction extends AbstractAction {
// to extract everything (the worker in the above loop doesn't get created because isImage is true) // to extract everything (the worker in the above loop doesn't get created because isImage is true)
// It's also unclear to me why we need the two separate worker types. // It's also unclear to me why we need the two separate worker types.
if (isImage && !copyList.isEmpty()) { if (isImage && !copyList.isEmpty()) {
try{ try {
ExtractUnallocWorker worker = new ExtractUnallocWorker(copyList); ExtractUnallocWorker worker = new ExtractUnallocWorker(copyList);
worker.execute(); worker.execute();
} catch (Exception ex){ } catch (Exception ex) {
logger.log(Level.WARNING, "Error creating ExtractUnallocWorker", ex); logger.log(Level.WARNING, "Error creating ExtractUnallocWorker", ex);
MessageNotifyUtil.Message.info(NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.imageError")); MessageNotifyUtil.Message.info(NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.imageError"));
} }
} }
} }
} }
}
/**
* Get the export directory path.
*
* @param openCase The current case.
*
* @return The export directory path.
*/
private String getExportDirectory(Case openCase) {
String caseExportPath = openCase.getExportDirectory();
if (userDefinedExportPath == null) {
return caseExportPath;
}
File file = new File(userDefinedExportPath);
if (file.exists() == false || file.isDirectory() == false) {
return caseExportPath;
}
return userDefinedExportPath;
}
/**
* Update the default export directory. If the directory path matches the
* case export directory, then the directory used will always match the
* export directory of any given case. Otherwise, the path last used will be
* saved.
*
* @param exportPath The export path.
* @param openCase The current case.
*/
private void updateExportDirectory(String exportPath, Case openCase) {
if (exportPath.equalsIgnoreCase(openCase.getExportDirectory())) {
userDefinedExportPath = null;
} else {
userDefinedExportPath = exportPath;
}
} }
/** /**
* Gets all the unallocated files in a given Content. * Gets all the unallocated files in a given Content.
* *
* @param c Content to get Unallocated Files from * @param content Content to get Unallocated Files from
* *
* @return A list<LayoutFile> if it didn't crash List may be empty. * @return A list<LayoutFile> if it didn't crash List may be empty.
*/ */
private List<LayoutFile> getUnallocFiles(Content c) { private List<LayoutFile> getUnallocFiles(Content content) {
UnallocVisitor uv = new UnallocVisitor(); UnallocVisitor unallocVisitor = new UnallocVisitor();
try { try {
for (Content contentChild : c.getChildren()) { for (Content contentChild : content.getChildren()) {
if (contentChild instanceof AbstractContent) { if (contentChild instanceof AbstractContent) {
return contentChild.accept(uv); //call on first non-artifact child added to database return contentChild.accept(unallocVisitor); //call on first non-artifact child added to database
} }
} }
} catch (TskCoreException tce) { } catch (TskCoreException tce) {
@ -239,36 +295,35 @@ final class ExtractUnallocAction extends AbstractAction {
} }
synchronized static private void addVolumeInProgress(String volumeOutputFileName) throws TskCoreException { synchronized static private void addVolumeInProgress(String volumeOutputFileName) throws TskCoreException {
if(volumesInProgress.contains(volumeOutputFileName)){ if (volumesInProgress.contains(volumeOutputFileName)) {
throw new TskCoreException("Already writing unallocated space to " + volumeOutputFileName); throw new TskCoreException("Already writing unallocated space to " + volumeOutputFileName);
} }
volumesInProgress.add(volumeOutputFileName); volumesInProgress.add(volumeOutputFileName);
} }
synchronized static private void removeVolumeInProgress(String volumeOutputFileName){ synchronized static private void removeVolumeInProgress(String volumeOutputFileName) {
volumesInProgress.remove(volumeOutputFileName); volumesInProgress.remove(volumeOutputFileName);
} }
synchronized static private boolean isVolumeInProgress(String volumeOutputFileName){ synchronized static private boolean isVolumeInProgress(String volumeOutputFileName) {
return volumesInProgress.contains(volumeOutputFileName); return volumesInProgress.contains(volumeOutputFileName);
} }
synchronized static private void addImageInProgress(Long id) throws TskCoreException { synchronized static private void addImageInProgress(Long objId) throws TskCoreException {
if(imagesInProgress.contains(id)){ if (imagesInProgress.contains(objId)) {
throw new TskCoreException("Image " + id + " is in use"); throw new TskCoreException("Image " + objId + " is in use");
} }
imagesInProgress.add(id); imagesInProgress.add(objId);
} }
synchronized static private void removeImageInProgress(Long id){ synchronized static private void removeImageInProgress(Long objId) {
imagesInProgress.remove(id); imagesInProgress.remove(objId);
} }
synchronized static private boolean isImageInProgress(Long id){ synchronized static private boolean isImageInProgress(Long objId) {
return imagesInProgress.contains(id); return imagesInProgress.contains(objId);
} }
/** /**
* Private class for dispatching the file IO in a background thread. * Private class for dispatching the file IO in a background thread.
*/ */
@ -276,9 +331,9 @@ final class ExtractUnallocAction extends AbstractAction {
private ProgressHandle progress; private ProgressHandle progress;
private boolean canceled = false; private boolean canceled = false;
private List<OutputFileData> outputFileDataList = new ArrayList<>(); private final List<OutputFileData> outputFileDataList = new ArrayList<>();
private File currentlyProcessing; private File currentlyProcessing;
private int totalSizeinMegs; private final int totalSizeinMegs;
long totalBytes = 0; long totalBytes = 0;
ExtractUnallocWorker(OutputFileData outputFileData) throws TskCoreException { ExtractUnallocWorker(OutputFileData outputFileData) throws TskCoreException {
@ -294,18 +349,18 @@ final class ExtractUnallocAction extends AbstractAction {
//Getting the total megs this worker is going to be doing //Getting the total megs this worker is going to be doing
for (OutputFileData outputFileData : outputFileDataList) { for (OutputFileData outputFileData : outputFileDataList) {
try{ try {
// If a volume is locked, skip it but continue trying to process any other requested volumes // If a volume is locked, skip it but continue trying to process any other requested volumes
addVolumeInProgress(outputFileData.getFileName()); addVolumeInProgress(outputFileData.getFileName());
totalBytes += outputFileData.getSizeInBytes(); totalBytes += outputFileData.getSizeInBytes();
this.outputFileDataList.add(outputFileData); this.outputFileDataList.add(outputFileData);
} catch (TskCoreException ex){ } catch (TskCoreException ex) {
logger.log(Level.WARNING, "Already extracting data into " + outputFileData.getFileName()); logger.log(Level.WARNING, "Already extracting data into {0}", outputFileData.getFileName());
} }
} }
// If we don't have anything to output (because of locking), throw an exception // If we don't have anything to output (because of locking), throw an exception
if(this.outputFileDataList.isEmpty()){ if (this.outputFileDataList.isEmpty()) {
throw new TskCoreException("No unallocated files can be extracted"); throw new TskCoreException("No unallocated files can be extracted");
} }
@ -314,9 +369,9 @@ final class ExtractUnallocAction extends AbstractAction {
private int toMb(long bytes) { private int toMb(long bytes) {
if (bytes > 1024 && (bytes / 1024.0) <= Double.MAX_VALUE) { if (bytes > 1024 && (bytes / 1024.0) <= Double.MAX_VALUE) {
double Mb = ((bytes / 1024.0) / 1024.0);//Bytes -> Megabytes double megabytes = ((bytes / 1024.0) / 1024.0);//Bytes -> Megabytes
if (Mb <= Integer.MAX_VALUE) { if (megabytes <= Integer.MAX_VALUE) {
return (int) Math.ceil(Mb); return (int) Math.ceil(megabytes);
} }
} }
return 0; return 0;
@ -347,7 +402,7 @@ final class ExtractUnallocAction extends AbstractAction {
int mbs = 0; //Increments every 128th tick of kbs int mbs = 0; //Increments every 128th tick of kbs
for (OutputFileData outputFileData : this.outputFileDataList) { for (OutputFileData outputFileData : this.outputFileDataList) {
currentlyProcessing = outputFileData.getFile(); currentlyProcessing = outputFileData.getFile();
logger.log(Level.INFO, "Writing Unalloc file to " + currentlyProcessing.getPath()); //NON-NLS logger.log(Level.INFO, "Writing Unalloc file to {0}", currentlyProcessing.getPath()); //NON-NLS
OutputStream outputStream = new FileOutputStream(currentlyProcessing); OutputStream outputStream = new FileOutputStream(currentlyProcessing);
long bytes = 0; long bytes = 0;
int i = 0; int i = 0;
@ -374,9 +429,9 @@ final class ExtractUnallocAction extends AbstractAction {
if (canceled) { if (canceled) {
outputFileData.getFile().delete(); outputFileData.getFile().delete();
logger.log(Level.INFO, "Canceled extraction of " + outputFileData.getFileName() + " and deleted file"); //NON-NLS logger.log(Level.INFO, "Canceled extraction of {0} and deleted file", outputFileData.getFileName()); //NON-NLS
} else { } else {
logger.log(Level.INFO, "Finished writing unalloc file " + outputFileData.getFile().getPath()); //NON-NLS logger.log(Level.INFO, "Finished writing unalloc file {0}", outputFileData.getFile().getPath()); //NON-NLS
} }
} }
progress.finish(); progress.finish();
@ -589,7 +644,7 @@ final class ExtractUnallocAction extends AbstractAction {
*/ */
private class OutputFileData { private class OutputFileData {
private List<LayoutFile> layoutFiles; private final List<LayoutFile> layoutFiles;
private final long sizeInBytes; private final long sizeInBytes;
private long volumeId; private long volumeId;
private long imageId; private long imageId;

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2015-2017 Basis Technology Corp. * Copyright 2015-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -19,16 +19,19 @@
package org.sleuthkit.autopsy.guiutils; package org.sleuthkit.autopsy.guiutils;
import java.awt.Component; import java.awt.Component;
import java.lang.reflect.InvocationTargetException;
import javax.swing.ImageIcon; import javax.swing.ImageIcon;
import javax.swing.JTable; import javax.swing.JTable;
import static javax.swing.SwingConstants.CENTER; import static javax.swing.SwingConstants.CENTER;
import org.openide.nodes.Node;
import org.openide.util.ImageUtilities; import org.openide.util.ImageUtilities;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.datamodel.NodeProperty;
/** /**
* A JTable cell renderer that represents a status as a center-aligned icon, and * A JTable and outline view cell renderer that represents a status as a
* grays out the cell if the table is disabled. The statuses represented are OK, * center-aligned icon, and grays out the cell if the table is disabled. The
* WARNING, and ERROR. * statuses represented are OK, WARNING, and ERROR.
*/ */
public class StatusIconCellRenderer extends GrayableCellRenderer { public class StatusIconCellRenderer extends GrayableCellRenderer {
@ -45,8 +48,20 @@ public class StatusIconCellRenderer extends GrayableCellRenderer {
@Override @Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
setHorizontalAlignment(CENTER); setHorizontalAlignment(CENTER);
if ((value instanceof Status)) { Object switchValue = null;
switch((Status) value) { if ((value instanceof NodeProperty)) {
//The Outline view has properties in the cell, the value contained in the property is what we want
try {
switchValue = ((Node.Property) value).getValue();
} catch (IllegalAccessException | InvocationTargetException ex) {
//Unable to get the value from the NodeProperty no Icon will be displayed
}
} else {
//JTables contain the value we want directly in the cell
switchValue = value;
}
if ((switchValue instanceof Status)) {
switch ((Status) switchValue) {
case OK: case OK:
setIcon(OK_ICON); setIcon(OK_ICON);
setToolTipText(org.openide.util.NbBundle.getMessage(StatusIconCellRenderer.class, "StatusIconCellRenderer.tooltiptext.ok")); setToolTipText(org.openide.util.NbBundle.getMessage(StatusIconCellRenderer.class, "StatusIconCellRenderer.tooltiptext.ok"));
@ -60,8 +75,7 @@ public class StatusIconCellRenderer extends GrayableCellRenderer {
setToolTipText(org.openide.util.NbBundle.getMessage(StatusIconCellRenderer.class, "StatusIconCellRenderer.tooltiptext.error")); setToolTipText(org.openide.util.NbBundle.getMessage(StatusIconCellRenderer.class, "StatusIconCellRenderer.tooltiptext.error"));
break; break;
} }
} } else {
else {
setIcon(null); setIcon(null);
setText(""); setText("");
} }

View File

@ -134,7 +134,7 @@ class ImageWriter implements PropertyChangeListener{
@Messages({ @Messages({
"# {0} - data source name", "# {0} - data source name",
"ImageWriter.progressBar.message=Finishing acquisition of {0}" "ImageWriter.progressBar.message=Finishing acquisition of {0} (unplug device to cancel)"
}) })
private void startFinishImage(String dataSourceName){ private void startFinishImage(String dataSourceName){

View File

@ -25,22 +25,27 @@ import java.io.OutputStream;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level; import java.util.logging.Level;
import net.sf.sevenzipjbinding.ArchiveFormat; import net.sf.sevenzipjbinding.ArchiveFormat;
import static net.sf.sevenzipjbinding.ArchiveFormat.RAR; import static net.sf.sevenzipjbinding.ArchiveFormat.RAR;
import net.sf.sevenzipjbinding.ExtractAskMode;
import net.sf.sevenzipjbinding.ISequentialOutStream; import net.sf.sevenzipjbinding.ISequentialOutStream;
import net.sf.sevenzipjbinding.ISevenZipInArchive; import net.sf.sevenzipjbinding.ISevenZipInArchive;
import net.sf.sevenzipjbinding.SevenZip; import net.sf.sevenzipjbinding.SevenZip;
import net.sf.sevenzipjbinding.SevenZipException; import net.sf.sevenzipjbinding.SevenZipException;
import net.sf.sevenzipjbinding.SevenZipNativeInitializationException; import net.sf.sevenzipjbinding.SevenZipNativeInitializationException;
import net.sf.sevenzipjbinding.simple.ISimpleInArchive;
import net.sf.sevenzipjbinding.simple.ISimpleInArchiveItem;
import net.sf.sevenzipjbinding.ExtractOperationResult; import net.sf.sevenzipjbinding.ExtractOperationResult;
import net.sf.sevenzipjbinding.IArchiveExtractCallback;
import net.sf.sevenzipjbinding.ICryptoGetTextPassword;
import net.sf.sevenzipjbinding.PropID;
import org.netbeans.api.progress.ProgressHandle; import org.netbeans.api.progress.ProgressHandle;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
@ -158,8 +163,12 @@ class SevenZipExtractor {
* *
* @param archiveFile the AbstractFile for the parent archive which * @param archiveFile the AbstractFile for the parent archive which
* which we are checking * which we are checking
* @param archiveFileItem the current item being extracted from the parent * @param inArchive The SevenZip archive currently open for extraction
* archive *
* @param inArchiveItemIndex Index of item inside the SevenZip archive. Each
* file inside an archive is associated with a unique
* integer
*
* @param depthMap a concurrent hashmap which keeps track of the * @param depthMap a concurrent hashmap which keeps track of the
* depth of all nested archives, key of objectID * depth of all nested archives, key of objectID
* @param escapedFilePath the path to the archiveFileItem which has been * @param escapedFilePath the path to the archiveFileItem which has been
@ -167,19 +176,22 @@ class SevenZipExtractor {
* *
* @return true if potential zip bomb, false otherwise * @return true if potential zip bomb, false otherwise
*/ */
private boolean isZipBombArchiveItemCheck(AbstractFile archiveFile, ISimpleInArchiveItem archiveFileItem, ConcurrentHashMap<Long, Archive> depthMap, String escapedFilePath) { private boolean isZipBombArchiveItemCheck(AbstractFile archiveFile, ISevenZipInArchive inArchive, int inArchiveItemIndex, ConcurrentHashMap<Long, Archive> depthMap, String escapedFilePath) {
try { try {
final Long archiveItemSize = archiveFileItem.getSize(); final Long archiveItemSize = (Long) inArchive.getProperty(
inArchiveItemIndex, PropID.SIZE);
//skip the check for small files //skip the check for small files
if (archiveItemSize == null || archiveItemSize < MIN_COMPRESSION_RATIO_SIZE) { if (archiveItemSize == null || archiveItemSize < MIN_COMPRESSION_RATIO_SIZE) {
return false; return false;
} }
final Long archiveItemPackedSize = archiveFileItem.getPackedSize(); final Long archiveItemPackedSize = (Long) inArchive.getProperty(
inArchiveItemIndex, PropID.PACKED_SIZE);
if (archiveItemPackedSize == null || archiveItemPackedSize <= 0) { if (archiveItemPackedSize == null || archiveItemPackedSize <= 0) {
logger.log(Level.WARNING, "Cannot getting compression ratio, cannot detect if zipbomb: {0}, item: {1}", new Object[]{archiveFile.getName(), archiveFileItem.getPath()}); //NON-NLS logger.log(Level.WARNING, "Cannot getting compression ratio, cannot detect if zipbomb: {0}, item: {1}", //NON-NLS
new Object[]{archiveFile.getName(), (String) inArchive.getProperty(inArchiveItemIndex, PropID.PATH)}); //NON-NLS
return false; return false;
} }
@ -366,8 +378,9 @@ class SevenZipExtractor {
* *
* @throws SevenZipException * @throws SevenZipException
*/ */
private String getPathInArchive(ISimpleInArchiveItem item, int itemNumber, AbstractFile archiveFile) throws SevenZipException { private String getPathInArchive(ISevenZipInArchive archive, int inArchiveItemIndex, AbstractFile archiveFile) throws SevenZipException {
String pathInArchive = item.getPath(); String pathInArchive = (String) archive.getProperty(
inArchiveItemIndex, PropID.PATH);
if (pathInArchive == null || pathInArchive.isEmpty()) { if (pathInArchive == null || pathInArchive.isEmpty()) {
//some formats (.tar.gz) may not be handled correctly -- file in archive has no name/path //some formats (.tar.gz) may not be handled correctly -- file in archive has no name/path
@ -400,7 +413,7 @@ class SevenZipExtractor {
} }
} }
if (useName == null) { if (useName == null) {
pathInArchive = "/" + archName + "/" + Integer.toString(itemNumber); pathInArchive = "/" + archName + "/" + Integer.toString(inArchiveItemIndex);
} else { } else {
pathInArchive = "/" + useName; pathInArchive = "/" + useName;
} }
@ -428,69 +441,6 @@ class SevenZipExtractor {
return node == null ? null : archiveFilePath + "/" + node.getFileName(); return node == null ? null : archiveFilePath + "/" + node.getFileName();
} }
/**
* Unpack an archive item to the disk using a password if specified.
*
* @param item - the archive item to unpack
* @param unpackedNode - the unpackedNode to add derivedInfo to
* @param password - the password for the archive, null if not
* used
* @param freeDiskSpace - the amount of free disk space
* @param uniqueExtractedName - the name of the file to extract the item to
*
* @return unpackedNode - the updated unpackedNode
*
* @throws SevenZipException
*/
private SevenZipExtractor.UnpackedTree.UnpackedNode unpackNode(ISimpleInArchiveItem item, SevenZipExtractor.UnpackedTree.UnpackedNode unpackedNode, String password, long freeDiskSpace, String uniqueExtractedName) throws SevenZipException {
//unpack locally if a file
final String localAbsPath = moduleDirAbsolute + File.separator + uniqueExtractedName;
final String localRelPath = moduleDirRelative + File.separator + uniqueExtractedName;
final Date createTime = item.getCreationTime();
final Date accessTime = item.getLastAccessTime();
final Date writeTime = item.getLastWriteTime();
final long createtime = createTime == null ? 0L : createTime.getTime() / 1000;
final long modtime = writeTime == null ? 0L : writeTime.getTime() / 1000;
final long accesstime = accessTime == null ? 0L : accessTime.getTime() / 1000;
SevenZipExtractor.UnpackStream unpackStream = null;
boolean isDir = item.isFolder();
if (!isDir) {
try {
// NOTE: item.getSize() may return null in case of certain
// archiving formats. Eg: BZ2
if (item.getSize() != null) {
unpackStream = new SevenZipExtractor.KnownSizeUnpackStream(localAbsPath, item.getSize());
} else {
unpackStream = new SevenZipExtractor.UnknownSizeUnpackStream(localAbsPath, freeDiskSpace);
}
ExtractOperationResult result;
if (password == null) {
result = item.extractSlow(unpackStream);
} else {
result = item.extractSlow(unpackStream, password);
}
if (result != ExtractOperationResult.OK) {
logger.log(Level.WARNING, "Extraction of : {0} encountered error {1}", new Object[]{localAbsPath, result}); //NON-NLS
return null;
}
} catch (SevenZipException e) {
//could be something unexpected with this file, move on
logger.log(Level.WARNING, "Could not extract file from archive: " + localAbsPath, e); //NON-NLS
} finally {
if (unpackStream != null) {
//record derived data in unode, to be traversed later after unpacking the archive
unpackedNode.addDerivedInfo(unpackStream.getSize(), !isDir,
0L, createtime, accesstime, modtime, localRelPath);
unpackStream.close();
}
}
} else { // this is a directory, size is always 0
unpackedNode.addDerivedInfo(0, !isDir, 0L, createtime, accesstime, modtime, localRelPath);
}
return unpackedNode;
}
/** /**
* Unpack the file to local folder and return a list of derived files * Unpack the file to local folder and return a list of derived files
* *
@ -523,7 +473,6 @@ class SevenZipExtractor {
boolean hasEncrypted = false; boolean hasEncrypted = false;
boolean fullEncryption = true; boolean fullEncryption = true;
boolean progressStarted = false; boolean progressStarted = false;
int processedItems = 0;
final String archiveFilePath = getArchiveFilePath(archiveFile); final String archiveFilePath = getArchiveFilePath(archiveFile);
final String escapedArchiveFilePath = FileUtil.escapeFileName(archiveFilePath); final String escapedArchiveFilePath = FileUtil.escapeFileName(archiveFilePath);
HashMap<String, ZipFileStatusWrapper> statusMap = new HashMap<>(); HashMap<String, ZipFileStatusWrapper> statusMap = new HashMap<>();
@ -590,7 +539,7 @@ class SevenZipExtractor {
logger.log(Level.INFO, "Count of items in archive: {0}: {1}", new Object[]{escapedArchiveFilePath, numItems}); //NON-NLS logger.log(Level.INFO, "Count of items in archive: {0}: {1}", new Object[]{escapedArchiveFilePath, numItems}); //NON-NLS
progress.start(numItems); progress.start(numItems);
progressStarted = true; progressStarted = true;
final ISimpleInArchive simpleInArchive = inArchive.getSimpleInterface(); progress.progress(archiveFile.getName() + ": Analyzing archive metadata and creating local files");
//setup the archive local root folder //setup the archive local root folder
final String uniqueArchiveFileName = FileUtil.escapeFileName(EmbeddedFileExtractorIngestModule.getUniqueName(archiveFile)); final String uniqueArchiveFileName = FileUtil.escapeFileName(EmbeddedFileExtractorIngestModule.getUniqueName(archiveFile));
@ -614,25 +563,18 @@ class SevenZipExtractor {
//currently getFreeDiskSpace always returns DISK_FREE_SPACE_UNKNOWN //currently getFreeDiskSpace always returns DISK_FREE_SPACE_UNKNOWN
freeDiskSpace = IngestMonitor.DISK_FREE_SPACE_UNKNOWN; freeDiskSpace = IngestMonitor.DISK_FREE_SPACE_UNKNOWN;
} }
//unpack and process every item in archive
int itemNumber = 0;
for (ISimpleInArchiveItem item : simpleInArchive.getArchiveItems()) { Map<Integer, InArchiveItemDetails> archiveDetailsMap = new LinkedHashMap<>();
String pathInArchive = getPathInArchive(item, itemNumber, archiveFile); for (int inArchiveItemIndex = 0; inArchiveItemIndex < numItems; inArchiveItemIndex++) {
if (isZipBombArchiveItemCheck(archiveFile, inArchive, inArchiveItemIndex, depthMap, escapedArchiveFilePath)) {
//query for path in db
++itemNumber;
//check if possible zip bomb
if (isZipBombArchiveItemCheck(archiveFile, item, depthMap, escapedArchiveFilePath)) {
unpackSuccessful = false; unpackSuccessful = false;
return unpackSuccessful; return unpackSuccessful;
} }
SevenZipExtractor.UnpackedTree.UnpackedNode unpackedNode = unpackedTree.addNode(pathInArchive);
//update progress bar
progress.progress(archiveFile.getName() + ": " + item.getPath(), processedItems);
final boolean isEncrypted = item.isEncrypted(); String pathInArchive = getPathInArchive(inArchive, inArchiveItemIndex, archiveFile);
SevenZipExtractor.UnpackedTree.UnpackedNode unpackedNode = unpackedTree.addNode(pathInArchive);
final boolean isEncrypted = (Boolean) inArchive.getProperty(inArchiveItemIndex, PropID.ENCRYPTED);
if (isEncrypted && password == null) { if (isEncrypted && password == null) {
logger.log(Level.WARNING, "Skipping encrypted file in archive: {0}", pathInArchive); //NON-NLS logger.log(Level.WARNING, "Skipping encrypted file in archive: {0}", pathInArchive); //NON-NLS
@ -642,20 +584,25 @@ class SevenZipExtractor {
} else { } else {
fullEncryption = false; fullEncryption = false;
} }
// NOTE: item.getSize() may return null in case of certain
// NOTE: item size may return null in case of certain
// archiving formats. Eg: BZ2 // archiving formats. Eg: BZ2
//check if unpacking this file will result in out of disk space //check if unpacking this file will result in out of disk space
//this is additional to zip bomb prevention mechanism //this is additional to zip bomb prevention mechanism
if (freeDiskSpace != IngestMonitor.DISK_FREE_SPACE_UNKNOWN && item.getSize() != null && item.getSize() > 0) { //if free space is known and file is not empty. Long archiveItemSize = (Long) inArchive.getProperty(
long newDiskSpace = freeDiskSpace - item.getSize(); inArchiveItemIndex, PropID.SIZE);
if (freeDiskSpace != IngestMonitor.DISK_FREE_SPACE_UNKNOWN && archiveItemSize != null && archiveItemSize > 0) { //if free space is known and file is not empty.
String archiveItemPath = (String) inArchive.getProperty(
inArchiveItemIndex, PropID.PATH);
long newDiskSpace = freeDiskSpace - archiveItemSize;
if (newDiskSpace < MIN_FREE_DISK_SPACE) { if (newDiskSpace < MIN_FREE_DISK_SPACE) {
String msg = NbBundle.getMessage(SevenZipExtractor.class, String msg = NbBundle.getMessage(SevenZipExtractor.class,
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.notEnoughDiskSpace.msg", "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.notEnoughDiskSpace.msg",
escapedArchiveFilePath, item.getPath()); escapedArchiveFilePath, archiveItemPath);
String details = NbBundle.getMessage(SevenZipExtractor.class, String details = NbBundle.getMessage(SevenZipExtractor.class,
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.notEnoughDiskSpace.details"); "EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.notEnoughDiskSpace.details");
services.postMessage(IngestMessage.createErrorMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details)); services.postMessage(IngestMessage.createErrorMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details));
logger.log(Level.INFO, "Skipping archive item due to insufficient disk space: {0}, {1}", new String[]{escapedArchiveFilePath, item.getPath()}); //NON-NLS logger.log(Level.INFO, "Skipping archive item due to insufficient disk space: {0}, {1}", new String[]{escapedArchiveFilePath, archiveItemPath}); //NON-NLS
logger.log(Level.INFO, "Available disk space: {0}", new Object[]{freeDiskSpace}); //NON-NLS logger.log(Level.INFO, "Available disk space: {0}", new Object[]{freeDiskSpace}); //NON-NLS
unpackSuccessful = false; unpackSuccessful = false;
continue; //skip this file continue; //skip this file
@ -664,42 +611,61 @@ class SevenZipExtractor {
freeDiskSpace = newDiskSpace; freeDiskSpace = newDiskSpace;
} }
} }
final String uniqueExtractedName = FileUtil.escapeFileName(uniqueArchiveFileName + File.separator + (item.getItemIndex() / 1000) + File.separator + item.getItemIndex() + "_" + new File(pathInArchive).getName()); final String uniqueExtractedName = FileUtil.escapeFileName(uniqueArchiveFileName + File.separator + (inArchiveItemIndex / 1000) + File.separator + inArchiveItemIndex + "_" + new File(pathInArchive).getName());
final String localAbsPath = moduleDirAbsolute + File.separator + uniqueExtractedName;
final String localRelPath = moduleDirRelative + File.separator + uniqueExtractedName;
//create local dirs and empty files before extracted //create local dirs and empty files before extracted
File localFile = new java.io.File(moduleDirAbsolute + File.separator + uniqueExtractedName); File localFile = new java.io.File(localAbsPath);
//cannot rely on files in top-bottom order //cannot rely on files in top-bottom order
if (!localFile.exists()) { if (!localFile.exists()) {
try { try {
if (item.isFolder()) { if ((Boolean) inArchive.getProperty(
inArchiveItemIndex, PropID.IS_FOLDER)) {
localFile.mkdirs(); localFile.mkdirs();
} else { } else {
localFile.getParentFile().mkdirs(); localFile.getParentFile().mkdirs();
try { try {
localFile.createNewFile(); localFile.createNewFile();
} catch (IOException e) { } catch (IOException e) {
logger.log(Level.SEVERE, "Error creating extracted file: " + localFile.getAbsolutePath(), e); //NON-NLS logger.log(Level.SEVERE, "Error creating extracted file: "//NON-NLS
+ localFile.getAbsolutePath(), e);
} }
} }
} catch (SecurityException e) { } catch (SecurityException e) {
logger.log(Level.SEVERE, "Error setting up output path for unpacked file: {0}", pathInArchive); //NON-NLS logger.log(Level.SEVERE, "Error setting up output path for unpacked file: {0}", //NON-NLS
pathInArchive); //NON-NLS
//TODO consider bail out / msg to the user //TODO consider bail out / msg to the user
} }
} }
// skip the rest of this loop if we couldn't create the file // skip the rest of this loop if we couldn't create the file
//continue will skip details from being added to the map
if (localFile.exists() == false) { if (localFile.exists() == false) {
continue; continue;
} }
//find this node in the hierarchy, create if neede;
unpackedNode = unpackNode(item, unpackedNode, password, //Store archiveItemIndex with local paths and unpackedNode reference.
freeDiskSpace, uniqueExtractedName); //Necessary for the extract call back to write the current archive
if (unpackedNode == null) { //file to the correct disk location and to correctly update it's
unpackSuccessful = false; //corresponding unpackedNode
archiveDetailsMap.put(inArchiveItemIndex, new InArchiveItemDetails(
unpackedNode, localAbsPath, localRelPath));
} }
//update units for progress bar int[] extractionIndices = getExtractableFilesFromDetailsMap(archiveDetailsMap);
++processedItems;
} StandardIArchiveExtractCallback archiveCallBack
= new StandardIArchiveExtractCallback(
inArchive, archiveFile, progress,
archiveDetailsMap, password, freeDiskSpace);
//According to the documentation, indices in sorted order are optimal
//for efficiency. Hence, the LinkedHashMap and linear processing of
//inArchiveItemIndex. False indicates non-test mode
inArchive.extract(extractionIndices, false, archiveCallBack);
unpackSuccessful = unpackSuccessful & archiveCallBack.wasSuccessful();
// add them to the DB. We wait until the end so that we have the metadata on all of the // add them to the DB. We wait until the end so that we have the metadata on all of the
// intermediate nodes since the order is not guaranteed // intermediate nodes since the order is not guaranteed
try { try {
@ -799,6 +765,21 @@ class SevenZipExtractor {
return unpackSuccessful; return unpackSuccessful;
} }
/**
* Produce a list of archive indices needed for the call to extract, which
* will open the archive and begin unpacking the files.
*/
private int[] getExtractableFilesFromDetailsMap(
Map<Integer, InArchiveItemDetails> archiveDetailsMap) {
Integer[] wrappedExtractionIndices = archiveDetailsMap.keySet()
.toArray(new Integer[archiveDetailsMap.size()]);
return Arrays.stream(wrappedExtractionIndices)
.mapToInt(Integer::intValue)
.toArray();
}
/** /**
* Stream used to unpack the archive to local file * Stream used to unpack the archive to local file
*/ */
@ -933,6 +914,207 @@ class SevenZipExtractor {
} }
} }
/**
* Wrapper for necessary details used in StandardIArchiveExtractCallback
*/
private static class InArchiveItemDetails {
private final SevenZipExtractor.UnpackedTree.UnpackedNode unpackedNode;
private final String localAbsPath;
private final String localRelPath;
public InArchiveItemDetails(
SevenZipExtractor.UnpackedTree.UnpackedNode unpackedNode,
String localAbsPath, String localRelPath) {
this.unpackedNode = unpackedNode;
this.localAbsPath = localAbsPath;
this.localRelPath = localRelPath;
}
public SevenZipExtractor.UnpackedTree.UnpackedNode getUnpackedNode() {
return unpackedNode;
}
public String getLocalAbsPath() {
return localAbsPath;
}
public String getLocalRelPath() {
return localRelPath;
}
}
/**
* Call back class used by extract to expand archive files. This is the most
* efficient way to process according to the sevenzip binding documentation.
*/
private static class StandardIArchiveExtractCallback
implements IArchiveExtractCallback, ICryptoGetTextPassword {
private final AbstractFile archiveFile;
private final ISevenZipInArchive inArchive;
private SevenZipExtractor.UnpackStream unpackStream = null;
private final Map<Integer, InArchiveItemDetails> archiveDetailsMap;
private final ProgressHandle progressHandle;
private int inArchiveItemIndex;
private final long freeDiskSpace;
private long createTimeInSeconds;
private long modTimeInSeconds;
private long accessTimeInSeconds;
private boolean isFolder;
private final String password;
private boolean unpackSuccessful = true;
public StandardIArchiveExtractCallback(ISevenZipInArchive inArchive,
AbstractFile archiveFile, ProgressHandle progressHandle,
Map<Integer, InArchiveItemDetails> archiveDetailsMap,
String password, long freeDiskSpace) {
this.inArchive = inArchive;
this.freeDiskSpace = freeDiskSpace;
this.progressHandle = progressHandle;
this.archiveFile = archiveFile;
this.archiveDetailsMap = archiveDetailsMap;
this.password = password;
}
/**
* Get stream is called by the internal framework as it traverses
* the archive structure. The ISequentialOutStream is where the
* archive file contents will be expanded and written to the local disk.
*
* Skips folders, as there is nothing to extract.
*
* @param inArchiveItemIndex current location of the
* @param mode Will always be EXTRACT
* @return
* @throws SevenZipException
*/
@Override
public ISequentialOutStream getStream(int inArchiveItemIndex,
ExtractAskMode mode) throws SevenZipException {
this.inArchiveItemIndex = inArchiveItemIndex;
isFolder = (Boolean) inArchive
.getProperty(inArchiveItemIndex, PropID.IS_FOLDER);
if (isFolder || mode != ExtractAskMode.EXTRACT) {
return null;
}
final Long archiveItemSize = (Long) inArchive.getProperty(
inArchiveItemIndex, PropID.SIZE);
final String localAbsPath = archiveDetailsMap.get(
inArchiveItemIndex).getLocalAbsPath();
if (archiveItemSize != null) {
unpackStream = new SevenZipExtractor.KnownSizeUnpackStream(
localAbsPath, archiveItemSize);
} else {
unpackStream = new SevenZipExtractor.UnknownSizeUnpackStream(
localAbsPath, freeDiskSpace);
}
return unpackStream;
}
/**
* Retrieves the file metadata from the archive before extraction.
* Called after getStream.
*
* @param mode Will always be EXTRACT.
* @throws SevenZipException
*/
@Override
public void prepareOperation(ExtractAskMode mode) throws SevenZipException {
final Date createTime = (Date) inArchive.getProperty(
inArchiveItemIndex, PropID.CREATION_TIME);
final Date accessTime = (Date) inArchive.getProperty(
inArchiveItemIndex, PropID.LAST_ACCESS_TIME);
final Date writeTime = (Date) inArchive.getProperty(
inArchiveItemIndex, PropID.LAST_WRITE_TIME);
createTimeInSeconds = createTime == null ? 0L
: createTime.getTime() / 1000;
modTimeInSeconds = writeTime == null ? 0L
: writeTime.getTime() / 1000;
accessTimeInSeconds = accessTime == null ? 0L
: accessTime.getTime() / 1000;
}
/**
* Updates the unpackedNode data in the tree after the archive has been
* expanded to local disk.
*
* @param EOR - ExtractOperationResult
*
* @throws SevenZipException
*/
@Override
public void setOperationResult(ExtractOperationResult result) throws SevenZipException {
progressHandle.progress(archiveFile.getName() + ": "
+ (String) inArchive.getProperty(inArchiveItemIndex, PropID.PATH),
inArchiveItemIndex);
final SevenZipExtractor.UnpackedTree.UnpackedNode unpackedNode
= archiveDetailsMap.get(inArchiveItemIndex).getUnpackedNode();
final String localRelPath = archiveDetailsMap.get(
inArchiveItemIndex).getLocalRelPath();
if (isFolder) {
unpackedNode.addDerivedInfo(0,
!(Boolean) inArchive.getProperty(inArchiveItemIndex, PropID.IS_FOLDER),
0L, createTimeInSeconds, accessTimeInSeconds, modTimeInSeconds,
localRelPath);
return;
}
final String localAbsPath = archiveDetailsMap.get(
inArchiveItemIndex).getLocalAbsPath();
if (result != ExtractOperationResult.OK) {
logger.log(Level.WARNING, "Extraction of : {0} encountered error {1}", //NON-NLS
new Object[]{localAbsPath, result});
unpackSuccessful = false;
}
//record derived data in unode, to be traversed later after unpacking the archive
unpackedNode.addDerivedInfo(unpackStream.getSize(),
!(Boolean) inArchive.getProperty(inArchiveItemIndex, PropID.IS_FOLDER),
0L, createTimeInSeconds, accessTimeInSeconds, modTimeInSeconds, localRelPath);
unpackStream.close();
}
@Override
public void setTotal(long value) throws SevenZipException {
//Not necessary for extract, left intenionally blank
}
@Override
public void setCompleted(long value) throws SevenZipException {
//Not necessary for extract, left intenionally blank
}
/**
* Called when opening encrypted archive files.
*
* @return - Password supplied by user
*
* @throws SevenZipException
*/
@Override
public String cryptoGetTextPassword() throws SevenZipException {
return password;
}
public boolean wasSuccessful() {
return unpackSuccessful;
}
}
/** /**
* Representation of the files in the archive. Used to track of local tree * Representation of the files in the archive. Used to track of local tree
* file hierarchy, archive depth, and files created to easily and reliably * file hierarchy, archive depth, and files created to easily and reliably
@ -1290,7 +1472,8 @@ class SevenZipExtractor {
} }
/** /**
* Set the flag which identifies whether this file has been determined to be a zip bomb to true. * Set the flag which identifies whether this file has been determined
* to be a zip bomb to true.
*/ */
synchronized void flagAsZipBomb() { synchronized void flagAsZipBomb() {
flaggedAsZipBomb = true; flaggedAsZipBomb = true;

View File

@ -26,20 +26,21 @@
<Layout> <Layout>
<DimensionLayout dim="0"> <DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0"> <Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0"> <Component id="jScrollPane1" max="32767" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Component id="instructionLabel" min="-2" max="-2" attributes="0"/> <Component id="instructionLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/> <EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group> </Group>
<Component id="jScrollPane1" max="32767" attributes="0"/> <Group type="102" alignment="0" attributes="0">
<Component id="pasteFromClipboardButton" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="121" max="32767" attributes="0"/>
<Component id="okButton" min="-2" pref="84" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="cancelButton" min="-2" pref="84" max="-2" attributes="0"/>
</Group> </Group>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="AddValuesToHashDatabaseButton" alignment="1" min="-2" max="-2" attributes="0"/>
<Component id="cancelButton" alignment="1" min="-2" max="-2" attributes="0"/>
<Component id="pasteFromClipboardButton" alignment="1" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
</Group> </Group>
@ -47,21 +48,18 @@
</DimensionLayout> </DimensionLayout>
<DimensionLayout dim="1"> <DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="1" attributes="0">
<EmptySpace max="-2" attributes="0"/> <EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="instructionLabel" min="-2" max="-2" attributes="0"/> <Component id="instructionLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0"> <Component id="jScrollPane1" pref="279" max="32767" attributes="0"/>
<Group type="102" attributes="0"> <EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="AddValuesToHashDatabaseButton" min="-2" max="-2" attributes="0"/> <Group type="103" groupAlignment="3" attributes="0">
<EmptySpace max="-2" attributes="0"/> <Component id="pasteFromClipboardButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="cancelButton" min="-2" max="-2" attributes="0"/> <Component id="okButton" alignment="3" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/> <Component id="cancelButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="pasteFromClipboardButton" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<Component id="jScrollPane1" pref="274" max="32767" attributes="0"/> <EmptySpace min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group> </Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
@ -102,14 +100,14 @@
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="pasteFromClipboardButtonActionPerformed"/> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="pasteFromClipboardButtonActionPerformed"/>
</Events> </Events>
</Component> </Component>
<Component class="javax.swing.JButton" name="AddValuesToHashDatabaseButton"> <Component class="javax.swing.JButton" name="okButton">
<Properties> <Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/hashdatabase/Bundle.properties" key="AddHashValuesToDatabaseDialog.AddValuesToHashDatabaseButton.text_2" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/modules/hashdatabase/Bundle.properties" key="AddHashValuesToDatabaseDialog.okButton.text_2" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
<Events> <Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="AddValuesToHashDatabaseButtonActionPerformed"/> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="okButtonActionPerformed"/>
</Events> </Events>
</Component> </Component>
<Component class="javax.swing.JButton" name="cancelButton"> <Component class="javax.swing.JButton" name="cancelButton">

View File

@ -73,7 +73,7 @@ public class AddHashValuesToDatabaseDialog extends javax.swing.JDialog {
} else { } else {
setDefaultCloseOperation(0); setDefaultCloseOperation(0);
} }
AddValuesToHashDatabaseButton.setEnabled(enable); okButton.setEnabled(enable);
cancelButton.setEnabled(enable); cancelButton.setEnabled(enable);
pasteFromClipboardButton.setEnabled(enable); pasteFromClipboardButton.setEnabled(enable);
} }
@ -91,7 +91,7 @@ public class AddHashValuesToDatabaseDialog extends javax.swing.JDialog {
jScrollPane1 = new javax.swing.JScrollPane(); jScrollPane1 = new javax.swing.JScrollPane();
hashValuesTextArea = new javax.swing.JTextArea(); hashValuesTextArea = new javax.swing.JTextArea();
pasteFromClipboardButton = new javax.swing.JButton(); pasteFromClipboardButton = new javax.swing.JButton();
AddValuesToHashDatabaseButton = new javax.swing.JButton(); okButton = new javax.swing.JButton();
cancelButton = new javax.swing.JButton(); cancelButton = new javax.swing.JButton();
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
@ -115,10 +115,10 @@ public class AddHashValuesToDatabaseDialog extends javax.swing.JDialog {
} }
}); });
org.openide.awt.Mnemonics.setLocalizedText(AddValuesToHashDatabaseButton, org.openide.util.NbBundle.getMessage(AddHashValuesToDatabaseDialog.class, "AddHashValuesToDatabaseDialog.AddValuesToHashDatabaseButton.text_2")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(okButton, org.openide.util.NbBundle.getMessage(AddHashValuesToDatabaseDialog.class, "AddHashValuesToDatabaseDialog.okButton.text_2")); // NOI18N
AddValuesToHashDatabaseButton.addActionListener(new java.awt.event.ActionListener() { okButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) { public void actionPerformed(java.awt.event.ActionEvent evt) {
AddValuesToHashDatabaseButtonActionPerformed(evt); okButtonActionPerformed(evt);
} }
}); });
@ -136,31 +136,30 @@ public class AddHashValuesToDatabaseDialog extends javax.swing.JDialog {
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addContainerGap() .addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jScrollPane1)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addComponent(instructionLabel) .addComponent(instructionLabel)
.addGap(0, 0, Short.MAX_VALUE)) .addGap(0, 0, Short.MAX_VALUE))
.addComponent(jScrollPane1)) .addGroup(layout.createSequentialGroup()
.addGap(18, 18, 18) .addComponent(pasteFromClipboardButton)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 121, Short.MAX_VALUE)
.addComponent(AddValuesToHashDatabaseButton, javax.swing.GroupLayout.Alignment.TRAILING) .addComponent(okButton, javax.swing.GroupLayout.PREFERRED_SIZE, 84, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(cancelButton, javax.swing.GroupLayout.Alignment.TRAILING) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(pasteFromClipboardButton, javax.swing.GroupLayout.Alignment.TRAILING)) .addComponent(cancelButton, javax.swing.GroupLayout.PREFERRED_SIZE, 84, javax.swing.GroupLayout.PREFERRED_SIZE)))
.addContainerGap()) .addContainerGap())
); );
layout.setVerticalGroup( layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup() .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap() .addContainerGap()
.addComponent(instructionLabel) .addComponent(instructionLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 279, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup()
.addComponent(AddValuesToHashDatabaseButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(cancelButton) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(pasteFromClipboardButton)
.addComponent(pasteFromClipboardButton)) .addComponent(okButton)
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 274, Short.MAX_VALUE)) .addComponent(cancelButton))
.addContainerGap()) .addContainerGap())
); );
@ -213,17 +212,17 @@ public class AddHashValuesToDatabaseDialog extends javax.swing.JDialog {
} }
}//GEN-LAST:event_hashValuesTextAreaMouseClicked }//GEN-LAST:event_hashValuesTextAreaMouseClicked
private void AddValuesToHashDatabaseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_AddValuesToHashDatabaseButtonActionPerformed private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed
AddHashValuesToDatabaseProgressDialog progressDialog = new AddHashValuesToDatabaseProgressDialog(this, hashDb, hashValuesTextArea.getText()); AddHashValuesToDatabaseProgressDialog progressDialog = new AddHashValuesToDatabaseProgressDialog(this, hashDb, hashValuesTextArea.getText());
progressDialog.addHashValuesToDatabase(); progressDialog.addHashValuesToDatabase();
}//GEN-LAST:event_AddValuesToHashDatabaseButtonActionPerformed }//GEN-LAST:event_okButtonActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton AddValuesToHashDatabaseButton;
private javax.swing.JButton cancelButton; private javax.swing.JButton cancelButton;
private javax.swing.JTextArea hashValuesTextArea; private javax.swing.JTextArea hashValuesTextArea;
private javax.swing.JLabel instructionLabel; private javax.swing.JLabel instructionLabel;
private javax.swing.JScrollPane jScrollPane1; private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JButton okButton;
private javax.swing.JButton pasteFromClipboardButton; private javax.swing.JButton pasteFromClipboardButton;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
} }

View File

@ -87,8 +87,8 @@ HashDbImportDatabaseDialog.importHashDbErr=Import Hash Set Error
HashDbImportDatabaseDialog.mustSelectHashDbFilePathMsg=A hash set file path must be selected. HashDbImportDatabaseDialog.mustSelectHashDbFilePathMsg=A hash set file path must be selected.
HashDbImportDatabaseDialog.hashDbDoesNotExistMsg=The selected hash set does not exist. HashDbImportDatabaseDialog.hashDbDoesNotExistMsg=The selected hash set does not exist.
HashDbImportDatabaseDialog.errorMessage.failedToOpenHashDbMsg=Failed to open hash set at {0}. HashDbImportDatabaseDialog.errorMessage.failedToOpenHashDbMsg=Failed to open hash set at {0}.
HashDbIngestModule.moduleName=Hash Lookup HashLookupModuleFactory.moduleName.text=Hash Lookup
HashDbIngestModule.moduleDescription=Identifies known and notable files using supplied hash sets, such as a standard NSRL hash set. HashLookupModuleFactory.moduleDescription.text=Identifies known and notable files using supplied hash sets, such as a standard NSRL hash set.
HashDbIngestModule.fileReadErrorMsg=Read Error\: {0} HashDbIngestModule.fileReadErrorMsg=Read Error\: {0}
HashDbIngestModule.calcHashValueErr=Error encountered while calculating the hash value for {0}. HashDbIngestModule.calcHashValueErr=Error encountered while calculating the hash value for {0}.
HashDbIngestModule.hashLookupErrorMsg=Hash Lookup Error\: {0} HashDbIngestModule.hashLookupErrorMsg=Hash Lookup Error\: {0}
@ -172,7 +172,6 @@ HashDbSearchPanel.hashTable.defaultModel.title.text=MD5 Hashes
AddHashValuesToDatabaseDialog.JDialog.Title=Add Hashes to Hash Set AddHashValuesToDatabaseDialog.JDialog.Title=Add Hashes to Hash Set
AddHashValuesToDatabaseDialog.instructionLabel.text_1=Paste MD5 hash values (one per line) below: AddHashValuesToDatabaseDialog.instructionLabel.text_1=Paste MD5 hash values (one per line) below:
AddHashValuesToDatabaseDialog.cancelButton.text_2=Cancel AddHashValuesToDatabaseDialog.cancelButton.text_2=Cancel
AddHashValuesToDatabaseDialog.AddValuesToHashDatabaseButton.text_2=Add Hashes to Hash Set
AddHashValuesToDatabaseDialog.pasteFromClipboardButton.text_2=Paste From Clipboard AddHashValuesToDatabaseDialog.pasteFromClipboardButton.text_2=Paste From Clipboard
AddHashValuesToDatabaseProgressDialog.okButton.text=OK AddHashValuesToDatabaseProgressDialog.okButton.text=OK
AddHashValuesToDatabaseProgressDialog.statusLabel.text=status AddHashValuesToDatabaseProgressDialog.statusLabel.text=status
@ -237,3 +236,4 @@ HashDbCreateDatabaseDialog.centralRepoRadioButton.text=Remote (Central Repositor
HashDbCreateDatabaseDialog.lbOrg.text=Source Organization: HashDbCreateDatabaseDialog.lbOrg.text=Source Organization:
HashDbCreateDatabaseDialog.orgButton.text=Manage Organizations HashDbCreateDatabaseDialog.orgButton.text=Manage Organizations
HashDbCreateDatabaseDialog.databasePathLabel.text=Hash Set Path: HashDbCreateDatabaseDialog.databasePathLabel.text=Hash Set Path:
AddHashValuesToDatabaseDialog.okButton.text_2=OK

View File

@ -76,8 +76,8 @@ HashDbImportDatabaseDialog.importHashDbErr=\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\
HashDbImportDatabaseDialog.mustSelectHashDbFilePathMsg=\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306e\u30d5\u30a1\u30a4\u30eb\u30d1\u30b9\u306e\u9078\u629e\u304c\u5fc5\u8981\u3067\u3059\u3002 HashDbImportDatabaseDialog.mustSelectHashDbFilePathMsg=\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306e\u30d5\u30a1\u30a4\u30eb\u30d1\u30b9\u306e\u9078\u629e\u304c\u5fc5\u8981\u3067\u3059\u3002
HashDbImportDatabaseDialog.hashDbDoesNotExistMsg=\u9078\u629e\u3055\u308c\u305f\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306f\u5b58\u5728\u3057\u307e\u305b\u3093\u3002 HashDbImportDatabaseDialog.hashDbDoesNotExistMsg=\u9078\u629e\u3055\u308c\u305f\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306f\u5b58\u5728\u3057\u307e\u305b\u3093\u3002
HashDbImportDatabaseDialog.errorMessage.failedToOpenHashDbMsg={0}\u3067\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u958b\u304f\u306e\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002 HashDbImportDatabaseDialog.errorMessage.failedToOpenHashDbMsg={0}\u3067\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u958b\u304f\u306e\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002
HashDbIngestModule.moduleName=\u30cf\u30c3\u30b7\u30e5\u30eb\u30c3\u30af\u30a2\u30c3\u30d7 HashLookupModuleFactory.moduleName.text=\u30cf\u30c3\u30b7\u30e5\u30eb\u30c3\u30af\u30a2\u30c3\u30d7
HashDbIngestModule.moduleDescription=\u6a19\u6e96\u306eNSRL\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306a\u3069\u3001\u63d0\u4f9b\u3055\u308c\u305f\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u5229\u7528\u3057\u3066\u3001\u65e2\u77e5\u307e\u305f\u306f\u7591\u308f\u3057\u3044\u3082\u306e\u3092\u691c\u77e5\u3057\u307e\u3059\u3002 HashLookupModuleFactory.moduleDescription.text=\u6a19\u6e96\u306eNSRL\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306a\u3069\u3001\u63d0\u4f9b\u3055\u308c\u305f\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u5229\u7528\u3057\u3066\u3001\u65e2\u77e5\u307e\u305f\u306f\u7591\u308f\u3057\u3044\u3082\u306e\u3092\u691c\u77e5\u3057\u307e\u3059\u3002
HashDbIngestModule.noKnownHashDbSetMsg=\u65e2\u77e5\u306e\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304c\u5b58\u5728\u3057\u307e\u305b\u3093\u3002 HashDbIngestModule.noKnownHashDbSetMsg=\u65e2\u77e5\u306e\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304c\u5b58\u5728\u3057\u307e\u305b\u3093\u3002
HashDbIngestModule.knownFileSearchWillNotExecuteWarn=\u65e2\u77e5\u306e\u30d5\u30a1\u30a4\u30eb\u691c\u7d22\u304c\u5b9f\u884c\u3055\u308c\u307e\u305b\u3093\u3002 HashDbIngestModule.knownFileSearchWillNotExecuteWarn=\u65e2\u77e5\u306e\u30d5\u30a1\u30a4\u30eb\u691c\u7d22\u304c\u5b9f\u884c\u3055\u308c\u307e\u305b\u3093\u3002
HashDbIngestModule.noKnownBadHashDbSetMsg=\u65e2\u77e5\u306e\u60aa\u8cea\u306a\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30bb\u30c3\u30c8\u306f\u3042\u308a\u307e\u305b\u3093\u3002 HashDbIngestModule.noKnownBadHashDbSetMsg=\u65e2\u77e5\u306e\u60aa\u8cea\u306a\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30bb\u30c3\u30c8\u306f\u3042\u308a\u307e\u305b\u3093\u3002
@ -86,7 +86,6 @@ HashDbIngestModule.knownBadFileSearchWillNotExecuteWarn=\u65e2\u77e5\u306e\u60aa
HashDbIngestModule.fileReadErrorMsg=\u8aad\u307f\u8fbc\u307f\u30a8\u30e9\u30fc\uff1a {0} HashDbIngestModule.fileReadErrorMsg=\u8aad\u307f\u8fbc\u307f\u30a8\u30e9\u30fc\uff1a {0}
HashDbIngestModule.calcHashValueErr={0}\u306e\u30cf\u30c3\u30b7\u30e5\u5024\u3092\u8a08\u7b97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 HashDbIngestModule.calcHashValueErr={0}\u306e\u30cf\u30c3\u30b7\u30e5\u5024\u3092\u8a08\u7b97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002
HashDbIngestModule.hashLookupErrorMsg=\u30cf\u30c3\u30b7\u30e5\u30eb\u30c3\u30af\u30a2\u30c3\u30d7\u30a8\u30e9\u30fc\uff1a {0} HashDbIngestModule.hashLookupErrorMsg=\u30cf\u30c3\u30b7\u30e5\u30eb\u30c3\u30af\u30a2\u30c3\u30d7\u30a8\u30e9\u30fc\uff1a {0}
HashDbIngestModule.settingKnownBadStateErr={0}\u306e\u65e2\u77e5\u306e\u60aa\u8cea\u30b9\u30c6\u30fc\u30bf\u30b9\u3092\u8a2d\u5b9a\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002
HashDbIngestModule.lookingUpKnownBadHashValueErr={0}\u306e\u65e2\u77e5\u306e\u60aa\u8cea\u30cf\u30c3\u30b7\u30e5\u5024\u3092\u30eb\u30c3\u30af\u30a2\u30c3\u30d7\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 HashDbIngestModule.lookingUpKnownBadHashValueErr={0}\u306e\u65e2\u77e5\u306e\u60aa\u8cea\u30cf\u30c3\u30b7\u30e5\u5024\u3092\u30eb\u30c3\u30af\u30a2\u30c3\u30d7\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002
HashDbIngestModule.lookingUpKnownHashValueErr={0}\u306e\u65e2\u77e5\u306e\u30cf\u30c3\u30b7\u30e5\u5024\u3092\u30eb\u30c3\u30af\u30a2\u30c3\u30d7\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 HashDbIngestModule.lookingUpKnownHashValueErr={0}\u306e\u65e2\u77e5\u306e\u30cf\u30c3\u30b7\u30e5\u5024\u3092\u30eb\u30c3\u30af\u30a2\u30c3\u30d7\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002
HashDbIngestModule.postToBB.fileName=\u30d5\u30a1\u30a4\u30eb\u540d HashDbIngestModule.postToBB.fileName=\u30d5\u30a1\u30a4\u30eb\u540d
@ -172,7 +171,6 @@ AddHashValuesToDatabaseDialog.JDialog.Title=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9
HashLookupSettingsPanel.addHashesToDatabaseButton.text=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306b\u30cf\u30c3\u30b7\u30e5\u3092\u8ffd\u52a0 HashLookupSettingsPanel.addHashesToDatabaseButton.text=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306b\u30cf\u30c3\u30b7\u30e5\u3092\u8ffd\u52a0
AddHashValuesToDatabaseDialog.instructionLabel.text_1=\u4e0b\u8a18\u306bMD5\u306e\u30cf\u30c3\u30b7\u30e5\u5024\u3092\u8cbc\u308a\u4ed8\u3051\u308b\uff08\u30e9\u30a4\u30f3\u3054\u3068\u306b\u4e00\u3064\u305a\u3064\uff09\uff1a AddHashValuesToDatabaseDialog.instructionLabel.text_1=\u4e0b\u8a18\u306bMD5\u306e\u30cf\u30c3\u30b7\u30e5\u5024\u3092\u8cbc\u308a\u4ed8\u3051\u308b\uff08\u30e9\u30a4\u30f3\u3054\u3068\u306b\u4e00\u3064\u305a\u3064\uff09\uff1a
AddHashValuesToDatabaseDialog.cancelButton.text_2=\u30ad\u30e3\u30f3\u30bb\u30eb AddHashValuesToDatabaseDialog.cancelButton.text_2=\u30ad\u30e3\u30f3\u30bb\u30eb
AddHashValuesToDatabaseDialog.AddValuesToHashDatabaseButton.text_2=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306b\u30cf\u30c3\u30b7\u30e5\u3092\u8ffd\u52a0
AddHashValuesToDatabaseDialog.pasteFromClipboardButton.text_2=\u30af\u30ea\u30c3\u30d7\u30dc\u30fc\u30c9\u304b\u3089\u8cbc\u308a\u4ed8\u3051\u308b AddHashValuesToDatabaseDialog.pasteFromClipboardButton.text_2=\u30af\u30ea\u30c3\u30d7\u30dc\u30fc\u30c9\u304b\u3089\u8cbc\u308a\u4ed8\u3051\u308b
AddHashValuesToDatabaseProgressDialog.okButton.text=OK AddHashValuesToDatabaseProgressDialog.okButton.text=OK
AddHashValuesToDatabaseProgressDialog.statusLabel.text=\u30b9\u30c6\u30fc\u30bf\u30b9 AddHashValuesToDatabaseProgressDialog.statusLabel.text=\u30b9\u30c6\u30fc\u30bf\u30b9
@ -207,3 +205,4 @@ HashLookupSettingsPanel.nameLabel.text=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c
HashLookupSettingsPanel.hashDatabasesLabel.text=\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\uff1a HashLookupSettingsPanel.hashDatabasesLabel.text=\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\uff1a
HashLookupSettingsPanel.importDatabaseButton.text=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u30a4\u30f3\u30dd\u30fc\u30c8 HashLookupSettingsPanel.importDatabaseButton.text=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u30a4\u30f3\u30dd\u30fc\u30c8
HashDbCreateDatabaseDialog.databasePathLabel.text=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30d1\u30b9\uff1a HashDbCreateDatabaseDialog.databasePathLabel.text=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30d1\u30b9\uff1a
AddHashValuesToDatabaseDialog.okButton.text_2=OK

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011 - 2013 Basis Technology Corp. * Copyright 2011-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -53,7 +53,10 @@ import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.datamodel.TskException; import org.sleuthkit.datamodel.TskException;
@NbBundle.Messages({ /**
* File ingest module to mark files based on hash values.
*/
@Messages({
"HashDbIngestModule.noKnownBadHashDbSetMsg=No notable hash set.", "HashDbIngestModule.noKnownBadHashDbSetMsg=No notable hash set.",
"HashDbIngestModule.knownBadFileSearchWillNotExecuteWarn=Notable file search will not be executed.", "HashDbIngestModule.knownBadFileSearchWillNotExecuteWarn=Notable file search will not be executed.",
"HashDbIngestModule.noKnownHashDbSetMsg=No known hash set.", "HashDbIngestModule.noKnownHashDbSetMsg=No known hash set.",
@ -67,18 +70,21 @@ public class HashDbIngestModule implements FileIngestModule {
private final SleuthkitCase skCase; private final SleuthkitCase skCase;
private final HashDbManager hashDbManager = HashDbManager.getInstance(); private final HashDbManager hashDbManager = HashDbManager.getInstance();
private final HashLookupModuleSettings settings; private final HashLookupModuleSettings settings;
private List<HashDb> knownBadHashSets = new ArrayList<>(); private final List<HashDb> knownBadHashSets = new ArrayList<>();
private List<HashDb> knownHashSets = new ArrayList<>(); private final List<HashDb> knownHashSets = new ArrayList<>();
private long jobId; private long jobId;
private static final HashMap<Long, IngestJobTotals> totalsForIngestJobs = new HashMap<>(); private static final HashMap<Long, IngestJobTotals> totalsForIngestJobs = new HashMap<>();
private static final IngestModuleReferenceCounter refCounter = new IngestModuleReferenceCounter(); private static final IngestModuleReferenceCounter refCounter = new IngestModuleReferenceCounter();
private Blackboard blackboard; private Blackboard blackboard;
/**
* A container of values for storing ingest metrics for the job.
*/
private static class IngestJobTotals { private static class IngestJobTotals {
private AtomicLong totalKnownBadCount = new AtomicLong(0); private final AtomicLong totalKnownBadCount = new AtomicLong(0);
private AtomicLong totalCalctime = new AtomicLong(0); private final AtomicLong totalCalctime = new AtomicLong(0);
private AtomicLong totalLookuptime = new AtomicLong(0); private final AtomicLong totalLookuptime = new AtomicLong(0);
} }
private static synchronized IngestJobTotals getTotalsForIngestJobs(long ingestJobId) { private static synchronized IngestJobTotals getTotalsForIngestJobs(long ingestJobId) {
@ -90,6 +96,15 @@ public class HashDbIngestModule implements FileIngestModule {
return totals; return totals;
} }
/**
* Create a HashDbIngestModule object that will mark files based on a
* supplied list of hash values. The supplied HashLookupModuleSettings
* object is used to configure the module.
*
* @param settings The module settings.
*
* @throws NoCurrentCaseException If there is no open case.
*/
HashDbIngestModule(HashLookupModuleSettings settings) throws NoCurrentCaseException { HashDbIngestModule(HashLookupModuleSettings settings) throws NoCurrentCaseException {
this.settings = settings; this.settings = settings;
skCase = Case.getCurrentCaseThrows().getSleuthkitCase(); skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
@ -140,12 +155,18 @@ public class HashDbIngestModule implements FileIngestModule {
enabledHashSets.add(db); enabledHashSets.add(db);
} }
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
logger.log(Level.WARNING, "Error getting index status for " + db.getDisplayName()+ " hash set", ex); //NON-NLS logger.log(Level.WARNING, "Error getting index status for " + db.getDisplayName() + " hash set", ex); //NON-NLS
} }
} }
} }
} }
@Messages({
"# {0} - File name",
"HashDbIngestModule.dialogTitle.errorFindingArtifacts=Error Finding Artifacts: {0}",
"# {0} - File name",
"HashDbIngestModule.errorMessage.lookingForFileArtifacts=Error encountered while looking for existing artifacts for {0}."
})
@Override @Override
public ProcessResult process(AbstractFile file) { public ProcessResult process(AbstractFile file) {
try { try {
@ -156,8 +177,8 @@ public class HashDbIngestModule implements FileIngestModule {
} }
// Skip unallocated space files. // Skip unallocated space files.
if ((file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS) || if ((file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS)
file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK))) { || file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK))) {
return ProcessResult.OK; return ProcessResult.OK;
} }
@ -181,6 +202,7 @@ public class HashDbIngestModule implements FileIngestModule {
// calc hash value // calc hash value
String name = file.getName(); String name = file.getName();
long fileId = file.getId();
String md5Hash = file.getMd5Hash(); String md5Hash = file.getMd5Hash();
if (md5Hash == null || md5Hash.isEmpty()) { if (md5Hash == null || md5Hash.isEmpty()) {
try { try {
@ -203,15 +225,11 @@ public class HashDbIngestModule implements FileIngestModule {
totals.totalCalctime.addAndGet(delta); totals.totalCalctime.addAndGet(delta);
} catch (IOException ex) { } catch (IOException ex) {
logger.log(Level.WARNING, "Error calculating hash of file " + name, ex); //NON-NLS logger.log(Level.WARNING, String.format("Error calculating hash of file '%s' (id=%d).", name, fileId), ex); //NON-NLS
services.postMessage(IngestMessage.createErrorMessage( services.postMessage(IngestMessage.createErrorMessage(
HashLookupModuleFactory.getModuleName(), HashLookupModuleFactory.getModuleName(),
NbBundle.getMessage(this.getClass(), NbBundle.getMessage(this.getClass(), "HashDbIngestModule.fileReadErrorMsg", name),
"HashDbIngestModule.fileReadErrorMsg", NbBundle.getMessage(this.getClass(), "HashDbIngestModule.calcHashValueErr", name)));
name),
NbBundle.getMessage(this.getClass(),
"HashDbIngestModule.calcHashValueErr",
name)));
return ProcessResult.ERROR; return ProcessResult.ERROR;
} }
} }
@ -245,21 +263,37 @@ public class HashDbIngestModule implements FileIngestModule {
} }
} }
/*
* We have a match. Now create an artifact if it is
* determined that one hasn't been created yet.
*/
List<BlackboardAttribute> attributesList = new ArrayList<>();
attributesList.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, HashLookupModuleFactory.getModuleName(), hashSetName));
try {
org.sleuthkit.datamodel.Blackboard tskBlackboard = skCase.getBlackboard();
if (tskBlackboard.artifactExists(file, BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT, attributesList) == false) {
postHashSetHitToBlackboard(file, md5Hash, hashSetName, comment, db.getSendIngestMessages()); postHashSetHitToBlackboard(file, md5Hash, hashSetName, comment, db.getSendIngestMessages());
} }
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, String.format(
"A problem occurred while checking for existing artifacts for file '%s' (id=%d).", name, fileId), ex); //NON-NLS
services.postMessage(IngestMessage.createErrorMessage(
HashLookupModuleFactory.getModuleName(),
Bundle.HashDbIngestModule_dialogTitle_errorFindingArtifacts(name),
Bundle.HashDbIngestModule_errorMessage_lookingForFileArtifacts(name)));
ret = ProcessResult.ERROR;
}
}
long delta = (System.currentTimeMillis() - lookupstart); long delta = (System.currentTimeMillis() - lookupstart);
totals.totalLookuptime.addAndGet(delta); totals.totalLookuptime.addAndGet(delta);
} catch (TskException ex) { } catch (TskException ex) {
logger.log(Level.WARNING, "Couldn't lookup notable hash for file " + name + " - see sleuthkit log for details", ex); //NON-NLS logger.log(Level.WARNING, String.format(
"Couldn't lookup notable hash for file '%s' (id=%d) - see sleuthkit log for details", name, fileId), ex); //NON-NLS
services.postMessage(IngestMessage.createErrorMessage( services.postMessage(IngestMessage.createErrorMessage(
HashLookupModuleFactory.getModuleName(), HashLookupModuleFactory.getModuleName(),
NbBundle.getMessage(this.getClass(), NbBundle.getMessage(this.getClass(), "HashDbIngestModule.hashLookupErrorMsg", name),
"HashDbIngestModule.hashLookupErrorMsg", NbBundle.getMessage(this.getClass(), "HashDbIngestModule.lookingUpKnownBadHashValueErr", name)));
name),
NbBundle.getMessage(this.getClass(),
"HashDbIngestModule.lookingUpKnownBadHashValueErr",
name)));
ret = ProcessResult.ERROR; ret = ProcessResult.ERROR;
} }
} }
@ -279,15 +313,12 @@ public class HashDbIngestModule implements FileIngestModule {
totals.totalLookuptime.addAndGet(delta); totals.totalLookuptime.addAndGet(delta);
} catch (TskException ex) { } catch (TskException ex) {
logger.log(Level.WARNING, "Couldn't lookup known hash for file " + name + " - see sleuthkit log for details", ex); //NON-NLS logger.log(Level.WARNING, String.format(
"Couldn't lookup known hash for file '%s' (id=%d) - see sleuthkit log for details", name, fileId), ex); //NON-NLS
services.postMessage(IngestMessage.createErrorMessage( services.postMessage(IngestMessage.createErrorMessage(
HashLookupModuleFactory.getModuleName(), HashLookupModuleFactory.getModuleName(),
NbBundle.getMessage(this.getClass(), NbBundle.getMessage(this.getClass(), "HashDbIngestModule.hashLookupErrorMsg", name),
"HashDbIngestModule.hashLookupErrorMsg", NbBundle.getMessage(this.getClass(), "HashDbIngestModule.lookingUpKnownHashValueErr", name)));
name),
NbBundle.getMessage(this.getClass(),
"HashDbIngestModule.lookingUpKnownHashValueErr",
name)));
ret = ProcessResult.ERROR; ret = ProcessResult.ERROR;
} }
} }
@ -296,18 +327,29 @@ public class HashDbIngestModule implements FileIngestModule {
return ret; return ret;
} }
@Messages({"HashDbIngestModule.indexError.message=Failed to index hashset hit artifact for keyword search."}) /**
* Post a hash set hit to the blackboard.
*
* @param abstractFile The file to be processed.
* @param md5Hash The MD5 hash value of the file.
* @param hashSetName The name of the hash set with which to associate
* the hit.
* @param comment A comment to be attached to the artifact.
* @param showInboxMessage Show a message in the inbox?
*/
@Messages({
"HashDbIngestModule.indexError.message=Failed to index hashset hit artifact for keyword search."
})
private void postHashSetHitToBlackboard(AbstractFile abstractFile, String md5Hash, String hashSetName, String comment, boolean showInboxMessage) { private void postHashSetHitToBlackboard(AbstractFile abstractFile, String md5Hash, String hashSetName, String comment, boolean showInboxMessage) {
try { try {
String MODULE_NAME = NbBundle.getMessage(HashDbIngestModule.class, "HashDbIngestModule.moduleName"); String moduleName = HashLookupModuleFactory.getModuleName();
BlackboardArtifact badFile = abstractFile.newArtifact(ARTIFACT_TYPE.TSK_HASHSET_HIT); BlackboardArtifact badFile = abstractFile.newArtifact(ARTIFACT_TYPE.TSK_HASHSET_HIT);
Collection<BlackboardAttribute> attributes = new ArrayList<>(); Collection<BlackboardAttribute> attributes = new ArrayList<>();
//TODO Revisit usage of deprecated constructor as per TSK-583 //TODO Revisit usage of deprecated constructor as per TSK-583
//BlackboardAttribute att2 = new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID(), MODULE_NAME, "Known Bad", hashSetName); //BlackboardAttribute att2 = new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID(), MODULE_NAME, "Known Bad", hashSetName);
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, hashSetName)); attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, moduleName, hashSetName));
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_HASH_MD5, MODULE_NAME, md5Hash)); attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_HASH_MD5, moduleName, md5Hash));
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COMMENT, MODULE_NAME, comment)); attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COMMENT, moduleName, comment));
badFile.addAttributes(attributes); badFile.addAttributes(attributes);
@ -351,19 +393,24 @@ public class HashDbIngestModule implements FileIngestModule {
detailsSb.append("</table>"); //NON-NLS detailsSb.append("</table>"); //NON-NLS
services.postMessage(IngestMessage.createDataMessage(HashLookupModuleFactory.getModuleName(), services.postMessage(IngestMessage.createDataMessage(HashLookupModuleFactory.getModuleName(),
NbBundle.getMessage(this.getClass(), NbBundle.getMessage(this.getClass(), "HashDbIngestModule.postToBB.knownBadMsg", abstractFile.getName()),
"HashDbIngestModule.postToBB.knownBadMsg",
abstractFile.getName()),
detailsSb.toString(), detailsSb.toString(),
abstractFile.getName() + md5Hash, abstractFile.getName() + md5Hash,
badFile)); badFile));
} }
services.fireModuleDataEvent(new ModuleDataEvent(MODULE_NAME, ARTIFACT_TYPE.TSK_HASHSET_HIT, Collections.singletonList(badFile))); services.fireModuleDataEvent(new ModuleDataEvent(moduleName, ARTIFACT_TYPE.TSK_HASHSET_HIT, Collections.singletonList(badFile)));
} catch (TskException ex) { } catch (TskException ex) {
logger.log(Level.WARNING, "Error creating blackboard artifact", ex); //NON-NLS logger.log(Level.WARNING, "Error creating blackboard artifact", ex); //NON-NLS
} }
} }
/**
* Post a message summarizing the results of the ingest.
*
* @param jobId The ID of the job.
* @param knownBadHashSets The list of hash sets for "known bad" files.
* @param knownHashSets The list of hash sets for "known" files.
*/
private static synchronized void postSummary(long jobId, private static synchronized void postSummary(long jobId,
List<HashDb> knownBadHashSets, List<HashDb> knownHashSets) { List<HashDb> knownBadHashSets, List<HashDb> knownHashSets) {
IngestJobTotals jobTotals = getTotalsForIngestJobs(jobId); IngestJobTotals jobTotals = getTotalsForIngestJobs(jobId);
@ -399,8 +446,7 @@ public class HashDbIngestModule implements FileIngestModule {
IngestServices.getInstance().postMessage(IngestMessage.createMessage( IngestServices.getInstance().postMessage(IngestMessage.createMessage(
IngestMessage.MessageType.INFO, IngestMessage.MessageType.INFO,
HashLookupModuleFactory.getModuleName(), HashLookupModuleFactory.getModuleName(),
NbBundle.getMessage(HashDbIngestModule.class, NbBundle.getMessage(HashDbIngestModule.class, "HashDbIngestModule.complete.hashLookupResults"),
"HashDbIngestModule.complete.hashLookupResults"),
detailsSb.toString())); detailsSb.toString()));
} }
} }

View File

@ -42,13 +42,18 @@ public class HashLookupModuleFactory extends IngestModuleFactoryAdapter {
return getModuleName(); return getModuleName();
} }
/**
* Get the name of the module.
*
* @return The module name.
*/
static String getModuleName() { static String getModuleName() {
return NbBundle.getMessage(HashLookupModuleFactory.class, "HashDbIngestModule.moduleName"); return NbBundle.getMessage(HashLookupModuleFactory.class, "HashLookupModuleFactory.moduleName.text");
} }
@Override @Override
public String getModuleDescription() { public String getModuleDescription() {
return NbBundle.getMessage(HashLookupModuleFactory.class, "HashDbIngestModule.moduleDescription"); return NbBundle.getMessage(HashLookupModuleFactory.class, "HashLookupModuleFactory.moduleDescription.text");
} }
@Override @Override
@ -101,8 +106,8 @@ public class HashLookupModuleFactory extends IngestModuleFactoryAdapter {
@Override @Override
public FileIngestModule createFileIngestModule(IngestModuleIngestJobSettings settings) { public FileIngestModule createFileIngestModule(IngestModuleIngestJobSettings settings) {
if (!(settings instanceof HashLookupModuleSettings)) { if (!(settings instanceof HashLookupModuleSettings)) {
throw new IllegalArgumentException( throw new IllegalArgumentException(NbBundle.getMessage(this.getClass(),
NbBundle.getMessage(this.getClass(), "HashLookupModuleFactory.createFileIngestModule.exception.msg")); "HashLookupModuleFactory.createFileIngestModule.exception.msg"));
} }
try { try {
return new HashDbIngestModule((HashLookupModuleSettings) settings); return new HashDbIngestModule((HashLookupModuleSettings) settings);

View File

@ -80,7 +80,6 @@ class ReportHTML implements TableReportModule {
private static final int MAX_THUMBS_PER_PAGE = 1000; private static final int MAX_THUMBS_PER_PAGE = 1000;
private static final String HTML_SUBDIR = "content"; private static final String HTML_SUBDIR = "content";
private Case currentCase; private Case currentCase;
private SleuthkitCase skCase;
static Integer THUMBNAIL_COLUMNS = 5; static Integer THUMBNAIL_COLUMNS = 5;
private Map<String, Integer> dataTypes; private Map<String, Integer> dataTypes;
@ -109,7 +108,6 @@ class ReportHTML implements TableReportModule {
// Refesh the member variables // Refesh the member variables
private void refresh() throws NoCurrentCaseException { private void refresh() throws NoCurrentCaseException {
currentCase = Case.getCurrentCaseThrows(); currentCase = Case.getCurrentCaseThrows();
skCase = currentCase.getSleuthkitCase();
dataTypes = new TreeMap<>(); dataTypes = new TreeMap<>();
@ -274,7 +272,7 @@ class ReportHTML implements TableReportModule {
in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/accounts.png"); //NON-NLS in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/accounts.png"); //NON-NLS
break; break;
default: default:
logger.log(Level.WARNING, "useDataTypeIcon: unhandled artifact type = " + dataType); //NON-NLS logger.log(Level.WARNING, "useDataTypeIcon: unhandled artifact type = {0}", dataType); //NON-NLS
in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/star.png"); //NON-NLS in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/star.png"); //NON-NLS
iconFileName = "star.png"; //NON-NLS iconFileName = "star.png"; //NON-NLS
iconFilePath = subPath + File.separator + iconFileName; iconFilePath = subPath + File.separator + iconFileName;
@ -627,12 +625,19 @@ class ReportHTML implements TableReportModule {
int positionCounter = 0; int positionCounter = 0;
for (String cell : row) { for (String cell : row) {
// position-dependent code used to format this report. Not great, but understandable for formatting. // position-dependent code used to format this report. Not great, but understandable for formatting.
if (positionCounter == 1) { // Convert the file name to a hyperlink and left-align it switch (positionCounter) {
case 1:
// Convert the file name to a hyperlink and left-align it
builder.append("\t\t<td class=\"left_align_cell\">").append(localFileLink.toString()).append(cell).append("</a></td>\n"); //NON-NLS builder.append("\t\t<td class=\"left_align_cell\">").append(localFileLink.toString()).append(cell).append("</a></td>\n"); //NON-NLS
} else if (positionCounter == 7) { // Right-align the bytes column. break;
case 7:
// Right-align the bytes column.
builder.append("\t\t<td class=\"right_align_cell\">").append(cell).append("</td>\n"); //NON-NLS builder.append("\t\t<td class=\"right_align_cell\">").append(cell).append("</td>\n"); //NON-NLS
} else { // Regular case, not a file name nor a byte count break;
default:
// Regular case, not a file name nor a byte count
builder.append("\t\t<td>").append(cell).append("</td>\n"); //NON-NLS builder.append("\t\t<td>").append(cell).append("</td>\n"); //NON-NLS
break;
} }
++positionCounter; ++positionCounter;
} }
@ -719,7 +724,7 @@ class ReportHTML implements TableReportModule {
for (int i = 0; i < tags.size(); i++) { for (int i = 0; i < tags.size(); i++) {
ContentTag tag = tags.get(i); ContentTag tag = tags.get(i);
String notableString = tag.getName().getKnownStatus() == TskData.FileKnown.BAD ? TagsManager.getNotableTagLabel() : ""; String notableString = tag.getName().getKnownStatus() == TskData.FileKnown.BAD ? TagsManager.getNotableTagLabel() : "";
linkToThumbnail.append(tag.getName().getDisplayName() + notableString); linkToThumbnail.append(tag.getName().getDisplayName()).append(notableString);
if (i != tags.size() - 1) { if (i != tags.size() - 1) {
linkToThumbnail.append(", "); linkToThumbnail.append(", ");
} }
@ -751,12 +756,9 @@ class ReportHTML implements TableReportModule {
return true; return true;
} }
AbstractFile file = (AbstractFile) c; AbstractFile file = (AbstractFile) c;
if (file.isDir() return file.isDir()
|| file.getType() == TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS || file.getType() == TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS
|| file.getType() == TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS) { || file.getType() == TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS;
return true;
}
return false;
} }
/** /**
@ -782,8 +784,14 @@ class ReportHTML implements TableReportModule {
localFileFolder.mkdirs(); localFileFolder.mkdirs();
} }
// Construct a file tagName for the local file that incorporates the file id to ensure uniqueness. /*
String fileName = file.getName(); * Construct a file tagName for the local file that incorporates the
* file ID to ensure uniqueness.
*
* Note: File name is normalized to account for possible attribute name
* which will be separated by a ':' character.
*/
String fileName = org.sleuthkit.autopsy.coreutils.FileUtil.escapeFileName(file.getName());
String objectIdSuffix = "_" + file.getId(); String objectIdSuffix = "_" + file.getId();
int lastDotIndex = fileName.lastIndexOf("."); int lastDotIndex = fileName.lastIndexOf(".");
if (lastDotIndex != -1 && lastDotIndex != 0) { if (lastDotIndex != -1 && lastDotIndex != 0) {
@ -1052,9 +1060,9 @@ class ReportHTML implements TableReportModule {
* Write the summary of the current case for this report. * Write the summary of the current case for this report.
*/ */
private void writeSummary() { private void writeSummary() {
Writer out = null; Writer output = null;
try { try {
out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(subPath + "summary.html"), "UTF-8")); //NON-NLS output = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(subPath + "summary.html"), "UTF-8")); //NON-NLS
StringBuilder head = new StringBuilder(); StringBuilder head = new StringBuilder();
head.append("<html>\n<head>\n<title>").append( //NON-NLS head.append("<html>\n<head>\n<title>").append( //NON-NLS
NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.title")).append("</title>\n"); //NON-NLS NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.title")).append("</title>\n"); //NON-NLS
@ -1080,7 +1088,7 @@ class ReportHTML implements TableReportModule {
head.append("li {padding-bottom: 5px;}"); head.append("li {padding-bottom: 5px;}");
head.append("</style>\n"); //NON-NLS head.append("</style>\n"); //NON-NLS
head.append("</head>\n<body>\n"); //NON-NLS head.append("</head>\n<body>\n"); //NON-NLS
out.write(head.toString()); output.write(head.toString());
DateFormat datetimeFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); DateFormat datetimeFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
Date date = new Date(); Date date = new Date();
@ -1119,7 +1127,7 @@ class ReportHTML implements TableReportModule {
} }
summary.append("</div>\n"); //NON-NLS summary.append("</div>\n"); //NON-NLS
summary.append("</body></html>"); //NON-NLS summary.append("</body></html>"); //NON-NLS
out.write(summary.toString()); output.write(summary.toString());
} catch (FileNotFoundException ex) { } catch (FileNotFoundException ex) {
logger.log(Level.SEVERE, "Could not find summary.html file to write to."); //NON-NLS logger.log(Level.SEVERE, "Could not find summary.html file to write to."); //NON-NLS
} catch (UnsupportedEncodingException ex) { } catch (UnsupportedEncodingException ex) {
@ -1130,9 +1138,9 @@ class ReportHTML implements TableReportModule {
logger.log(Level.WARNING, "Unable to get current sleuthkit Case for the HTML report."); logger.log(Level.WARNING, "Unable to get current sleuthkit Case for the HTML report.");
} finally { } finally {
try { try {
if (out != null) { if (output != null) {
out.flush(); output.flush();
out.close(); output.close();
} }
} catch (IOException ex) { } catch (IOException ex) {
} }
@ -1292,9 +1300,24 @@ class ReportHTML implements TableReportModule {
return summary; return summary;
} }
/**
* Create a thumbnail of a given file.
*
* @param file The file from which to create the thumbnail.
*
* @return The path to the thumbnail file, or null if a thumbnail couldn't
* be created.
*/
private String prepareThumbnail(AbstractFile file) { private String prepareThumbnail(AbstractFile file) {
BufferedImage bufferedThumb = ImageUtils.getThumbnail(file, ImageUtils.ICON_SIZE_MEDIUM); BufferedImage bufferedThumb = ImageUtils.getThumbnail(file, ImageUtils.ICON_SIZE_MEDIUM);
File thumbFile = Paths.get(thumbsPath, file.getName() + ".png").toFile();
/*
* File name is normalized to account for possible attribute name which
* will be separated by a ':' character.
*/
String fileName = org.sleuthkit.autopsy.coreutils.FileUtil.escapeFileName(file.getName());
File thumbFile = Paths.get(thumbsPath, fileName + ".png").toFile();
if (bufferedThumb == null) { if (bufferedThumb == null) {
return null; return null;
} }

View File

@ -97,6 +97,9 @@
<ResourceString bundle="org/sleuthkit/autopsy/report/Bundle.properties" key="ReportVisualPanel2.allResultsRadioButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/report/Bundle.properties" key="ReportVisualPanel2.allResultsRadioButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="allResultsRadioButtonActionPerformed"/>
</Events>
</Component> </Component>
<Component class="javax.swing.JLabel" name="dataLabel"> <Component class="javax.swing.JLabel" name="dataLabel">
<Properties> <Properties>

View File

@ -124,7 +124,9 @@ final class ReportVisualPanel2 extends JPanel {
tagsList.addMouseListener(new MouseAdapter() { tagsList.addMouseListener(new MouseAdapter() {
@Override @Override
public void mousePressed(MouseEvent evt) { public void mousePressed(MouseEvent evt) {
if (!taggedResultsRadioButton.isSelected()) {
return;
}
int index = tagsList.locationToIndex(evt.getPoint()); int index = tagsList.locationToIndex(evt.getPoint());
if (index < tagsModel.getSize() && index >= 0) { if (index < tagsModel.getSize() && index >= 0) {
String value = tagsModel.getElementAt(index); String value = tagsModel.getElementAt(index);
@ -184,16 +186,26 @@ final class ReportVisualPanel2 extends JPanel {
return tagStates; return tagStates;
} }
/**
* Are any tags selected?
*
* @return True if any tags are selected; otherwise false.
*/
private boolean areTagsSelected() { private boolean areTagsSelected() {
boolean result = false; boolean result = false;
for (Entry<String, Boolean> entry : tagStates.entrySet()) { for (Entry<String, Boolean> entry : tagStates.entrySet()) {
if (entry.getValue()) { if (entry.getValue()) {
result = true; result = true;
break;
} }
} }
return result; return result;
} }
/**
* Set the Finish button as either enabled or disabled depending on the UI
* component selections.
*/
private void updateFinishButton() { private void updateFinishButton() {
if (taggedResultsRadioButton.isSelected()) { if (taggedResultsRadioButton.isSelected()) {
wizPanel.setFinish(areTagsSelected()); wizPanel.setFinish(areTagsSelected());
@ -209,6 +221,19 @@ final class ReportVisualPanel2 extends JPanel {
return taggedResultsRadioButton.isSelected(); return taggedResultsRadioButton.isSelected();
} }
/**
* Set all tagged results as either selected or unselected.
*
* @param selected Should all tagged results be selected?
*/
void setAllTaggedResultsSelected(boolean selected) {
for (String tag : tags) {
tagStates.put(tag, (selected ? Boolean.TRUE : Boolean.FALSE));
}
tagsList.repaint();
wizPanel.setFinish(selected);
}
/** /**
* This method is called from within the constructor to initialize the form. * 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 * WARNING: Do NOT modify this code. The content of this method is always
@ -234,6 +259,11 @@ final class ReportVisualPanel2 extends JPanel {
optionsButtonGroup.add(allResultsRadioButton); optionsButtonGroup.add(allResultsRadioButton);
org.openide.awt.Mnemonics.setLocalizedText(allResultsRadioButton, org.openide.util.NbBundle.getMessage(ReportVisualPanel2.class, "ReportVisualPanel2.allResultsRadioButton.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(allResultsRadioButton, org.openide.util.NbBundle.getMessage(ReportVisualPanel2.class, "ReportVisualPanel2.allResultsRadioButton.text")); // NOI18N
allResultsRadioButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
allResultsRadioButtonActionPerformed(evt);
}
});
org.openide.awt.Mnemonics.setLocalizedText(dataLabel, org.openide.util.NbBundle.getMessage(ReportVisualPanel2.class, "ReportVisualPanel2.dataLabel.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(dataLabel, org.openide.util.NbBundle.getMessage(ReportVisualPanel2.class, "ReportVisualPanel2.dataLabel.text")); // NOI18N
@ -312,25 +342,21 @@ final class ReportVisualPanel2 extends JPanel {
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
private void selectAllButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_selectAllButtonActionPerformed private void selectAllButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_selectAllButtonActionPerformed
for (String tag : tags) { setAllTaggedResultsSelected(true);
tagStates.put(tag, Boolean.TRUE);
}
tagsList.repaint();
wizPanel.setFinish(true);
}//GEN-LAST:event_selectAllButtonActionPerformed }//GEN-LAST:event_selectAllButtonActionPerformed
private void deselectAllButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deselectAllButtonActionPerformed private void deselectAllButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deselectAllButtonActionPerformed
for (String tag : tags) { setAllTaggedResultsSelected(false);
tagStates.put(tag, Boolean.FALSE);
}
tagsList.repaint();
wizPanel.setFinish(false);
}//GEN-LAST:event_deselectAllButtonActionPerformed }//GEN-LAST:event_deselectAllButtonActionPerformed
private void advancedButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_advancedButtonActionPerformed private void advancedButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_advancedButtonActionPerformed
artifactStates = dialog.display(); artifactStates = dialog.display();
}//GEN-LAST:event_advancedButtonActionPerformed }//GEN-LAST:event_advancedButtonActionPerformed
private void allResultsRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_allResultsRadioButtonActionPerformed
setAllTaggedResultsSelected(false);
}//GEN-LAST:event_allResultsRadioButtonActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton advancedButton; private javax.swing.JButton advancedButton;
private javax.swing.JRadioButton allResultsRadioButton; private javax.swing.JRadioButton allResultsRadioButton;

View File

@ -38,6 +38,7 @@ import java.util.Set;
import java.util.TreeSet; import java.util.TreeSet;
import java.util.logging.Level; import java.util.logging.Level;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.casemodule.services.TagsManager; import org.sleuthkit.autopsy.casemodule.services.TagsManager;
@ -264,6 +265,7 @@ class TableReportGenerator {
/** /**
* Make table for tagged files * Make table for tagged files
*/ */
@Messages({"ReportGenerator.tagTable.header.userName=User Name"})
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
private void makeContentTagsTables() { private void makeContentTagsTables() {
@ -287,6 +289,7 @@ class TableReportGenerator {
NbBundle.getMessage(this.getClass(), "ReportGenerator.htmlOutput.header.tag"), NbBundle.getMessage(this.getClass(), "ReportGenerator.htmlOutput.header.tag"),
NbBundle.getMessage(this.getClass(), "ReportGenerator.htmlOutput.header.file"), NbBundle.getMessage(this.getClass(), "ReportGenerator.htmlOutput.header.file"),
NbBundle.getMessage(this.getClass(), "ReportGenerator.htmlOutput.header.comment"), NbBundle.getMessage(this.getClass(), "ReportGenerator.htmlOutput.header.comment"),
NbBundle.getMessage(this.getClass(), "ReportGenerator.tagTable.header.userName"),
NbBundle.getMessage(this.getClass(), "ReportGenerator.htmlOutput.header.timeModified"), NbBundle.getMessage(this.getClass(), "ReportGenerator.htmlOutput.header.timeModified"),
NbBundle.getMessage(this.getClass(), "ReportGenerator.htmlOutput.header.timeChanged"), NbBundle.getMessage(this.getClass(), "ReportGenerator.htmlOutput.header.timeChanged"),
NbBundle.getMessage(this.getClass(), "ReportGenerator.htmlOutput.header.timeAccessed"), NbBundle.getMessage(this.getClass(), "ReportGenerator.htmlOutput.header.timeAccessed"),
@ -324,7 +327,7 @@ class TableReportGenerator {
fileName = tag.getContent().getName(); fileName = tag.getContent().getName();
} }
ArrayList<String> rowData = new ArrayList<>(Arrays.asList(tag.getName().getDisplayName() + notableString, fileName, tag.getComment())); ArrayList<String> rowData = new ArrayList<>(Arrays.asList(tag.getName().getDisplayName() + notableString, fileName, tag.getComment(), tag.getUserName()));
Content content = tag.getContent(); Content content = tag.getContent();
if (content instanceof AbstractFile) { if (content instanceof AbstractFile) {
AbstractFile file = (AbstractFile) content; AbstractFile file = (AbstractFile) content;
@ -386,7 +389,8 @@ class TableReportGenerator {
NbBundle.getMessage(this.getClass(), "ReportGenerator.tagTable.header.resultType"), NbBundle.getMessage(this.getClass(), "ReportGenerator.tagTable.header.resultType"),
NbBundle.getMessage(this.getClass(), "ReportGenerator.tagTable.header.tag"), NbBundle.getMessage(this.getClass(), "ReportGenerator.tagTable.header.tag"),
NbBundle.getMessage(this.getClass(), "ReportGenerator.tagTable.header.comment"), NbBundle.getMessage(this.getClass(), "ReportGenerator.tagTable.header.comment"),
NbBundle.getMessage(this.getClass(), "ReportGenerator.tagTable.header.srcFile")))); NbBundle.getMessage(this.getClass(), "ReportGenerator.tagTable.header.srcFile"),
NbBundle.getMessage(this.getClass(), "ReportGenerator.tagTable.header.userName"))));
// Give the modules the rows for the content tags. // Give the modules the rows for the content tags.
for (BlackboardArtifactTag tag : tags) { for (BlackboardArtifactTag tag : tags) {
@ -396,7 +400,8 @@ class TableReportGenerator {
} }
List<String> row; List<String> row;
row = new ArrayList<>(Arrays.asList(tag.getArtifact().getArtifactTypeName(), tag.getName().getDisplayName() + notableString, tag.getComment(), tag.getContent().getName())); row = new ArrayList<>(Arrays.asList(tag.getArtifact().getArtifactTypeName(), tag.getName().getDisplayName() + notableString,
tag.getComment(), tag.getContent().getName(), tag.getUserName()));
tableReport.addRow(row); tableReport.addRow(row);
// check if the tag is an image that we should later make a thumbnail for // check if the tag is an image that we should later make a thumbnail for
@ -582,7 +587,7 @@ class TableReportGenerator {
adHocCountQuery += " AND (art.artifact_id = tag.artifact_id) AND (tag.tag_name_id IN (" + tagIDList + ")) "; //NON-NLS adHocCountQuery += " AND (art.artifact_id = tag.artifact_id) AND (tag.tag_name_id IN (" + tagIDList + ")) "; //NON-NLS
} }
adHocCountQuery += "EXCEPT " + // NON-NLS adHocCountQuery += "EXCEPT " + // NON-NLS
"SELECT art.artifact_id FROM blackboard_artifacts AS art, blackboard_attributes AS att1 WHERE (att1.artifact_id = art.artifact_id) AND (art.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID() + ") AND (att1.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID() + ")) "; //NON-NLS "SELECT art.artifact_id FROM blackboard_artifacts AS art, blackboard_attributes AS att1 WHERE (att1.artifact_id = art.artifact_id) AND (art.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID() + ") AND (att1.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID() + ")) AS adHocHits"; //NON-NLS
int adHocCount = 0; int adHocCount = 0;
try (SleuthkitCase.CaseDbQuery dbQuery = openCase.getSleuthkitCase().executeQuery(adHocCountQuery)) { try (SleuthkitCase.CaseDbQuery dbQuery = openCase.getSleuthkitCase().executeQuery(adHocCountQuery)) {
@ -600,7 +605,7 @@ class TableReportGenerator {
// Create the query to get the keyword list names // Create the query to get the keyword list names
if (openCase.getCaseType() == Case.CaseType.MULTI_USER_CASE) { if (openCase.getCaseType() == Case.CaseType.MULTI_USER_CASE) {
orderByClause = "ORDER BY convert_to(att.value_text, 'SQL_ASCII') ASC NULLS FIRST"; //NON-NLS orderByClause = "ORDER BY convert_to(list, 'SQL_ASCII') ASC NULLS FIRST"; //NON-NLS
} else { } else {
orderByClause = "ORDER BY list ASC"; //NON-NLS orderByClause = "ORDER BY list ASC"; //NON-NLS
} }
@ -621,8 +626,9 @@ class TableReportGenerator {
"AND (tag.tag_name_id IN (" + tagIDList + ")) "; //NON-NLS "AND (tag.tag_name_id IN (" + tagIDList + ")) "; //NON-NLS
} }
if (adHocCount > 0) { if (adHocCount > 0) {
keywordListQuery += " UNION SELECT \"\" AS list "; keywordListQuery += " UNION SELECT \'\' AS list ";
} }
keywordListQuery = "SELECT * FROM ( " + keywordListQuery + " ) kwListNames ";
keywordListQuery += "GROUP BY list " + orderByClause; //NON-NLS keywordListQuery += "GROUP BY list " + orderByClause; //NON-NLS
// Make the table of contents links for each list type // Make the table of contents links for each list type
@ -645,17 +651,17 @@ class TableReportGenerator {
BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getDisplayName())); BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getDisplayName()));
} catch (TskCoreException | SQLException ex) { } catch (TskCoreException | SQLException ex) {
errorList.add(NbBundle.getMessage(this.getClass(), "ReportGenerator.errList.failedQueryKWLists")); errorList.add(NbBundle.getMessage(this.getClass(), "ReportGenerator.errList.failedQueryKWLists"));
logger.log(Level.SEVERE, "Failed to query keyword lists: ", ex); //NON-NLS logger.log(Level.SEVERE, "Failed to query keyword lists with query " + keywordListQuery, ex); //NON-NLS
return; return;
} }
// Query for keywords, grouped by list // Query for keywords, grouped by list
if (openCase.getCaseType() == Case.CaseType.MULTI_USER_CASE) { if (openCase.getCaseType() == Case.CaseType.MULTI_USER_CASE) {
orderByClause = "ORDER BY convert_to(att3.value_text, 'SQL_ASCII') ASC NULLS FIRST, " //NON-NLS orderByClause = "ORDER BY convert_to(list, 'SQL_ASCII') ASC NULLS FIRST, " //NON-NLS
+ "convert_to(att1.value_text, 'SQL_ASCII') ASC NULLS FIRST, " //NON-NLS + "convert_to(keyword, 'SQL_ASCII') ASC NULLS FIRST, " //NON-NLS
+ "convert_to(f.parent_path, 'SQL_ASCII') ASC NULLS FIRST, " //NON-NLS + "convert_to(parent_path, 'SQL_ASCII') ASC NULLS FIRST, " //NON-NLS
+ "convert_to(f.name, 'SQL_ASCII') ASC NULLS FIRST, " //NON-NLS + "convert_to(name, 'SQL_ASCII') ASC NULLS FIRST, " //NON-NLS
+ "convert_to(att2.value_text, 'SQL_ASCII') ASC NULLS FIRST"; //NON-NLS + "convert_to(preview, 'SQL_ASCII') ASC NULLS FIRST"; //NON-NLS
} else { } else {
orderByClause = "ORDER BY list ASC, keyword ASC, parent_path ASC, name ASC, preview ASC"; //NON-NLS orderByClause = "ORDER BY list ASC, keyword ASC, parent_path ASC, name ASC, preview ASC"; //NON-NLS
} }
@ -684,7 +690,7 @@ class TableReportGenerator {
// Query for keywords that are not part of a list // Query for keywords that are not part of a list
String keywordAdHocQuery = String keywordAdHocQuery =
"SELECT art.artifact_id AS artifact_id, art.obj_id AS obj_id, att1.value_text AS keyword, att2.value_text AS preview, \"\" AS list, f.name AS name, f.parent_path AS parent_path " + // NON-NLS "SELECT art.artifact_id AS artifact_id, art.obj_id AS obj_id, att1.value_text AS keyword, att2.value_text AS preview, \'\' AS list, f.name AS name, f.parent_path AS parent_path " + // NON-NLS
"FROM blackboard_artifacts AS art, blackboard_attributes AS att1, blackboard_attributes AS att2, tsk_files AS f " + // NON-NLS "FROM blackboard_artifacts AS art, blackboard_attributes AS att1, blackboard_attributes AS att2, tsk_files AS f " + // NON-NLS
"WHERE " + // NON-NLS "WHERE " + // NON-NLS
" (art.artifact_id IN (SELECT art.artifact_id FROM blackboard_artifacts AS art, blackboard_attributes AS att1 WHERE (att1.artifact_id = art.artifact_id) AND (art.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID() + ") " + // NON-NLS " (art.artifact_id IN (SELECT art.artifact_id FROM blackboard_artifacts AS art, blackboard_attributes AS att1 WHERE (att1.artifact_id = art.artifact_id) AND (art.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID() + ") " + // NON-NLS
@ -697,7 +703,7 @@ class TableReportGenerator {
"AND (att2.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW.getTypeID() + ") " + // NON-NLS "AND (att2.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW.getTypeID() + ") " + // NON-NLS
"AND (art.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID() + ") "; // NON-NLS "AND (art.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID() + ") "; // NON-NLS
String keywordsQuery = keywordListsQuery + " UNION " + keywordAdHocQuery + orderByClause; String keywordsQuery = "SELECT * FROM ( " + keywordListsQuery + " UNION " + keywordAdHocQuery + " ) kwHits " + orderByClause;
try (SleuthkitCase.CaseDbQuery dbQuery = openCase.getSleuthkitCase().executeQuery(keywordsQuery)) { try (SleuthkitCase.CaseDbQuery dbQuery = openCase.getSleuthkitCase().executeQuery(keywordsQuery)) {
ResultSet resultSet = dbQuery.getResultSet(); ResultSet resultSet = dbQuery.getResultSet();
@ -770,7 +776,7 @@ class TableReportGenerator {
tableModule.endDataType(); tableModule.endDataType();
} catch (TskCoreException | SQLException ex) { } catch (TskCoreException | SQLException ex) {
errorList.add(NbBundle.getMessage(this.getClass(), "ReportGenerator.errList.failedQueryKWs")); errorList.add(NbBundle.getMessage(this.getClass(), "ReportGenerator.errList.failedQueryKWs"));
logger.log(Level.SEVERE, "Failed to query keywords: ", ex); //NON-NLS logger.log(Level.SEVERE, "Failed to query keywords with query " + keywordsQuery, ex); //NON-NLS
} }
} }

View File

@ -0,0 +1,272 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2018-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.sqlitereader;
import java.io.File;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.casemodule.services.FileManager;
import org.sleuthkit.autopsy.casemodule.services.Services;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Reads rows from SQLite tables and returns results in a list collection.
*/
public class SQLiteReader implements AutoCloseable {
private final Connection connection;
/**
* Writes data source file contents to local disk and opens a sqlite JDBC
* connection.
*
* @param sqliteDbFile Data source abstract file
* @param localDiskPath Location for database contents to be copied to
* @throws ClassNotFoundException missing SQLite JDBC class
* @throws SQLException Exception opening JDBC connection
* @throws IOException Exception writing file contents
* @throws NoCurrentCaseException Current case closed during file copying
* @throws TskCoreException Exception finding files from abstract file
*/
public SQLiteReader(AbstractFile sqliteDbFile, String localDiskPath) throws ClassNotFoundException,
SQLException, IOException, NoCurrentCaseException, TskCoreException{
writeDataSourceToLocalDisk(sqliteDbFile, localDiskPath);
connection = getDatabaseConnection(localDiskPath);
}
/**
* Copies the data source file contents to local drive for processing.
*
* @param file AbstractFile from the data source
* @param localDiskPath Local drive path to copy AbstractFile contents
* @throws IOException Exception writing file contents
* @throws NoCurrentCaseException Current case closed during file copying
* @throws TskCoreException Exception finding files from abstract file
*/
private void writeDataSourceToLocalDisk(AbstractFile file, String localDiskPath)
throws IOException, NoCurrentCaseException, TskCoreException {
File localDatabaseFile = new File(localDiskPath);
if (!localDatabaseFile.exists()) {
ContentUtils.writeToFile(file, localDatabaseFile);
// Look for any meta files associated with this DB - WAL, SHM, etc.
findAndCopySQLiteMetaFile(file, file.getName() + "-wal");
findAndCopySQLiteMetaFile(file, file.getName() + "-shm");
}
}
/**
* Searches for a meta file associated with the give SQLite database. If found,
* copies the file to the local disk folder
*
* @param sqliteFile SQLIte db file being processed
* @param metaFileName name of meta file to look for
* @throws NoCurrentCaseException Case has been closed.
* @throws TskCoreException fileManager cannot find AbstractFile files.
* @throws IOException Issue during writing to file.
*/
private void findAndCopySQLiteMetaFile(AbstractFile sqliteFile,
String metaFileName) throws NoCurrentCaseException, TskCoreException, IOException {
Case openCase = Case.getCurrentCaseThrows();
SleuthkitCase sleuthkitCase = openCase.getSleuthkitCase();
Services services = new Services(sleuthkitCase);
FileManager fileManager = services.getFileManager();
List<AbstractFile> metaFiles = fileManager.findFiles(
sqliteFile.getDataSource(), metaFileName,
sqliteFile.getParent().getName());
if (metaFiles != null) {
for (AbstractFile metaFile : metaFiles) {
String tmpMetafilePathName = openCase.getTempDirectory() +
File.separator + metaFile.getName();
File tmpMetafile = new File(tmpMetafilePathName);
ContentUtils.writeToFile(metaFile, tmpMetafile);
}
}
}
/**
* Opens a JDBC connection to the sqlite database specified by the path
* parameter.
*
* @param databasePath Local path of sqlite database
* @return Connection JDBC connection, to be maintained and closed by the reader
* @throws ClassNotFoundException missing SQLite JDBC class
* @throws SQLException Exception during opening database connection
*/
private Connection getDatabaseConnection(String databasePath)
throws ClassNotFoundException, SQLException {
// Load the SQLite JDBC driver, if necessary.
Class.forName("org.sqlite.JDBC"); //NON-NLS
return DriverManager.getConnection(
"jdbc:sqlite:" + databasePath); //NON-NLS
}
/**
* Retrieves a map view of table names to table schemas (in the form of
* CREATE TABLE statments).
*
* @return A map of table names to table schemas
* @throws SQLException
*/
public Map<String, String> getTableSchemas()
throws SQLException {
Map<String, String> dbTablesMap = new TreeMap<>();
try (Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(
"SELECT name, sql FROM sqlite_master " //NON-NLS
+ " WHERE type= 'table' " //NON-NLS
+ " ORDER BY name;")){ //NON-NLS
while (resultSet.next()) {
String tableName = resultSet.getString("name"); //NON-NLS
String tableSQL = resultSet.getString("sql"); //NON-NLS
dbTablesMap.put(tableName, tableSQL);
}
}
return dbTablesMap;
}
/**
* Retrieves the total number of rows from a table in the SQLite database.
*
* @param tableName
* @return Row count from tableName
* @throws SQLException
*/
public Integer getTableRowCount(String tableName) throws SQLException {
try (Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(
"SELECT count (*) as count FROM " + tableName)){ //NON-NLS
return resultSet.getInt("count"); //NON-NLS
}
}
/**
* Retrieves all rows from a given table in the SQLite database. If only a
* subset of rows are desired, see the overloaded function below.
*
* @param tableName
* @return List of rows, where each row is
* represented as a column-value map.
* @throws SQLException
*/
public List<Map<String, Object>> getRowsFromTable(String tableName) throws SQLException {
//This method does not directly call its overloaded counterpart
//since the second parameter would need to be retreived from a call to
//getTableRowCount().
try(Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(
"SELECT * FROM " + tableName)) { //NON-NLS
return resultSetToList(resultSet);
}
}
/**
* Retrieves a subset of the rows from a given table in the SQLite database.
*
* @param tableName
* @param startRow Desired start index (rows begin at 1)
* @param numRowsToRead Number of rows past the start index
* @return List of rows, where each row is
* represented as a column-value map.
* @throws SQLException
*/
public List<Map<String, Object>> getRowsFromTable(String tableName,
int startRow, int numRowsToRead) throws SQLException{
try(Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(
"SELECT * FROM " + tableName //NON-NLS
+ " LIMIT " + Integer.toString(numRowsToRead) //NON-NLS
+ " OFFSET " + Integer.toString(startRow - 1))) { //NON-NLS
return resultSetToList(resultSet);
}
}
/**
* Converts a ResultSet (row results from a table read) into a list.
*
* @param resultSet row results from a table read
* @return List of rows, where each row is
* represented as a column-value map.
* @throws SQLException occurs if ResultSet is closed while attempting to
* access it's data.
*/
@NbBundle.Messages("SQLiteReader.BlobNotShown.message=BLOB Data not shown")
private List<Map<String, Object>> resultSetToList(ResultSet resultSet) throws SQLException {
ResultSetMetaData metaData = resultSet.getMetaData();
int columns = metaData.getColumnCount();
List<Map<String, Object>> rowMap = new ArrayList<>();
while (resultSet.next()) {
Map<String, Object> row = new LinkedHashMap<>(columns);
for (int i = 1; i <= columns; ++i) {
if (resultSet.getObject(i) == null) {
row.put(metaData.getColumnName(i), "");
} else {
if (metaData.getColumnTypeName(i).compareToIgnoreCase("blob") == 0) {
row.put(metaData.getColumnName(i), Bundle.SQLiteReader_BlobNotShown_message());
} else {
row.put(metaData.getColumnName(i), resultSet.getObject(i));
}
}
}
rowMap.add(row);
}
return rowMap;
}
/**
* Closes underlying JDBC connection.
*
* @throws SQLException
*/
@Override
public void close() throws SQLException {
connection.close();
}
}

View File

@ -567,8 +567,11 @@ class ListTimeline extends BorderPane {
super.updateItem(item, empty); super.updateItem(item, empty);
if (empty || item == null) { if (empty || item == null) {
setText(null); setText(null);
setTooltip(null);
} else { } else {
setText(textSupplier.apply(getEvent())); String text = textSupplier.apply(getEvent());
setText(text);
setTooltip(new Tooltip(text));
} }
} }
} }

View File

@ -23,6 +23,7 @@ import java.nio.file.Paths;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import junit.framework.Test; import junit.framework.Test;
import org.netbeans.junit.NbModuleSuite; import org.netbeans.junit.NbModuleSuite;
import org.netbeans.junit.NbTestCase; import org.netbeans.junit.NbTestCase;
@ -37,6 +38,7 @@ import org.sleuthkit.autopsy.testutils.CaseUtils;
import org.sleuthkit.autopsy.testutils.IngestUtils; import org.sleuthkit.autopsy.testutils.IngestUtils;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
/** /**
@ -45,7 +47,7 @@ import org.sleuthkit.datamodel.TskCoreException;
public class EmbeddedFileTest extends NbTestCase { public class EmbeddedFileTest extends NbTestCase {
private static final String CASE_NAME = "EmbeddedFileTest"; private static final String CASE_NAME = "EmbeddedFileTest";
private final Path IMAGE_PATH = Paths.get(this.getDataDir().toString(), "EmbeddedIM_img1_v1.vhd"); private final Path IMAGE_PATH = Paths.get(this.getDataDir().toString(), "EmbeddedIM_img1_v2.vhd");
public static final String HASH_VALUE = "098f6bcd4621d373cade4e832627b4f6"; public static final String HASH_VALUE = "098f6bcd4621d373cade4e832627b4f6";
private static final int DEEP_FOLDER_COUNT = 25; private static final int DEEP_FOLDER_COUNT = 25;
private Case openCase; private Case openCase;
@ -92,31 +94,47 @@ public class EmbeddedFileTest extends NbTestCase {
CaseUtils.closeCurrentCase(testSucceeded); CaseUtils.closeCurrentCase(testSucceeded);
} }
public void testEncryption() { public void testEncryptionAndZipBomb() {
try { try {
List<AbstractFile> results = openCase.getSleuthkitCase().findAllFilesWhere("name LIKE '%%'"); List<AbstractFile> results = openCase.getSleuthkitCase().findAllFilesWhere("name LIKE '%%'");
String protectedName1 = "password_protected.zip"; final String zipBombSetName = "Possible Zip Bomb";
String protectedName2 = "level1_protected.zip"; final String protectedName1 = "password_protected.zip";
String protectedName3 = "42.zip"; final String protectedName2 = "level1_protected.zip";
assertEquals(2207, results.size()); final String protectedName3 = "42.zip";
final String depthZipBomb = "DepthTriggerZipBomb.zip";
final String ratioZipBomb = "RatioTriggerZipBomb.zip";
int zipBombs = 0;
assertEquals("The number of files in the test image has changed", 2221, results.size());
int passwdProtectedZips = 0; int passwdProtectedZips = 0;
for (AbstractFile file : results) { for (AbstractFile file : results) {
//.zip file has artifact TSK_ENCRYPTION_DETECTED //.zip file has artifact TSK_ENCRYPTION_DETECTED
if (file.getName().equalsIgnoreCase(protectedName1) || file.getName().equalsIgnoreCase(protectedName2) || file.getName().equalsIgnoreCase(protectedName3)){ if (file.getName().equalsIgnoreCase(protectedName1) || file.getName().equalsIgnoreCase(protectedName2) || file.getName().equalsIgnoreCase(protectedName3)){
ArrayList<BlackboardArtifact> artifacts = file.getAllArtifacts(); ArrayList<BlackboardArtifact> artifacts = file.getAllArtifacts();
assertEquals(1, artifacts.size()); assertEquals("Password protected zip file " + file.getName() + " has incorrect number of artifacts", 1, artifacts.size());
for (BlackboardArtifact artifact : artifacts) { for (BlackboardArtifact artifact : artifacts) {
assertEquals(artifact.getArtifactTypeID(), BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED.getTypeID()); assertEquals("Artifact for password protected zip file " + file.getName() + " has incorrect type ID", artifact.getArtifactTypeID(), BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED.getTypeID());
passwdProtectedZips++; passwdProtectedZips++;
} }
} else if (file.getName().equalsIgnoreCase(depthZipBomb) || file.getName().equalsIgnoreCase(ratioZipBomb)){
ArrayList<BlackboardArtifact> artifacts = file.getAllArtifacts();
assertEquals("Zip bomb " + file.getName() + " has incorrect number of artifacts", 1, artifacts.size());
for (BlackboardArtifact artifact : artifacts) {
assertEquals("Artifact for Zip bomb " + file.getName() + " has incorrect type ID", artifact.getArtifactTypeID(), BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID());
BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME));
assertNotNull("No attribute found for artifact on zip bomb " + file.getName(), attribute);
assertEquals("Interesting artifact on file, " + file.getName() + ", does not reflect it being a zip bomb", zipBombSetName, attribute.getDisplayString());
zipBombs++;
}
} else {//No other files have artifact defined } else {//No other files have artifact defined
assertEquals(0, file.getAllArtifacts().size()); assertEquals("Unexpected file, " + file.getName() + ", has artifacts", 0, file.getAllArtifacts().size());
} }
} }
//Make sure 3 password protected zip files have been tested: password_protected.zip, level1_protected.zip and 42.zip that we download for bomb testing. //Make sure 3 password protected zip files have been tested: password_protected.zip, level1_protected.zip and 42.zip that we download for bomb testing.
assertEquals(3, passwdProtectedZips); assertEquals("Unexpected number of artifacts reflecting password protected zip files found", 3, passwdProtectedZips);
//Make sure 2 zip bomb files have been tested: DepthTriggerZipBomb.zip and RatioTriggerZipBomb.zip.
assertEquals("Unexpected number of artifacts reflecting zip bombs found", 2, zipBombs);
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
Exceptions.printStackTrace(ex); Exceptions.printStackTrace(ex);
Assert.fail(ex); Assert.fail(ex);

View File

@ -1,8 +1,8 @@
Manifest-Version: 1.0 Manifest-Version: 1.0
OpenIDE-Module: org.sleuthkit.autopsy.corelibs/3 OpenIDE-Module: org.sleuthkit.autopsy.corelibs/3
OpenIDE-Module-Implementation-Version: 4 OpenIDE-Module-Implementation-Version: 5
OpenIDE-Module-Localizing-Bundle: org/sleuthkit/autopsy/corelibs/Bundle.properties OpenIDE-Module-Localizing-Bundle: org/sleuthkit/autopsy/corelibs/Bundle.properties
OpenIDE-Module-Specification-Version: 1.1 OpenIDE-Module-Specification-Version: 1.2
AutoUpdate-Show-In-Client: true AutoUpdate-Show-In-Client: true
AutoUpdate-Essential-Module: true AutoUpdate-Essential-Module: true

View File

@ -135,7 +135,7 @@
<compile-dependency/> <compile-dependency/>
<run-dependency> <run-dependency>
<release-version>10</release-version> <release-version>10</release-version>
<specification-version>10.11</specification-version> <specification-version>10.12</specification-version>
</run-dependency> </run-dependency>
</dependency> </dependency>
<dependency> <dependency>
@ -144,7 +144,7 @@
<compile-dependency/> <compile-dependency/>
<run-dependency> <run-dependency>
<release-version>3</release-version> <release-version>3</release-version>
<specification-version>1.1</specification-version> <specification-version>1.2</specification-version>
</run-dependency> </run-dependency>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -326,7 +326,7 @@ final class AutoIngestJobsNode extends AbstractNode {
ss.put(new NodeProperty<>(Bundle.AutoIngestJobsNode_jobCreated_text(), Bundle.AutoIngestJobsNode_jobCreated_text(), Bundle.AutoIngestJobsNode_jobCreated_text(), ss.put(new NodeProperty<>(Bundle.AutoIngestJobsNode_jobCreated_text(), Bundle.AutoIngestJobsNode_jobCreated_text(), Bundle.AutoIngestJobsNode_jobCreated_text(),
jobWrapper.getManifest().getDateFileCreated())); jobWrapper.getManifest().getDateFileCreated()));
ss.put(new NodeProperty<>(Bundle.AutoIngestJobsNode_priority_text(), Bundle.AutoIngestJobsNode_priority_text(), Bundle.AutoIngestJobsNode_priority_text(), ss.put(new NodeProperty<>(Bundle.AutoIngestJobsNode_priority_text(), Bundle.AutoIngestJobsNode_priority_text(), Bundle.AutoIngestJobsNode_priority_text(),
jobWrapper.getPriority() > 0 ? Bundle.AutoIngestJobsNode_prioritized_true() : Bundle.AutoIngestJobsNode_prioritized_false())); jobWrapper.getPriority()));
break; break;
case RUNNING_JOB: case RUNNING_JOB:
AutoIngestJob.StageDetails status = jobWrapper.getProcessingStageDetails(); AutoIngestJob.StageDetails status = jobWrapper.getProcessingStageDetails();

View File

@ -31,6 +31,7 @@ import org.sleuthkit.autopsy.datamodel.EmptyNode;
import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestJobsNode.AutoIngestJobStatus; import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestJobsNode.AutoIngestJobStatus;
import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestJobsNode.JobNode; import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestJobsNode.JobNode;
import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestNodeRefreshEvents.AutoIngestRefreshEvent; import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestNodeRefreshEvents.AutoIngestRefreshEvent;
import org.sleuthkit.autopsy.guiutils.StatusIconCellRenderer;
/** /**
* A panel which displays an outline view with all jobs for a specified status. * A panel which displays an outline view with all jobs for a specified status.
@ -84,6 +85,7 @@ final class AutoIngestJobsPanel extends javax.swing.JPanel implements ExplorerMa
indexOfColumn = getColumnIndexByName(Bundle.AutoIngestJobsNode_priority_text()); indexOfColumn = getColumnIndexByName(Bundle.AutoIngestJobsNode_priority_text());
if (indexOfColumn != INVALID_INDEX) { if (indexOfColumn != INVALID_INDEX) {
outline.getColumnModel().getColumn(indexOfColumn).setPreferredWidth(INITIAL_PRIORITIZED_WIDTH); outline.getColumnModel().getColumn(indexOfColumn).setPreferredWidth(INITIAL_PRIORITIZED_WIDTH);
outline.getColumnModel().getColumn(indexOfColumn).setCellRenderer(new PrioritizedIconCellRenderer());
} }
break; break;
case RUNNING_JOB: case RUNNING_JOB:
@ -108,6 +110,7 @@ final class AutoIngestJobsPanel extends javax.swing.JPanel implements ExplorerMa
indexOfColumn = getColumnIndexByName(Bundle.AutoIngestJobsNode_status_text()); indexOfColumn = getColumnIndexByName(Bundle.AutoIngestJobsNode_status_text());
if (indexOfColumn != INVALID_INDEX) { if (indexOfColumn != INVALID_INDEX) {
outline.getColumnModel().getColumn(indexOfColumn).setPreferredWidth(INITIAL_STATUS_WIDTH); outline.getColumnModel().getColumn(indexOfColumn).setPreferredWidth(INITIAL_STATUS_WIDTH);
outline.getColumnModel().getColumn(indexOfColumn).setCellRenderer(new StatusIconCellRenderer());
} }
break; break;
default: default:

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2017 Basis Technology Corp. * Copyright 2017-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -19,21 +19,22 @@
package org.sleuthkit.autopsy.experimental.autoingest; package org.sleuthkit.autopsy.experimental.autoingest;
import java.awt.Component; import java.awt.Component;
import java.lang.reflect.InvocationTargetException;
import javax.swing.ImageIcon; import javax.swing.ImageIcon;
import javax.swing.JTable; import javax.swing.JTable;
import static javax.swing.SwingConstants.CENTER; import static javax.swing.SwingConstants.CENTER;
import org.openide.nodes.Node;
import org.openide.util.ImageUtilities; import org.openide.util.ImageUtilities;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.guiutils.GrayableCellRenderer; import org.sleuthkit.autopsy.guiutils.GrayableCellRenderer;
import org.sleuthkit.autopsy.guiutils.StatusIconCellRenderer; import org.sleuthkit.autopsy.datamodel.NodeProperty;
/** /**
* A JTable cell renderer that represents whether the priority value of a job * A JTable and Outline view cell renderer that represents whether the priority
* has ever been increased, tick if prioritized nothing if not. * value of a job has ever been increased, tick if prioritized nothing if not.
*/ */
class PrioritizedIconCellRenderer extends GrayableCellRenderer { class PrioritizedIconCellRenderer extends GrayableCellRenderer {
@Messages({ @Messages({
"PrioritizedIconCellRenderer.prioritized.tooltiptext=This job has been prioritized. The most recently prioritized job should be processed next.", "PrioritizedIconCellRenderer.prioritized.tooltiptext=This job has been prioritized. The most recently prioritized job should be processed next.",
"PrioritizedIconCellRenderer.notPrioritized.tooltiptext=This job has not been prioritized." "PrioritizedIconCellRenderer.notPrioritized.tooltiptext=This job has not been prioritized."
@ -44,13 +45,25 @@ class PrioritizedIconCellRenderer extends GrayableCellRenderer {
@Override @Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
setHorizontalAlignment(CENTER); setHorizontalAlignment(CENTER);
if ((value instanceof Integer)) { Object switchValue = null;
if ((int) value == 0) { if ((value instanceof NodeProperty)) {
setIcon(null); //The Outline view has properties in the cell, the value contained in the property is what we want
setToolTipText(org.openide.util.NbBundle.getMessage(PrioritizedIconCellRenderer.class, "PrioritizedIconCellRenderer.notPrioritized.tooltiptext")); try {
switchValue = ((Node.Property) value).getValue();
} catch (IllegalAccessException | InvocationTargetException ignored) {
//Unable to get the value from the NodeProperty no Icon will be displayed
}
} else { } else {
//JTables contain the value we want directly in the cell
switchValue = value;
}
if (switchValue instanceof Integer && (int) switchValue != 0) {
setIcon(checkedIcon); setIcon(checkedIcon);
setToolTipText(org.openide.util.NbBundle.getMessage(PrioritizedIconCellRenderer.class, "PrioritizedIconCellRenderer.prioritized.tooltiptext")); setToolTipText(org.openide.util.NbBundle.getMessage(PrioritizedIconCellRenderer.class, "PrioritizedIconCellRenderer.prioritized.tooltiptext"));
} else {
setIcon(null);
if (switchValue instanceof Integer) {
setToolTipText(org.openide.util.NbBundle.getMessage(PrioritizedIconCellRenderer.class, "PrioritizedIconCellRenderer.notPrioritized.tooltiptext"));
} }
} }
grayCellIfTableNotEnabled(table, isSelected); grayCellIfTableNotEnabled(table, isSelected);

View File

@ -19,14 +19,11 @@
package org.sleuthkit.autopsy.experimental.objectdetection; package org.sleuthkit.autopsy.experimental.objectdetection;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.logging.Level; import java.util.logging.Level;
import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.opencv.core.CvException; import org.opencv.core.CvException;
import org.opencv.core.Mat; import org.opencv.core.Mat;
import org.opencv.core.MatOfByte; import org.opencv.core.MatOfByte;
@ -52,7 +49,6 @@ import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_OBJECT_DETECTED; import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_OBJECT_DETECTED;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.ReadContentInputStream;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
/** /**
@ -61,6 +57,7 @@ import org.sleuthkit.datamodel.TskCoreException;
public class ObjectDetectectionFileIngestModule extends FileIngestModuleAdapter { public class ObjectDetectectionFileIngestModule extends FileIngestModuleAdapter {
private final static Logger logger = Logger.getLogger(ObjectDetectectionFileIngestModule.class.getName()); private final static Logger logger = Logger.getLogger(ObjectDetectectionFileIngestModule.class.getName());
private final static int MAX_FILE_SIZE = 100000000; //Max size of pictures to perform object detection on
private static final IngestModuleReferenceCounter refCounter = new IngestModuleReferenceCounter(); private static final IngestModuleReferenceCounter refCounter = new IngestModuleReferenceCounter();
private long jobId; private long jobId;
private Map<String, CascadeClassifier> classifiers; private Map<String, CascadeClassifier> classifiers;
@ -101,14 +98,23 @@ public class ObjectDetectectionFileIngestModule extends FileIngestModuleAdapter
public ProcessResult process(AbstractFile file) { public ProcessResult process(AbstractFile file) {
if (!classifiers.isEmpty() && ImageUtils.isImageThumbnailSupported(file)) { if (!classifiers.isEmpty() && ImageUtils.isImageThumbnailSupported(file)) {
//Any image we can create a thumbnail for is one we should apply the classifiers to //Any image we can create a thumbnail for is one we should apply the classifiers to
InputStream inputStream = new ReadContentInputStream(file);
byte[] imageInMemory; if (file.getSize() > MAX_FILE_SIZE) {
//prevent it from allocating gigabytes of memory for extremely large files
logger.log(Level.INFO, "Encountered file " + file.getParentPath() + file.getName() + " with object id of "
+ file.getId() + " which exceeds max file size of " + MAX_FILE_SIZE + " bytes, with a size of " + file.getSize());
return IngestModule.ProcessResult.OK;
}
byte[] imageInMemory = new byte[(int) file.getSize()];
try { try {
imageInMemory = IOUtils.toByteArray(inputStream); file.read(imageInMemory, 0, file.getSize());
} catch (IOException ex) { } catch (TskCoreException ex) {
logger.log(Level.WARNING, "Unable to read image to byte array for performing object detection on " + file.getParentPath() + file.getName() + " with object id of " + file.getId(), ex); logger.log(Level.WARNING, "Unable to read image to byte array for performing object detection on " + file.getParentPath() + file.getName() + " with object id of " + file.getId(), ex);
return IngestModule.ProcessResult.ERROR; return IngestModule.ProcessResult.ERROR;
} }
Mat originalImage; Mat originalImage;
try { try {
originalImage = Highgui.imdecode(new MatOfByte(imageInMemory), Highgui.IMREAD_GRAYSCALE); originalImage = Highgui.imdecode(new MatOfByte(imageInMemory), Highgui.IMREAD_GRAYSCALE);
@ -121,7 +127,6 @@ public class ObjectDetectectionFileIngestModule extends FileIngestModuleAdapter
logger.log(Level.SEVERE, "Unexpected Exception encountered attempting to use OpenCV to decode picture: " + file.getParentPath() + file.getName() + " with object id of " + file.getId(), unexpectedException); logger.log(Level.SEVERE, "Unexpected Exception encountered attempting to use OpenCV to decode picture: " + file.getParentPath() + file.getName() + " with object id of " + file.getId(), unexpectedException);
return IngestModule.ProcessResult.ERROR; return IngestModule.ProcessResult.ERROR;
} }
MatOfRect detectionRectangles = new MatOfRect(); //the rectangles which reprent the coordinates on the image for where objects were detected MatOfRect detectionRectangles = new MatOfRect(); //the rectangles which reprent the coordinates on the image for where objects were detected
for (String classifierKey : classifiers.keySet()) { for (String classifierKey : classifiers.keySet()) {
//apply each classifier to the file //apply each classifier to the file
@ -132,7 +137,7 @@ public class ObjectDetectectionFileIngestModule extends FileIngestModuleAdapter
continue; continue;
} catch (Exception unexpectedException) { } catch (Exception unexpectedException) {
//hopefully an unnecessary generic exception catch but currently present to catch any exceptions OpenCv throws which may not be documented //hopefully an unnecessary generic exception catch but currently present to catch any exceptions OpenCv throws which may not be documented
logger.log(Level.SEVERE, "Unexpected Exception encountered for image " + file.getParentPath() + file.getName() + " with object id of " + file.getId() +" while trying to apply classifier " + classifierKey, unexpectedException); logger.log(Level.SEVERE, "Unexpected Exception encountered for image " + file.getParentPath() + file.getName() + " with object id of " + file.getId() + " while trying to apply classifier " + classifierKey, unexpectedException);
continue; continue;
} }
@ -163,10 +168,14 @@ public class ObjectDetectectionFileIngestModule extends FileIngestModuleAdapter
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Failed to create blackboard artifact for '%s'.", file.getParentPath() + file.getName()), ex); //NON-NLS logger.log(Level.SEVERE, String.format("Failed to create blackboard artifact for '%s'.", file.getParentPath() + file.getName()), ex); //NON-NLS
detectionRectangles.release();
originalImage.release();
return IngestModule.ProcessResult.ERROR; return IngestModule.ProcessResult.ERROR;
} }
} }
} }
detectionRectangles.release();
originalImage.release();
} }
return IngestModule.ProcessResult.OK; return IngestModule.ProcessResult.OK;

View File

@ -127,7 +127,7 @@
<compile-dependency/> <compile-dependency/>
<run-dependency> <run-dependency>
<release-version>10</release-version> <release-version>10</release-version>
<specification-version>10.11</specification-version> <specification-version>10.12</specification-version>
</run-dependency> </run-dependency>
</dependency> </dependency>
<dependency> <dependency>
@ -136,7 +136,7 @@
<compile-dependency/> <compile-dependency/>
<run-dependency> <run-dependency>
<release-version>3</release-version> <release-version>3</release-version>
<specification-version>1.1</specification-version> <specification-version>1.2</specification-version>
</run-dependency> </run-dependency>
</dependency> </dependency>
</module-dependencies> </module-dependencies>

View File

@ -1,7 +1,7 @@
Manifest-Version: 1.0 Manifest-Version: 1.0
AutoUpdate-Show-In-Client: true AutoUpdate-Show-In-Client: true
OpenIDE-Module: org.sleuthkit.autopsy.keywordsearch/6 OpenIDE-Module: org.sleuthkit.autopsy.keywordsearch/6
OpenIDE-Module-Implementation-Version: 19 OpenIDE-Module-Implementation-Version: 20
OpenIDE-Module-Install: org/sleuthkit/autopsy/keywordsearch/Installer.class OpenIDE-Module-Install: org/sleuthkit/autopsy/keywordsearch/Installer.class
OpenIDE-Module-Layer: org/sleuthkit/autopsy/keywordsearch/layer.xml OpenIDE-Module-Layer: org/sleuthkit/autopsy/keywordsearch/layer.xml
OpenIDE-Module-Localizing-Bundle: org/sleuthkit/autopsy/keywordsearch/Bundle.properties OpenIDE-Module-Localizing-Bundle: org/sleuthkit/autopsy/keywordsearch/Bundle.properties

View File

@ -119,7 +119,7 @@
<compile-dependency/> <compile-dependency/>
<run-dependency> <run-dependency>
<release-version>10</release-version> <release-version>10</release-version>
<specification-version>10.11</specification-version> <specification-version>10.12</specification-version>
</run-dependency> </run-dependency>
</dependency> </dependency>
<dependency> <dependency>
@ -128,7 +128,7 @@
<compile-dependency/> <compile-dependency/>
<run-dependency> <run-dependency>
<release-version>3</release-version> <release-version>3</release-version>
<specification-version>1.1</specification-version> <specification-version>1.2</specification-version>
</run-dependency> </run-dependency>
</dependency> </dependency>
</module-dependencies> </module-dependencies>

View File

@ -83,9 +83,17 @@ class AdHocSearchChildFactory extends ChildFactory<KeyValue> {
.collect(Collectors.toList()); .collect(Collectors.toList());
private final Collection<AdHocQueryRequest> queryRequests; private final Collection<AdHocQueryRequest> queryRequests;
private final boolean saveResults;
AdHocSearchChildFactory(Collection<AdHocQueryRequest> queryRequests) { /**
* Constructor
*
* @param queryRequests Query results
* @param saveResults Flag whether to save search results as KWS artifacts.
*/
AdHocSearchChildFactory(Collection<AdHocQueryRequest> queryRequests, boolean saveResults) {
this.queryRequests = queryRequests; this.queryRequests = queryRequests;
this.saveResults = saveResults;
} }
/** /**
@ -223,7 +231,7 @@ class AdHocSearchChildFactory extends ChildFactory<KeyValue> {
//cannot reuse snippet in BlackboardResultWriter //cannot reuse snippet in BlackboardResultWriter
//because for regex searches in UI we compress results by showing a content per regex once (even if multiple term hits) //because for regex searches in UI we compress results by showing a content per regex once (even if multiple term hits)
//whereas in bb we write every hit per content separately //whereas in bb we write every hit per content separately
new BlackboardResultWriter(queryResults, queryRequest.getKeywordList().getName()).execute(); new BlackboardResultWriter(queryResults, queryRequest.getKeywordList().getName(), saveResults).execute();
return true; return true;
} }
@ -396,10 +404,12 @@ class AdHocSearchChildFactory extends ChildFactory<KeyValue> {
private final KeywordSearchQuery query; private final KeywordSearchQuery query;
private final QueryResults hits; private final QueryResults hits;
private static final int QUERY_DISPLAY_LEN = 40; private static final int QUERY_DISPLAY_LEN = 40;
private final boolean saveResults;
BlackboardResultWriter(QueryResults hits, String listName) { BlackboardResultWriter(QueryResults hits, String listName, boolean saveResults) {
this.hits = hits; this.hits = hits;
this.query = hits.getQuery(); this.query = hits.getQuery();
this.saveResults = saveResults;
} }
protected void finalizeWorker() { protected void finalizeWorker() {
@ -414,7 +424,7 @@ class AdHocSearchChildFactory extends ChildFactory<KeyValue> {
final String queryDisp = queryStr.length() > QUERY_DISPLAY_LEN ? queryStr.substring(0, QUERY_DISPLAY_LEN - 1) + " ..." : queryStr; final String queryDisp = queryStr.length() > QUERY_DISPLAY_LEN ? queryStr.substring(0, QUERY_DISPLAY_LEN - 1) + " ..." : queryStr;
try { try {
progress = ProgressHandle.createHandle(NbBundle.getMessage(this.getClass(), "KeywordSearchResultFactory.progress.saving", queryDisp), () -> BlackboardResultWriter.this.cancel(true)); progress = ProgressHandle.createHandle(NbBundle.getMessage(this.getClass(), "KeywordSearchResultFactory.progress.saving", queryDisp), () -> BlackboardResultWriter.this.cancel(true));
hits.process(progress, null, this, false); hits.process(progress, null, this, false, saveResults);
} finally { } finally {
finalizeWorker(); finalizeWorker();
} }

View File

@ -74,8 +74,10 @@ class AdHocSearchDelegator {
/** /**
* Execute the keyword search based on keywords passed into constructor. * Execute the keyword search based on keywords passed into constructor.
* Post results into a new DataResultViewer. * Post results into a new DataResultViewer.
*
* @param saveResults Flag whether to save search results as KWS artifacts.
*/ */
public void execute() { public void execute(boolean saveResults) {
Collection<AdHocQueryRequest> queryRequests = new ArrayList<>(); Collection<AdHocQueryRequest> queryRequests = new ArrayList<>();
int queryID = 0; int queryID = 0;
StringBuilder queryConcat = new StringBuilder(); // concatenation of all query strings StringBuilder queryConcat = new StringBuilder(); // concatenation of all query strings
@ -95,7 +97,7 @@ class AdHocSearchDelegator {
Node rootNode; Node rootNode;
if (queryRequests.size() > 0) { if (queryRequests.size() > 0) {
Children childNodes = Children childNodes =
Children.create(new AdHocSearchChildFactory(queryRequests), true); Children.create(new AdHocSearchChildFactory(queryRequests, saveResults), true);
rootNode = new AbstractNode(childNodes); rootNode = new AbstractNode(childNodes);
} else { } else {

View File

@ -98,8 +98,10 @@ abstract class AdHocSearchPanel extends javax.swing.JPanel {
/** /**
* Performs the search using the selected keywords. Creates a * Performs the search using the selected keywords. Creates a
* DataResultTopComponent with the results. * DataResultTopComponent with the results.
*
* @param saveResults Flag whether to save search results as KWS artifacts.
*/ */
public void search() { public void search(boolean saveResults) {
boolean isIngestRunning = IngestManager.getInstance().isIngestRunning(); boolean isIngestRunning = IngestManager.getInstance().isIngestRunning();
if (filesIndexed == 0) { if (filesIndexed == 0) {
@ -138,7 +140,7 @@ abstract class AdHocSearchPanel extends javax.swing.JPanel {
AdHocSearchDelegator man = new AdHocSearchDelegator(keywordLists, getDataSourcesSelected()); AdHocSearchDelegator man = new AdHocSearchDelegator(keywordLists, getDataSourcesSelected());
if (man.validate()) { if (man.validate()) {
man.execute(); man.execute(saveResults);
} else { } else {
KeywordSearchUtil.displayDialog(keywordSearchErrorDialogHeader, NbBundle.getMessage(this.getClass(), KeywordSearchUtil.displayDialog(keywordSearchErrorDialogHeader, NbBundle.getMessage(this.getClass(),
"AbstractKeywordSearchPerformer.search.invalidSyntaxHeader"), KeywordSearchUtil.DIALOG_MESSAGE_TYPE.ERROR); "AbstractKeywordSearchPerformer.search.invalidSyntaxHeader"), KeywordSearchUtil.DIALOG_MESSAGE_TYPE.ERROR);

View File

@ -313,3 +313,7 @@ ExtractedContentPanel.pagesLabel.text=Page:
DropdownSingleTermSearchPanel.dataSourceCheckBox.text=Restrict search to the selected data sources: DropdownSingleTermSearchPanel.dataSourceCheckBox.text=Restrict search to the selected data sources:
DropdownListSearchPanel.dataSourceCheckBox.text=Restrict search to the selected data sources: DropdownListSearchPanel.dataSourceCheckBox.text=Restrict search to the selected data sources:
DropdownSingleTermSearchPanel.ingestIndexLabel.text=Files Indexed: DropdownSingleTermSearchPanel.ingestIndexLabel.text=Files Indexed:
DropdownSingleTermSearchPanel.jSaveSearchResults.toolTipText=Perform keyword search without saving the results in the form of keyword hit artifacts
DropdownSingleTermSearchPanel.jSaveSearchResults.text=Save search results
DropdownListSearchPanel.jSaveSearchResults.toolTipText=Perform keyword search without saving the results in the form of keyword hit artifacts
DropdownListSearchPanel.jSaveSearchResults.text=Save search results

View File

@ -23,38 +23,41 @@
<Layout> <Layout>
<DimensionLayout dim="0"> <DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Component id="jSplitPane1" alignment="0" pref="400" max="32767" attributes="0"/> <Component id="jSplitPane1" alignment="0" pref="0" max="32767" attributes="0"/>
<Component id="jScrollPane1" max="32767" attributes="0"/>
<Group type="102" attributes="0"> <Group type="102" attributes="0">
<EmptySpace min="-2" pref="4" max="-2" attributes="0"/> <Group type="103" groupAlignment="0" attributes="0">
<Component id="dataSourceCheckBox" min="-2" max="-2" attributes="0"/>
<Component id="jSaveSearchResults" alignment="0" min="-2" max="-2" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Component id="searchAddButton" linkSize="1" min="-2" max="-2" attributes="0"/> <Component id="searchAddButton" linkSize="1" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="manageListsButton" linkSize="1" min="-2" max="-2" attributes="0"/> <Component id="manageListsButton" linkSize="1" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/> <EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="ingestIndexLabel" min="-2" max="-2" attributes="0"/> <Component id="ingestIndexLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group> </Group>
<Group type="102" attributes="0">
<Component id="dataSourceCheckBox" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group> </Group>
<Component id="jScrollPane1" max="32767" attributes="0"/> <EmptySpace min="0" pref="120" max="32767" attributes="0"/>
</Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
<DimensionLayout dim="1"> <DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0">
<Component id="jSplitPane1" min="-2" pref="183" max="-2" attributes="0"/> <Component id="jSplitPane1" min="-2" pref="183" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="dataSourceCheckBox" min="-2" max="-2" attributes="0"/> <Component id="dataSourceCheckBox" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="jScrollPane1" min="-2" pref="65" max="-2" attributes="0"/> <Component id="jScrollPane1" min="-2" pref="65" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Component id="jSaveSearchResults" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0"> <Group type="103" groupAlignment="3" attributes="0">
<Component id="manageListsButton" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="manageListsButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="searchAddButton" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="searchAddButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="ingestIndexLabel" alignment="3" min="-2" pref="13" max="-2" attributes="0"/> <Component id="ingestIndexLabel" alignment="3" min="-2" pref="13" max="-2" attributes="0"/>
</Group> </Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace min="-2" pref="23" max="-2" attributes="0"/>
</Group> </Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
@ -226,5 +229,15 @@
</Component> </Component>
</SubComponents> </SubComponents>
</Container> </Container>
<Component class="javax.swing.JCheckBox" name="jSaveSearchResults">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="DropdownListSearchPanel.jSaveSearchResults.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="DropdownListSearchPanel.jSaveSearchResults.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents> </SubComponents>
</Form> </Form>

View File

@ -164,6 +164,8 @@ class DropdownListSearchPanel extends AdHocSearchPanel {
} }
listsTableModel.resync(); listsTableModel.resync();
updateIngestIndexLabel(); updateIngestIndexLabel();
jSaveSearchResults.setSelected(true);
} }
private void updateIngestIndexLabel() { private void updateIngestIndexLabel() {
@ -206,6 +208,7 @@ class DropdownListSearchPanel extends AdHocSearchPanel {
dataSourceCheckBox = new javax.swing.JCheckBox(); dataSourceCheckBox = new javax.swing.JCheckBox();
jScrollPane1 = new javax.swing.JScrollPane(); jScrollPane1 = new javax.swing.JScrollPane();
dataSourceList = new javax.swing.JList<>(); dataSourceList = new javax.swing.JList<>();
jSaveSearchResults = new javax.swing.JCheckBox();
setFont(getFont().deriveFont(getFont().getStyle() & ~java.awt.Font.BOLD, 11)); setFont(getFont().deriveFont(getFont().getStyle() & ~java.awt.Font.BOLD, 11));
@ -265,23 +268,26 @@ class DropdownListSearchPanel extends AdHocSearchPanel {
dataSourceList.setMinimumSize(new java.awt.Dimension(0, 200)); dataSourceList.setMinimumSize(new java.awt.Dimension(0, 200));
jScrollPane1.setViewportView(dataSourceList); jScrollPane1.setViewportView(dataSourceList);
jSaveSearchResults.setText(org.openide.util.NbBundle.getMessage(DropdownListSearchPanel.class, "DropdownListSearchPanel.jSaveSearchResults.text")); // NOI18N
jSaveSearchResults.setToolTipText(org.openide.util.NbBundle.getMessage(DropdownListSearchPanel.class, "DropdownListSearchPanel.jSaveSearchResults.toolTipText")); // NOI18N
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout); this.setLayout(layout);
layout.setHorizontalGroup( layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jSplitPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE) .addComponent(jSplitPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)
.addComponent(jScrollPane1)
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(dataSourceCheckBox)
.addComponent(jSaveSearchResults)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addGap(4, 4, 4)
.addComponent(searchAddButton) .addComponent(searchAddButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(manageListsButton) .addComponent(manageListsButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(ingestIndexLabel) .addComponent(ingestIndexLabel)))
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addGap(0, 120, Short.MAX_VALUE))
.addGroup(layout.createSequentialGroup()
.addComponent(dataSourceCheckBox)
.addGap(0, 0, Short.MAX_VALUE))
.addComponent(jScrollPane1)
); );
layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {manageListsButton, searchAddButton}); layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {manageListsButton, searchAddButton});
@ -290,16 +296,18 @@ class DropdownListSearchPanel extends AdHocSearchPanel {
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addComponent(jSplitPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 183, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(jSplitPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 183, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(dataSourceCheckBox) .addComponent(dataSourceCheckBox)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 65, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 65, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(jSaveSearchResults)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(manageListsButton) .addComponent(manageListsButton)
.addComponent(searchAddButton) .addComponent(searchAddButton)
.addComponent(ingestIndexLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 13, javax.swing.GroupLayout.PREFERRED_SIZE)) .addComponent(ingestIndexLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 13, javax.swing.GroupLayout.PREFERRED_SIZE))
.addContainerGap()) .addGap(23, 23, 23))
); );
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
@ -319,6 +327,7 @@ class DropdownListSearchPanel extends AdHocSearchPanel {
private javax.swing.JCheckBox dataSourceCheckBox; private javax.swing.JCheckBox dataSourceCheckBox;
private javax.swing.JList<String> dataSourceList; private javax.swing.JList<String> dataSourceList;
private javax.swing.JLabel ingestIndexLabel; private javax.swing.JLabel ingestIndexLabel;
private javax.swing.JCheckBox jSaveSearchResults;
private javax.swing.JScrollPane jScrollPane1; private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JSplitPane jSplitPane1; private javax.swing.JSplitPane jSplitPane1;
private javax.swing.JTable keywordsTable; private javax.swing.JTable keywordsTable;
@ -333,7 +342,7 @@ class DropdownListSearchPanel extends AdHocSearchPanel {
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
try { try {
search(); search(jSaveSearchResults.isSelected());
} finally { } finally {
setCursor(null); setCursor(null);
} }

View File

@ -59,21 +59,22 @@
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="5" max="-2" attributes="0"/> <EmptySpace min="-2" pref="5" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0"> <Group type="102" attributes="0">
<Component id="exactRadioButton" min="-2" max="-2" attributes="0"/> <Component id="exactRadioButton" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/> <EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="substringRadioButton" min="-2" max="-2" attributes="0"/> <Component id="substringRadioButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="regexRadioButton" min="-2" max="-2" attributes="0"/> <Component id="regexRadioButton" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<Component id="dataSourceCheckBox" min="-2" max="-2" attributes="0"/>
<Component id="jScrollPane1" min="-2" pref="297" max="-2" attributes="0"/> <Component id="jScrollPane1" min="-2" pref="297" max="-2" attributes="0"/>
<Component id="jSaveSearchResults" min="-2" max="-2" attributes="0"/>
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0">
<Component id="searchButton" min="-2" max="-2" attributes="0"/> <Component id="searchButton" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/> <EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="ingestIndexLabel" min="-2" max="-2" attributes="0"/> <Component id="ingestIndexLabel" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<Component id="keywordTextField" alignment="0" min="-2" pref="296" max="-2" attributes="0"/> <Component id="keywordTextField" alignment="0" min="-2" pref="296" max="-2" attributes="0"/>
<Component id="dataSourceCheckBox" alignment="0" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<EmptySpace max="32767" attributes="0"/> <EmptySpace max="32767" attributes="0"/>
</Group> </Group>
@ -83,23 +84,25 @@
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="keywordTextField" max="32767" attributes="0"/> <Component id="keywordTextField" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/> <EmptySpace type="unrelated" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0"> <Group type="103" groupAlignment="3" attributes="0">
<Component id="exactRadioButton" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="exactRadioButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="substringRadioButton" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="substringRadioButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="regexRadioButton" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="regexRadioButton" alignment="3" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="dataSourceCheckBox" min="-2" max="-2" attributes="0"/> <Component id="dataSourceCheckBox" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="jScrollPane1" min="-2" pref="61" max="-2" attributes="0"/> <Component id="jScrollPane1" min="-2" pref="61" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="jSaveSearchResults" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0"> <Group type="103" groupAlignment="3" attributes="0">
<Component id="searchButton" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="searchButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="ingestIndexLabel" alignment="3" min="-2" pref="13" max="-2" attributes="0"/> <Component id="ingestIndexLabel" alignment="3" min="-2" pref="13" max="-2" attributes="0"/>
</Group> </Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace pref="22" max="32767" attributes="0"/>
</Group> </Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
@ -217,5 +220,15 @@
</Property> </Property>
</Properties> </Properties>
</Component> </Component>
<Component class="javax.swing.JCheckBox" name="jSaveSearchResults">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="DropdownSingleTermSearchPanel.jSaveSearchResults.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="DropdownSingleTermSearchPanel.jSaveSearchResults.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents> </SubComponents>
</Form> </Form>

View File

@ -121,6 +121,8 @@ public class DropdownSingleTermSearchPanel extends AdHocSearchPanel {
ingestRunning = IngestManager.getInstance().isIngestRunning(); ingestRunning = IngestManager.getInstance().isIngestRunning();
updateIngestIndexLabel(); updateIngestIndexLabel();
jSaveSearchResults.setSelected(true);
IngestManager.getInstance().addIngestJobEventListener(new PropertyChangeListener() { IngestManager.getInstance().addIngestJobEventListener(new PropertyChangeListener() {
@Override @Override
public void propertyChange(PropertyChangeEvent evt) { public void propertyChange(PropertyChangeEvent evt) {
@ -221,6 +223,7 @@ public class DropdownSingleTermSearchPanel extends AdHocSearchPanel {
jScrollPane1 = new javax.swing.JScrollPane(); jScrollPane1 = new javax.swing.JScrollPane();
dataSourceList = new javax.swing.JList<>(); dataSourceList = new javax.swing.JList<>();
ingestIndexLabel = new javax.swing.JLabel(); ingestIndexLabel = new javax.swing.JLabel();
jSaveSearchResults = new javax.swing.JCheckBox();
org.openide.awt.Mnemonics.setLocalizedText(cutMenuItem, org.openide.util.NbBundle.getMessage(DropdownSingleTermSearchPanel.class, "DropdownSearchPanel.cutMenuItem.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(cutMenuItem, org.openide.util.NbBundle.getMessage(DropdownSingleTermSearchPanel.class, "DropdownSearchPanel.cutMenuItem.text")); // NOI18N
rightClickMenu.add(cutMenuItem); rightClickMenu.add(cutMenuItem);
@ -280,6 +283,9 @@ public class DropdownSingleTermSearchPanel extends AdHocSearchPanel {
ingestIndexLabel.setFont(ingestIndexLabel.getFont().deriveFont(ingestIndexLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 10)); ingestIndexLabel.setFont(ingestIndexLabel.getFont().deriveFont(ingestIndexLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 10));
org.openide.awt.Mnemonics.setLocalizedText(ingestIndexLabel, org.openide.util.NbBundle.getMessage(DropdownSingleTermSearchPanel.class, "DropdownSingleTermSearchPanel.ingestIndexLabel.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(ingestIndexLabel, org.openide.util.NbBundle.getMessage(DropdownSingleTermSearchPanel.class, "DropdownSingleTermSearchPanel.ingestIndexLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(jSaveSearchResults, org.openide.util.NbBundle.getMessage(DropdownSingleTermSearchPanel.class, "DropdownSingleTermSearchPanel.jSaveSearchResults.text")); // NOI18N
jSaveSearchResults.setToolTipText(org.openide.util.NbBundle.getMessage(DropdownSingleTermSearchPanel.class, "DropdownSingleTermSearchPanel.jSaveSearchResults.toolTipText")); // NOI18N
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout); this.setLayout(layout);
layout.setHorizontalGroup( layout.setHorizontalGroup(
@ -293,34 +299,37 @@ public class DropdownSingleTermSearchPanel extends AdHocSearchPanel {
.addComponent(substringRadioButton) .addComponent(substringRadioButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(regexRadioButton)) .addComponent(regexRadioButton))
.addComponent(dataSourceCheckBox)
.addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 297, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 297, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(jSaveSearchResults)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addComponent(searchButton) .addComponent(searchButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addGap(18, 18, 18)
.addComponent(ingestIndexLabel)) .addComponent(ingestIndexLabel))
.addComponent(keywordTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 296, javax.swing.GroupLayout.PREFERRED_SIZE)) .addComponent(keywordTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 296, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(dataSourceCheckBox))
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
); );
layout.setVerticalGroup( layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addContainerGap() .addContainerGap()
.addComponent(keywordTextField, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(keywordTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(exactRadioButton) .addComponent(exactRadioButton)
.addComponent(substringRadioButton) .addComponent(substringRadioButton)
.addComponent(regexRadioButton)) .addComponent(regexRadioButton))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(dataSourceCheckBox) .addComponent(dataSourceCheckBox)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 61, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 61, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(jSaveSearchResults)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(searchButton) .addComponent(searchButton)
.addComponent(ingestIndexLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 13, javax.swing.GroupLayout.PREFERRED_SIZE)) .addComponent(ingestIndexLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 13, javax.swing.GroupLayout.PREFERRED_SIZE))
.addContainerGap()) .addContainerGap(22, Short.MAX_VALUE))
); );
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
@ -340,7 +349,7 @@ public class DropdownSingleTermSearchPanel extends AdHocSearchPanel {
*/ */
private void keywordTextFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_keywordTextFieldActionPerformed private void keywordTextFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_keywordTextFieldActionPerformed
try { try {
search(); search(jSaveSearchResults.isSelected());
} catch (Exception e) { } catch (Exception e) {
LOGGER.log(Level.SEVERE, "Error performing ad hoc single keyword search", e); //NON-NLS LOGGER.log(Level.SEVERE, "Error performing ad hoc single keyword search", e); //NON-NLS
} }
@ -431,6 +440,7 @@ public class DropdownSingleTermSearchPanel extends AdHocSearchPanel {
private javax.swing.JList<String> dataSourceList; private javax.swing.JList<String> dataSourceList;
private javax.swing.JRadioButton exactRadioButton; private javax.swing.JRadioButton exactRadioButton;
private javax.swing.JLabel ingestIndexLabel; private javax.swing.JLabel ingestIndexLabel;
private javax.swing.JCheckBox jSaveSearchResults;
private javax.swing.JScrollPane jScrollPane1; private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JTextField keywordTextField; private javax.swing.JTextField keywordTextField;
private javax.swing.JMenuItem pasteMenuItem; private javax.swing.JMenuItem pasteMenuItem;

View File

@ -563,7 +563,7 @@ final class IngestSearchRunner {
subProgresses[keywordsSearched].progress(keywordList.getName() + ": " + queryDisplayStr, unitProgress); subProgresses[keywordsSearched].progress(keywordList.getName() + ": " + queryDisplayStr, unitProgress);
// Create blackboard artifacts // Create blackboard artifacts
newResults.process(null, subProgresses[keywordsSearched], this, keywordList.getIngestMessages()); newResults.process(null, subProgresses[keywordsSearched], this, keywordList.getIngestMessages(), true);
} //if has results } //if has results

View File

@ -570,7 +570,9 @@ public final class KeywordSearchIngestModule implements FileIngestModule {
putIngestStatus(jobId, aFile.getId(), IngestStatus.SKIPPED_ERROR_TEXTEXTRACT); putIngestStatus(jobId, aFile.getId(), IngestStatus.SKIPPED_ERROR_TEXTEXTRACT);
} }
if ((wasTextAdded == false) && (aFile.getNameExtension().equalsIgnoreCase("txt"))) { if ((wasTextAdded == false) && (aFile.getNameExtension().equalsIgnoreCase("txt") && !(aFile.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.CARVED)))) {
//Carved Files should be the only type of unallocated files capable of a txt extension and
//should be ignored by the TextFileExtractor because they may contain more than one text encoding
try { try {
if (Ingester.getDefault().indexText(txtFileExtractor, aFile, context)) { if (Ingester.getDefault().indexText(txtFileExtractor, aFile, context)) {
putIngestStatus(jobId, aFile.getId(), IngestStatus.TEXT_INGESTED); putIngestStatus(jobId, aFile.getId(), IngestStatus.TEXT_INGESTED);

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2017 Basis Technology Corp. * Copyright 2011-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -32,14 +32,18 @@ import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrDocument; import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList; import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.params.CursorMarkParams; import org.apache.solr.common.params.CursorMarkParams;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.EscapeUtil; import org.sleuthkit.autopsy.coreutils.EscapeUtil;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.Version; import org.sleuthkit.autopsy.coreutils.Version;
import org.sleuthkit.datamodel.Blackboard;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskException; import org.sleuthkit.datamodel.TskException;
@ -203,15 +207,44 @@ class LuceneQuery implements KeywordSearchQuery {
* @param listName The name of the keyword list that contained the * @param listName The name of the keyword list that contained the
* keyword for which the hit was found. * keyword for which the hit was found.
* *
* * @return The newly created artifact, or null if one wasn't created due to
* @return The newly created artifact or null if there was a problem * either the artifact already existing or an error while trying to
* creating it. * create it.
*/ */
@Override @Override
public BlackboardArtifact postKeywordHitToBlackboard(Content content, Keyword foundKeyword, KeywordHit hit, String snippet, String listName) { public BlackboardArtifact postKeywordHitToBlackboard(Content content, Keyword foundKeyword, KeywordHit hit, String snippet, String listName) {
final String MODULE_NAME = KeywordSearchModuleFactory.getModuleName(); final String MODULE_NAME = KeywordSearchModuleFactory.getModuleName();
Collection<BlackboardAttribute> attributes = new ArrayList<>(); List<BlackboardAttribute> attributesList = new ArrayList<>();
attributesList.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD, MODULE_NAME, foundKeyword.getSearchTerm()));
if (originalKeyword != null) {
BlackboardAttribute.ATTRIBUTE_TYPE selType = originalKeyword.getArtifactAttributeType();
if (selType != null) {
attributesList.add(new BlackboardAttribute(selType, MODULE_NAME, foundKeyword.getSearchTerm()));
}
if (originalKeyword.searchTermIsWholeWord()) {
attributesList.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE, MODULE_NAME, KeywordSearch.QueryType.LITERAL.ordinal()));
} else {
attributesList.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE, MODULE_NAME, KeywordSearch.QueryType.SUBSTRING.ordinal()));
}
}
if (StringUtils.isNotBlank(listName)) {
attributesList.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, listName));
}
try {
SleuthkitCase tskCase = Case.getCurrentCaseThrows().getSleuthkitCase();
Blackboard blackboard = tskCase.getBlackboard();
if (blackboard.artifactExists(content, BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT, attributesList)) {
return null;
}
} catch (NoCurrentCaseException | TskCoreException ex) {
logger.log(Level.SEVERE, String.format(
"A problem occurred while checking for existing artifacts for file '%s' (id=%d).",
content.getName(), content.getId()), ex); //NON-NLS
}
BlackboardArtifact bba; BlackboardArtifact bba;
try { try {
bba = content.newArtifact(ARTIFACT_TYPE.TSK_KEYWORD_HIT); bba = content.newArtifact(ARTIFACT_TYPE.TSK_KEYWORD_HIT);
@ -221,32 +254,15 @@ class LuceneQuery implements KeywordSearchQuery {
} }
if (snippet != null) { if (snippet != null) {
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW, MODULE_NAME, snippet)); attributesList.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW, MODULE_NAME, snippet));
}
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD, MODULE_NAME, foundKeyword.getSearchTerm()));
if (StringUtils.isNotBlank(listName)) {
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, listName));
}
if (originalKeyword != null) {
BlackboardAttribute.ATTRIBUTE_TYPE selType = originalKeyword.getArtifactAttributeType();
if (selType != null) {
attributes.add(new BlackboardAttribute(selType, MODULE_NAME, foundKeyword.getSearchTerm()));
}
if (originalKeyword.searchTermIsWholeWord()) {
attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE, MODULE_NAME, KeywordSearch.QueryType.LITERAL.ordinal()));
} else {
attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE, MODULE_NAME, KeywordSearch.QueryType.SUBSTRING.ordinal()));
}
} }
hit.getArtifactID().ifPresent(artifactID hit.getArtifactID().ifPresent(artifactID
-> attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, artifactID)) -> attributesList.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, artifactID))
); );
try { try {
bba.addAttributes(attributes); //write out to bb bba.addAttributes(attributesList); //write out to bb
return bba; return bba;
} catch (TskCoreException e) { } catch (TskCoreException e) {
logger.log(Level.WARNING, "Error adding bb attributes to artifact", e); //NON-NLS logger.log(Level.WARNING, "Error adding bb attributes to artifact", e); //NON-NLS

View File

@ -141,9 +141,10 @@ class QueryResults {
* @param notifyInbox Whether or not to write a message to the ingest * @param notifyInbox Whether or not to write a message to the ingest
* messages inbox if there is a keyword hit in the text * messages inbox if there is a keyword hit in the text
* exrtacted from the text source object. * exrtacted from the text source object.
* @param saveResults Flag whether to save search results as KWS artifacts.
* *
*/ */
void process(ProgressHandle progress, ProgressContributor subProgress, SwingWorker<?, ?> worker, boolean notifyInbox) { void process(ProgressHandle progress, ProgressContributor subProgress, SwingWorker<?, ?> worker, boolean notifyInbox, boolean saveResults) {
/* /*
* Initialize the progress indicator to the number of keywords that will * Initialize the progress indicator to the number of keywords that will
* be processed. * be processed.
@ -219,6 +220,7 @@ class QueryResults {
logger.log(Level.SEVERE, "Failed to get text source object for ", tskCoreException); //NON-NLS logger.log(Level.SEVERE, "Failed to get text source object for ", tskCoreException); //NON-NLS
} }
if (saveResults) {
/* /*
* Post an artifact for the hit to the blackboard. * Post an artifact for the hit to the blackboard.
*/ */
@ -238,6 +240,7 @@ class QueryResults {
} }
} }
} }
}
++keywordsProcessed; ++keywordsProcessed;
} }

View File

@ -36,7 +36,6 @@ import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrDocument; import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList; import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.params.CursorMarkParams; import org.apache.solr.common.params.CursorMarkParams;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
@ -50,11 +49,13 @@ import static org.sleuthkit.autopsy.keywordsearch.TermsComponentQuery.KEYWORD_SE
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Account; import org.sleuthkit.datamodel.Account;
import org.sleuthkit.datamodel.AccountFileInstance; import org.sleuthkit.datamodel.AccountFileInstance;
import org.sleuthkit.datamodel.Blackboard;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.TskData;
@ -74,7 +75,7 @@ import org.sleuthkit.datamodel.TskData;
*/ */
final class RegexQuery implements KeywordSearchQuery { final class RegexQuery implements KeywordSearchQuery {
public static final Logger LOGGER = Logger.getLogger(RegexQuery.class.getName()); public static final Logger logger = Logger.getLogger(RegexQuery.class.getName());
/** /**
* Lucene regular expressions do not support the following Java predefined * Lucene regular expressions do not support the following Java predefined
@ -213,7 +214,7 @@ final class RegexQuery implements KeywordSearchQuery {
hitsForKeyword.add(hit); hitsForKeyword.add(hit);
} }
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Error creating keyword hits", ex); //NON-NLS logger.log(Level.SEVERE, "Error creating keyword hits", ex); //NON-NLS
} }
} }
@ -223,7 +224,7 @@ final class RegexQuery implements KeywordSearchQuery {
} }
cursorMark = nextCursorMark; cursorMark = nextCursorMark;
} catch (KeywordSearchModuleException ex) { } catch (KeywordSearchModuleException ex) {
LOGGER.log(Level.SEVERE, "Error executing Regex Solr Query: " + keywordString, ex); //NON-NLS logger.log(Level.SEVERE, "Error executing Regex Solr Query: " + keywordString, ex); //NON-NLS
MessageNotifyUtil.Notify.error(NbBundle.getMessage(Server.class, "Server.query.exception.msg", keywordString), ex.getCause().getMessage()); MessageNotifyUtil.Notify.error(NbBundle.getMessage(Server.class, "Server.query.exception.msg", keywordString), ex.getCause().getMessage());
} }
} }
@ -437,16 +438,16 @@ final class RegexQuery implements KeywordSearchQuery {
* @param listName The name of the keyword list that contained the * @param listName The name of the keyword list that contained the
* keyword for which the hit was found. * keyword for which the hit was found.
* *
* * @return The newly created artifact, or null if one wasn't created due to
* @return The newly created artifact or null if there was a problem * either the artifact already existing or an error while trying to
* creating it. * create it.
*/ */
@Override @Override
public BlackboardArtifact postKeywordHitToBlackboard(Content content, Keyword foundKeyword, KeywordHit hit, String snippet, String listName) { public BlackboardArtifact postKeywordHitToBlackboard(Content content, Keyword foundKeyword, KeywordHit hit, String snippet, String listName) {
final String MODULE_NAME = KeywordSearchModuleFactory.getModuleName(); final String MODULE_NAME = KeywordSearchModuleFactory.getModuleName();
if (content == null) { if (content == null) {
LOGGER.log(Level.WARNING, "Error adding artifact for keyword hit to blackboard"); //NON-NLS logger.log(Level.WARNING, "Error adding artifact for keyword hit to blackboard"); //NON-NLS
return null; return null;
} }
@ -458,41 +459,52 @@ final class RegexQuery implements KeywordSearchQuery {
return null; return null;
} }
List<BlackboardAttribute> attributesList = new ArrayList<>();
attributesList.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD, MODULE_NAME, foundKeyword.getSearchTerm()));
attributesList.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE, MODULE_NAME, KeywordSearch.QueryType.REGEX.ordinal()));
attributesList.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP, MODULE_NAME, getQueryString()));
if (StringUtils.isNotBlank(listName)) {
attributesList.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, listName));
}
try {
SleuthkitCase tskCase = Case.getCurrentCaseThrows().getSleuthkitCase();
Blackboard blackboard = tskCase.getBlackboard();
if (blackboard.artifactExists(content, BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT, attributesList)) {
return null;
}
} catch (NoCurrentCaseException | TskCoreException ex) {
logger.log(Level.SEVERE, String.format(
"A problem occurred while checking for existing artifacts for file '%s' (id=%d).",
content.getName(), content.getId()), ex); //NON-NLS
}
/* /*
* Create a "plain vanilla" keyword hit artifact with keyword and * Create a "plain vanilla" keyword hit artifact with keyword and
* regex attributes * regex attributes
*/ */
BlackboardArtifact newArtifact; BlackboardArtifact newArtifact;
Collection<BlackboardAttribute> attributes = new ArrayList<>();
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD, MODULE_NAME, foundKeyword.getSearchTerm()));
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP, MODULE_NAME, getQueryString()));
try { try {
newArtifact = content.newArtifact(ARTIFACT_TYPE.TSK_KEYWORD_HIT); newArtifact = content.newArtifact(ARTIFACT_TYPE.TSK_KEYWORD_HIT);
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Error adding artifact for keyword hit to blackboard", ex); //NON-NLS logger.log(Level.SEVERE, "Error adding artifact for keyword hit to blackboard", ex); //NON-NLS
return null; return null;
} }
if (StringUtils.isNotBlank(listName)) {
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, listName));
}
if (snippet != null) { if (snippet != null) {
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW, MODULE_NAME, snippet)); attributesList.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW, MODULE_NAME, snippet));
} }
hit.getArtifactID().ifPresent(artifactID hit.getArtifactID().ifPresent(artifactID
-> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, artifactID)) -> attributesList.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, artifactID))
); );
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE, MODULE_NAME, KeywordSearch.QueryType.REGEX.ordinal()));
try { try {
newArtifact.addAttributes(attributes); newArtifact.addAttributes(attributesList);
return newArtifact; return newArtifact;
} catch (TskCoreException e) { } catch (TskCoreException e) {
LOGGER.log(Level.SEVERE, "Error adding bb attributes for terms search artifact", e); //NON-NLS logger.log(Level.SEVERE, "Error adding bb attributes for terms search artifact", e); //NON-NLS
return null; return null;
} }
} }
@ -502,7 +514,7 @@ final class RegexQuery implements KeywordSearchQuery {
final String MODULE_NAME = KeywordSearchModuleFactory.getModuleName(); final String MODULE_NAME = KeywordSearchModuleFactory.getModuleName();
if (originalKeyword.getArtifactAttributeType() != ATTRIBUTE_TYPE.TSK_CARD_NUMBER) { if (originalKeyword.getArtifactAttributeType() != ATTRIBUTE_TYPE.TSK_CARD_NUMBER) {
LOGGER.log(Level.SEVERE, "Keyword hit is not a credit card number"); //NON-NLS logger.log(Level.SEVERE, "Keyword hit is not a credit card number"); //NON-NLS
return; return;
} }
/* /*
@ -525,13 +537,13 @@ final class RegexQuery implements KeywordSearchQuery {
if (ccnAttribute == null || StringUtils.isBlank(ccnAttribute.getValueString())) { if (ccnAttribute == null || StringUtils.isBlank(ccnAttribute.getValueString())) {
if (hit.isArtifactHit()) { if (hit.isArtifactHit()) {
LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for artifact keyword hit: term = %s, snippet = '%s', artifact id = %d", foundKeyword.getSearchTerm(), hit.getSnippet(), hit.getArtifactID().get())); //NON-NLS logger.log(Level.SEVERE, String.format("Failed to parse credit card account number for artifact keyword hit: term = %s, snippet = '%s', artifact id = %d", foundKeyword.getSearchTerm(), hit.getSnippet(), hit.getArtifactID().get())); //NON-NLS
} else { } else {
try { try {
LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for content keyword hit: term = %s, snippet = '%s', object id = %d", foundKeyword.getSearchTerm(), hit.getSnippet(), hit.getContentID())); //NON-NLS logger.log(Level.SEVERE, String.format("Failed to parse credit card account number for content keyword hit: term = %s, snippet = '%s', object id = %d", foundKeyword.getSearchTerm(), hit.getSnippet(), hit.getContentID())); //NON-NLS
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for content keyword hit: term = %s, snippet = '%s' ", foundKeyword.getSearchTerm(), hit.getSnippet())); //NON-NLS logger.log(Level.SEVERE, String.format("Failed to parse credit card account number for content keyword hit: term = %s, snippet = '%s' ", foundKeyword.getSearchTerm(), hit.getSnippet())); //NON-NLS
LOGGER.log(Level.SEVERE, "There was a error getting contentID for keyword hit.", ex); //NON-NLS logger.log(Level.SEVERE, "There was a error getting contentID for keyword hit.", ex); //NON-NLS
} }
} }
return; return;
@ -599,7 +611,7 @@ final class RegexQuery implements KeywordSearchQuery {
ccAccountInstance.addAttributes(attributes); ccAccountInstance.addAttributes(attributes);
} catch (TskCoreException | NoCurrentCaseException ex) { } catch (TskCoreException | NoCurrentCaseException ex) {
LOGGER.log(Level.SEVERE, "Error creating CCN account instance", ex); //NON-NLS logger.log(Level.SEVERE, "Error creating CCN account instance", ex); //NON-NLS
} }

View File

@ -17,8 +17,9 @@
* limitations under the License. * limitations under the License.
*/ */
package org.sleuthkit.autopsy.keywordsearch; package org.sleuthkit.autopsy.keywordsearch;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.BufferedInputStream;
import java.io.Reader; import java.io.Reader;
import java.util.logging.Level; import java.util.logging.Level;
import org.apache.tika.parser.txt.CharsetDetector; import org.apache.tika.parser.txt.CharsetDetector;
@ -53,15 +54,16 @@ final class TextFileExtractor extends ContentTextExtractor {
@Override @Override
public Reader getReader(Content source) throws TextExtractorException { public Reader getReader(Content source) throws TextExtractorException {
CharsetDetector detector = new CharsetDetector(); CharsetDetector detector = new CharsetDetector();
ReadContentInputStream stream = new ReadContentInputStream(source); //wrap stream in a BufferedInputStream so that it supports the mark/reset methods necessary for the CharsetDetector
InputStream stream = new BufferedInputStream(new ReadContentInputStream(source));
try { try {
detector.setText(stream); detector.setText(stream);
} catch (IOException ex) { } catch (IOException ex) {
throw new TextExtractorException("Unable to get string from detected text in UnicodeTextExtractor", ex); throw new TextExtractorException("Unable to get string from detected text in TextFileExtractor", ex);
} }
CharsetMatch match = detector.detect(); CharsetMatch match = detector.detect();
if (match.getConfidence() < MIN_MATCH_CONFIDENCE) { if (match.getConfidence() < MIN_MATCH_CONFIDENCE) {
throw new TextExtractorException("Text does not match any character set with a high enough confidence for UnicodeTextExtractor"); throw new TextExtractorException("Text does not match any character set with a high enough confidence for TextFileExtractor");
} }
return match.getReader(); return match.getReader();

View File

@ -1,29 +1,25 @@
---------------- VERSION 4.8.0 -------------- ---------------- VERSION 4.8.0 --------------
New Features: New Features:
- The case tree view can now be grouped by data source. - Data Source Grouping:
- Added a common files search tool that finds all instances of a file in a case. -- The case tree view can now be grouped by data source.
- Text extraction optionally includes optical character recognition (OCR). -- Keyword and file search can now be restricted to a data source.
- Data source(s) filter added to ad hoc keyword search and file search by - Central Repository / Corrrelation:
attributes. -- New common files search feature that finds files that exist in multiple devices in the same case.
- SQLite tables can be now be exported to CSV files. -- The Other Occurrences content viewer now shows matches in the current case (in addition to central repository).
- User defined tags now appear first in tagging menus. -- Central repository options panel now shows cases that are in repo.
- Eliminated one tagging sub menu layer for faster tagging. - A comment about a file can be created and saved in the central repository so that future cases and see it.
- Added Replace Tag item to tagging menus (shortcut for delete tag, add tag). - Keyword Search:
- The Other Occurrences content viewer now shows matches in the current case. -- Can enable OCR text extraction of PDF and JPG files using Tesseract.
- A listing of cases in the central repository is displayed by the -- Keyword search module normalizes Unicode text.
central repository options panel. -- Keyword search module uses ICU to convert text files that do not have a BOM.
- An interesting file artifact is now created when a "zip bomb" is detected. - Tagging:
- Text and queries sent to Solr are now normalized to handle diacritics, -- Tagging menu changed to have user defined tags at top and "quick tag" removed one level of menus.
ligatures, narrow and wide width Japanese characters, etc. -- New "Replace Tag" feature to change the tag on an item.
- An object detection ingest module that uses OpenCV and user-supplied - Other:
classifiers has been added to the "experimental" Net Beans Module (NBM). -- SQLite tables can be now be exported to CSV files.
- A data source processor that runs Volatility on a memory image has been -- An interesting file artifact is now created when a "zip bomb" is detected.
added to the "experimental" NBM. -- An object detection ingest module was added to the Experimental module. It requires an OpenCV trained model.
- Comments can be added to all files (file correlation properties) recorded
in the central repository using a results view context menu item.
- Comments can be added to all correlation properties recorded
in the central repository using an Other Occurrences results content viewer
context menu item.
Bug Fixes: Bug Fixes:
- Expanding the case tree is more efficient. - Expanding the case tree is more efficient.

View File

@ -1,6 +1,6 @@
Manifest-Version: 1.0 Manifest-Version: 1.0
OpenIDE-Module: org.sleuthkit.autopsy.recentactivity/6 OpenIDE-Module: org.sleuthkit.autopsy.recentactivity/6
OpenIDE-Module-Implementation-Version: 15 OpenIDE-Module-Implementation-Version: 16
OpenIDE-Module-Layer: org/sleuthkit/autopsy/recentactivity/layer.xml OpenIDE-Module-Layer: org/sleuthkit/autopsy/recentactivity/layer.xml
OpenIDE-Module-Localizing-Bundle: org/sleuthkit/autopsy/recentactivity/Bundle.properties OpenIDE-Module-Localizing-Bundle: org/sleuthkit/autopsy/recentactivity/Bundle.properties
OpenIDE-Module-Requires: OpenIDE-Module-Requires:

View File

@ -60,7 +60,7 @@
<compile-dependency/> <compile-dependency/>
<run-dependency> <run-dependency>
<release-version>10</release-version> <release-version>10</release-version>
<specification-version>10.11</specification-version> <specification-version>10.12</specification-version>
</run-dependency> </run-dependency>
</dependency> </dependency>
</module-dependencies> </module-dependencies>

View File

@ -1,3 +1,3 @@
<project name="TSK_VERSION"> <project name="TSK_VERSION">
<property name="TSK_VERSION" value="4.6.1"/> <property name="TSK_VERSION" value="4.6.2"/>
</project> </project>

View File

@ -6,8 +6,8 @@ app.name=${branding.token}
### if left unset, version will default to today's date ### if left unset, version will default to today's date
app.version=4.8.0 app.version=4.8.0
### build.type must be one of: DEVELOPMENT, RELEASE ### build.type must be one of: DEVELOPMENT, RELEASE
build.type=RELEASE #build.type=RELEASE
#build.type=DEVELOPMENT build.type=DEVELOPMENT
project.org.netbeans.progress=org-netbeans-api-progress project.org.netbeans.progress=org-netbeans-api-progress
project.org.sleuthkit.autopsy.experimental=Experimental project.org.sleuthkit.autopsy.experimental=Experimental

View File

@ -36,7 +36,7 @@
<compile-dependency/> <compile-dependency/>
<run-dependency> <run-dependency>
<release-version>10</release-version> <release-version>10</release-version>
<specification-version>10.11</specification-version> <specification-version>10.12</specification-version>
</run-dependency> </run-dependency>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -24,7 +24,7 @@ errorLog () {
exit 1 exit 1
} }
Verify we can find the script #Verify we can find the script
if [[ -x "$AUTOPSY_BIN" ]]; then if [[ -x "$AUTOPSY_BIN" ]]; then
infoLog "Autopsy found" infoLog "Autopsy found"
else else
@ -66,7 +66,11 @@ showAndReadOptions () {
echo [$x] "${word}" echo [$x] "${word}"
x=$((x + 1)) x=$((x + 1))
done done
read option read -n 1 option
if [[ $option = "" ]] || ! [[ "$option" =~ ^[0-9]+$ ]]; then
echo "Please choose a valid option"
showAndReadOptions
fi
} }

View File

@ -2,7 +2,7 @@
# Verifies programs are installed and copies native code into the Autopsy folder structure # Verifies programs are installed and copies native code into the Autopsy folder structure
TSK_VERSION=4.6.1 TSK_VERSION=4.6.2
# Verify PhotoRec was installed # Verify PhotoRec was installed
photorec_filepath=/usr/bin/photorec photorec_filepath=/usr/bin/photorec