Update to be functional with most recent common files code.

Merge branch 'commonfiles' of https://github.com/briangsweeney/autopsy into 3870-pure-callable

# Conflicts:
#	Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResultRootNode.java
#	Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValue.java
#	Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetadata.java
#	Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetadataBuilder.java
#	Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesPanel.java
#	Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java
#	Core/src/org/sleuthkit/autopsy/corecomponents/MultiLayerTableFilterNode.java
#	Core/src/org/sleuthkit/autopsy/datamodel/InstanceCountNode.java
#	Core/src/org/sleuthkit/autopsy/datamodel/Md5Node.java
#	Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java
This commit is contained in:
Andrew Ziehl 2018-08-06 13:47:37 -07:00
commit c86d2618bf
356 changed files with 12519 additions and 5975 deletions

View File

@ -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,23 @@
<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=1_xPSnp0UDOO9sIPpvdtW_dRtD5SW9EID" dest="${test-input}/EmbeddedIM_img2_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"/>
<get src="https://drive.google.com/uc?id=1HwpDlMa1J7h9AmEd1JhLYGnzihslZJUj" dest="${test-input}/c1ds1_v1.vhd" skipexisting="true"/>
<get src="https://drive.google.com/uc?id=1Z7mWChlLIpIlScD-DVNGFik5A_rnwS_9" dest="${test-input}/c1ds2_v1.vhd" skipexisting="true"/>
<get src="https://drive.google.com/uc?id=14ZxYGyng_eKPe2yaiKDkJLoNcKHIHhW5" dest="${test-input}/c2ds1_v1.vhd" skipexisting="true"/>
<get src="https://drive.google.com/uc?id=1xv3Lz9m2QLq35ofDfHQNe9aHVtVzHUsj" dest="${test-input}/c2ds2_v1.vhd" skipexisting="true"/>
<get src="https://drive.google.com/uc?id=1sg-znklB9yJAWq8i1cF2W-QLOM4FjZyv" dest="${test-input}/c3ds1_v1.vhd" skipexisting="true"/>
<get src="https://drive.google.com/uc?id=1qXyaSlm3hMhv0jl6JkZEficknKjYNOlt" dest="${test-input}/c3ds2_v1.vhd" skipexisting="true"/>
</target>
<target name="get-deps" depends="init-ivy,getTSKJars,get-thirdparty-dependencies,get-InternalPythonModules, download-binlist,getTestDataFiles">

View File

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

View File

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

View File

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

View File

@ -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"

View File

@ -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",

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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"

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Form>

View File

@ -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;

View File

@ -0,0 +1,127 @@
/*
* 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
* @param newComment the newComment for the tag use an empty string for no newComment
*/
@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, String newComment) {
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, newComment);
} 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);
}
}

View File

@ -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, String newComment) {
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, newComment);
} 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);
}
}

View File

@ -0,0 +1,201 @@
/*
* 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 - the TagName which is being removed from the item
* @param newTagName - the TagName which is being added to the itme
* @param comment the comment associated with the tag, empty string for no comment
*/
abstract protected void replaceTag(T oldTag, TagName newTagName, String comment);
/**
* 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(), oldtag.getComment());
});
});
// 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, oldtag.getComment());
});
}
});
add(newTagMenuItem);
// 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.
JMenuItem tagAndCommentItem = new JMenuItem(NbBundle.getMessage(this.getClass(), "AddTagAction.tagAndComment"));
tagAndCommentItem.addActionListener((ActionEvent event) -> {
GetTagNameAndCommentDialog.TagNameAndComment tagNameAndComment = GetTagNameAndCommentDialog.doDialog();
if (null != tagNameAndComment) {
selectedTags.forEach((oldtag) -> {
replaceTag(oldtag, tagNameAndComment.getTagName(), tagNameAndComment.getComment());
});
}
});
add(tagAndCommentItem);
}
}
}

View File

@ -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();
}

View File

@ -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;

View File

@ -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 {
/**

View File

@ -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().

View File

@ -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")

View File

@ -36,6 +36,7 @@ import javax.swing.Box.Filler;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JToggleButton;
import javax.swing.SwingUtilities;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
@ -67,7 +68,7 @@ final class AddImageWizardSelectDspVisual extends JPanel {
} catch (NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "Exception while getting open case.", ex);
}
//add actionlistner to listen for change
}
@ -132,7 +133,7 @@ final class AddImageWizardSelectDspVisual extends JPanel {
//Add the button
JToggleButton dspButton = createDspButton(dspType);
dspButton.addActionListener(cbActionListener);
if ((Case.getCurrentCaseThrows().getCaseType() == Case.CaseType.MULTI_USER_CASE) && dspType.equals(LocalDiskDSProcessor.getType())){
if ((Case.getCurrentCaseThrows().getCaseType() == Case.CaseType.MULTI_USER_CASE) && dspType.equals(LocalDiskDSProcessor.getType())) {
dspButton.setEnabled(false); //disable the button for local disk DSP when this is a multi user case
dspButton.setSelected(false);
shouldAddMultiUserWarning = true;
@ -172,6 +173,9 @@ final class AddImageWizardSelectDspVisual extends JPanel {
constraints.weighty = 1;
gridBagLayout.setConstraints(vertGlue, constraints);
jPanel1.setLayout(gridBagLayout);
SwingUtilities.invokeLater(() -> {
jScrollPane1.getVerticalScrollBar().setValue(0);
});
}
/**

View File

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

View File

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

View File

@ -129,6 +129,7 @@ public class Case {
private static final String EXPORT_FOLDER = "Export"; //NON-NLS
private static final String LOG_FOLDER = "Log"; //NON-NLS
private static final String REPORTS_FOLDER = "Reports"; //NON-NLS
private static final String CONFIG_FOLDER = "Config"; // NON-NLS
private static final String TEMP_FOLDER = "Temp"; //NON-NLS
private static final String MODULE_FOLDER = "ModuleOutput"; //NON-NLS
private static final String CASE_ACTION_THREAD_NAME = "%s-case-action";
@ -291,9 +292,10 @@ public class Case {
*/
ADDING_DATA_SOURCE_FAILED,
/**
* A new data source has been added to the current case. The old value
* of the PropertyChangeEvent is null, the new value is the newly-added
* data source (type: Content). Cast the PropertyChangeEvent to
* A new data source or series of data sources have been added to the
* current case. The old value of the PropertyChangeEvent is null, the
* new value is the newly-added data source (type: Content). Cast the
* PropertyChangeEvent to
* org.sleuthkit.autopsy.casemodule.events.DataSourceAddedEvent to
* access additional event data.
*/
@ -1098,6 +1100,10 @@ public class Case {
/*
* Open the top components (windows within the main application
* window).
*
* Note: If the core windows are not opened here, they will be
* opened via the DirectoryTreeTopComponent 'propertyChange()'
* method on a DATA_SOURCE_ADDED event.
*/
if (newCurrentCase.hasData()) {
CoreComponentControl.openCoreWindows();
@ -1351,6 +1357,16 @@ public class Case {
return getOrCreateSubdirectory(REPORTS_FOLDER);
}
/**
* Gets the full path to the config directory for this case, creating it if
* it does not exist.
*
* @return The config directory path.
*/
public String getConfigDirectory() {
return getOrCreateSubdirectory(CONFIG_FOLDER);
}
/**
* Gets the full path to the module output directory for this case, creating
* it if it does not exist.

View File

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

View File

@ -257,6 +257,7 @@ final class LocalDiskPanel extends JPanel {
pathTextField.setEnabled(copyImageCheckbox.isSelected());
browseButton.setEnabled(copyImageCheckbox.isSelected());
changeDatabasePathCheckbox.setEnabled(copyImageCheckbox.isSelected());
changeDatabasePathCheckbox.setSelected(copyImageCheckbox.isSelected());
fireUpdateEvent();
}//GEN-LAST:event_copyImageCheckboxActionPerformed

View File

@ -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());
}
}

View File

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

View File

@ -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;

View File

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

View File

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

View File

@ -1,6 +1,5 @@
OptionsCategory_Name_TagNamesOptions=Tags
OptionsCategory_TagNames=TagNames
Blackboard.unableToIndexArtifact.error.msg=Unable to index blackboard artifact {0}
TagNameDialog.title.text=New Tag
TagNameDialog.JOptionPane.tagNameIllegalCharacters.message=Tag name may not contain any of the following symbols\: \\ \: * ? " < > | , ;
TagNameDialog.JOptionPane.tagNameIllegalCharacters.title=Invalid character in tag name

View File

@ -54,7 +54,7 @@ final class TagNameDefinition implements Comparable<TagNameDefinition> {
private static final List<String> STANDARD_TAG_DISPLAY_NAMES = Arrays.asList(Bundle.TagNameDefinition_predefTagNames_bookmark_text(), Bundle.TagNameDefinition_predefTagNames_followUp_text(),
Bundle.TagNameDefinition_predefTagNames_notableItem_text(), DhsImageCategory.ONE.getDisplayName(),
DhsImageCategory.TWO.getDisplayName(), DhsImageCategory.THREE.getDisplayName(),
DhsImageCategory.FOUR.getDisplayName(), DhsImageCategory.FIVE.getDisplayName());
DhsImageCategory.FOUR.getDisplayName(), DhsImageCategory.FIVE.getDisplayName(), DhsImageCategory.ZERO.getDisplayName());
private final String displayName;
private final String description;
private final TagName.HTML_COLOR color;

View File

@ -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.
*

View File

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

View File

@ -18,6 +18,7 @@
*/
package org.sleuthkit.autopsy.centralrepository;
import org.openide.util.NbBundle.Messages;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
@ -26,24 +27,30 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeIns
* Dialog to allow Central Repository file instance comments to be added and
* modified.
*/
@Messages({"CentralRepoCommentDialog.title.addEditCentralRepoComment=Add/Edit Central Repository Comment"})
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
final class CentralRepoCommentDialog extends javax.swing.JDialog {
private final CorrelationAttribute correlationAttribute;
private boolean commentUpdated = false;
private String currentComment = "";
/**
* Create an instance.
*
* @param correlationAttribute The correlation attribute to be modified.
* @param title The title to assign the dialog.
*/
CentralRepoCommentDialog(CorrelationAttribute correlationAttribute, String title) {
super(WindowManager.getDefault().getMainWindow(), title);
CentralRepoCommentDialog(CorrelationAttribute correlationAttribute) {
super(WindowManager.getDefault().getMainWindow(), Bundle.CentralRepoCommentDialog_title_addEditCentralRepoComment());
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 +78,17 @@ 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 +186,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();

View File

@ -0,0 +1,60 @@
/*
* 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> actionsList = new ArrayList<>();
Collection<? extends AbstractFile> selectedFiles = Utilities.actionsGlobalContext().lookupAll(AbstractFile.class);
if (selectedFiles.size() != 1) {
return actionsList;
}
for (AbstractFile file : selectedFiles) {
if (EamDbUtil.useCentralRepo() && EamArtifactUtil.isSupportedAbstractFileType(file) && file.isFile()) {
AddEditCentralRepoCommentAction action = new AddEditCentralRepoCommentAction(file);
if (action.getCorrelationAttribute() == null) {
action.setEnabled(false);
}
actionsList.add(action);
}
}
return actionsList;
}
}

View File

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

View File

@ -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, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
</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>

View File

@ -25,16 +25,19 @@ 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;
import org.sleuthkit.autopsy.coreutils.Logger;
import java.util.stream.Collectors;
import javax.swing.JFileChooser;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
@ -45,6 +48,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 +61,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 +73,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 +83,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 +124,18 @@ 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 = new AddEditCentralRepoCommentAction(selectedNode.createCorrelationAttribute());
action.actionPerformed(null);
String currentComment = action.getComment();
if (currentComment != null) {
selectedNode.updateComment(action.getComment());
otherCasesTable.repaint();
}
} catch (EamDbException ex) {
logger.log(Level.SEVERE, "Error performing Add/Edit Central Repository Comment action", ex);
}
}
}
};
@ -137,6 +150,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 +171,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();
@ -187,23 +201,14 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
"DataContentViewerOtherCases.caseDetailsDialog.noCaseNameError=Error",
"DataContentViewerOtherCases.noOpenCase.errMsg=No open case available."})
private void showCaseDetails(int selectedRowViewIdx) {
Case openCase;
try {
openCase = Case.getCurrentCaseThrows();
} catch (NoCurrentCaseException ex) {
JOptionPane.showConfirmDialog(showCaseDetailsMenuItem,
Bundle.DataContentViewerOtherCases_noOpenCase_errMsg(),
Bundle.DataContentViewerOtherCases_noOpenCase_errMsg(),
DEFAULT_OPTION, PLAIN_MESSAGE);
return;
}
String caseDisplayName = Bundle.DataContentViewerOtherCases_caseDetailsDialog_noCaseNameError();
try {
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(),
@ -213,7 +218,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
}
caseDisplayName = eamCasePartial.getDisplayName();
// query case details
CorrelationCase eamCase = dbManager.getCase(openCase);
CorrelationCase eamCase = dbManager.getCaseByUUID(eamCasePartial.getCaseUUID());
if (eamCase == null) {
JOptionPane.showConfirmDialog(showCaseDetailsMenuItem,
Bundle.DataContentViewerOtherCases_caseDetailsDialog_noDetails(),
@ -234,6 +239,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
DEFAULT_OPTION, PLAIN_MESSAGE);
}
} catch (EamDbException ex) {
logger.log(Level.SEVERE, "Error loading case details", ex);
JOptionPane.showConfirmDialog(showCaseDetailsMenuItem,
Bundle.DataContentViewerOtherCases_caseDetailsDialog_noDetails(),
caseDisplayName,
@ -307,6 +313,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
// start with empty table
tableModel.clearTable();
correlationAttributes.clear();
earliestCaseDate.setText(Bundle.DataContentViewerOtherCases_earliestCaseNotAvailable());
}
@Override
@ -453,10 +460,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 +506,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 +559,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 +585,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 +684,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 +704,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
clearMessageOnTableStatusPanel();
setColumnWidths();
}
setEarliestCaseDate();
}
private void setColumnWidths() {
@ -701,6 +746,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 +793,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 +854,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 +869,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 +915,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 +944,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);
}
}

View File

@ -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();
}

View File

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

View File

@ -38,7 +38,7 @@ import java.util.logging.Level;
import org.sleuthkit.autopsy.casemodule.Case;
import static org.sleuthkit.autopsy.centralrepository.datamodel.EamDbUtil.updateSchemaVersion;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.healthmonitor.EnterpriseHealthMonitor;
import org.sleuthkit.autopsy.healthmonitor.HealthMonitor;
import org.sleuthkit.autopsy.healthmonitor.TimingMetric;
import org.sleuthkit.datamodel.CaseDbSchemaVersionNumber;
import org.sleuthkit.datamodel.TskData;
@ -58,6 +58,9 @@ abstract class AbstractSqlEamDb implements EamDb {
protected int bulkArtifactsThreshold;
private final Map<String, Collection<CorrelationAttribute>> bulkArtifacts;
// Maximum length for the value column in the instance tables
static final int MAX_VALUE_LENGTH = 256;
// number of instances to keep in bulk queue before doing an insert.
// Update Test code if this changes. It's hard coded there.
static final int DEFAULT_BULK_THRESHHOLD = 1000;
@ -384,6 +387,46 @@ abstract class AbstractSqlEamDb implements EamDb {
return eamCaseResult;
}
/**
* Retrieves Case details based on Case ID
*
* @param caseID unique identifier for a case
*
* @return The retrieved case
*/
@Override
public CorrelationCase getCaseById(int caseId) throws EamDbException {
// @@@ We should have a cache here...
Connection conn = connect();
CorrelationCase eamCaseResult = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
String sql = "SELECT cases.id as case_id, case_uid, case_name, creation_date, case_number, examiner_name, "
+ "examiner_email, examiner_phone, notes, organizations.id as org_id, org_name, poc_name, poc_email, poc_phone "
+ "FROM cases "
+ "LEFT JOIN organizations ON cases.org_id=organizations.id "
+ "WHERE cases.id=?";
try {
preparedStatement = conn.prepareStatement(sql);
preparedStatement.setInt(1, caseId);
resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
eamCaseResult = getEamCaseFromResultSet(resultSet);
}
} catch (SQLException ex) {
throw new EamDbException("Error getting case details.", ex); // NON-NLS
} finally {
EamDbUtil.closeStatement(preparedStatement);
EamDbUtil.closeResultSet(resultSet);
EamDbUtil.closeConnection(conn);
}
return eamCaseResult;
}
/**
* Retrieves cases that are in DB.
@ -432,7 +475,8 @@ abstract class AbstractSqlEamDb implements EamDb {
if (eamDataSource.getCaseID() == -1) {
throw new EamDbException("Case ID is -1");
} else if (eamDataSource.getID() != -1) {
throw new EamDbException("Database ID is already set in object");
// This data source is already in the central repo
return;
}
Connection conn = connect();
@ -498,6 +542,48 @@ abstract class AbstractSqlEamDb implements EamDb {
return eamDataSourceResult;
}
/**
* Retrieves Data Source details based on data source ID
*
* @param correlationCase the current CorrelationCase used for ensuring
* uniqueness of DataSource
* @param dataSourceId the data source ID number
*
* @return The data source
*/
@Override
public CorrelationDataSource getDataSourceById(CorrelationCase correlationCase, int dataSourceId) throws EamDbException {
if (correlationCase == null) {
throw new EamDbException("Correlation case is null");
}
Connection conn = connect();
CorrelationDataSource eamDataSourceResult = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
String sql = "SELECT * FROM data_sources WHERE id=? AND case_id=?"; // NON-NLS
try {
preparedStatement = conn.prepareStatement(sql);
preparedStatement.setInt(1, dataSourceId);
preparedStatement.setInt(2, correlationCase.getID());
resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
eamDataSourceResult = getEamDataSourceFromResultSet(resultSet);
}
} catch (SQLException ex) {
throw new EamDbException("Error getting data source.", ex); // NON-NLS
} finally {
EamDbUtil.closeStatement(preparedStatement);
EamDbUtil.closeResultSet(resultSet);
EamDbUtil.closeConnection(conn);
}
return eamDataSourceResult;
}
/**
* Return a list of data sources in the DB
@ -550,6 +636,13 @@ abstract class AbstractSqlEamDb implements EamDb {
if (eamArtifact.getCorrelationValue() == null) {
throw new EamDbException("Correlation value is null");
}
if (eamArtifact.getCorrelationValue().length() >= MAX_VALUE_LENGTH) {
throw new EamDbException("Artifact value too long for central repository."
+ "\nCorrelationArtifact ID: " + eamArtifact.getID()
+ "\nCorrelationArtifact Type: " + eamArtifact.getCorrelationType().getDisplayName()
+ "\nCorrelationArtifact Value: " + eamArtifact.getCorrelationValue());
}
Connection conn = connect();
@ -659,7 +752,7 @@ abstract class AbstractSqlEamDb implements EamDb {
return artifactInstances;
}
/**
* Retrieves eamArtifact instances from the database that are associated
* with the aType and filePath
@ -881,7 +974,7 @@ abstract class AbstractSqlEamDb implements EamDb {
sql
+= "+ (SELECT count(*) FROM "
+ table_name
+ " WHERE case_id=(SELECT id FROM cases WHERE case_uid=?) and data_source_id=(SELECT id FROM data_sources WHERE device_id=?))";
+ " WHERE data_source_id=(SELECT data_sources.id FROM cases INNER JOIN data_sources ON cases.id = data_sources.case_id WHERE case_uid=? and device_id=?))";
}
try {
@ -975,27 +1068,50 @@ abstract class AbstractSqlEamDb implements EamDb {
if (!eamArtifact.getCorrelationValue().isEmpty()) {
if (eamInstance.getCorrelationCase() == null) {
throw new EamDbException("CorrelationAttributeInstance case is null");
throw new EamDbException("CorrelationAttributeInstance case is null for: "
+ "\n\tCorrelationArtifact ID: " + eamArtifact.getID()
+ "\n\tCorrelationArtifact Type: " + eamArtifact.getCorrelationType().getDisplayName()
+ "\n\tCorrelationArtifact Value: " + eamArtifact.getCorrelationValue());
}
if (eamInstance.getCorrelationDataSource() == null) {
throw new EamDbException("CorrelationAttributeInstance data source is null");
throw new EamDbException("CorrelationAttributeInstance data source is null for: "
+ "\n\tCorrelationArtifact ID: " + eamArtifact.getID()
+ "\n\tCorrelationArtifact Type: " + eamArtifact.getCorrelationType().getDisplayName()
+ "\n\tCorrelationArtifact Value: " + eamArtifact.getCorrelationValue());
}
if (eamInstance.getKnownStatus() == null) {
throw new EamDbException("CorrelationAttributeInstance known status is null");
throw new EamDbException("CorrelationAttributeInstance known status is null for: "
+ "\n\tCorrelationArtifact ID: " + eamArtifact.getID()
+ "\n\tCorrelationArtifact Type: " + eamArtifact.getCorrelationType().getDisplayName()
+ "\n\tCorrelationArtifact Value: " + eamArtifact.getCorrelationValue()
+ "\n\tEam Instance: "
+ "\n\t\tCaseId: " + eamInstance.getCorrelationDataSource().getCaseID()
+ "\n\t\tDeviceID: " + eamInstance.getCorrelationDataSource().getDeviceID());
}
bulkPs.setString(1, eamInstance.getCorrelationCase().getCaseUUID());
bulkPs.setString(2, eamInstance.getCorrelationDataSource().getDeviceID());
bulkPs.setInt(3, eamInstance.getCorrelationDataSource().getCaseID());
bulkPs.setString(4, eamArtifact.getCorrelationValue());
bulkPs.setString(5, eamInstance.getFilePath());
bulkPs.setByte(6, eamInstance.getKnownStatus().getFileKnownValue());
if ("".equals(eamInstance.getComment())) {
bulkPs.setNull(7, Types.INTEGER);
if (eamArtifact.getCorrelationValue().length() < MAX_VALUE_LENGTH) {
bulkPs.setString(1, eamInstance.getCorrelationCase().getCaseUUID());
bulkPs.setString(2, eamInstance.getCorrelationDataSource().getDeviceID());
bulkPs.setInt(3, eamInstance.getCorrelationDataSource().getCaseID());
bulkPs.setString(4, eamArtifact.getCorrelationValue());
bulkPs.setString(5, eamInstance.getFilePath());
bulkPs.setByte(6, eamInstance.getKnownStatus().getFileKnownValue());
if ("".equals(eamInstance.getComment())) {
bulkPs.setNull(7, Types.INTEGER);
} else {
bulkPs.setString(7, eamInstance.getComment());
}
bulkPs.addBatch();
} else {
bulkPs.setString(7, eamInstance.getComment());
logger.log(Level.WARNING, ("Artifact value too long for central repository."
+ "\n\tCorrelationArtifact ID: " + eamArtifact.getID()
+ "\n\tCorrelationArtifact Type: " + eamArtifact.getCorrelationType().getDisplayName()
+ "\n\tCorrelationArtifact Value: " + eamArtifact.getCorrelationValue())
+ "\n\tEam Instance: "
+ "\n\t\tCaseId: " + eamInstance.getCorrelationDataSource().getCaseID()
+ "\n\t\tDeviceID: " + eamInstance.getCorrelationDataSource().getDeviceID()
+ "\n\t\tFilePath: " + eamInstance.getFilePath());
}
bulkPs.addBatch();
}
}
}
@ -1004,8 +1120,8 @@ abstract class AbstractSqlEamDb implements EamDb {
bulkArtifacts.get(type.getDbTableName()).clear();
}
TimingMetric timingMetric = EnterpriseHealthMonitor.getTimingMetric("Correlation Engine: Bulk insert");
EnterpriseHealthMonitor.submitTimingMetric(timingMetric);
TimingMetric timingMetric = HealthMonitor.getTimingMetric("Correlation Engine: Bulk insert");
HealthMonitor.submitTimingMetric(timingMetric);
// Reset state
bulkArtifactsCount = 0;
@ -1740,11 +1856,13 @@ abstract class AbstractSqlEamDb implements EamDb {
return 0 < badInstances;
}
/**
* Process the Artifact instance in the EamDb
*
* @param type EamArtifact.Type to search for
* @param type EamArtifact.Type to search for
* @param instanceTableCallback callback to process the instance
*
* @throws EamDbException
*/
@Override
@ -1772,12 +1890,58 @@ 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);
}
}
/**
* Process the Artifact instance in the EamDb give a where clause
*
* @param type EamArtifact.Type to search for
* @param instanceTableCallback callback to process the instance
* @param whereClause query string to execute
* @throws EamDbException
*/
@Override
public void processInstanceTableWhere(CorrelationAttribute.Type type, String whereClause, InstanceTableCallback instanceTableCallback) throws EamDbException {
if (type == null) {
throw new EamDbException("Correlation type is null");
}
if (instanceTableCallback == null) {
throw new EamDbException("Callback interface is null");
}
if(whereClause == null) {
throw new EamDbException("Where clause is null");
}
Connection conn = connect();
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
String tableName = EamDbUtil.correlationTypeToInstanceTableName(type);
StringBuilder sql = new StringBuilder(3);
sql.append("select * from ");
sql.append(tableName);
sql.append(" WHERE ");
sql.append(whereClause);
try {
preparedStatement = conn.prepareStatement(sql.toString());
resultSet = preparedStatement.executeQuery();
instanceTableCallback.process(resultSet);
} catch (SQLException ex) {
throw new EamDbException("Error getting all artifact instances from instances table", ex);
} finally {
EamDbUtil.closeStatement(preparedStatement);
EamDbUtil.closeResultSet(resultSet);
EamDbUtil.closeConnection(conn);
}
}
@Override
public EamOrganization newOrganization(EamOrganization eamOrg) throws EamDbException {
if (eamOrg == null) {

View File

@ -67,6 +67,7 @@ public class CorrelationDataSource implements Serializable {
/**
* Create a CorrelationDataSource object from a TSK Content object.
* This will add it to the central repository.
*
* @param correlationCase the current CorrelationCase used for ensuring
* uniqueness of DataSource

View File

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

View File

@ -175,13 +175,21 @@ public interface EamDb {
*/
CorrelationCase getCaseByUUID(String caseUUID) throws EamDbException;
/**
* Retrieves Case details based on Case ID
*
* @param caseID unique identifier for a case
*
* @return The retrieved case
*/
CorrelationCase getCaseById(int caseId) throws EamDbException;
/**
* Retrieves cases that are in DB.
*
* @return List of cases
*/
List<CorrelationCase> getCases() throws EamDbException;
/**
* Creates new Data Source in the database
*
@ -200,6 +208,18 @@ public interface EamDb {
*/
CorrelationDataSource getDataSource(CorrelationCase correlationCase, String dataSourceDeviceId) throws EamDbException;
/**
* Retrieves Data Source details based on data source ID
*
* @param correlationCase the current CorrelationCase used for ensuring
* uniqueness of DataSource
* @param dataSourceId the data source ID number
*
* @return The data source
*/
CorrelationDataSource getDataSourceById(CorrelationCase correlationCase, int dataSourceId) throws EamDbException;
/**
* Retrieves data sources that are in DB
*
@ -225,7 +245,7 @@ public interface EamDb {
* @return List of artifact instances for a given type/value
*/
List<CorrelationAttributeInstance> getArtifactInstancesByTypeValue(CorrelationAttribute.Type aType, String value) throws EamDbException;
/**
* Retrieves eamArtifact instances from the database that are associated
* with the aType and filePath
@ -685,4 +705,15 @@ public interface EamDb {
* @throws EamDbException
*/
void processInstanceTable(CorrelationAttribute.Type type, InstanceTableCallback instanceTableCallback) throws EamDbException;
/**
* Process the Artifact instance in the EamDb
*
* @param type EamArtifact.Type to search for
* @param instanceTableCallback callback to process the instance
* @param whereClause query string to execute
* @throws EamDbException
*/
void processInstanceTableWhere(CorrelationAttribute.Type type, String whereClause, InstanceTableCallback instanceTableCallback) throws EamDbException;
}

View File

@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.centralrepository.datamodel;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import org.apache.commons.dbcp2.BasicDataSource;

View File

@ -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);
}

View File

@ -450,10 +450,10 @@ final class CaseEventListener implements PropertyChangeListener {
correlationCase = dbManager.newCase(openCase);
}
if (null == dbManager.getDataSource(correlationCase, deviceId)) {
dbManager.newDataSource(CorrelationDataSource.fromTSKDataSource(correlationCase, newDataSource));
CorrelationDataSource.fromTSKDataSource(correlationCase, newDataSource);
}
} catch (EamDbException ex) {
LOGGER.log(Level.SEVERE, "Error connecting to Central Repository database.", ex); //NON-NLS
LOGGER.log(Level.SEVERE, "Error adding new data source to the central repository", ex); //NON-NLS
} catch (TskCoreException | TskDataException ex) {
LOGGER.log(Level.SEVERE, "Error getting data source from DATA_SOURCE_ADDED event content.", ex); //NON-NLS
}

View File

@ -276,12 +276,12 @@ public class IngestEventsListener {
}
}
if (FALSE == eamArtifacts.isEmpty()) {
try {
for (CorrelationAttribute eamArtifact : eamArtifacts) {
for (CorrelationAttribute eamArtifact : eamArtifacts) {
try {
dbManager.addArtifact(eamArtifact);
} catch (EamDbException ex) {
LOGGER.log(Level.SEVERE, "Error adding artifact to database.", ex); //NON-NLS
}
} catch (EamDbException ex) {
LOGGER.log(Level.SEVERE, "Error connecting to Central Repository database.", ex); //NON-NLS
}
} // DATA_ADDED
}

View File

@ -49,7 +49,7 @@ import org.sleuthkit.datamodel.HashUtility;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.autopsy.centralrepository.eventlisteners.IngestEventsListener;
import org.sleuthkit.autopsy.healthmonitor.EnterpriseHealthMonitor;
import org.sleuthkit.autopsy.healthmonitor.HealthMonitor;
import org.sleuthkit.autopsy.healthmonitor.TimingMetric;
/**
@ -135,9 +135,9 @@ final class IngestModule implements FileIngestModule {
*/
if (abstractFile.getKnown() != TskData.FileKnown.KNOWN && flagTaggedNotableItems) {
try {
TimingMetric timingMetric = EnterpriseHealthMonitor.getTimingMetric("Correlation Engine: Notable artifact query");
TimingMetric timingMetric = HealthMonitor.getTimingMetric("Correlation Engine: Notable artifact query");
List<String> caseDisplayNamesList = dbManager.getListCasesHavingArtifactInstancesKnownBad(filesType, md5);
EnterpriseHealthMonitor.submitTimingMetric(timingMetric);
HealthMonitor.submitTimingMetric(timingMetric);
if (!caseDisplayNamesList.isEmpty()) {
postCorrelatedBadFileToBlackboard(abstractFile, caseDisplayNamesList);
}

View File

@ -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.

View File

@ -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, &quot;{key}&quot;)"/>
<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, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
</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"/>

View File

@ -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
}

View File

@ -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());

View File

@ -0,0 +1,169 @@
<?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, &quot;{key}&quot;)"/>
</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="-2" pref="6" max="-2" attributes="0"/>
<Component id="showCasesPanel" pref="1188" max="32767" attributes="0"/>
<EmptySpace min="-2" pref="6" max="-2" 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="-2" pref="6" max="-2" attributes="0"/>
<Component id="showCasesPanel" pref="473" max="32767" attributes="0"/>
<EmptySpace max="-2" 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="1188" 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="473" 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="1423" max="32767" attributes="0"/>
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
<Component id="innerCaseScrollPane" alignment="0" pref="1423" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<EmptySpace min="0" pref="500" max="32767" attributes="0"/>
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
<Component id="innerCaseScrollPane" alignment="0" pref="500" 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, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
</Property>
</AccessibilityProperties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="closeButtonActionPerformed"/>
</Events>
</Component>
</SubComponents>
</Form>

View File

@ -0,0 +1,190 @@
/*
* 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, 1423, Short.MAX_VALUE)
.addGroup(outCasesPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(innerCaseScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 1423, Short.MAX_VALUE))
);
outCasesPaneLayout.setVerticalGroup(
outCasesPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 500, Short.MAX_VALUE)
.addGroup(outCasesPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(innerCaseScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 500, 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, 1188, 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, 473, 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(6, 6, 6)
.addComponent(showCasesPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 1188, Short.MAX_VALUE)
.addGap(6, 6, 6))
.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(6, 6, 6)
.addComponent(showCasesPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 473, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.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
}

View File

@ -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();
}
}

View File

@ -0,0 +1,163 @@
/*
*
* 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.commonfilesearch;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.datamodel.DisplayableItemNode;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Represents an instance in either the CaseDB or CR that had a common match.
* Different implementations know how to get the instance details from either
* CaseDB or CR.
*
* Defines leaf-type nodes used in the Common Files Search results tree. Leaf
* nodes, may describe common attributes which exist in the current case DB or
* in the Central Repo. When a reference to the AbstractFile is lacking (such as
* in the case that a common attribute is found in the Central Repo) not all
* features of the Content Viewer, and context menu can be supported. Thus,
* multiple types of leaf nodes are required to represent Common Attribute
* Instance nodes.
*/
public abstract class AbstractCommonAttributeInstance {
private final Long abstractFileObjectId;
private final String caseName;
private final String dataSource;
/**
* Create a leaf node for attributes found in files in the current case db.
*
* @param abstractFileReference file from which the common attribute was
* found
* @param cachedFiles storage for abstract files which have been used
* already so we can avoid extra roundtrips to the case db
* @param dataSource datasource where this attribute appears
* @param caseName case where this attribute appears
*/
AbstractCommonAttributeInstance(Long abstractFileReference, String dataSource, String caseName) {
this.abstractFileObjectId = abstractFileReference;
this.caseName = caseName;
this.dataSource = dataSource;
}
/**
* Create a leaf node for attributes found in the central repo and not
* available in the current data case.
*
* @param cachedFiles storage for abstract files which have been used
* already so we can avoid extra roundtrips to the case db
*/
AbstractCommonAttributeInstance() {
this.abstractFileObjectId = -1L;
this.caseName = "";
this.dataSource = "";
}
/**
* Get an AbstractFile for this instance if it can be retrieved from the
* CaseDB.
*
* @return AbstractFile corresponding to this common attribute or null if it
* cannot be found (for example, in the event that this is a central repo
* file)
*/
abstract AbstractFile getAbstractFile();
/**
* Create a list of leaf nodes, to be used to display a row in the tree
* table
*
* @return leaf nodes for tree
*/
abstract DisplayableItemNode[] generateNodes();
/**
* The name of the case where this common attribute is found.
*
* @return case name
*/
String getCaseName() {
return this.caseName;
}
/**
* Get string name of the data source where this common attribute appears.
*
* @return data source name
*/
public String getDataSource() {
/**
* Even though we could get this from the CR record or the AbstractFile,
* we want to avoid getting it from the AbstractFile because it would be
* an extra database roundtrip.
*/
return this.dataSource;
}
/**
* ObjectId of the AbstractFile that is equivalent to the file from which
* this common attribute instance
*
* @return the abstractFileObjectId
*/
public Long getAbstractFileObjectId() {
return abstractFileObjectId;
}
/**
* Use this to create an AbstractCommonAttributeInstanceNode of the
* appropriate type. In any case, we'll get something which extends
* DisplayableItemNode which can be used to populate the tree.
*
* If the common attribute in question could be derived from an AbstractFile
* in the present SleuthkitCase, we can use an
* IntraCaseCommonAttributeInstanceNode which enables extended functionality
* in the context menu and in the content viewer.
*
* Otherwise, we will get an InterCaseCommonAttributeInstanceNode which
* supports only baseline functionality.
*
* @param attributeInstance common file attribute instance form the central
* repo
* @param abstractFile an AbstractFile from which the attribute instance was
* found - applies to CaseDbCommonAttributeInstance only
* @param currentCaseName
* @return the appropriate leaf node for the results tree
* @throws TskCoreException
*/
static DisplayableItemNode createNode(CorrelationAttribute attribute, AbstractFile abstractFile, String currentCaseName) throws TskCoreException {
DisplayableItemNode leafNode;
CorrelationAttributeInstance attributeInstance = attribute.getInstances().get(0);
if (abstractFile == null) {
leafNode = new CentralRepoCommonAttributeInstanceNode(attributeInstance);
} else {
final String abstractFileDataSourceName = abstractFile.getDataSource().getName();
leafNode = new CaseDBCommonAttributeInstanceNode(abstractFile, currentCaseName, abstractFileDataSourceName);
}
return leafNode;
}
}

View File

@ -0,0 +1,213 @@
/*
*
* 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.commonfilesearch;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Prototype for an object which finds files with common attributes.
* Subclass this and implement findFiles in order
*/
public abstract class AbstractCommonAttributeSearcher {
private final Map<Long, String> dataSourceIdToNameMap;
private boolean filterByMedia;
private boolean filterByDoc;
AbstractCommonAttributeSearcher(Map<Long, String> dataSourceIdMap, boolean filterByMedia, boolean filterByDoc){
this.filterByDoc = filterByDoc;
this.filterByMedia = filterByMedia;
this.dataSourceIdToNameMap = dataSourceIdMap;
}
Map<Long, String> getDataSourceIdToNameMap(){
return Collections.unmodifiableMap(this.dataSourceIdToNameMap);
}
/**
* Implement this to search for files with common attributes. Creates an
* object (CommonAttributeSearchResults) which contains all of the information
* required to display a tree view in the UI. The view will contain 3 layers:
* a top level node, indicating the number matches each of it's children possess,
* a mid level node indicating the matched attribute,
* @return
* @throws TskCoreException
* @throws NoCurrentCaseException
* @throws SQLException
* @throws EamDbException
*/
public abstract CommonAttributeSearchResults findFiles() throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException;
/**
* Implement this to create a descriptive string for the tab which will display
* this data.
* @return an informative string
*/
@NbBundle.Messages({
"AbstractCommonFilesMetadataBuilder.buildTabTitle.titleIntraAll=Common Files (All Data Sources, %s)",
"AbstractCommonFilesMetadataBuilder.buildTabTitle.titleIntraSingle=Common Files (Data Source: %s, %s)",
"AbstractCommonFilesMetadataBuilder.buildTabTitle.titleInterAll=Common Files (All Central Repository Cases, %s)",
"AbstractCommonFilesMetadataBuilder.buildTabTitle.titleInterSingle=Common Files (Central Repository Case: %s, %s)",
})
abstract String buildTabTitle();
@NbBundle.Messages({
"AbstractCommonFilesMetadataBuilder.buildCategorySelectionString.doc=Documents",
"AbstractCommonFilesMetadataBuilder.buildCategorySelectionString.media=Media",
"AbstractCommonFilesMetadataBuilder.buildCategorySelectionString.all=All File Categories"
})
String buildCategorySelectionString() {
if (!this.isFilterByDoc() && !this.isFilterByMedia()) {
return Bundle.AbstractCommonFilesMetadataBuilder_buildCategorySelectionString_all();
} else {
List<String> filters = new ArrayList<>();
if (this.isFilterByDoc()) {
filters.add(Bundle.AbstractCommonFilesMetadataBuilder_buildCategorySelectionString_doc());
}
if (this.isFilterByMedia()) {
filters.add(Bundle.AbstractCommonFilesMetadataBuilder_buildCategorySelectionString_media());
}
return String.join(", ", filters);
}
}
static Map<Integer, CommonAttributeValueList> collateMatchesByNumberOfInstances(Map<String, CommonAttributeValue> commonFiles) {
//collate matches by number of matching instances - doing this in sql doesnt seem efficient
Map<Integer, CommonAttributeValueList> instanceCollatedCommonFiles = new TreeMap<>();
for(CommonAttributeValue md5Metadata : commonFiles.values()){
Integer size = md5Metadata.getInstanceCount();
if(instanceCollatedCommonFiles.containsKey(size)){
instanceCollatedCommonFiles.get(size).addMetadataToList(md5Metadata);
} else {
CommonAttributeValueList value = new CommonAttributeValueList();
value.addMetadataToList(md5Metadata);
instanceCollatedCommonFiles.put(size, value);
}
}
return instanceCollatedCommonFiles;
}
/*
* The set of the MIME types that will be checked for extension mismatches
* when checkType is ONLY_MEDIA.
* ".jpg", ".jpeg", ".png", ".psd", ".nef", ".tiff", ".bmp", ".tec"
* ".aaf", ".3gp", ".asf", ".avi", ".m1v", ".m2v", //NON-NLS
* ".m4v", ".mp4", ".mov", ".mpeg", ".mpg", ".mpe", ".mp4", ".rm", ".wmv", ".mpv", ".flv", ".swf"
*/
static final Set<String> MEDIA_PICS_VIDEO_MIME_TYPES = Stream.of(
"image/bmp", //NON-NLS
"image/gif", //NON-NLS
"image/jpeg", //NON-NLS
"image/png", //NON-NLS
"image/tiff", //NON-NLS
"image/vnd.adobe.photoshop", //NON-NLS
"image/x-raw-nikon", //NON-NLS
"image/x-ms-bmp", //NON-NLS
"image/x-icon", //NON-NLS
"video/webm", //NON-NLS
"video/3gpp", //NON-NLS
"video/3gpp2", //NON-NLS
"video/ogg", //NON-NLS
"video/mpeg", //NON-NLS
"video/mp4", //NON-NLS
"video/quicktime", //NON-NLS
"video/x-msvideo", //NON-NLS
"video/x-flv", //NON-NLS
"video/x-m4v", //NON-NLS
"video/x-ms-wmv", //NON-NLS
"application/vnd.ms-asf", //NON-NLS
"application/vnd.rn-realmedia", //NON-NLS
"application/x-shockwave-flash" //NON-NLS
).collect(Collectors.toSet());
/*
* The set of the MIME types that will be checked for extension mismatches
* when checkType is ONLY_TEXT_FILES.
* ".doc", ".docx", ".odt", ".xls", ".xlsx", ".ppt", ".pptx"
* ".txt", ".rtf", ".log", ".text", ".xml"
* ".html", ".htm", ".css", ".js", ".php", ".aspx"
* ".pdf"
*/
static final Set<String> TEXT_FILES_MIME_TYPES = Stream.of(
"text/plain", //NON-NLS
"application/rtf", //NON-NLS
"application/pdf", //NON-NLS
"text/css", //NON-NLS
"text/html", //NON-NLS
"text/csv", //NON-NLS
"application/json", //NON-NLS
"application/javascript", //NON-NLS
"application/xml", //NON-NLS
"text/calendar", //NON-NLS
"application/x-msoffice", //NON-NLS
"application/x-ooxml", //NON-NLS
"application/msword", //NON-NLS
"application/vnd.openxmlformats-officedocument.wordprocessingml.document", //NON-NLS
"application/vnd.ms-powerpoint", //NON-NLS
"application/vnd.openxmlformats-officedocument.presentationml.presentation", //NON-NLS
"application/vnd.ms-excel", //NON-NLS
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", //NON-NLS
"application/vnd.oasis.opendocument.presentation", //NON-NLS
"application/vnd.oasis.opendocument.spreadsheet", //NON-NLS
"application/vnd.oasis.opendocument.text" //NON-NLS
).collect(Collectors.toSet());
/**
* @return the filterByMedia
*/
boolean isFilterByMedia() {
return filterByMedia;
}
/**
* @param filterByMedia the filterByMedia to set
*/
void setFilterByMedia(boolean filterByMedia) {
this.filterByMedia = filterByMedia;
}
/**
* @return the filterByDoc
*/
boolean isFilterByDoc() {
return filterByDoc;
}
/**
* @param filterByDoc the filterByDoc to set
*/
void setFilterByDoc(boolean filterByDoc) {
this.filterByDoc = filterByDoc;
}
}

View File

@ -0,0 +1,61 @@
/*
*
* 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.commonfilesearch;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Algorithm which finds files anywhere in the Central Repo which also occur in
* present case.
*/
public class AllInterCaseCommonAttributeSearcher extends InterCaseCommonAttributeSearcher {
/**
*
* @param filterByMediaMimeType match only on files whose mime types can be
* broadly categorized as media types
* @param filterByDocMimeType match only on files whose mime types can be
* broadly categorized as document types
* @throws EamDbException
*/
public AllInterCaseCommonAttributeSearcher(Map<Long, String> dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) throws EamDbException {
super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType);
}
@Override
public CommonAttributeSearchResults findFiles() throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException {
InterCaseSearchResultsProcessor eamDbAttrInst = new InterCaseSearchResultsProcessor(this.getDataSourceIdToNameMap());
Map<Integer, CommonAttributeValueList> interCaseCommonFiles = eamDbAttrInst.findInterCaseCommonAttributeValues(Case.getCurrentCase());
return new CommonAttributeSearchResults(interCaseCommonFiles);
}
@Override
String buildTabTitle() {
final String buildCategorySelectionString = this.buildCategorySelectionString();
final String titleTemplate = Bundle.AbstractCommonFilesMetadataBuilder_buildTabTitle_titleInterAll();
return String.format(titleTemplate, new Object[]{buildCategorySelectionString});
}
}

View File

@ -20,13 +20,14 @@
package org.sleuthkit.autopsy.commonfilesearch;
import java.util.Map;
import org.sleuthkit.datamodel.TskData.FileKnown;
/**
* Provides logic for selecting common files from all data sources.
*/
final public class AllDataSourcesCommonFilesAlgorithm extends CommonFilesMetadataBuilder {
final public class AllIntraCaseCommonAttributeSearcher extends IntraCaseCommonAttributeSearcher {
private static final String WHERE_CLAUSE = "%s md5 in (select md5 from tsk_files where (known != 1 OR known IS NULL)%s GROUP BY md5 HAVING COUNT(*) > 1) order by md5"; //NON-NLS
private static final String WHERE_CLAUSE = "%s md5 in (select md5 from tsk_files where (known != "+ FileKnown.KNOWN.getFileKnownValue() + " OR known IS NULL)%s GROUP BY md5 HAVING COUNT(DISTINCT data_source_obj_id) > 1) order by md5"; //NON-NLS
/**
* Implements the algorithm for getting common files across all data
@ -36,7 +37,7 @@ final public class AllDataSourcesCommonFilesAlgorithm extends CommonFilesMetadat
* @param filterByMediaMimeType match only on files whose mime types can be broadly categorized as media types
* @param filterByDocMimeType match only on files whose mime types can be broadly categorized as document types
*/
public AllDataSourcesCommonFilesAlgorithm(Map<Long, String> dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) {
public AllIntraCaseCommonAttributeSearcher(Map<Long, String> dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) {
super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType);
}
@ -48,9 +49,9 @@ final public class AllDataSourcesCommonFilesAlgorithm extends CommonFilesMetadat
}
@Override
protected String buildTabTitle() {
String buildTabTitle() {
final String buildCategorySelectionString = this.buildCategorySelectionString();
final String titleTemplate = Bundle.CommonFilesMetadataBuilder_buildTabTitle_titleAll();
final String titleTemplate = Bundle.AbstractCommonFilesMetadataBuilder_buildTabTitle_titleIntraAll();
return String.format(titleTemplate, new Object[]{buildCategorySelectionString});
}
}

View File

@ -1,15 +1,24 @@
CommonFilesPanel.searchButton.text=Search
CommonFilesPanel.withinDataSourceRadioButton.text=At least one instance of a given MD5 must appear in the data source selected below:
CommonFilesPanel.allDataSourcesRadioButton.text=Files can be in any data source
CommonFilesPanel.cancelButton.text=Cancel
CommonFilesPanel.cancelButton.actionCommand=Cancel
CommonFilesPanel.selectedFileCategoriesButton.text=Match on the following file categories:
CommonFilesPanel.selectedFileCategoriesButton.toolTipText=Select from the options below...
CommonFilesPanel.pictureVideoCheckbox.text=Pictures and Videos
CommonFilesPanel.documentsCheckbox.text=Documents
CommonFilesPanel.commonFilesSearchLabel.text=<html>Find duplicate files in the current case.</html>
CommonFilesPanel.allFileCategoriesRadioButton.toolTipText=No filtering applied to results...
CommonFilesPanel.allFileCategoriesRadioButton.text=Match on all file types
CommonFilesPanel.commonFilesSearchLabel.text=<html>Find files in multiple data sources in the current case.</html>
CommonFilesPanel.text=Indicate which data sources to consider while searching for duplicates:
CommonFilesPanel.categoriesLabel.text=Indicate which file types to include in results:
CommonFilesPanel.errorText.text=In order to search, you must select a file category.
CommonFilesPanel.jRadioButton1.text=jRadioButton1
CommonFilesPanel.jRadioButton2.text=With previous cases in the Central Repository
CommonFilesPanel.intraCaseRadio.label=Correlate within current case only
CommonFilesPanel.interCaseRadio.label=Correlate amongst all known cases (uses Central Repo)
IntraCasePanel.allDataSourcesRadioButton.text=Matches may be from any data source
IntraCasePanel.withinDataSourceRadioButton.text=At least one match must appear in the data source selected below:
InterCasePanel.specificCentralRepoCaseRadio.text=Matches must be from the following Central Repo case:
InterCasePanel.anyCentralRepoCaseRadio.text=Matches may be from any Central Repo case
CommonAttributePanel.selectedFileCategoriesButton.toolTipText=Select from the options below...
CommonAttributePanel.selectedFileCategoriesButton.text=Only the selected file types:
CommonAttributePanel.allFileCategoriesRadioButton.toolTipText=No filtering applied to results...
CommonAttributePanel.allFileCategoriesRadioButton.text=All file types
CommonAttributePanel.cancelButton.actionCommand=Cancel
CommonAttributePanel.cancelButton.text=Cancel
CommonAttributePanel.searchButton.text=Search
CommonAttributePanel.commonFilesSearchLabel2.text=Scope of Search
CommonAttributePanel.intraCaseRadio.text=Within current case
CommonAttributePanel.commonFilesSearchLabel1.text=<html>Find common files to correlate data soures or cases.</html>
CommonAttributePanel.errorText.text=In order to search, you must select a file category.
CommonAttributePanel.categoriesLabel.text=File Types To Include:
CommonAttributePanel.documentsCheckbox.text=Documents
CommonAttributePanel.pictureVideoCheckbox.text=Pictures and Videos

View File

@ -0,0 +1,75 @@
/*
*
* 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.commonfilesearch;
import java.util.Arrays;
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.autopsy.datamodel.DisplayableItemNode;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Encapsulates data required to instantiate a <code>FileInstanceNode</code> for an instance in the CaseDB
*/
final public class CaseDBCommonAttributeInstance extends AbstractCommonAttributeInstance {
private static final Logger LOGGER = Logger.getLogger(CaseDBCommonAttributeInstance.class.getName());
/**
* Create meta data required to find an abstract file and build a
* FileInstanceNode.
*
* @param objectId id of abstract file to find
* @param dataSourceName name of datasource where the object is found
*/
CaseDBCommonAttributeInstance(Long abstractFileReference, String dataSource, String caseName) {
super(abstractFileReference, dataSource, caseName);
}
@Override
public DisplayableItemNode[] generateNodes() {
final CaseDBCommonAttributeInstanceNode intraCaseCommonAttributeInstanceNode = new CaseDBCommonAttributeInstanceNode(this.getAbstractFile(), this.getCaseName(), this.getDataSource());
return Arrays.asList(intraCaseCommonAttributeInstanceNode).toArray(new DisplayableItemNode[1]);
}
@Override
AbstractFile getAbstractFile() {
Case currentCase;
try {
currentCase = Case.getCurrentCaseThrows();
SleuthkitCase tskDb = currentCase.getSleuthkitCase();
AbstractFile abstractFile = tskDb.findAllFilesWhere(String.format("obj_id in (%s)", this.getAbstractFileObjectId())).get(0);
return abstractFile;
} catch (TskCoreException | NoCurrentCaseException ex) {
LOGGER.log(Level.SEVERE, String.format("Unable to find AbstractFile for record with obj_id: %s. Node not created.", new Object[]{this.getAbstractFileObjectId()}), ex);
return null;
}
}
}

View File

@ -0,0 +1,90 @@
/*
* 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.commonfilesearch;
import org.apache.commons.lang3.StringUtils;
import org.openide.nodes.Sheet;
import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor;
import org.sleuthkit.autopsy.datamodel.FileNode;
import org.sleuthkit.autopsy.datamodel.NodeProperty;
import org.sleuthkit.datamodel.AbstractFile;
/**
* Node that wraps CaseDBCommonAttributeInstance to represent a file instance stored
* in the CaseDB.
*/
public class CaseDBCommonAttributeInstanceNode extends FileNode {
private final String caseName;
private final String dataSource;
/**
* Create a node which can be used in a multilayer tree table and is based
* on an <code>AbstractFile</code>.
*
* @param fsContent
* @param dataSource
*/
public CaseDBCommonAttributeInstanceNode(AbstractFile fsContent, String caseName, String dataSource) {
super(fsContent);
this.caseName = caseName;
this.dataSource = dataSource;
}
@Override
public boolean isLeafTypeNode(){
//Not used atm - could maybe be leveraged for better use in Children objects
return true;
}
@Override
public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
return visitor.visit(this);
}
public String getCase(){
return this.caseName;
}
public String getDataSource() {
return this.dataSource;
}
@Override
protected Sheet createSheet() {
Sheet sheet = new Sheet();
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
if (sheetSet == null) {
sheetSet = Sheet.createPropertiesSet();
sheet.put(sheetSet);
}
final String NO_DESCR = Bundle.CommonFilesSearchResultsViewerTable_noDescText();
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), NO_DESCR, this.getContent().getName()));
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), NO_DESCR, this.getContent().getParentPath()));
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), NO_DESCR, getHashSetHitsCsvList(this.getContent())));
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), NO_DESCR, this.getDataSource()));
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_mimeTypeColLbl(), Bundle.CommonFilesSearchResultsViewerTable_mimeTypeColLbl(), NO_DESCR, StringUtils.defaultString(this.getContent().getMIMEType())));
this.addTagProperty(sheetSet);
return sheet;
}
}

View File

@ -0,0 +1,137 @@
/*
*
* 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.commonfilesearch;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.DisplayableItemNode;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Represents that a row in the CR was found in multiple cases.
*
* Generates a DisplayableItmeNode using a CentralRepositoryFile.
*/
final public class CentralRepoCommonAttributeInstance extends AbstractCommonAttributeInstance {
private static final Logger LOGGER = Logger.getLogger(CentralRepoCommonAttributeInstance.class.getName());
private final Integer crFileId;
private CorrelationAttribute currentAttribute;
private final Map<String, Long> dataSourceNameToIdMap;
CentralRepoCommonAttributeInstance(Integer attrInstId, Map<Long, String> dataSourceIdToNameMap) {
super();
this.crFileId = attrInstId;
this.dataSourceNameToIdMap = invertMap(dataSourceIdToNameMap);
}
void setCurrentAttributeInst(CorrelationAttribute attribute) {
this.currentAttribute = attribute;
}
@Override
AbstractFile getAbstractFile() {
Case currentCase;
if (this.currentAttribute != null) {
final CorrelationAttributeInstance currentAttributeInstance = this.currentAttribute.getInstances().get(0);
String currentFullPath = currentAttributeInstance.getFilePath();
String currentDataSource = currentAttributeInstance.getCorrelationDataSource().getName();
if(this.dataSourceNameToIdMap.containsKey(currentDataSource)){
Long dataSourceObjectId = this.dataSourceNameToIdMap.get(currentDataSource);
try {
currentCase = Case.getCurrentCaseThrows();
SleuthkitCase tskDb = currentCase.getSleuthkitCase();
File fileFromPath = new File(currentFullPath);
String fileName = fileFromPath.getName();
String parentPath = (fileFromPath.getParent() + File.separator).replace("\\", "/");
final String whereClause = String.format("lower(name) = '%s' AND md5 = '%s' AND lower(parent_path) = '%s' AND data_source_obj_id = %s", fileName, currentAttribute.getCorrelationValue(), parentPath, dataSourceObjectId);
List<AbstractFile> potentialAbstractFiles = tskDb.findAllFilesWhere(whereClause);
if(potentialAbstractFiles.isEmpty()){
return null;
} else if(potentialAbstractFiles.size() > 1){
LOGGER.log(Level.WARNING, String.format("Unable to find an exact match for AbstractFile for record with filePath: %s. May have returned the wrong file.", new Object[]{currentFullPath}));
return potentialAbstractFiles.get(0);
} else {
return potentialAbstractFiles.get(0);
}
} catch (TskCoreException | NoCurrentCaseException ex) {
LOGGER.log(Level.SEVERE, String.format("Unable to find AbstractFile for record with filePath: %s. Node not created.", new Object[]{currentFullPath}), ex);
return null;
}
} else {
return null;
}
}
return null;
}
@Override
public DisplayableItemNode[] generateNodes() {
// @@@ We should be doing more of this work in teh generateKeys method. We want to do as little as possible in generateNodes
InterCaseSearchResultsProcessor eamDbAttrInst = new InterCaseSearchResultsProcessor();
CorrelationAttribute corrAttr = eamDbAttrInst.findSingleCorrelationAttribute(crFileId);
List<DisplayableItemNode> attrInstNodeList = new ArrayList<>(0);
String currCaseDbName = Case.getCurrentCase().getDisplayName();
try {
this.setCurrentAttributeInst(corrAttr);
AbstractFile abstractFileForAttributeInstance = this.getAbstractFile();
DisplayableItemNode generatedInstNode = AbstractCommonAttributeInstance.createNode(corrAttr, abstractFileForAttributeInstance, currCaseDbName);
attrInstNodeList.add(generatedInstNode);
} catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, String.format("Unable to get DataSource for record with md5: %s. Node not created.", new Object[]{corrAttr.getCorrelationValue()}), ex);
}
return attrInstNodeList.toArray(new DisplayableItemNode[attrInstNodeList.size()]);
}
private Map<String, Long> invertMap(Map<Long, String> dataSourceIdToNameMap) {
HashMap<String, Long> invertedMap = new HashMap<>();
for (Map.Entry<Long, String> entry : dataSourceIdToNameMap.entrySet()){
invertedMap.put(entry.getValue(), entry.getKey());
}
return invertedMap;
}
}

View File

@ -0,0 +1,116 @@
/*
*
* 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.commonfilesearch;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.swing.Action;
import org.openide.nodes.Children;
import org.openide.nodes.Sheet;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.datamodel.DisplayableItemNode;
import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor;
import org.sleuthkit.autopsy.datamodel.NodeProperty;
/**
* Used by the Common Files search feature to encapsulate instances of a given
* MD5s matched in the search. These nodes will be children of <code>Md5Node</code>s.
*
* Use this type for files which are not in the current case, but from the
* Central Repo. Contrast with <code>SleuthkitCase</code> which should be used
* when the FileInstance was found in the case presently open in Autopsy.
*/
public class CentralRepoCommonAttributeInstanceNode extends DisplayableItemNode {
private final CorrelationAttributeInstance crFile;
CentralRepoCommonAttributeInstanceNode(CorrelationAttributeInstance content) {
super(Children.LEAF, Lookups.fixed(content));
this.crFile = content;
this.setDisplayName(new File(this.crFile.getFilePath()).getName());
}
public CorrelationAttributeInstance getCorrelationAttributeInstance(){
return this.crFile;
}
@Override
public Action[] getActions(boolean context){
List<Action> actionsList = new ArrayList<>();
actionsList.addAll(Arrays.asList(super.getActions(true)));
return actionsList.toArray(new Action[actionsList.size()]);
}
@Override
public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
return visitor.visit(this);
}
@Override
public boolean isLeafTypeNode() {
return true;
}
@Override
public String getItemType() {
//objects of type FileNode will co-occur in the treetable with objects
// of this type and they will need to provide the same key
return CaseDBCommonAttributeInstanceNode.class.getName();
}
@Override
protected Sheet createSheet(){
Sheet sheet = new Sheet();
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
if(sheetSet == null){
sheetSet = Sheet.createPropertiesSet();
sheet.put(sheetSet);
}
final CorrelationAttributeInstance centralRepoFile = this.getCorrelationAttributeInstance();
final String fullPath = centralRepoFile.getFilePath();
final File file = new File(fullPath);
final String caseName = centralRepoFile.getCorrelationCase().getDisplayName();
final String name = file.getName();
final String parent = file.getParent();
final String dataSourceName = centralRepoFile.getCorrelationDataSource().getName();
final String NO_DESCR = Bundle.CommonFilesSearchResultsViewerTable_noDescText();
sheetSet.put(new NodeProperty<>(org.sleuthkit.autopsy.commonfilesearch.Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), org.sleuthkit.autopsy.commonfilesearch.Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), NO_DESCR, name));
sheetSet.put(new NodeProperty<>(org.sleuthkit.autopsy.commonfilesearch.Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), org.sleuthkit.autopsy.commonfilesearch.Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), NO_DESCR, parent));
sheetSet.put(new NodeProperty<>(org.sleuthkit.autopsy.commonfilesearch.Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), org.sleuthkit.autopsy.commonfilesearch.Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), NO_DESCR, ""));
sheetSet.put(new NodeProperty<>(org.sleuthkit.autopsy.commonfilesearch.Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), org.sleuthkit.autopsy.commonfilesearch.Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), NO_DESCR, dataSourceName));
sheetSet.put(new NodeProperty<>(org.sleuthkit.autopsy.commonfilesearch.Bundle.CommonFilesSearchResultsViewerTable_mimeTypeColLbl(), org.sleuthkit.autopsy.commonfilesearch.Bundle.CommonFilesSearchResultsViewerTable_mimeTypeColLbl(), NO_DESCR, ""));
sheetSet.put(new NodeProperty<>(org.sleuthkit.autopsy.commonfilesearch.Bundle.CommonFilesSearchResultsViewerTable_caseColLbl1(), org.sleuthkit.autopsy.commonfilesearch.Bundle.CommonFilesSearchResultsViewerTable_caseColLbl1(), NO_DESCR, caseName));
return sheet;
}
}

View File

@ -0,0 +1,301 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JDialogFormInfo">
<NonVisualComponents>
<Component class="javax.swing.ButtonGroup" name="fileTypeFilterButtonGroup">
</Component>
<Component class="javax.swing.ButtonGroup" name="interIntraButtonGroup">
</Component>
</NonVisualComponents>
<Properties>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[412, 350]"/>
</Property>
<Property name="resizable" type="boolean" value="false"/>
</Properties>
<SyntheticProperties>
<SyntheticProperty name="formSizePolicy" type="int" value="2"/>
</SyntheticProperties>
<Events>
<EventHandler event="windowClosed" listener="java.awt.event.WindowListener" parameters="java.awt.event.WindowEvent" handler="formWindowClosed"/>
</Events>
<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"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,123,0,0,1,-57"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
<SubComponents>
<Container class="javax.swing.JPanel" name="jPanel1">
<Properties>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[412, 350]"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
<BorderConstraints direction="Center"/>
</Constraint>
</Constraints>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="searchButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="cancelButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="errorText" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="commonFilesSearchLabel2" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="intraCaseRadio" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="interCaseRadio" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="commonFilesSearchLabel1" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="categoriesLabel" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="selectedFileCategoriesButton" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="35" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="documentsCheckbox" min="-2" max="-2" attributes="0"/>
<Component id="pictureVideoCheckbox" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="allFileCategoriesRadioButton" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="20" max="-2" attributes="0"/>
<Component id="layoutPanel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="10" max="-2" attributes="0"/>
</Group>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="commonFilesSearchLabel1" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="commonFilesSearchLabel2" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="intraCaseRadio" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="interCaseRadio" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="79" max="-2" attributes="0"/>
<Component id="categoriesLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="selectedFileCategoriesButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="pictureVideoCheckbox" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="documentsCheckbox" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="allFileCategoriesRadioButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="searchButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="cancelButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="errorText" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<EmptySpace min="-2" pref="98" max="-2" attributes="0"/>
<Component id="layoutPanel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="180" max="-2" attributes="0"/>
</Group>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="commonFilesSearchLabel2">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonAttributePanel.commonFilesSearchLabel2.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="focusable" type="boolean" value="false"/>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="searchButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonAttributePanel.searchButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
<Property name="horizontalTextPosition" type="int" value="10"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="searchButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="cancelButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonAttributePanel.cancelButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="actionCommand" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonAttributePanel.cancelButton.actionCommand" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="horizontalTextPosition" type="int" value="10"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cancelButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JRadioButton" name="allFileCategoriesRadioButton">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="fileTypeFilterButtonGroup"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonAttributePanel.allFileCategoriesRadioButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonAttributePanel.allFileCategoriesRadioButton.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="allFileCategoriesRadioButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JRadioButton" name="selectedFileCategoriesButton">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="fileTypeFilterButtonGroup"/>
</Property>
<Property name="selected" type="boolean" value="true"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonAttributePanel.selectedFileCategoriesButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonAttributePanel.selectedFileCategoriesButton.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="selectedFileCategoriesButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JCheckBox" name="pictureVideoCheckbox">
<Properties>
<Property name="selected" type="boolean" value="true"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonAttributePanel.pictureVideoCheckbox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="pictureVideoCheckboxActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JCheckBox" name="documentsCheckbox">
<Properties>
<Property name="selected" type="boolean" value="true"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonAttributePanel.documentsCheckbox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="documentsCheckboxActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="categoriesLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonAttributePanel.categoriesLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="name" type="java.lang.String" value="" noResource="true"/>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="errorText">
<Properties>
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="0" green="0" red="ff" type="rgb"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonAttributePanel.errorText.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="commonFilesSearchLabel1">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonAttributePanel.commonFilesSearchLabel1.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="focusable" type="boolean" value="false"/>
</Properties>
</Component>
<Component class="javax.swing.JRadioButton" name="intraCaseRadio">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="interIntraButtonGroup"/>
</Property>
<Property name="selected" type="boolean" value="true"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonAttributePanel.intraCaseRadio.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="intraCaseRadioActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JRadioButton" name="interCaseRadio">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="interIntraButtonGroup"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonFilesPanel.jRadioButton2.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="interCaseRadioActionPerformed"/>
</Events>
</Component>
<Container class="java.awt.Panel" name="layoutPanel">
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignCardLayout"/>
<SubComponents>
<Component class="org.sleuthkit.autopsy.commonfilesearch.IntraCasePanel" name="intraCasePanel">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignCardLayout" value="org.netbeans.modules.form.compat2.layouts.DesignCardLayout$CardConstraintsDescription">
<CardConstraints cardName="card3"/>
</Constraint>
</Constraints>
</Component>
<Component class="org.sleuthkit.autopsy.commonfilesearch.InterCasePanel" name="interCasePanel">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignCardLayout" value="org.netbeans.modules.form.compat2.layouts.DesignCardLayout$CardConstraintsDescription">
<CardConstraints cardName="card2"/>
</Constraint>
</Constraints>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Container>
</SubComponents>
</Form>

View File

@ -0,0 +1,716 @@
/*
* 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.commonfilesearch;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import javax.swing.JFrame;
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.openide.windows.WindowManager;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
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.corecomponentinterfaces.DataResultViewer;
import org.sleuthkit.autopsy.corecomponents.DataResultTopComponent;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
import org.sleuthkit.autopsy.corecomponents.TableFilterNode;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.directorytree.DataResultFilterNode;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Panel used for common files search configuration and configuration business
* logic. Nested within CommonFilesDialog.
*/
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
public final class CommonAttributePanel extends javax.swing.JDialog {
private static final long serialVersionUID = 1L;
private static final Long NO_DATA_SOURCE_SELECTED = -1L;
private static final Logger LOGGER = Logger.getLogger(CommonAttributePanel.class.getName());
private boolean pictureViewCheckboxState;
private boolean documentsCheckboxState;
/**
* Creates new form CommonFilesPanel
*/
@NbBundle.Messages({
"CommonFilesPanel.title=Common Files Panel",
"CommonFilesPanel.exception=Unexpected Exception loading DataSources.",
"CommonFilesPanel.frame.title=Find Common Files",
"CommonFilesPanel.frame.msg=Find Common Files"})
public CommonAttributePanel() {
super(new JFrame(Bundle.CommonFilesPanel_frame_title()),
Bundle.CommonFilesPanel_frame_msg(), true);
initComponents();
this.setLocationRelativeTo(WindowManager.getDefault().getMainWindow());
this.errorText.setVisible(false);
this.setupDataSources();
if (CommonAttributePanel.isEamDbAvailable()) {
this.setupCases();
} else {
this.disableIntercaseSearch();
}
}
private static boolean isEamDbAvailable() {
try {
EamDb DbManager = EamDb.getInstance();
return DbManager != null;
} catch (EamDbException ex) {
LOGGER.log(Level.SEVERE, "Unexpected exception while checking for EamDB enabled.", ex);
}
return false;
}
private void disableIntercaseSearch() {
this.intraCaseRadio.setSelected(true);
this.interCaseRadio.setEnabled(false);
}
@NbBundle.Messages({
"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.",
"CommonFilesPanel.search.done.interupted=Something went wrong finding common files.",
"CommonFilesPanel.search.done.sqlException=Unable to query db for files or data sources."})
private void search() {
String pathText = Bundle.CommonFilesPanel_search_results_pathText();
new SwingWorker<CommonAttributeSearchResults, Void>() {
private String tabTitle;
private ProgressHandle progress;
private void setTitleForAllDataSources() {
this.tabTitle = Bundle.CommonFilesPanel_search_results_titleAll();
}
private void setTitleForSingleSource(Long dataSourceId) {
final String CommonFilesPanel_search_results_titleSingle = Bundle.CommonFilesPanel_search_results_titleSingle();
final Object[] dataSourceName = new Object[]{intraCasePanel.getDataSourceMap().get(dataSourceId)};
this.tabTitle = String.format(CommonFilesPanel_search_results_titleSingle, dataSourceName);
}
@Override
@SuppressWarnings({"BoxedValueEquality", "NumberEquality"})
protected CommonAttributeSearchResults doInBackground() throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException, Exception {
progress = ProgressHandle.createHandle(Bundle.CommonFilesPanel_search_done_searchProgressGathering());
progress.start();
progress.switchToIndeterminate();
Long dataSourceId = intraCasePanel.getSelectedDataSourceId();
Integer caseId = interCasePanel.getSelectedCaseId();
AbstractCommonAttributeSearcher builder;
CommonAttributeSearchResults metadata;
boolean filterByMedia = false;
boolean filterByDocuments = false;
if (selectedFileCategoriesButton.isSelected()) {
if (pictureVideoCheckbox.isSelected()) {
filterByMedia = true;
}
if (documentsCheckbox.isSelected()) {
filterByDocuments = true;
}
}
if (CommonAttributePanel.this.interCaseRadio.isSelected()) {
if (caseId == InterCasePanel.NO_CASE_SELECTED) {
builder = new AllInterCaseCommonAttributeSearcher(intraCasePanel.getDataSourceMap(), filterByMedia, filterByDocuments);
} else {
builder = new SingleInterCaseCommonAttributeSearcher(caseId, intraCasePanel.getDataSourceMap(), filterByMedia, filterByDocuments);
}
} else {
if (dataSourceId == CommonAttributePanel.NO_DATA_SOURCE_SELECTED) {
builder = new AllIntraCaseCommonAttributeSearcher(intraCasePanel.getDataSourceMap(), filterByMedia, filterByDocuments);
setTitleForAllDataSources();
} else {
builder = new SingleIntraCaseCommonAttributeSearcher(dataSourceId, intraCasePanel.getDataSourceMap(), filterByMedia, filterByDocuments);
setTitleForSingleSource(dataSourceId);
}
}
metadata = builder.findFiles();
this.tabTitle = builder.buildTabTitle();
return metadata;
}
@Override
protected void done() {
try {
super.done();
CommonAttributeSearchResults metadata = this.get();
CommonAttributeSearchResultRootNode commonFilesNode = new CommonAttributeSearchResultRootNode(metadata);
// -3969
DataResultFilterNode dataResultFilterNode = new DataResultFilterNode(commonFilesNode, ExplorerManager.find(CommonAttributePanel.this));
TableFilterNode tableFilterWithDescendantsNode = new TableFilterNode(dataResultFilterNode, 3);
DataResultViewerTable table = new CommonAttributesSearchResultsViewerTable();
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());
} catch (ExecutionException ex) {
String errorMessage;
Throwable inner = ex.getCause();
if (inner instanceof TskCoreException) {
LOGGER.log(Level.SEVERE, "Failed to load files from database.", ex);
errorMessage = Bundle.CommonFilesPanel_search_done_tskCoreException();
} else if (inner instanceof NoCurrentCaseException) {
LOGGER.log(Level.SEVERE, "Current case has been closed.", ex);
errorMessage = Bundle.CommonFilesPanel_search_done_noCurrentCaseException();
} else if (inner instanceof SQLException) {
LOGGER.log(Level.SEVERE, "Unable to query db for files.", ex);
errorMessage = Bundle.CommonFilesPanel_search_done_sqlException();
} else {
LOGGER.log(Level.SEVERE, "Unexpected exception while running Common Files Search.", ex);
errorMessage = Bundle.CommonFilesPanel_search_done_exception();
}
MessageNotifyUtil.Message.error(errorMessage);
}
}
}.execute();
}
/**
* Sets up the data sources dropdown and returns the data sources map for
* future usage.
*
* @return a mapping of data correlationCase ids to data correlationCase
* names
*/
@NbBundle.Messages({
"CommonFilesPanel.setupDataSources.done.tskCoreException=Unable to run query against DB.",
"CommonFilesPanel.setupDataSources.done.noCurrentCaseException=Unable to open case file.",
"CommonFilesPanel.setupDataSources.done.exception=Unexpected exception loading data sources.",
"CommonFilesPanel.setupDataSources.done.interupted=Something went wrong building the Common Files Search dialog box.",
"CommonFilesPanel.setupDataSources.done.sqlException=Unable to query db for data sources.",
"CommonFilesPanel.setupDataSources.updateUi.noDataSources=No data sources were found."})
private void setupDataSources() {
new SwingWorker<Map<Long, String>, Void>() {
private void updateUi() {
final Map<Long, String> dataSourceMap = CommonAttributePanel.this.intraCasePanel.getDataSourceMap();
String[] dataSourcesNames = new String[dataSourceMap.size()];
//only enable all this stuff if we actually have datasources
if (dataSourcesNames.length > 0) {
dataSourcesNames = dataSourceMap.values().toArray(dataSourcesNames);
CommonAttributePanel.this.intraCasePanel.setDataModel(new DataSourceComboBoxModel(dataSourcesNames));
boolean multipleDataSources = this.caseHasMultipleSources();
CommonAttributePanel.this.intraCasePanel.rigForMultipleDataSources(multipleDataSources);
//TODO this should be attached to the intra/inter radio buttons
CommonAttributePanel.this.setSearchButtonEnabled(true);
}
}
private boolean caseHasMultipleSources() {
return CommonAttributePanel.this.intraCasePanel.getDataSourceMap().size() > 2;
}
@Override
protected Map<Long, String> doInBackground() throws NoCurrentCaseException, TskCoreException, SQLException {
DataSourceLoader loader = new DataSourceLoader();
return loader.getDataSourceMap();
}
@Override
protected void done() {
try {
CommonAttributePanel.this.intraCasePanel.setDataSourceMap(this.get());
updateUi();
} catch (InterruptedException ex) {
LOGGER.log(Level.SEVERE, "Interrupted while building Common Files Search dialog.", ex);
MessageNotifyUtil.Message.error(Bundle.CommonFilesPanel_setupDataSources_done_interupted());
} catch (ExecutionException ex) {
String errorMessage;
Throwable inner = ex.getCause();
if (inner instanceof TskCoreException) {
LOGGER.log(Level.SEVERE, "Failed to load data sources from database.", ex);
errorMessage = Bundle.CommonFilesPanel_setupDataSources_done_tskCoreException();
} else if (inner instanceof NoCurrentCaseException) {
LOGGER.log(Level.SEVERE, "Current case has been closed.", ex);
errorMessage = Bundle.CommonFilesPanel_setupDataSources_done_noCurrentCaseException();
} else if (inner instanceof SQLException) {
LOGGER.log(Level.SEVERE, "Unable to query db for data sources.", ex);
errorMessage = Bundle.CommonFilesPanel_setupDataSources_done_sqlException();
} else {
LOGGER.log(Level.SEVERE, "Unexpected exception while building Common Files Search dialog panel.", ex);
errorMessage = Bundle.CommonFilesPanel_setupDataSources_done_exception();
}
MessageNotifyUtil.Message.error(errorMessage);
}
}
}.execute();
}
@NbBundle.Messages({
"CommonFilesPanel.setupCases.done.interruptedException=Something went wrong building the Common Files Search dialog box.",
"CommonFilesPanel.setupCases.done.exeutionException=Unexpected exception loading cases."})
private void setupCases() {
new SwingWorker<Map<Integer, String>, Void>() {
private void updateUi() {
final Map<Integer, String> caseMap = CommonAttributePanel.this.interCasePanel.getCaseMap();
String[] caseNames = new String[caseMap.size()];
if (caseNames.length > 0) {
caseNames = caseMap.values().toArray(caseNames);
CommonAttributePanel.this.interCasePanel.setCaseList(new DataSourceComboBoxModel(caseNames));
boolean multipleCases = this.centralRepoHasMultipleCases();
CommonAttributePanel.this.interCasePanel.rigForMultipleCases(multipleCases);
} else {
CommonAttributePanel.this.disableIntercaseSearch();
}
}
private Map<Integer, String> mapDataSources(List<CorrelationCase> cases) throws Exception {
Map<Integer, String> casemap = new HashMap<>();
CorrelationCase currentCorCase = EamDb.getInstance().getCase(Case.getCurrentCase());
for (CorrelationCase correlationCase : cases) {
if (currentCorCase.getID() != correlationCase.getID()) { // if not the current Case
casemap.put(correlationCase.getID(), correlationCase.getDisplayName());
}
}
return casemap;
}
@Override
protected Map<Integer, String> doInBackground() throws Exception {
List<CorrelationCase> dataSources = EamDb.getInstance().getCases();
Map<Integer, String> caseMap = mapDataSources(dataSources);
return caseMap;
}
@Override
protected void done() {
try {
Map<Integer, String> cases = this.get();
CommonAttributePanel.this.interCasePanel.setCaseMap(cases);
this.updateUi();
} catch (InterruptedException ex) {
LOGGER.log(Level.SEVERE, "Interrupted while building Common Files Search dialog.", ex);
MessageNotifyUtil.Message.error(Bundle.CommonFilesPanel_setupCases_done_interruptedException());
} catch (ExecutionException ex) {
LOGGER.log(Level.SEVERE, "Unexpected exception while building Common Files Search dialog.", ex);
MessageNotifyUtil.Message.error(Bundle.CommonFilesPanel_setupCases_done_exeutionException());
}
}
private boolean centralRepoHasMultipleCases() {
return CommonAttributePanel.this.interCasePanel.centralRepoHasMultipleCases();
}
}.execute();
}
/**
* 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() {
fileTypeFilterButtonGroup = new javax.swing.ButtonGroup();
interIntraButtonGroup = new javax.swing.ButtonGroup();
jPanel1 = new javax.swing.JPanel();
commonFilesSearchLabel2 = new javax.swing.JLabel();
searchButton = new javax.swing.JButton();
cancelButton = new javax.swing.JButton();
allFileCategoriesRadioButton = new javax.swing.JRadioButton();
selectedFileCategoriesButton = new javax.swing.JRadioButton();
pictureVideoCheckbox = new javax.swing.JCheckBox();
documentsCheckbox = new javax.swing.JCheckBox();
categoriesLabel = new javax.swing.JLabel();
errorText = new javax.swing.JLabel();
commonFilesSearchLabel1 = new javax.swing.JLabel();
intraCaseRadio = new javax.swing.JRadioButton();
interCaseRadio = new javax.swing.JRadioButton();
layoutPanel = new java.awt.Panel();
intraCasePanel = new org.sleuthkit.autopsy.commonfilesearch.IntraCasePanel();
interCasePanel = new org.sleuthkit.autopsy.commonfilesearch.InterCasePanel();
setMinimumSize(new java.awt.Dimension(412, 350));
setPreferredSize(new java.awt.Dimension(412, 350));
setResizable(false);
addWindowListener(new java.awt.event.WindowAdapter() {
public void windowClosed(java.awt.event.WindowEvent evt) {
formWindowClosed(evt);
}
});
jPanel1.setPreferredSize(new java.awt.Dimension(412, 350));
org.openide.awt.Mnemonics.setLocalizedText(commonFilesSearchLabel2, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.commonFilesSearchLabel2.text")); // NOI18N
commonFilesSearchLabel2.setFocusable(false);
org.openide.awt.Mnemonics.setLocalizedText(searchButton, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.searchButton.text")); // NOI18N
searchButton.setEnabled(false);
searchButton.setHorizontalTextPosition(javax.swing.SwingConstants.LEADING);
searchButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
searchButtonActionPerformed(evt);
}
});
org.openide.awt.Mnemonics.setLocalizedText(cancelButton, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.cancelButton.text")); // NOI18N
cancelButton.setActionCommand(org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.cancelButton.actionCommand")); // NOI18N
cancelButton.setHorizontalTextPosition(javax.swing.SwingConstants.LEADING);
cancelButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
cancelButtonActionPerformed(evt);
}
});
fileTypeFilterButtonGroup.add(allFileCategoriesRadioButton);
org.openide.awt.Mnemonics.setLocalizedText(allFileCategoriesRadioButton, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.allFileCategoriesRadioButton.text")); // NOI18N
allFileCategoriesRadioButton.setToolTipText(org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.allFileCategoriesRadioButton.toolTipText")); // NOI18N
allFileCategoriesRadioButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
allFileCategoriesRadioButtonActionPerformed(evt);
}
});
fileTypeFilterButtonGroup.add(selectedFileCategoriesButton);
selectedFileCategoriesButton.setSelected(true);
org.openide.awt.Mnemonics.setLocalizedText(selectedFileCategoriesButton, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.selectedFileCategoriesButton.text")); // NOI18N
selectedFileCategoriesButton.setToolTipText(org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.selectedFileCategoriesButton.toolTipText")); // NOI18N
selectedFileCategoriesButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
selectedFileCategoriesButtonActionPerformed(evt);
}
});
pictureVideoCheckbox.setSelected(true);
org.openide.awt.Mnemonics.setLocalizedText(pictureVideoCheckbox, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.pictureVideoCheckbox.text")); // NOI18N
pictureVideoCheckbox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
pictureVideoCheckboxActionPerformed(evt);
}
});
documentsCheckbox.setSelected(true);
org.openide.awt.Mnemonics.setLocalizedText(documentsCheckbox, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.documentsCheckbox.text")); // NOI18N
documentsCheckbox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
documentsCheckboxActionPerformed(evt);
}
});
org.openide.awt.Mnemonics.setLocalizedText(categoriesLabel, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.categoriesLabel.text")); // NOI18N
categoriesLabel.setName(""); // NOI18N
errorText.setForeground(new java.awt.Color(255, 0, 0));
org.openide.awt.Mnemonics.setLocalizedText(errorText, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.errorText.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(commonFilesSearchLabel1, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.commonFilesSearchLabel1.text")); // NOI18N
commonFilesSearchLabel1.setFocusable(false);
interIntraButtonGroup.add(intraCaseRadio);
intraCaseRadio.setSelected(true);
org.openide.awt.Mnemonics.setLocalizedText(intraCaseRadio, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.intraCaseRadio.text")); // NOI18N
intraCaseRadio.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
intraCaseRadioActionPerformed(evt);
}
});
interIntraButtonGroup.add(interCaseRadio);
org.openide.awt.Mnemonics.setLocalizedText(interCaseRadio, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonFilesPanel.jRadioButton2.text")); // NOI18N
interCaseRadio.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
interCaseRadioActionPerformed(evt);
}
});
layoutPanel.setLayout(new java.awt.CardLayout());
layoutPanel.add(intraCasePanel, "card3");
layoutPanel.add(interCasePanel, "card2");
javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
jPanel1.setLayout(jPanel1Layout);
jPanel1Layout.setHorizontalGroup(
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel1Layout.createSequentialGroup()
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel1Layout.createSequentialGroup()
.addContainerGap()
.addComponent(searchButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(cancelButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(errorText))
.addGroup(jPanel1Layout.createSequentialGroup()
.addContainerGap()
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(commonFilesSearchLabel2)
.addComponent(intraCaseRadio)
.addComponent(interCaseRadio)
.addComponent(commonFilesSearchLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(categoriesLabel)
.addComponent(selectedFileCategoriesButton)))
.addGroup(jPanel1Layout.createSequentialGroup()
.addGap(35, 35, 35)
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(documentsCheckbox)
.addComponent(pictureVideoCheckbox)))
.addGroup(jPanel1Layout.createSequentialGroup()
.addContainerGap()
.addComponent(allFileCategoriesRadioButton)))
.addContainerGap())
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel1Layout.createSequentialGroup()
.addGap(20, 20, 20)
.addComponent(layoutPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(10, 10, 10)))
);
jPanel1Layout.setVerticalGroup(
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel1Layout.createSequentialGroup()
.addContainerGap()
.addComponent(commonFilesSearchLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(commonFilesSearchLabel2)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(intraCaseRadio)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(interCaseRadio)
.addGap(79, 79, 79)
.addComponent(categoriesLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(selectedFileCategoriesButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(pictureVideoCheckbox)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(documentsCheckbox)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(allFileCategoriesRadioButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(searchButton)
.addComponent(cancelButton)
.addComponent(errorText))
.addContainerGap())
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup()
.addGap(98, 98, 98)
.addComponent(layoutPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(180, 180, 180)))
);
getContentPane().add(jPanel1, java.awt.BorderLayout.CENTER);
}// </editor-fold>//GEN-END:initComponents
private void searchButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchButtonActionPerformed
search();
SwingUtilities.windowForComponent(this).dispose();
}//GEN-LAST:event_searchButtonActionPerformed
private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed
SwingUtilities.windowForComponent(this).dispose();
}//GEN-LAST:event_cancelButtonActionPerformed
private void allFileCategoriesRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_allFileCategoriesRadioButtonActionPerformed
this.manageCheckBoxState();
this.toggleErrorTextAndSearchBox();
}//GEN-LAST:event_allFileCategoriesRadioButtonActionPerformed
private void selectedFileCategoriesButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_selectedFileCategoriesButtonActionPerformed
this.manageCheckBoxState();
}//GEN-LAST:event_selectedFileCategoriesButtonActionPerformed
private void pictureVideoCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pictureVideoCheckboxActionPerformed
this.toggleErrorTextAndSearchBox();
}//GEN-LAST:event_pictureVideoCheckboxActionPerformed
private void documentsCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_documentsCheckboxActionPerformed
this.toggleErrorTextAndSearchBox();
}//GEN-LAST:event_documentsCheckboxActionPerformed
private void intraCaseRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_intraCaseRadioActionPerformed
((java.awt.CardLayout) this.layoutPanel.getLayout()).first(this.layoutPanel);
handleIntraCaseSearchCriteriaChanged();
}//GEN-LAST:event_intraCaseRadioActionPerformed
public void handleIntraCaseSearchCriteriaChanged() {
if (this.areIntraCaseSearchCriteriaMet()) {
this.searchButton.setEnabled(true);
this.hideErrorMessages();
} else {
this.searchButton.setEnabled(false);
this.hideErrorMessages();
this.showIntraCaseErrorMessage();
}
}
private void interCaseRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_interCaseRadioActionPerformed
((java.awt.CardLayout) this.layoutPanel.getLayout()).last(this.layoutPanel);
handleInterCaseSearchCriteriaChanged();
}//GEN-LAST:event_interCaseRadioActionPerformed
private void formWindowClosed(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowClosed
SwingUtilities.windowForComponent(this).dispose();
}//GEN-LAST:event_formWindowClosed
public void handleInterCaseSearchCriteriaChanged() {
if (this.areInterCaseSearchCriteriaMet()) {
this.searchButton.setEnabled(true);
this.hideErrorMessages();
} else {
this.searchButton.setEnabled(false);
this.hideErrorMessages();
this.showInterCaseErrorMessage();
}
}
private void toggleErrorTextAndSearchBox() {
if (!this.pictureVideoCheckbox.isSelected() && !this.documentsCheckbox.isSelected() && !this.allFileCategoriesRadioButton.isSelected()) {
this.searchButton.setEnabled(false);
this.errorText.setVisible(true);
} else {
this.searchButton.setEnabled(true);
this.errorText.setVisible(false);
}
}
private void manageCheckBoxState() {
this.pictureViewCheckboxState = this.pictureVideoCheckbox.isSelected();
this.documentsCheckboxState = this.documentsCheckbox.isSelected();
if (this.allFileCategoriesRadioButton.isSelected()) {
this.pictureVideoCheckbox.setEnabled(false);
this.documentsCheckbox.setEnabled(false);
}
if (this.selectedFileCategoriesButton.isSelected()) {
this.pictureVideoCheckbox.setSelected(this.pictureViewCheckboxState);
this.documentsCheckbox.setSelected(this.documentsCheckboxState);
this.pictureVideoCheckbox.setEnabled(true);
this.documentsCheckbox.setEnabled(true);
this.toggleErrorTextAndSearchBox();
}
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JRadioButton allFileCategoriesRadioButton;
private javax.swing.JButton cancelButton;
private javax.swing.JLabel categoriesLabel;
private javax.swing.JLabel commonFilesSearchLabel1;
private javax.swing.JLabel commonFilesSearchLabel2;
private javax.swing.JCheckBox documentsCheckbox;
private javax.swing.JLabel errorText;
private javax.swing.ButtonGroup fileTypeFilterButtonGroup;
private org.sleuthkit.autopsy.commonfilesearch.InterCasePanel interCasePanel;
private javax.swing.JRadioButton interCaseRadio;
private javax.swing.ButtonGroup interIntraButtonGroup;
private org.sleuthkit.autopsy.commonfilesearch.IntraCasePanel intraCasePanel;
private javax.swing.JRadioButton intraCaseRadio;
private javax.swing.JPanel jPanel1;
private java.awt.Panel layoutPanel;
private javax.swing.JCheckBox pictureVideoCheckbox;
private javax.swing.JButton searchButton;
private javax.swing.JRadioButton selectedFileCategoriesButton;
// End of variables declaration//GEN-END:variables
void setSearchButtonEnabled(boolean enabled) {
this.searchButton.setEnabled(enabled);
}
private boolean areIntraCaseSearchCriteriaMet() {
return this.intraCasePanel.areSearchCriteriaMet();
}
private boolean areInterCaseSearchCriteriaMet() {
return this.interCasePanel.areSearchCriteriaMet();
}
private void hideErrorMessages() {
this.errorText.setVisible(false);
}
private void showIntraCaseErrorMessage() {
this.errorText.setText(this.intraCasePanel.getErrorMessage());
this.errorText.setVisible(true);
}
private void showInterCaseErrorMessage() {
this.errorText.setText(this.interCasePanel.getErrorMessage());
this.errorText.setVisible(true);
}
}

View File

@ -18,7 +18,6 @@
*/
package org.sleuthkit.autopsy.commonfilesearch;
import org.sleuthkit.autopsy.datamodel.InstanceCountNode;
import java.util.List;
import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children;
@ -28,12 +27,14 @@ import org.sleuthkit.autopsy.datamodel.DisplayableItemNode;
import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor;
/**
* Wrapper node for <code>Md5Node</code> used to display common files search
* results in the top right pane. Calls <code>Md5NodeFactory</code>.
* Top-level node to store common file search results. Current structure is:
* - node for number of matches
* -- node for MD5/commmon attribute
* --- node for instance.
*/
final public class CommonFilesNode extends DisplayableItemNode {
final public class CommonAttributeSearchResultRootNode extends DisplayableItemNode {
CommonFilesNode(CommonFilesMetadata metadataList) {
CommonAttributeSearchResultRootNode(CommonAttributeSearchResults metadataList) {
super(Children.create(new InstanceCountNodeFactory(metadataList), true));
}
@ -58,25 +59,33 @@ final public class CommonFilesNode extends DisplayableItemNode {
public String getItemType() {
return getClass().getName();
}
/**
* Used to generate <code>InstanceCountNode</code>s.
*/
static class InstanceCountNodeFactory extends ChildFactory<Integer>{
static class InstanceCountNodeFactory extends ChildFactory<Integer> {
private final CommonFilesMetadata metadata;
InstanceCountNodeFactory(CommonFilesMetadata metadata) {
this.metadata = metadata;
private final CommonAttributeSearchResults searchResults;
/**
* Build a factory which converts a <code>CommonAttributeSearchResults</code>
* object into <code>DisplayableItemNode</code>s.
* @param searchResults
*/
InstanceCountNodeFactory(CommonAttributeSearchResults searchResults){
this.searchResults = searchResults;
}
@Override
protected boolean createKeys(List<Integer> list) {
list.addAll(this.metadata.getMetadata().keySet());
list.addAll(this.searchResults.getMetadata().keySet());
return true;
}
@Override
protected Node createNodeForKey(Integer instanceCount) {
Md5MetadataList md5Metadata = this.metadata.getMetadataForMd5(instanceCount);
return new InstanceCountNode(instanceCount, md5Metadata);
protected Node createNodeForKey(Integer instanceCount){
CommonAttributeValueList attributeValues = this.searchResults.getAttributeValuesForInstanceCount(instanceCount);
return new InstanceCountNode(instanceCount, attributeValues);
}
}
}

View File

@ -0,0 +1,82 @@
/*
*
* 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.commonfilesearch;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* Stores the results from the various types of common attribute searching
* Stores results based on how they are currently displayed in the UI
*/
final public class CommonAttributeSearchResults {
// maps instance count to list of attribute values.
private final Map<Integer, CommonAttributeValueList> instanceCountToAttributeValues;
/**
* Create a values object which can be handed off to the node factories.
*
* @param values list of CommonAttributeValue indexed by size of
* CommonAttributeValue
*/
CommonAttributeSearchResults(Map<Integer, CommonAttributeValueList> metadata){
this.instanceCountToAttributeValues = metadata;
}
/**
* Find the child node whose children have the specified number of children.
*
* This is a convenience method - you can also iterate over
* <code>getValues()</code>.
*
* @param isntanceCound key
* @return list of values which represent matches
*/
CommonAttributeValueList getAttributeValuesForInstanceCount(Integer instanceCount) {
return this.instanceCountToAttributeValues.get(instanceCount);
}
/**
* Get an unmodifiable collection of values, indexed by number of
* grandchildren, which represents the common attributes found in the
* search.
* @return map of sizes of children to list of matches
*/
public Map<Integer, CommonAttributeValueList> getMetadata() {
return Collections.unmodifiableMap(this.instanceCountToAttributeValues);
}
/**
* How many distinct common files exist for this search results?
* @return number of common files
*/
public int size() {
int count = 0;
for (CommonAttributeValueList data : this.instanceCountToAttributeValues.values()) {
for(CommonAttributeValue md5 : data.getMetadataList()){
count += md5.getInstanceCount();
}
}
return count;
}
}

View File

@ -19,52 +19,74 @@
*/
package org.sleuthkit.autopsy.commonfilesearch;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* Encapsulates data required to instantiate an <code>Md5Node</code>.
* Defines a value that was in the common file search results
* as well as information about its instances.
*/
final public class Md5Metadata {
final public class CommonAttributeValue {
private final String md5;
private final List<FileInstanceMetadata> fileInstances;
Md5Metadata(String md5, List<FileInstanceMetadata> fileInstances){
private final List<AbstractCommonAttributeInstance> fileInstances;
CommonAttributeValue(String md5, List<AbstractCommonAttributeInstance> fileInstances) {
this.md5 = md5;
this.fileInstances = fileInstances;
}
public String getMd5(){
CommonAttributeValue(String md5) {
this.md5 = md5;
this.fileInstances = new ArrayList<>();
}
public String getValue() {
return this.md5;
}
void addFileInstanceMetadata(FileInstanceMetadata metadata){
this.fileInstances.add(metadata);
}
public Collection<FileInstanceMetadata> getMetadata(){
return Collections.unmodifiableCollection(this.fileInstances);
}
/**
* How many distinct file instances exist for the MD5 represented by this object?
* @return number of instances
* concatenate cases this value was seen into a single string
*
* @return
*/
public int size(){
return this.fileInstances.size();
public String getCases() {
final String cases = this.fileInstances.stream().map(AbstractCommonAttributeInstance::getCaseName).collect(Collectors.joining(", "));
return cases;
}
public String getDataSources() {
Set<String> sources = new HashSet<> ();
for(FileInstanceMetadata data : this.fileInstances){
sources.add(data.getDataSourceName());
Set<String> sources = new HashSet<>();
for (AbstractCommonAttributeInstance data : this.fileInstances) {
sources.add(data.getDataSource());
}
return String.join(", ", sources);
final String dataSources = String.join(", ", sources);
return dataSources;
}
void addInstance(AbstractCommonAttributeInstance metadata) {
this.fileInstances.add(metadata);
}
public Collection<AbstractCommonAttributeInstance> getInstances() {
return Collections.unmodifiableCollection(this.fileInstances);
}
/**
* How many distinct file instances exist for the MD5 represented by this
* object?
*
* @return number of instances
*/
public int getInstanceCount() {
return this.fileInstances.size();
}

View File

@ -28,10 +28,10 @@ import java.util.List;
* results. Subclass this to implement different selections of files from the
* case.
*/
final public class Md5MetadataList {
final public class CommonAttributeValueList {
private final List<Md5Metadata> metadataList;
private final List<Md5Metadata> delayedMetadataList;
private final List<CommonAttributeValue> metadataList;
private final List<CommonAttributeValue> delayedMetadataList;
/**
* Create a metadata object containing the list of metadata which can be
@ -39,17 +39,17 @@ final public class Md5MetadataList {
*
* @param metadata list of Md5Metadata indexed by size of Md5Metadata
*/
Md5MetadataList(List<Md5Metadata> metadata) {
CommonAttributeValueList(List<CommonAttributeValue> metadata) {
this.metadataList = new ArrayList<>();
this.delayedMetadataList = metadata;
}
Md5MetadataList() {
CommonAttributeValueList() {
this.metadataList = new ArrayList<>();
this.delayedMetadataList = new ArrayList<>();
}
public List<Md5Metadata> getMetadataList() {
public List<CommonAttributeValue> getMetadataList() {
return Collections.unmodifiableList(this.metadataList);
}
@ -59,7 +59,7 @@ final public class Md5MetadataList {
}
}
public void addMetadataToList(Md5Metadata metadata) {
public void addMetadataToList(CommonAttributeValue metadata) {
delayedMetadataList.add(metadata);
}
}

View File

@ -0,0 +1,155 @@
/*
*
* 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.commonfilesearch;
import java.util.List;
import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.nodes.Sheet;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.DisplayableItemNode;
import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor;
import org.sleuthkit.autopsy.datamodel.NodeProperty;
/**
* Represents the layer in the tree for the value (such as MD5) that was in multiple places.
* Children are instances of that value.
*/
public class CommonAttributeValueNode extends DisplayableItemNode {
private static final Logger LOGGER = Logger.getLogger(CommonAttributeValueNode.class.getName());
private final String value;
private final int commonFileCount;
private final String cases;
private final String dataSources;
@NbBundle.Messages({
"Md5Node.Md5Node.format=MD5: %s"
})
/**
* Create a Match node whose children will all have this object in common.
* @param data the common feature, and the children
*/
public CommonAttributeValueNode(CommonAttributeValue data) {
super(Children.create(
new FileInstanceNodeFactory(data), true));
this.commonFileCount = data.getInstanceCount();
this.cases = data.getCases();
// @@ We seem to be doing this string concat twice. We also do it in getDataSources()
this.dataSources = String.join(", ", data.getDataSources());
this.value = data.getValue();
this.setDisplayName(String.format(Bundle.Md5Node_Md5Node_format(), this.value));
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/fileset-icon-16.png"); //NON-NLS
}
/**
* How many files are in common? This will be the number of children.
* @return int
*/
int getCommonFileCount() {
return this.commonFileCount;
}
String getCases(){
return this.cases;
}
/**
* Datasources where these matches occur.
* @return string delimited list of sources
*/
String getDataSources() {
return this.dataSources;
}
/**
* MD5 which is common to these matches
* @return string md5 hash
*/
public String getValue() {
return this.value;
}
@NbBundle.Messages({"Md5Node.createSheet.noDescription= "})
@Override
protected Sheet createSheet() {
Sheet sheet = new Sheet();
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
if (sheetSet == null) {
sheetSet = Sheet.createPropertiesSet();
sheet.put(sheetSet);
}
final String NO_DESCR = Bundle.Md5Node_createSheet_noDescription();
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), NO_DESCR, ""));
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), NO_DESCR, ""));
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), NO_DESCR, ""));
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), NO_DESCR, this.getDataSources()));
return sheet;
}
@Override
public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
return visitor.visit(this);
}
@Override
public boolean isLeafTypeNode() {
return false;
}
@Override
public String getItemType() {
return getClass().getName();
}
/**
* Child generator for <code>SleuthkitCaseFileInstanceNode</code> of
* <code>CommonAttributeValueNode</code>.
*/
static class FileInstanceNodeFactory extends ChildFactory<AbstractCommonAttributeInstance> {
private final CommonAttributeValue descendants;
FileInstanceNodeFactory(CommonAttributeValue descendants) {
this.descendants = descendants;
}
@Override
protected boolean createKeys(List<AbstractCommonAttributeInstance> list) {
list.addAll(this.descendants.getInstances());
return true;
}
@Override
protected Node[] createNodesForKey(AbstractCommonAttributeInstance searchResult) {
return searchResult.generateNodes();
}
}
}

View File

@ -0,0 +1,100 @@
/*
*
* 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.commonfilesearch;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
import org.sleuthkit.autopsy.corecomponents.DelayedLoadChildNodesOnTreeExpansion;
/**
* <code>DataResultViewerTable</code> which overrides the default column
* header width calculations. The <code>CommonAttributesSearchResultsViewerTable</code>
* presents multiple tiers of data which are not always present and it may not
* make sense to try to calculate the column widths for such tables by sampling
* rows and looking for wide cells. Rather, we just pick some reasonable values.
*/
public class CommonAttributesSearchResultsViewerTable extends DataResultViewerTable {
private static final Map<String, Integer> COLUMN_WIDTHS;
private static final long serialVersionUID = 1L;
private static final Logger LOGGER = Logger.getLogger(CommonAttributesSearchResultsViewerTable.class.getName());
private static final int DEFAULT_WIDTH = 100;
static {
Map<String, Integer> map = new HashMap<>();
map.put(Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), 260);
map.put(Bundle.CommonFilesSearchResultsViewerTable_instancesColLbl(), 65);
map.put(Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), 300);
map.put(Bundle.CommonFilesSearchResultsViewerTable_caseColLbl1(), 200);
map.put(Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), 200);
map.put(Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), 100);
map.put(Bundle.CommonFilesSearchResultsViewerTable_mimeTypeColLbl(), 130);
map.put(Bundle.CommonFilesSearchResultsViewerTable_tagsColLbl1(), 300);;
COLUMN_WIDTHS = Collections.unmodifiableMap(map);
}
public CommonAttributesSearchResultsViewerTable() {
super(new DelayedLoadChildNodesOnTreeExpansion());
}
@NbBundle.Messages({
"CommonFilesSearchResultsViewerTable.noDescText= ",
"CommonFilesSearchResultsViewerTable.filesColLbl=Files",
"CommonFilesSearchResultsViewerTable.instancesColLbl=Instances",
"CommonFilesSearchResultsViewerTable.pathColLbl=Parent Path",
"CommonFilesSearchResultsViewerTable.hashsetHitsColLbl=Hash Set Hits",
"CommonFilesSearchResultsViewerTable.caseColLbl1=Case",
"CommonFilesSearchResultsViewerTable.dataSourceColLbl=Data Source",
"CommonFilesSearchResultsViewerTable.mimeTypeColLbl=MIME Type",
"CommonFilesSearchResultsViewerTable.tagsColLbl1=Tags"
})
@Override
protected void setColumnWidths() {
TableColumnModel model = this.getColumnModel();
Enumeration<TableColumn> columnsEnumerator = model.getColumns();
while (columnsEnumerator.hasMoreElements()) {
TableColumn column = columnsEnumerator.nextElement();
final String headerValue = column.getHeaderValue().toString();
final Integer defaultWidth = COLUMN_WIDTHS.get(headerValue);
if(defaultWidth == null){
column.setPreferredWidth(DEFAULT_WIDTH);
LOGGER.log(Level.SEVERE, String.format("Tried to set width on a column not supported by the CommonFilesSearchResultsViewerTable: %s", headerValue));
} else {
column.setPreferredWidth(defaultWidth);
}
}
}
}

View File

@ -1,51 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JDialogFormInfo">
<Properties>
<Property name="defaultCloseOperation" type="int" value="2"/>
<Property name="size" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[340, 320]"/>
</Property>
</Properties>
<SyntheticProperties>
<SyntheticProperty name="formSizePolicy" type="int" value="1"/>
<SyntheticProperty name="generateCenter" type="boolean" value="true"/>
</SyntheticProperties>
<Events>
<EventHandler event="windowClosed" listener="java.awt.event.WindowListener" parameters="java.awt.event.WindowEvent" handler="formWindowClosed"/>
</Events>
<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" alignment="0" attributes="0">
<Component id="commonFilesPanel1" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="commonFilesPanel1" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="org.sleuthkit.autopsy.commonfilesearch.CommonFilesPanel" name="commonFilesPanel1">
</Component>
</SubComponents>
</Form>

View File

@ -1,94 +0,0 @@
/*
* 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.commonfilesearch;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import org.openide.util.NbBundle;
import org.openide.windows.WindowManager;
/**
* Dialog box for configuring and running common files search.
*/
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
public final class CommonFilesDialog extends javax.swing.JDialog {
private static final long serialVersionUID = 1L;
/**
* Creates new form CommonFilesDialog
*/
@NbBundle.Messages({
"CommonFilesDialog.frame.title=Find Common Files",
"CommonFilesDialog.frame.msg=Find Common Files"})
public CommonFilesDialog() {
super(new JFrame(Bundle.CommonFilesDialog_frame_title()),
Bundle.CommonFilesDialog_frame_msg(), true);
initComponents();
this.setResizable(false);
this.setLocationRelativeTo(WindowManager.getDefault().getMainWindow());
}
/**
* 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() {
commonFilesPanel1 = new org.sleuthkit.autopsy.commonfilesearch.CommonFilesPanel();
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
setSize(new java.awt.Dimension(340, 320));
addWindowListener(new java.awt.event.WindowAdapter() {
public void windowClosed(java.awt.event.WindowEvent evt) {
formWindowClosed(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()
.addComponent(commonFilesPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 0, Short.MAX_VALUE))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(commonFilesPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 0, Short.MAX_VALUE))
);
pack();
setLocationRelativeTo(null);
}// </editor-fold>//GEN-END:initComponents
private void formWindowClosed(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowClosed
SwingUtilities.windowForComponent(this).dispose();
}//GEN-LAST:event_formWindowClosed
// Variables declaration - do not modify//GEN-BEGIN:variables
private org.sleuthkit.autopsy.commonfilesearch.CommonFilesPanel commonFilesPanel1;
// End of variables declaration//GEN-END:variables
}

View File

@ -1,74 +0,0 @@
/*
*
* 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.commonfilesearch;
import java.util.Collections;
import java.util.Map;
/**
* Utility and wrapper model around data required for Common Files Search results.
* Subclass this to implement different selections of files from the case.
*/
final public class CommonFilesMetadata {
private final Map<Integer, Md5MetadataList> metadata;
/**
* Create a metadata object which can be handed off to the node
* factories.
*
* @param metadata list of Md5Metadata indexed by size of Md5Metadata
*/
CommonFilesMetadata(Map<Integer, Md5MetadataList> metadata){
this.metadata = metadata;
}
/**
* Find the meta data for the given md5.
*
* This is a convenience method - you can also iterate over
* <code>getMetadata()</code>.
*
* @param md5 key
* @return
*/
Md5MetadataList getMetadataForMd5(Integer instanceCount) {
return this.metadata.get(instanceCount);
}
public Map<Integer, Md5MetadataList> getMetadata() {
return Collections.unmodifiableMap(this.metadata);
}
/**
* How many distinct file instances exist for this metadata?
* @return number of file instances
*/
public int size() {
int count = 0;
for (Md5MetadataList data : this.metadata.values()) {
for(Md5Metadata md5 : data.getMetadataList()){
count += md5.size();
}
}
return count;
}
}

View File

@ -1,277 +0,0 @@
/*
*
* 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.commonfilesearch;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.datamodel.HashUtility;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery;
import org.sleuthkit.datamodel.TskCoreException;
/**
*
* Generates a <code>List<CommonFilesMetadata></code> when
* <code>findCommonFiles()</code> is called, which organizes files by md5 to
* prepare to display in viewer.
*
* This entire thing runs on a background thread where exceptions are handled.
*/
@SuppressWarnings("PMD.AbstractNaming")
public abstract class CommonFilesMetadataBuilder {
private final Map<Long, String> dataSourceIdToNameMap;
private final boolean filterByMedia;
private final boolean filterByDoc;
private static final String filterByMimeTypesWhereClause = " and mime_type in (%s)"; //NON-NLS // where %s is csv list of mime_types to filter on
/*
* The set of the MIME types that will be checked for extension mismatches
* when checkType is ONLY_MEDIA.
* ".jpg", ".jpeg", ".png", ".psd", ".nef", ".tiff", ".bmp", ".tec"
* ".aaf", ".3gp", ".asf", ".avi", ".m1v", ".m2v", //NON-NLS
* ".m4v", ".mp4", ".mov", ".mpeg", ".mpg", ".mpe", ".mp4", ".rm", ".wmv", ".mpv", ".flv", ".swf"
*/
private static final Set<String> MEDIA_PICS_VIDEO_MIME_TYPES = Stream.of(
"image/bmp", //NON-NLS
"image/gif", //NON-NLS
"image/jpeg", //NON-NLS
"image/png", //NON-NLS
"image/tiff", //NON-NLS
"image/vnd.adobe.photoshop", //NON-NLS
"image/x-raw-nikon", //NON-NLS
"image/x-ms-bmp", //NON-NLS
"image/x-icon", //NON-NLS
"video/webm", //NON-NLS
"video/3gpp", //NON-NLS
"video/3gpp2", //NON-NLS
"video/ogg", //NON-NLS
"video/mpeg", //NON-NLS
"video/mp4", //NON-NLS
"video/quicktime", //NON-NLS
"video/x-msvideo", //NON-NLS
"video/x-flv", //NON-NLS
"video/x-m4v", //NON-NLS
"video/x-ms-wmv", //NON-NLS
"application/vnd.ms-asf", //NON-NLS
"application/vnd.rn-realmedia", //NON-NLS
"application/x-shockwave-flash" //NON-NLS
).collect(Collectors.toSet());
/*
* The set of the MIME types that will be checked for extension mismatches
* when checkType is ONLY_TEXT_FILES.
* ".doc", ".docx", ".odt", ".xls", ".xlsx", ".ppt", ".pptx"
* ".txt", ".rtf", ".log", ".text", ".xml"
* ".html", ".htm", ".css", ".js", ".php", ".aspx"
* ".pdf"
*/
private static final Set<String> TEXT_FILES_MIME_TYPES = Stream.of(
"text/plain", //NON-NLS
"application/rtf", //NON-NLS
"application/pdf", //NON-NLS
"text/css", //NON-NLS
"text/html", //NON-NLS
"text/csv", //NON-NLS
"application/json", //NON-NLS
"application/javascript", //NON-NLS
"application/xml", //NON-NLS
"text/calendar", //NON-NLS
"application/x-msoffice", //NON-NLS
"application/x-ooxml", //NON-NLS
"application/msword", //NON-NLS
"application/vnd.openxmlformats-officedocument.wordprocessingml.document", //NON-NLS
"application/vnd.ms-powerpoint", //NON-NLS
"application/vnd.openxmlformats-officedocument.presentationml.presentation", //NON-NLS
"application/vnd.ms-excel", //NON-NLS
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", //NON-NLS
"application/vnd.oasis.opendocument.presentation", //NON-NLS
"application/vnd.oasis.opendocument.spreadsheet", //NON-NLS
"application/vnd.oasis.opendocument.text" //NON-NLS
).collect(Collectors.toSet());
/**
* Subclass this to implement different algorithms for getting common files.
*
* @param dataSourceIdMap a map of obj_id to datasource name
* @param filterByMediaMimeType match only on files whose mime types can be
* broadly categorized as media types
* @param filterByDocMimeType match only on files whose mime types can be
* broadly categorized as document types
*/
CommonFilesMetadataBuilder(Map<Long, String> dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) {
dataSourceIdToNameMap = dataSourceIdMap;
filterByMedia = filterByMediaMimeType;
filterByDoc = filterByDocMimeType;
}
/**
* Use this as a prefix when building the SQL select statement.
*
* <ul>
* <li>You only have to specify the WHERE clause if you use this.</li>
* <li>If you do not use this string, you must use at least the columns
* selected below, in that order.</li>
* </ul>
*/
static final String SELECT_PREFIX = "SELECT obj_id, md5, data_source_obj_id from tsk_files where"; //NON-NLS
/**
* Should build a SQL SELECT statement to be passed to
* SleuthkitCase.executeQuery(sql) which will select the desired file ids
* and MD5 hashes.
*
* The statement should select obj_id, md5, data_source_obj_id in that
* order.
*
* @return sql string select statement
*/
protected abstract String buildSqlSelectStatement();
/**
* Generate a meta data object which encapsulates everything need to add the
* tree table tab to the top component.
*
* @return a data object with all of the matched files in a hierarchical
* format
* @throws TskCoreException
* @throws NoCurrentCaseException
* @throws SQLException
*/
public CommonFilesMetadata findCommonFiles() throws TskCoreException, NoCurrentCaseException, SQLException {
Map<String, Md5Metadata> commonFiles = new HashMap<>();
SleuthkitCase sleuthkitCase = Case.getCurrentCaseThrows().getSleuthkitCase();
String selectStatement = this.buildSqlSelectStatement();
try (
CaseDbQuery query = sleuthkitCase.executeQuery(selectStatement);
ResultSet resultSet = query.getResultSet()) {
while (resultSet.next()) {
Long objectId = resultSet.getLong(1);
String md5 = resultSet.getString(2);
Long dataSourceId = resultSet.getLong(3);
String dataSource = this.dataSourceIdToNameMap.get(dataSourceId);
if (md5 == null || HashUtility.isNoDataMd5(md5)) {
continue;
}
if (commonFiles.containsKey(md5)) {
final Md5Metadata md5Metadata = commonFiles.get(md5);
md5Metadata.addFileInstanceMetadata(new FileInstanceMetadata(objectId, dataSource));
} else {
final List<FileInstanceMetadata> fileInstances = new ArrayList<>();
fileInstances.add(new FileInstanceMetadata(objectId, dataSource));
Md5Metadata md5Metadata = new Md5Metadata(md5, fileInstances);
commonFiles.put(md5, md5Metadata);
}
}
}
Map<Integer, Md5MetadataList> instanceCollatedCommonFiles = new TreeMap<>();
for(Md5Metadata md5Metadata : commonFiles.values()){
Integer size = md5Metadata.size();
if(instanceCollatedCommonFiles.containsKey(size)){
instanceCollatedCommonFiles.get(size).addMetadataToList(md5Metadata);
} else {
Md5MetadataList metaList = new Md5MetadataList();
metaList.addMetadataToList(md5Metadata);
instanceCollatedCommonFiles.put(size, metaList);
}
}
return new CommonFilesMetadata(instanceCollatedCommonFiles);
}
/**
* Should be used by subclasses, in their
* <code>buildSqlSelectStatement()</code> function to create an SQL boolean
* expression which will filter our matches based on mime type. The
* expression will be conjoined to base query with an AND operator.
*
* @return sql fragment of the form:
* 'and "mime_type" in ( [comma delimited list of mime types] )'
* or empty string in the event that no types to filter on were given.
*/
String determineMimeTypeFilter() {
Set<String> mimeTypesToFilterOn = new HashSet<>();
String mimeTypeString = "";
if (filterByMedia) {
mimeTypesToFilterOn.addAll(MEDIA_PICS_VIDEO_MIME_TYPES);
}
if (filterByDoc) {
mimeTypesToFilterOn.addAll(TEXT_FILES_MIME_TYPES);
}
StringBuilder mimeTypeFilter = new StringBuilder(mimeTypesToFilterOn.size());
if (!mimeTypesToFilterOn.isEmpty()) {
for (String mimeType : mimeTypesToFilterOn) {
mimeTypeFilter.append("'").append(mimeType).append("',");
}
mimeTypeString = mimeTypeFilter.toString().substring(0, mimeTypeFilter.length() - 1);
mimeTypeString = String.format(filterByMimeTypesWhereClause, new Object[]{mimeTypeString});
}
return mimeTypeString;
}
@NbBundle.Messages({
"CommonFilesMetadataBuilder.buildTabTitle.titleAll=Common Files (All Data Sources, %s)",
"CommonFilesMetadataBuilder.buildTabTitle.titleSingle=Common Files (Match Within Data Source: %s, %s)"
})
protected abstract String buildTabTitle();
@NbBundle.Messages({
"CommonFilesMetadataBuilder.buildCategorySelectionString.doc=Documents",
"CommonFilesMetadataBuilder.buildCategorySelectionString.media=Media",
"CommonFilesMetadataBuilder.buildCategorySelectionString.all=All File Categories"
})
protected String buildCategorySelectionString() {
if (!this.filterByDoc && !this.filterByMedia) {
return Bundle.CommonFilesMetadataBuilder_buildCategorySelectionString_all();
} else {
List<String> filters = new ArrayList<>();
if (this.filterByDoc) {
filters.add(Bundle.CommonFilesMetadataBuilder_buildCategorySelectionString_doc());
}
if (this.filterByMedia) {
filters.add(Bundle.CommonFilesMetadataBuilder_buildCategorySelectionString_media());
}
return String.join(", ", filters);
}
}
}

View File

@ -1,261 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<NonVisualComponents>
<Component class="javax.swing.ButtonGroup" name="dataSourcesButtonGroup">
</Component>
<Component class="javax.swing.ButtonGroup" name="fileTypeFilterButtonGroup">
</Component>
</NonVisualComponents>
<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 max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" max="-2" attributes="0">
<Group type="102" alignment="1" attributes="0">
<Component id="errorText" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Component id="searchButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="cancelButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="categoriesLabel" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="commonFilesSearchLabel" min="-2" max="-2" attributes="0"/>
<Component id="dataSourceLabel" alignment="0" min="-2" max="-2" attributes="0"/>
<Group type="102" attributes="0">
<EmptySpace min="6" pref="6" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="29" max="-2" attributes="0"/>
<Component id="selectDataSourceComboBox" min="-2" pref="261" max="-2" attributes="0"/>
</Group>
<Component id="withinDataSourceRadioButton" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="allDataSourcesRadioButton" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="allFileCategoriesRadioButton" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="selectedFileCategoriesButton" alignment="0" min="-2" max="-2" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="21" pref="21" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="pictureVideoCheckbox" min="-2" max="-2" attributes="0"/>
<Component id="documentsCheckbox" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</Group>
</Group>
</Group>
<EmptySpace min="-2" pref="19" max="-2" attributes="0"/>
</Group>
</Group>
</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="commonFilesSearchLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="18" max="-2" attributes="0"/>
<Component id="dataSourceLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="allDataSourcesRadioButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="withinDataSourceRadioButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="selectDataSourceComboBox" min="-2" max="-2" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="categoriesLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="selectedFileCategoriesButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="pictureVideoCheckbox" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="documentsCheckbox" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="allFileCategoriesRadioButton" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="cancelButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="searchButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="errorText" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JButton" name="searchButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonFilesPanel.searchButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
<Property name="horizontalTextPosition" type="int" value="10"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="searchButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JRadioButton" name="allDataSourcesRadioButton">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="dataSourcesButtonGroup"/>
</Property>
<Property name="selected" type="boolean" value="true"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonFilesPanel.allDataSourcesRadioButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="horizontalAlignment" type="int" value="0"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="allDataSourcesRadioButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JRadioButton" name="withinDataSourceRadioButton">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="dataSourcesButtonGroup"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonFilesPanel.withinDataSourceRadioButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="horizontalAlignment" type="int" value="0"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="withinDataSourceRadioButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JComboBox" name="selectDataSourceComboBox">
<Properties>
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="dataSourcesList" type="code"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="selectDataSourceComboBoxActionPerformed"/>
</Events>
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
</AuxValues>
</Component>
<Component class="javax.swing.JLabel" name="commonFilesSearchLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonFilesPanel.commonFilesSearchLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="focusable" type="boolean" value="false"/>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="cancelButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonFilesPanel.cancelButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="actionCommand" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonFilesPanel.cancelButton.actionCommand" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="horizontalTextPosition" type="int" value="10"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cancelButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JRadioButton" name="allFileCategoriesRadioButton">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="fileTypeFilterButtonGroup"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonFilesPanel.allFileCategoriesRadioButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonFilesPanel.allFileCategoriesRadioButton.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="allFileCategoriesRadioButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JRadioButton" name="selectedFileCategoriesButton">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="fileTypeFilterButtonGroup"/>
</Property>
<Property name="selected" type="boolean" value="true"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonFilesPanel.selectedFileCategoriesButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonFilesPanel.selectedFileCategoriesButton.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="selectedFileCategoriesButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JCheckBox" name="pictureVideoCheckbox">
<Properties>
<Property name="selected" type="boolean" value="true"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonFilesPanel.pictureVideoCheckbox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="pictureVideoCheckboxActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JCheckBox" name="documentsCheckbox">
<Properties>
<Property name="selected" type="boolean" value="true"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonFilesPanel.documentsCheckbox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="documentsCheckboxActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="dataSourceLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonFilesPanel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="name" type="java.lang.String" value="" noResource="true"/>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="categoriesLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonFilesPanel.categoriesLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="name" type="java.lang.String" value="" noResource="true"/>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="errorText">
<Properties>
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="0" green="0" red="ff" type="rgb"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="CommonFilesPanel.errorText.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Form>

View File

@ -1,569 +0,0 @@
/*
* 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.commonfilesearch;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import javax.swing.ComboBoxModel;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import org.openide.explorer.ExplorerManager;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer;
import org.sleuthkit.autopsy.corecomponents.DataResultTopComponent;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
import org.sleuthkit.autopsy.corecomponents.DelayedLoadChildNodesOnTreeExpansion;
import org.sleuthkit.autopsy.corecomponents.MultiLayerTableFilterNode;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.directorytree.DataResultFilterNode;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Panel used for common files search configuration and configuration business
* logic. Nested within CommonFilesDialog.
*/
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
public final class CommonFilesPanel extends javax.swing.JPanel {
private static final long serialVersionUID = 1L;
private static final Long NO_DATA_SOURCE_SELECTED = -1L;
private ComboBoxModel<String> dataSourcesList = new DataSourceComboBoxModel();
private Map<Long, String> dataSourceMap;
private static final Logger LOGGER = Logger.getLogger(CommonFilesPanel.class.getName());
private boolean singleDataSource = false;
private String selectedDataSource = "";
private boolean pictureViewCheckboxState;
private boolean documentsCheckboxState;
/**
* Creates new form CommonFilesPanel
*/
@NbBundle.Messages({
"CommonFilesPanel.title=Common Files Panel",
"CommonFilesPanel.exception=Unexpected Exception loading DataSources."})
public CommonFilesPanel() {
initComponents();
this.setupDataSources();
this.errorText.setVisible(false);
}
/**
* Sets up the data sources dropdown and returns the data sources map for
* future usage.
*
* @return a mapping of data source ids to data source names
*/
@NbBundle.Messages({
"CommonFilesPanel.buildDataSourceMap.done.tskCoreException=Unable to run query against DB.",
"CommonFilesPanel.buildDataSourceMap.done.noCurrentCaseException=Unable to open case file.",
"CommonFilesPanel.buildDataSourceMap.done.exception=Unexpected exception building data sources map.",
"CommonFilesPanel.buildDataSourceMap.done.interupted=Something went wrong building the Common Files Search dialog box.",
"CommonFilesPanel.buildDataSourceMap.done.sqlException=Unable to query db for data sources.",
"CommonFilesPanel.buildDataSourcesMap.updateUi.noDataSources=No data sources were found."})
private void setupDataSources() {
new SwingWorker<Map<Long, String>, Void>() {
private void updateUi() {
String[] dataSourcesNames = new String[CommonFilesPanel.this.dataSourceMap.size()];
//only enable all this stuff if we actually have datasources
if (dataSourcesNames.length > 0) {
dataSourcesNames = CommonFilesPanel.this.dataSourceMap.values().toArray(dataSourcesNames);
CommonFilesPanel.this.dataSourcesList = new DataSourceComboBoxModel(dataSourcesNames);
CommonFilesPanel.this.selectDataSourceComboBox.setModel(CommonFilesPanel.this.dataSourcesList);
boolean multipleDataSources = this.caseHasMultipleSources();
CommonFilesPanel.this.allDataSourcesRadioButton.setEnabled(multipleDataSources);
CommonFilesPanel.this.allDataSourcesRadioButton.setSelected(multipleDataSources);
if (!multipleDataSources) {
CommonFilesPanel.this.withinDataSourceRadioButton.setSelected(true);
withinDataSourceSelected(true);
}
CommonFilesPanel.this.searchButton.setEnabled(true);
} else {
MessageNotifyUtil.Message.info(Bundle.CommonFilesPanel_buildDataSourcesMap_updateUi_noDataSources());
CommonFilesPanel.this.cancelButtonActionPerformed(null);
}
}
private boolean caseHasMultipleSources() {
return CommonFilesPanel.this.dataSourceMap.size() >= 2;
}
@Override
protected Map<Long, String> doInBackground() throws NoCurrentCaseException, TskCoreException, SQLException {
DataSourceLoader loader = new DataSourceLoader();
return loader.getDataSourceMap();
}
@Override
protected void done() {
try {
CommonFilesPanel.this.dataSourceMap = this.get();
updateUi();
} catch (InterruptedException ex) {
LOGGER.log(Level.SEVERE, "Interrupted while building Common Files Search dialog.", ex);
MessageNotifyUtil.Message.error(Bundle.CommonFilesPanel_buildDataSourceMap_done_interupted());
} catch (ExecutionException ex) {
String errorMessage;
Throwable inner = ex.getCause();
if (inner instanceof TskCoreException) {
LOGGER.log(Level.SEVERE, "Failed to load data sources from database.", ex);
errorMessage = Bundle.CommonFilesPanel_buildDataSourceMap_done_tskCoreException();
} else if (inner instanceof NoCurrentCaseException) {
LOGGER.log(Level.SEVERE, "Current case has been closed.", ex);
errorMessage = Bundle.CommonFilesPanel_buildDataSourceMap_done_noCurrentCaseException();
} else if (inner instanceof SQLException) {
LOGGER.log(Level.SEVERE, "Unable to query db for data sources.", ex);
errorMessage = Bundle.CommonFilesPanel_buildDataSourceMap_done_sqlException();
} else {
LOGGER.log(Level.SEVERE, "Unexpected exception while building Common Files Search dialog panel.", ex);
errorMessage = Bundle.CommonFilesPanel_buildDataSourceMap_done_exception();
}
MessageNotifyUtil.Message.error(errorMessage);
}
}
}.execute();
}
@NbBundle.Messages({
"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.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.",
"CommonFilesPanel.search.done.interupted=Something went wrong finding common files.",
"CommonFilesPanel.search.done.sqlException=Unable to query db for files or data sources."})
private void search() {
String pathText = Bundle.CommonFilesPanel_search_results_pathText();
new SwingWorker<CommonFilesMetadata, Void>() {
private String tabTitle;
private void setTitleForAllDataSources() {
this.tabTitle = Bundle.CommonFilesPanel_search_results_titleAll();
}
private void setTitleForSingleSource(Long dataSourceId) {
final String CommonFilesPanel_search_results_titleSingle = Bundle.CommonFilesPanel_search_results_titleSingle();
final Object[] dataSourceName = new Object[]{dataSourceMap.get(dataSourceId)};
this.tabTitle = String.format(CommonFilesPanel_search_results_titleSingle, dataSourceName);
}
private Long determineDataSourceId() {
Long selectedObjId = CommonFilesPanel.NO_DATA_SOURCE_SELECTED;
if (CommonFilesPanel.this.singleDataSource) {
for (Entry<Long, String> dataSource : CommonFilesPanel.this.dataSourceMap.entrySet()) {
if (dataSource.getValue().equals(CommonFilesPanel.this.selectedDataSource)) {
selectedObjId = dataSource.getKey();
break;
}
}
}
return selectedObjId;
}
@Override
@SuppressWarnings({"BoxedValueEquality", "NumberEquality"})
protected CommonFilesMetadata doInBackground() throws TskCoreException, NoCurrentCaseException, SQLException {
Long dataSourceId = determineDataSourceId();
CommonFilesMetadataBuilder builder;
boolean filterByMedia = false;
boolean filterByDocuments = false;
if (selectedFileCategoriesButton.isSelected()) {
if (pictureVideoCheckbox.isSelected()) {
filterByMedia = true;
}
if (documentsCheckbox.isSelected()) {
filterByDocuments = true;
}
}
if (dataSourceId == CommonFilesPanel.NO_DATA_SOURCE_SELECTED) {
builder = new AllDataSourcesCommonFilesAlgorithm(CommonFilesPanel.this.dataSourceMap, filterByMedia, filterByDocuments);
setTitleForAllDataSources();
} else {
builder = new SingleDataSource(dataSourceId, CommonFilesPanel.this.dataSourceMap, filterByMedia, filterByDocuments);
setTitleForSingleSource(dataSourceId);
}
this.tabTitle = builder.buildTabTitle();
CommonFilesMetadata metadata = builder.findCommonFiles();
return metadata;
}
@Override
protected void done() {
try {
super.done();
CommonFilesMetadata metadata = get();
CommonFilesNode commonFilesNode = new CommonFilesNode(metadata);
//TODO this could be enumerating the children!!! Instead delay enumeration by dynamically loading nodes with DelayedLoadChildNodesOnTreeExpansion()
DataResultFilterNode dataResultFilterNode = new DataResultFilterNode(commonFilesNode, ExplorerManager.find(CommonFilesPanel.this));
MultiLayerTableFilterNode tableFilterWithDescendantsNode = new MultiLayerTableFilterNode(dataResultFilterNode, 3);
DataResultViewerTable table = new DataResultViewerTable(new DelayedLoadChildNodesOnTreeExpansion());
Collection<DataResultViewer> viewers = new ArrayList<>(1);
viewers.add(table);
DataResultTopComponent.createInstance(tabTitle, pathText, tableFilterWithDescendantsNode, metadata.size(), viewers);
} catch (InterruptedException ex) {
LOGGER.log(Level.SEVERE, "Interrupted while loading Common Files", ex);
MessageNotifyUtil.Message.error(Bundle.CommonFilesPanel_search_done_interupted());
} catch (ExecutionException ex) {
String errorMessage;
Throwable inner = ex.getCause();
if (inner instanceof TskCoreException) {
LOGGER.log(Level.SEVERE, "Failed to load files from database.", ex);
errorMessage = Bundle.CommonFilesPanel_search_done_tskCoreException();
} else if (inner instanceof NoCurrentCaseException) {
LOGGER.log(Level.SEVERE, "Current case has been closed.", ex);
errorMessage = Bundle.CommonFilesPanel_search_done_noCurrentCaseException();
} else if (inner instanceof SQLException) {
LOGGER.log(Level.SEVERE, "Unable to query db for files.", ex);
errorMessage = Bundle.CommonFilesPanel_search_done_sqlException();
} else {
LOGGER.log(Level.SEVERE, "Unexpected exception while running Common Files Search.", ex);
errorMessage = Bundle.CommonFilesPanel_search_done_exception();
}
MessageNotifyUtil.Message.error(errorMessage);
}
}
}.execute();
}
/**
* 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() {
dataSourcesButtonGroup = new javax.swing.ButtonGroup();
fileTypeFilterButtonGroup = new javax.swing.ButtonGroup();
searchButton = new javax.swing.JButton();
allDataSourcesRadioButton = new javax.swing.JRadioButton();
withinDataSourceRadioButton = new javax.swing.JRadioButton();
selectDataSourceComboBox = new javax.swing.JComboBox<>();
commonFilesSearchLabel = new javax.swing.JLabel();
cancelButton = new javax.swing.JButton();
allFileCategoriesRadioButton = new javax.swing.JRadioButton();
selectedFileCategoriesButton = new javax.swing.JRadioButton();
pictureVideoCheckbox = new javax.swing.JCheckBox();
documentsCheckbox = new javax.swing.JCheckBox();
dataSourceLabel = new javax.swing.JLabel();
categoriesLabel = new javax.swing.JLabel();
errorText = new javax.swing.JLabel();
org.openide.awt.Mnemonics.setLocalizedText(searchButton, org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.searchButton.text")); // NOI18N
searchButton.setEnabled(false);
searchButton.setHorizontalTextPosition(javax.swing.SwingConstants.LEADING);
searchButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
searchButtonActionPerformed(evt);
}
});
dataSourcesButtonGroup.add(allDataSourcesRadioButton);
allDataSourcesRadioButton.setSelected(true);
org.openide.awt.Mnemonics.setLocalizedText(allDataSourcesRadioButton, org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.allDataSourcesRadioButton.text")); // NOI18N
allDataSourcesRadioButton.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
allDataSourcesRadioButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
allDataSourcesRadioButtonActionPerformed(evt);
}
});
dataSourcesButtonGroup.add(withinDataSourceRadioButton);
org.openide.awt.Mnemonics.setLocalizedText(withinDataSourceRadioButton, org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.withinDataSourceRadioButton.text")); // NOI18N
withinDataSourceRadioButton.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
withinDataSourceRadioButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
withinDataSourceRadioButtonActionPerformed(evt);
}
});
selectDataSourceComboBox.setModel(dataSourcesList);
selectDataSourceComboBox.setEnabled(false);
selectDataSourceComboBox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
selectDataSourceComboBoxActionPerformed(evt);
}
});
org.openide.awt.Mnemonics.setLocalizedText(commonFilesSearchLabel, org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.commonFilesSearchLabel.text")); // NOI18N
commonFilesSearchLabel.setFocusable(false);
org.openide.awt.Mnemonics.setLocalizedText(cancelButton, org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.cancelButton.text")); // NOI18N
cancelButton.setActionCommand(org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.cancelButton.actionCommand")); // NOI18N
cancelButton.setHorizontalTextPosition(javax.swing.SwingConstants.LEADING);
cancelButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
cancelButtonActionPerformed(evt);
}
});
fileTypeFilterButtonGroup.add(allFileCategoriesRadioButton);
org.openide.awt.Mnemonics.setLocalizedText(allFileCategoriesRadioButton, org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.allFileCategoriesRadioButton.text")); // NOI18N
allFileCategoriesRadioButton.setToolTipText(org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.allFileCategoriesRadioButton.toolTipText")); // NOI18N
allFileCategoriesRadioButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
allFileCategoriesRadioButtonActionPerformed(evt);
}
});
fileTypeFilterButtonGroup.add(selectedFileCategoriesButton);
selectedFileCategoriesButton.setSelected(true);
org.openide.awt.Mnemonics.setLocalizedText(selectedFileCategoriesButton, org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.selectedFileCategoriesButton.text")); // NOI18N
selectedFileCategoriesButton.setToolTipText(org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.selectedFileCategoriesButton.toolTipText")); // NOI18N
selectedFileCategoriesButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
selectedFileCategoriesButtonActionPerformed(evt);
}
});
pictureVideoCheckbox.setSelected(true);
org.openide.awt.Mnemonics.setLocalizedText(pictureVideoCheckbox, org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.pictureVideoCheckbox.text")); // NOI18N
pictureVideoCheckbox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
pictureVideoCheckboxActionPerformed(evt);
}
});
documentsCheckbox.setSelected(true);
org.openide.awt.Mnemonics.setLocalizedText(documentsCheckbox, org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.documentsCheckbox.text")); // NOI18N
documentsCheckbox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
documentsCheckboxActionPerformed(evt);
}
});
org.openide.awt.Mnemonics.setLocalizedText(dataSourceLabel, org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.text")); // NOI18N
dataSourceLabel.setName(""); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(categoriesLabel, org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.categoriesLabel.text")); // NOI18N
categoriesLabel.setName(""); // NOI18N
errorText.setForeground(new java.awt.Color(255, 0, 0));
org.openide.awt.Mnemonics.setLocalizedText(errorText, org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.errorText.text")); // NOI18N
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addComponent(errorText)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(searchButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(cancelButton)
.addContainerGap())
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(categoriesLabel)
.addComponent(commonFilesSearchLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(dataSourceLabel)
.addGroup(layout.createSequentialGroup()
.addGap(6, 6, 6)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(29, 29, 29)
.addComponent(selectDataSourceComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 261, javax.swing.GroupLayout.PREFERRED_SIZE))
.addComponent(withinDataSourceRadioButton)
.addComponent(allDataSourcesRadioButton)
.addComponent(allFileCategoriesRadioButton)
.addComponent(selectedFileCategoriesButton)
.addGroup(layout.createSequentialGroup()
.addGap(21, 21, 21)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(pictureVideoCheckbox)
.addComponent(documentsCheckbox))))))
.addGap(19, 19, 19))))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap()
.addComponent(commonFilesSearchLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(18, 18, 18)
.addComponent(dataSourceLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(allDataSourcesRadioButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(withinDataSourceRadioButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(selectDataSourceComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(18, 18, 18)
.addComponent(categoriesLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(selectedFileCategoriesButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(pictureVideoCheckbox)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(documentsCheckbox)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(allFileCategoriesRadioButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(cancelButton)
.addComponent(searchButton)
.addComponent(errorText)))
);
}// </editor-fold>//GEN-END:initComponents
private void searchButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchButtonActionPerformed
search();
SwingUtilities.windowForComponent(this).dispose();
}//GEN-LAST:event_searchButtonActionPerformed
private void allDataSourcesRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_allDataSourcesRadioButtonActionPerformed
selectDataSourceComboBox.setEnabled(!allDataSourcesRadioButton.isSelected());
singleDataSource = false;
}//GEN-LAST:event_allDataSourcesRadioButtonActionPerformed
private void selectDataSourceComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_selectDataSourceComboBoxActionPerformed
final Object selectedItem = selectDataSourceComboBox.getSelectedItem();
if (selectedItem != null) {
selectedDataSource = selectedItem.toString();
} else {
selectedDataSource = "";
}
}//GEN-LAST:event_selectDataSourceComboBoxActionPerformed
private void withinDataSourceRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_withinDataSourceRadioButtonActionPerformed
withinDataSourceSelected(withinDataSourceRadioButton.isSelected());
}//GEN-LAST:event_withinDataSourceRadioButtonActionPerformed
private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed
SwingUtilities.windowForComponent(this).dispose();
}//GEN-LAST:event_cancelButtonActionPerformed
private void allFileCategoriesRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_allFileCategoriesRadioButtonActionPerformed
this.manageCheckBoxState();
this.toggleErrorTextAndSearchBox();
}//GEN-LAST:event_allFileCategoriesRadioButtonActionPerformed
private void selectedFileCategoriesButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_selectedFileCategoriesButtonActionPerformed
this.manageCheckBoxState();
}//GEN-LAST:event_selectedFileCategoriesButtonActionPerformed
private void pictureVideoCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pictureVideoCheckboxActionPerformed
this.toggleErrorTextAndSearchBox();
}//GEN-LAST:event_pictureVideoCheckboxActionPerformed
private void documentsCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_documentsCheckboxActionPerformed
this.toggleErrorTextAndSearchBox();
}//GEN-LAST:event_documentsCheckboxActionPerformed
private void toggleErrorTextAndSearchBox() {
if (!this.pictureVideoCheckbox.isSelected() && !this.documentsCheckbox.isSelected() && !this.allFileCategoriesRadioButton.isSelected()) {
this.searchButton.setEnabled(false);
this.errorText.setVisible(true);
} else {
this.searchButton.setEnabled(true);
this.errorText.setVisible(false);
}
}
private void withinDataSourceSelected(boolean selected) {
selectDataSourceComboBox.setEnabled(selected);
if (selectDataSourceComboBox.isEnabled()) {
selectDataSourceComboBox.setSelectedIndex(0);
singleDataSource = true;
}
}
private void manageCheckBoxState() {
if (this.allFileCategoriesRadioButton.isSelected()) {
this.pictureViewCheckboxState = this.pictureVideoCheckbox.isSelected();
this.documentsCheckboxState = this.documentsCheckbox.isSelected();
this.pictureVideoCheckbox.setEnabled(false);
this.documentsCheckbox.setEnabled(false);
}
if (this.selectedFileCategoriesButton.isSelected()) {
this.pictureVideoCheckbox.setSelected(this.pictureViewCheckboxState);
this.documentsCheckbox.setSelected(this.documentsCheckboxState);
this.pictureVideoCheckbox.setEnabled(true);
this.documentsCheckbox.setEnabled(true);
this.toggleErrorTextAndSearchBox();
}
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JRadioButton allDataSourcesRadioButton;
private javax.swing.JRadioButton allFileCategoriesRadioButton;
private javax.swing.JButton cancelButton;
private javax.swing.JLabel categoriesLabel;
private javax.swing.JLabel commonFilesSearchLabel;
private javax.swing.JLabel dataSourceLabel;
private javax.swing.ButtonGroup dataSourcesButtonGroup;
private javax.swing.JCheckBox documentsCheckbox;
private javax.swing.JLabel errorText;
private javax.swing.ButtonGroup fileTypeFilterButtonGroup;
private javax.swing.JCheckBox pictureVideoCheckbox;
private javax.swing.JButton searchButton;
private javax.swing.JComboBox<String> selectDataSourceComboBox;
private javax.swing.JRadioButton selectedFileCategoriesButton;
private javax.swing.JRadioButton withinDataSourceRadioButton;
// End of variables declaration//GEN-END:variables
}

View File

@ -19,28 +19,45 @@
package org.sleuthkit.autopsy.commonfilesearch;
import java.awt.event.ActionEvent;
import java.util.logging.Level;
import org.openide.util.HelpCtx;
import org.openide.util.NbBundle;
import org.openide.util.actions.CallableSystemAction;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.core.Installer;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.autopsy.coreutils.Logger;
/**
* Encapsulates a menu action which triggers the common files search dialog.
*/
final public class CommonFilesSearchAction extends CallableSystemAction {
private static final Logger LOGGER = Logger.getLogger(CommonFilesSearchAction.class.getName());
private static CommonFilesSearchAction instance = null;
private static final long serialVersionUID = 1L;
CommonFilesSearchAction() {
super();
this.setEnabled(false);
}
@Override
public boolean isEnabled(){
return super.isEnabled() && Case.isCaseOpen() && Installer.isJavaFxInited();
boolean shouldBeEnabled = false;
try {
shouldBeEnabled = Case.isCaseOpen()
&& Case.getCurrentCase().getDataSources().size() > 1
|| (EamDb.isEnabled() && EamDb.getInstance().getCases().size() > 1);
} catch(TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Error getting data sources for action enabled check", ex);
} catch (EamDbException ex) {
LOGGER.log(Level.SEVERE, "Error getting CR cases for action enabled check", ex);
}
return super.isEnabled() && shouldBeEnabled;
}
public static synchronized CommonFilesSearchAction getDefault() {
@ -52,12 +69,12 @@ final public class CommonFilesSearchAction extends CallableSystemAction {
@Override
public void actionPerformed(ActionEvent event) {
new CommonFilesDialog().setVisible(true);
new CommonAttributePanel().setVisible(true);
}
@Override
public void performAction() {
new CommonFilesDialog().setVisible(true);
new CommonAttributePanel().setVisible(true);
}
@NbBundle.Messages({

View File

@ -30,7 +30,7 @@ public class DataSourceComboBoxModel extends AbstractListModel<String> implement
private static final long serialVersionUID = 1L;
private final String[] dataSourceList;
String selection = null;
private String selection = null;
/**
* Use this to initialize the panel

View File

@ -1,56 +0,0 @@
/*
*
* 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.commonfilesearch;
/**
* Encapsulates data required to instantiate a <code>FileInstanceNode</code>.
*/
final public class FileInstanceMetadata {
private final Long objectId;
private final String dataSourceName;
/**
* Create meta data required to find an abstract file and build a FileInstanceNode.
* @param objectId id of abstract file to find
* @param dataSourceName name of datasource where the object is found
*/
FileInstanceMetadata(Long objectId, String dataSourceName) {
this.objectId = objectId;
this.dataSourceName = dataSourceName;
}
/**
* obj_id for the file represented by this object
* @return
*/
public Long getObjectId(){
return this.objectId;
}
/**
* Name of datasource where this instance was found.
* @return
*/
public String getDataSourceName(){
return this.dataSourceName;
}
}

View File

@ -0,0 +1,153 @@
/*
*
* 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.commonfilesearch;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.nodes.Sheet;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.datamodel.DisplayableItemNode;
import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor;
import org.sleuthkit.autopsy.datamodel.NodeProperty;
/**
* Node used to indicate the number of matches found with the MD5 children
* of this Node.
*/
final public class InstanceCountNode extends DisplayableItemNode {
final private int instanceCount;
final private CommonAttributeValueList attributeValues;
/**
* Create a node with the given number of instances, and the given
* selection of metadata.
* @param instanceCount
* @param attributeValues
*/
@NbBundle.Messages({
"InstanceCountNode.displayName=Files with %s instances (%s)"
})
public InstanceCountNode(int instanceCount, CommonAttributeValueList attributeValues) {
super(Children.create(new CommonAttributeValueNodeFactory(attributeValues.getMetadataList()), true));
this.instanceCount = instanceCount;
this.attributeValues = attributeValues;
this.setDisplayName(String.format(Bundle.InstanceCountNode_displayName(), Integer.toString(instanceCount), attributeValues.getMetadataList().size()));
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/fileset-icon-16.png"); //NON-NLS
}
/**
* Number of matches found for each of the MD5 children.
* @return int match count
*/
int getInstanceCount() {
return this.instanceCount;
}
public void refresh() {
attributeValues.displayDelayedMetadata();
setChildren(Children.create(new CommonAttributeValueNodeFactory(attributeValues.getMetadataList()), false));
}
/**
* Get a list of metadata for the MD5s which are children of this object.
* @return List<Md5Metadata>
*/
CommonAttributeValueList getAttributeValues() {
return this.attributeValues;
}
@Override
public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
return visitor.visit(this);
}
@Override
public boolean isLeafTypeNode() {
return false;
}
@Override
public String getItemType() {
return getClass().getName();
}
@NbBundle.Messages({"InstanceCountNode.createSheet.noDescription= "})
@Override
protected Sheet createSheet() {
Sheet sheet = new Sheet();
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
if (sheetSet == null) {
sheetSet = Sheet.createPropertiesSet();
sheet.put(sheetSet);
}
final String NO_DESCR = Bundle.InstanceCountNode_createSheet_noDescription();
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), NO_DESCR, ""));
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_instancesColLbl(), Bundle.CommonFilesSearchResultsViewerTable_instancesColLbl(), NO_DESCR, this.getInstanceCount()));
return sheet;
}
/**
* ChildFactory which builds CommonFileParentNodes from the
* CommonFilesMetaaData models.
*/
static class CommonAttributeValueNodeFactory extends ChildFactory<String> {
/**
* List of models, each of which is a parent node matching a single md5,
* containing children FileNodes.
*/
// maps sting version of value to value Object (??)
private final Map<String, CommonAttributeValue> metadata;
CommonAttributeValueNodeFactory(List<CommonAttributeValue> attributeValues) {
this.metadata = new HashMap<>();
Iterator<CommonAttributeValue> iterator = attributeValues.iterator();
while (iterator.hasNext()) {
CommonAttributeValue attributeValue = iterator.next();
this.metadata.put(attributeValue.getValue(), attributeValue);
}
}
@Override
protected boolean createKeys(List<String> list) {
// @@@ We should just use CommonAttributeValue as the key...
list.addAll(this.metadata.keySet());
return true;
}
@Override
protected Node createNodeForKey(String attributeValue) {
CommonAttributeValue md5Metadata = this.metadata.get(attributeValue);
return new CommonAttributeValueNode(md5Metadata);
}
}
}

View File

@ -0,0 +1,59 @@
/*
*
* 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.commonfilesearch;
import java.util.Map;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
/**
* Provides logic for selecting common files from all data sources and all cases
* in the Central Repo.
*/
abstract class InterCaseCommonAttributeSearcher extends AbstractCommonAttributeSearcher {
private final EamDb dbManager;
/**
* Implements the algorithm for getting common files across all data sources
* and all cases. Can filter on mime types conjoined by logical AND.
*
* @param filterByMediaMimeType match only on files whose mime types can be
* broadly categorized as media types
* @param filterByDocMimeType match only on files whose mime types can be
* broadly categorized as document types
*
* @throws EamDbException
*/
InterCaseCommonAttributeSearcher(Map<Long, String> dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) throws EamDbException {
super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType);
dbManager = EamDb.getInstance();
}
protected CorrelationCase getCorrelationCaseFromId(int correlationCaseId) throws EamDbException {
for (CorrelationCase cCase : this.dbManager.getCases()) {
if (cCase.getID() == correlationCaseId) {
return cCase;
}
}
throw new IllegalArgumentException("Cannot locate case.");
}
}

View File

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<NonVisualComponents>
<Component class="javax.swing.ButtonGroup" name="buttonGroup">
</Component>
</NonVisualComponents>
<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 max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="anyCentralRepoCaseRadio" alignment="0" min="-2" max="-2" attributes="0"/>
<Group type="102" attributes="0">
<EmptySpace min="21" pref="21" max="-2" attributes="0"/>
<Component id="caseComboBox" min="-2" pref="261" max="-2" attributes="0"/>
</Group>
<Component id="specificCentralRepoCaseRadio" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="anyCentralRepoCaseRadio" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="specificCentralRepoCaseRadio" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="caseComboBox" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JRadioButton" name="anyCentralRepoCaseRadio">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="buttonGroup"/>
</Property>
<Property name="selected" type="boolean" value="true"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="InterCasePanel.anyCentralRepoCaseRadio.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="anyCentralRepoCaseRadioActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JRadioButton" name="specificCentralRepoCaseRadio">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="buttonGroup"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="InterCasePanel.specificCentralRepoCaseRadio.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="specificCentralRepoCaseRadioActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JComboBox" name="caseComboBox">
<Properties>
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="casesList" type="code"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
</AuxValues>
</Component>
</SubComponents>
</Form>

View File

@ -0,0 +1,193 @@
/*
*
* 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.commonfilesearch;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import javax.swing.ComboBoxModel;
import org.openide.util.NbBundle;
/**
* UI controls for Common Files Search scenario where the user intends to find
* common files between cases in addition to the present case.
*/
public class InterCasePanel extends javax.swing.JPanel {
private static final long serialVersionUID = 1L;
static final int NO_CASE_SELECTED = -1;
private ComboBoxModel<String> casesList = new DataSourceComboBoxModel();
private final Map<Integer, String> caseMap;
private String errorMessage;
/**
* Creates new form InterCasePanel
*/
public InterCasePanel() {
initComponents();
this.errorMessage = "";
this.caseMap = new HashMap<>();
}
private void specificCaseSelected(boolean selected) {
this.specificCentralRepoCaseRadio.setEnabled(selected);
if (this.specificCentralRepoCaseRadio.isEnabled()) {
this.caseComboBox.setEnabled(true);
this.caseComboBox.setSelectedIndex(0);
}
}
String getErrorMessage(){
return this.errorMessage;
}
/**
* 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() {
buttonGroup = new javax.swing.ButtonGroup();
anyCentralRepoCaseRadio = new javax.swing.JRadioButton();
specificCentralRepoCaseRadio = new javax.swing.JRadioButton();
caseComboBox = new javax.swing.JComboBox<>();
buttonGroup.add(anyCentralRepoCaseRadio);
anyCentralRepoCaseRadio.setSelected(true);
org.openide.awt.Mnemonics.setLocalizedText(anyCentralRepoCaseRadio, org.openide.util.NbBundle.getMessage(InterCasePanel.class, "InterCasePanel.anyCentralRepoCaseRadio.text")); // NOI18N
anyCentralRepoCaseRadio.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
anyCentralRepoCaseRadioActionPerformed(evt);
}
});
buttonGroup.add(specificCentralRepoCaseRadio);
org.openide.awt.Mnemonics.setLocalizedText(specificCentralRepoCaseRadio, org.openide.util.NbBundle.getMessage(InterCasePanel.class, "InterCasePanel.specificCentralRepoCaseRadio.text")); // NOI18N
specificCentralRepoCaseRadio.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
specificCentralRepoCaseRadioActionPerformed(evt);
}
});
caseComboBox.setModel(casesList);
caseComboBox.setEnabled(false);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(anyCentralRepoCaseRadio)
.addGroup(layout.createSequentialGroup()
.addGap(21, 21, 21)
.addComponent(caseComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 261, javax.swing.GroupLayout.PREFERRED_SIZE))
.addComponent(specificCentralRepoCaseRadio))
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(anyCentralRepoCaseRadio)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(specificCentralRepoCaseRadio)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(caseComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
);
}// </editor-fold>//GEN-END:initComponents
private void specificCentralRepoCaseRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_specificCentralRepoCaseRadioActionPerformed
this.caseComboBox.setEnabled(true);
if(this.caseComboBox.isEnabled() && this.caseComboBox.getSelectedItem() == null){
this.caseComboBox.setSelectedIndex(0);
}
}//GEN-LAST:event_specificCentralRepoCaseRadioActionPerformed
private void anyCentralRepoCaseRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_anyCentralRepoCaseRadioActionPerformed
this.caseComboBox.setEnabled(false);
}//GEN-LAST:event_anyCentralRepoCaseRadioActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JRadioButton anyCentralRepoCaseRadio;
private javax.swing.ButtonGroup buttonGroup;
private javax.swing.JComboBox<String> caseComboBox;
private javax.swing.JRadioButton specificCentralRepoCaseRadio;
// End of variables declaration//GEN-END:variables
Map<Integer, String> getCaseMap() {
return Collections.unmodifiableMap(this.caseMap);
}
void setCaseList(DataSourceComboBoxModel dataSourceComboBoxModel) {
this.casesList = dataSourceComboBoxModel;
this.caseComboBox.setModel(dataSourceComboBoxModel);
}
void rigForMultipleCases(boolean multipleCases) {
this.anyCentralRepoCaseRadio.setEnabled(multipleCases);
this.anyCentralRepoCaseRadio.setEnabled(multipleCases);
if(!multipleCases){
this.specificCentralRepoCaseRadio.setSelected(true);
this.specificCaseSelected(true);
}
}
void setCaseMap(Map<Integer, String> caseMap) {
this.caseMap.clear();
this.caseMap.putAll(caseMap);
}
boolean centralRepoHasMultipleCases() {
return this.caseMap.size() >= 2;
}
Integer getSelectedCaseId(){
for(Entry<Integer, String> entry : this.caseMap.entrySet()){
if(entry.getValue().equals(this.caseComboBox.getSelectedItem())){
return entry.getKey();
}
}
return InterCasePanel.NO_CASE_SELECTED;
}
@NbBundle.Messages({
"InterCasePanel.showInterCaseErrorMessage.message=Cannot run intercase correlation search: no cases in Central Repository."
})
boolean areSearchCriteriaMet() {
if(this.caseMap.isEmpty()){
this.errorMessage = Bundle.InterCasePanel_showInterCaseErrorMessage_message();
return false;
} else {
return true;
}
}
}

View File

@ -0,0 +1,230 @@
/*
* 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.commonfilesearch;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationDataSource;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
import org.sleuthkit.autopsy.centralrepository.datamodel.InstanceTableCallback;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.datamodel.HashUtility;
/**
* Used to process and return CorrelationCase md5s from the EamDB for
* CommonFilesSearch.
*/
final class InterCaseSearchResultsProcessor {
private Map<Long, String> dataSources;
private static final Logger LOGGER = Logger.getLogger(CommonAttributePanel.class.getName());
private final String interCaseWhereClause = "value IN (SELECT value FROM file_instances"
+ " WHERE value IN (SELECT value FROM file_instances"
+ " WHERE case_id=%s AND (known_status !=%s OR known_status IS NULL) GROUP BY value)"
+ " GROUP BY value HAVING COUNT(DISTINCT case_id) > 1) ORDER BY value";
private final String singleInterCaseWhereClause = "value IN (SELECT value FROM file_instances "
+ "WHERE value IN (SELECT value FROM file_instances "
+ "WHERE case_id=%s AND (known_status !=%s OR known_status IS NULL) GROUP BY value) "
+ "AND (case_id=%s OR case_id=%s) GROUP BY value HAVING COUNT(DISTINCT case_id) > 1) ORDER BY value";
InterCaseSearchResultsProcessor(Map<Long, String> dataSources){
this.dataSources = dataSources;
}
InterCaseSearchResultsProcessor(){}
/**
* Finds a single CorrelationAttribute given an id.
*
* @param attrbuteId Row of CorrelationAttribute to retrieve from the EamDb
* @return CorrelationAttribute object representation of retrieved match
*/
CorrelationAttribute findSingleCorrelationAttribute(int attrbuteId) {
try {
InterCaseCommonAttributeRowCallback instancetableCallback = new InterCaseCommonAttributeRowCallback();
EamDb DbManager = EamDb.getInstance();
CorrelationAttribute.Type fileType = DbManager.getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID);
DbManager.processInstanceTableWhere(fileType, String.format("id = %s", attrbuteId), instancetableCallback);
return instancetableCallback.getCorrelationAttribute();
} catch (EamDbException ex) {
LOGGER.log(Level.SEVERE, "Error accessing EamDb processing InstanceTable row.", ex);
}
return null;
}
/**
* Given the current case, fins all intercase common files from the EamDb
* and builds maps of obj id to md5 and case.
*
* @param currentCase The current TSK Case.
*/
Map<Integer, CommonAttributeValueList> findInterCaseCommonAttributeValues(Case currentCase) {
try {
InterCaseCommonAttributesCallback instancetableCallback = new InterCaseCommonAttributesCallback();
EamDb DbManager = EamDb.getInstance();
CorrelationAttribute.Type fileType = DbManager.getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID);
int caseId = DbManager.getCase(currentCase).getID();
DbManager.processInstanceTableWhere(fileType, String.format(interCaseWhereClause, caseId,
TskData.FileKnown.KNOWN.getFileKnownValue()),
instancetableCallback);
return instancetableCallback.getInstanceCollatedCommonFiles();
} catch (EamDbException ex) {
LOGGER.log(Level.SEVERE, "Error accessing EamDb processing CaseInstancesTable.", ex);
}
return new HashMap<>();
}
/**
* Given the current case, and a specific case of interest, finds common
* files which exist between cases from the EamDb. Builds maps of obj id to
* md5 and case.
*
* @param currentCase The current TSK Case.
* @param singleCase The case of interest. Matches must exist in this case.
*/
Map<Integer, CommonAttributeValueList> findSingleInterCaseCommonAttributeValues(Case currentCase, CorrelationCase singleCase) {
try {
InterCaseCommonAttributesCallback instancetableCallback = new InterCaseCommonAttributesCallback();
EamDb DbManager = EamDb.getInstance();
CorrelationAttribute.Type fileType = DbManager.getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID);
int caseId = DbManager.getCase(currentCase).getID();
int targetCaseId = singleCase.getID();
DbManager.processInstanceTableWhere(fileType, String.format(singleInterCaseWhereClause, caseId,
TskData.FileKnown.KNOWN.getFileKnownValue(), caseId, targetCaseId), instancetableCallback);
return instancetableCallback.getInstanceCollatedCommonFiles();
} catch (EamDbException ex) {
LOGGER.log(Level.SEVERE, "Error accessing EamDb processing CaseInstancesTable.", ex);
}
return new HashMap<>();
}
/**
* Callback to use with findInterCaseCommonAttributeValues which generates a
* list of md5s for common files search
*/
private class InterCaseCommonAttributesCallback implements InstanceTableCallback {
final Map<Integer, CommonAttributeValueList> instanceCollatedCommonFiles = new HashMap<>();
private CommonAttributeValue commonAttributeValue = null;
private String previousRowMd5 = "";
@Override
public void process(ResultSet resultSet) {
try {
while (resultSet.next()) {
int resultId = InstanceTableCallback.getId(resultSet);
String md5Value = InstanceTableCallback.getValue(resultSet);
if (previousRowMd5.isEmpty()) {
previousRowMd5 = md5Value;
}
if (md5Value == null || HashUtility.isNoDataMd5(md5Value)) {
continue;
}
countAndAddCommonAttributes(md5Value, resultId);
}
} catch (SQLException ex) {
LOGGER.log(Level.WARNING, "Error getting artifact instances from database.", ex); // NON-NLS
}
}
private void countAndAddCommonAttributes(String md5Value, int resultId) {
if (commonAttributeValue == null) {
commonAttributeValue = new CommonAttributeValue(md5Value);
}
if (!md5Value.equals(previousRowMd5)) {
int size = commonAttributeValue.getInstanceCount();
if (instanceCollatedCommonFiles.containsKey(size)) {
instanceCollatedCommonFiles.get(size).addMetadataToList(commonAttributeValue);
} else {
CommonAttributeValueList value = new CommonAttributeValueList();
value.addMetadataToList(commonAttributeValue);
instanceCollatedCommonFiles.put(size, value);
}
commonAttributeValue = new CommonAttributeValue(md5Value);
previousRowMd5 = md5Value;
}
// we don't *have* all the information for the rows in the CR,
// so we need to consult the present case via the SleuthkitCase object
// Later, when the FileInstanceNode is built. Therefore, build node generators for now.
AbstractCommonAttributeInstance searchResult = new CentralRepoCommonAttributeInstance(resultId, InterCaseSearchResultsProcessor.this.dataSources);
commonAttributeValue.addInstance(searchResult);
}
Map<Integer, CommonAttributeValueList> getInstanceCollatedCommonFiles() {
return Collections.unmodifiableMap(instanceCollatedCommonFiles);
}
}
/**
* Callback to use with findSingleCorrelationAttribute which retrieves a
* single CorrelationAttribute from the EamDb.
*/
private class InterCaseCommonAttributeRowCallback implements InstanceTableCallback {
CorrelationAttribute correlationAttribute = null;
@Override
public void process(ResultSet resultSet) {
try {
EamDb DbManager = EamDb.getInstance();
CorrelationAttribute.Type fileType = DbManager.getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID);
while (resultSet.next()) {
CorrelationCase correlationCase = DbManager.getCaseById(InstanceTableCallback.getCaseId(resultSet));
CorrelationDataSource dataSource = DbManager.getDataSourceById(correlationCase, InstanceTableCallback.getDataSourceId(resultSet));
correlationAttribute = DbManager.getCorrelationAttribute(fileType,
correlationCase,
dataSource,
InstanceTableCallback.getValue(resultSet),
InstanceTableCallback.getFilePath(resultSet));
}
} catch (SQLException | EamDbException ex) {
LOGGER.log(Level.WARNING, "Error getting single correlation artifact instance from database.", ex); // NON-NLS
}
}
CorrelationAttribute getCorrelationAttribute() {
return correlationAttribute;
}
}
}

View File

@ -0,0 +1,166 @@
/*
*
* 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.commonfilesearch;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.datamodel.HashUtility;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery;
import org.sleuthkit.datamodel.TskCoreException;
/**
*
* Generates a <code>List<CommonFilesMetadata></code> when
* <code>findFiles()</code> is called, which organizes files by md5 to
* prepare to display in viewer.
*
* This entire thing runs on a background thread where exceptions are handled.
*/
@SuppressWarnings("PMD.AbstractNaming")
public abstract class IntraCaseCommonAttributeSearcher extends AbstractCommonAttributeSearcher {
private static final String FILTER_BY_MIME_TYPES_WHERE_CLAUSE = " and mime_type in (%s)"; //NON-NLS // where %s is csv list of mime_types to filter on
/**
* Subclass this to implement different algorithms for getting common files.
*
* @param dataSourceIdMap a map of obj_id to datasource name
* @param filterByMediaMimeType match only on files whose mime types can be
* broadly categorized as media types
* @param filterByDocMimeType match only on files whose mime types can be
* broadly categorized as document types
*/
IntraCaseCommonAttributeSearcher(Map<Long, String> dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) {
super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType);
}
/**
* Use this as a prefix when building the SQL select statement.
*
* <ul>
* <li>You only have to specify the WHERE clause if you use this.</li>
* <li>If you do not use this string, you must use at least the columns
* selected below, in that order.</li>
* </ul>
*/
static final String SELECT_PREFIX = "SELECT obj_id, md5, data_source_obj_id from tsk_files where"; //NON-NLS
/**
* Should build a SQL SELECT statement to be passed to
* SleuthkitCase.executeQuery(sql) which will select the desired file ids
* and MD5 hashes.
*
* The statement should select obj_id, md5, data_source_obj_id in that
* order.
*
* @return sql string select statement
*/
protected abstract String buildSqlSelectStatement();
/**
* Generate a meta data object which encapsulates everything need to add the
* tree table tab to the top component.
*
* @return a data object with all of the matched files in a hierarchical
* format
* @throws TskCoreException
* @throws NoCurrentCaseException
* @throws SQLException
*/
@Override
public CommonAttributeSearchResults findFiles() throws TskCoreException, NoCurrentCaseException, SQLException {
Map<String, CommonAttributeValue> commonFiles = new HashMap<>();
final Case currentCase = Case.getCurrentCaseThrows();
final String caseName = currentCase.getDisplayName();
SleuthkitCase sleuthkitCase = currentCase.getSleuthkitCase();
String selectStatement = this.buildSqlSelectStatement();
try (
CaseDbQuery query = sleuthkitCase.executeQuery(selectStatement);
ResultSet resultSet = query.getResultSet()) {
while (resultSet.next()) {
Long objectId = resultSet.getLong(1);
String md5 = resultSet.getString(2);
Long dataSourceId = resultSet.getLong(3);
String dataSource = this.getDataSourceIdToNameMap().get(dataSourceId);
if (md5 == null || HashUtility.isNoDataMd5(md5)) {
continue;
}
if (commonFiles.containsKey(md5)) {
final CommonAttributeValue commonAttributeValue = commonFiles.get(md5);
commonAttributeValue.addInstance(new CaseDBCommonAttributeInstance(objectId, dataSource, caseName));
} else {
final CommonAttributeValue commonAttributeValue = new CommonAttributeValue(md5);
commonAttributeValue.addInstance(new CaseDBCommonAttributeInstance(objectId, dataSource, caseName));
commonFiles.put(md5, commonAttributeValue);
}
}
}
Map<Integer, CommonAttributeValueList> instanceCollatedCommonFiles = collateMatchesByNumberOfInstances(commonFiles);
return new CommonAttributeSearchResults(instanceCollatedCommonFiles);
}
/**
* Should be used by subclasses, in their
* <code>buildSqlSelectStatement()</code> function to create an SQL boolean
* expression which will filter our matches based on mime type. The
* expression will be conjoined to base query with an AND operator.
*
* @return sql fragment of the form:
* 'and "mime_type" in ( [comma delimited list of mime types] )'
* or empty string in the event that no types to filter on were given.
*/
String determineMimeTypeFilter() {
Set<String> mimeTypesToFilterOn = new HashSet<>();
String mimeTypeString = "";
if (isFilterByMedia()) {
mimeTypesToFilterOn.addAll(MEDIA_PICS_VIDEO_MIME_TYPES);
}
if (isFilterByDoc()) {
mimeTypesToFilterOn.addAll(TEXT_FILES_MIME_TYPES);
}
StringBuilder mimeTypeFilter = new StringBuilder(mimeTypesToFilterOn.size());
if (!mimeTypesToFilterOn.isEmpty()) {
for (String mimeType : mimeTypesToFilterOn) {
mimeTypeFilter.append("'").append(mimeType).append("',");
}
mimeTypeString = mimeTypeFilter.toString().substring(0, mimeTypeFilter.length() - 1);
mimeTypeString = String.format(FILTER_BY_MIME_TYPES_WHERE_CLAUSE, new Object[]{mimeTypeString});
}
return mimeTypeString;
}
}

View File

@ -0,0 +1,97 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<NonVisualComponents>
<Component class="javax.swing.ButtonGroup" name="buttonGroup">
</Component>
</NonVisualComponents>
<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">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="allDataSourcesRadioButton" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="withinDataSourceRadioButton" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="27" max="-2" attributes="0"/>
<Component id="selectDataSourceComboBox" min="-2" pref="261" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="allDataSourcesRadioButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="withinDataSourceRadioButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="selectDataSourceComboBox" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JRadioButton" name="allDataSourcesRadioButton">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="buttonGroup"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="IntraCasePanel.allDataSourcesRadioButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="horizontalAlignment" type="int" value="0"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="allDataSourcesRadioButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JRadioButton" name="withinDataSourceRadioButton">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="buttonGroup"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commonfilesearch/Bundle.properties" key="IntraCasePanel.withinDataSourceRadioButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="horizontalAlignment" type="int" value="0"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="withinDataSourceRadioButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JComboBox" name="selectDataSourceComboBox">
<Properties>
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="dataSourcesList" type="code"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="selectDataSourceComboBoxActionPerformed"/>
</Events>
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
</AuxValues>
</Component>
</SubComponents>
</Form>

View File

@ -0,0 +1,218 @@
/*
*
* 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.commonfilesearch;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import javax.swing.ComboBoxModel;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.Logger;
/**
* UI controls for Common Files Search scenario where the user intends to find
* common files between datasources. It is an inner panel which provides the ability
* to select all datasources or a single datasource from a dropdown list of
* sources in the current case.
*/
public class IntraCasePanel extends javax.swing.JPanel {
private static final long serialVersionUID = 1L;
static final long NO_DATA_SOURCE_SELECTED = -1;
private static final Logger LOGGER = Logger.getLogger(CommonAttributePanel.class.getName());
private boolean singleDataSource;
private String selectedDataSource;
private ComboBoxModel<String> dataSourcesList = new DataSourceComboBoxModel();
private final Map<Long, String> dataSourceMap;
private String errorMessage;
/**
* Creates new form IntraCasePanel
*/
public IntraCasePanel() {
initComponents();
this.errorMessage = "";
this.dataSourceMap = new HashMap<>();
}
public boolean isSingleDataSource(){
return this.singleDataSource;
}
public String getSelectedDataSource(){
if(this.singleDataSource && this.selectedDataSource != null){
return selectedDataSource;
} else {
return "";
}
}
public Map<Long, String> getDataSourceMap(){
return Collections.unmodifiableMap(this.dataSourceMap);
}
Long getSelectedDataSourceId(){
for(Entry<Long, String> entry : this.dataSourceMap.entrySet()){
if(entry.getValue().equals(this.selectDataSourceComboBox.getSelectedItem())){
return entry.getKey();
}
}
return IntraCasePanel.NO_DATA_SOURCE_SELECTED;
}
/**
* 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() {
buttonGroup = new javax.swing.ButtonGroup();
allDataSourcesRadioButton = new javax.swing.JRadioButton();
withinDataSourceRadioButton = new javax.swing.JRadioButton();
selectDataSourceComboBox = new javax.swing.JComboBox<>();
buttonGroup.add(allDataSourcesRadioButton);
org.openide.awt.Mnemonics.setLocalizedText(allDataSourcesRadioButton, org.openide.util.NbBundle.getMessage(IntraCasePanel.class, "IntraCasePanel.allDataSourcesRadioButton.text")); // NOI18N
allDataSourcesRadioButton.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
allDataSourcesRadioButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
allDataSourcesRadioButtonActionPerformed(evt);
}
});
buttonGroup.add(withinDataSourceRadioButton);
org.openide.awt.Mnemonics.setLocalizedText(withinDataSourceRadioButton, org.openide.util.NbBundle.getMessage(IntraCasePanel.class, "IntraCasePanel.withinDataSourceRadioButton.text")); // NOI18N
withinDataSourceRadioButton.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
withinDataSourceRadioButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
withinDataSourceRadioButtonActionPerformed(evt);
}
});
selectDataSourceComboBox.setModel(dataSourcesList);
selectDataSourceComboBox.setEnabled(false);
selectDataSourceComboBox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
selectDataSourceComboBoxActionPerformed(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(allDataSourcesRadioButton)
.addComponent(withinDataSourceRadioButton)))
.addGroup(layout.createSequentialGroup()
.addGap(27, 27, 27)
.addComponent(selectDataSourceComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 261, javax.swing.GroupLayout.PREFERRED_SIZE)))
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(allDataSourcesRadioButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(withinDataSourceRadioButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(selectDataSourceComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
);
}// </editor-fold>//GEN-END:initComponents
private void allDataSourcesRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_allDataSourcesRadioButtonActionPerformed
selectDataSourceComboBox.setEnabled(!allDataSourcesRadioButton.isSelected());
singleDataSource = false;
}//GEN-LAST:event_allDataSourcesRadioButtonActionPerformed
private void withinDataSourceRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_withinDataSourceRadioButtonActionPerformed
withinDataSourceSelected(withinDataSourceRadioButton.isSelected());
}//GEN-LAST:event_withinDataSourceRadioButtonActionPerformed
private void selectDataSourceComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_selectDataSourceComboBoxActionPerformed
final Object selectedItem = selectDataSourceComboBox.getSelectedItem();
if (selectedItem != null) {
selectedDataSource = selectedItem.toString();
} else {
selectedDataSource = "";
}
}//GEN-LAST:event_selectDataSourceComboBoxActionPerformed
private void withinDataSourceSelected(boolean selected) {
selectDataSourceComboBox.setEnabled(selected);
if (selectDataSourceComboBox.isEnabled()) {
selectDataSourceComboBox.setSelectedIndex(0);
singleDataSource = true;
}
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JRadioButton allDataSourcesRadioButton;
private javax.swing.ButtonGroup buttonGroup;
private javax.swing.JComboBox<String> selectDataSourceComboBox;
private javax.swing.JRadioButton withinDataSourceRadioButton;
// End of variables declaration//GEN-END:variables
void setDataModel(DataSourceComboBoxModel dataSourceComboBoxModel) {
this.dataSourcesList = dataSourceComboBoxModel;
this.selectDataSourceComboBox.setModel(dataSourcesList);
}
void rigForMultipleDataSources(boolean multipleDataSources) {
this.withinDataSourceRadioButton.setEnabled(multipleDataSources);
this.allDataSourcesRadioButton.setSelected(!multipleDataSources);
this.withinDataSourceRadioButton.setSelected(multipleDataSources);
this.withinDataSourceSelected(multipleDataSources);
}
void setDataSourceMap(Map<Long, String> dataSourceMap) {
this.dataSourceMap.clear();
this.dataSourceMap.putAll(dataSourceMap);
}
@NbBundle.Messages({
"IntraCasePanel.areSearchCriteriaMet.message=Cannot run intra-case correlation search."
})
boolean areSearchCriteriaMet() {
if(this.dataSourceMap.isEmpty()){
this.errorMessage = Bundle.IntraCasePanel_areSearchCriteriaMet_message();
return false;
} else {
return true;
}
}
String getErrorMessage() {
return this.errorMessage;
}
}

View File

@ -0,0 +1,86 @@
/*
*
* 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.commonfilesearch;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
import org.sleuthkit.datamodel.TskCoreException;
/**
*
*
*/
public class SingleInterCaseCommonAttributeSearcher extends InterCaseCommonAttributeSearcher {
private final int corrleationCaseId;
private String correlationCaseName;
/**
*
* @param correlationCaseId
* @param filterByMediaMimeType
* @param filterByDocMimeType
* @throws EamDbException
*/
public SingleInterCaseCommonAttributeSearcher(int correlationCaseId, Map<Long, String> dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) throws EamDbException {
super(dataSourceIdMap,filterByMediaMimeType, filterByDocMimeType);
this.corrleationCaseId = correlationCaseId;
this.correlationCaseName = "";
}
/**
* Collect metadata required to render the tree table where matches must
* occur in the case with the given ID.
*
* @param correlationCaseId id of case where matches must occur (no other matches will be shown)
* @return business object needed to populate tree table with results
* @throws TskCoreException
* @throws NoCurrentCaseException
* @throws SQLException
* @throws EamDbException
*/
@Override
public CommonAttributeSearchResults findFiles() throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException {
CorrelationCase cCase = this.getCorrelationCaseFromId(this.corrleationCaseId);
correlationCaseName = cCase.getDisplayName();
return this.findFiles(cCase);
}
CommonAttributeSearchResults findFiles(CorrelationCase correlationCase) throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException {
InterCaseSearchResultsProcessor eamDbAttrInst = new InterCaseSearchResultsProcessor(this.getDataSourceIdToNameMap());
Map<Integer, CommonAttributeValueList> interCaseCommonFiles = eamDbAttrInst.findSingleInterCaseCommonAttributeValues(Case.getCurrentCase(), correlationCase);
return new CommonAttributeSearchResults(interCaseCommonFiles);
}
@Override
String buildTabTitle() {
final String buildCategorySelectionString = this.buildCategorySelectionString();
final String titleTemplate = Bundle.AbstractCommonFilesMetadataBuilder_buildTabTitle_titleInterSingle();
return String.format(titleTemplate, new Object[]{correlationCaseName, buildCategorySelectionString});
}
}

View File

@ -20,13 +20,14 @@
package org.sleuthkit.autopsy.commonfilesearch;
import java.util.Map;
import org.sleuthkit.datamodel.TskData.FileKnown;
/**
* Provides logic for selecting common files from a single data source.
*/
final public class SingleDataSource extends CommonFilesMetadataBuilder {
final public class SingleIntraCaseCommonAttributeSearcher extends IntraCaseCommonAttributeSearcher {
private static final String WHERE_CLAUSE = "%s md5 in (select md5 from tsk_files where md5 in (select md5 from tsk_files where (known != 1 OR known IS NULL) and data_source_obj_id=%s%s) GROUP BY md5 HAVING COUNT(*) > 1) order by md5"; //NON-NLS
private static final String WHERE_CLAUSE = "%s md5 in (select md5 from tsk_files where md5 in (select md5 from tsk_files where (known != "+ FileKnown.KNOWN.getFileKnownValue() + " OR known IS NULL) and data_source_obj_id=%s%s) GROUP BY md5 HAVING COUNT(DISTINCT data_source_obj_id) > 1) order by md5"; //NON-NLS
private final Long selectedDataSourceId;
private final String dataSourceName;
@ -40,7 +41,7 @@ final public class SingleDataSource extends CommonFilesMetadataBuilder {
* @param filterByDocMimeType match only on files whose mime types can be
* broadly categorized as document types
*/
public SingleDataSource(Long dataSourceId, Map<Long, String> dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) {
public SingleIntraCaseCommonAttributeSearcher(Long dataSourceId, Map<Long, String> dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) {
super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType);
this.selectedDataSourceId = dataSourceId;
this.dataSourceName = dataSourceIdMap.get(this.selectedDataSourceId);
@ -49,13 +50,13 @@ final public class SingleDataSource extends CommonFilesMetadataBuilder {
@Override
protected String buildSqlSelectStatement() {
Object[] args = new String[]{SELECT_PREFIX, Long.toString(this.selectedDataSourceId), determineMimeTypeFilter()};
return String.format(SingleDataSource.WHERE_CLAUSE, args);
return String.format(SingleIntraCaseCommonAttributeSearcher.WHERE_CLAUSE, args);
}
@Override
protected String buildTabTitle() {
public String buildTabTitle() {
final String buildCategorySelectionString = this.buildCategorySelectionString();
final String titleTemplate = Bundle.CommonFilesMetadataBuilder_buildTabTitle_titleSingle();
final String titleTemplate = Bundle.AbstractCommonFilesMetadataBuilder_buildTabTitle_titleIntraSingle();
return String.format(titleTemplate, new Object[]{this.dataSourceName, buildCategorySelectionString});
}
}
}

View File

@ -33,7 +33,7 @@ import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.corecomponents.DataResultPanel;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
import org.sleuthkit.autopsy.corecomponents.SingleLayerTableFilterNode;
import org.sleuthkit.autopsy.corecomponents.TableFilterNode;
import org.sleuthkit.autopsy.directorytree.DataResultFilterNode;
/**
@ -142,7 +142,7 @@ public final class MessageBrowser extends JPanel implements ExplorerManager.Prov
rootNode = selectedNode;
}
messagesResultPanel.setPath(rootNode.getDisplayName());
messagesResultPanel.setNode(new SingleLayerTableFilterNode(new DataResultFilterNode(rootNode, gacExplorerManager), true));
messagesResultPanel.setNode(new TableFilterNode(new DataResultFilterNode(rootNode, gacExplorerManager), true));
}
}
}

View File

@ -38,7 +38,7 @@ import org.openide.util.NbBundle;
import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
import org.sleuthkit.autopsy.corecomponents.DataResultPanel;
import org.sleuthkit.autopsy.corecomponents.SingleLayerTableFilterNode;
import org.sleuthkit.autopsy.corecomponents.TableFilterNode;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.FileNode;
import org.sleuthkit.autopsy.datamodel.NodeProperty;
@ -99,7 +99,7 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont
@NbBundle.Messages("MessageContentViewer.AtrachmentsPanel.title=Attachments")
public MessageContentViewer() {
initComponents();
drp = DataResultPanel.createInstanceUninitialized(Bundle.MessageContentViewer_AtrachmentsPanel_title(), "", new SingleLayerTableFilterNode(Node.EMPTY, false), 0, null);
drp = DataResultPanel.createInstanceUninitialized(Bundle.MessageContentViewer_AtrachmentsPanel_title(), "", new TableFilterNode(Node.EMPTY, false), 0, null);
attachmentsScrollPane.setViewportView(drp);
msgbodyTabbedPane.setEnabledAt(ATTM_TAB_INDEX, true);
@ -596,7 +596,7 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont
msgbodyTabbedPane.setEnabledAt(ATTM_TAB_INDEX, numberOfAttachments > 0);
msgbodyTabbedPane.setTitleAt(ATTM_TAB_INDEX, "Attachments (" + numberOfAttachments + ")");
drp.setNode(new SingleLayerTableFilterNode(new DataResultFilterNode(new AbstractNode(
drp.setNode(new TableFilterNode(new DataResultFilterNode(new AbstractNode(
new AttachmentsChildren(attachments)), null), true));
}

Some files were not shown because too many files have changed in this diff Show More