mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-06 21:00:22 +00:00
Merge branch 'develop' of https://github.com/sleuthkit/autopsy into live_autopsy_warning
This commit is contained in:
commit
1932d4381b
@ -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"/>
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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>
|
||||||
|
@ -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
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
|
@ -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.
|
||||||
*/
|
*/
|
||||||
|
@ -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.
|
||||||
*
|
*
|
||||||
|
@ -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());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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, "{key}")"/>
|
|
||||||
</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, "{key}")"/>
|
|
||||||
</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">
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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:
|
||||||
|
@ -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>
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
213
Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeData.java
Normal file → Executable file
213
Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeData.java
Normal file → Executable 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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())));
|
||||||
|
|
||||||
|
@ -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");
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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(","));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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());
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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}/:\"*?<>|]+", "_");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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) {
|
||||||
|
@ -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> {
|
||||||
|
|
||||||
|
@ -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.
|
@ -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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
|
@ -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());
|
||||||
|
@ -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");
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
<Events>
|
||||||
|
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="showOnlyCurrentUserTagsCheckboxActionPerformed"/>
|
||||||
|
</Events>
|
||||||
|
</Component>
|
||||||
</SubComponents>
|
</SubComponents>
|
||||||
</Form>
|
</Form>
|
||||||
|
@ -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.
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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("");
|
||||||
}
|
}
|
||||||
|
@ -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){
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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, "{key}")"/>
|
<ResourceString bundle="org/sleuthkit/autopsy/modules/hashdatabase/Bundle.properties" key="AddHashValuesToDatabaseDialog.okButton.text_2" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
</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">
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -97,6 +97,9 @@
|
|||||||
<ResourceString bundle="org/sleuthkit/autopsy/report/Bundle.properties" key="ReportVisualPanel2.allResultsRadioButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
<ResourceString bundle="org/sleuthkit/autopsy/report/Bundle.properties" key="ReportVisualPanel2.allResultsRadioButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
</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>
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
272
Core/src/org/sleuthkit/autopsy/sqlitereader/SQLiteReader.java
Executable file
272
Core/src/org/sleuthkit/autopsy/sqlitereader/SQLiteReader.java
Executable 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();
|
||||||
|
}
|
||||||
|
}
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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>
|
||||||
|
@ -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();
|
||||||
|
@ -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:
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
@ -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>
|
||||||
|
@ -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
|
||||||
|
@ -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>
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
|
@ -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, "{key}")"/>
|
||||||
|
</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, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
</Component>
|
||||||
</SubComponents>
|
</SubComponents>
|
||||||
</Form>
|
</Form>
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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, "{key}")"/>
|
||||||
|
</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, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
</Component>
|
||||||
</SubComponents>
|
</SubComponents>
|
||||||
</Form>
|
</Form>
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
44
NEWS.txt
44
NEWS.txt
@ -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.
|
||||||
|
@ -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:
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
@ -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
|
||||||
|
@ -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>
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user