mirror of
https://github.com/elisspace/autopsy.git
synced 2026-10-01 23:09:56 +00:00
Merge branch 'develop' of https://github.com/briangsweeney/autopsy into 3910-match-counts
This commit is contained in:
+13
-11
@@ -73,6 +73,8 @@
|
||||
tofile="${ext.dir}/mchange-commons-java-0.2.9.jar"/>
|
||||
<copy file="${env.TSK_HOME}/bindings/java/lib/c3p0-0.9.5.jar"
|
||||
tofile="${ext.dir}/c3p0-0.9.5.jar"/>
|
||||
<copy file="${env.TSK_HOME}/bindings/java/lib/SparseBitSet-1.1.jar"
|
||||
tofile="${ext.dir}/SparseBitSet-1.1.jar"/>
|
||||
</target>
|
||||
|
||||
<target name="download-binlist">
|
||||
@@ -84,17 +86,17 @@
|
||||
|
||||
<target name="getTestDataFiles">
|
||||
<mkdir dir="${basedir}/test/qa-functional/data"/>
|
||||
<get src="https://drive.google.com/uc?id=1dLYGctuvRQMmnzfXPppTM_9gB49eLc_g" dest="${test-input}/embedded.vhd" skipexisting="true"/>
|
||||
<get src="https://drive.google.com/uc?id=1JACMDyH4y54ypGzFWl82ZzMQf3qbrioP" dest="${test-input}/encryption_detection_bitlocker_test.vhd" skipexisting="true"/>
|
||||
<get src="https://drive.google.com/uc?id=17sGybvmBGsWWJYo1IWKmO04oG9hKpPi3" dest="${test-input}/encryption_detection_sqlcipher_test.vhd" skipexisting="true"/>
|
||||
<get src="https://drive.google.com/uc?id=0BxdBkzm5VKGNT0dGY0dqcHVsU3M" dest="${test-input}/filter_test1.img" skipexisting="true"/>
|
||||
<get src="https://drive.google.com/uc?id=1bghoSm7z7nhmGIxlllyY1MMlbLntxm7n" dest="${test-input}/local_files_test.zip" skipexisting="true"/>
|
||||
<get src="https://drive.google.com/uc?id=1BrSiUQ1fzxFS9vIaK4mYKX6qIVp9kRWT" dest="${test-input}/password_detection_test.img" skipexisting="true"/>
|
||||
<get src="https://drive.google.com/uc?id=1HD8s4rculgHV1qZT5g80Kg7j4m1qccrN" dest="${test-input}/veracrypt_detection_test.vhd" skipexisting="true"/>
|
||||
<get src="https://drive.google.com/uc?id=1O5D09fFCFpXZqw0uLEs8kVLtfYTxqXAd" dest="${test-input}/commonfiles_image1_v1.vhd" skipexisting="true"/>
|
||||
<get src="https://drive.google.com/uc?id=1rMP1QTI0LdppzdypbG-4BDwkKcR3tHXc" dest="${test-input}/commonfiles_image2_v1.vhd" skipexisting="true"/>
|
||||
<get src="https://drive.google.com/uc?id=1OdwyJ2lru55ZPdvwzj3pq6sXIys27i4x" dest="${test-input}/commonfiles_image3_v1.vhd" skipexisting="true"/>
|
||||
<get src="https://drive.google.com/uc?id=1GoF2x0km5AyFvE926ttN20lrMX1oLN7E" dest="${test-input}/commonfiles_image4_v1.vhd" skipexisting="true"/>
|
||||
<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=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=0BxdBkzm5VKGNT0dGY0dqcHVsU3M" dest="${test-input}/IngestFilters_img1_v1.img" skipexisting="true"/>
|
||||
<get src="https://drive.google.com/uc?id=1bghoSm7z7nhmGIxlllyY1MMlbLntxm7n" dest="${test-input}/IngestFilters_local1_v1.zip" skipexisting="true"/>
|
||||
<get src="https://drive.google.com/uc?id=1BrSiUQ1fzxFS9vIaK4mYKX6qIVp9kRWT" dest="${test-input}/PasswordDetection_img1_v1.img" skipexisting="true"/>
|
||||
<get src="https://drive.google.com/uc?id=1HD8s4rculgHV1qZT5g80Kg7j4m1qccrN" dest="${test-input}/VeracryptDetection_img1_v1.vhd" skipexisting="true"/>
|
||||
<get src="https://drive.google.com/uc?id=1mr9waEDG5H8GBBn_yXwMUQ6senwC1goL" dest="${test-input}/CommonFiles_img1_v1.vhd" skipexisting="true"/>
|
||||
<get src="https://drive.google.com/uc?id=1-vmbmAAb2HBLbf58GpAA97ozGUFiYHbN" dest="${test-input}/CommonFiles_img2_v1.vhd" skipexisting="true"/>
|
||||
<get src="https://drive.google.com/uc?id=1ghDjm0NhI3ShMQ38E-4o7XrGeexpjdJb" dest="${test-input}/CommonFiles_img3_v1.vhd" skipexisting="true"/>
|
||||
<get src="https://drive.google.com/uc?id=1SJYJFjiumKEtQmeMPsQ6G3xmABarbKm_" dest="${test-input}/CommonFiles_img4_v1.vhd" skipexisting="true"/>
|
||||
</target>
|
||||
|
||||
<target name="get-deps" depends="init-ivy,getTSKJars,get-thirdparty-dependencies,get-InternalPythonModules, download-binlist,getTestDataFiles">
|
||||
|
||||
@@ -39,6 +39,7 @@ file.reference.curator-recipes-2.8.0.jar=release/modules/ext/curator-recipes-2.8
|
||||
file.reference.xmpcore-5.1.3.jar=release/modules/ext/xmpcore-5.1.3.jar
|
||||
file.reference.xz-1.6.jar=release/modules/ext/xz-1.6.jar
|
||||
file.reference.zookeeper-3.4.6.jar=release/modules/ext/zookeeper-3.4.6.jar
|
||||
file.reference.SparseBitSet-1.1.jar=release/modules/ext/SparseBitSet-1.1.jar
|
||||
javac.source=1.8
|
||||
javac.compilerargs=-Xlint -Xlint:-serial
|
||||
license.file=../LICENSE-2.0.txt
|
||||
|
||||
@@ -498,6 +498,10 @@
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/xmpcore-5.1.3.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/xmpcore-5.1.3.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/SparseBitSet-1.1.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/SparseBitSet-1.1.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
</data>
|
||||
</configuration>
|
||||
|
||||
@@ -37,8 +37,8 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
* Instances of this Action allow users to apply tags to blackboard artifacts.
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"AddBlackboardArtifactTagAction.singularTagResult=Tag Result",
|
||||
"AddBlackboardArtifactTagAction.pluralTagResult=Tag Results",
|
||||
"AddBlackboardArtifactTagAction.singularTagResult=Add Result Tag",
|
||||
"AddBlackboardArtifactTagAction.pluralTagResult=Add Result Tags",
|
||||
"# {0} - artifactName",
|
||||
"AddBlackboardArtifactTagAction.unableToTag.msg=Unable to tag {0}.",
|
||||
"AddBlackboardArtifactTagAction.taggingErr=Tagging Error"
|
||||
|
||||
@@ -38,8 +38,8 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
* Instances of this Action allow users to apply tags to content.
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"AddContentTagAction.singularTagFile=Tag File",
|
||||
"AddContentTagAction.pluralTagFile=Tag Files",
|
||||
"AddContentTagAction.singularTagFile=Add File Tag",
|
||||
"AddContentTagAction.pluralTagFile=Add File Tags",
|
||||
"# {0} - fileName",
|
||||
"AddContentTagAction.unableToTag.msg=Unable to tag {0}, not a regular file.",
|
||||
"AddContentTagAction.cannotApplyTagErr=Cannot Apply Tag",
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
package org.sleuthkit.autopsy.actions;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.logging.Level;
|
||||
@@ -92,6 +94,7 @@ abstract class AddTagAction extends AbstractAction implements Presenter.Popup {
|
||||
|
||||
// Get the current set of tag names.
|
||||
Map<String, TagName> tagNamesMap = null;
|
||||
List<String> standardTagNames = TagsManager.getStandardTagNames();
|
||||
try {
|
||||
TagsManager tagsManager = Case.getCurrentCaseThrows().getServices().getTagsManager();
|
||||
tagNamesMap = new TreeMap<>(tagsManager.getDisplayNamesToTagNamesMap());
|
||||
@@ -101,6 +104,7 @@ abstract class AddTagAction extends AbstractAction implements Presenter.Popup {
|
||||
|
||||
// Create a menu item for each of the existing and visible tags.
|
||||
// Selecting one of these menu items adds a tag with the associated tag name.
|
||||
List<JMenuItem> standardTagMenuitems = new ArrayList<>();
|
||||
if (null != tagNamesMap && !tagNamesMap.isEmpty()) {
|
||||
for (Map.Entry<String, TagName> entry : tagNamesMap.entrySet()) {
|
||||
String tagDisplayName = entry.getKey();
|
||||
@@ -114,15 +118,26 @@ abstract class AddTagAction extends AbstractAction implements Presenter.Popup {
|
||||
tagNameItem.addActionListener((ActionEvent e) -> {
|
||||
getAndAddTag(entry.getKey(), entry.getValue(), NO_COMMENT);
|
||||
});
|
||||
|
||||
add(tagNameItem);
|
||||
|
||||
// Show custom tags before predefined tags in the menu
|
||||
if (standardTagNames.contains(tagDisplayName)) {
|
||||
standardTagMenuitems.add(tagNameItem);
|
||||
} else {
|
||||
add(tagNameItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (getItemCount() > 0) {
|
||||
addSeparator();
|
||||
}
|
||||
|
||||
|
||||
standardTagMenuitems.forEach((menuItem) -> {
|
||||
add(menuItem);
|
||||
});
|
||||
|
||||
addSeparator();
|
||||
|
||||
// Create a "Choose Tag and Comment..." menu item. Selecting this item initiates
|
||||
// a dialog that can be used to create or select a tag name with an
|
||||
// optional comment and adds a tag with the resulting name.
|
||||
|
||||
@@ -20,7 +20,7 @@ AddTagAction.newTag=New Tag...
|
||||
AddTagAction.tagAndComment=Tag and Comment...
|
||||
AddBookmarkTagAction.bookmark.text=Bookmark
|
||||
GetTagNameAndCommentDialog.noTags=No Tags
|
||||
GetTagNameAndCommentDialog.createTag=Create Tag
|
||||
GetTagNameAndCommentDialog.selectTag=Select Tag
|
||||
GetTagNameAndCommentDialog.cancelName=cancel
|
||||
GetTagNameDialog.createTag=Create Tag
|
||||
GetTagNameDialog.cancelName=Cancel
|
||||
|
||||
@@ -23,7 +23,7 @@ AddTagAction.noTags=\u30bf\u30b0\u7121\u3057
|
||||
AddTagAction.newTag=\u65b0\u898f\u30bf\u30b0\u2026
|
||||
AddTagAction.tagAndComment=\u30bf\u30b0\u3068\u30b3\u30e1\u30f3\u30c8\u3092\u8ffd\u52a0\u2026
|
||||
GetTagNameAndCommentDialog.noTags=\u30bf\u30b0\u7121\u3057
|
||||
GetTagNameAndCommentDialog.createTag=\u30bf\u30b0\u3092\u4f5c\u6210
|
||||
GetTagNameAndCommentDialog.selectTag=\u30bf\u30b0\u3092\u9078\u629e
|
||||
GetTagNameAndCommentDialog.cancelName=\u30ad\u30e3\u30f3\u30bb\u30eb
|
||||
GetTagNameDialog.createTag=\u30bf\u30b0\u3092\u4f5c\u6210
|
||||
GetTagNameDialog.cancelName=\u30ad\u30e3\u30f3\u30bb\u30eb
|
||||
|
||||
@@ -38,7 +38,7 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
* artifacts.
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"DeleteBlackboardArtifactTagAction.deleteTag=Delete Tag",
|
||||
"DeleteBlackboardArtifactTagAction.deleteTag=Remove Selected Tag(s)",
|
||||
"# {0} - tagName",
|
||||
"DeleteBlackboardArtifactTagAction.unableToDelTag.msg=Unable to delete tag {0}.",
|
||||
"DeleteBlackboardArtifactTagAction.tagDelErr=Tag Deletion Error"
|
||||
|
||||
@@ -37,7 +37,7 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
* Instances of this Action allow users to delete tags applied to content.
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"DeleteContentTagAction.deleteTag=Delete Tag",
|
||||
"DeleteContentTagAction.deleteTag=Remove Selected Tag(s)",
|
||||
"# {0} - tagName",
|
||||
"DeleteContentTagAction.unableToDelTag.msg=Unable to delete tag {0}.",
|
||||
"DeleteContentTagAction.tagDelErr=Tag Deletion Error"
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
package org.sleuthkit.autopsy.actions;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@@ -138,7 +139,7 @@ public class DeleteFileBlackboardArtifactTagAction extends AbstractAction implem
|
||||
*/
|
||||
@NbBundle.Messages({"# {0} - artifactID",
|
||||
"DeleteFileBlackboardArtifactTagAction.deleteTags.alert=Unable to untag artifact {0}."})
|
||||
private class TagMenu extends JMenu {
|
||||
private final class TagMenu extends JMenu {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@@ -153,6 +154,8 @@ public class DeleteFileBlackboardArtifactTagAction extends AbstractAction implem
|
||||
= selectedBlackboardArtifactsList.iterator().next();
|
||||
|
||||
Map<String, TagName> tagNamesMap = null;
|
||||
List<String> standardTagNames = TagsManager.getStandardTagNames();
|
||||
List<JMenuItem> standardTagMenuitems = new ArrayList<>();
|
||||
try {
|
||||
// Get the current set of tag names.
|
||||
TagsManager tagsManager = Case.getCurrentCaseThrows().getServices().getTagsManager();
|
||||
@@ -182,7 +185,12 @@ public class DeleteFileBlackboardArtifactTagAction extends AbstractAction implem
|
||||
tagNameItem.addActionListener((ActionEvent e) -> {
|
||||
deleteTag(tagName, artifactTag, artifact.getArtifactID());
|
||||
});
|
||||
add(tagNameItem);
|
||||
// Show custom tags before predefined tags in the menu
|
||||
if (standardTagNames.contains(tagDisplayName)) {
|
||||
standardTagMenuitems.add(tagNameItem);
|
||||
} else {
|
||||
add(tagNameItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -192,6 +200,12 @@ public class DeleteFileBlackboardArtifactTagAction extends AbstractAction implem
|
||||
}
|
||||
}
|
||||
|
||||
if ((getItemCount() > 0) && !standardTagMenuitems.isEmpty() ){
|
||||
addSeparator();
|
||||
}
|
||||
standardTagMenuitems.forEach((menuItem) -> {
|
||||
add(menuItem);
|
||||
});
|
||||
if (getItemCount() == 0) {
|
||||
setEnabled(false);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
package org.sleuthkit.autopsy.actions;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@@ -136,7 +137,7 @@ public class DeleteFileContentTagAction extends AbstractAction implements Presen
|
||||
* creating or selecting a tag name for a tag and specifying an optional tag
|
||||
* comment.
|
||||
*/
|
||||
private class TagMenu extends JMenu {
|
||||
private final class TagMenu extends JMenu {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@@ -150,6 +151,8 @@ public class DeleteFileContentTagAction extends AbstractAction implements Presen
|
||||
AbstractFile file = selectedAbstractFilesList.iterator().next();
|
||||
|
||||
Map<String, TagName> tagNamesMap = null;
|
||||
List<String> standardTagNames = TagsManager.getStandardTagNames();
|
||||
List<JMenuItem> standardTagMenuitems = new ArrayList<>();
|
||||
try {
|
||||
// Get the current set of tag names.
|
||||
TagsManager tagsManager = Case.getCurrentCaseThrows().getServices().getTagsManager();
|
||||
@@ -179,7 +182,13 @@ public class DeleteFileContentTagAction extends AbstractAction implements Presen
|
||||
tagNameItem.addActionListener((ActionEvent e) -> {
|
||||
deleteTag(tagName, contentTag, file.getId());
|
||||
});
|
||||
add(tagNameItem);
|
||||
|
||||
// Show custom tags before predefined tags in the menu
|
||||
if (standardTagNames.contains(tagDisplayName)) {
|
||||
standardTagMenuitems.add(tagNameItem);
|
||||
} else {
|
||||
add(tagNameItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -189,6 +198,13 @@ public class DeleteFileContentTagAction extends AbstractAction implements Presen
|
||||
}
|
||||
}
|
||||
|
||||
if ((getItemCount() > 0) && !standardTagMenuitems.isEmpty() ){
|
||||
addSeparator();
|
||||
}
|
||||
standardTagMenuitems.forEach((menuItem) -> {
|
||||
add(menuItem);
|
||||
});
|
||||
|
||||
if(getItemCount() == 0) {
|
||||
setEnabled(false);
|
||||
}
|
||||
|
||||
@@ -24,11 +24,11 @@
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<Component id="newTagButton" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace pref="165" max="32767" attributes="0"/>
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
<Component id="okButton" linkSize="1" min="-2" pref="67" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="cancelButton" linkSize="1" min="-2" max="-2" attributes="0"/>
|
||||
@@ -38,14 +38,14 @@
|
||||
<Component id="commentLabel" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="tagLabel" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||
<EmptySpace type="unrelated" min="-2" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="commentText" max="32767" attributes="0"/>
|
||||
<Component id="tagCombo" max="32767" attributes="0"/>
|
||||
<Component id="jScrollPane1" pref="318" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
@@ -58,11 +58,11 @@
|
||||
<Component id="tagLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="commentLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="commentText" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="commentLabel" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="jScrollPane1" min="-2" pref="51" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace pref="37" max="32767" attributes="0"/>
|
||||
<EmptySpace pref="22" max="32767" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="cancelButton" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="okButton" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
@@ -125,16 +125,6 @@
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JTextField" name="commentText">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/actions/Bundle.properties" key="GetTagNameAndCommentDialog.commentText.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/actions/Bundle.properties" key="GetTagNameAndCommentDialog.commentText.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="newTagButton">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
@@ -145,5 +135,26 @@
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="newTagButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Container class="javax.swing.JScrollPane" name="jScrollPane1">
|
||||
<AuxValues>
|
||||
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JTextArea" name="commentText">
|
||||
<Properties>
|
||||
<Property name="columns" type="int" value="20"/>
|
||||
<Property name="rows" type="int" value="5"/>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/actions/Bundle.properties" key="GetTagNameAndCommentDialog.commentText.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/actions/Bundle.properties" key="GetTagNameAndCommentDialog.commentText.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
|
||||
@@ -22,9 +22,13 @@ import java.awt.Component;
|
||||
import java.awt.Window;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.logging.Level;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.ActionMap;
|
||||
import javax.swing.DefaultListCellRenderer;
|
||||
@@ -50,7 +54,8 @@ import org.sleuthkit.datamodel.TskData;
|
||||
public class GetTagNameAndCommentDialog extends JDialog {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final Set<TagName> tagNamesSet = new HashSet<>();
|
||||
private final List<TagName> tagNamesList = new ArrayList<>();
|
||||
private final List<TagName> standardTagNamesList = new ArrayList<>();
|
||||
private TagNameAndComment tagNameAndComment = null;
|
||||
|
||||
public static class TagNameAndComment {
|
||||
@@ -105,7 +110,7 @@ public class GetTagNameAndCommentDialog extends JDialog {
|
||||
|
||||
private GetTagNameAndCommentDialog(Window owner) {
|
||||
super(owner,
|
||||
NbBundle.getMessage(GetTagNameAndCommentDialog.class, "GetTagNameAndCommentDialog.createTag"),
|
||||
NbBundle.getMessage(GetTagNameAndCommentDialog.class, "GetTagNameAndCommentDialog.selectTag"),
|
||||
ModalityType.APPLICATION_MODAL);
|
||||
}
|
||||
|
||||
@@ -144,16 +149,29 @@ public class GetTagNameAndCommentDialog extends JDialog {
|
||||
// not exist in the database).
|
||||
try {
|
||||
TagsManager tagsManager = Case.getCurrentCaseThrows().getServices().getTagsManager();
|
||||
tagNamesSet.addAll(tagsManager.getAllTagNames());
|
||||
List<String> standardTagNames = TagsManager.getStandardTagNames();
|
||||
Map<String, TagName> tagNamesMap = new TreeMap<>(tagsManager.getDisplayNamesToTagNamesMap());
|
||||
|
||||
tagNamesMap.entrySet().stream().map((entry) -> entry.getValue()).forEachOrdered((tagName) -> {
|
||||
if (standardTagNames.contains(tagName.getDisplayName())) {
|
||||
standardTagNamesList.add(tagName);
|
||||
} else {
|
||||
tagNamesList.add(tagName);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
} catch (TskCoreException | NoCurrentCaseException ex) {
|
||||
Logger.getLogger(GetTagNameAndCommentDialog.class
|
||||
.getName()).log(Level.SEVERE, "Failed to get tag names", ex); //NON-NLS
|
||||
}
|
||||
for (TagName tag : tagNamesSet) {
|
||||
|
||||
tagNamesList.forEach((tag) -> {
|
||||
tagCombo.addItem(tag);
|
||||
}
|
||||
});
|
||||
|
||||
standardTagNamesList.forEach((tag) -> {
|
||||
tagCombo.addItem(tag);
|
||||
});
|
||||
|
||||
// Center and show the dialog box.
|
||||
this.setLocationRelativeTo(this.getOwner());
|
||||
@@ -174,8 +192,9 @@ public class GetTagNameAndCommentDialog extends JDialog {
|
||||
tagCombo = new javax.swing.JComboBox<TagName>();
|
||||
tagLabel = new javax.swing.JLabel();
|
||||
commentLabel = new javax.swing.JLabel();
|
||||
commentText = new javax.swing.JTextField();
|
||||
newTagButton = new javax.swing.JButton();
|
||||
jScrollPane1 = new javax.swing.JScrollPane();
|
||||
commentText = new javax.swing.JTextArea();
|
||||
|
||||
addWindowListener(new java.awt.event.WindowAdapter() {
|
||||
public void windowClosing(java.awt.event.WindowEvent evt) {
|
||||
@@ -203,9 +222,6 @@ public class GetTagNameAndCommentDialog extends JDialog {
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(commentLabel, org.openide.util.NbBundle.getMessage(GetTagNameAndCommentDialog.class, "GetTagNameAndCommentDialog.commentLabel.text")); // NOI18N
|
||||
|
||||
commentText.setText(org.openide.util.NbBundle.getMessage(GetTagNameAndCommentDialog.class, "GetTagNameAndCommentDialog.commentText.text")); // NOI18N
|
||||
commentText.setToolTipText(org.openide.util.NbBundle.getMessage(GetTagNameAndCommentDialog.class, "GetTagNameAndCommentDialog.commentText.toolTipText")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(newTagButton, org.openide.util.NbBundle.getMessage(GetTagNameAndCommentDialog.class, "GetTagNameAndCommentDialog.newTagButton.text")); // NOI18N
|
||||
newTagButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
@@ -213,6 +229,12 @@ public class GetTagNameAndCommentDialog extends JDialog {
|
||||
}
|
||||
});
|
||||
|
||||
commentText.setColumns(20);
|
||||
commentText.setRows(5);
|
||||
commentText.setText(org.openide.util.NbBundle.getMessage(GetTagNameAndCommentDialog.class, "GetTagNameAndCommentDialog.commentText.text")); // NOI18N
|
||||
commentText.setToolTipText(org.openide.util.NbBundle.getMessage(GetTagNameAndCommentDialog.class, "GetTagNameAndCommentDialog.commentText.toolTipText")); // NOI18N
|
||||
jScrollPane1.setViewportView(commentText);
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
|
||||
getContentPane().setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
@@ -222,7 +244,7 @@ public class GetTagNameAndCommentDialog extends JDialog {
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
|
||||
.addComponent(newTagButton)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 165, Short.MAX_VALUE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(okButton, javax.swing.GroupLayout.PREFERRED_SIZE, 67, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(cancelButton))
|
||||
@@ -232,8 +254,8 @@ public class GetTagNameAndCommentDialog extends JDialog {
|
||||
.addComponent(tagLabel))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(commentText)
|
||||
.addComponent(tagCombo, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))))
|
||||
.addComponent(tagCombo, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 318, Short.MAX_VALUE))))
|
||||
.addContainerGap())
|
||||
);
|
||||
|
||||
@@ -247,10 +269,10 @@ public class GetTagNameAndCommentDialog extends JDialog {
|
||||
.addComponent(tagCombo, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(tagLabel))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(commentLabel)
|
||||
.addComponent(commentText, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 37, Short.MAX_VALUE)
|
||||
.addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 51, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 22, Short.MAX_VALUE)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(cancelButton)
|
||||
.addComponent(okButton)
|
||||
@@ -282,7 +304,7 @@ public class GetTagNameAndCommentDialog extends JDialog {
|
||||
private void newTagButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_newTagButtonActionPerformed
|
||||
TagName newTagName = GetTagNameDialog.doDialog(this);
|
||||
if (newTagName != null) {
|
||||
tagNamesSet.add(newTagName);
|
||||
tagNamesList.add(newTagName);
|
||||
tagCombo.addItem(newTagName);
|
||||
tagCombo.setSelectedItem(newTagName);
|
||||
}
|
||||
@@ -291,7 +313,8 @@ public class GetTagNameAndCommentDialog extends JDialog {
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JButton cancelButton;
|
||||
private javax.swing.JLabel commentLabel;
|
||||
private javax.swing.JTextField commentText;
|
||||
private javax.swing.JTextArea commentText;
|
||||
private javax.swing.JScrollPane jScrollPane1;
|
||||
private javax.swing.JButton newTagButton;
|
||||
private javax.swing.JButton okButton;
|
||||
private javax.swing.JComboBox<TagName> tagCombo;
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* 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.actions;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.logging.Level;
|
||||
import javafx.application.Platform;
|
||||
import javafx.scene.control.Alert;
|
||||
import javax.swing.SwingWorker;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.Utilities;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.casemodule.services.TagsManager;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifactTag;
|
||||
import org.sleuthkit.datamodel.TagName;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* This Action allows users to replace a tag applied to blackboard
|
||||
* artifacts, with another tag
|
||||
*/
|
||||
public final class ReplaceBlackboardArtifactTagAction extends ReplaceTagAction<BlackboardArtifactTag> {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(ReplaceBlackboardArtifactTagAction.class.getName());
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
// 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
|
||||
// node in the array returns a reference to the same action object from Node.getActions(boolean).
|
||||
private static ReplaceBlackboardArtifactTagAction instance;
|
||||
|
||||
public static synchronized ReplaceBlackboardArtifactTagAction getInstance() {
|
||||
if (null == instance) {
|
||||
instance = new ReplaceBlackboardArtifactTagAction();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
private ReplaceBlackboardArtifactTagAction() {
|
||||
super(MENU_TEXT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the specified tag on the given artifact with the new one
|
||||
*
|
||||
* @param oldArtifactTag tag to be replaced
|
||||
* @param newTagName name of the tag to replace with
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"# {0} - old tag name",
|
||||
"# {1} - artifactID",
|
||||
"ReplaceBlackboardArtifactTagAction.replaceTag.alert=Unable to replace tag {0} for artifact {1}."})
|
||||
@Override
|
||||
protected void replaceTag( BlackboardArtifactTag oldArtifactTag, TagName newTagName) {
|
||||
new SwingWorker<Void, Void>() {
|
||||
|
||||
@Override
|
||||
protected Void doInBackground() throws Exception {
|
||||
TagsManager tagsManager;
|
||||
try {
|
||||
tagsManager = Case.getCurrentCaseThrows().getServices().getTagsManager();
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.SEVERE, "Error replacing artifact tag. No open case found.", ex); //NON-NLS
|
||||
Platform.runLater(()
|
||||
-> new Alert(Alert.AlertType.ERROR, Bundle.ReplaceBlackboardArtifactTagAction_replaceTag_alert(oldArtifactTag.getName().getDisplayName(), oldArtifactTag.getArtifact().getArtifactID())).show()
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
logger.log(Level.INFO, "Replacing tag {0} with tag {1} for artifact {2}", new Object[]{oldArtifactTag.getName().getDisplayName(), newTagName.getDisplayName(), oldArtifactTag.getContent().getName()}); //NON-NLS
|
||||
|
||||
tagsManager.deleteBlackboardArtifactTag(oldArtifactTag);
|
||||
tagsManager.addBlackboardArtifactTag(oldArtifactTag.getArtifact(), newTagName);
|
||||
|
||||
} catch (TskCoreException tskCoreException) {
|
||||
logger.log(Level.SEVERE, "Error replacing artifact tag", tskCoreException); //NON-NLS
|
||||
Platform.runLater(()
|
||||
-> new Alert(Alert.AlertType.ERROR, Bundle.ReplaceBlackboardArtifactTagAction_replaceTag_alert(oldArtifactTag.getName().getDisplayName(), oldArtifactTag.getArtifact().getArtifactID())).show()
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done() {
|
||||
super.done();
|
||||
try {
|
||||
get();
|
||||
} catch (InterruptedException | ExecutionException ex) {
|
||||
logger.log(Level.SEVERE, "Unexpected exception while replacing artifact tag", ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of tags selected by user to replace
|
||||
*
|
||||
* @return a list of tags
|
||||
*/
|
||||
@Override
|
||||
Collection<? extends BlackboardArtifactTag> getTagsToReplace() {
|
||||
return Utilities.actionsGlobalContext().lookupAll(BlackboardArtifactTag.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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.actions;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.logging.Level;
|
||||
import javafx.application.Platform;
|
||||
import javafx.scene.control.Alert;
|
||||
import javax.swing.SwingWorker;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.Utilities;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.casemodule.services.TagsManager;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.datamodel.ContentTag;
|
||||
import org.sleuthkit.datamodel.TagName;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* This Action allow users to replace a content tag with another tag
|
||||
*/
|
||||
public final class ReplaceContentTagAction extends ReplaceTagAction<ContentTag> {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(ReplaceContentTagAction.class.getName());
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
// 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
|
||||
// node in the array returns a reference to the same action object from Node.getActions(boolean).
|
||||
private static ReplaceContentTagAction instance;
|
||||
|
||||
public static synchronized ReplaceContentTagAction getInstance() {
|
||||
if (null == instance) {
|
||||
instance = new ReplaceContentTagAction();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
private ReplaceContentTagAction() {
|
||||
super(MENU_TEXT);
|
||||
}
|
||||
|
||||
@NbBundle.Messages({
|
||||
"# {0} - old tag name",
|
||||
"# {1} - content obj id",
|
||||
"ReplaceContentTagAction.replaceTag.alert=Unable to replace tag {0} for {1}."})
|
||||
@Override
|
||||
protected void replaceTag(ContentTag oldTag, TagName newTagName) {
|
||||
new SwingWorker<Void, Void>() {
|
||||
|
||||
@Override
|
||||
protected Void doInBackground() throws Exception {
|
||||
TagsManager tagsManager;
|
||||
try {
|
||||
tagsManager = Case.getCurrentCaseThrows().getServices().getTagsManager();
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.SEVERE, "Error replacing artifact tag. No open case found.", ex); //NON-NLS
|
||||
Platform.runLater(()
|
||||
-> new Alert(Alert.AlertType.ERROR, Bundle.ReplaceContentTagAction_replaceTag_alert(oldTag.getName().getDisplayName(), oldTag.getContent().getName())).show()
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
logger.log(Level.INFO, "Replacing tag {0} with tag {1} for artifact {2}", new Object[]{oldTag.getName().getDisplayName(), newTagName.getDisplayName(), oldTag.getContent().getName()}); //NON-NLS
|
||||
|
||||
tagsManager.deleteContentTag(oldTag);
|
||||
tagsManager.addContentTag(oldTag.getContent(), newTagName);
|
||||
|
||||
} catch (TskCoreException tskCoreException) {
|
||||
logger.log(Level.SEVERE, "Error replacing artifact tag", tskCoreException); //NON-NLS
|
||||
Platform.runLater(()
|
||||
-> new Alert(Alert.AlertType.ERROR, Bundle.ReplaceContentTagAction_replaceTag_alert(oldTag.getName().getDisplayName(), oldTag.getContent().getName())).show()
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done() {
|
||||
super.done();
|
||||
try {
|
||||
get();
|
||||
} catch (InterruptedException | ExecutionException ex) {
|
||||
logger.log(Level.SEVERE, "Unexpected exception while replacing content tag", ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of content tags selected by user to replace
|
||||
*
|
||||
* @return a list of tags
|
||||
*/
|
||||
@Override
|
||||
Collection<? extends ContentTag> getTagsToReplace() {
|
||||
return Utilities.actionsGlobalContext().lookupAll(ContentTag.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* 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.actions;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.JMenu;
|
||||
import javax.swing.JMenuItem;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.actions.Presenter;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.casemodule.services.TagsManager;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.datamodel.Tag;
|
||||
import org.sleuthkit.datamodel.TagName;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
|
||||
/**
|
||||
* Abstract class to define context action to replace a tag with another
|
||||
*
|
||||
* @param <T> tag type
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"ReplaceTagAction.replaceTag=Replace Selected Tag(s) With"
|
||||
})
|
||||
abstract class ReplaceTagAction<T extends Tag> extends AbstractAction implements Presenter.Popup {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
protected static final String MENU_TEXT = NbBundle.getMessage(ReplaceTagAction.class,
|
||||
"ReplaceTagAction.replaceTag");
|
||||
|
||||
ReplaceTagAction(String menuText) {
|
||||
super(menuText);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses of replaceTagAction should not override actionPerformed,
|
||||
* but instead override replaceTag.
|
||||
*
|
||||
* @param event
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("NoopMethodInAbstractClass")
|
||||
public void actionPerformed(ActionEvent event) {
|
||||
}
|
||||
|
||||
protected String getActionDisplayName() {
|
||||
return MENU_TEXT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to actually replace the selected tag with the given new tag
|
||||
*
|
||||
* @param oldTag
|
||||
* @param newTagName
|
||||
*/
|
||||
abstract protected void replaceTag(T oldTag, TagName newTagName);
|
||||
|
||||
/**
|
||||
* Returns elected tags which are to be replaced
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
abstract Collection<? extends T> getTagsToReplace();
|
||||
|
||||
|
||||
@Override
|
||||
public JMenuItem getPopupPresenter() {
|
||||
return new ReplaceTagMenu();
|
||||
}
|
||||
|
||||
/**
|
||||
* Instances of this class implement a context menu user interface for
|
||||
* selecting a tag name to replace the tag with
|
||||
*/
|
||||
private final class ReplaceTagMenu extends JMenu {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
ReplaceTagMenu() {
|
||||
super(getActionDisplayName());
|
||||
|
||||
final Collection<? extends T> selectedTags = getTagsToReplace();
|
||||
|
||||
// Get the current set of tag names.
|
||||
Map<String, TagName> tagNamesMap = null;
|
||||
List<String> standardTagNames = TagsManager.getStandardTagNames();
|
||||
try {
|
||||
TagsManager tagsManager = Case.getCurrentCaseThrows().getServices().getTagsManager();
|
||||
tagNamesMap = new TreeMap<>(tagsManager.getDisplayNamesToTagNamesMap());
|
||||
} catch (TskCoreException | NoCurrentCaseException ex) {
|
||||
Logger.getLogger(ReplaceTagMenu.class.getName()).log(Level.SEVERE, "Failed to get tag names", ex); //NON-NLS
|
||||
}
|
||||
|
||||
List<JMenuItem> standardTagMenuitems = new ArrayList<>();
|
||||
// Ideally we should'nt allow user to pick a replacement tag that's already been applied to an item
|
||||
// In the very least we don't allow them to pick the same tag as the one they are trying to replace
|
||||
Set<String> existingTagNames = new HashSet<>();
|
||||
if (!selectedTags.isEmpty()) {
|
||||
T firstTag = selectedTags.iterator().next();
|
||||
existingTagNames.add(firstTag.getName().getDisplayName());
|
||||
}
|
||||
|
||||
if (null != tagNamesMap && !tagNamesMap.isEmpty()) {
|
||||
for (Map.Entry<String, TagName> entry : tagNamesMap.entrySet()) {
|
||||
String tagDisplayName = entry.getKey();
|
||||
String notableString = entry.getValue().getKnownStatus() == TskData.FileKnown.BAD ? TagsManager.getNotableTagLabel() : "";
|
||||
JMenuItem tagNameItem = new JMenuItem(tagDisplayName + notableString);
|
||||
// for the bookmark tag name only, added shortcut label
|
||||
if (tagDisplayName.equals(NbBundle.getMessage(AddTagAction.class, "AddBookmarkTagAction.bookmark.text"))) {
|
||||
tagNameItem.setAccelerator(AddBookmarkTagAction.BOOKMARK_SHORTCUT);
|
||||
}
|
||||
|
||||
// Add action to replace the tag
|
||||
tagNameItem.addActionListener((ActionEvent event) -> {
|
||||
selectedTags.forEach((oldtag) -> {
|
||||
replaceTag(oldtag, entry.getValue());
|
||||
});
|
||||
});
|
||||
|
||||
// Don't allow replacing a tag with same tag.
|
||||
if (existingTagNames.contains(tagDisplayName)) {
|
||||
tagNameItem.setEnabled(false);
|
||||
}
|
||||
|
||||
|
||||
// Show custom tags before predefined tags in the menu
|
||||
if (standardTagNames.contains(tagDisplayName)) {
|
||||
standardTagMenuitems.add(tagNameItem);
|
||||
} else {
|
||||
add(tagNameItem);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
JMenuItem empty = new JMenuItem(NbBundle.getMessage(this.getClass(), "AddTagAction.noTags"));
|
||||
empty.setEnabled(false);
|
||||
add(empty);
|
||||
}
|
||||
|
||||
//
|
||||
if (this.getItemCount() > 0) {
|
||||
addSeparator();
|
||||
}
|
||||
standardTagMenuitems.forEach((menuItem) -> {
|
||||
add(menuItem);
|
||||
});
|
||||
|
||||
addSeparator();
|
||||
JMenuItem newTagMenuItem = new JMenuItem(NbBundle.getMessage(this.getClass(), "AddTagAction.newTag"));
|
||||
newTagMenuItem.addActionListener((ActionEvent event) -> {
|
||||
TagName newTagName = GetTagNameDialog.doDialog();
|
||||
if (null != newTagName) {
|
||||
selectedTags.forEach((oldtag) -> {
|
||||
replaceTag(oldtag, newTagName);
|
||||
});
|
||||
}
|
||||
});
|
||||
add(newTagMenuItem);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011 Basis Technology Corp.
|
||||
* Copyright 2011-2018 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -18,15 +18,22 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.casemodule;
|
||||
|
||||
import java.awt.Frame;
|
||||
import javax.swing.JDialog;
|
||||
|
||||
/**
|
||||
* Dialog to show add image error messages
|
||||
*/
|
||||
public class AddImageErrorsDialog extends javax.swing.JDialog {
|
||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||
public class AddImageErrorsDialog extends JDialog {
|
||||
|
||||
/**
|
||||
* Creates new form AddImageErrorsDialog
|
||||
*
|
||||
* @param parent The parent frame.
|
||||
* @param modal Does this dialog act as a modal?
|
||||
*/
|
||||
public AddImageErrorsDialog(java.awt.Frame parent, boolean modal) {
|
||||
public AddImageErrorsDialog(Frame parent, boolean modal) {
|
||||
super(parent, modal);
|
||||
initComponents();
|
||||
}
|
||||
|
||||
@@ -57,6 +57,7 @@ import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
* {@link AddImageWizardIngestConfigPanel} (which is a bit weird if you ask m
|
||||
* -jm)
|
||||
*/
|
||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||
class AddImageWizardAddingProgressPanel extends ShortcutWizardDescriptorPanel {
|
||||
|
||||
private boolean readyToIngest = false;
|
||||
@@ -79,7 +80,7 @@ class AddImageWizardAddingProgressPanel extends ShortcutWizardDescriptorPanel {
|
||||
*/
|
||||
private AddImageWizardAddingProgressVisual component;
|
||||
private final Set<ChangeListener> listeners = new HashSet<>(1); // or can use ChangeSupport in NB 6.0
|
||||
private final List<Content> newContents = Collections.synchronizedList(new ArrayList<Content>());
|
||||
private final List<Content> newContents = Collections.synchronizedList(new ArrayList<>());
|
||||
private final DSPProgressMonitorImpl dspProgressMonitorImpl = new DSPProgressMonitorImpl();
|
||||
private IngestJobSettings ingestJobSettings;
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ import org.sleuthkit.autopsy.ingest.runIngestModuleWizard.ShortcutWizardDescript
|
||||
* The "Add Image" wizard panel1 handling the logic of selecting image file(s)
|
||||
* to add to Case, and pick the time zone.
|
||||
*/
|
||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||
class AddImageWizardDataSourceSettingsPanel extends ShortcutWizardDescriptorPanel implements PropertyChangeListener {
|
||||
|
||||
/**
|
||||
|
||||
@@ -40,10 +40,11 @@ import org.sleuthkit.autopsy.ingest.runIngestModuleWizard.ShortcutWizardDescript
|
||||
* TODO: review this for dead code. think about moving logic of adding image to
|
||||
* 3rd panel( {@link AddImageWizardAddingProgressPanel}) separate class -jm
|
||||
*/
|
||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||
class AddImageWizardIngestConfigPanel extends ShortcutWizardDescriptorPanel {
|
||||
|
||||
@Messages("AddImageWizardIngestConfigPanel.name.text=Configure Ingest Modules")
|
||||
private IngestJobSettingsPanel ingestJobSettingsPanel;
|
||||
private final IngestJobSettingsPanel ingestJobSettingsPanel;
|
||||
/**
|
||||
* The visual component that displays this panel. If you need to access the
|
||||
* component from this class, just use getComponent().
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-2017 Basis Technology Corp.
|
||||
* Copyright 2011-2018 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -36,6 +36,7 @@ import org.sleuthkit.autopsy.ingest.runIngestModuleWizard.ShortcutWizardDescript
|
||||
* Create a wizard panel which contains a panel allowing the selection of the
|
||||
* DataSourceProcessor
|
||||
*/
|
||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||
final class AddImageWizardSelectDspPanel extends ShortcutWizardDescriptorPanel implements PropertyChangeListener {
|
||||
|
||||
@NbBundle.Messages("SelectDataSourceProcessorPanel.name.text=Select Type of Data Source To Add")
|
||||
|
||||
@@ -279,7 +279,10 @@ final class LocalFilesPanel extends javax.swing.JPanel {
|
||||
*
|
||||
* @param paths Absolute paths to the selected data source
|
||||
*/
|
||||
@NbBundle.Messages("LocalFilesPanel.pathValidation.error=WARNING: Exception while gettting opon case.")
|
||||
@NbBundle.Messages({
|
||||
"LocalFilesPanel.pathValidation.dataSourceOnCDriveError=Warning: Path to multi-user data source is on \"C:\" drive",
|
||||
"LocalFilesPanel.pathValidation.getOpenCase=WARNING: Exception while gettting open case."
|
||||
})
|
||||
private void warnIfPathIsInvalid(final List<String> pathsList) {
|
||||
errorLabel.setVisible(false);
|
||||
|
||||
@@ -289,13 +292,13 @@ final class LocalFilesPanel extends javax.swing.JPanel {
|
||||
for (String currentPath : pathsList) {
|
||||
if (!PathValidator.isValid(currentPath, currentCaseType)) {
|
||||
errorLabel.setVisible(true);
|
||||
errorLabel.setText(NbBundle.getMessage(this.getClass(), "DataSourceOnCDriveError.text"));
|
||||
errorLabel.setText(Bundle.LocalFilesPanel_pathValidation_dataSourceOnCDriveError());
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
errorLabel.setVisible(true);
|
||||
errorLabel.setText(Bundle.LocalFilesPanel_pathValidation_error());
|
||||
errorLabel.setText(Bundle.LocalFilesPanel_pathValidation_getOpenCase());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-2017 Basis Technology Corp.
|
||||
* Copyright 2011-2018 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -28,6 +28,7 @@ import org.openide.windows.WindowManager;
|
||||
/**
|
||||
* This class extends a JDialog and maintains the MultiUserCasesPanel.
|
||||
*/
|
||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||
final class MultiUserCasesDialog extends JDialog {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@@ -121,6 +121,15 @@ public class TagsManager implements Closeable {
|
||||
return tagDisplayNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of names of standard/predefined tags
|
||||
*
|
||||
* @return list of predefined tag names
|
||||
*/
|
||||
public static List<String> getStandardTagNames() {
|
||||
return TagNameDefinition.getStandardTagNames();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a per case Autopsy service that manages the addition of
|
||||
* content and artifact tags to the case database.
|
||||
@@ -157,6 +166,21 @@ public class TagsManager implements Closeable {
|
||||
return caseDb.getTagNamesInUse();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @param dsObjId data source object id
|
||||
*
|
||||
* @return A list, possibly empty, of TagName data transfer objects (DTOs)
|
||||
* for the rows.
|
||||
*
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
public List<TagName> getTagNamesInUse(long dsObjId) throws TskCoreException {
|
||||
return caseDb.getTagNamesInUse(dsObjId);
|
||||
}
|
||||
/**
|
||||
* 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
|
||||
@@ -392,6 +416,24 @@ public class TagsManager implements Closeable {
|
||||
return caseDb.getContentTagsCountByTagName(tagName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets content tags count by tag name, for the given data source
|
||||
*
|
||||
* @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
|
||||
*
|
||||
* @return A count of the content tags with the specified tag name, and for
|
||||
* the given data source
|
||||
*
|
||||
* @throws TskCoreException If there is an error getting the tags count from
|
||||
* the case database.
|
||||
*/
|
||||
public long getContentTagsCountByTagName(TagName tagName, long dsObjId) throws TskCoreException {
|
||||
return caseDb.getContentTagsCountByTagName(tagName, dsObjId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a content tag by tag id.
|
||||
*
|
||||
@@ -421,6 +463,23 @@ public class TagsManager implements Closeable {
|
||||
return caseDb.getContentTagsByTagName(tagName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets content tags by tag name, for the given data source.
|
||||
*
|
||||
* @param tagName The tag name of interest.
|
||||
*
|
||||
* @param dsObjId data source object id
|
||||
*
|
||||
* @return A list, possibly empty, of the content tags with the specified
|
||||
* tag name, and for the given data source.
|
||||
*
|
||||
* @throws TskCoreException If there is an error getting the tags from the
|
||||
* case database.
|
||||
*/
|
||||
public List<ContentTag> getContentTagsByTagName(TagName tagName, long dsObjId) throws TskCoreException {
|
||||
return caseDb.getContentTagsByTagName(tagName, dsObjId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets content tags count by content.
|
||||
*
|
||||
@@ -522,6 +581,24 @@ public class TagsManager implements Closeable {
|
||||
return caseDb.getBlackboardArtifactTagsCountByTagName(tagName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an artifact tags count by tag name, for the given data source.
|
||||
*
|
||||
* @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
|
||||
*
|
||||
* @return A count of the artifact tags with the specified tag name,
|
||||
* for the given data source.
|
||||
*
|
||||
* @throws TskCoreException If there is an error getting the tags count from
|
||||
* the case database.
|
||||
*/
|
||||
public long getBlackboardArtifactTagsCountByTagName(TagName tagName, long dsObjId) throws TskCoreException {
|
||||
return caseDb.getBlackboardArtifactTagsCountByTagName(tagName, dsObjId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an artifact tag by tag id.
|
||||
*
|
||||
@@ -553,6 +630,24 @@ public class TagsManager implements Closeable {
|
||||
return caseDb.getBlackboardArtifactTagsByTagName(tagName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets artifact tags by tag name, for specified data source.
|
||||
*
|
||||
* @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
|
||||
*
|
||||
* @return A list, possibly empty, of the artifact tags with the specified
|
||||
* tag name, for the specified data source.
|
||||
*
|
||||
* @throws TskCoreException If there is an error getting the tags from the
|
||||
* case database.
|
||||
*/
|
||||
public List<BlackboardArtifactTag> getBlackboardArtifactTagsByTagName(TagName tagName, long dsObjId) throws TskCoreException {
|
||||
return caseDb.getBlackboardArtifactTagsByTagName(tagName, dsObjId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets artifact tags for a particular artifact.
|
||||
*
|
||||
|
||||
+7
-1
@@ -83,8 +83,13 @@ public final class AddEditCentralRepoCommentAction extends AbstractAction {
|
||||
* 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
|
||||
* the comment attached otherwise.
|
||||
*
|
||||
* The current comment for this instance is returned in case it is needed to
|
||||
* update the display.
|
||||
*
|
||||
* @return the current comment for this instance
|
||||
*/
|
||||
public void addEditCentralRepoComment() {
|
||||
public String addEditCentralRepoComment() {
|
||||
CentralRepoCommentDialog centralRepoCommentDialog = new CentralRepoCommentDialog(correlationAttribute, title);
|
||||
centralRepoCommentDialog.display();
|
||||
|
||||
@@ -103,6 +108,7 @@ public final class AddEditCentralRepoCommentAction extends AbstractAction {
|
||||
logger.log(Level.SEVERE, "Error connecting to Central Repository database.", ex);
|
||||
}
|
||||
}
|
||||
return centralRepoCommentDialog.getComment();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -31,6 +31,7 @@ final class CentralRepoCommentDialog extends javax.swing.JDialog {
|
||||
|
||||
private final CorrelationAttribute correlationAttribute;
|
||||
private boolean commentUpdated = false;
|
||||
private String currentComment = "";
|
||||
|
||||
/**
|
||||
* Create an instance.
|
||||
@@ -44,6 +45,11 @@ final class CentralRepoCommentDialog extends javax.swing.JDialog {
|
||||
initComponents();
|
||||
|
||||
CorrelationAttributeInstance instance = correlationAttribute.getInstances().get(0);
|
||||
|
||||
// Store the original comment
|
||||
if (instance.getComment() != null) {
|
||||
currentComment = instance.getComment();
|
||||
}
|
||||
|
||||
pathLabel.setText(instance.getFilePath());
|
||||
commentTextArea.setText(instance.getComment());
|
||||
@@ -71,6 +77,16 @@ final class CentralRepoCommentDialog extends javax.swing.JDialog {
|
||||
boolean isCommentUpdated() {
|
||||
return commentUpdated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current comment.
|
||||
* If the user hit OK, this will be the new comment.
|
||||
* If the user canceled, this will be the original comment.
|
||||
* @return the comment
|
||||
*/
|
||||
String getComment() {
|
||||
return currentComment;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called from within the constructor to initialize the form.
|
||||
@@ -168,8 +184,8 @@ final class CentralRepoCommentDialog extends javax.swing.JDialog {
|
||||
}//GEN-LAST:event_cancelButtonActionPerformed
|
||||
|
||||
private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed
|
||||
String comment = commentTextArea.getText();
|
||||
correlationAttribute.getInstances().get(0).setComment(comment);
|
||||
currentComment = commentTextArea.getText();
|
||||
correlationAttribute.getInstances().get(0).setComment(currentComment);
|
||||
commentUpdated = true;
|
||||
|
||||
dispose();
|
||||
|
||||
Executable
+56
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import javax.swing.Action;
|
||||
import org.openide.util.Utilities;
|
||||
import org.openide.util.lookup.ServiceProvider;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamArtifactUtil;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbUtil;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.ContextMenuActionsProvider;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
|
||||
/**
|
||||
* This creates a single context menu item for adding or editing a Central
|
||||
* Repository comment.
|
||||
*/
|
||||
@ServiceProvider(service = ContextMenuActionsProvider.class)
|
||||
public class CentralRepoContextMenuActionsProvider implements ContextMenuActionsProvider {
|
||||
|
||||
@Override
|
||||
public List<Action> getActions() {
|
||||
ArrayList<Action> actions = new ArrayList<>();
|
||||
Collection<? extends AbstractFile> selectedFiles = Utilities.actionsGlobalContext().lookupAll(AbstractFile.class);
|
||||
|
||||
if (selectedFiles.size() != 1) {
|
||||
return actions;
|
||||
}
|
||||
|
||||
for (AbstractFile file : selectedFiles) {
|
||||
if (EamDbUtil.useCentralRepo() && EamArtifactUtil.isSupportedAbstractFileType(file) && file.isFile()) {
|
||||
actions.add(AddEditCentralRepoCommentAction.createAddEditCentralRepoCommentAction(file));
|
||||
}
|
||||
}
|
||||
|
||||
return actions;
|
||||
}
|
||||
}
|
||||
@@ -4,3 +4,6 @@ DataContentViewerOtherCases.table.toolTip.text=Click column name to sort. Right-
|
||||
DataContentViewerOtherCases.exportToCSVMenuItem.text=Export Selected Rows to CSV
|
||||
DataContentViewerOtherCases.showCommonalityMenuItem.text=Show Frequency
|
||||
DataContentViewerOtherCases.addCommentMenuItem.text=Add/Edit Comment
|
||||
DataContentViewerOtherCases.earliestCaseDate.text=Earliest Case Date
|
||||
DataContentViewerOtherCases.earliestCaseLabel.toolTipText=
|
||||
DataContentViewerOtherCases.earliestCaseLabel.text=Central Repository Starting Date:
|
||||
|
||||
+52
-31
@@ -80,7 +80,7 @@
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="otherCasesPanel" pref="52" max="32767" attributes="0"/>
|
||||
<Component id="otherCasesPanel" pref="483" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
@@ -103,10 +103,10 @@
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<EmptySpace min="0" pref="60" max="32767" attributes="0"/>
|
||||
<EmptySpace min="0" pref="483" max="32767" attributes="0"/>
|
||||
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="tableContainerPanel" pref="52" max="32767" attributes="0"/>
|
||||
<Component id="tableContainerPanel" pref="483" max="32767" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="0" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
@@ -124,17 +124,36 @@
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="tableScrollPane" alignment="1" max="32767" attributes="0"/>
|
||||
<Component id="tableStatusPanel" alignment="1" max="32767" attributes="0"/>
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<Component id="tableStatusPanel" pref="1282" max="32767" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="218" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Component id="tableScrollPane" alignment="0" max="32767" attributes="0"/>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="earliestCaseLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="earliestCaseDate" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||
<Component id="tableStatusPanelLabel" max="32767" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="tableScrollPane" max="32767" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<Component id="tableScrollPane" pref="176" max="32767" attributes="0"/>
|
||||
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="1" attributes="0">
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="earliestCaseLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="earliestCaseDate" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Component id="tableStatusPanelLabel" min="-2" pref="16" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
|
||||
<Component id="tableStatusPanel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
@@ -174,6 +193,23 @@
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Component class="javax.swing.JLabel" name="earliestCaseLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle.properties" key="DataContentViewerOtherCases.earliestCaseLabel.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/centralrepository/contentviewer/Bundle.properties" key="DataContentViewerOtherCases.earliestCaseLabel.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="earliestCaseDate">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle.properties" key="DataContentViewerOtherCases.earliestCaseDate.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Container class="javax.swing.JPanel" name="tableStatusPanel">
|
||||
<Properties>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
@@ -185,37 +221,22 @@
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
|
||||
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="tableStatusPanelLabel" pref="780" max="32767" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<EmptySpace min="0" pref="16" max="32767" attributes="0"/>
|
||||
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="tableStatusPanelLabel" min="-2" pref="16" max="-2" attributes="0"/>
|
||||
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<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>
|
||||
</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>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
|
||||
+193
-133
@@ -25,11 +25,15 @@ import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.logging.Level;
|
||||
@@ -45,6 +49,8 @@ import javax.swing.JPanel;
|
||||
import javax.swing.filechooser.FileNameExtensionFilter;
|
||||
import javax.swing.table.TableCellRenderer;
|
||||
import javax.swing.table.TableColumn;
|
||||
import org.joda.time.DateTimeZone;
|
||||
import org.joda.time.LocalDateTime;
|
||||
import org.openide.nodes.Node;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.openide.util.lookup.ServiceProvider;
|
||||
@@ -56,7 +62,6 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamArtifactUtil;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationDataSource;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
@@ -69,7 +74,6 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbUtil;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
import org.sleuthkit.datamodel.TskDataException;
|
||||
|
||||
/**
|
||||
* View correlation results from other cases
|
||||
@@ -80,6 +84,8 @@ import org.sleuthkit.datamodel.TskDataException;
|
||||
"DataContentViewerOtherCases.toolTip=Displays instances of the selected file/artifact from other occurrences.",})
|
||||
public class DataContentViewerOtherCases extends JPanel implements DataContentViewer {
|
||||
|
||||
private static final long serialVersionUID = -1L;
|
||||
|
||||
private final static Logger logger = Logger.getLogger(DataContentViewerOtherCases.class.getName());
|
||||
|
||||
private final DataContentViewerOtherCasesTableModel tableModel;
|
||||
@@ -119,10 +125,15 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
} else if (jmi.equals(showCommonalityMenuItem)) {
|
||||
showCommonalityDetails();
|
||||
} else if (jmi.equals(addCommentMenuItem)) {
|
||||
CorrelationAttribute selectedAttribute = (CorrelationAttribute) tableModel.getRow(otherCasesTable.getSelectedRow());
|
||||
AddEditCentralRepoCommentAction action = AddEditCentralRepoCommentAction.createAddEditCommentAction(selectedAttribute);
|
||||
action.addEditCentralRepoComment();
|
||||
otherCasesTable.repaint();
|
||||
try {
|
||||
OtherOccurrenceNodeData selectedNode = (OtherOccurrenceNodeData) tableModel.getRow(otherCasesTable.getSelectedRow());
|
||||
AddEditCentralRepoCommentAction action = AddEditCentralRepoCommentAction.createAddEditCommentAction(selectedNode.createCorrelationAttribute());
|
||||
String currentComment = action.addEditCentralRepoComment();
|
||||
selectedNode.updateComment(currentComment);
|
||||
otherCasesTable.repaint();
|
||||
} catch (EamDbException ex) {
|
||||
logger.log(Level.SEVERE, "Error performing Add/Edit Comment action", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -137,6 +148,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
TableCellRenderer renderer = new DataContentViewerOtherCasesTableCellRenderer();
|
||||
otherCasesTable.setDefaultRenderer(Object.class, renderer);
|
||||
tableStatusPanelLabel.setVisible(false);
|
||||
|
||||
}
|
||||
|
||||
@Messages({"DataContentViewerOtherCases.correlatedArtifacts.isEmpty=There are no files or artifacts to correlate.",
|
||||
@@ -157,7 +169,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
Bundle.DataContentViewerOtherCases_correlatedArtifacts_title(),
|
||||
DEFAULT_OPTION, PLAIN_MESSAGE);
|
||||
} else {
|
||||
StringBuilder msg = new StringBuilder();
|
||||
StringBuilder msg = new StringBuilder(correlationAttributes.size());
|
||||
int percentage;
|
||||
try {
|
||||
EamDb dbManager = EamDb.getInstance();
|
||||
@@ -202,8 +214,8 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
if (-1 != selectedRowViewIdx) {
|
||||
EamDb dbManager = EamDb.getInstance();
|
||||
int selectedRowModelIdx = otherCasesTable.convertRowIndexToModel(selectedRowViewIdx);
|
||||
CorrelationAttribute eamArtifact = (CorrelationAttribute) tableModel.getRow(selectedRowModelIdx);
|
||||
CorrelationCase eamCasePartial = eamArtifact.getInstances().get(0).getCorrelationCase();
|
||||
OtherOccurrenceNodeData nodeData = (OtherOccurrenceNodeData) tableModel.getRow(selectedRowModelIdx);
|
||||
CorrelationCase eamCasePartial = nodeData.getCorrelationAttributeInstance().getCorrelationCase();
|
||||
if (eamCasePartial == null) {
|
||||
JOptionPane.showConfirmDialog(showCaseDetailsMenuItem,
|
||||
Bundle.DataContentViewerOtherCases_caseDetailsDialog_noDetailsReference(),
|
||||
@@ -307,6 +319,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
// start with empty table
|
||||
tableModel.clearTable();
|
||||
correlationAttributes.clear();
|
||||
earliestCaseDate.setText(Bundle.DataContentViewerOtherCases_earliestCaseNotAvailable());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -453,10 +466,45 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Messages({"DataContentViewerOtherCases.earliestCaseNotAvailable= Not Enabled."})
|
||||
/**
|
||||
* Query the database for artifact instances from other cases correlated to
|
||||
* the given central repository artifact. Instances from the same datasource
|
||||
* / device will also be included.
|
||||
* Gets the list of Eam Cases and determines the earliest case creation date.
|
||||
* Sets the label to display the earliest date string to the user.
|
||||
*/
|
||||
private void setEarliestCaseDate() {
|
||||
String dateStringDisplay = Bundle.DataContentViewerOtherCases_earliestCaseNotAvailable();
|
||||
|
||||
if (EamDb.isEnabled()) {
|
||||
LocalDateTime earliestDate = LocalDateTime.now(DateTimeZone.UTC);
|
||||
DateFormat datetimeFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.US);
|
||||
try {
|
||||
EamDb dbManager = EamDb.getInstance();
|
||||
List<CorrelationCase> cases = dbManager.getCases();
|
||||
for (CorrelationCase aCase : cases) {
|
||||
LocalDateTime caseDate = LocalDateTime.fromDateFields(datetimeFormat.parse(aCase.getCreationDate()));
|
||||
|
||||
if (caseDate.isBefore(earliestDate)) {
|
||||
earliestDate = caseDate;
|
||||
dateStringDisplay = aCase.getCreationDate();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} catch (EamDbException ex) {
|
||||
logger.log(Level.SEVERE, "Error getting list of cases from database.", ex); // NON-NLS
|
||||
} catch (ParseException ex) {
|
||||
logger.log(Level.SEVERE, "Error parsing date of cases from database.", ex); // NON-NLS
|
||||
}
|
||||
|
||||
}
|
||||
earliestCaseDate.setText(dateStringDisplay);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query the central repo database (if enabled) and the case database to find all
|
||||
* artifact instances correlated to the given central repository artifact. If the
|
||||
* central repo is not enabled, this will only return files from the current case
|
||||
* with matching MD5 hashes.
|
||||
*
|
||||
* @param corAttr CorrelationAttribute to query for
|
||||
* @param dataSourceName Data source to filter results
|
||||
@@ -464,33 +512,46 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
*
|
||||
* @return A collection of correlated artifact instances
|
||||
*/
|
||||
private Map<UniquePathKey, CorrelationAttributeInstance> getCorrelatedInstances(CorrelationAttribute corAttr, String dataSourceName, String deviceId) {
|
||||
private Map<UniquePathKey,OtherOccurrenceNodeData> getCorrelatedInstances(CorrelationAttribute corAttr, String dataSourceName, String deviceId) {
|
||||
// @@@ Check exception
|
||||
try {
|
||||
final Case openCase = Case.getCurrentCase();
|
||||
String caseUUID = openCase.getName();
|
||||
String filePath = (file.getParentPath() + file.getName()).toLowerCase();
|
||||
HashMap<UniquePathKey, CorrelationAttributeInstance> artifactInstances = new HashMap<>();
|
||||
|
||||
HashMap<UniquePathKey,OtherOccurrenceNodeData> nodeDataMap = new HashMap<>();
|
||||
|
||||
if (EamDb.isEnabled()) {
|
||||
EamDb dbManager = EamDb.getInstance();
|
||||
artifactInstances.putAll(dbManager.getArtifactInstancesByTypeValue(corAttr.getCorrelationType(), corAttr.getCorrelationValue()).stream()
|
||||
.filter(artifactInstance -> !artifactInstance.getFilePath().equals(filePath)
|
||||
|| !artifactInstance.getCorrelationCase().getCaseUUID().equals(caseUUID)
|
||||
|| !artifactInstance.getCorrelationDataSource().getName().equals(dataSourceName)
|
||||
|| !artifactInstance.getCorrelationDataSource().getDeviceID().equals(deviceId))
|
||||
.collect(Collectors.toMap(correlationAttr -> new UniquePathKey(correlationAttr.getCorrelationDataSource().getDeviceID(), correlationAttr.getFilePath()),
|
||||
correlationAttr -> correlationAttr)));
|
||||
}
|
||||
List<CorrelationAttributeInstance> instances = EamDb.getInstance().getArtifactInstancesByTypeValue(corAttr.getCorrelationType(), corAttr.getCorrelationValue());
|
||||
|
||||
if (corAttr.getCorrelationType().getDisplayName().equals("Files")) {
|
||||
List<AbstractFile> caseDbFiles = addCaseDbMatches(corAttr, openCase);
|
||||
for (AbstractFile caseDbFile : caseDbFiles) {
|
||||
addOrUpdateAttributeInstance(openCase, artifactInstances, caseDbFile);
|
||||
for (CorrelationAttributeInstance artifactInstance:instances) {
|
||||
|
||||
// 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:
|
||||
// - the case UUID is different
|
||||
// - the data source name is different
|
||||
// - the data source device ID is different
|
||||
// - the file path is different
|
||||
if (!artifactInstance.getCorrelationCase().getCaseUUID().equals(caseUUID)
|
||||
|| !artifactInstance.getCorrelationDataSource().getName().equals(dataSourceName)
|
||||
|| !artifactInstance.getCorrelationDataSource().getDeviceID().equals(deviceId)
|
||||
|| !artifactInstance.getFilePath().equalsIgnoreCase(file.getParentPath() + file.getName())) {
|
||||
|
||||
OtherOccurrenceNodeData newNode = new OtherOccurrenceNodeData(artifactInstance, corAttr.getCorrelationType(), corAttr.getCorrelationValue());
|
||||
UniquePathKey uniquePathKey = new UniquePathKey(newNode);
|
||||
nodeDataMap.put(uniquePathKey, newNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return artifactInstances;
|
||||
if (corAttr.getCorrelationType().getDisplayName().equals("Files")) {
|
||||
List<AbstractFile> caseDbFiles = getCaseDbMatches(corAttr, openCase);
|
||||
|
||||
for (AbstractFile caseDbFile : caseDbFiles) {
|
||||
addOrUpdateNodeData(openCase, nodeDataMap, caseDbFile);
|
||||
}
|
||||
}
|
||||
|
||||
return nodeDataMap;
|
||||
} catch (EamDbException ex) {
|
||||
logger.log(Level.SEVERE, "Error getting artifact instances from database.", ex); // NON-NLS
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
@@ -504,9 +565,17 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
return new HashMap<>(0);
|
||||
}
|
||||
|
||||
private List<AbstractFile> addCaseDbMatches(CorrelationAttribute corAttr, Case openCase) throws NoCurrentCaseException, TskCoreException, EamDbException {
|
||||
/**
|
||||
* 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 openCase The current case
|
||||
* @return List of matching AbstractFile objects
|
||||
* @throws NoCurrentCaseException
|
||||
* @throws TskCoreException
|
||||
* @throws EamDbException
|
||||
*/
|
||||
private List<AbstractFile> getCaseDbMatches(CorrelationAttribute corAttr, Case openCase) throws NoCurrentCaseException, TskCoreException, EamDbException {
|
||||
String md5 = corAttr.getCorrelationValue();
|
||||
|
||||
SleuthkitCase tsk = openCase.getSleuthkitCase();
|
||||
List<AbstractFile> matches = tsk.findAllFilesWhere(String.format("md5 = '%s'", new Object[]{md5}));
|
||||
|
||||
@@ -522,75 +591,64 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the file to the artifactInstances map if it does not already exist
|
||||
*
|
||||
* @param autopsyCase
|
||||
* @param artifactInstances
|
||||
* Adds the file to the nodeDataMap map if it does not already exist
|
||||
*
|
||||
* @param autopsyCase
|
||||
* @param nodeDataMap
|
||||
* @param newFile
|
||||
*
|
||||
* @throws TskCoreException
|
||||
* @throws EamDbException
|
||||
*/
|
||||
private void addOrUpdateAttributeInstance(final Case autopsyCase, Map<UniquePathKey, CorrelationAttributeInstance> artifactInstances, AbstractFile newFile) throws TskCoreException, EamDbException {
|
||||
|
||||
// figure out if the casedb file is known via either hash or tags
|
||||
TskData.FileKnown localKnown = newFile.getKnown();
|
||||
|
||||
if (localKnown != TskData.FileKnown.BAD) {
|
||||
private void addOrUpdateNodeData(final Case autopsyCase, Map<UniquePathKey,OtherOccurrenceNodeData> nodeDataMap, AbstractFile newFile) throws TskCoreException, EamDbException {
|
||||
|
||||
OtherOccurrenceNodeData newNode = new OtherOccurrenceNodeData(newFile, autopsyCase);
|
||||
|
||||
// If the caseDB object has a notable tag associated with it, update
|
||||
// the known status to BAD
|
||||
if (newNode.getKnown() != TskData.FileKnown.BAD) {
|
||||
List<ContentTag> fileMatchTags = autopsyCase.getServices().getTagsManager().getContentTagsByContent(newFile);
|
||||
for (ContentTag tag : fileMatchTags) {
|
||||
TskData.FileKnown tagKnownStatus = tag.getName().getKnownStatus();
|
||||
if (tagKnownStatus.equals(TskData.FileKnown.BAD)) {
|
||||
localKnown = TskData.FileKnown.BAD;
|
||||
newNode.updateKnown(TskData.FileKnown.BAD);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// make a key to see if the file is already in the map
|
||||
String filePath = newFile.getParentPath() + newFile.getName();
|
||||
String deviceId;
|
||||
try {
|
||||
deviceId = autopsyCase.getSleuthkitCase().getDataSource(newFile.getDataSource().getId()).getDeviceId();
|
||||
} catch (TskDataException | TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "Error getting data source info: {0}", ex);
|
||||
return;
|
||||
}
|
||||
UniquePathKey uniquePathKey = new UniquePathKey(deviceId, filePath);
|
||||
|
||||
// double check that the CR version is BAD if the caseDB version is BAD.
|
||||
if (artifactInstances.containsKey(uniquePathKey)) {
|
||||
if (localKnown == TskData.FileKnown.BAD) {
|
||||
CorrelationAttributeInstance prevInstance = artifactInstances.get(uniquePathKey);
|
||||
prevInstance.setKnownStatus(localKnown);
|
||||
// Make a key to see if the file is already in the map
|
||||
UniquePathKey uniquePathKey = new UniquePathKey(newNode);
|
||||
|
||||
// If this node is already in the list, the only thing we need to do is
|
||||
// update the known status to BAD if the caseDB version had known status BAD.
|
||||
// Otherwise this is a new node so add the new node to the map.
|
||||
if (nodeDataMap.containsKey(uniquePathKey)) {
|
||||
if (newNode.getKnown() == TskData.FileKnown.BAD) {
|
||||
OtherOccurrenceNodeData prevInstance = nodeDataMap.get(uniquePathKey);
|
||||
prevInstance.updateKnown(newNode.getKnown());
|
||||
}
|
||||
} // add the data from the case DB by pushing data into CorrelationAttributeInstance class
|
||||
else {
|
||||
// NOTE: If we are in here, it is likely because CR is not enabled. So, we cannot rely
|
||||
// on any of the methods that query the DB.
|
||||
CorrelationCase correlationCase = new CorrelationCase(autopsyCase.getName(), autopsyCase.getDisplayName());
|
||||
|
||||
CorrelationDataSource correlationDataSource = CorrelationDataSource.fromTSKDataSource(correlationCase, newFile.getDataSource());
|
||||
|
||||
CorrelationAttributeInstance caseDbInstance = new CorrelationAttributeInstance(correlationCase, correlationDataSource, filePath, "", localKnown);
|
||||
artifactInstances.put(uniquePathKey, caseDbInstance);
|
||||
} else {
|
||||
nodeDataMap.put(uniquePathKey, newNode);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSupported(Node node) {
|
||||
this.file = this.getAbstractFileFromNode(node);
|
||||
// Is supported if this node
|
||||
// has correlatable content (File, BlackboardArtifact) OR
|
||||
// other common files across datasources.
|
||||
|
||||
// Is supported if one of the following is true:
|
||||
// - The central repo is enabled and the node has correlatable content
|
||||
// (either through the MD5 hash of the associated file or through a BlackboardArtifact)
|
||||
// - The central repo is disabled and the backing file has a valid MD5 hash
|
||||
this.file = this.getAbstractFileFromNode(node);
|
||||
if (EamDb.isEnabled()) {
|
||||
return this.file != null
|
||||
&& this.file.getSize() > 0
|
||||
&& !getCorrelationAttributesFromNode(node).isEmpty();
|
||||
} else {
|
||||
return this.file != null
|
||||
&& this.file.getSize() > 0;
|
||||
&& this.file.getSize() > 0
|
||||
&& ((this.file.getMd5Hash() != null) && ( ! this.file.getMd5Hash().isEmpty()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -632,22 +690,14 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
// get the attributes we can correlate on
|
||||
correlationAttributes.addAll(getCorrelationAttributesFromNode(node));
|
||||
for (CorrelationAttribute corAttr : correlationAttributes) {
|
||||
Map<UniquePathKey, CorrelationAttributeInstance> corAttrInstances = new HashMap<>(0);
|
||||
Map<UniquePathKey,OtherOccurrenceNodeData> correlatedNodeDataMap = new HashMap<>(0);
|
||||
|
||||
// get correlation and reference set instances from DB
|
||||
corAttrInstances.putAll(getCorrelatedInstances(corAttr, dataSourceName, deviceId));
|
||||
correlatedNodeDataMap.putAll(getCorrelatedInstances(corAttr, dataSourceName, deviceId));
|
||||
|
||||
correlatedNodeDataMap.values().forEach((nodeData) -> {
|
||||
tableModel.addNodeData(nodeData);
|
||||
|
||||
corAttrInstances.values().forEach((corAttrInstance) -> {
|
||||
try {
|
||||
CorrelationAttribute newCeArtifact = new CorrelationAttribute(
|
||||
corAttr.getCorrelationType(),
|
||||
corAttr.getCorrelationValue()
|
||||
);
|
||||
newCeArtifact.addInstance(corAttrInstance);
|
||||
tableModel.addEamArtifact(newCeArtifact);
|
||||
} catch (EamDbException ex) {
|
||||
logger.log(Level.SEVERE, "Error creating correlation attribute", ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -660,6 +710,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
clearMessageOnTableStatusPanel();
|
||||
setColumnWidths();
|
||||
}
|
||||
setEarliestCaseDate();
|
||||
}
|
||||
|
||||
private void setColumnWidths() {
|
||||
@@ -701,6 +752,8 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
tableContainerPanel = new javax.swing.JPanel();
|
||||
tableScrollPane = new javax.swing.JScrollPane();
|
||||
otherCasesTable = new javax.swing.JTable();
|
||||
earliestCaseLabel = new javax.swing.JLabel();
|
||||
earliestCaseDate = new javax.swing.JLabel();
|
||||
tableStatusPanel = new javax.swing.JPanel();
|
||||
tableStatusPanelLabel = new javax.swing.JLabel();
|
||||
|
||||
@@ -746,44 +799,55 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
otherCasesTable.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_INTERVAL_SELECTION);
|
||||
tableScrollPane.setViewportView(otherCasesTable);
|
||||
|
||||
tableStatusPanel.setPreferredSize(new java.awt.Dimension(1500, 16));
|
||||
org.openide.awt.Mnemonics.setLocalizedText(earliestCaseLabel, org.openide.util.NbBundle.getMessage(DataContentViewerOtherCases.class, "DataContentViewerOtherCases.earliestCaseLabel.text")); // NOI18N
|
||||
earliestCaseLabel.setToolTipText(org.openide.util.NbBundle.getMessage(DataContentViewerOtherCases.class, "DataContentViewerOtherCases.earliestCaseLabel.toolTipText")); // NOI18N
|
||||
|
||||
tableStatusPanelLabel.setForeground(new java.awt.Color(255, 0, 51));
|
||||
org.openide.awt.Mnemonics.setLocalizedText(earliestCaseDate, org.openide.util.NbBundle.getMessage(DataContentViewerOtherCases.class, "DataContentViewerOtherCases.earliestCaseDate.text")); // NOI18N
|
||||
|
||||
tableStatusPanel.setPreferredSize(new java.awt.Dimension(1500, 16));
|
||||
|
||||
javax.swing.GroupLayout tableStatusPanelLayout = new javax.swing.GroupLayout(tableStatusPanel);
|
||||
tableStatusPanel.setLayout(tableStatusPanelLayout);
|
||||
tableStatusPanelLayout.setHorizontalGroup(
|
||||
tableStatusPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 0, Short.MAX_VALUE)
|
||||
.addGroup(tableStatusPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(tableStatusPanelLayout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(tableStatusPanelLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 780, Short.MAX_VALUE)
|
||||
.addContainerGap()))
|
||||
);
|
||||
tableStatusPanelLayout.setVerticalGroup(
|
||||
tableStatusPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 16, Short.MAX_VALUE)
|
||||
.addGroup(tableStatusPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(tableStatusPanelLayout.createSequentialGroup()
|
||||
.addComponent(tableStatusPanelLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(0, 0, Short.MAX_VALUE)))
|
||||
);
|
||||
|
||||
tableStatusPanelLabel.setForeground(new java.awt.Color(255, 0, 51));
|
||||
|
||||
javax.swing.GroupLayout tableContainerPanelLayout = new javax.swing.GroupLayout(tableContainerPanel);
|
||||
tableContainerPanel.setLayout(tableContainerPanelLayout);
|
||||
tableContainerPanelLayout.setHorizontalGroup(
|
||||
tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(tableScrollPane, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(tableStatusPanel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, tableContainerPanelLayout.createSequentialGroup()
|
||||
.addComponent(tableStatusPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 1282, Short.MAX_VALUE)
|
||||
.addGap(218, 218, 218))
|
||||
.addComponent(tableScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addGroup(tableContainerPanelLayout.createSequentialGroup()
|
||||
.addComponent(earliestCaseLabel)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(earliestCaseDate)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addComponent(tableStatusPanelLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addContainerGap())
|
||||
);
|
||||
tableContainerPanelLayout.setVerticalGroup(
|
||||
tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(tableContainerPanelLayout.createSequentialGroup()
|
||||
.addComponent(tableScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, tableContainerPanelLayout.createSequentialGroup()
|
||||
.addComponent(tableScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 176, Short.MAX_VALUE)
|
||||
.addGap(0, 0, 0)
|
||||
.addGroup(tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
|
||||
.addGroup(tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(earliestCaseLabel)
|
||||
.addComponent(earliestCaseDate))
|
||||
.addComponent(tableStatusPanelLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addGap(0, 0, 0)
|
||||
.addComponent(tableStatusPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addContainerGap())
|
||||
.addGap(0, 0, 0))
|
||||
);
|
||||
|
||||
javax.swing.GroupLayout otherCasesPanelLayout = new javax.swing.GroupLayout(otherCasesPanel);
|
||||
@@ -796,10 +860,10 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
);
|
||||
otherCasesPanelLayout.setVerticalGroup(
|
||||
otherCasesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 60, Short.MAX_VALUE)
|
||||
.addGap(0, 483, Short.MAX_VALUE)
|
||||
.addGroup(otherCasesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(otherCasesPanelLayout.createSequentialGroup()
|
||||
.addComponent(tableContainerPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 52, Short.MAX_VALUE)
|
||||
.addComponent(tableContainerPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 483, Short.MAX_VALUE)
|
||||
.addGap(0, 0, 0)))
|
||||
);
|
||||
|
||||
@@ -811,28 +875,31 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(otherCasesPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 52, Short.MAX_VALUE)
|
||||
.addComponent(otherCasesPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 483, Short.MAX_VALUE)
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
private void rightClickPopupMenuPopupMenuWillBecomeVisible(javax.swing.event.PopupMenuEvent evt) {//GEN-FIRST:event_rightClickPopupMenuPopupMenuWillBecomeVisible
|
||||
boolean addCommentMenuItemVisible = false;
|
||||
boolean enableCentralRepoActions = false;
|
||||
|
||||
if (EamDbUtil.useCentralRepo() && otherCasesTable.getSelectedRowCount() == 1) {
|
||||
int rowIndex = otherCasesTable.getSelectedRow();
|
||||
CorrelationAttribute selectedAttribute = (CorrelationAttribute) tableModel.getRow(rowIndex);
|
||||
if (selectedAttribute.getInstances().get(0).isDatabaseInstance()
|
||||
&& selectedAttribute.getCorrelationType().getId() == CorrelationAttribute.FILES_TYPE_ID) {
|
||||
addCommentMenuItemVisible = true;
|
||||
OtherOccurrenceNodeData selectedNode = (OtherOccurrenceNodeData) tableModel.getRow(rowIndex);
|
||||
if (selectedNode.isCentralRepoNode()) {
|
||||
enableCentralRepoActions = true;
|
||||
}
|
||||
}
|
||||
|
||||
addCommentMenuItem.setVisible(addCommentMenuItemVisible);
|
||||
addCommentMenuItem.setVisible(enableCentralRepoActions);
|
||||
showCaseDetailsMenuItem.setVisible(enableCentralRepoActions);
|
||||
showCommonalityMenuItem.setVisible(enableCentralRepoActions);
|
||||
}//GEN-LAST:event_rightClickPopupMenuPopupMenuWillBecomeVisible
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JFileChooser CSVFileChooser;
|
||||
private javax.swing.JMenuItem addCommentMenuItem;
|
||||
private javax.swing.JLabel earliestCaseDate;
|
||||
private javax.swing.JLabel earliestCaseLabel;
|
||||
private javax.swing.JMenuItem exportToCSVMenuItem;
|
||||
private javax.swing.JPanel otherCasesPanel;
|
||||
private javax.swing.JTable otherCasesTable;
|
||||
@@ -854,33 +921,26 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
|
||||
private final String dataSourceID;
|
||||
private final String filePath;
|
||||
private final String type;
|
||||
|
||||
UniquePathKey(String theDataSource, String theFilePath) {
|
||||
UniquePathKey(OtherOccurrenceNodeData nodeData) {
|
||||
super();
|
||||
dataSourceID = theDataSource;
|
||||
filePath = theFilePath.toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return the dataSourceID device ID
|
||||
*/
|
||||
String getDataSourceID() {
|
||||
return dataSourceID;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return the filPath including the filename and extension.
|
||||
*/
|
||||
String getFilePath() {
|
||||
return filePath;
|
||||
dataSourceID = nodeData.getDeviceID();
|
||||
if (nodeData.getFilePath() != null) {
|
||||
filePath = nodeData.getFilePath().toLowerCase();
|
||||
} else {
|
||||
filePath = null;
|
||||
}
|
||||
type = nodeData.getType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (other instanceof UniquePathKey) {
|
||||
return ((UniquePathKey) other).getDataSourceID().equals(dataSourceID) && ((UniquePathKey) other).getFilePath().equals(filePath);
|
||||
UniquePathKey otherKey = (UniquePathKey)(other);
|
||||
return ( Objects.equals(otherKey.dataSourceID, this.dataSourceID)
|
||||
&& Objects.equals(otherKey.filePath, this.filePath)
|
||||
&& Objects.equals(otherKey.type, this.type));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -890,7 +950,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
//int hash = 7;
|
||||
//hash = 67 * hash + this.dataSourceID.hashCode();
|
||||
//hash = 67 * hash + this.filePath.hashCode();
|
||||
return Objects.hash(dataSourceID, filePath);
|
||||
return Objects.hash(dataSourceID, filePath, type);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+24
-26
@@ -68,10 +68,10 @@ public class DataContentViewerOtherCasesTableModel extends AbstractTableModel {
|
||||
}
|
||||
};
|
||||
|
||||
List<CorrelationAttribute> eamArtifacts;
|
||||
List<OtherOccurrenceNodeData> nodeDataList;
|
||||
|
||||
DataContentViewerOtherCasesTableModel() {
|
||||
eamArtifacts = new ArrayList<>();
|
||||
nodeDataList = new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -95,7 +95,7 @@ public class DataContentViewerOtherCasesTableModel extends AbstractTableModel {
|
||||
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
return eamArtifacts.size();
|
||||
return nodeDataList.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -105,15 +105,15 @@ public class DataContentViewerOtherCasesTableModel extends AbstractTableModel {
|
||||
|
||||
@Override
|
||||
public Object getValueAt(int rowIdx, int colIdx) {
|
||||
if (0 == eamArtifacts.size()) {
|
||||
if (0 == nodeDataList.size()) {
|
||||
return Bundle.DataContentViewerOtherCasesTableModel_noData();
|
||||
}
|
||||
|
||||
return mapValueById(rowIdx, TableColumns.values()[colIdx]);
|
||||
}
|
||||
|
||||
public Object getRow(int rowIdx) {
|
||||
return eamArtifacts.get(rowIdx);
|
||||
Object getRow(int rowIdx) {
|
||||
return nodeDataList.get(rowIdx);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -125,40 +125,39 @@ public class DataContentViewerOtherCasesTableModel extends AbstractTableModel {
|
||||
* @return value in the cell
|
||||
*/
|
||||
private Object mapValueById(int rowIdx, TableColumns colId) {
|
||||
CorrelationAttribute eamArtifact = eamArtifacts.get(rowIdx);
|
||||
CorrelationAttributeInstance eamArtifactInstance = eamArtifact.getInstances().get(0);
|
||||
OtherOccurrenceNodeData nodeData = nodeDataList.get(rowIdx);
|
||||
String value = Bundle.DataContentViewerOtherCasesTableModel_noData();
|
||||
|
||||
switch (colId) {
|
||||
case CASE_NAME:
|
||||
if (null != eamArtifactInstance.getCorrelationCase()) {
|
||||
value = eamArtifactInstance.getCorrelationCase().getDisplayName();
|
||||
if (null != nodeData.getCaseName()) {
|
||||
value = nodeData.getCaseName();
|
||||
}
|
||||
break;
|
||||
case DEVICE:
|
||||
if (null != eamArtifactInstance.getCorrelationDataSource()) {
|
||||
value = eamArtifactInstance.getCorrelationDataSource().getDeviceID();
|
||||
if (null != nodeData.getDeviceID()) {
|
||||
value = nodeData.getDeviceID();
|
||||
}
|
||||
break;
|
||||
case DATA_SOURCE:
|
||||
if (null != eamArtifactInstance.getCorrelationDataSource()) {
|
||||
value = eamArtifactInstance.getCorrelationDataSource().getName();
|
||||
if (null != nodeData.getDataSourceName()) {
|
||||
value = nodeData.getDataSourceName();
|
||||
}
|
||||
break;
|
||||
case FILE_PATH:
|
||||
value = eamArtifactInstance.getFilePath();
|
||||
value = nodeData.getFilePath();
|
||||
break;
|
||||
case TYPE:
|
||||
value = eamArtifact.getCorrelationType().getDisplayName();
|
||||
value = nodeData.getType();
|
||||
break;
|
||||
case VALUE:
|
||||
value = eamArtifact.getCorrelationValue();
|
||||
value = nodeData.getValue();
|
||||
break;
|
||||
case KNOWN:
|
||||
value = eamArtifactInstance.getKnownStatus().getName();
|
||||
value = nodeData.getKnown().getName();
|
||||
break;
|
||||
case COMMENT:
|
||||
value = eamArtifactInstance.getComment();
|
||||
value = nodeData.getComment();
|
||||
break;
|
||||
}
|
||||
return value;
|
||||
@@ -170,18 +169,17 @@ public class DataContentViewerOtherCasesTableModel extends AbstractTableModel {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add one local central repository artifact to the table.
|
||||
* Add one correlated instance object to the table
|
||||
*
|
||||
* @param eamArtifact central repository artifact to add to the
|
||||
* table
|
||||
* @param newNodeData data to add to the table
|
||||
*/
|
||||
public void addEamArtifact(CorrelationAttribute eamArtifact) {
|
||||
eamArtifacts.add(eamArtifact);
|
||||
void addNodeData(OtherOccurrenceNodeData newNodeData) {
|
||||
nodeDataList.add(newNodeData);
|
||||
fireTableDataChanged();
|
||||
}
|
||||
|
||||
public void clearTable() {
|
||||
eamArtifacts.clear();
|
||||
void clearTable() {
|
||||
nodeDataList.clear();
|
||||
fireTableDataChanged();
|
||||
}
|
||||
|
||||
|
||||
+233
@@ -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 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;
|
||||
}
|
||||
}
|
||||
@@ -1772,7 +1772,7 @@ abstract class AbstractSqlEamDb implements EamDb {
|
||||
} catch (SQLException ex) {
|
||||
throw new EamDbException("Error getting all artifact instances from instances table", ex);
|
||||
} finally {
|
||||
EamDbUtil.closePreparedStatement(preparedStatement);
|
||||
EamDbUtil.closeStatement(preparedStatement);
|
||||
EamDbUtil.closeResultSet(resultSet);
|
||||
EamDbUtil.closeConnection(conn);
|
||||
}
|
||||
|
||||
+1
-1
@@ -221,7 +221,7 @@ public final class PostgresEamDbSettings {
|
||||
LOGGER.log(Level.SEVERE, "Failed to execute database existance query.", ex); // NON-NLS
|
||||
return false;
|
||||
} finally {
|
||||
EamDbUtil.closePreparedStatement(ps);
|
||||
EamDbUtil.closeStatement(ps);
|
||||
EamDbUtil.closeResultSet(rs);
|
||||
EamDbUtil.closeConnection(conn);
|
||||
}
|
||||
|
||||
@@ -65,3 +65,13 @@ GlobalSettingsPanel.manageOrganizationButton.text=Manage Organizations
|
||||
GlobalSettingsPanel.lbCentralRepository.text=A central repository allows you to correlate files and results between cases.
|
||||
GlobalSettingsPanel.pnCorrelationProperties.border.title=Correlation Properties
|
||||
GlobalSettingsPanel.organizationPanel.border.title=Organizations
|
||||
GlobalSettingsPanel.casesPanel.border.title=Case Details
|
||||
GlobalSettingsPanel.showCasesButton.text=Show Cases
|
||||
ShowCasesDialog.closeButton.AccessibleContext.accessibleName=Close
|
||||
ShowCasesDialog.closeButton.actionCommand=Close
|
||||
ShowCasesDialog.closeButton.text=Close
|
||||
ShowCasesDialog.caseDetailsTable.toolTipText=Click column name to sort. Right-click on the table for more options.
|
||||
ShowCasesDialog.title=Case Details
|
||||
GlobalSettingsPanel.Case\ Details.AccessibleContext.accessibleName=Cases Details
|
||||
ShowCasesDialog.caseDetailsTable.AccessibleContext.accessibleDescription=Click column name to sort.
|
||||
GlobalSettingsPanel.casesTextArea.text=Display table that lists central repository case details.
|
||||
|
||||
+114
-16
@@ -27,7 +27,7 @@
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="jScrollPane1" alignment="0" pref="488" max="32767" attributes="0"/>
|
||||
<Component id="jScrollPane1" alignment="0" pref="517" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
@@ -57,24 +57,26 @@
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="lbCentralRepository" max="32767" attributes="0"/>
|
||||
<Component id="lbCentralRepository" pref="1022" max="32767" attributes="0"/>
|
||||
<Group type="102" attributes="0">
|
||||
<Component id="cbUseCentralRepo" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="tbOops" min="-2" pref="974" max="-2" attributes="0"/>
|
||||
<EmptySpace pref="36" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<Group type="103" groupAlignment="1" attributes="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="pnDatabaseConfiguration" alignment="0" max="32767" attributes="0"/>
|
||||
<Component id="pnCorrelationProperties" alignment="0" pref="1012" max="32767" attributes="0"/>
|
||||
<Component id="organizationPanel" max="32767" attributes="0"/>
|
||||
<Component id="pnCorrelationProperties" alignment="0" pref="1016" max="32767" attributes="0"/>
|
||||
<Component id="organizationPanel" alignment="1" max="32767" attributes="0"/>
|
||||
<Component id="casesPanel" alignment="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="102" attributes="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="cbUseCentralRepo" min="-2" max="-2" attributes="0"/>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="tbOops" min="-2" pref="974" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
@@ -90,8 +92,10 @@
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="organizationPanel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="casesPanel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="tbOops" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="0" pref="92" max="32767" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
@@ -328,7 +332,7 @@
|
||||
<Group type="102" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="organizationScrollPane" max="32767" attributes="0"/>
|
||||
<Component id="organizationScrollPane" pref="992" max="32767" attributes="0"/>
|
||||
<Group type="102" attributes="0">
|
||||
<Component id="manageOrganizationButton" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
|
||||
@@ -398,6 +402,100 @@
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Container class="javax.swing.JPanel" name="casesPanel">
|
||||
<Properties>
|
||||
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
|
||||
<Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo">
|
||||
<TitledBorder title="Case Details">
|
||||
<ResourceString PropertyName="titleX" bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="GlobalSettingsPanel.casesPanel.border.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<Font PropertyName="font" name="Tahoma" size="12" style="0"/>
|
||||
</TitledBorder>
|
||||
</Border>
|
||||
</Property>
|
||||
<Property name="name" type="java.lang.String" value="Case Details" noResource="true"/>
|
||||
</Properties>
|
||||
<AccessibilityProperties>
|
||||
<Property name="AccessibleContext.accessibleName" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="GlobalSettingsPanel.Case Details.AccessibleContext.accessibleName" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</AccessibilityProperties>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="casesScrollPane" alignment="0" max="32767" attributes="0"/>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="showCasesButton" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="casesScrollPane" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="showCasesButton" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="8" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JButton" name="showCasesButton">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="GlobalSettingsPanel.showCasesButton.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="showCasesButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Container class="javax.swing.JScrollPane" name="casesScrollPane">
|
||||
<Properties>
|
||||
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
|
||||
<Border info="null"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JTextArea" name="casesTextArea">
|
||||
<Properties>
|
||||
<Property name="editable" type="boolean" value="false"/>
|
||||
<Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
|
||||
<Color blue="f0" green="f0" red="f0" type="rgb"/>
|
||||
</Property>
|
||||
<Property name="columns" type="int" value="20"/>
|
||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
|
||||
<Font name="Tahoma" size="11" style="0"/>
|
||||
</Property>
|
||||
<Property name="lineWrap" type="boolean" value="true"/>
|
||||
<Property name="rows" type="int" value="2"/>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="GlobalSettingsPanel.casesTextArea.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="wrapStyleWord" type="boolean" value="true"/>
|
||||
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
|
||||
<Border info="null"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Component class="javax.swing.JTextField" name="tbOops">
|
||||
<Properties>
|
||||
<Property name="editable" type="boolean" value="false"/>
|
||||
|
||||
+84
-17
@@ -124,6 +124,10 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
|
||||
manageOrganizationButton = new javax.swing.JButton();
|
||||
organizationScrollPane = new javax.swing.JScrollPane();
|
||||
organizationTextArea = new javax.swing.JTextArea();
|
||||
casesPanel = new javax.swing.JPanel();
|
||||
showCasesButton = new javax.swing.JButton();
|
||||
casesScrollPane = new javax.swing.JScrollPane();
|
||||
casesTextArea = new javax.swing.JTextArea();
|
||||
tbOops = new javax.swing.JTextField();
|
||||
|
||||
setName(""); // NOI18N
|
||||
@@ -276,7 +280,7 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
|
||||
.addGroup(organizationPanelLayout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(organizationPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(organizationScrollPane)
|
||||
.addComponent(organizationScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 992, Short.MAX_VALUE)
|
||||
.addGroup(organizationPanelLayout.createSequentialGroup()
|
||||
.addComponent(manageOrganizationButton)
|
||||
.addGap(0, 0, Short.MAX_VALUE)))
|
||||
@@ -292,6 +296,52 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
|
||||
.addGap(8, 8, 8))
|
||||
);
|
||||
|
||||
casesPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(null, org.openide.util.NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.casesPanel.border.title"), javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Tahoma", 0, 12))); // NOI18N
|
||||
casesPanel.setName("Case Details"); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(showCasesButton, org.openide.util.NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.showCasesButton.text")); // NOI18N
|
||||
showCasesButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
showCasesButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
casesScrollPane.setBorder(null);
|
||||
|
||||
casesTextArea.setEditable(false);
|
||||
casesTextArea.setBackground(new java.awt.Color(240, 240, 240));
|
||||
casesTextArea.setColumns(20);
|
||||
casesTextArea.setFont(new java.awt.Font("Tahoma", 0, 11)); // NOI18N
|
||||
casesTextArea.setLineWrap(true);
|
||||
casesTextArea.setRows(2);
|
||||
casesTextArea.setText(org.openide.util.NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.casesTextArea.text")); // NOI18N
|
||||
casesTextArea.setWrapStyleWord(true);
|
||||
casesTextArea.setBorder(null);
|
||||
casesScrollPane.setViewportView(casesTextArea);
|
||||
|
||||
javax.swing.GroupLayout casesPanelLayout = new javax.swing.GroupLayout(casesPanel);
|
||||
casesPanel.setLayout(casesPanelLayout);
|
||||
casesPanelLayout.setHorizontalGroup(
|
||||
casesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(casesPanelLayout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(casesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(casesScrollPane)
|
||||
.addGroup(casesPanelLayout.createSequentialGroup()
|
||||
.addComponent(showCasesButton)
|
||||
.addGap(0, 0, Short.MAX_VALUE)))
|
||||
.addContainerGap())
|
||||
);
|
||||
casesPanelLayout.setVerticalGroup(
|
||||
casesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, casesPanelLayout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(casesScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(showCasesButton)
|
||||
.addGap(8, 8, 8))
|
||||
);
|
||||
|
||||
tbOops.setEditable(false);
|
||||
tbOops.setFont(tbOops.getFont().deriveFont(tbOops.getFont().getStyle() | java.awt.Font.BOLD, 12));
|
||||
tbOops.setText(org.openide.util.NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.tbOops.text")); // NOI18N
|
||||
@@ -301,20 +351,21 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
|
||||
jPanel1.setLayout(jPanel1Layout);
|
||||
jPanel1Layout.setHorizontalGroup(
|
||||
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(lbCentralRepository, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(lbCentralRepository, javax.swing.GroupLayout.DEFAULT_SIZE, 1022, Short.MAX_VALUE)
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addComponent(cbUseCentralRepo)
|
||||
.addGap(0, 0, Short.MAX_VALUE))
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(tbOops, javax.swing.GroupLayout.PREFERRED_SIZE, 974, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addContainerGap(36, Short.MAX_VALUE))
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup()
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
|
||||
.addComponent(pnDatabaseConfiguration, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(pnCorrelationProperties, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 1012, Short.MAX_VALUE)
|
||||
.addComponent(organizationPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(pnDatabaseConfiguration, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(pnCorrelationProperties, javax.swing.GroupLayout.DEFAULT_SIZE, 1016, Short.MAX_VALUE)
|
||||
.addComponent(organizationPanel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(casesPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
.addContainerGap())
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(cbUseCentralRepo)
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(tbOops, javax.swing.GroupLayout.PREFERRED_SIZE, 974, javax.swing.GroupLayout.PREFERRED_SIZE)))
|
||||
.addGap(0, 0, Short.MAX_VALUE))
|
||||
);
|
||||
jPanel1Layout.setVerticalGroup(
|
||||
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
@@ -329,10 +380,14 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(organizationPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(casesPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(tbOops, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(0, 92, Short.MAX_VALUE))
|
||||
.addContainerGap())
|
||||
);
|
||||
|
||||
casesPanel.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.Case Details.AccessibleContext.accessibleName")); // NOI18N
|
||||
|
||||
jScrollPane1.setViewportView(jPanel1);
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
@@ -343,13 +398,13 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 488, Short.MAX_VALUE)
|
||||
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 517, Short.MAX_VALUE)
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
private void bnManageTypesActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnManageTypesActionPerformed
|
||||
store();
|
||||
ManageCorrelationPropertiesDialog dialog = new ManageCorrelationPropertiesDialog();
|
||||
ManageCorrelationPropertiesDialog manageCorrelationDialog = new ManageCorrelationPropertiesDialog();
|
||||
firePropertyChange(OptionsPanelController.PROP_VALID, null, null);
|
||||
}//GEN-LAST:event_bnManageTypesActionPerformed
|
||||
|
||||
@@ -374,9 +429,14 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
|
||||
|
||||
private void manageOrganizationButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_manageOrganizationButtonActionPerformed
|
||||
store();
|
||||
ManageOrganizationsDialog dialog = new ManageOrganizationsDialog();
|
||||
ManageOrganizationsDialog manageOrganizationsDialog = new ManageOrganizationsDialog();
|
||||
}//GEN-LAST:event_manageOrganizationButtonActionPerformed
|
||||
|
||||
private void showCasesButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_showCasesButtonActionPerformed
|
||||
store();
|
||||
ShowCasesDialog showCasesDialog = new ShowCasesDialog();
|
||||
}//GEN-LAST:event_showCasesButtonActionPerformed
|
||||
|
||||
@Override
|
||||
@Messages({"GlobalSettingsPanel.validationerrMsg.mustConfigure=Configure the database to enable this module."})
|
||||
public void load() {
|
||||
@@ -553,12 +613,18 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
|
||||
organizationPanel.setEnabled(enable && !ingestRunning);
|
||||
organizationTextArea.setEnabled(enable && !ingestRunning);
|
||||
manageOrganizationButton.setEnabled(enable && !ingestRunning);
|
||||
showCasesButton.setEnabled(enable && !ingestRunning);
|
||||
casesPanel.setEnabled(enable && !ingestRunning);
|
||||
casesTextArea.setEnabled(enable && !ingestRunning);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JButton bnDbConfigure;
|
||||
private javax.swing.JButton bnManageTypes;
|
||||
private javax.swing.JPanel casesPanel;
|
||||
private javax.swing.JScrollPane casesScrollPane;
|
||||
private javax.swing.JTextArea casesTextArea;
|
||||
private javax.swing.JCheckBox cbUseCentralRepo;
|
||||
private javax.swing.JScrollPane correlationPropertiesScrollPane;
|
||||
private javax.swing.JTextArea correlationPropertiesTextArea;
|
||||
@@ -577,6 +643,7 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
|
||||
private javax.swing.JTextArea organizationTextArea;
|
||||
private javax.swing.JPanel pnCorrelationProperties;
|
||||
private javax.swing.JPanel pnDatabaseConfiguration;
|
||||
private javax.swing.JButton showCasesButton;
|
||||
private javax.swing.JTextField tbOops;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
}
|
||||
|
||||
+3
-4
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Central Repository
|
||||
*
|
||||
* Copyright 2015-2017 Basis Technology Corp.
|
||||
* Copyright 2015-2018 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -18,8 +18,6 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.centralrepository.optionspanel;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Toolkit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
@@ -38,8 +36,9 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
|
||||
|
||||
/**
|
||||
* Dialog to handle management of artifact types handled by the Central
|
||||
* Repository
|
||||
* Repository
|
||||
*/
|
||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||
final class ManageCorrelationPropertiesDialog extends javax.swing.JDialog {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(ManageCorrelationPropertiesDialog.class.getName());
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.6" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JDialogFormInfo">
|
||||
<Properties>
|
||||
<Property name="title" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="ShowCasesDialog.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[545, 415]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<SyntheticProperties>
|
||||
<SyntheticProperty name="formSizePolicy" type="int" value="1"/>
|
||||
<SyntheticProperty name="generateCenter" type="boolean" value="false"/>
|
||||
</SyntheticProperties>
|
||||
<AuxValues>
|
||||
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<EmptySpace min="0" pref="4" max="32767" attributes="0"/>
|
||||
<Component id="showCasesPanel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="0" pref="4" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
<Component id="closeButton" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<EmptySpace min="0" pref="6" max="32767" attributes="0"/>
|
||||
<Component id="showCasesPanel" min="-2" pref="426" max="-2" attributes="0"/>
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
<Component id="closeButton" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JPanel" name="showCasesPanel">
|
||||
<Properties>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[527, 407]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<EmptySpace min="0" pref="527" max="32767" attributes="0"/>
|
||||
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
|
||||
<Component id="showCasesScrollPane" alignment="0" pref="527" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<EmptySpace min="0" pref="426" max="32767" attributes="0"/>
|
||||
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
|
||||
<Component id="showCasesScrollPane" alignment="0" pref="407" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JScrollPane" name="showCasesScrollPane">
|
||||
<Properties>
|
||||
<Property name="verticalScrollBarPolicy" type="int" value="21"/>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[535, 415]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JPanel" name="outCasesPane">
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<EmptySpace min="0" pref="1222" max="32767" attributes="0"/>
|
||||
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="innerCaseScrollPane" min="-2" pref="1222" max="-2" attributes="0"/>
|
||||
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<EmptySpace min="0" pref="400" max="32767" attributes="0"/>
|
||||
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
|
||||
<Component id="innerCaseScrollPane" alignment="0" pref="400" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JScrollPane" name="innerCaseScrollPane">
|
||||
<Properties>
|
||||
<Property name="horizontalScrollBarPolicy" type="int" value="31"/>
|
||||
</Properties>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JTable" name="caseDetailsTable">
|
||||
<Properties>
|
||||
<Property name="autoCreateRowSorter" type="boolean" value="true"/>
|
||||
<Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||
<Connection code="tableModel" type="code"/>
|
||||
</Property>
|
||||
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="ShowCasesDialog.caseDetailsTable.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="selectionModel" type="javax.swing.ListSelectionModel" editor="org.netbeans.modules.form.editors2.JTableSelectionModelEditor">
|
||||
<JTableSelectionModel selectionMode="1"/>
|
||||
</Property>
|
||||
<Property name="tableHeader" type="javax.swing.table.JTableHeader" editor="org.netbeans.modules.form.editors2.JTableHeaderEditor">
|
||||
<TableHeader reorderingAllowed="false" resizingAllowed="true"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AccessibilityProperties>
|
||||
<Property name="AccessibleContext.accessibleDescription" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="ShowCasesDialog.caseDetailsTable.AccessibleContext.accessibleDescription" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</AccessibilityProperties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Component class="javax.swing.JButton" name="closeButton">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="ShowCasesDialog.closeButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="actionCommand" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="ShowCasesDialog.closeButton.actionCommand" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AccessibilityProperties>
|
||||
<Property name="AccessibleContext.accessibleName" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="ShowCasesDialog.closeButton.AccessibleContext.accessibleName" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</AccessibilityProperties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="closeButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
* Central Repository
|
||||
*
|
||||
* Copyright 2015-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.optionspanel;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JFrame;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.openide.windows.WindowManager;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
|
||||
/**
|
||||
* Dialog to display table of CorrelationCase information from the CR tab of options.
|
||||
*/
|
||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||
final class ShowCasesDialog extends JDialog {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final static Logger logger = Logger.getLogger(ShowCasesDialog.class.getName());
|
||||
|
||||
private final ShowCasesTableModel tableModel;
|
||||
@Messages({"ShowCasesDialog.title_text=All Cases Details"})
|
||||
/**
|
||||
* Creates new form ShowCases Panel
|
||||
*/
|
||||
ShowCasesDialog() {
|
||||
super((JFrame) WindowManager.getDefault().getMainWindow(),
|
||||
Bundle.ShowCasesDialog_title_text(),
|
||||
true);
|
||||
tableModel = new ShowCasesTableModel();
|
||||
initComponents();
|
||||
try {
|
||||
EamDb dbManager = EamDb.getInstance();
|
||||
List<CorrelationCase> eamCases = dbManager.getCases();
|
||||
for(CorrelationCase eamCase : eamCases) {
|
||||
tableModel.addEamCase(eamCase);
|
||||
}
|
||||
} catch (EamDbException ex) {
|
||||
logger.log(Level.SEVERE, "Error getting list of cases from database.", ex); // NON-NLS
|
||||
}
|
||||
display();
|
||||
}
|
||||
|
||||
private void display() {
|
||||
this.setLocationRelativeTo(WindowManager.getDefault().getMainWindow());
|
||||
setVisible(true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This method is called from within the constructor to initialize the form.
|
||||
* WARNING: Do NOT modify this code. The content of this method is always
|
||||
* regenerated by the Form Editor.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
showCasesPanel = new javax.swing.JPanel();
|
||||
showCasesScrollPane = new javax.swing.JScrollPane();
|
||||
outCasesPane = new javax.swing.JPanel();
|
||||
innerCaseScrollPane = new javax.swing.JScrollPane();
|
||||
caseDetailsTable = new javax.swing.JTable();
|
||||
closeButton = new javax.swing.JButton();
|
||||
|
||||
setTitle(org.openide.util.NbBundle.getMessage(ShowCasesDialog.class, "ShowCasesDialog.title")); // NOI18N
|
||||
setMinimumSize(new java.awt.Dimension(545, 415));
|
||||
|
||||
showCasesPanel.setPreferredSize(new java.awt.Dimension(527, 407));
|
||||
|
||||
showCasesScrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
|
||||
showCasesScrollPane.setPreferredSize(new java.awt.Dimension(535, 415));
|
||||
|
||||
innerCaseScrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
|
||||
|
||||
caseDetailsTable.setAutoCreateRowSorter(true);
|
||||
caseDetailsTable.setModel(tableModel);
|
||||
caseDetailsTable.setToolTipText(org.openide.util.NbBundle.getMessage(ShowCasesDialog.class, "ShowCasesDialog.caseDetailsTable.toolTipText")); // NOI18N
|
||||
caseDetailsTable.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_INTERVAL_SELECTION);
|
||||
caseDetailsTable.getTableHeader().setReorderingAllowed(false);
|
||||
innerCaseScrollPane.setViewportView(caseDetailsTable);
|
||||
caseDetailsTable.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(ShowCasesDialog.class, "ShowCasesDialog.caseDetailsTable.AccessibleContext.accessibleDescription")); // NOI18N
|
||||
|
||||
javax.swing.GroupLayout outCasesPaneLayout = new javax.swing.GroupLayout(outCasesPane);
|
||||
outCasesPane.setLayout(outCasesPaneLayout);
|
||||
outCasesPaneLayout.setHorizontalGroup(
|
||||
outCasesPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 1222, Short.MAX_VALUE)
|
||||
.addGroup(outCasesPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(outCasesPaneLayout.createSequentialGroup()
|
||||
.addComponent(innerCaseScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 1222, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(0, 0, Short.MAX_VALUE)))
|
||||
);
|
||||
outCasesPaneLayout.setVerticalGroup(
|
||||
outCasesPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 400, Short.MAX_VALUE)
|
||||
.addGroup(outCasesPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(innerCaseScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE))
|
||||
);
|
||||
|
||||
showCasesScrollPane.setViewportView(outCasesPane);
|
||||
|
||||
javax.swing.GroupLayout showCasesPanelLayout = new javax.swing.GroupLayout(showCasesPanel);
|
||||
showCasesPanel.setLayout(showCasesPanelLayout);
|
||||
showCasesPanelLayout.setHorizontalGroup(
|
||||
showCasesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 527, Short.MAX_VALUE)
|
||||
.addGroup(showCasesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(showCasesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 527, Short.MAX_VALUE))
|
||||
);
|
||||
showCasesPanelLayout.setVerticalGroup(
|
||||
showCasesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 407, Short.MAX_VALUE)
|
||||
.addGroup(showCasesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(showCasesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 407, Short.MAX_VALUE))
|
||||
);
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(closeButton, org.openide.util.NbBundle.getMessage(ShowCasesDialog.class, "ShowCasesDialog.closeButton.text")); // NOI18N
|
||||
closeButton.setActionCommand(org.openide.util.NbBundle.getMessage(ShowCasesDialog.class, "ShowCasesDialog.closeButton.actionCommand")); // NOI18N
|
||||
closeButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
closeButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
|
||||
getContentPane().setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGap(0, 4, Short.MAX_VALUE)
|
||||
.addComponent(showCasesPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(0, 4, Short.MAX_VALUE))
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
|
||||
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(closeButton)
|
||||
.addContainerGap())
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGap(0, 6, Short.MAX_VALUE)
|
||||
.addComponent(showCasesPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 426, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(closeButton)
|
||||
.addContainerGap())
|
||||
);
|
||||
|
||||
closeButton.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(ShowCasesDialog.class, "ShowCasesDialog.closeButton.AccessibleContext.accessibleName")); // NOI18N
|
||||
|
||||
pack();
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
private void closeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_closeButtonActionPerformed
|
||||
dispose();
|
||||
}//GEN-LAST:event_closeButtonActionPerformed
|
||||
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JTable caseDetailsTable;
|
||||
private javax.swing.JButton closeButton;
|
||||
private javax.swing.JScrollPane innerCaseScrollPane;
|
||||
private javax.swing.JPanel outCasesPane;
|
||||
private javax.swing.JPanel showCasesPanel;
|
||||
private javax.swing.JScrollPane showCasesScrollPane;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
|
||||
|
||||
}
|
||||
+187
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* 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.optionspanel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase;
|
||||
|
||||
/**
|
||||
* Model for cells to display correlation case information
|
||||
*/
|
||||
class ShowCasesTableModel extends AbstractTableModel {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Messages({"ShowCasesTableModel.case=Case Name",
|
||||
"ShowCasesTableModel.creationDate=Creation Date",
|
||||
"ShowCasesTableModel.caseNumber=Case Number",
|
||||
"ShowCasesTableModel.examinerName=Examiner Name",
|
||||
"ShowCasesTableModel.examinerEmail=Examiner Email",
|
||||
"ShowCasesTableModel.examinerPhone=Examiner Phone",
|
||||
"ShowCasesTableModel.notes=Notes",
|
||||
"ShowCasesTableModel.noData=No Cases"})
|
||||
/**
|
||||
* Enum which lists columns of interest from CorrelationCase.
|
||||
*/
|
||||
enum TableColumns {
|
||||
// Ordering here determines displayed column order in Content Viewer.
|
||||
// If order is changed, update the CellRenderer to ensure correct row coloring.
|
||||
CASE_NAME(Bundle.ShowCasesTableModel_case(), 200),
|
||||
CREATION_DATE(Bundle.ShowCasesTableModel_creationDate(), 150),
|
||||
CASE_NUMBER(Bundle.ShowCasesTableModel_caseNumber(), 100),
|
||||
EXAMINER_NAME(Bundle.ShowCasesTableModel_examinerName(), 200),
|
||||
EXAMINER_EMAIL(Bundle.ShowCasesTableModel_examinerEmail(), 100),
|
||||
EXAMINER_PHONE(Bundle.ShowCasesTableModel_examinerPhone(), 100),
|
||||
NOTES(Bundle.ShowCasesTableModel_notes(), 450);
|
||||
|
||||
private final String columnName;
|
||||
private final int columnWidth;
|
||||
|
||||
TableColumns(String columnName, int columnWidth) {
|
||||
this.columnName = columnName;
|
||||
this.columnWidth = columnWidth;
|
||||
}
|
||||
|
||||
String columnName() {
|
||||
return columnName;
|
||||
}
|
||||
|
||||
int columnWidth() {
|
||||
return columnWidth;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* list of Eam Cases from central repository.
|
||||
*/
|
||||
private List<CorrelationCase> eamCases;
|
||||
|
||||
ShowCasesTableModel() {
|
||||
eamCases = new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount() {
|
||||
return TableColumns.values().length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the preferred width that has been configured for this column.
|
||||
*
|
||||
* A value of 0 means that no preferred width has been defined for this
|
||||
* column.
|
||||
*
|
||||
* @param colIdx Column index
|
||||
*
|
||||
* @return preferred column width >= 0
|
||||
*/
|
||||
int getColumnPreferredWidth(int colIdx) {
|
||||
return TableColumns.values()[colIdx].columnWidth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
return eamCases.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColumnName(int colIdx) {
|
||||
return TableColumns.values()[colIdx].columnName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValueAt(int rowIdx, int colIdx) {
|
||||
if (eamCases.isEmpty()) {
|
||||
return Bundle.ShowCasesTableModel_noData();
|
||||
}
|
||||
|
||||
return mapValueById(rowIdx, TableColumns.values()[colIdx]);
|
||||
}
|
||||
|
||||
Object getRow(int rowIdx) {
|
||||
return eamCases.get(rowIdx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a rowIdx and colId to the value in that cell.
|
||||
*
|
||||
* @param rowIdx Index of row to search
|
||||
* @param colId ID of column to search
|
||||
*
|
||||
* @return value in the cell
|
||||
*/
|
||||
private Object mapValueById(int rowIdx, TableColumns colId) {
|
||||
CorrelationCase eamCase = eamCases.get(rowIdx);
|
||||
String value = Bundle.ShowCasesTableModel_noData();
|
||||
|
||||
switch (colId) {
|
||||
case CASE_NAME:
|
||||
value = eamCase.getDisplayName();
|
||||
break;
|
||||
case CREATION_DATE:
|
||||
value = eamCase.getCreationDate();
|
||||
break;
|
||||
case CASE_NUMBER:
|
||||
value = eamCase.getCaseNumber();
|
||||
break;
|
||||
case EXAMINER_NAME:
|
||||
value = eamCase.getExaminerName();
|
||||
break;
|
||||
case EXAMINER_EMAIL:
|
||||
value = eamCase.getExaminerEmail();
|
||||
break;
|
||||
case EXAMINER_PHONE:
|
||||
value = eamCase.getExaminerPhone();
|
||||
break;
|
||||
case NOTES:
|
||||
value = eamCase.getNotes();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<String> getColumnClass(int colIdx) {
|
||||
return String.class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add one local central repository case to the table.
|
||||
*
|
||||
* @param eamCase central repository case to add to the
|
||||
* table
|
||||
*/
|
||||
void addEamCase(CorrelationCase eamCase) {
|
||||
eamCases.add(eamCase);
|
||||
fireTableDataChanged();
|
||||
}
|
||||
|
||||
void clearTable() {
|
||||
eamCases.clear();
|
||||
fireTableDataChanged();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -28,9 +28,11 @@ import java.util.logging.Level;
|
||||
import javax.swing.ComboBoxModel;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.SwingWorker;
|
||||
import org.netbeans.api.progress.ProgressHandle;
|
||||
import org.openide.explorer.ExplorerManager;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer;
|
||||
import org.sleuthkit.autopsy.corecomponents.DataResultTopComponent;
|
||||
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
|
||||
@@ -164,6 +166,8 @@ public final class CommonFilesPanel extends javax.swing.JPanel {
|
||||
"CommonFilesPanel.search.results.titleAll=Common Files (All Data Sources)",
|
||||
"CommonFilesPanel.search.results.titleSingle=Common Files (Match Within Data Source: %s)",
|
||||
"CommonFilesPanel.search.results.pathText=Common Files Search Results",
|
||||
"CommonFilesPanel.search.done.searchProgressGathering=Gathering Common Files Search Results.",
|
||||
"CommonFilesPanel.search.done.searchProgressDisplay=Displaying Common Files Search Results.",
|
||||
"CommonFilesPanel.search.done.tskCoreException=Unable to run query against DB.",
|
||||
"CommonFilesPanel.search.done.noCurrentCaseException=Unable to open case file.",
|
||||
"CommonFilesPanel.search.done.exception=Unexpected exception running Common Files Search.",
|
||||
@@ -175,7 +179,8 @@ public final class CommonFilesPanel extends javax.swing.JPanel {
|
||||
new SwingWorker<CommonFilesMetadata, Void>() {
|
||||
|
||||
private String tabTitle;
|
||||
|
||||
private ProgressHandle progress;
|
||||
|
||||
private void setTitleForAllDataSources() {
|
||||
this.tabTitle = Bundle.CommonFilesPanel_search_results_titleAll();
|
||||
}
|
||||
@@ -202,7 +207,11 @@ public final class CommonFilesPanel extends javax.swing.JPanel {
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({"BoxedValueEquality", "NumberEquality"})
|
||||
protected CommonFilesMetadata doInBackground() throws TskCoreException, NoCurrentCaseException, SQLException {
|
||||
protected CommonFilesMetadata doInBackground() throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException {
|
||||
progress = ProgressHandle.createHandle(Bundle.CommonFilesPanel_search_done_searchProgressGathering());
|
||||
progress.start();
|
||||
progress.switchToIndeterminate();
|
||||
|
||||
Long dataSourceId = determineDataSourceId();
|
||||
|
||||
CommonFilesMetadataBuilder builder;
|
||||
@@ -237,7 +246,6 @@ public final class CommonFilesPanel extends javax.swing.JPanel {
|
||||
protected void done() {
|
||||
try {
|
||||
super.done();
|
||||
|
||||
CommonFilesMetadata metadata = get();
|
||||
|
||||
CommonFilesNode commonFilesNode = new CommonFilesNode(metadata);
|
||||
@@ -251,9 +259,9 @@ public final class CommonFilesPanel extends javax.swing.JPanel {
|
||||
|
||||
Collection<DataResultViewer> viewers = new ArrayList<>(1);
|
||||
viewers.add(table);
|
||||
|
||||
progress.setDisplayName(Bundle.CommonFilesPanel_search_done_searchProgressDisplay());
|
||||
DataResultTopComponent.createInstance(tabTitle, pathText, tableFilterWithDescendantsNode, metadata.size(), viewers);
|
||||
|
||||
progress.finish();
|
||||
} catch (InterruptedException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Interrupted while loading Common Files", ex);
|
||||
MessageNotifyUtil.Message.error(Bundle.CommonFilesPanel_search_done_interupted());
|
||||
|
||||
@@ -69,6 +69,7 @@ public final class UserPreferences {
|
||||
private static final String MODE = "AutopsyMode"; // NON-NLS
|
||||
private static final String MAX_NUM_OF_LOG_FILE = "MaximumNumberOfLogFiles";
|
||||
private static final int LOG_FILE_NUM_INT = 10;
|
||||
public static final String GROUP_ITEMS_IN_TREE_BY_DATASOURCE = "GroupItemsInTreeByDataSource"; //NON-NLS
|
||||
|
||||
// Prevent instantiation.
|
||||
private UserPreferences() {
|
||||
@@ -187,6 +188,14 @@ public final class UserPreferences {
|
||||
preferences.putInt(NUMBER_OF_FILE_INGEST_THREADS, value);
|
||||
}
|
||||
|
||||
public static boolean groupItemsInTreeByDatasource() {
|
||||
return preferences.getBoolean(GROUP_ITEMS_IN_TREE_BY_DATASOURCE, false);
|
||||
}
|
||||
|
||||
public static void setGroupItemsInTreeByDatasource(boolean value) {
|
||||
preferences.putBoolean(GROUP_ITEMS_IN_TREE_BY_DATASOURCE, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads persisted case database connection info.
|
||||
*
|
||||
|
||||
+19
-4
@@ -1,12 +1,24 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-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.corecomponents;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Toolkit;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JPanel;
|
||||
import org.openide.windows.WindowManager;
|
||||
@@ -16,6 +28,7 @@ import org.openide.windows.WindowManager;
|
||||
* the panel given to it. No additional buttons or features, except the default
|
||||
* close operation, which is set to dispose.
|
||||
*/
|
||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||
public class AdvancedConfigurationCleanDialog extends javax.swing.JDialog {
|
||||
|
||||
/**
|
||||
@@ -27,6 +40,8 @@ public class AdvancedConfigurationCleanDialog extends javax.swing.JDialog {
|
||||
|
||||
/**
|
||||
* Creates new form AdvancedConfigurationDialog
|
||||
*
|
||||
* @param resizable Is the dialog resizable?
|
||||
*/
|
||||
public AdvancedConfigurationCleanDialog(boolean resizable) {
|
||||
super((JFrame) WindowManager.getDefault().getMainWindow(), true);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011 Basis Technology Corp.
|
||||
* Copyright 2011-2018 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -16,26 +16,18 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* AdvancedConfigurationDialog.java
|
||||
*
|
||||
* Created on Feb 28, 2012, 4:47:31 PM
|
||||
*/
|
||||
package org.sleuthkit.autopsy.corecomponents;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.event.ActionListener;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JPanel;
|
||||
import org.openide.windows.WindowManager;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author dfickling
|
||||
* Advanced configuration dialog.
|
||||
*/
|
||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||
public class AdvancedConfigurationDialog extends javax.swing.JDialog {
|
||||
|
||||
/**
|
||||
@@ -47,6 +39,8 @@ public class AdvancedConfigurationDialog extends javax.swing.JDialog {
|
||||
|
||||
/**
|
||||
* Creates new form AdvancedConfigurationDialog
|
||||
*
|
||||
* @param resizable Is the dialog resizable?
|
||||
*/
|
||||
public AdvancedConfigurationDialog(boolean resizable) {
|
||||
super((JFrame) WindowManager.getDefault().getMainWindow(), true);
|
||||
|
||||
@@ -26,7 +26,7 @@ LBL_Description=<div style=\"font-size: 12pt; font-family: Verdana, 'Verdana CE'
|
||||
Format_OperatingSystem_Value={0} version {1} running on {2}
|
||||
LBL_Copyright=<div style\="font-size\: 12pt; font-family\: Verdana, 'Verdana CE', Arial, 'Arial CE', 'Lucida Grande CE', lucida, 'Helvetica CE', sans-serif; ">Autopsy™ is a digital forensics platform based on The Sleuth Kit™ and other tools. <br><ul><li>General Information: <a style\="color\: \#1E2A60;" href\="http\://www.sleuthkit.org">http\://www.sleuthkit.org</a>.</li><li>Training: <a style\="color\: \#1E2A60;" href\="http://www.basistech.com/autopsy-training">http://www.basistech.com/autopsy-training</a></li><li>Commercial Support: <a style\="color\: \#1E2A60;" href\="http://www.basistech.com/digital-forensics/autopsy/support/">http://www.basistech.com/digital-forensics/autopsy/support/</a></li></ul>Copyright © 2003-2018. </div>
|
||||
URL_ON_IMG=http://www.sleuthkit.org/
|
||||
URL_ON_HELP=http://sleuthkit.org/autopsy/docs/user-docs/4.7.0/
|
||||
URL_ON_HELP=http://sleuthkit.org/autopsy/docs/user-docs/4.8.0/
|
||||
FILE_FOR_LOCAL_HELP=file:///
|
||||
INDEX_FOR_LOCAL_HELP=/docs/index.html
|
||||
LBL_Close=Close
|
||||
|
||||
@@ -258,21 +258,6 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
|
||||
map.put(EXTENSION.toString(), content.getNameExtension());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Action[] getActions(boolean context) {
|
||||
List<Action> actionsList = new ArrayList<>();
|
||||
|
||||
actionsList.addAll(Arrays.asList(super.getActions(true)));
|
||||
|
||||
// Create the "Add/Edit Central Repository Comment" menu item if the enabled.
|
||||
AbstractFile file = content;
|
||||
if (EamDbUtil.useCentralRepo() && EamArtifactUtil.isSupportedAbstractFileType(file) && file.isFile()) {
|
||||
actionsList.add(AddEditCentralRepoCommentAction.createAddEditCentralRepoCommentAction(file));
|
||||
}
|
||||
|
||||
return actionsList.toArray(new Action[actionsList.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by subclasses of AbstractAbstractFileNode to add the tags property
|
||||
* to their sheets.
|
||||
|
||||
@@ -162,12 +162,12 @@ abstract class AbstractContentChildren<T> extends Keys<T> {
|
||||
|
||||
@Override
|
||||
public AbstractNode visit(DeletedContent dc) {
|
||||
return new DeletedContent.DeletedContentsNode(dc.getSleuthkitCase());
|
||||
return new DeletedContent.DeletedContentsNode(dc.getSleuthkitCase(), dc.filteringDataSourceObjId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractNode visit(FileSize dc) {
|
||||
return new FileSize.FileSizeRootNode(dc.getSleuthkitCase());
|
||||
return new FileSize.FileSizeRootNode(dc.getSleuthkitCase(), dc.filteringDataSourceObjId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -192,22 +192,27 @@ abstract class AbstractContentChildren<T> extends Keys<T> {
|
||||
|
||||
@Override
|
||||
public AbstractNode visit(Tags tagsNodeKey) {
|
||||
return tagsNodeKey.new RootNode();
|
||||
return tagsNodeKey.new RootNode(tagsNodeKey.filteringDataSourceObjId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractNode visit(DataSources i) {
|
||||
return new DataSourcesNode();
|
||||
return new DataSourcesNode(i.filteringDataSourceObjId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractNode visit(DataSourceGrouping datasourceGrouping) {
|
||||
return new DataSourceGroupingNode(datasourceGrouping.getDataSource());
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractNode visit(Views v) {
|
||||
return new ViewsNode(v.getSleuthkitCase());
|
||||
return new ViewsNode(v.getSleuthkitCase(), v.filteringDataSourceObjId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractNode visit(Results r) {
|
||||
return new ResultsNode(r.getSleuthkitCase());
|
||||
public AbstractNode visit(Results results) {
|
||||
return new ResultsNode(results.getSleuthkitCase(), results.filteringDataSourceObjId() );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -110,6 +110,17 @@ public abstract class AbstractContentNode<T extends Content> extends ContentNode
|
||||
*/
|
||||
public static boolean contentHasVisibleContentChildren(Content c){
|
||||
if (c != null) {
|
||||
|
||||
try {
|
||||
if( ! c.hasChildren()) {
|
||||
return false;
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
|
||||
logger.log(Level.SEVERE, "Error checking if the node has children, for content: " + c, ex); //NON-NLS
|
||||
return false;
|
||||
}
|
||||
|
||||
String query = "SELECT COUNT(obj_id) AS count FROM "
|
||||
+ " ( SELECT obj_id FROM tsk_objects WHERE par_obj_id = " + c.getId() + " AND type = "
|
||||
+ TskData.ObjectType.ARTIFACT.getObjectType()
|
||||
|
||||
@@ -28,6 +28,8 @@ import org.sleuthkit.autopsy.datamodel.accounts.Accounts;
|
||||
public interface AutopsyItemVisitor<T> {
|
||||
|
||||
T visit(DataSources i);
|
||||
|
||||
T visit(DataSourceGrouping datasourceGrouping);
|
||||
|
||||
T visit(Views v);
|
||||
|
||||
@@ -173,6 +175,11 @@ public interface AutopsyItemVisitor<T> {
|
||||
return defaultVisit(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T visit(DataSourceGrouping datasourceGrouping) {
|
||||
return defaultVisit(datasourceGrouping);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T visit(Results r) {
|
||||
return defaultVisit(r);
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* 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.datamodel;
|
||||
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import org.openide.nodes.ChildFactory;
|
||||
import org.openide.nodes.Node;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.core.UserPreferences;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.SleuthkitVisitableItem;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
|
||||
/**
|
||||
* Child factory to create the top level children of the autopsy tree
|
||||
*
|
||||
*/
|
||||
public class AutopsyTreeChildrenFactory extends ChildFactory.Detachable<Object> {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(AutopsyTreeChildrenFactory.class.getName());
|
||||
private final SleuthkitCase tskCase;
|
||||
|
||||
/**
|
||||
* Constructs the child factory
|
||||
* @param tskCase
|
||||
*/
|
||||
public AutopsyTreeChildrenFactory(SleuthkitCase tskCase) {
|
||||
this.tskCase = tskCase;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener for handling DATA_SOURCE_ADDED events.
|
||||
*/
|
||||
private final PropertyChangeListener pcl = new PropertyChangeListener() {
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
String eventType = evt.getPropertyName();
|
||||
if (eventType.equals(Case.Events.DATA_SOURCE_ADDED.toString())) {
|
||||
refreshChildren();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
protected void addNotify() {
|
||||
super.addNotify();
|
||||
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED), pcl);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void removeNotify() {
|
||||
super.removeNotify();
|
||||
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED), pcl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates keys for the top level children.
|
||||
*
|
||||
* @param list list of keys created
|
||||
* @return true, indicating that the key list is complete
|
||||
*/
|
||||
@Override
|
||||
protected boolean createKeys(List<Object> list) {
|
||||
|
||||
try {
|
||||
if (UserPreferences.groupItemsInTreeByDatasource()) {
|
||||
List<DataSource> dataSources = tskCase.getDataSources();
|
||||
List<DataSourceGrouping> keys = new ArrayList<>();
|
||||
dataSources.forEach((datasource) -> {
|
||||
keys.add(new DataSourceGrouping(datasource));
|
||||
});
|
||||
list.addAll(keys);
|
||||
|
||||
list.add(new Reports());
|
||||
} else {
|
||||
|
||||
List<AutopsyVisitableItem> keys = new ArrayList<>(Arrays.asList(
|
||||
new DataSources(),
|
||||
new Views(tskCase),
|
||||
new Results(tskCase),
|
||||
new Tags(),
|
||||
new Reports()));
|
||||
|
||||
list.addAll(keys);
|
||||
}
|
||||
|
||||
} catch (TskCoreException tskCoreException) {
|
||||
logger.log(Level.SEVERE, "Error getting datas sources list from the database.", tskCoreException);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates nodes for the top level Key
|
||||
*
|
||||
* @param key
|
||||
*
|
||||
* @return Node for the key, null if key is unknown.
|
||||
*/
|
||||
@Override
|
||||
protected Node createNodeForKey(Object key) {
|
||||
|
||||
if (key instanceof SleuthkitVisitableItem) {
|
||||
return ((SleuthkitVisitableItem) key).accept(new RootContentChildren.CreateSleuthkitNodeVisitor());
|
||||
} else if (key instanceof AutopsyVisitableItem) {
|
||||
return ((AutopsyVisitableItem) key).accept(new RootContentChildren.CreateAutopsyNodeVisitor());
|
||||
}
|
||||
else {
|
||||
logger.log(Level.SEVERE, "Unknown key type ", key.getClass().getName());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the children
|
||||
*/
|
||||
public void refreshChildren() {
|
||||
refresh(true);
|
||||
}
|
||||
}
|
||||
@@ -221,11 +221,6 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
actionsList.addAll(Arrays.asList(super.getActions(context)));
|
||||
AbstractFile file = getLookup().lookup(AbstractFile.class);
|
||||
|
||||
// Create the "Add/Edit Central Repository Comment" menu item if the enabled.
|
||||
if (file != null && file.isFile() && EamDbUtil.useCentralRepo()) {
|
||||
actionsList.add(AddEditCentralRepoCommentAction.createAddEditCentralRepoCommentAction(file));
|
||||
}
|
||||
|
||||
//if this artifact has a time stamp add the action to view it in the timeline
|
||||
try {
|
||||
if (ViewArtifactInTimelineAction.hasSupportedTimeStamp(artifact)) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2013-2016 Basis Technology Corp.
|
||||
* Copyright 2013-2018 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -28,7 +28,6 @@ import org.openide.nodes.Children;
|
||||
import org.openide.nodes.Sheet;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.lookup.Lookups;
|
||||
import org.sleuthkit.autopsy.actions.DeleteBlackboardArtifactTagAction;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
||||
import org.sleuthkit.autopsy.timeline.actions.ViewArtifactInTimelineAction;
|
||||
@@ -132,8 +131,8 @@ public class BlackboardArtifactTagNode extends DisplayableItemNode {
|
||||
actions.add(ViewFileInTimelineAction.createViewSourceFileAction(file));
|
||||
}
|
||||
actions.add(new ViewTaggedArtifactAction(BlackboardArtifactTagNode_viewSourceArtifact_text(), artifact));
|
||||
actions.addAll(DataModelActionsFactory.getActions(tag.getContent(), true));
|
||||
actions.add(DeleteBlackboardArtifactTagAction.getInstance());
|
||||
actions.addAll(DataModelActionsFactory.getActions(tag, true));
|
||||
|
||||
return actions.toArray(new Action[0]);
|
||||
}
|
||||
|
||||
|
||||
@@ -56,6 +56,7 @@ DataModelActionsFactory.viewNewWin.text=View in New Window
|
||||
DataModelActionsFactory.openExtViewer.text=Open in External Viewer
|
||||
DataModelActionsFactory.srfFileSameMD5.text=Search for files with the same MD5 hash
|
||||
DataSourcesNode.name=Data Sources
|
||||
DataSourcesNode.group_by_datasource.name=Data Source Files
|
||||
DataSourcesNode.createSheet.name.name=Name
|
||||
DataSourcesNode.createSheet.name.displayName=Name
|
||||
DataSourcesNode.createSheet.name.desc=no description
|
||||
|
||||
@@ -57,6 +57,7 @@ DataModelActionsFactory.viewNewWin.text=\u65b0\u898f\u30a6\u30a3\u30f3\u30c9\u30
|
||||
DataModelActionsFactory.openExtViewer.text=\u5916\u90e8\u30d3\u30e5\u30fc\u30a2\u306b\u8868\u793a
|
||||
DataModelActionsFactory.srfFileSameMD5.text=\u540c\u3058MD5\u30cf\u30c3\u30b7\u30e5\u3092\u6301\u3064\u30d5\u30a1\u30a4\u30eb\u3092\u691c\u7d22
|
||||
DataSourcesNode.name=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9
|
||||
DataSourcesNode.group_by_datasource.name=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u30d5\u30a1\u30a4\u30eb
|
||||
DataSourcesNode.createSheet.name.name=\u540d\u524d
|
||||
DataSourcesNode.createSheet.name.displayName=\u540d\u524d
|
||||
DataSourcesNode.createSheet.name.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093
|
||||
|
||||
@@ -29,7 +29,6 @@ import org.openide.nodes.Sheet;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.openide.util.lookup.Lookups;
|
||||
import org.sleuthkit.autopsy.actions.DeleteContentTagAction;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.timeline.actions.ViewFileInTimelineAction;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
@@ -128,8 +127,9 @@ class ContentTagNode extends DisplayableItemNode {
|
||||
if (file != null) {
|
||||
actions.add(ViewFileInTimelineAction.createViewFileAction(file));
|
||||
}
|
||||
actions.addAll(DataModelActionsFactory.getActions(tag.getContent(), false));
|
||||
actions.add(DeleteContentTagAction.getInstance());
|
||||
|
||||
actions.addAll(DataModelActionsFactory.getActions(tag, false));
|
||||
|
||||
return actions.toArray(new Action[actions.size()]);
|
||||
}
|
||||
|
||||
|
||||
@@ -28,8 +28,12 @@ import org.openide.util.NbBundle;
|
||||
import org.openide.util.Utilities;
|
||||
import org.sleuthkit.autopsy.actions.AddBlackboardArtifactTagAction;
|
||||
import org.sleuthkit.autopsy.actions.AddContentTagAction;
|
||||
import org.sleuthkit.autopsy.actions.DeleteBlackboardArtifactTagAction;
|
||||
import org.sleuthkit.autopsy.actions.DeleteContentTagAction;
|
||||
import org.sleuthkit.autopsy.actions.DeleteFileBlackboardArtifactTagAction;
|
||||
import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction;
|
||||
import org.sleuthkit.autopsy.actions.ReplaceBlackboardArtifactTagAction;
|
||||
import org.sleuthkit.autopsy.actions.ReplaceContentTagAction;
|
||||
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
|
||||
import org.sleuthkit.autopsy.datamodel.Reports.ReportNode;
|
||||
import org.sleuthkit.autopsy.directorytree.ExternalViewerAction;
|
||||
@@ -39,7 +43,9 @@ import org.sleuthkit.autopsy.directorytree.NewWindowViewAction;
|
||||
import org.sleuthkit.autopsy.directorytree.ViewContextAction;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifactTag;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.ContentTag;
|
||||
import org.sleuthkit.datamodel.DerivedFile;
|
||||
import org.sleuthkit.datamodel.Directory;
|
||||
import org.sleuthkit.datamodel.File;
|
||||
@@ -351,6 +357,80 @@ public class DataModelActionsFactory {
|
||||
return actionsList;
|
||||
}
|
||||
|
||||
public static List<Action> getActions(ContentTag contentTag, boolean isArtifactSource) {
|
||||
|
||||
List<Action> actionsList = new ArrayList<>();
|
||||
actionsList.add(new ViewContextAction((isArtifactSource ? VIEW_SOURCE_FILE_IN_DIR : VIEW_FILE_IN_DIR), contentTag.getContent()));
|
||||
final ContentTagNode tagNode = new ContentTagNode(contentTag);
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(new NewWindowViewAction(VIEW_IN_NEW_WINDOW, tagNode));
|
||||
actionsList.add(new ExternalViewerAction(OPEN_IN_EXTERNAL_VIEWER, tagNode));
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(ExtractAction.getInstance());
|
||||
actionsList.add(new HashSearchAction(SEARCH_FOR_FILES_SAME_MD5, tagNode));
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(AddContentTagAction.getInstance());
|
||||
if (isArtifactSource) {
|
||||
actionsList.add(AddBlackboardArtifactTagAction.getInstance());
|
||||
}
|
||||
|
||||
final Collection<AbstractFile> selectedFilesList =
|
||||
new HashSet<>(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class));
|
||||
if(selectedFilesList.size() == 1) {
|
||||
actionsList.add(DeleteFileContentTagAction.getInstance());
|
||||
}
|
||||
if(isArtifactSource) {
|
||||
final Collection<BlackboardArtifact> selectedArtifactsList =
|
||||
new HashSet<>(Utilities.actionsGlobalContext().lookupAll(BlackboardArtifact.class));
|
||||
if(selectedArtifactsList.size() == 1) {
|
||||
actionsList.add(DeleteFileBlackboardArtifactTagAction.getInstance());
|
||||
}
|
||||
}
|
||||
|
||||
actionsList.add(DeleteContentTagAction.getInstance());
|
||||
actionsList.add(ReplaceContentTagAction.getInstance());
|
||||
|
||||
actionsList.addAll(ContextMenuExtensionPoint.getActions());
|
||||
return actionsList;
|
||||
}
|
||||
|
||||
|
||||
public static List<Action> getActions(BlackboardArtifactTag artifactTag, boolean isArtifactSource) {
|
||||
List<Action> actionsList = new ArrayList<>();
|
||||
actionsList.add(new ViewContextAction((isArtifactSource ? VIEW_SOURCE_FILE_IN_DIR : VIEW_FILE_IN_DIR), artifactTag.getContent()));
|
||||
final BlackboardArtifactTagNode tagNode = new BlackboardArtifactTagNode(artifactTag);
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(new NewWindowViewAction(VIEW_IN_NEW_WINDOW, tagNode));
|
||||
actionsList.add(new ExternalViewerAction(OPEN_IN_EXTERNAL_VIEWER, tagNode));
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(ExtractAction.getInstance());
|
||||
actionsList.add(new HashSearchAction(SEARCH_FOR_FILES_SAME_MD5, tagNode));
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(AddContentTagAction.getInstance());
|
||||
if (isArtifactSource) {
|
||||
actionsList.add(AddBlackboardArtifactTagAction.getInstance());
|
||||
}
|
||||
|
||||
final Collection<AbstractFile> selectedFilesList =
|
||||
new HashSet<>(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class));
|
||||
if(selectedFilesList.size() == 1) {
|
||||
actionsList.add(DeleteFileContentTagAction.getInstance());
|
||||
}
|
||||
if(isArtifactSource) {
|
||||
final Collection<BlackboardArtifact> selectedArtifactsList =
|
||||
new HashSet<>(Utilities.actionsGlobalContext().lookupAll(BlackboardArtifact.class));
|
||||
if(selectedArtifactsList.size() == 1) {
|
||||
actionsList.add(DeleteFileBlackboardArtifactTagAction.getInstance());
|
||||
}
|
||||
}
|
||||
|
||||
actionsList.add(DeleteBlackboardArtifactTagAction.getInstance());
|
||||
actionsList.add(ReplaceBlackboardArtifactTagAction.getInstance());
|
||||
|
||||
actionsList.addAll(ContextMenuExtensionPoint.getActions());
|
||||
return actionsList;
|
||||
}
|
||||
|
||||
public static List<Action> getActions(Content content, boolean isArtifactSource) {
|
||||
if (content instanceof File) {
|
||||
return getActions((File) content, isArtifactSource);
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.datamodel;
|
||||
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
|
||||
/**
|
||||
* A top level UI grouping of Files, Views, Results, Tags
|
||||
* for 'Group by Data Source' view of the tree.
|
||||
*
|
||||
*/
|
||||
public class DataSourceGrouping implements AutopsyVisitableItem {
|
||||
|
||||
private final DataSource dataSource;
|
||||
|
||||
public DataSourceGrouping(DataSource dataSource) {
|
||||
this.dataSource = dataSource;
|
||||
}
|
||||
|
||||
DataSource getDataSource() {
|
||||
return this.dataSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T accept(AutopsyItemVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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.datamodel;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
import java.util.logging.Level;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
import org.sleuthkit.datamodel.Image;
|
||||
import org.sleuthkit.datamodel.LocalFilesDataSource;
|
||||
|
||||
|
||||
/**
|
||||
* Data source grouping node - an optional grouping node in the data tree view
|
||||
*
|
||||
*/
|
||||
class DataSourceGroupingNode extends DisplayableItemNode {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(DataSourceGroupingNode.class.getName());
|
||||
|
||||
/**
|
||||
* Creates a data source grouping node for the given data source.
|
||||
*
|
||||
* @param dataSource specifies the data source
|
||||
*/
|
||||
DataSourceGroupingNode(DataSource dataSource) {
|
||||
|
||||
super (Optional.ofNullable(createDSGroupingNodeChildren(dataSource))
|
||||
.orElse(new RootContentChildren(Arrays.asList(Collections.EMPTY_LIST))));
|
||||
|
||||
if (dataSource instanceof Image) {
|
||||
Image image = (Image) dataSource;
|
||||
|
||||
super.setName(image.getName());
|
||||
super.setDisplayName(image.getName());
|
||||
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/image.png");
|
||||
} else if (dataSource instanceof LocalFilesDataSource) {
|
||||
LocalFilesDataSource localFilesDataSource = (LocalFilesDataSource) dataSource;
|
||||
|
||||
super.setName(localFilesDataSource.getName());
|
||||
super.setDisplayName(localFilesDataSource.getName());
|
||||
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/fileset-icon-16.png");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLeafTypeNode() {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static RootContentChildren createDSGroupingNodeChildren(DataSource dataSource) {
|
||||
|
||||
long dsObjId = dataSource.getId();
|
||||
try {
|
||||
return new RootContentChildren(Arrays.asList(
|
||||
new DataSources(dsObjId),
|
||||
new Views(Case.getCurrentCaseThrows().getSleuthkitCase(), dsObjId),
|
||||
new Results(Case.getCurrentCaseThrows().getSleuthkitCase(), dsObjId),
|
||||
new Tags(dsObjId) )
|
||||
|
||||
);
|
||||
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.SEVERE, "Error getting open case.", ex); //NON-NLS
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getItemType() {
|
||||
return getClass().getName();
|
||||
}
|
||||
}
|
||||
@@ -23,9 +23,20 @@ package org.sleuthkit.autopsy.datamodel;
|
||||
*/
|
||||
public class DataSources implements AutopsyVisitableItem {
|
||||
|
||||
private final long datasourceObjId;
|
||||
|
||||
public DataSources() {
|
||||
this(0);
|
||||
}
|
||||
|
||||
public DataSources(long datasourceObjId) {
|
||||
this.datasourceObjId = datasourceObjId;
|
||||
}
|
||||
|
||||
long filteringDataSourceObjId() {
|
||||
return this.datasourceObjId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T accept(AutopsyItemVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
|
||||
@@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.datamodel;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
@@ -33,6 +34,7 @@ import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskDataException;
|
||||
|
||||
/**
|
||||
* Nodes for the images
|
||||
@@ -40,22 +42,27 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
public class DataSourcesNode extends DisplayableItemNode {
|
||||
|
||||
public static final String NAME = NbBundle.getMessage(DataSourcesNode.class, "DataSourcesNode.name");
|
||||
private final String displayName;
|
||||
|
||||
// NOTE: The images passed in via argument will be ignored.
|
||||
@Deprecated
|
||||
public DataSourcesNode(List<Content> images) {
|
||||
super(new DataSourcesNodeChildren(), Lookups.singleton(NAME));
|
||||
init();
|
||||
this(0);
|
||||
}
|
||||
|
||||
public DataSourcesNode() {
|
||||
super(new DataSourcesNodeChildren(), Lookups.singleton(NAME));
|
||||
init();
|
||||
this(0);
|
||||
}
|
||||
|
||||
public DataSourcesNode(long dsObjId) {
|
||||
super(new DataSourcesNodeChildren(dsObjId), Lookups.singleton(NAME));
|
||||
displayName = (dsObjId > 0) ? NbBundle.getMessage(DataSourcesNode.class, "DataSourcesNode.group_by_datasource.name") : NAME;
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
setName(NAME);
|
||||
setDisplayName(NAME);
|
||||
setDisplayName(displayName);
|
||||
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/image.png"); //NON-NLS
|
||||
}
|
||||
|
||||
@@ -70,14 +77,20 @@ public class DataSourcesNode extends DisplayableItemNode {
|
||||
public static class DataSourcesNodeChildren extends AbstractContentChildren<Content> {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(DataSourcesNodeChildren.class.getName());
|
||||
|
||||
private final long datasourceObjId;
|
||||
|
||||
List<Content> currentKeys;
|
||||
|
||||
public DataSourcesNodeChildren() {
|
||||
super();
|
||||
this.currentKeys = new ArrayList<>();
|
||||
this(0);
|
||||
}
|
||||
|
||||
public DataSourcesNodeChildren(long dsObjId) {
|
||||
super();
|
||||
this.currentKeys = new ArrayList<>();
|
||||
this.datasourceObjId = dsObjId;
|
||||
}
|
||||
|
||||
private final PropertyChangeListener pcl = new PropertyChangeListener() {
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
@@ -103,9 +116,15 @@ public class DataSourcesNode extends DisplayableItemNode {
|
||||
|
||||
private void reloadKeys() {
|
||||
try {
|
||||
currentKeys = Case.getCurrentCaseThrows().getDataSources();
|
||||
if (datasourceObjId == 0) {
|
||||
currentKeys = Case.getCurrentCaseThrows().getDataSources();
|
||||
}
|
||||
else {
|
||||
Content content = Case.getCurrentCaseThrows().getSleuthkitCase().getDataSource(datasourceObjId);
|
||||
currentKeys = new ArrayList<>(Arrays.asList(content));
|
||||
}
|
||||
setKeys(currentKeys);
|
||||
} catch (TskCoreException | NoCurrentCaseException ex) {
|
||||
} catch (TskCoreException | NoCurrentCaseException | TskDataException ex) {
|
||||
logger.log(Level.SEVERE, "Error getting data sources: {0}", ex.getMessage()); // NON-NLS
|
||||
setKeys(Collections.<Content>emptySet());
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ import org.sleuthkit.datamodel.TskData;
|
||||
public class DeletedContent implements AutopsyVisitableItem {
|
||||
|
||||
private SleuthkitCase skCase;
|
||||
private final long datasourceObjId;
|
||||
|
||||
@NbBundle.Messages({"DeletedContent.fsDelFilter.text=File System",
|
||||
"DeletedContent.allDelFilter.text=All"})
|
||||
@@ -101,9 +102,18 @@ public class DeletedContent implements AutopsyVisitableItem {
|
||||
}
|
||||
|
||||
public DeletedContent(SleuthkitCase skCase) {
|
||||
this.skCase = skCase;
|
||||
this(skCase, 0);
|
||||
}
|
||||
|
||||
public DeletedContent(SleuthkitCase skCase, long dsObjId) {
|
||||
this.skCase = skCase;
|
||||
this.datasourceObjId = dsObjId;
|
||||
}
|
||||
|
||||
long filteringDataSourceObjId() {
|
||||
return this.datasourceObjId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T accept(AutopsyItemVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
@@ -118,8 +128,8 @@ public class DeletedContent implements AutopsyVisitableItem {
|
||||
@NbBundle.Messages("DeletedContent.deletedContentsNode.name=Deleted Files")
|
||||
private static final String NAME = Bundle.DeletedContent_deletedContentsNode_name();
|
||||
|
||||
DeletedContentsNode(SleuthkitCase skCase) {
|
||||
super(Children.create(new DeletedContentsChildren(skCase), true), Lookups.singleton(NAME));
|
||||
DeletedContentsNode(SleuthkitCase skCase, long datasourceObjId) {
|
||||
super(Children.create(new DeletedContentsChildren(skCase, datasourceObjId), true), Lookups.singleton(NAME));
|
||||
super.setName(NAME);
|
||||
super.setDisplayName(NAME);
|
||||
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file-icon-deleted.png"); //NON-NLS
|
||||
@@ -164,11 +174,13 @@ public class DeletedContent implements AutopsyVisitableItem {
|
||||
|
||||
private SleuthkitCase skCase;
|
||||
private Observable notifier;
|
||||
private final long datasourceObjId;
|
||||
// true if we have already told user that not all files will be shown
|
||||
private static volatile boolean maxFilesDialogShown = false;
|
||||
|
||||
public DeletedContentsChildren(SleuthkitCase skCase) {
|
||||
public DeletedContentsChildren(SleuthkitCase skCase, long dsObjId) {
|
||||
this.skCase = skCase;
|
||||
this.datasourceObjId = dsObjId;
|
||||
this.notifier = new DeletedContentsChildrenObservable();
|
||||
}
|
||||
|
||||
@@ -257,24 +269,27 @@ public class DeletedContent implements AutopsyVisitableItem {
|
||||
|
||||
@Override
|
||||
protected Node createNodeForKey(DeletedContent.DeletedContentFilter key) {
|
||||
return new DeletedContentNode(skCase, key, notifier);
|
||||
return new DeletedContentNode(skCase, key, notifier, datasourceObjId);
|
||||
}
|
||||
|
||||
public class DeletedContentNode extends DisplayableItemNode {
|
||||
|
||||
private final DeletedContent.DeletedContentFilter filter;
|
||||
private final long datasourceObjId;
|
||||
|
||||
// Use version that has observer for updates
|
||||
@Deprecated
|
||||
DeletedContentNode(SleuthkitCase skCase, DeletedContent.DeletedContentFilter filter) {
|
||||
super(Children.create(new DeletedContentChildren(filter, skCase, null), true), Lookups.singleton(filter.getDisplayName()));
|
||||
DeletedContentNode(SleuthkitCase skCase, DeletedContent.DeletedContentFilter filter, long dsObjId) {
|
||||
super(Children.create(new DeletedContentChildren(filter, skCase, null, dsObjId ), true), Lookups.singleton(filter.getDisplayName()));
|
||||
this.filter = filter;
|
||||
this.datasourceObjId = dsObjId;
|
||||
init();
|
||||
}
|
||||
|
||||
DeletedContentNode(SleuthkitCase skCase, DeletedContent.DeletedContentFilter filter, Observable o) {
|
||||
super(Children.create(new DeletedContentChildren(filter, skCase, o), true), Lookups.singleton(filter.getDisplayName()));
|
||||
DeletedContentNode(SleuthkitCase skCase, DeletedContent.DeletedContentFilter filter, Observable o, long dsObjId) {
|
||||
super(Children.create(new DeletedContentChildren(filter, skCase, o, dsObjId), true), Lookups.singleton(filter.getDisplayName()));
|
||||
this.filter = filter;
|
||||
this.datasourceObjId = dsObjId;
|
||||
init();
|
||||
o.addObserver(new DeletedContentNodeObserver());
|
||||
}
|
||||
@@ -299,7 +314,7 @@ public class DeletedContent implements AutopsyVisitableItem {
|
||||
|
||||
private void updateDisplayName() {
|
||||
//get count of children without preloading all children nodes
|
||||
final long count = DeletedContentChildren.calculateItems(skCase, filter);
|
||||
final long count = DeletedContentChildren.calculateItems(skCase, filter, datasourceObjId);
|
||||
//final long count = getChildren().getNodesCount(true);
|
||||
super.setDisplayName(filter.getDisplayName() + " (" + count + ")");
|
||||
}
|
||||
@@ -351,11 +366,13 @@ public class DeletedContent implements AutopsyVisitableItem {
|
||||
private static final Logger logger = Logger.getLogger(DeletedContentChildren.class.getName());
|
||||
private static final int MAX_OBJECTS = 10001;
|
||||
private final Observable notifier;
|
||||
private final long datasourceObjId;
|
||||
|
||||
DeletedContentChildren(DeletedContent.DeletedContentFilter filter, SleuthkitCase skCase, Observable o) {
|
||||
DeletedContentChildren(DeletedContent.DeletedContentFilter filter, SleuthkitCase skCase, Observable o, long datasourceObjId) {
|
||||
this.skCase = skCase;
|
||||
this.filter = filter;
|
||||
this.notifier = o;
|
||||
this.datasourceObjId = datasourceObjId;
|
||||
}
|
||||
|
||||
private final Observer observer = new DeletedContentChildrenObserver();
|
||||
@@ -366,7 +383,7 @@ public class DeletedContent implements AutopsyVisitableItem {
|
||||
@Override
|
||||
public void update(Observable o, Object arg) {
|
||||
refresh(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -405,7 +422,7 @@ public class DeletedContent implements AutopsyVisitableItem {
|
||||
return true;
|
||||
}
|
||||
|
||||
static private String makeQuery(DeletedContent.DeletedContentFilter filter) {
|
||||
static private String makeQuery(DeletedContent.DeletedContentFilter filter, long filteringDSObjId) {
|
||||
String query = "";
|
||||
switch (filter) {
|
||||
case FS_DELETED_FILTER:
|
||||
@@ -443,6 +460,10 @@ public class DeletedContent implements AutopsyVisitableItem {
|
||||
+ " OR known IS NULL)"; //NON-NLS
|
||||
}
|
||||
|
||||
if (UserPreferences.groupItemsInTreeByDatasource()) {
|
||||
query += " AND data_source_obj_id = " + filteringDSObjId;
|
||||
}
|
||||
|
||||
query += " LIMIT " + MAX_OBJECTS; //NON-NLS
|
||||
return query;
|
||||
}
|
||||
@@ -450,7 +471,7 @@ public class DeletedContent implements AutopsyVisitableItem {
|
||||
private List<AbstractFile> runFsQuery() {
|
||||
List<AbstractFile> ret = new ArrayList<>();
|
||||
|
||||
String query = makeQuery(filter);
|
||||
String query = makeQuery(filter, datasourceObjId);
|
||||
try {
|
||||
ret = skCase.findAllFilesWhere(query);
|
||||
} catch (TskCoreException e) {
|
||||
@@ -469,9 +490,9 @@ public class DeletedContent implements AutopsyVisitableItem {
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
static long calculateItems(SleuthkitCase sleuthkitCase, DeletedContent.DeletedContentFilter filter) {
|
||||
static long calculateItems(SleuthkitCase sleuthkitCase, DeletedContent.DeletedContentFilter filter, long datasourceObjId) {
|
||||
try {
|
||||
return sleuthkitCase.countFilesWhere(makeQuery(filter));
|
||||
return sleuthkitCase.countFilesWhere(makeQuery(filter, datasourceObjId));
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Error getting deleted files search view count", ex); //NON-NLS
|
||||
return 0;
|
||||
|
||||
@@ -61,6 +61,8 @@ public interface DisplayableItemNodeVisitor<T> {
|
||||
*/
|
||||
T visit(ViewsNode vn);
|
||||
|
||||
T visit(DataSourceGroupingNode dataSourceGroupingNode);
|
||||
|
||||
T visit(org.sleuthkit.autopsy.datamodel.FileTypesByExtension.FileExtensionNode fsfn);
|
||||
|
||||
T visit(DeletedContentNode dcn);
|
||||
@@ -343,6 +345,11 @@ public interface DisplayableItemNodeVisitor<T> {
|
||||
return defaultVisit(vn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T visit(DataSourceGroupingNode dataSourceGroupingNode) {
|
||||
return defaultVisit(dataSourceGroupingNode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T visit(ResultsNode rn) {
|
||||
return defaultVisit(rn);
|
||||
|
||||
@@ -40,6 +40,7 @@ import org.openide.util.NbBundle;
|
||||
import org.openide.util.lookup.Lookups;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.core.UserPreferences;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
|
||||
@@ -86,10 +87,29 @@ public class EmailExtracted implements AutopsyVisitableItem {
|
||||
}
|
||||
private SleuthkitCase skCase;
|
||||
private final EmailResults emailResults;
|
||||
private final long datasourceObjId;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param skCase Case DB
|
||||
*/
|
||||
public EmailExtracted(SleuthkitCase skCase) {
|
||||
this(skCase, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param skCase Case DB
|
||||
* @param objId Object id of the data source
|
||||
*
|
||||
*/
|
||||
public EmailExtracted(SleuthkitCase skCase, long objId) {
|
||||
this.skCase = skCase;
|
||||
this.datasourceObjId = objId;
|
||||
emailResults = new EmailResults();
|
||||
}
|
||||
|
||||
@@ -141,6 +161,9 @@ public class EmailExtracted implements AutopsyVisitableItem {
|
||||
+ "attribute_type_id=" + pathAttrId //NON-NLS
|
||||
+ " AND blackboard_attributes.artifact_id=blackboard_artifacts.artifact_id" //NON-NLS
|
||||
+ " AND blackboard_artifacts.artifact_type_id=" + artId; //NON-NLS
|
||||
if (UserPreferences.groupItemsInTreeByDatasource()) {
|
||||
query += " AND blackboard_artifacts.data_source_obj_id = " + datasourceObjId;
|
||||
}
|
||||
|
||||
try (CaseDbQuery dbQuery = skCase.executeQuery(query)) {
|
||||
ResultSet resultSet = dbQuery.getResultSet();
|
||||
|
||||
@@ -35,9 +35,11 @@ import org.openide.util.NbBundle;
|
||||
import org.openide.util.lookup.Lookups;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.core.UserPreferences;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
|
||||
import org.sleuthkit.datamodel.Blackboard;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT;
|
||||
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG;
|
||||
@@ -57,12 +59,31 @@ import org.sleuthkit.datamodel.TskException;
|
||||
public class ExtractedContent implements AutopsyVisitableItem {
|
||||
|
||||
private SleuthkitCase skCase; // set to null after case has been closed
|
||||
private Blackboard blackboard;
|
||||
public static final String NAME = NbBundle.getMessage(RootNode.class, "ExtractedContentNode.name.text");
|
||||
private final long datasourceObjId;
|
||||
|
||||
/**
|
||||
* Constructs extracted content object
|
||||
*
|
||||
* @param skCase Case DB
|
||||
*/
|
||||
public ExtractedContent(SleuthkitCase skCase) {
|
||||
this.skCase = skCase;
|
||||
this(skCase, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs extracted content object
|
||||
*
|
||||
* @param skCase Case DB
|
||||
* @param objId Object id of the parent datasource
|
||||
*/
|
||||
public ExtractedContent(SleuthkitCase skCase, long objId) {
|
||||
this.skCase = skCase;
|
||||
this.datasourceObjId = objId;
|
||||
this.blackboard = skCase.getBlackboard();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T accept(AutopsyItemVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
@@ -270,7 +291,10 @@ public class ExtractedContent implements AutopsyVisitableItem {
|
||||
//TEST COMMENT
|
||||
if (skCase != null) {
|
||||
try {
|
||||
List<BlackboardArtifact.Type> types = skCase.getArtifactTypesInUse();
|
||||
List<BlackboardArtifact.Type> types = (UserPreferences.groupItemsInTreeByDatasource()) ?
|
||||
blackboard.getArtifactTypesInUse(datasourceObjId) :
|
||||
skCase.getArtifactTypesInUse() ;
|
||||
|
||||
types.removeAll(doNotShow);
|
||||
Collections.sort(types,
|
||||
new Comparator<BlackboardArtifact.Type>() {
|
||||
@@ -332,7 +356,9 @@ public class ExtractedContent implements AutopsyVisitableItem {
|
||||
// a performance increase might be had by adding a
|
||||
// "getBlackboardArtifactCount()" method to skCase
|
||||
try {
|
||||
this.childCount = skCase.getBlackboardArtifactsTypeCount(type.getTypeID());
|
||||
this.childCount = UserPreferences.groupItemsInTreeByDatasource() ?
|
||||
blackboard.getArtifactsCount(type.getTypeID(), datasourceObjId) :
|
||||
skCase.getBlackboardArtifactsTypeCount(type.getTypeID());
|
||||
} catch (TskException ex) {
|
||||
Logger.getLogger(TypeNode.class.getName())
|
||||
.log(Level.WARNING, "Error getting child count", ex); //NON-NLS
|
||||
@@ -454,7 +480,10 @@ public class ExtractedContent implements AutopsyVisitableItem {
|
||||
protected boolean createKeys(List<BlackboardArtifact> list) {
|
||||
if (skCase != null) {
|
||||
try {
|
||||
List<BlackboardArtifact> arts = skCase.getBlackboardArtifacts(type.getTypeID());
|
||||
List<BlackboardArtifact> arts =
|
||||
UserPreferences.groupItemsInTreeByDatasource() ?
|
||||
blackboard.getArtifacts(type.getTypeID(), datasourceObjId) :
|
||||
skCase.getBlackboardArtifacts(type.getTypeID());
|
||||
list.addAll(arts);
|
||||
} catch (TskException ex) {
|
||||
Logger.getLogger(ArtifactFactory.class.getName()).log(Level.SEVERE, "Couldn't get blackboard artifacts from database", ex); //NON-NLS
|
||||
|
||||
@@ -61,6 +61,7 @@ import org.sleuthkit.datamodel.VirtualDirectory;
|
||||
public class FileSize implements AutopsyVisitableItem {
|
||||
|
||||
private SleuthkitCase skCase;
|
||||
private final long datasourceObjId;
|
||||
|
||||
public enum FileSizeFilter implements AutopsyVisitableItem {
|
||||
|
||||
@@ -97,9 +98,14 @@ public class FileSize implements AutopsyVisitableItem {
|
||||
}
|
||||
|
||||
public FileSize(SleuthkitCase skCase) {
|
||||
this.skCase = skCase;
|
||||
this(skCase, 0);
|
||||
}
|
||||
|
||||
public FileSize(SleuthkitCase skCase, long dsObjId) {
|
||||
this.skCase = skCase;
|
||||
this.datasourceObjId = dsObjId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T accept(AutopsyItemVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
@@ -109,6 +115,9 @@ public class FileSize implements AutopsyVisitableItem {
|
||||
return this.skCase;
|
||||
}
|
||||
|
||||
long filteringDataSourceObjId() {
|
||||
return this.datasourceObjId;
|
||||
}
|
||||
/*
|
||||
* Root node. Children are nodes for specific sizes.
|
||||
*/
|
||||
@@ -116,8 +125,8 @@ public class FileSize implements AutopsyVisitableItem {
|
||||
|
||||
private static final String NAME = NbBundle.getMessage(FileSize.class, "FileSize.fileSizeRootNode.name");
|
||||
|
||||
FileSizeRootNode(SleuthkitCase skCase) {
|
||||
super(Children.create(new FileSizeRootChildren(skCase), true), Lookups.singleton(NAME));
|
||||
FileSizeRootNode(SleuthkitCase skCase, long datasourceObjId) {
|
||||
super(Children.create(new FileSizeRootChildren(skCase, datasourceObjId), true), Lookups.singleton(NAME));
|
||||
super.setName(NAME);
|
||||
super.setDisplayName(NAME);
|
||||
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file-size-16.png"); //NON-NLS
|
||||
@@ -161,10 +170,12 @@ public class FileSize implements AutopsyVisitableItem {
|
||||
public static class FileSizeRootChildren extends ChildFactory<org.sleuthkit.autopsy.datamodel.FileSize.FileSizeFilter> {
|
||||
|
||||
private SleuthkitCase skCase;
|
||||
private final long datasourceObjId;
|
||||
private Observable notifier;
|
||||
|
||||
public FileSizeRootChildren(SleuthkitCase skCase) {
|
||||
public FileSizeRootChildren(SleuthkitCase skCase, long datasourceObjId) {
|
||||
this.skCase = skCase;
|
||||
this.datasourceObjId = datasourceObjId;
|
||||
notifier = new FileSizeRootChildrenObservable();
|
||||
}
|
||||
|
||||
@@ -248,7 +259,7 @@ public class FileSize implements AutopsyVisitableItem {
|
||||
|
||||
@Override
|
||||
protected Node createNodeForKey(FileSizeFilter key) {
|
||||
return new FileSizeNode(skCase, key, notifier);
|
||||
return new FileSizeNode(skCase, key, notifier, datasourceObjId);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -257,12 +268,14 @@ public class FileSize implements AutopsyVisitableItem {
|
||||
public class FileSizeNode extends DisplayableItemNode {
|
||||
|
||||
private FileSizeFilter filter;
|
||||
private final long datasourceObjId;
|
||||
|
||||
// use version with observer instead so that it updates
|
||||
@Deprecated
|
||||
FileSizeNode(SleuthkitCase skCase, FileSizeFilter filter) {
|
||||
super(Children.create(new FileSizeChildren(filter, skCase, null), true), Lookups.singleton(filter.getDisplayName()));
|
||||
FileSizeNode(SleuthkitCase skCase, FileSizeFilter filter, long datasourceObjId) {
|
||||
super(Children.create(new FileSizeChildren(filter, skCase, null, datasourceObjId), true), Lookups.singleton(filter.getDisplayName()));
|
||||
this.filter = filter;
|
||||
this.datasourceObjId = datasourceObjId;
|
||||
init();
|
||||
}
|
||||
|
||||
@@ -272,10 +285,12 @@ public class FileSize implements AutopsyVisitableItem {
|
||||
* @param filter
|
||||
* @param o Observable that provides updates when events are
|
||||
* fired
|
||||
* @param datasourceObjId filter by data source, if configured in user preferences
|
||||
*/
|
||||
FileSizeNode(SleuthkitCase skCase, FileSizeFilter filter, Observable o) {
|
||||
super(Children.create(new FileSizeChildren(filter, skCase, o), true), Lookups.singleton(filter.getDisplayName()));
|
||||
FileSizeNode(SleuthkitCase skCase, FileSizeFilter filter, Observable o, long datasourceObjId) {
|
||||
super(Children.create(new FileSizeChildren(filter, skCase, o, datasourceObjId), true), Lookups.singleton(filter.getDisplayName()));
|
||||
this.filter = filter;
|
||||
this.datasourceObjId = datasourceObjId;
|
||||
init();
|
||||
o.addObserver(new FileSizeNodeObserver());
|
||||
}
|
||||
@@ -309,7 +324,7 @@ public class FileSize implements AutopsyVisitableItem {
|
||||
}
|
||||
|
||||
private void updateDisplayName() {
|
||||
final long numVisibleChildren = FileSizeChildren.calculateItems(skCase, filter);
|
||||
final long numVisibleChildren = FileSizeChildren.calculateItems(skCase, filter, datasourceObjId);
|
||||
super.setDisplayName(filter.getDisplayName() + " (" + numVisibleChildren + ")");
|
||||
}
|
||||
|
||||
@@ -349,6 +364,7 @@ public class FileSize implements AutopsyVisitableItem {
|
||||
private final SleuthkitCase skCase;
|
||||
private final FileSizeFilter filter;
|
||||
private final Observable notifier;
|
||||
private final long datasourceObjId;
|
||||
private static final Logger logger = Logger.getLogger(FileSizeChildren.class.getName());
|
||||
|
||||
/**
|
||||
@@ -358,10 +374,12 @@ public class FileSize implements AutopsyVisitableItem {
|
||||
* @param o Observable that provides updates when new files are
|
||||
* added to case
|
||||
*/
|
||||
FileSizeChildren(FileSizeFilter filter, SleuthkitCase skCase, Observable o) {
|
||||
FileSizeChildren(FileSizeFilter filter, SleuthkitCase skCase, Observable o, long dsObjId) {
|
||||
this.skCase = skCase;
|
||||
this.filter = filter;
|
||||
this.notifier = o;
|
||||
this.datasourceObjId = dsObjId;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -395,7 +413,7 @@ public class FileSize implements AutopsyVisitableItem {
|
||||
return true;
|
||||
}
|
||||
|
||||
private static String makeQuery(FileSizeFilter filter) {
|
||||
private static String makeQuery(FileSizeFilter filter, long filteringDSObjId) {
|
||||
String query;
|
||||
switch (filter) {
|
||||
case SIZE_50_200:
|
||||
@@ -427,6 +445,11 @@ public class FileSize implements AutopsyVisitableItem {
|
||||
query += " AND (type != " + TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.getFileType() + ")"; //NON-NLS
|
||||
}
|
||||
|
||||
// filter by datasource if indicated in user preferences
|
||||
if (UserPreferences.groupItemsInTreeByDatasource()) {
|
||||
query += " AND data_source_obj_id = " + filteringDSObjId;
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
@@ -434,7 +457,7 @@ public class FileSize implements AutopsyVisitableItem {
|
||||
List<AbstractFile> ret = new ArrayList<>();
|
||||
|
||||
try {
|
||||
String query = makeQuery(filter);
|
||||
String query = makeQuery(filter, datasourceObjId);
|
||||
|
||||
ret = skCase.findAllFilesWhere(query);
|
||||
} catch (Exception e) {
|
||||
@@ -449,9 +472,9 @@ public class FileSize implements AutopsyVisitableItem {
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
static long calculateItems(SleuthkitCase sleuthkitCase, FileSizeFilter filter) {
|
||||
static long calculateItems(SleuthkitCase sleuthkitCase, FileSizeFilter filter, long datasourceObjId) {
|
||||
try {
|
||||
return sleuthkitCase.countFilesWhere(makeQuery(filter));
|
||||
return sleuthkitCase.countFilesWhere(makeQuery(filter, datasourceObjId));
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Error getting files by size search view count", ex); //NON-NLS
|
||||
return 0;
|
||||
|
||||
@@ -72,11 +72,18 @@ public final class FileTypes implements AutopsyVisitableItem {
|
||||
|
||||
private final SleuthkitCase skCase;
|
||||
|
||||
private final long datasourceObjId;
|
||||
|
||||
FileTypes(SleuthkitCase skCase) {
|
||||
this.skCase = skCase;
|
||||
updateShowCounts();
|
||||
this(skCase, 0);
|
||||
}
|
||||
|
||||
FileTypes(SleuthkitCase skCase, long dsObjId) {
|
||||
this.skCase = skCase;
|
||||
this.datasourceObjId = dsObjId;
|
||||
updateShowCounts();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T accept(AutopsyItemVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
@@ -86,6 +93,9 @@ public final class FileTypes implements AutopsyVisitableItem {
|
||||
return skCase;
|
||||
}
|
||||
|
||||
long filteringDataSourceObjId() {
|
||||
return this.datasourceObjId;
|
||||
}
|
||||
/**
|
||||
* Check the db to determine if the nodes should show child counts.
|
||||
*/
|
||||
|
||||
@@ -69,6 +69,10 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
long filteringDataSourceObjId() {
|
||||
return typesRoot.filteringDataSourceObjId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens for case and ingest invest. Updates observers when events are
|
||||
* fired. FileType and FileTypes nodes are all listening to this.
|
||||
@@ -359,6 +363,9 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
|
||||
+ (UserPreferences.hideKnownFilesInViewsTree()
|
||||
? " AND (known IS NULL OR known != " + TskData.FileKnown.KNOWN.getFileKnownValue() + ")"
|
||||
: " ")
|
||||
+ (UserPreferences.groupItemsInTreeByDatasource()
|
||||
? " AND data_source_obj_id = " + filteringDataSourceObjId()
|
||||
: " ")
|
||||
+ " AND (extension IN (" + filter.getFilter().stream()
|
||||
.map(String::toLowerCase)
|
||||
.map(s -> "'"+StringUtils.substringAfter(s, ".")+"'")
|
||||
|
||||
@@ -42,6 +42,7 @@ import org.openide.util.NbBundle;
|
||||
import org.openide.util.lookup.Lookups;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.core.UserPreferences;
|
||||
import static org.sleuthkit.autopsy.core.UserPreferences.hideKnownFilesInViewsTree;
|
||||
import static org.sleuthkit.autopsy.core.UserPreferences.hideSlackFilesInViewsTree;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
@@ -91,15 +92,16 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
|
||||
* @return The base expression to be used in the where clause of queries for
|
||||
* files by mime type.
|
||||
*/
|
||||
static private String createBaseWhereExpr() {
|
||||
private String createBaseWhereExpr() {
|
||||
return "(dir_type = " + TskData.TSK_FS_NAME_TYPE_ENUM.REG.getValue() + ")"
|
||||
+ " AND (type IN ("
|
||||
+ TskData.TSK_DB_FILES_TYPE_ENUM.FS.ordinal() + ","
|
||||
+ TskData.TSK_DB_FILES_TYPE_ENUM.CARVED.ordinal() + ","
|
||||
+ TskData.TSK_DB_FILES_TYPE_ENUM.DERIVED.ordinal() + ","
|
||||
+ TskData.TSK_DB_FILES_TYPE_ENUM.LOCAL.ordinal()
|
||||
+ (hideSlackFilesInViewsTree() ? "" : ("," + TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.ordinal()))
|
||||
+ (hideSlackFilesInViewsTree() ? "" : ("," + TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.ordinal()))
|
||||
+ "))"
|
||||
+ ( UserPreferences.groupItemsInTreeByDatasource() ? " AND data_source_obj_id = " + this.filteringDataSourceObjId() : " ")
|
||||
+ (hideKnownFilesInViewsTree() ? (" AND (known IS NULL OR known != " + TskData.FileKnown.KNOWN.getFileKnownValue() + ")") : "");
|
||||
}
|
||||
|
||||
@@ -188,6 +190,10 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
long filteringDataSourceObjId() {
|
||||
return typesRoot.filteringDataSourceObjId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to check if the node in question is a ByMimeTypeNode which is
|
||||
* empty.
|
||||
|
||||
@@ -42,6 +42,7 @@ import org.openide.util.NbBundle;
|
||||
import org.openide.util.lookup.Lookups;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.core.UserPreferences;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
|
||||
@@ -63,9 +64,29 @@ public class HashsetHits implements AutopsyVisitableItem {
|
||||
private static final Logger logger = Logger.getLogger(HashsetHits.class.getName());
|
||||
private SleuthkitCase skCase;
|
||||
private final HashsetResults hashsetResults;
|
||||
|
||||
private final long datasourceObjId;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param skCase Case DB
|
||||
*
|
||||
*/
|
||||
public HashsetHits(SleuthkitCase skCase) {
|
||||
this(skCase, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param skCase Case DB
|
||||
* @param objId Object id of the data source
|
||||
*
|
||||
*/
|
||||
public HashsetHits(SleuthkitCase skCase, long objId) {
|
||||
this.skCase = skCase;
|
||||
this.datasourceObjId = objId;
|
||||
hashsetResults = new HashsetResults();
|
||||
}
|
||||
|
||||
@@ -120,7 +141,10 @@ public class HashsetHits implements AutopsyVisitableItem {
|
||||
+ "attribute_type_id=" + setNameId //NON-NLS
|
||||
+ " AND blackboard_attributes.artifact_id=blackboard_artifacts.artifact_id" //NON-NLS
|
||||
+ " AND blackboard_artifacts.artifact_type_id=" + artId; //NON-NLS
|
||||
|
||||
if (UserPreferences.groupItemsInTreeByDatasource()) {
|
||||
query += " AND blackboard_artifacts.data_source_obj_id = " + datasourceObjId;
|
||||
}
|
||||
|
||||
try (CaseDbQuery dbQuery = skCase.executeQuery(query)) {
|
||||
ResultSet resultSet = dbQuery.getResultSet();
|
||||
synchronized (hashSetHitsMap) {
|
||||
|
||||
@@ -42,6 +42,7 @@ import org.openide.util.NbBundle;
|
||||
import org.openide.util.lookup.Lookups;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.core.UserPreferences;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
|
||||
@@ -59,9 +60,28 @@ public class InterestingHits implements AutopsyVisitableItem {
|
||||
private static final Logger logger = Logger.getLogger(InterestingHits.class.getName());
|
||||
private SleuthkitCase skCase;
|
||||
private final InterestingResults interestingResults = new InterestingResults();
|
||||
private final long datasourceObjId;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param skCase Case DB
|
||||
*
|
||||
*/
|
||||
public InterestingHits(SleuthkitCase skCase) {
|
||||
this(skCase, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param skCase Case DB
|
||||
* @param objId Object id of the data source
|
||||
*
|
||||
*/
|
||||
public InterestingHits(SleuthkitCase skCase, long objId) {
|
||||
this.skCase = skCase;
|
||||
this.datasourceObjId = objId;
|
||||
interestingResults.update();
|
||||
}
|
||||
|
||||
@@ -112,6 +132,9 @@ public class InterestingHits implements AutopsyVisitableItem {
|
||||
+ "attribute_type_id=" + setNameId //NON-NLS
|
||||
+ " AND blackboard_attributes.artifact_id=blackboard_artifacts.artifact_id" //NON-NLS
|
||||
+ " AND blackboard_artifacts.artifact_type_id=" + artId; //NON-NLS
|
||||
if (UserPreferences.groupItemsInTreeByDatasource()) {
|
||||
query += " AND blackboard_artifacts.data_source_obj_id = " + datasourceObjId;
|
||||
}
|
||||
|
||||
try (CaseDbQuery dbQuery = skCase.executeQuery(query)) {
|
||||
synchronized (interestingItemsMap) {
|
||||
|
||||
@@ -44,6 +44,7 @@ import org.openide.util.NbBundle;
|
||||
import org.openide.util.lookup.Lookups;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.core.UserPreferences;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import static org.sleuthkit.autopsy.datamodel.Bundle.*;
|
||||
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||
@@ -73,6 +74,7 @@ public class KeywordHits implements AutopsyVisitableItem {
|
||||
|
||||
private SleuthkitCase skCase;
|
||||
private final KeywordResults keywordResults;
|
||||
private final long datasourceObjId;
|
||||
|
||||
/**
|
||||
* String used in the instance MAP so that exact matches and substring can
|
||||
@@ -81,6 +83,7 @@ public class KeywordHits implements AutopsyVisitableItem {
|
||||
*/
|
||||
private static final String DEFAULT_INSTANCE_NAME = "DEFAULT_INSTANCE_NAME";
|
||||
|
||||
|
||||
/**
|
||||
* query attributes table for the ones that we need for the tree
|
||||
*/
|
||||
@@ -101,8 +104,25 @@ public class KeywordHits implements AutopsyVisitableItem {
|
||||
return (instances.size() == 1) && (instances.get(0).equals(DEFAULT_INSTANCE_NAME));
|
||||
}
|
||||
|
||||
public KeywordHits(SleuthkitCase skCase) {
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param skCase Case DB
|
||||
*/
|
||||
KeywordHits(SleuthkitCase skCase) {
|
||||
this(skCase, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param skCase Case DB
|
||||
* @param objId Object id of the data source
|
||||
*
|
||||
*/
|
||||
public KeywordHits(SleuthkitCase skCase, long objId) {
|
||||
this.skCase = skCase;
|
||||
this.datasourceObjId = objId;
|
||||
keywordResults = new KeywordResults();
|
||||
}
|
||||
|
||||
@@ -300,7 +320,12 @@ public class KeywordHits implements AutopsyVisitableItem {
|
||||
return;
|
||||
}
|
||||
|
||||
try (CaseDbQuery dbQuery = skCase.executeQuery(KEYWORD_HIT_ATTRIBUTES_QUERY)) {
|
||||
String queryStr = KEYWORD_HIT_ATTRIBUTES_QUERY;
|
||||
if (UserPreferences.groupItemsInTreeByDatasource()) {
|
||||
queryStr += " AND blackboard_artifacts.data_source_obj_id = " + datasourceObjId;
|
||||
}
|
||||
|
||||
try (CaseDbQuery dbQuery = skCase.executeQuery(queryStr)) {
|
||||
ResultSet resultSet = dbQuery.getResultSet();
|
||||
while (resultSet.next()) {
|
||||
long artifactId = resultSet.getLong("artifact_id"); //NON-NLS
|
||||
|
||||
@@ -26,11 +26,17 @@ import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
public class Results implements AutopsyVisitableItem {
|
||||
|
||||
private SleuthkitCase skCase;
|
||||
private final long datasourceObjId;
|
||||
|
||||
public Results(SleuthkitCase skCase) {
|
||||
this.skCase = skCase;
|
||||
this(skCase, 0);
|
||||
}
|
||||
|
||||
public Results(SleuthkitCase skCase, long dsObjId) {
|
||||
this.skCase = skCase;
|
||||
this.datasourceObjId = dsObjId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T accept(AutopsyItemVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
@@ -39,4 +45,8 @@ public class Results implements AutopsyVisitableItem {
|
||||
public SleuthkitCase getSleuthkitCase() {
|
||||
return skCase;
|
||||
}
|
||||
|
||||
long filteringDataSourceObjId() {
|
||||
return datasourceObjId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-2016 Basis Technology Corp.
|
||||
* Copyright 2011-2018 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -33,17 +33,22 @@ public class ResultsNode extends DisplayableItemNode {
|
||||
@NbBundle.Messages("ResultsNode.name.text=Results")
|
||||
public static final String NAME = Bundle.ResultsNode_name_text();
|
||||
|
||||
|
||||
|
||||
public ResultsNode(SleuthkitCase sleuthkitCase) {
|
||||
super(new RootContentChildren(Arrays.asList(
|
||||
new ExtractedContent(sleuthkitCase),
|
||||
new KeywordHits(sleuthkitCase),
|
||||
new HashsetHits(sleuthkitCase),
|
||||
new EmailExtracted(sleuthkitCase),
|
||||
new InterestingHits(sleuthkitCase),
|
||||
new Accounts(sleuthkitCase)
|
||||
)), Lookups.singleton(NAME));
|
||||
this(sleuthkitCase, 0);
|
||||
}
|
||||
|
||||
public ResultsNode(SleuthkitCase sleuthkitCase, long dsObjId) {
|
||||
super(
|
||||
|
||||
new RootContentChildren(Arrays.asList(
|
||||
new ExtractedContent(sleuthkitCase, dsObjId ),
|
||||
new KeywordHits(sleuthkitCase, dsObjId),
|
||||
new HashsetHits(sleuthkitCase, dsObjId),
|
||||
new EmailExtracted(sleuthkitCase, dsObjId),
|
||||
new InterestingHits(sleuthkitCase, dsObjId ),
|
||||
new Accounts(sleuthkitCase, dsObjId) )
|
||||
),
|
||||
Lookups.singleton(NAME));
|
||||
setName(NAME);
|
||||
setDisplayName(NAME);
|
||||
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/results.png"); //NON-NLS
|
||||
|
||||
@@ -36,6 +36,7 @@ import org.openide.util.lookup.Lookups;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.casemodule.services.TagsManager;
|
||||
import org.sleuthkit.autopsy.core.UserPreferences;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifactTag;
|
||||
@@ -57,6 +58,20 @@ public class Tags implements AutopsyVisitableItem {
|
||||
private final String DISPLAY_NAME = NbBundle.getMessage(RootNode.class, "TagsNode.displayName.text");
|
||||
private final String ICON_PATH = "org/sleuthkit/autopsy/images/tag-folder-blue-icon-16.png"; //NON-NLS
|
||||
|
||||
private final long datasourceObjId;
|
||||
|
||||
Tags() {
|
||||
this(0);
|
||||
}
|
||||
|
||||
Tags(long dsObjId) {
|
||||
this.datasourceObjId = dsObjId;
|
||||
}
|
||||
|
||||
long filteringDataSourceObjId() {
|
||||
return this.datasourceObjId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T accept(AutopsyItemVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
@@ -83,11 +98,13 @@ public class Tags implements AutopsyVisitableItem {
|
||||
*/
|
||||
public class RootNode extends DisplayableItemNode {
|
||||
|
||||
public RootNode() {
|
||||
super(Children.create(new TagNameNodeFactory(), true), Lookups.singleton(DISPLAY_NAME));
|
||||
|
||||
public RootNode(long objId) {
|
||||
super(Children.create(new TagNameNodeFactory(objId), true), Lookups.singleton(DISPLAY_NAME));
|
||||
super.setName(DISPLAY_NAME);
|
||||
super.setDisplayName(DISPLAY_NAME);
|
||||
this.setIconBaseWithExtension(ICON_PATH);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -121,6 +138,8 @@ public class Tags implements AutopsyVisitableItem {
|
||||
|
||||
private class TagNameNodeFactory extends ChildFactory.Detachable<TagName> implements Observer {
|
||||
|
||||
private final long datasourceObjId;
|
||||
|
||||
private final Set<Case.Events> CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED,
|
||||
Case.Events.BLACKBOARD_ARTIFACT_TAG_DELETED,
|
||||
Case.Events.CONTENT_TAG_ADDED,
|
||||
@@ -176,6 +195,15 @@ public class Tags implements AutopsyVisitableItem {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param objId data source object id
|
||||
*/
|
||||
TagNameNodeFactory(long objId) {
|
||||
this.datasourceObjId = objId;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addNotify() {
|
||||
IngestManager.getInstance().addIngestJobEventListener(pcl);
|
||||
@@ -196,7 +224,11 @@ public class Tags implements AutopsyVisitableItem {
|
||||
@Override
|
||||
protected boolean createKeys(List<TagName> keys) {
|
||||
try {
|
||||
List<TagName> tagNamesInUse = Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUse();
|
||||
|
||||
List<TagName> tagNamesInUse = UserPreferences.groupItemsInTreeByDatasource() ?
|
||||
Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUse(datasourceObjId) :
|
||||
Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUse()
|
||||
;
|
||||
Collections.sort(tagNamesInUse);
|
||||
keys.addAll(tagNamesInUse);
|
||||
} catch (TskCoreException | NoCurrentCaseException ex) {
|
||||
@@ -244,8 +276,15 @@ public class Tags implements AutopsyVisitableItem {
|
||||
long tagsCount = 0;
|
||||
try {
|
||||
TagsManager tm = Case.getCurrentCaseThrows().getServices().getTagsManager();
|
||||
tagsCount = tm.getContentTagsCountByTagName(tagName);
|
||||
tagsCount += tm.getBlackboardArtifactTagsCountByTagName(tagName);
|
||||
if (UserPreferences.groupItemsInTreeByDatasource()) {
|
||||
tagsCount = tm.getContentTagsCountByTagName(tagName, datasourceObjId);
|
||||
tagsCount += tm.getBlackboardArtifactTagsCountByTagName(tagName, datasourceObjId);
|
||||
}
|
||||
else {
|
||||
tagsCount = tm.getContentTagsCountByTagName(tagName);
|
||||
tagsCount += tm.getBlackboardArtifactTagsCountByTagName(tagName);
|
||||
}
|
||||
|
||||
} 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
|
||||
}
|
||||
@@ -348,7 +387,9 @@ public class Tags implements AutopsyVisitableItem {
|
||||
private void updateDisplayName() {
|
||||
long tagsCount = 0;
|
||||
try {
|
||||
tagsCount = Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsCountByTagName(tagName);
|
||||
tagsCount = UserPreferences.groupItemsInTreeByDatasource() ?
|
||||
Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsCountByTagName(tagName, datasourceObjId) :
|
||||
Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsCountByTagName(tagName);
|
||||
} 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
|
||||
}
|
||||
@@ -403,7 +444,11 @@ public class Tags implements AutopsyVisitableItem {
|
||||
protected boolean createKeys(List<ContentTag> keys) {
|
||||
// Use the content tags bearing the specified tag name as the keys.
|
||||
try {
|
||||
keys.addAll(Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByTagName(tagName));
|
||||
List<ContentTag> contentTags = UserPreferences.groupItemsInTreeByDatasource() ?
|
||||
Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByTagName(tagName, datasourceObjId) :
|
||||
Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByTagName(tagName);
|
||||
|
||||
keys.addAll(contentTags);
|
||||
} catch (TskCoreException | NoCurrentCaseException ex) {
|
||||
Logger.getLogger(ContentTagNodeFactory.class.getName()).log(Level.SEVERE, "Failed to get tag names", ex); //NON-NLS
|
||||
}
|
||||
@@ -447,7 +492,9 @@ public class Tags implements AutopsyVisitableItem {
|
||||
private void updateDisplayName() {
|
||||
long tagsCount = 0;
|
||||
try {
|
||||
tagsCount = Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagName(tagName);
|
||||
tagsCount = UserPreferences.groupItemsInTreeByDatasource() ?
|
||||
Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagName(tagName, datasourceObjId) :
|
||||
Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagName(tagName);
|
||||
} 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
|
||||
}
|
||||
@@ -502,7 +549,10 @@ public class Tags implements AutopsyVisitableItem {
|
||||
protected boolean createKeys(List<BlackboardArtifactTag> keys) {
|
||||
try {
|
||||
// Use the blackboard artifact tags bearing the specified tag name as the keys.
|
||||
keys.addAll(Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsByTagName(tagName));
|
||||
List<BlackboardArtifactTag> artifactTags = UserPreferences.groupItemsInTreeByDatasource() ?
|
||||
Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsByTagName(tagName, datasourceObjId) :
|
||||
Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsByTagName(tagName);
|
||||
keys.addAll(artifactTags);
|
||||
} catch (TskCoreException | NoCurrentCaseException ex) {
|
||||
Logger.getLogger(BlackboardArtifactTagNodeFactory.class.getName()).log(Level.SEVERE, "Failed to get tag names", ex); //NON-NLS
|
||||
}
|
||||
|
||||
@@ -26,11 +26,21 @@ import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
public class Views implements AutopsyVisitableItem {
|
||||
|
||||
private SleuthkitCase skCase;
|
||||
private final long datasourceObjId;
|
||||
|
||||
public Views(SleuthkitCase skCase) {
|
||||
this.skCase = skCase;
|
||||
this(skCase, 0);
|
||||
}
|
||||
|
||||
public Views(SleuthkitCase skCase, long dsObjId) {
|
||||
this.skCase = skCase;
|
||||
this.datasourceObjId = dsObjId;
|
||||
}
|
||||
|
||||
long filteringDataSourceObjId() {
|
||||
return this.datasourceObjId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T accept(AutopsyItemVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-2014 Basis Technology Corp.
|
||||
* Copyright 2011-2018 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -34,19 +34,28 @@ public class ViewsNode extends DisplayableItemNode {
|
||||
public static final String NAME = NbBundle.getMessage(ViewsNode.class, "ViewsNode.name.text");
|
||||
|
||||
public ViewsNode(SleuthkitCase sleuthkitCase) {
|
||||
super(new RootContentChildren(Arrays.asList(
|
||||
new FileTypes(sleuthkitCase),
|
||||
// June '15: Recent Files was removed because it was not useful w/out filtering
|
||||
// add it back in if we can filter the results to a more managable size.
|
||||
// new RecentFiles(sleuthkitCase),
|
||||
new DeletedContent(sleuthkitCase),
|
||||
new FileSize(sleuthkitCase))),
|
||||
Lookups.singleton(NAME));
|
||||
this(sleuthkitCase, 0);
|
||||
}
|
||||
|
||||
public ViewsNode(SleuthkitCase sleuthkitCase, long dsObjId) {
|
||||
|
||||
super(
|
||||
new RootContentChildren(Arrays.asList(
|
||||
new FileTypes(sleuthkitCase, dsObjId),
|
||||
// June '15: Recent Files was removed because it was not useful w/out filtering
|
||||
// add it back in if we can filter the results to a more managable size.
|
||||
// new RecentFiles(sleuthkitCase),
|
||||
new DeletedContent(sleuthkitCase, dsObjId),
|
||||
new FileSize(sleuthkitCase, dsObjId))
|
||||
),
|
||||
Lookups.singleton(NAME)
|
||||
);
|
||||
setName(NAME);
|
||||
setDisplayName(NAME);
|
||||
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/views.png"); //NON-NLS
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isLeafTypeNode() {
|
||||
return false;
|
||||
|
||||
@@ -58,6 +58,7 @@ import org.openide.util.Utilities;
|
||||
import org.openide.util.lookup.Lookups;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.core.UserPreferences;
|
||||
import org.sleuthkit.autopsy.corecomponents.DataResultTopComponent;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datamodel.AutopsyItemVisitor;
|
||||
@@ -94,6 +95,8 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
final public static String NAME = Bundle.AccountsRootNode_name();
|
||||
|
||||
private SleuthkitCase skCase;
|
||||
private final long datasourceObjId;
|
||||
|
||||
private final EventBus reviewStatusBus = new EventBus("ReviewStatusBus");
|
||||
|
||||
/* Should rejected accounts be shown in the accounts section of the tree. */
|
||||
@@ -108,12 +111,24 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
* @param skCase The SleuthkitCase object to use for db queries.
|
||||
*/
|
||||
public Accounts(SleuthkitCase skCase) {
|
||||
this(skCase, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param skCase The SleuthkitCase object to use for db queries.
|
||||
* @param objId Object id of the data source
|
||||
*/
|
||||
public Accounts(SleuthkitCase skCase, long objId) {
|
||||
this.skCase = skCase;
|
||||
this.datasourceObjId = objId;
|
||||
|
||||
this.rejectActionInstance = new RejectAccounts();
|
||||
this.approveActionInstance = new ApproveAccounts();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public <T> T accept(AutopsyItemVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
@@ -130,6 +145,18 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
return showRejected ? " " : " AND blackboard_artifacts.review_status_id != " + BlackboardArtifact.ReviewStatus.REJECTED.getID() + " "; //NON-NLS
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the clause to filter artifacts by data source.
|
||||
*
|
||||
* @return A clause that will or will not filter artifacts by datasource
|
||||
* based on the UserPreferences groupItemsInTreeByDatasource setting
|
||||
*/
|
||||
private String getFilterByDataSourceClause() {
|
||||
return (UserPreferences.groupItemsInTreeByDatasource()) ?
|
||||
" AND blackboard_artifacts.data_source_obj_id = " + datasourceObjId + " "
|
||||
: " ";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a new Action that when invoked toggles showing rejected artifacts on
|
||||
* or off.
|
||||
@@ -291,10 +318,14 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
|
||||
@Override
|
||||
protected boolean createKeys(List<String> list) {
|
||||
try (SleuthkitCase.CaseDbQuery executeQuery = skCase.executeQuery(
|
||||
"SELECT DISTINCT blackboard_attributes.value_text as account_type "
|
||||
+ " FROM blackboard_attributes "
|
||||
+ " WHERE blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID());
|
||||
String accountTypesInUseQuery =
|
||||
"SELECT DISTINCT blackboard_attributes.value_text as account_type "
|
||||
+ " FROM blackboard_artifacts " //NON-NLS
|
||||
+ " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
|
||||
+ " WHERE blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID()
|
||||
+ getFilterByDataSourceClause();
|
||||
|
||||
try (SleuthkitCase.CaseDbQuery executeQuery = skCase.executeQuery(accountTypesInUseQuery );
|
||||
ResultSet resultSet = executeQuery.getResultSet()) {
|
||||
while (resultSet.next()) {
|
||||
String accountType = resultSet.getString("account_type");
|
||||
@@ -429,6 +460,7 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
|
||||
+ " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS
|
||||
+ " AND blackboard_attributes.value_text = '" + accountType.getTypeName() + "'" //NON-NLS
|
||||
+ getFilterByDataSourceClause()
|
||||
+ getRejectedArtifactFilterClause(); //NON-NLS
|
||||
try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
|
||||
ResultSet rs = results.getResultSet();) {
|
||||
@@ -739,6 +771,7 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
+ " AND account_type.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS
|
||||
+ " AND account_type.value_text = '" + Account.Type.CREDIT_CARD.getTypeName() + "'" //NON-NLS
|
||||
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
|
||||
+ getFilterByDataSourceClause()
|
||||
+ getRejectedArtifactFilterClause()
|
||||
+ " GROUP BY blackboard_artifacts.obj_id, solr_document_id " //NON-NLS
|
||||
+ " ORDER BY hits DESC "; //NON-NLS
|
||||
@@ -807,6 +840,7 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
+ " AND account_type.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS
|
||||
+ " AND account_type.value_text = '" + Account.Type.CREDIT_CARD.getTypeName() + "'" //NON-NLS
|
||||
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
|
||||
+ getFilterByDataSourceClause()
|
||||
+ getRejectedArtifactFilterClause()
|
||||
+ " GROUP BY blackboard_artifacts.obj_id, solr_attribute.value_text ) AS foo";
|
||||
try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
|
||||
@@ -943,6 +977,7 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
+ " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id" //NON-NLS
|
||||
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
|
||||
+ " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS
|
||||
+ getFilterByDataSourceClause()
|
||||
+ getRejectedArtifactFilterClause()
|
||||
+ " GROUP BY BIN " //NON-NLS
|
||||
+ " ORDER BY BIN "; //NON-NLS
|
||||
@@ -1009,6 +1044,7 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
+ " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id" //NON-NLS
|
||||
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
|
||||
+ " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS
|
||||
+ getFilterByDataSourceClause()
|
||||
+ getRejectedArtifactFilterClause(); //NON-NLS
|
||||
try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
|
||||
ResultSet resultSet = results.getResultSet();) {
|
||||
@@ -1304,6 +1340,7 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
|
||||
+ " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS
|
||||
+ " AND blackboard_attributes.value_text >= '" + bin.getBINStart() + "' AND blackboard_attributes.value_text < '" + (bin.getBINEnd() + 1) + "'" //NON-NLS
|
||||
+ getFilterByDataSourceClause()
|
||||
+ getRejectedArtifactFilterClause()
|
||||
+ " ORDER BY blackboard_attributes.value_text"; //NON-NLS
|
||||
try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
|
||||
@@ -1375,6 +1412,7 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
|
||||
+ " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS
|
||||
+ " AND blackboard_attributes.value_text >= '" + bin.getBINStart() + "' AND blackboard_attributes.value_text < '" + (bin.getBINEnd() + 1) + "'" //NON-NLS
|
||||
+ getFilterByDataSourceClause()
|
||||
+ getRejectedArtifactFilterClause();
|
||||
try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query);
|
||||
ResultSet resultSet = results.getResultSet();) {
|
||||
|
||||
@@ -35,6 +35,7 @@ import org.openide.windows.WindowManager;
|
||||
/**
|
||||
* A dialog for adding or editing an external viewer rule
|
||||
*/
|
||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||
class AddExternalViewerRuleDialog extends JDialog {
|
||||
|
||||
private ExternalViewerRule rule;
|
||||
|
||||
@@ -119,3 +119,4 @@ AddExternalViewerRulePanel.browseButton.text=Browse
|
||||
AddExternalViewerRulePanel.exePathTextField.text=
|
||||
AddExternalViewerRulePanel.exePathLabel.text=Path of the program to use for files with this type or extension
|
||||
AddExternalViewerRulePanel.extRadioButton.text=Extension
|
||||
DirectoryTreeTopComponent.groupByDatasourceCheckBox.text=Group by Data Source
|
||||
|
||||
@@ -16,14 +16,17 @@
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="treeView" alignment="0" pref="262" max="32767" attributes="0"/>
|
||||
<Group type="102" attributes="0">
|
||||
<EmptySpace min="-2" pref="5" max="-2" attributes="0"/>
|
||||
<Component id="treeView" alignment="0" max="32767" attributes="0"/>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="backButton" min="-2" pref="23" max="-2" attributes="0"/>
|
||||
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
|
||||
<Component id="forwardButton" min="-2" pref="23" max="-2" attributes="0"/>
|
||||
<EmptySpace pref="46" max="32767" attributes="0"/>
|
||||
<Component id="showRejectedCheckBox" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace pref="65" max="32767" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="showRejectedCheckBox" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="groupByDatasourceCheckBox" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
@@ -31,14 +34,23 @@
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace min="-2" pref="5" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="1" attributes="0">
|
||||
<Component id="forwardButton" min="-2" pref="26" max="-2" attributes="1"/>
|
||||
<Component id="backButton" min="-2" pref="26" max="-2" attributes="1"/>
|
||||
<Component id="showRejectedCheckBox" min="-2" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<EmptySpace min="-2" pref="5" max="-2" attributes="0"/>
|
||||
<Component id="showRejectedCheckBox" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="groupByDatasourceCheckBox" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="1" attributes="0">
|
||||
<Component id="forwardButton" min="-2" pref="26" max="-2" attributes="1"/>
|
||||
<Component id="backButton" min="-2" pref="26" max="-2" attributes="1"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
<Component id="treeView" pref="854" max="32767" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="treeView" pref="838" max="32767" attributes="0"/>
|
||||
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
@@ -130,5 +142,15 @@
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JCheckBox" name="groupByDatasourceCheckBox">
|
||||
<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.groupByDatasourceCheckBox.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="groupByDatasourceCheckBoxActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
|
||||
@@ -30,6 +30,7 @@ import java.util.EnumSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.prefs.PreferenceChangeEvent;
|
||||
@@ -64,8 +65,6 @@ import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datamodel.ArtifactNodeSelectionInfo;
|
||||
import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode;
|
||||
import org.sleuthkit.autopsy.datamodel.CreditCards;
|
||||
import org.sleuthkit.autopsy.datamodel.DataSources;
|
||||
import org.sleuthkit.autopsy.datamodel.DataSourcesNode;
|
||||
import org.sleuthkit.autopsy.datamodel.DisplayableItemNode;
|
||||
import org.sleuthkit.autopsy.datamodel.EmailExtracted;
|
||||
import org.sleuthkit.autopsy.datamodel.EmptyNode;
|
||||
@@ -73,12 +72,8 @@ import org.sleuthkit.autopsy.datamodel.ExtractedContent;
|
||||
import org.sleuthkit.autopsy.datamodel.FileTypesByMimeType;
|
||||
import org.sleuthkit.autopsy.datamodel.InterestingHits;
|
||||
import org.sleuthkit.autopsy.datamodel.KeywordHits;
|
||||
import org.sleuthkit.autopsy.datamodel.Reports;
|
||||
import org.sleuthkit.autopsy.datamodel.Results;
|
||||
import org.sleuthkit.autopsy.datamodel.ResultsNode;
|
||||
import org.sleuthkit.autopsy.datamodel.RootContentChildren;
|
||||
import org.sleuthkit.autopsy.datamodel.Tags;
|
||||
import org.sleuthkit.autopsy.datamodel.Views;
|
||||
import org.sleuthkit.autopsy.datamodel.AutopsyTreeChildrenFactory;
|
||||
import org.sleuthkit.autopsy.datamodel.ViewsNode;
|
||||
import org.sleuthkit.autopsy.datamodel.accounts.Accounts;
|
||||
import org.sleuthkit.autopsy.datamodel.accounts.BINRange;
|
||||
@@ -107,7 +102,8 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
private final LinkedList<String[]> forwardList;
|
||||
private static final String PREFERRED_ID = "DirectoryTreeTopComponent"; //NON-NLS
|
||||
private static final Logger LOGGER = Logger.getLogger(DirectoryTreeTopComponent.class.getName());
|
||||
private RootContentChildren contentChildren;
|
||||
private AutopsyTreeChildrenFactory autopsyTreeChildrenFactory;
|
||||
private Children autopsyTreeChildren;
|
||||
|
||||
/**
|
||||
* the constructor
|
||||
@@ -130,6 +126,8 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
this.forwardList = new LinkedList<>();
|
||||
backButton.setEnabled(false);
|
||||
forwardButton.setEnabled(false);
|
||||
|
||||
groupByDatasourceCheckBox.setSelected(UserPreferences.groupItemsInTreeByDatasource());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -142,6 +140,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
switch (evt.getKey()) {
|
||||
case UserPreferences.HIDE_KNOWN_FILES_IN_DATA_SRCS_TREE:
|
||||
case UserPreferences.HIDE_SLACK_FILES_IN_DATA_SRCS_TREE:
|
||||
case UserPreferences.GROUP_ITEMS_IN_TREE_BY_DATASOURCE:
|
||||
refreshContentTreeSafe();
|
||||
break;
|
||||
case UserPreferences.HIDE_KNOWN_FILES_IN_VIEWS_TREE:
|
||||
@@ -182,6 +181,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
backButton = new javax.swing.JButton();
|
||||
forwardButton = new javax.swing.JButton();
|
||||
showRejectedCheckBox = new javax.swing.JCheckBox();
|
||||
groupByDatasourceCheckBox = new javax.swing.JCheckBox();
|
||||
|
||||
treeView.setBorder(null);
|
||||
|
||||
@@ -219,30 +219,45 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(showRejectedCheckBox, org.openide.util.NbBundle.getMessage(DirectoryTreeTopComponent.class, "DirectoryTreeTopComponent.showRejectedCheckBox.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(groupByDatasourceCheckBox, org.openide.util.NbBundle.getMessage(DirectoryTreeTopComponent.class, "DirectoryTreeTopComponent.groupByDatasourceCheckBox.text")); // NOI18N
|
||||
groupByDatasourceCheckBox.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
groupByDatasourceCheckBoxActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(treeView, javax.swing.GroupLayout.DEFAULT_SIZE, 262, Short.MAX_VALUE)
|
||||
.addComponent(treeView)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGap(5, 5, 5)
|
||||
.addContainerGap()
|
||||
.addComponent(backButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(0, 0, 0)
|
||||
.addComponent(forwardButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 46, Short.MAX_VALUE)
|
||||
.addComponent(showRejectedCheckBox)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 65, Short.MAX_VALUE)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(showRejectedCheckBox)
|
||||
.addComponent(groupByDatasourceCheckBox))
|
||||
.addContainerGap())
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGap(5, 5, 5)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
|
||||
.addComponent(forwardButton, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(backButton, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(showRejectedCheckBox))
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGap(5, 5, 5)
|
||||
.addComponent(showRejectedCheckBox)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(groupByDatasourceCheckBox))
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
|
||||
.addComponent(forwardButton, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(backButton, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE))))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(treeView, javax.swing.GroupLayout.DEFAULT_SIZE, 854, Short.MAX_VALUE)
|
||||
.addComponent(treeView, javax.swing.GroupLayout.DEFAULT_SIZE, 838, Short.MAX_VALUE)
|
||||
.addGap(0, 0, 0))
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
@@ -296,9 +311,14 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
this.setCursor(null);
|
||||
}//GEN-LAST:event_forwardButtonActionPerformed
|
||||
|
||||
private void groupByDatasourceCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_groupByDatasourceCheckBoxActionPerformed
|
||||
UserPreferences.setGroupItemsInTreeByDatasource(this.groupByDatasourceCheckBox.isSelected());
|
||||
}//GEN-LAST:event_groupByDatasourceCheckBoxActionPerformed
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JButton backButton;
|
||||
private javax.swing.JButton forwardButton;
|
||||
private javax.swing.JCheckBox groupByDatasourceCheckBox;
|
||||
private javax.swing.JCheckBox showRejectedCheckBox;
|
||||
private javax.swing.JScrollPane treeView;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
@@ -376,13 +396,10 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
} else {
|
||||
// if there's at least one image, load the image and open the top component
|
||||
final SleuthkitCase tskCase = currentCase.getSleuthkitCase();
|
||||
contentChildren = new RootContentChildren(Arrays.asList(
|
||||
new DataSources(),
|
||||
new Views(tskCase),
|
||||
new Results(tskCase),
|
||||
new Tags(),
|
||||
new Reports()));
|
||||
Node root = new AbstractNode(contentChildren) {
|
||||
|
||||
autopsyTreeChildrenFactory = new AutopsyTreeChildrenFactory(tskCase);
|
||||
autopsyTreeChildren = Children.create(autopsyTreeChildrenFactory, true);
|
||||
Node root = new AbstractNode(autopsyTreeChildren) {
|
||||
//JIRA-2807: What is the point of these overrides?
|
||||
/**
|
||||
* to override the right click action in the white blank space
|
||||
@@ -422,17 +439,23 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
TreeView tree = getTree();
|
||||
|
||||
Node results = rootChildren.findChild(ResultsNode.NAME);
|
||||
tree.expandNode(results);
|
||||
Children resultsChildren = results.getChildren();
|
||||
Arrays.stream(resultsChildren.getNodes()).forEach(tree::expandNode);
|
||||
if (!Objects.isNull(results)) {
|
||||
tree.expandNode(results);
|
||||
Children resultsChildren = results.getChildren();
|
||||
Arrays.stream(resultsChildren.getNodes()).forEach(tree::expandNode);
|
||||
|
||||
Accounts accounts = resultsChildren.findChild(Accounts.NAME).getLookup().lookup(Accounts.class);
|
||||
showRejectedCheckBox.setAction(accounts.newToggleShowRejectedAction());
|
||||
showRejectedCheckBox.setSelected(false);
|
||||
Accounts accounts = resultsChildren.findChild(Accounts.NAME).getLookup().lookup(Accounts.class);
|
||||
if (!Objects.isNull(accounts)) {
|
||||
showRejectedCheckBox.setAction(accounts.newToggleShowRejectedAction());
|
||||
showRejectedCheckBox.setSelected(false);
|
||||
}
|
||||
}
|
||||
|
||||
Node views = rootChildren.findChild(ViewsNode.NAME);
|
||||
Arrays.stream(views.getChildren().getNodes()).forEach(tree::expandNode);
|
||||
tree.collapseNode(views);
|
||||
if (!Objects.isNull(views)) {
|
||||
Arrays.stream(views.getChildren().getNodes()).forEach(tree::expandNode);
|
||||
tree.collapseNode(views);
|
||||
}
|
||||
/*
|
||||
* JIRA-2806: What is this supposed to do? Right now it selects
|
||||
* the data sources node, but the comment seems to indicate
|
||||
@@ -464,7 +487,10 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
// of changing the selected node fires a handler that tries to make
|
||||
// dataResult active)
|
||||
try {
|
||||
em.setSelectedNodes(get());
|
||||
Node[] selections = get();
|
||||
if (selections != null && selections.length > 0){
|
||||
em.setSelectedNodes(selections);
|
||||
}
|
||||
} catch (PropertyVetoException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Error setting default selected node.", ex); //NON-NLS
|
||||
} catch (InterruptedException | ExecutionException ex) {
|
||||
@@ -486,7 +512,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
@Override
|
||||
public void componentClosed() {
|
||||
//@@@ push the selection node to null?
|
||||
contentChildren = null;
|
||||
autopsyTreeChildren = null;
|
||||
}
|
||||
|
||||
void writeProperties(java.util.Properties p) {
|
||||
@@ -729,7 +755,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
*/
|
||||
String[] currentLast = backList.peekLast();
|
||||
String lastNodeName = null;
|
||||
if (currentLast != null) {
|
||||
if (currentLast != null && currentLast.length > 0) {
|
||||
lastNodeName = currentLast[currentLast.length - 1];
|
||||
}
|
||||
|
||||
@@ -773,27 +799,56 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
* Refresh the content node part of the dir tree safely in the EDT thread
|
||||
*/
|
||||
public void refreshContentTreeSafe() {
|
||||
SwingUtilities.invokeLater(this::refreshDataSourceTree);
|
||||
SwingUtilities.invokeLater(this::rebuildTree);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes changed content nodes
|
||||
* Rebuilds the directory tree
|
||||
*/
|
||||
private void refreshDataSourceTree() {
|
||||
Node selectedNode = getSelectedNode();
|
||||
final String[] selectedPath = NodeOp.createPath(selectedNode, em.getRootContext());
|
||||
Children rootChildren = em.getRootContext().getChildren();
|
||||
Node dataSourcesFilterNode = rootChildren.findChild(DataSourcesNode.NAME);
|
||||
if (dataSourcesFilterNode == null) {
|
||||
LOGGER.log(Level.SEVERE, "Cannot find data sources filter node, won't refresh the content tree"); //NON-NLS
|
||||
return;
|
||||
}
|
||||
Node dataSourcesNode = ((DirectoryTreeFilterNode) dataSourcesFilterNode).getOriginal();
|
||||
DataSourcesNode.DataSourcesNodeChildren contentRootChildren = (DataSourcesNode.DataSourcesNodeChildren) dataSourcesNode.getChildren();
|
||||
contentRootChildren.refreshContentKeys();
|
||||
setSelectedNode(selectedPath, DataSourcesNode.NAME);
|
||||
}
|
||||
private void rebuildTree() {
|
||||
|
||||
// refresh all children of the root.
|
||||
autopsyTreeChildrenFactory.refreshChildren();
|
||||
|
||||
// Select the first node and reset the selection history
|
||||
// This should happen on the EDT once the tree has been rebuilt.
|
||||
// hence the SwingWorker that does this in the done() method
|
||||
new SwingWorker<Void, Void>() {
|
||||
|
||||
@Override
|
||||
protected Void doInBackground() throws Exception {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done() {
|
||||
super.done();
|
||||
try {
|
||||
get();
|
||||
selectFirstChildNode();
|
||||
resetHistory();
|
||||
} catch (InterruptedException | ExecutionException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Error selecting tree node.", ex); //NON-NLS
|
||||
} //NON-NLS
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects the first node in the tree.
|
||||
*
|
||||
*/
|
||||
private void selectFirstChildNode () {
|
||||
Children rootChildren = em.getRootContext().getChildren();
|
||||
|
||||
if (rootChildren.getNodesCount() > 0) {
|
||||
Node firstNode = rootChildren.getNodeAt(0);
|
||||
if (firstNode != null) {
|
||||
final String[] selectedPath = NodeOp.createPath(firstNode, em.getRootContext());
|
||||
setSelectedNode(selectedPath, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Set the selected node using a path to a previously selected node.
|
||||
*
|
||||
|
||||
@@ -24,6 +24,7 @@ import java.beans.PropertyVetoException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.logging.Level;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import javax.swing.AbstractAction;
|
||||
@@ -33,6 +34,8 @@ import org.openide.explorer.view.TreeView;
|
||||
import org.openide.nodes.Children;
|
||||
import org.openide.nodes.Node;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.core.UserPreferences;
|
||||
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
||||
import org.sleuthkit.autopsy.datamodel.AbstractFsContentNode;
|
||||
@@ -44,9 +47,12 @@ import org.sleuthkit.autopsy.datamodel.RootContentChildren;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.ContentVisitor;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
import org.sleuthkit.datamodel.FileSystem;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
import org.sleuthkit.datamodel.TskDataException;
|
||||
import org.sleuthkit.datamodel.VolumeSystem;
|
||||
|
||||
/**
|
||||
@@ -122,7 +128,9 @@ public class ViewContextAction extends AbstractAction {
|
||||
@Override
|
||||
@Messages({
|
||||
"ViewContextAction.errorMessage.cannotFindDirectory=Failed to locate directory.",
|
||||
"ViewContextAction.errorMessage.cannotSelectDirectory=Failed to select directory in tree.",})
|
||||
"ViewContextAction.errorMessage.cannotSelectDirectory=Failed to select directory in tree.",
|
||||
"ViewContextAction.errorMessage.cannotFindNode=Failed to locate data source node in tree."
|
||||
})
|
||||
public void actionPerformed(ActionEvent event) {
|
||||
EventQueue.invokeLater(() -> {
|
||||
/*
|
||||
@@ -130,7 +138,40 @@ public class ViewContextAction extends AbstractAction {
|
||||
*/
|
||||
DirectoryTreeTopComponent treeViewTopComponent = DirectoryTreeTopComponent.findInstance();
|
||||
ExplorerManager treeViewExplorerMgr = treeViewTopComponent.getExplorerManager();
|
||||
Node parentTreeViewNode = treeViewExplorerMgr.getRootContext().getChildren().findChild(DataSourcesNode.NAME);
|
||||
|
||||
Node parentTreeViewNode;
|
||||
if (UserPreferences.groupItemsInTreeByDatasource()) { // 'Group by Data Source' view
|
||||
|
||||
SleuthkitCase skCase;
|
||||
String dsname;
|
||||
try {
|
||||
// get the objid/name of the datasource of the selected content.
|
||||
skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
|
||||
long contentDSObjid = content.getDataSource().getId();
|
||||
DataSource datasource = skCase.getDataSource(contentDSObjid);
|
||||
dsname = datasource.getName();
|
||||
|
||||
Children rootChildren = treeViewExplorerMgr.getRootContext().getChildren();
|
||||
Node datasourceGroupingNode = rootChildren.findChild(dsname);
|
||||
if (! Objects.isNull(datasourceGroupingNode) ) {
|
||||
Children dsChildren = datasourceGroupingNode.getChildren();
|
||||
parentTreeViewNode = dsChildren.findChild(DataSourcesNode.NAME);
|
||||
}
|
||||
else {
|
||||
MessageNotifyUtil.Message.error(Bundle.ViewContextAction_errorMessage_cannotFindNode());
|
||||
logger.log(Level.SEVERE, "Failed to locate data source node in tree."); //NON-NLS
|
||||
return;
|
||||
}
|
||||
} catch (NoCurrentCaseException| TskDataException | TskCoreException ex) {
|
||||
MessageNotifyUtil.Message.error(Bundle.ViewContextAction_errorMessage_cannotFindNode());
|
||||
logger.log(Level.SEVERE, "Failed to locate data source node in tree.", ex); //NON-NLS
|
||||
return;
|
||||
}
|
||||
} else { // Classic view
|
||||
// Start the search at the DataSourcesNode
|
||||
parentTreeViewNode = treeViewExplorerMgr.getRootContext().getChildren().findChild(DataSourcesNode.NAME);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the parent content for the content to be selected in the
|
||||
* results view. If the parent content is null, then the specified
|
||||
|
||||
@@ -16,24 +16,18 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* FileSearchDialog.java
|
||||
*
|
||||
* Created on Mar 5, 2012, 1:57:33 PM
|
||||
*/
|
||||
package org.sleuthkit.autopsy.filesearch;
|
||||
|
||||
import org.openide.util.NbBundle;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import javax.swing.JFrame;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.windows.WindowManager;
|
||||
|
||||
/**
|
||||
* File search dialog
|
||||
*/
|
||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||
class FileSearchDialog extends javax.swing.JDialog {
|
||||
|
||||
/**
|
||||
|
||||
@@ -35,6 +35,7 @@ import org.openide.windows.WindowManager;
|
||||
/**
|
||||
* A dialog that displays ingest task progress snapshots.
|
||||
*/
|
||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||
public final class IngestProgressSnapshotDialog extends JDialog {
|
||||
|
||||
private static final String TITLE = NbBundle.getMessage(IngestProgressSnapshotDialog.class, "IngestProgressSnapshotDialog.title.text");
|
||||
|
||||
@@ -13,7 +13,7 @@ EmbeddedFileExtractorIngestModule.ArchiveExtractor.encryptionFileLevel=Content-o
|
||||
EmbeddedFileExtractorIngestModule.ArchiveExtractor.encryptionFull=Full Encryption (Archive File)
|
||||
EmbeddedFileExtractorIngestModule.ArchiveExtractor.init.errInitModule.details=Error initializing output dir\: {0}\: {1}
|
||||
EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnMsg=Possible ZIP bomb detected in archive\: {0}, item\: {1}
|
||||
EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnDetails=Compression ratio is {0}, skipping item in {1}.
|
||||
EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnDetails=Compression ratio is {0}, skipping items in {1}.
|
||||
EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.warnMsg.zipBomb=Possible ZIP bomb detected\: {0}
|
||||
EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.warnDetails.zipBomb=The archive is {0} levels deep, skipping processing of {1}
|
||||
EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.unknownPath.msg=Unknown item path in archive\: {0}, will use\: {1}
|
||||
|
||||
+25
-8
@@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.modules.embeddedfileextractor;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
@@ -30,6 +31,8 @@ import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector;
|
||||
import net.sf.sevenzipjbinding.SevenZipNativeInitializationException;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.ingest.FileIngestModuleAdapter;
|
||||
import org.sleuthkit.autopsy.ingest.IngestModuleReferenceCounter;
|
||||
import org.sleuthkit.autopsy.modules.embeddedfileextractor.SevenZipExtractor.Archive;
|
||||
|
||||
/**
|
||||
* A file level ingest module that extracts embedded files from supported
|
||||
@@ -44,10 +47,13 @@ import org.sleuthkit.autopsy.ingest.FileIngestModuleAdapter;
|
||||
})
|
||||
public final class EmbeddedFileExtractorIngestModule extends FileIngestModuleAdapter {
|
||||
|
||||
static final String[] SUPPORTED_EXTENSIONS = {"zip", "rar", "arj", "7z", "7zip", "gzip", "gz", "bzip2", "tar", "tgz",}; // "iso"}; NON-NLS
|
||||
//Outer concurrent hashmap with keys of JobID, inner concurrentHashmap with keys of objectID
|
||||
private static final ConcurrentHashMap<Long, ConcurrentHashMap<Long, Archive>> mapOfDepthTrees = new ConcurrentHashMap<>();
|
||||
private static final IngestModuleReferenceCounter refCounter = new IngestModuleReferenceCounter();
|
||||
private MSOfficeEmbeddedContentExtractor officeExtractor;
|
||||
private SevenZipExtractor archiveExtractor;
|
||||
private FileTypeDetector fileTypeDetector;
|
||||
private long jobId;
|
||||
|
||||
/**
|
||||
* Constructs a file level ingest module that extracts embedded files from
|
||||
@@ -64,9 +70,10 @@ public final class EmbeddedFileExtractorIngestModule extends FileIngestModuleAda
|
||||
* case database for extracted (derived) file paths. The absolute path
|
||||
* is used to write the extracted (derived) files to local storage.
|
||||
*/
|
||||
jobId = context.getJobId();
|
||||
String moduleDirRelative = null;
|
||||
String moduleDirAbsolute = null;
|
||||
|
||||
|
||||
try {
|
||||
final Case currentCase = Case.getCurrentCaseThrows();
|
||||
moduleDirRelative = Paths.get(currentCase.getModuleOutputDirectoryRelativePath(), EmbeddedFileExtractorModuleFactory.getModuleName()).toString();
|
||||
@@ -94,16 +101,18 @@ public final class EmbeddedFileExtractorIngestModule extends FileIngestModuleAda
|
||||
} catch (FileTypeDetector.FileTypeDetectorInitException ex) {
|
||||
throw new IngestModuleException(Bundle.CannotRunFileTypeDetection(), ex);
|
||||
}
|
||||
|
||||
/*
|
||||
* Construct a 7Zip file extractor for processing archive files.
|
||||
*/
|
||||
try {
|
||||
this.archiveExtractor = new SevenZipExtractor(context, fileTypeDetector, moduleDirRelative, moduleDirAbsolute);
|
||||
} catch (SevenZipNativeInitializationException ex) {
|
||||
throw new IngestModuleException(Bundle.UnableToInitializeLibraries(), ex);
|
||||
}
|
||||
|
||||
if (refCounter.incrementAndGet(jobId) == 1) {
|
||||
/*
|
||||
* Construct a concurrentHashmap to keep track of depth in archives
|
||||
* while processing archive files.
|
||||
*/
|
||||
mapOfDepthTrees.put(jobId, new ConcurrentHashMap<>());
|
||||
}
|
||||
/*
|
||||
* Construct an embedded content extractor for processing Microsoft
|
||||
* Office documents.
|
||||
@@ -113,6 +122,7 @@ public final class EmbeddedFileExtractorIngestModule extends FileIngestModuleAda
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
throw new IngestModuleException(Bundle.EmbeddedFileExtractorIngestModule_UnableToGetMSOfficeExtractor_errMsg(), ex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -144,13 +154,20 @@ public final class EmbeddedFileExtractorIngestModule extends FileIngestModuleAda
|
||||
* type/format.
|
||||
*/
|
||||
if (archiveExtractor.isSevenZipExtractionSupported(abstractFile)) {
|
||||
archiveExtractor.unpack(abstractFile);
|
||||
archiveExtractor.unpack(abstractFile, mapOfDepthTrees.get(jobId));
|
||||
} else if (officeExtractor.isContentExtractionSupported(abstractFile)) {
|
||||
officeExtractor.extractEmbeddedContent(abstractFile);
|
||||
}
|
||||
return ProcessResult.OK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutDown() {
|
||||
if (refCounter.decrementAndGet(jobId) == 0) {
|
||||
mapOfDepthTrees.remove(jobId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a unique name for a file by concatentating the file name and the
|
||||
* file object id.
|
||||
|
||||
+2
-1
@@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.modules.embeddedfileextractor;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.AbstractAction;
|
||||
@@ -125,7 +126,7 @@ public class ExtractArchiveWithPasswordAction extends AbstractAction {
|
||||
}
|
||||
try {
|
||||
SevenZipExtractor extractor = new SevenZipExtractor(null, fileTypeDetector, moduleDirRelative, moduleDirAbsolute);
|
||||
done = extractor.unpack(archive, password);
|
||||
done = extractor.unpack(archive, new ConcurrentHashMap<>(), password);
|
||||
} catch (SevenZipNativeInitializationException ex) {
|
||||
IngestServices.getInstance().postMessage(IngestMessage.createWarningMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), "Unable to extract file with password", password));
|
||||
logger.log(Level.INFO, "Unable to extract file with password", ex);
|
||||
|
||||
+226
-118
@@ -29,6 +29,7 @@ import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.logging.Level;
|
||||
import net.sf.sevenzipjbinding.ArchiveFormat;
|
||||
import static net.sf.sevenzipjbinding.ArchiveFormat.RAR;
|
||||
@@ -73,7 +74,6 @@ class SevenZipExtractor {
|
||||
private IngestServices services = IngestServices.getInstance();
|
||||
private final IngestJobContext context;
|
||||
private final FileTypeDetector fileTypeDetector;
|
||||
static final String[] SUPPORTED_EXTENSIONS = {"zip", "rar", "arj", "7z", "7zip", "gzip", "gz", "bzip2", "tar", "tgz",}; // NON-NLS
|
||||
//encryption type strings
|
||||
private static final String ENCRYPTION_FILE_LEVEL = NbBundle.getMessage(EmbeddedFileExtractorIngestModule.class,
|
||||
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.encryptionFileLevel");
|
||||
@@ -84,8 +84,6 @@ class SevenZipExtractor {
|
||||
private static final int MAX_COMPRESSION_RATIO = 600;
|
||||
private static final long MIN_COMPRESSION_RATIO_SIZE = 500 * 1000000L;
|
||||
private static final long MIN_FREE_DISK_SPACE = 1 * 1000 * 1000000L; //1GB
|
||||
//counts archive depth
|
||||
private ArchiveDepthCountTree archiveDepthCountTree;
|
||||
|
||||
private String moduleDirRelative;
|
||||
private String moduleDirAbsolute;
|
||||
@@ -131,7 +129,6 @@ class SevenZipExtractor {
|
||||
this.fileTypeDetector = fileTypeDetector;
|
||||
this.moduleDirRelative = moduleDirRelative;
|
||||
this.moduleDirAbsolute = moduleDirAbsolute;
|
||||
this.archiveDepthCountTree = new ArchiveDepthCountTree();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -150,8 +147,8 @@ class SevenZipExtractor {
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the item inside archive is a potential zipbomb
|
||||
*
|
||||
@@ -159,12 +156,18 @@ class SevenZipExtractor {
|
||||
*
|
||||
* More heuristics to be added here
|
||||
*
|
||||
* @param archiveName the parent archive
|
||||
* @param archiveFileItem the archive item
|
||||
* @param archiveFile the AbstractFile for the parent archive which
|
||||
* which we are checking
|
||||
* @param archiveFileItem the current item being extracted from the parent
|
||||
* archive
|
||||
* @param depthMap a concurrent hashmap which keeps track of the
|
||||
* depth of all nested archives, key of objectID
|
||||
* @param escapedFilePath the path to the archiveFileItem which has been
|
||||
* escaped
|
||||
*
|
||||
* @return true if potential zip bomb, false otherwise
|
||||
*/
|
||||
private boolean isZipBombArchiveItemCheck(AbstractFile archiveFile, ISimpleInArchiveItem archiveFileItem) {
|
||||
private boolean isZipBombArchiveItemCheck(AbstractFile archiveFile, ISimpleInArchiveItem archiveFileItem, ConcurrentHashMap<Long, Archive> depthMap, String escapedFilePath) {
|
||||
try {
|
||||
final Long archiveItemSize = archiveFileItem.getSize();
|
||||
|
||||
@@ -183,20 +186,12 @@ class SevenZipExtractor {
|
||||
int cRatio = (int) (archiveItemSize / archiveItemPackedSize);
|
||||
|
||||
if (cRatio >= MAX_COMPRESSION_RATIO) {
|
||||
String itemName = archiveFileItem.getPath();
|
||||
logger.log(Level.INFO, "Possible zip bomb detected, compression ration: {0} for in archive item: {1}", new Object[]{cRatio, itemName}); //NON-NLS
|
||||
String msg = NbBundle.getMessage(SevenZipExtractor.class,
|
||||
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnMsg", archiveFile.getName(), itemName);
|
||||
String path;
|
||||
try {
|
||||
path = archiveFile.getUniquePath();
|
||||
} catch (TskCoreException ex) {
|
||||
path = archiveFile.getParentPath() + archiveFile.getName();
|
||||
}
|
||||
Archive rootArchive = depthMap.get(depthMap.get(archiveFile.getId()).getRootArchiveId());
|
||||
String details = NbBundle.getMessage(SevenZipExtractor.class,
|
||||
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnDetails", cRatio, path);
|
||||
//MessageNotifyUtil.Notify.error(msg, details);
|
||||
services.postMessage(IngestMessage.createWarningMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details));
|
||||
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnDetails",
|
||||
cRatio, FileUtil.escapeFileName(getArchiveFilePath(rootArchive.getArchiveFile())));
|
||||
|
||||
flagRootArchiveAsZipBomb(rootArchive, archiveFile, details, escapedFilePath);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@@ -208,6 +203,47 @@ class SevenZipExtractor {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag the root archive archive as a zipbomb by creating an interesting
|
||||
* file artifact and posting a message to the inbox for the user.
|
||||
*
|
||||
* @param rootArchive - the Archive which the artifact is to be for
|
||||
* @param archiveFile - the AbstractFile which for the file which
|
||||
* triggered the potential zip bomb to be detected
|
||||
* @param details - the String which contains the details about how
|
||||
* the potential zip bomb was detected
|
||||
* @param escapedFilePath - the escaped file path for the archiveFile
|
||||
*/
|
||||
private void flagRootArchiveAsZipBomb(Archive rootArchive, AbstractFile archiveFile, String details, String escapedFilePath) {
|
||||
rootArchive.flagAsZipBomb();
|
||||
logger.log(Level.INFO, details); //NON-NLS
|
||||
String msg = NbBundle.getMessage(SevenZipExtractor.class,
|
||||
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnMsg", archiveFile.getName(), escapedFilePath);
|
||||
try {
|
||||
BlackboardArtifact artifact = rootArchive.getArchiveFile().newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT);
|
||||
artifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME, EmbeddedFileExtractorModuleFactory.getModuleName(),
|
||||
"Possible Zip Bomb"));
|
||||
artifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION,
|
||||
EmbeddedFileExtractorModuleFactory.getModuleName(),
|
||||
Bundle.SevenZipExtractor_zipBombArtifactCreation_text(archiveFile.getName())));
|
||||
artifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT,
|
||||
EmbeddedFileExtractorModuleFactory.getModuleName(),
|
||||
details));
|
||||
try {
|
||||
// index the artifact for keyword search
|
||||
blackboard.indexArtifact(artifact);
|
||||
} catch (Blackboard.BlackboardException ex) {
|
||||
logger.log(Level.SEVERE, "Unable to index blackboard artifact " + artifact.getArtifactID(), ex); //NON-NLS
|
||||
MessageNotifyUtil.Notify.error(
|
||||
Bundle.SevenZipExtractor_indexError_message(), artifact.getDisplayName());
|
||||
}
|
||||
services.fireModuleDataEvent(new ModuleDataEvent(EmbeddedFileExtractorModuleFactory.getModuleName(), BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT));
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Error creating blackboard artifact for Zip Bomb Detection for file: " + escapedFilePath, ex); //NON-NLS
|
||||
}
|
||||
services.postMessage(IngestMessage.createWarningMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check file extension and return appropriate input options for
|
||||
* SevenZip.openInArchive()
|
||||
@@ -459,11 +495,13 @@ class SevenZipExtractor {
|
||||
* Unpack the file to local folder and return a list of derived files
|
||||
*
|
||||
* @param archiveFile file to unpack
|
||||
* @param depthMap - a concurrent hashmap which keeps track of the depth
|
||||
* of all nested archives, key of objectID
|
||||
*
|
||||
* @return list of unpacked derived files
|
||||
* @return true if unpacking is complete
|
||||
*/
|
||||
void unpack(AbstractFile archiveFile) {
|
||||
unpack(archiveFile, null);
|
||||
void unpack(AbstractFile archiveFile, ConcurrentHashMap<Long, Archive> depthMap) {
|
||||
unpack(archiveFile, depthMap, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -471,12 +509,16 @@ class SevenZipExtractor {
|
||||
* the password if specified.
|
||||
*
|
||||
* @param archiveFile - file to unpack
|
||||
* @param depthMap - a concurrent hashmap which keeps track of the depth
|
||||
* of all nested archives, key of objectID
|
||||
* @param password - the password to use, null for no password
|
||||
*
|
||||
* @return list of unpacked derived files
|
||||
* @return true if unpacking is complete
|
||||
*/
|
||||
@Messages({"SevenZipExtractor.indexError.message=Failed to index encryption detected artifact for keyword search."})
|
||||
boolean unpack(AbstractFile archiveFile, String password) {
|
||||
@Messages({"SevenZipExtractor.indexError.message=Failed to index encryption detected artifact for keyword search.",
|
||||
"# {0} - rootArchive",
|
||||
"SevenZipExtractor.zipBombArtifactCreation.text=Zip Bomb Detected {0}"})
|
||||
boolean unpack(AbstractFile archiveFile, ConcurrentHashMap<Long, Archive> depthMap, String password) {
|
||||
boolean unpackSuccessful = true; //initialized to true change to false if any files fail to extract and
|
||||
boolean hasEncrypted = false;
|
||||
boolean fullEncryption = true;
|
||||
@@ -491,8 +533,7 @@ class SevenZipExtractor {
|
||||
SevenZipContentReadStream stream = null;
|
||||
final ProgressHandle progress = ProgressHandle.createHandle(Bundle.EmbeddedFileExtractorIngestModule_ArchiveExtractor_moduleName());
|
||||
//recursion depth check for zip bomb
|
||||
final long archiveId = archiveFile.getId();
|
||||
SevenZipExtractor.ArchiveDepthCountTree.Archive parentAr;
|
||||
Archive parentAr;
|
||||
try {
|
||||
blackboard = Case.getCurrentCaseThrows().getServices().getBlackboard();
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
@@ -515,20 +556,24 @@ class SevenZipExtractor {
|
||||
unpackSuccessful = false;
|
||||
return unpackSuccessful;
|
||||
}
|
||||
parentAr = archiveDepthCountTree.findArchive(archiveId);
|
||||
parentAr = depthMap.get(archiveFile.getId());
|
||||
if (parentAr == null) {
|
||||
parentAr = archiveDepthCountTree.addArchive(null, archiveId);
|
||||
|
||||
} else if (parentAr.getDepth() == MAX_DEPTH) {
|
||||
String msg = NbBundle.getMessage(SevenZipExtractor.class,
|
||||
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.warnMsg.zipBomb", archiveFile.getName());
|
||||
String details = NbBundle.getMessage(SevenZipExtractor.class,
|
||||
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.warnDetails.zipBomb",
|
||||
parentAr.getDepth(), escapedArchiveFilePath);
|
||||
//MessageNotifyUtil.Notify.error(msg, details);
|
||||
services.postMessage(IngestMessage.createWarningMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details));
|
||||
unpackSuccessful = false;
|
||||
return unpackSuccessful;
|
||||
parentAr = new Archive(0, archiveFile.getId(), archiveFile);
|
||||
depthMap.put(archiveFile.getId(), parentAr);
|
||||
} else {
|
||||
Archive rootArchive = depthMap.get(parentAr.getRootArchiveId());
|
||||
if (rootArchive.isFlaggedAsZipBomb()) {
|
||||
//skip this archive as the root archive has already been determined to contain a zip bomb
|
||||
unpackSuccessful = false;
|
||||
return unpackSuccessful;
|
||||
} else if (parentAr.getDepth() == MAX_DEPTH) {
|
||||
String details = NbBundle.getMessage(SevenZipExtractor.class,
|
||||
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.warnDetails.zipBomb",
|
||||
parentAr.getDepth(), FileUtil.escapeFileName(getArchiveFilePath(rootArchive.getArchiveFile())));
|
||||
flagRootArchiveAsZipBomb(rootArchive, archiveFile, details, escapedArchiveFilePath);
|
||||
unpackSuccessful = false;
|
||||
return unpackSuccessful;
|
||||
}
|
||||
}
|
||||
try {
|
||||
stream = new SevenZipContentReadStream(new ReadContentInputStream(archiveFile));
|
||||
@@ -579,8 +624,9 @@ class SevenZipExtractor {
|
||||
++itemNumber;
|
||||
|
||||
//check if possible zip bomb
|
||||
if (isZipBombArchiveItemCheck(archiveFile, item)) {
|
||||
continue; //skip the item
|
||||
if (isZipBombArchiveItemCheck(archiveFile, item, depthMap, escapedArchiveFilePath)) {
|
||||
unpackSuccessful = false;
|
||||
return unpackSuccessful;
|
||||
}
|
||||
SevenZipExtractor.UnpackedTree.UnpackedNode unpackedNode = unpackedTree.addNode(pathInArchive);
|
||||
//update progress bar
|
||||
@@ -608,7 +654,6 @@ class SevenZipExtractor {
|
||||
escapedArchiveFilePath, item.getPath());
|
||||
String details = NbBundle.getMessage(SevenZipExtractor.class,
|
||||
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.notEnoughDiskSpace.details");
|
||||
//MessageNotifyUtil.Notify.error(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, "Available disk space: {0}", new Object[]{freeDiskSpace}); //NON-NLS
|
||||
@@ -666,7 +711,9 @@ class SevenZipExtractor {
|
||||
continue;
|
||||
}
|
||||
if (isSevenZipExtractionSupported(unpackedFile)) {
|
||||
archiveDepthCountTree.addArchive(parentAr, unpackedFile.getId());
|
||||
Archive child = new Archive(parentAr.getDepth() + 1, parentAr.getRootArchiveId(), archiveFile);
|
||||
parentAr.addChild(child);
|
||||
depthMap.put(unpackedFile.getId(), child);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -908,7 +955,7 @@ class SevenZipExtractor {
|
||||
this.rootNode = new UnpackedNode();
|
||||
this.rootNode.setFile(archiveFile);
|
||||
this.rootNode.setFileName(archiveFile.getName());
|
||||
this.rootNode.localRelPath = localPathRoot;
|
||||
this.rootNode.setLocalRelPath(localPathRoot);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -951,6 +998,7 @@ class SevenZipExtractor {
|
||||
// create new node
|
||||
if (child == null) {
|
||||
child = new UnpackedNode(childName, parent);
|
||||
parent.addChild(child);
|
||||
}
|
||||
|
||||
// go down one more level
|
||||
@@ -965,7 +1013,7 @@ class SevenZipExtractor {
|
||||
*/
|
||||
List<AbstractFile> getRootFileObjects() {
|
||||
List<AbstractFile> ret = new ArrayList<>();
|
||||
for (UnpackedNode child : rootNode.children) {
|
||||
for (UnpackedNode child : rootNode.getChildren()) {
|
||||
ret.add(child.getFile());
|
||||
}
|
||||
return ret;
|
||||
@@ -979,7 +1027,7 @@ class SevenZipExtractor {
|
||||
*/
|
||||
List<AbstractFile> getAllFileObjects() {
|
||||
List<AbstractFile> ret = new ArrayList<>();
|
||||
for (UnpackedNode child : rootNode.children) {
|
||||
for (UnpackedNode child : rootNode.getChildren()) {
|
||||
getAllFileObjectsRec(ret, child);
|
||||
}
|
||||
return ret;
|
||||
@@ -987,7 +1035,7 @@ class SevenZipExtractor {
|
||||
|
||||
private void getAllFileObjectsRec(List<AbstractFile> list, UnpackedNode parent) {
|
||||
list.add(parent.getFile());
|
||||
for (UnpackedNode child : parent.children) {
|
||||
for (UnpackedNode child : parent.getChildren()) {
|
||||
getAllFileObjectsRec(list, child);
|
||||
}
|
||||
}
|
||||
@@ -998,7 +1046,7 @@ class SevenZipExtractor {
|
||||
*/
|
||||
void updateOrAddFileToCaseRec(HashMap<String, ZipFileStatusWrapper> statusMap, String archiveFilePath) throws TskCoreException, NoCurrentCaseException {
|
||||
final FileManager fileManager = Case.getCurrentCaseThrows().getServices().getFileManager();
|
||||
for (UnpackedNode child : rootNode.children) {
|
||||
for (UnpackedNode child : rootNode.getChildren()) {
|
||||
updateOrAddFileToCaseRec(child, fileManager, statusMap, archiveFilePath);
|
||||
}
|
||||
}
|
||||
@@ -1055,7 +1103,7 @@ class SevenZipExtractor {
|
||||
node.getFileName()), ex);
|
||||
}
|
||||
//recurse adding the children if this file was incomplete the children presumably need to be added
|
||||
for (UnpackedNode child : node.children) {
|
||||
for (UnpackedNode child : node.getChildren()) {
|
||||
updateOrAddFileToCaseRec(child, fileManager, statusMap, getKeyFromUnpackedNode(node, archiveFilePath));
|
||||
}
|
||||
}
|
||||
@@ -1067,7 +1115,7 @@ class SevenZipExtractor {
|
||||
|
||||
private String fileName;
|
||||
private AbstractFile file;
|
||||
private List<UnpackedNode> children = new ArrayList<>();
|
||||
private final List<UnpackedNode> children = new ArrayList<>();
|
||||
private String localRelPath = "";
|
||||
private long size;
|
||||
private long ctime, crtime, atime, mtime;
|
||||
@@ -1082,31 +1130,53 @@ class SevenZipExtractor {
|
||||
UnpackedNode(String fileName, UnpackedNode parent) {
|
||||
this.fileName = fileName;
|
||||
this.parent = parent;
|
||||
this.localRelPath = parent.localRelPath + File.separator + fileName;
|
||||
//new child derived file will be set by unpack() method
|
||||
parent.children.add(this);
|
||||
this.localRelPath = parent.getLocalRelPath() + File.separator + fileName;
|
||||
}
|
||||
|
||||
public long getCtime() {
|
||||
long getCtime() {
|
||||
return ctime;
|
||||
}
|
||||
|
||||
public long getCrtime() {
|
||||
long getCrtime() {
|
||||
return crtime;
|
||||
}
|
||||
|
||||
public long getAtime() {
|
||||
long getAtime() {
|
||||
return atime;
|
||||
}
|
||||
|
||||
public long getMtime() {
|
||||
long getMtime() {
|
||||
return mtime;
|
||||
}
|
||||
|
||||
public void setFileName(String fileName) {
|
||||
void setFileName(String fileName) {
|
||||
this.fileName = fileName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a child to the list of child nodes associated with this node.
|
||||
*
|
||||
* @param child - the node which is a child node of this node
|
||||
*/
|
||||
void addChild(UnpackedNode child) {
|
||||
children.add(child);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get this nodes list of child UnpackedNode
|
||||
*
|
||||
* @return children - the UnpackedNodes which are children of this
|
||||
* node.
|
||||
*/
|
||||
List<UnpackedNode> getChildren() {
|
||||
return children;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the parent node of this node.
|
||||
*
|
||||
* @return - the parent UnpackedNode
|
||||
*/
|
||||
UnpackedNode getParent() {
|
||||
return parent;
|
||||
}
|
||||
@@ -1137,7 +1207,7 @@ class SevenZipExtractor {
|
||||
UnpackedNode getChild(String childFileName) {
|
||||
UnpackedNode ret = null;
|
||||
for (UnpackedNode child : children) {
|
||||
if (child.fileName.equals(childFileName)) {
|
||||
if (child.getFileName().equals(childFileName)) {
|
||||
ret = child;
|
||||
break;
|
||||
}
|
||||
@@ -1145,94 +1215,132 @@ class SevenZipExtractor {
|
||||
return ret;
|
||||
}
|
||||
|
||||
public String getFileName() {
|
||||
String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public AbstractFile getFile() {
|
||||
AbstractFile getFile() {
|
||||
return file;
|
||||
}
|
||||
|
||||
public String getLocalRelPath() {
|
||||
String getLocalRelPath() {
|
||||
return localRelPath;
|
||||
}
|
||||
|
||||
public long getSize() {
|
||||
/**
|
||||
* Set the local relative path associated with this UnpackedNode
|
||||
*
|
||||
* @param localRelativePath - the local relative path to be
|
||||
* associated with this node.
|
||||
*/
|
||||
void setLocalRelPath(String localRelativePath) {
|
||||
localRelPath = localRelativePath;
|
||||
}
|
||||
|
||||
long getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public boolean isIsFile() {
|
||||
boolean isIsFile() {
|
||||
return isFile;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tracks archive hierarchy and archive depth
|
||||
* Class to keep track of an objects id and its depth in the archive
|
||||
* structure.
|
||||
*/
|
||||
private static class ArchiveDepthCountTree {
|
||||
static class Archive {
|
||||
|
||||
//keeps all nodes refs for easy search
|
||||
private final List<Archive> archives = new ArrayList<>();
|
||||
//depth will be 0 for the root archive unpack was called on, and increase as unpack recurses down through archives contained within
|
||||
private final int depth;
|
||||
private final List<Archive> children;
|
||||
private final long rootArchiveId;
|
||||
private boolean flaggedAsZipBomb = false;
|
||||
private final AbstractFile archiveFile;
|
||||
|
||||
/**
|
||||
* Search for previously added parent archive by id
|
||||
* Create a new Archive object.
|
||||
*
|
||||
* @param objectId parent archive object id
|
||||
*
|
||||
* @return the archive node or null if not found
|
||||
* @param depth the depth in the archive structure - 0 will be
|
||||
* the root archive unpack was called on, and it
|
||||
* will increase as unpack recurses down through
|
||||
* archives contained within
|
||||
* @param rootArchiveId the unique object id of the root parent archive
|
||||
* of this archive
|
||||
* @param archiveFile the AbstractFile which this Archive object
|
||||
* represents
|
||||
*/
|
||||
Archive findArchive(long objectId) {
|
||||
for (Archive ar : archives) {
|
||||
if (ar.objectId == objectId) {
|
||||
return ar;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
Archive(int depth, long rootArchiveId, AbstractFile archiveFile) {
|
||||
this.children = new ArrayList<>();
|
||||
this.depth = depth;
|
||||
this.rootArchiveId = rootArchiveId;
|
||||
this.archiveFile = archiveFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new archive to track of depth
|
||||
* Add a child to the list of child archives associated with this
|
||||
* archive.
|
||||
*
|
||||
* @param parent parent archive or null
|
||||
* @param objectId object id of the new archive
|
||||
*
|
||||
* @return the archive added
|
||||
* @param child - the archive which is a child archive of this archive
|
||||
*/
|
||||
Archive addArchive(Archive parent, long objectId) {
|
||||
Archive child = new Archive(parent, objectId);
|
||||
archives.add(child);
|
||||
return child;
|
||||
void addChild(Archive child) {
|
||||
children.add(child);
|
||||
}
|
||||
|
||||
private static class Archive {
|
||||
/**
|
||||
* Set the flag which identifies whether this file has been determined to be a zip bomb to true.
|
||||
*/
|
||||
synchronized void flagAsZipBomb() {
|
||||
flaggedAsZipBomb = true;
|
||||
}
|
||||
|
||||
int depth;
|
||||
long objectId;
|
||||
Archive parent;
|
||||
List<Archive> children;
|
||||
/**
|
||||
* Gets whether or not this archive has been flagged as a zip bomb.
|
||||
*
|
||||
* @return True when flagged as a zip bomb, false if it is not flagged
|
||||
*/
|
||||
synchronized boolean isFlaggedAsZipBomb() {
|
||||
return flaggedAsZipBomb;
|
||||
}
|
||||
|
||||
Archive(Archive parent, long objectId) {
|
||||
this.parent = parent;
|
||||
this.objectId = objectId;
|
||||
children = new ArrayList<>();
|
||||
if (parent != null) {
|
||||
parent.children.add(this);
|
||||
this.depth = parent.depth + 1;
|
||||
} else {
|
||||
this.depth = 0;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Get the AbstractFile which this Archive object represents.
|
||||
*
|
||||
* @return archiveFile - the AbstractFile which this Archive represents.
|
||||
*/
|
||||
AbstractFile getArchiveFile() {
|
||||
return archiveFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* get archive depth of this archive
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
int getDepth() {
|
||||
return depth;
|
||||
}
|
||||
/**
|
||||
* Get the object id of the root archive which contained this archive.
|
||||
*
|
||||
* @return rootArchiveId - the objectID of the root archive
|
||||
*/
|
||||
long getRootArchiveId() {
|
||||
return rootArchiveId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the object id of this archive.
|
||||
*
|
||||
* @return the unique objectId of this archive from its AbstractFile
|
||||
*/
|
||||
long getObjectId() {
|
||||
return archiveFile.getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get archive depth of this archive
|
||||
*
|
||||
* @return depth - an integer representing that represents how many
|
||||
* times the upack method has been recursed from the root
|
||||
* archive unpack was called on
|
||||
*/
|
||||
int getDepth() {
|
||||
return depth;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1260,7 +1368,7 @@ class SevenZipExtractor {
|
||||
/**
|
||||
* Get the AbstractFile contained in this object
|
||||
*
|
||||
* @return abstractFile - The abstractFile this object wraps
|
||||
* @return archiveFile - The archiveFile this object wraps
|
||||
*/
|
||||
private AbstractFile getFile() {
|
||||
return abstractFile;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-2017 Basis Technology Corp.
|
||||
* Copyright 2011-2018 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -20,7 +20,6 @@ package org.sleuthkit.autopsy.modules.filetypeid;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.WindowAdapter;
|
||||
@@ -40,6 +39,7 @@ import org.sleuthkit.autopsy.modules.filetypeid.FileType.Signature;
|
||||
* A dialog box that allows a user to create a file type signature, to be added
|
||||
* to a selected file type.
|
||||
*/
|
||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||
final class AddFileTypeSignatureDialog extends JDialog {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@@ -30,6 +30,7 @@ import org.sleuthkit.autopsy.modules.filetypeid.FileType.Signature;
|
||||
/**
|
||||
* Panel for creating a file type signature to be added to a file type.
|
||||
*/
|
||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||
class AddFileTypeSignaturePanel extends javax.swing.JPanel {
|
||||
|
||||
private static final String RAW_SIGNATURE_TYPE_COMBO_BOX_ITEM = NbBundle.getMessage(FileTypeIdGlobalSettingsPanel.class, "FileTypeIdGlobalSettingsPanel.signatureComboBox.rawItem");
|
||||
|
||||
@@ -21,7 +21,7 @@ FilesSetRulePanel.extensionRadioButton.text=Extension Only
|
||||
FilesSetRulePanel.pathRegexCheckBox.text=Regex
|
||||
FilesSetRulePanel.pathTextField.text=
|
||||
FilesSetRulePanel.fullNameRadioButton.text=Full Name
|
||||
FilesSetRulePanel.nameRegexCheckbox.text=Regex
|
||||
FilesSetRulePanel.nameRegexCheckbox.text=Substring / Regex
|
||||
FilesSetRulePanel.ruleNameTextField.text=
|
||||
FilesSetRulePanel.nameTextField.text=
|
||||
FilesSetRulePanel.ruleNameLabel.text=Rule Name (Optional):
|
||||
@@ -36,8 +36,8 @@ FilesIdentifierIngestJobSettingsPanel.border.title=Select interesting files sets
|
||||
FilesSetRulePanel.jLabel1.text=Type:
|
||||
FilesSetRulePanel.interesting.jLabel5.text=Enter information about files that you want to find.
|
||||
FilesSetRulePanel.ingest.jLabel5.text=Enter information about files that you want to run ingest on.
|
||||
FilesSetRulePanel.nameCheck.text=Name Pattern:
|
||||
FilesSetRulePanel.pathCheck.text=Path Pattern:
|
||||
FilesSetRulePanel.nameCheck.text=Name:
|
||||
FilesSetRulePanel.pathCheck.text=Path Substring:
|
||||
FilesSetRulePanel.filesRadioButton.text=Files
|
||||
FilesSetRulePanel.dirsRadioButton.text=Directories
|
||||
FilesSetDefsPanel.interesting.setsListLabel.text=Rule Sets:
|
||||
@@ -56,18 +56,18 @@ FilesSetDefsPanel.newRuleButton.text=New Rule
|
||||
FilesSetDefsPanel.jLabel8.text=File Size:
|
||||
FilesSetDefsPanel.jLabel7.text=MIME Type:
|
||||
FilesSetDefsPanel.rulePathConditionRegexCheckBox.text=Regex
|
||||
FilesSetDefsPanel.jLabel4.text=Path Pattern:
|
||||
FilesSetDefsPanel.jLabel4.text=Path Substring:
|
||||
FilesSetDefsPanel.jLabel1.text=Rule Details
|
||||
FilesSetDefsPanel.dirsRadioButton.text=Directories
|
||||
FilesSetDefsPanel.jLabel2.text=File Type:
|
||||
FilesSetDefsPanel.deleteRuleButton.text=Delete Rule
|
||||
FilesSetDefsPanel.fileNameRegexCheckbox.text=Regex
|
||||
FilesSetDefsPanel.fileNameRegexCheckbox.text=Substring / Regex
|
||||
FilesSetDefsPanel.ignoreKnownFilesCheckbox.text=Ignore Known Files
|
||||
FilesSetDefsPanel.rulePathConditionTextField.text=
|
||||
FilesSetDefsPanel.fileNameRadioButton.text=File Name
|
||||
FilesSetDefsPanel.fileNameRadioButton.text=Full Name
|
||||
FilesSetDefsPanel.jLabel5.text=Description:
|
||||
FilesSetDefsPanel.fileNameTextField.text=
|
||||
FilesSetDefsPanel.jLabel3.text=Name Pattern:
|
||||
FilesSetDefsPanel.jLabel3.text=Name:
|
||||
FilesSetDefsPanel.fileNameExtensionRadioButton.text=Extension Only
|
||||
FilesSetDefsPanel.rulesListLabel.text=Rules:
|
||||
FilesSetDefsPanel.editRuleButton.text=Edit Rule
|
||||
|
||||
@@ -33,8 +33,11 @@ import org.openide.DialogDisplayer;
|
||||
import org.openide.NotifyDescriptor;
|
||||
import org.openide.modules.InstalledFileLocator;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.python.util.PythonInterpreter;
|
||||
import org.sleuthkit.autopsy.core.RuntimeProperties;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
||||
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
|
||||
import org.sleuthkit.autopsy.ingest.IngestModuleFactory;
|
||||
import org.sleuthkit.autopsy.report.GeneralReportModule;
|
||||
@@ -66,12 +69,22 @@ public final class JythonModuleLoader {
|
||||
public static List<GeneralReportModule> getGeneralReportModules() {
|
||||
return getInterfaceImplementations(new GeneralReportModuleDefFilter(), GeneralReportModule.class);
|
||||
}
|
||||
|
||||
@Messages({"JythonModuleLoader.pythonInterpreterError.title=Python Modules",
|
||||
"JythonModuleLoader.pythonInterpreterError.msg=Failed to load python modules, See log for more details"})
|
||||
private static <T> List<T> getInterfaceImplementations(LineFilter filter, Class<T> interfaceClass) {
|
||||
List<T> objects = new ArrayList<>();
|
||||
Set<File> pythonModuleDirs = new HashSet<>();
|
||||
PythonInterpreter interpreter = new PythonInterpreter();
|
||||
|
||||
PythonInterpreter interpreter = null;
|
||||
// This method has previously thrown unchecked exceptions when it could not load because of non-latin characters.
|
||||
try {
|
||||
interpreter = new PythonInterpreter();
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, "Failed to load python Intepreter. Cannot load python modules", ex);
|
||||
if(RuntimeProperties.runningWithGUI()){
|
||||
MessageNotifyUtil.Notify.show(Bundle.JythonModuleLoader_pythonInterpreterError_title(),Bundle.JythonModuleLoader_pythonInterpreterError_msg(), MessageNotifyUtil.MessageType.ERROR);
|
||||
}
|
||||
return objects;
|
||||
}
|
||||
// add python modules from 'autospy/build/cluster/InternalPythonModules' folder
|
||||
// which are copied from 'autopsy/*/release/InternalPythonModules' folders.
|
||||
for (File f : InstalledFileLocator.getDefault().locateAll("InternalPythonModules", "org.sleuthkit.autopsy.core", false)) { //NON-NLS
|
||||
|
||||
@@ -41,6 +41,10 @@ import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* Allow examiner to select artifacts on which to report.
|
||||
*/
|
||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||
public class ArtifactSelectionDialog extends javax.swing.JDialog {
|
||||
|
||||
private ArtifactModel model;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-2016 Basis Technology Corp.
|
||||
* Copyright 2011-2018 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -72,6 +72,7 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
* to choose a specific event and a time range around it to show in the Timeline
|
||||
* List View.
|
||||
*/
|
||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||
final class ShowInTimelineDialog extends Dialog<ViewInTimelineRequestedEvent> {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(ShowInTimelineDialog.class.getName());
|
||||
|
||||
+3
-3
@@ -46,7 +46,7 @@ import static junit.framework.Assert.assertTrue;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
|
||||
/**
|
||||
*
|
||||
* Functional tests for the Central Repository data model.
|
||||
*/
|
||||
public class CentralRepoDatamodelTest extends TestCase {
|
||||
|
||||
@@ -115,7 +115,7 @@ public class CentralRepoDatamodelTest extends TestCase {
|
||||
EamDbUtil.setUseCentralRepo(true);
|
||||
EamDbPlatformEnum.setSelectedPlatform(EamDbPlatformEnum.SQLITE.name());
|
||||
EamDbPlatformEnum.saveSelectedPlatform();
|
||||
} catch (Exception ex) {
|
||||
} catch (EamDbException ex) {
|
||||
Exceptions.printStackTrace(ex);
|
||||
Assert.fail(ex);
|
||||
}
|
||||
@@ -2554,7 +2554,7 @@ public class CentralRepoDatamodelTest extends TestCase {
|
||||
// This seems to help in allowing the Autopsy case to be deleted
|
||||
try {
|
||||
Thread.sleep(2000);
|
||||
} catch (Exception ex) {
|
||||
} catch (InterruptedException ex) {
|
||||
|
||||
}
|
||||
} catch (CaseActionException ex) {
|
||||
|
||||
+8
-9
@@ -1,5 +1,4 @@
|
||||
/*
|
||||
*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2018 Basis Technology Corp.
|
||||
@@ -84,20 +83,20 @@ class IntraCaseUtils {
|
||||
static final String PDF = "adsf.pdf"; //not a typo - it appears this way in the test image
|
||||
static final String EMPTY = "file.dat";
|
||||
|
||||
static final String SET1 = "commonfiles_image1_v1.vhd";
|
||||
static final String SET2 = "commonfiles_image2_v1.vhd";
|
||||
static final String SET3 = "commonfiles_image3_v1.vhd";
|
||||
static final String SET4 = "commonfiles_image4_v1.vhd";
|
||||
static final String SET1 = "CommonFiles_img1_v1.vhd";
|
||||
static final String SET2 = "CommonFiles_img2_v1.vhd";
|
||||
static final String SET3 = "CommonFiles_img3_v1.vhd";
|
||||
static final String SET4 = "CommonFiles_img4_v1.vhd";
|
||||
|
||||
private final DataSourceLoader dataSourceLoader;
|
||||
|
||||
private final String caseName;
|
||||
|
||||
IntraCaseUtils(NbTestCase nbTestCase, String caseName){
|
||||
imagePath1 = Paths.get(nbTestCase.getDataDir().toString(), "commonfiles_image1_v1.vhd");
|
||||
imagePath2 = Paths.get(nbTestCase.getDataDir().toString(), "commonfiles_image2_v1.vhd");
|
||||
imagePath3 = Paths.get(nbTestCase.getDataDir().toString(), "commonfiles_image3_v1.vhd");
|
||||
imagePath4 = Paths.get(nbTestCase.getDataDir().toString(), "commonfiles_image4_v1.vhd");
|
||||
imagePath1 = Paths.get(nbTestCase.getDataDir().toString(), SET1);
|
||||
imagePath2 = Paths.get(nbTestCase.getDataDir().toString(), SET2);
|
||||
imagePath3 = Paths.get(nbTestCase.getDataDir().toString(), SET3);
|
||||
imagePath4 = Paths.get(nbTestCase.getDataDir().toString(), SET4);
|
||||
|
||||
this.dataSourceLoader = new DataSourceLoader();
|
||||
|
||||
|
||||
@@ -39,11 +39,13 @@ import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* Functional tests for embedded files.
|
||||
*/
|
||||
public class EmbeddedFileTest extends NbTestCase {
|
||||
|
||||
private static final String CASE_NAME = "EmbeddedFileTest";
|
||||
private static final Path CASE_DIRECTORY_PATH = Paths.get(System.getProperty("java.io.tmpdir"), CASE_NAME);
|
||||
private final Path IMAGE_PATH = Paths.get(this.getDataDir().toString(),"embedded.vhd");
|
||||
private final Path IMAGE_PATH = Paths.get(this.getDataDir().toString(), "EmbeddedIM_img1_v1.vhd");
|
||||
public static final String HASH_VALUE = "098f6bcd4621d373cade4e832627b4f6";
|
||||
private static final int DEEP_FOLDER_COUNT = 25;
|
||||
private Case openCase;
|
||||
|
||||
@@ -50,10 +50,13 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
import org.sleuthkit.datamodel.TskData.TSK_DB_FILES_TYPE_ENUM;
|
||||
|
||||
/**
|
||||
* Functional tests for ingest file filters.
|
||||
*/
|
||||
public class IngestFileFiltersTest extends NbTestCase {
|
||||
|
||||
private final Path IMAGE_PATH = Paths.get(this.getDataDir().toString(),"filter_test1.img");
|
||||
private final Path ZIPFILE_PATH = Paths.get(this.getDataDir().toString(), "local_files_test.zip");
|
||||
private final Path IMAGE_PATH = Paths.get(this.getDataDir().toString(),"IngestFilters_img1_v1.img");
|
||||
private final Path ZIPFILE_PATH = Paths.get(this.getDataDir().toString(), "IngestFilters_local1_v1.zip");
|
||||
|
||||
private boolean testSucceeded;
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user