Merge branch 'develop' of https://github.com/sleuthkit/autopsy into 3868-normalize-cr

# Conflicts:
#	Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/OtherOccurrenceNodeData.java
This commit is contained in:
Brian Sweeney 2018-08-07 17:43:27 -06:00
commit 49e53f4780
24 changed files with 985 additions and 498 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -52,7 +52,6 @@ final class CentralRepoCommentDialog extends javax.swing.JDialog {
currentComment = instance.getComment(); currentComment = instance.getComment();
} }
pathLabel.setText(instance.getFilePath());
commentTextArea.setText(instance.getComment()); commentTextArea.setText(instance.getComment());
this.correlationAttribute = correlationAttribute; this.correlationAttribute = correlationAttribute;
@ -103,8 +102,6 @@ final class CentralRepoCommentDialog extends javax.swing.JDialog {
commentTextArea = new javax.swing.JTextArea(); commentTextArea = new javax.swing.JTextArea();
okButton = new javax.swing.JButton(); okButton = new javax.swing.JButton();
cancelButton = new javax.swing.JButton(); cancelButton = new javax.swing.JButton();
fileLabel = new javax.swing.JLabel();
pathLabel = new javax.swing.JLabel();
commentLabel = new javax.swing.JLabel(); commentLabel = new javax.swing.JLabel();
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
@ -131,10 +128,6 @@ final class CentralRepoCommentDialog extends javax.swing.JDialog {
} }
}); });
org.openide.awt.Mnemonics.setLocalizedText(fileLabel, org.openide.util.NbBundle.getMessage(CentralRepoCommentDialog.class, "CentralRepoCommentDialog.fileLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(pathLabel, org.openide.util.NbBundle.getMessage(CentralRepoCommentDialog.class, "CentralRepoCommentDialog.pathLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(commentLabel, org.openide.util.NbBundle.getMessage(CentralRepoCommentDialog.class, "CentralRepoCommentDialog.commentLabel.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(commentLabel, org.openide.util.NbBundle.getMessage(CentralRepoCommentDialog.class, "CentralRepoCommentDialog.commentLabel.text")); // NOI18N
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
@ -146,12 +139,7 @@ final class CentralRepoCommentDialog extends javax.swing.JDialog {
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 500, Short.MAX_VALUE) .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 500, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(commentLabel)
.addGroup(layout.createSequentialGroup()
.addComponent(fileLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(pathLabel))
.addComponent(commentLabel))
.addGap(0, 451, Short.MAX_VALUE)) .addGap(0, 451, Short.MAX_VALUE))
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addGap(0, 0, Short.MAX_VALUE) .addGap(0, 0, Short.MAX_VALUE)
@ -164,17 +152,13 @@ final class CentralRepoCommentDialog extends javax.swing.JDialog {
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addContainerGap() .addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(fileLabel)
.addComponent(pathLabel))
.addGap(19, 19, 19)
.addComponent(commentLabel) .addComponent(commentLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jScrollPane1) .addComponent(jScrollPane1)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(okButton) .addComponent(cancelButton)
.addComponent(cancelButton)) .addComponent(okButton))
.addContainerGap()) .addContainerGap())
); );
@ -197,9 +181,7 @@ final class CentralRepoCommentDialog extends javax.swing.JDialog {
private javax.swing.JButton cancelButton; private javax.swing.JButton cancelButton;
private javax.swing.JLabel commentLabel; private javax.swing.JLabel commentLabel;
private javax.swing.JTextArea commentTextArea; private javax.swing.JTextArea commentTextArea;
private javax.swing.JLabel fileLabel;
private javax.swing.JScrollPane jScrollPane1; private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JButton okButton; private javax.swing.JButton okButton;
private javax.swing.JLabel pathLabel;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
} }

View File

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

View File

@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.centralrepository.contentviewer; package org.sleuthkit.autopsy.centralrepository.contentviewer;
import java.awt.Component; import java.awt.Component;
import java.awt.FontMetrics;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.io.BufferedWriter; import java.io.BufferedWriter;
@ -87,7 +88,10 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
private static final long serialVersionUID = -1L; private static final long serialVersionUID = -1L;
private final static Logger logger = Logger.getLogger(DataContentViewerOtherCases.class.getName()); private static final Logger logger = Logger.getLogger(DataContentViewerOtherCases.class.getName());
private static final int DEFAULT_MIN_CELL_WIDTH = 15;
private static final int CELL_TEXT_WIDTH_PADDING = 5;
private final DataContentViewerOtherCasesTableModel tableModel; private final DataContentViewerOtherCasesTableModel tableModel;
private final Collection<CorrelationAttribute> correlationAttributes; private final Collection<CorrelationAttribute> correlationAttributes;
@ -127,7 +131,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
showCommonalityDetails(); showCommonalityDetails();
} else if (jmi.equals(addCommentMenuItem)) { } else if (jmi.equals(addCommentMenuItem)) {
try { try {
OtherOccurrenceNodeData selectedNode = (OtherOccurrenceNodeData) tableModel.getRow(otherCasesTable.getSelectedRow()); OtherOccurrenceNodeInstanceData selectedNode = (OtherOccurrenceNodeInstanceData) tableModel.getRow(otherCasesTable.getSelectedRow());
AddEditCentralRepoCommentAction action = new AddEditCentralRepoCommentAction(selectedNode.createCorrelationAttribute()); AddEditCentralRepoCommentAction action = new AddEditCentralRepoCommentAction(selectedNode.createCorrelationAttribute());
action.actionPerformed(null); action.actionPerformed(null);
String currentComment = action.getComment(); String currentComment = action.getComment();
@ -151,7 +155,6 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
// Set background of every nth row as light grey. // Set background of every nth row as light grey.
TableCellRenderer renderer = new DataContentViewerOtherCasesTableCellRenderer(); TableCellRenderer renderer = new DataContentViewerOtherCasesTableCellRenderer();
otherCasesTable.setDefaultRenderer(Object.class, renderer); otherCasesTable.setDefaultRenderer(Object.class, renderer);
tableStatusPanelLabel.setVisible(false);
} }
@ -215,7 +218,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
if (-1 != selectedRowViewIdx) { if (-1 != selectedRowViewIdx) {
EamDb dbManager = EamDb.getInstance(); EamDb dbManager = EamDb.getInstance();
int selectedRowModelIdx = otherCasesTable.convertRowIndexToModel(selectedRowViewIdx); int selectedRowModelIdx = otherCasesTable.convertRowIndexToModel(selectedRowViewIdx);
OtherOccurrenceNodeData nodeData = (OtherOccurrenceNodeData) tableModel.getRow(selectedRowModelIdx); OtherOccurrenceNodeInstanceData nodeData = (OtherOccurrenceNodeInstanceData) tableModel.getRow(selectedRowModelIdx);
CorrelationCase eamCasePartial = nodeData.getCorrelationAttributeInstance().getCorrelationCase(); CorrelationCase eamCasePartial = nodeData.getCorrelationAttributeInstance().getCorrelationCase();
if (eamCasePartial == null) { if (eamCasePartial == null) {
JOptionPane.showConfirmDialog(showCaseDetailsMenuItem, JOptionPane.showConfirmDialog(showCaseDetailsMenuItem,
@ -478,12 +481,12 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
@Messages({"DataContentViewerOtherCases.earliestCaseNotAvailable= Not Enabled."}) @Messages({"DataContentViewerOtherCases.earliestCaseNotAvailable= Not Enabled."})
/** /**
* Gets the list of Eam Cases and determines the earliest case creation date. * Gets the list of Eam Cases and determines the earliest case creation
* Sets the label to display the earliest date string to the user. * date. Sets the label to display the earliest date string to the user.
*/ */
private void setEarliestCaseDate() { private void setEarliestCaseDate() {
String dateStringDisplay = Bundle.DataContentViewerOtherCases_earliestCaseNotAvailable(); String dateStringDisplay = Bundle.DataContentViewerOtherCases_earliestCaseNotAvailable();
if (EamDb.isEnabled()) { if (EamDb.isEnabled()) {
LocalDateTime earliestDate = LocalDateTime.now(DateTimeZone.UTC); LocalDateTime earliestDate = LocalDateTime.now(DateTimeZone.UTC);
DateFormat datetimeFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.US); DateFormat datetimeFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.US);
@ -491,15 +494,15 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
EamDb dbManager = EamDb.getInstance(); EamDb dbManager = EamDb.getInstance();
List<CorrelationCase> cases = dbManager.getCases(); List<CorrelationCase> cases = dbManager.getCases();
for (CorrelationCase aCase : cases) { for (CorrelationCase aCase : cases) {
LocalDateTime caseDate = LocalDateTime.fromDateFields(datetimeFormat.parse(aCase.getCreationDate())); LocalDateTime caseDate = LocalDateTime.fromDateFields(datetimeFormat.parse(aCase.getCreationDate()));
if (caseDate.isBefore(earliestDate)) { if (caseDate.isBefore(earliestDate)) {
earliestDate = caseDate; earliestDate = caseDate;
dateStringDisplay = aCase.getCreationDate(); dateStringDisplay = aCase.getCreationDate();
} }
} }
} catch (EamDbException ex) { } catch (EamDbException ex) {
logger.log(Level.SEVERE, "Error getting list of cases from database.", ex); // NON-NLS logger.log(Level.SEVERE, "Error getting list of cases from database.", ex); // NON-NLS
} catch (ParseException ex) { } catch (ParseException ex) {
@ -511,10 +514,10 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
} }
/** /**
* Query the central repo database (if enabled) and the case database to find all * Query the central repo database (if enabled) and the case database to
* artifact instances correlated to the given central repository artifact. If the * find all artifact instances correlated to the given central repository
* central repo is not enabled, this will only return files from the current case * artifact. If the central repo is not enabled, this will only return files
* with matching MD5 hashes. * from the current case with matching MD5 hashes.
* *
* @param corAttr CorrelationAttribute to query for * @param corAttr CorrelationAttribute to query for
* @param dataSourceName Data source to filter results * @param dataSourceName Data source to filter results
@ -522,19 +525,19 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
* *
* @return A collection of correlated artifact instances * @return A collection of correlated artifact instances
*/ */
private Map<UniquePathKey,OtherOccurrenceNodeData> getCorrelatedInstances(CorrelationAttribute corAttr, String dataSourceName, String deviceId) { private Map<UniquePathKey, OtherOccurrenceNodeInstanceData> getCorrelatedInstances(CorrelationAttribute corAttr, String dataSourceName, String deviceId) {
// @@@ Check exception // @@@ Check exception
try { try {
final Case openCase = Case.getCurrentCase(); final Case openCase = Case.getCurrentCase();
String caseUUID = openCase.getName(); String caseUUID = openCase.getName();
HashMap<UniquePathKey,OtherOccurrenceNodeData> nodeDataMap = new HashMap<>(); HashMap<UniquePathKey, OtherOccurrenceNodeInstanceData> nodeDataMap = new HashMap<>();
if (EamDb.isEnabled()) { if (EamDb.isEnabled()) {
List<CorrelationAttributeInstance> instances = EamDb.getInstance().getArtifactInstancesByTypeValue(corAttr.getCorrelationType(), corAttr.getCorrelationValue()); List<CorrelationAttributeInstance> instances = EamDb.getInstance().getArtifactInstancesByTypeValue(corAttr.getCorrelationType(), corAttr.getCorrelationValue());
for (CorrelationAttributeInstance artifactInstance:instances) { for (CorrelationAttributeInstance artifactInstance : instances) {
// Only add the attribute if it isn't the object the user selected. // Only add the attribute if it isn't the object the user selected.
// We consider it to be a different object if at least one of the following is true: // We consider it to be a different object if at least one of the following is true:
// - the case UUID is different // - the case UUID is different
@ -546,14 +549,14 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|| !artifactInstance.getCorrelationDataSource().getDeviceID().equals(deviceId) || !artifactInstance.getCorrelationDataSource().getDeviceID().equals(deviceId)
|| !artifactInstance.getFilePath().equalsIgnoreCase(file.getParentPath() + file.getName())) { || !artifactInstance.getFilePath().equalsIgnoreCase(file.getParentPath() + file.getName())) {
OtherOccurrenceNodeData newNode = new OtherOccurrenceNodeData(artifactInstance, corAttr.getCorrelationType(), corAttr.getCorrelationValue()); OtherOccurrenceNodeInstanceData newNode = new OtherOccurrenceNodeInstanceData(artifactInstance, corAttr.getCorrelationType(), corAttr.getCorrelationValue());
UniquePathKey uniquePathKey = new UniquePathKey(newNode); UniquePathKey uniquePathKey = new UniquePathKey(newNode);
nodeDataMap.put(uniquePathKey, newNode); nodeDataMap.put(uniquePathKey, newNode);
} }
} }
} }
if (corAttr.getCorrelationType().getDisplayName().equals("Files")) { if (corAttr.getCorrelationType().getDisplayName().equals("Files")) {
List<AbstractFile> caseDbFiles = getCaseDbMatches(corAttr, openCase); List<AbstractFile> caseDbFiles = getCaseDbMatches(corAttr, openCase);
for (AbstractFile caseDbFile : caseDbFiles) { for (AbstractFile caseDbFile : caseDbFiles) {
@ -576,13 +579,17 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
} }
/** /**
* Get all other abstract files in the current case with the same MD5 as the selected node. * Get all other abstract files in the current case with the same MD5 as the
* selected node.
*
* @param corAttr The CorrelationAttribute containing the MD5 to search for * @param corAttr The CorrelationAttribute containing the MD5 to search for
* @param openCase The current case * @param openCase The current case
*
* @return List of matching AbstractFile objects * @return List of matching AbstractFile objects
*
* @throws NoCurrentCaseException * @throws NoCurrentCaseException
* @throws TskCoreException * @throws TskCoreException
* @throws EamDbException * @throws EamDbException
*/ */
private List<AbstractFile> getCaseDbMatches(CorrelationAttribute corAttr, Case openCase) throws NoCurrentCaseException, TskCoreException, EamDbException { private List<AbstractFile> getCaseDbMatches(CorrelationAttribute corAttr, Case openCase) throws NoCurrentCaseException, TskCoreException, EamDbException {
String md5 = corAttr.getCorrelationValue(); String md5 = corAttr.getCorrelationValue();
@ -602,18 +609,18 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
/** /**
* Adds the file to the nodeDataMap map if it does not already exist * Adds the file to the nodeDataMap map if it does not already exist
* *
* @param autopsyCase * @param autopsyCase
* @param nodeDataMap * @param nodeDataMap
* @param newFile * @param newFile
* *
* @throws TskCoreException * @throws TskCoreException
* @throws EamDbException * @throws EamDbException
*/ */
private void addOrUpdateNodeData(final Case autopsyCase, Map<UniquePathKey,OtherOccurrenceNodeData> nodeDataMap, AbstractFile newFile) throws TskCoreException, EamDbException { private void addOrUpdateNodeData(final Case autopsyCase, Map<UniquePathKey, OtherOccurrenceNodeInstanceData> nodeDataMap, AbstractFile newFile) throws TskCoreException, EamDbException {
OtherOccurrenceNodeData newNode = new OtherOccurrenceNodeData(newFile, autopsyCase); OtherOccurrenceNodeInstanceData newNode = new OtherOccurrenceNodeInstanceData(newFile, autopsyCase);
// If the caseDB object has a notable tag associated with it, update // If the caseDB object has a notable tag associated with it, update
// the known status to BAD // the known status to BAD
if (newNode.getKnown() != TskData.FileKnown.BAD) { if (newNode.getKnown() != TskData.FileKnown.BAD) {
@ -629,13 +636,13 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
// Make a key to see if the file is already in the map // Make a key to see if the file is already in the map
UniquePathKey uniquePathKey = new UniquePathKey(newNode); UniquePathKey uniquePathKey = new UniquePathKey(newNode);
// If this node is already in the list, the only thing we need to do is // 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. // 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. // Otherwise this is a new node so add the new node to the map.
if (nodeDataMap.containsKey(uniquePathKey)) { if (nodeDataMap.containsKey(uniquePathKey)) {
if (newNode.getKnown() == TskData.FileKnown.BAD) { if (newNode.getKnown() == TskData.FileKnown.BAD) {
OtherOccurrenceNodeData prevInstance = nodeDataMap.get(uniquePathKey); OtherOccurrenceNodeInstanceData prevInstance = nodeDataMap.get(uniquePathKey);
prevInstance.updateKnown(newNode.getKnown()); prevInstance.updateKnown(newNode.getKnown());
} }
} else { } else {
@ -658,7 +665,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
} else { } else {
return this.file != null return this.file != null
&& this.file.getSize() > 0 && this.file.getSize() > 0
&& ((this.file.getMd5Hash() != null) && ( ! this.file.getMd5Hash().isEmpty())); && ((this.file.getMd5Hash() != null) && (!this.file.getMd5Hash().isEmpty()));
} }
} }
@ -681,8 +688,10 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
* *
* @param node The node being viewed. * @param node The node being viewed.
*/ */
@Messages({"DataContentViewerOtherCases.table.isempty=There are no associated artifacts or files from other occurrences to display.", @Messages({
"DataContentViewerOtherCases.table.noArtifacts=Correlation cannot be performed on the selected file."}) "DataContentViewerOtherCases.table.noArtifacts=Item has no attributes with which to search.",
"DataContentViewerOtherCases.table.noResultsFound=No results found."
})
private void populateTable(Node node) { private void populateTable(Node node) {
String dataSourceName = ""; String dataSourceName = "";
String deviceId = ""; String deviceId = "";
@ -700,7 +709,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
// get the attributes we can correlate on // get the attributes we can correlate on
correlationAttributes.addAll(getCorrelationAttributesFromNode(node)); correlationAttributes.addAll(getCorrelationAttributesFromNode(node));
for (CorrelationAttribute corAttr : correlationAttributes) { for (CorrelationAttribute corAttr : correlationAttributes) {
Map<UniquePathKey,OtherOccurrenceNodeData> correlatedNodeDataMap = new HashMap<>(0); Map<UniquePathKey, OtherOccurrenceNodeInstanceData> correlatedNodeDataMap = new HashMap<>(0);
// get correlation and reference set instances from DB // get correlation and reference set instances from DB
correlatedNodeDataMap.putAll(getCorrelatedInstances(corAttr, dataSourceName, deviceId)); correlatedNodeDataMap.putAll(getCorrelatedInstances(corAttr, dataSourceName, deviceId));
@ -712,36 +721,45 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
} }
if (correlationAttributes.isEmpty()) { if (correlationAttributes.isEmpty()) {
// @@@ BC: We should have a more descriptive message than this. Mention that the file didn't have a MD5, etc. tableModel.addNodeData(new OtherOccurrenceNodeMessageData(Bundle.DataContentViewerOtherCases_table_noArtifacts()));
displayMessageOnTableStatusPanel(Bundle.DataContentViewerOtherCases_table_noArtifacts()); setColumnWidthToText(0, Bundle.DataContentViewerOtherCases_table_noArtifacts());
} else if (0 == tableModel.getRowCount()) { } else if (0 == tableModel.getRowCount()) {
displayMessageOnTableStatusPanel(Bundle.DataContentViewerOtherCases_table_isempty()); tableModel.addNodeData(new OtherOccurrenceNodeMessageData(Bundle.DataContentViewerOtherCases_table_noResultsFound()));
setColumnWidthToText(0, Bundle.DataContentViewerOtherCases_table_noResultsFound());
} else { } else {
clearMessageOnTableStatusPanel();
setColumnWidths(); setColumnWidths();
} }
setEarliestCaseDate(); setEarliestCaseDate();
} }
/**
* Adjust a given column for the text provided.
*
* @param columnIndex The index of the column to adjust.
* @param text The text whose length will be used to adjust the
* column width.
*/
private void setColumnWidthToText(int columnIndex, String text) {
TableColumn column = otherCasesTable.getColumnModel().getColumn(columnIndex);
FontMetrics fontMetrics = otherCasesTable.getFontMetrics(otherCasesTable.getFont());
int stringWidth = fontMetrics.stringWidth(text);
column.setMinWidth(stringWidth + CELL_TEXT_WIDTH_PADDING);
}
/**
* Adjust column widths to their preferred values.
*/
private void setColumnWidths() { private void setColumnWidths() {
for (int idx = 0; idx < tableModel.getColumnCount(); idx++) { for (int idx = 0; idx < tableModel.getColumnCount(); idx++) {
TableColumn column = otherCasesTable.getColumnModel().getColumn(idx); TableColumn column = otherCasesTable.getColumnModel().getColumn(idx);
int colWidth = tableModel.getColumnPreferredWidth(idx); column.setMinWidth(DEFAULT_MIN_CELL_WIDTH);
if (0 < colWidth) { int columnWidth = tableModel.getColumnPreferredWidth(idx);
column.setPreferredWidth(colWidth); if (columnWidth > 0) {
column.setPreferredWidth(columnWidth);
} }
} }
} }
private void displayMessageOnTableStatusPanel(String message) {
tableStatusPanelLabel.setText(message);
tableStatusPanelLabel.setVisible(true);
}
private void clearMessageOnTableStatusPanel() {
tableStatusPanelLabel.setVisible(false);
}
/** /**
* This method is called from within the constructor to initialize the form. * This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always * WARNING: Do NOT modify this code. The content of this method is always
@ -765,7 +783,6 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
earliestCaseLabel = new javax.swing.JLabel(); earliestCaseLabel = new javax.swing.JLabel();
earliestCaseDate = new javax.swing.JLabel(); earliestCaseDate = new javax.swing.JLabel();
tableStatusPanel = new javax.swing.JPanel(); tableStatusPanel = new javax.swing.JPanel();
tableStatusPanelLabel = new javax.swing.JLabel();
rightClickPopupMenu.addPopupMenuListener(new javax.swing.event.PopupMenuListener() { rightClickPopupMenu.addPopupMenuListener(new javax.swing.event.PopupMenuListener() {
public void popupMenuCanceled(javax.swing.event.PopupMenuEvent evt) { public void popupMenuCanceled(javax.swing.event.PopupMenuEvent evt) {
@ -827,8 +844,6 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
.addGap(0, 16, Short.MAX_VALUE) .addGap(0, 16, Short.MAX_VALUE)
); );
tableStatusPanelLabel.setForeground(new java.awt.Color(255, 0, 51));
javax.swing.GroupLayout tableContainerPanelLayout = new javax.swing.GroupLayout(tableContainerPanel); javax.swing.GroupLayout tableContainerPanelLayout = new javax.swing.GroupLayout(tableContainerPanel);
tableContainerPanel.setLayout(tableContainerPanelLayout); tableContainerPanel.setLayout(tableContainerPanelLayout);
tableContainerPanelLayout.setHorizontalGroup( tableContainerPanelLayout.setHorizontalGroup(
@ -841,20 +856,16 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
.addComponent(earliestCaseLabel) .addComponent(earliestCaseLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(earliestCaseDate) .addComponent(earliestCaseDate)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addComponent(tableStatusPanelLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addContainerGap())
); );
tableContainerPanelLayout.setVerticalGroup( tableContainerPanelLayout.setVerticalGroup(
tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, tableContainerPanelLayout.createSequentialGroup() .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, tableContainerPanelLayout.createSequentialGroup()
.addComponent(tableScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 176, Short.MAX_VALUE) .addComponent(tableScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 27, Short.MAX_VALUE)
.addGap(0, 0, 0) .addGap(2, 2, 2)
.addGroup(tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) .addGroup(tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addGroup(tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(earliestCaseLabel)
.addComponent(earliestCaseLabel) .addComponent(earliestCaseDate))
.addComponent(earliestCaseDate))
.addComponent(tableStatusPanelLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGap(0, 0, 0) .addGap(0, 0, 0)
.addComponent(tableStatusPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(tableStatusPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 0, 0)) .addGap(0, 0, 0))
@ -873,7 +884,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
.addGap(0, 483, Short.MAX_VALUE) .addGap(0, 483, Short.MAX_VALUE)
.addGroup(otherCasesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(otherCasesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(otherCasesPanelLayout.createSequentialGroup() .addGroup(otherCasesPanelLayout.createSequentialGroup()
.addComponent(tableContainerPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 483, Short.MAX_VALUE) .addComponent(tableContainerPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 59, Short.MAX_VALUE)
.addGap(0, 0, 0))) .addGap(0, 0, 0)))
); );
@ -885,7 +896,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
); );
layout.setVerticalGroup( layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(otherCasesPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 483, Short.MAX_VALUE) .addComponent(otherCasesPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 59, Short.MAX_VALUE)
); );
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
@ -895,8 +906,9 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
if (EamDbUtil.useCentralRepo() && otherCasesTable.getSelectedRowCount() == 1) { if (EamDbUtil.useCentralRepo() && otherCasesTable.getSelectedRowCount() == 1) {
int rowIndex = otherCasesTable.getSelectedRow(); int rowIndex = otherCasesTable.getSelectedRow();
OtherOccurrenceNodeData selectedNode = (OtherOccurrenceNodeData) tableModel.getRow(rowIndex); OtherOccurrenceNodeData selectedNode = (OtherOccurrenceNodeData) tableModel.getRow(rowIndex);
if (selectedNode.isCentralRepoNode()) { if (selectedNode instanceof OtherOccurrenceNodeInstanceData) {
enableCentralRepoActions = true; OtherOccurrenceNodeInstanceData instanceData = (OtherOccurrenceNodeInstanceData) selectedNode;
enableCentralRepoActions = instanceData.isCentralRepoNode();
} }
} }
@ -920,7 +932,6 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
private javax.swing.JPanel tableContainerPanel; private javax.swing.JPanel tableContainerPanel;
private javax.swing.JScrollPane tableScrollPane; private javax.swing.JScrollPane tableScrollPane;
private javax.swing.JPanel tableStatusPanel; private javax.swing.JPanel tableStatusPanel;
private javax.swing.JLabel tableStatusPanelLabel;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
/** /**
@ -933,7 +944,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
private final String filePath; private final String filePath;
private final String type; private final String type;
UniquePathKey(OtherOccurrenceNodeData nodeData) { UniquePathKey(OtherOccurrenceNodeInstanceData nodeData) {
super(); super();
dataSourceID = nodeData.getDeviceID(); dataSourceID = nodeData.getDeviceID();
if (nodeData.getFilePath() != null) { if (nodeData.getFilePath() != null) {
@ -947,9 +958,9 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
@Override @Override
public boolean equals(Object other) { public boolean equals(Object other) {
if (other instanceof UniquePathKey) { if (other instanceof UniquePathKey) {
UniquePathKey otherKey = (UniquePathKey)(other); UniquePathKey otherKey = (UniquePathKey) (other);
return ( Objects.equals(otherKey.dataSourceID, this.dataSourceID) return (Objects.equals(otherKey.dataSourceID, this.dataSourceID)
&& Objects.equals(otherKey.filePath, this.filePath) && Objects.equals(otherKey.filePath, this.filePath)
&& Objects.equals(otherKey.type, this.type)); && Objects.equals(otherKey.type, this.type));
} }
return false; return false;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,15 +1,15 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2018 Basis Technology Corp. * Copyright 2011-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -55,23 +55,33 @@ public class Tags implements AutopsyVisitableItem {
// override of Children.Keys<T>.createNodes(). // override of Children.Keys<T>.createNodes().
private final TagResults tagResults = new TagResults(); private final TagResults tagResults = new TagResults();
private final String DISPLAY_NAME = NbBundle.getMessage(RootNode.class, "TagsNode.displayName.text"); private final static String DISPLAY_NAME = NbBundle.getMessage(RootNode.class, "TagsNode.displayName.text");
private static final String USER_NAME_PROPERTY = "user.name"; //NON-NLS
private final String ICON_PATH = "org/sleuthkit/autopsy/images/tag-folder-blue-icon-16.png"; //NON-NLS private final String ICON_PATH = "org/sleuthkit/autopsy/images/tag-folder-blue-icon-16.png"; //NON-NLS
private final long datasourceObjId; private final long datasourceObjId;
Tags() { Tags() {
this(0); this(0);
} }
Tags(long dsObjId) { Tags(long dsObjId) {
this.datasourceObjId = dsObjId; this.datasourceObjId = dsObjId;
} }
/**
* Return the display name used by the tags node in the tree.
*
* @return - DISPLAY_NAME
*/
public static String getTagsDisplayName() {
return DISPLAY_NAME;
}
long filteringDataSourceObjId() { long filteringDataSourceObjId() {
return this.datasourceObjId; return this.datasourceObjId;
} }
@Override @Override
public <T> T accept(AutopsyItemVisitor<T> visitor) { public <T> T accept(AutopsyItemVisitor<T> visitor) {
return visitor.visit(this); return visitor.visit(this);
@ -98,13 +108,11 @@ public class Tags implements AutopsyVisitableItem {
*/ */
public class RootNode extends DisplayableItemNode { public class RootNode extends DisplayableItemNode {
public RootNode(long objId) { public RootNode(long objId) {
super(Children.create(new TagNameNodeFactory(objId), true), Lookups.singleton(DISPLAY_NAME)); super(Children.create(new TagNameNodeFactory(objId), true), Lookups.singleton(DISPLAY_NAME));
super.setName(DISPLAY_NAME); super.setName(DISPLAY_NAME);
super.setDisplayName(DISPLAY_NAME); super.setDisplayName(DISPLAY_NAME);
this.setIconBaseWithExtension(ICON_PATH); this.setIconBaseWithExtension(ICON_PATH);
} }
@Override @Override
@ -134,12 +142,20 @@ public class Tags implements AutopsyVisitableItem {
public String getItemType() { public String getItemType() {
return getClass().getName(); return getClass().getName();
} }
/**
* Cause the contents of the RootNode and its children to be updated.
*/
public void refresh() {
tagResults.update();
}
} }
private class TagNameNodeFactory extends ChildFactory.Detachable<TagName> implements Observer { private class TagNameNodeFactory extends ChildFactory.Detachable<TagName> implements Observer {
private final long datasourceObjId; private final long datasourceObjId;
private final Set<Case.Events> CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED, private final Set<Case.Events> CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED,
Case.Events.BLACKBOARD_ARTIFACT_TAG_DELETED, Case.Events.BLACKBOARD_ARTIFACT_TAG_DELETED,
Case.Events.CONTENT_TAG_ADDED, Case.Events.CONTENT_TAG_ADDED,
@ -197,13 +213,14 @@ public class Tags implements AutopsyVisitableItem {
/** /**
* Constructor * Constructor
*
* @param objId data source object id * @param objId data source object id
*/ */
TagNameNodeFactory(long objId) { TagNameNodeFactory(long objId) {
this.datasourceObjId = objId; this.datasourceObjId = objId;
} }
@Override @Override
protected void addNotify() { protected void addNotify() {
IngestManager.getInstance().addIngestJobEventListener(pcl); IngestManager.getInstance().addIngestJobEventListener(pcl);
@ -224,11 +241,17 @@ public class Tags implements AutopsyVisitableItem {
@Override @Override
protected boolean createKeys(List<TagName> keys) { protected boolean createKeys(List<TagName> keys) {
try { try {
List<TagName> tagNamesInUse;
List<TagName> tagNamesInUse = UserPreferences.groupItemsInTreeByDatasource() ? if (UserPreferences.showOnlyCurrentUserTags()) {
Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUse(datasourceObjId) : String userName = System.getProperty(USER_NAME_PROPERTY);
Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUse() tagNamesInUse = UserPreferences.groupItemsInTreeByDatasource()
; ? Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUseForUser(datasourceObjId, userName)
: Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUseForUser(userName);
} else {
tagNamesInUse = UserPreferences.groupItemsInTreeByDatasource()
? Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUse(datasourceObjId)
: Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUse();
}
Collections.sort(tagNamesInUse); Collections.sort(tagNamesInUse);
keys.addAll(tagNamesInUse); keys.addAll(tagNamesInUse);
} catch (TskCoreException | NoCurrentCaseException ex) { } catch (TskCoreException | NoCurrentCaseException ex) {
@ -276,15 +299,24 @@ public class Tags implements AutopsyVisitableItem {
long tagsCount = 0; long tagsCount = 0;
try { try {
TagsManager tm = Case.getCurrentCaseThrows().getServices().getTagsManager(); TagsManager tm = Case.getCurrentCaseThrows().getServices().getTagsManager();
if (UserPreferences.groupItemsInTreeByDatasource()) { if (UserPreferences.showOnlyCurrentUserTags()) {
tagsCount = tm.getContentTagsCountByTagName(tagName, datasourceObjId); String userName = System.getProperty(USER_NAME_PROPERTY);
tagsCount += tm.getBlackboardArtifactTagsCountByTagName(tagName, datasourceObjId); if (UserPreferences.groupItemsInTreeByDatasource()) {
tagsCount = tm.getContentTagsCountByTagNameForUser(tagName, datasourceObjId, userName);
tagsCount += tm.getBlackboardArtifactTagsCountByTagNameForUser(tagName, datasourceObjId, userName);
} else {
tagsCount = tm.getContentTagsCountByTagNameForUser(tagName, userName);
tagsCount += tm.getBlackboardArtifactTagsCountByTagNameForUser(tagName, userName);
}
} else {
if (UserPreferences.groupItemsInTreeByDatasource()) {
tagsCount = tm.getContentTagsCountByTagName(tagName, datasourceObjId);
tagsCount += tm.getBlackboardArtifactTagsCountByTagName(tagName, datasourceObjId);
} else {
tagsCount = tm.getContentTagsCountByTagName(tagName);
tagsCount += tm.getBlackboardArtifactTagsCountByTagName(tagName);
}
} }
else {
tagsCount = tm.getContentTagsCountByTagName(tagName);
tagsCount += tm.getBlackboardArtifactTagsCountByTagName(tagName);
}
} catch (TskCoreException | NoCurrentCaseException ex) { } catch (TskCoreException | NoCurrentCaseException ex) {
Logger.getLogger(TagNameNode.class.getName()).log(Level.SEVERE, "Failed to get tags count for " + tagName.getDisplayName() + " tag name", ex); //NON-NLS Logger.getLogger(TagNameNode.class.getName()).log(Level.SEVERE, "Failed to get tags count for " + tagName.getDisplayName() + " tag name", ex); //NON-NLS
} }
@ -387,9 +419,17 @@ public class Tags implements AutopsyVisitableItem {
private void updateDisplayName() { private void updateDisplayName() {
long tagsCount = 0; long tagsCount = 0;
try { try {
tagsCount = UserPreferences.groupItemsInTreeByDatasource() ?
Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsCountByTagName(tagName, datasourceObjId) : if (UserPreferences.showOnlyCurrentUserTags()) {
Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsCountByTagName(tagName); String userName = System.getProperty(USER_NAME_PROPERTY);
tagsCount = UserPreferences.groupItemsInTreeByDatasource()
? Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsCountByTagNameForUser(tagName, datasourceObjId, userName)
: Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsCountByTagNameForUser(tagName, userName);
} else {
tagsCount = UserPreferences.groupItemsInTreeByDatasource()
? Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsCountByTagName(tagName, datasourceObjId)
: Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsCountByTagName(tagName);
}
} catch (TskCoreException | NoCurrentCaseException ex) { } catch (TskCoreException | NoCurrentCaseException ex) {
Logger.getLogger(ContentTagTypeNode.class.getName()).log(Level.SEVERE, "Failed to get content tags count for " + tagName.getDisplayName() + " tag name", ex); //NON-NLS Logger.getLogger(ContentTagTypeNode.class.getName()).log(Level.SEVERE, "Failed to get content tags count for " + tagName.getDisplayName() + " tag name", ex); //NON-NLS
} }
@ -444,11 +484,19 @@ public class Tags implements AutopsyVisitableItem {
protected boolean createKeys(List<ContentTag> keys) { protected boolean createKeys(List<ContentTag> keys) {
// Use the content tags bearing the specified tag name as the keys. // Use the content tags bearing the specified tag name as the keys.
try { try {
List<ContentTag> contentTags = UserPreferences.groupItemsInTreeByDatasource() ? List<ContentTag> contentTags = UserPreferences.groupItemsInTreeByDatasource()
Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByTagName(tagName, datasourceObjId) : ? Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByTagName(tagName, datasourceObjId)
Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByTagName(tagName); : Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByTagName(tagName);
if (UserPreferences.showOnlyCurrentUserTags()) {
keys.addAll(contentTags); String userName = System.getProperty(USER_NAME_PROPERTY);
for (ContentTag tag : contentTags) {
if (userName.equals(tag.getUserName())) {
keys.add(tag);
}
}
} else {
keys.addAll(contentTags);
}
} catch (TskCoreException | NoCurrentCaseException ex) { } catch (TskCoreException | NoCurrentCaseException ex) {
Logger.getLogger(ContentTagNodeFactory.class.getName()).log(Level.SEVERE, "Failed to get tag names", ex); //NON-NLS Logger.getLogger(ContentTagNodeFactory.class.getName()).log(Level.SEVERE, "Failed to get tag names", ex); //NON-NLS
} }
@ -492,9 +540,16 @@ public class Tags implements AutopsyVisitableItem {
private void updateDisplayName() { private void updateDisplayName() {
long tagsCount = 0; long tagsCount = 0;
try { try {
tagsCount = UserPreferences.groupItemsInTreeByDatasource() ? if (UserPreferences.showOnlyCurrentUserTags()) {
Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagName(tagName, datasourceObjId) : String userName = System.getProperty(USER_NAME_PROPERTY);
Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagName(tagName); tagsCount = UserPreferences.groupItemsInTreeByDatasource()
? Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagNameForUser(tagName, datasourceObjId, userName)
: Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagNameForUser(tagName, userName);
} else {
tagsCount = UserPreferences.groupItemsInTreeByDatasource()
? Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagName(tagName, datasourceObjId)
: Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagName(tagName);
}
} catch (TskCoreException | NoCurrentCaseException ex) { } catch (TskCoreException | NoCurrentCaseException ex) {
Logger.getLogger(BlackboardArtifactTagTypeNode.class.getName()).log(Level.SEVERE, "Failed to get blackboard artifact tags count for " + tagName.getDisplayName() + " tag name", ex); //NON-NLS Logger.getLogger(BlackboardArtifactTagTypeNode.class.getName()).log(Level.SEVERE, "Failed to get blackboard artifact tags count for " + tagName.getDisplayName() + " tag name", ex); //NON-NLS
} }
@ -549,10 +604,19 @@ public class Tags implements AutopsyVisitableItem {
protected boolean createKeys(List<BlackboardArtifactTag> keys) { protected boolean createKeys(List<BlackboardArtifactTag> keys) {
try { try {
// Use the blackboard artifact tags bearing the specified tag name as the keys. // Use the blackboard artifact tags bearing the specified tag name as the keys.
List<BlackboardArtifactTag> artifactTags = UserPreferences.groupItemsInTreeByDatasource() ? List<BlackboardArtifactTag> artifactTags = UserPreferences.groupItemsInTreeByDatasource()
Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsByTagName(tagName, datasourceObjId) : ? Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsByTagName(tagName, datasourceObjId)
Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsByTagName(tagName); : Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsByTagName(tagName);
keys.addAll(artifactTags); if (UserPreferences.showOnlyCurrentUserTags()) {
String userName = System.getProperty(USER_NAME_PROPERTY);
for (BlackboardArtifactTag tag : artifactTags) {
if (userName.equals(tag.getUserName())) {
keys.add(tag);
}
}
} else {
keys.addAll(artifactTags);
}
} catch (TskCoreException | NoCurrentCaseException ex) { } catch (TskCoreException | NoCurrentCaseException ex) {
Logger.getLogger(BlackboardArtifactTagNodeFactory.class.getName()).log(Level.SEVERE, "Failed to get tag names", ex); //NON-NLS Logger.getLogger(BlackboardArtifactTagNodeFactory.class.getName()).log(Level.SEVERE, "Failed to get tag names", ex); //NON-NLS
} }

View File

@ -124,4 +124,5 @@ GroupDataSourcesDialog.dataSourceCountLabel.text=jLabel1
GroupDataSourcesDialog.queryLabel.text=Would you like to group by data source for faster loading? GroupDataSourcesDialog.queryLabel.text=Would you like to group by data source for faster loading?
GroupDataSourcesDialog.yesButton.text=Yes GroupDataSourcesDialog.yesButton.text=Yes
GroupDataSourcesDialog.noButton.text=No GroupDataSourcesDialog.noButton.text=No
GroupDataSourcesDialog.title=Group by Data Source? GroupDataSourcesDialog.title=Group by Data Source?
DirectoryTreeTopComponent.showOnlyCurrentUserTagsCheckbox.text=Hide Other User's Tags

View File

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

View File

@ -81,6 +81,7 @@ import org.sleuthkit.autopsy.datamodel.InterestingHits;
import org.sleuthkit.autopsy.datamodel.KeywordHits; import org.sleuthkit.autopsy.datamodel.KeywordHits;
import org.sleuthkit.autopsy.datamodel.ResultsNode; import org.sleuthkit.autopsy.datamodel.ResultsNode;
import org.sleuthkit.autopsy.datamodel.AutopsyTreeChildFactory; import org.sleuthkit.autopsy.datamodel.AutopsyTreeChildFactory;
import org.sleuthkit.autopsy.datamodel.Tags;
import org.sleuthkit.autopsy.datamodel.ViewsNode; import org.sleuthkit.autopsy.datamodel.ViewsNode;
import org.sleuthkit.autopsy.datamodel.accounts.Accounts; import org.sleuthkit.autopsy.datamodel.accounts.Accounts;
import org.sleuthkit.autopsy.datamodel.accounts.BINRange; import org.sleuthkit.autopsy.datamodel.accounts.BINRange;
@ -137,6 +138,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
forwardButton.setEnabled(false); forwardButton.setEnabled(false);
groupByDatasourceCheckBox.setSelected(UserPreferences.groupItemsInTreeByDatasource()); groupByDatasourceCheckBox.setSelected(UserPreferences.groupItemsInTreeByDatasource());
showOnlyCurrentUserTagsCheckbox.setSelected(UserPreferences.showOnlyCurrentUserTags());
} }
/** /**
@ -152,6 +154,9 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
case UserPreferences.GROUP_ITEMS_IN_TREE_BY_DATASOURCE: case UserPreferences.GROUP_ITEMS_IN_TREE_BY_DATASOURCE:
refreshContentTreeSafe(); refreshContentTreeSafe();
break; break;
case UserPreferences.SHOW_ONLY_CURRENT_USER_TAGS:
refreshTagsTree();
break;
case UserPreferences.HIDE_KNOWN_FILES_IN_VIEWS_TREE: case UserPreferences.HIDE_KNOWN_FILES_IN_VIEWS_TREE:
case UserPreferences.HIDE_SLACK_FILES_IN_VIEWS_TREE: case UserPreferences.HIDE_SLACK_FILES_IN_VIEWS_TREE:
// TODO: Need a way to refresh the Views subtree // TODO: Need a way to refresh the Views subtree
@ -191,6 +196,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
forwardButton = new javax.swing.JButton(); forwardButton = new javax.swing.JButton();
showRejectedCheckBox = new javax.swing.JCheckBox(); showRejectedCheckBox = new javax.swing.JCheckBox();
groupByDatasourceCheckBox = new javax.swing.JCheckBox(); groupByDatasourceCheckBox = new javax.swing.JCheckBox();
showOnlyCurrentUserTagsCheckbox = new javax.swing.JCheckBox();
treeView.setBorder(null); treeView.setBorder(null);
@ -235,6 +241,13 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
} }
}); });
org.openide.awt.Mnemonics.setLocalizedText(showOnlyCurrentUserTagsCheckbox, org.openide.util.NbBundle.getMessage(DirectoryTreeTopComponent.class, "DirectoryTreeTopComponent.showOnlyCurrentUserTagsCheckbox.text")); // NOI18N
showOnlyCurrentUserTagsCheckbox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
showOnlyCurrentUserTagsCheckboxActionPerformed(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout); this.setLayout(layout);
layout.setHorizontalGroup( layout.setHorizontalGroup(
@ -244,7 +257,9 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
.addComponent(backButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(backButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(forwardButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(forwardButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 51, Short.MAX_VALUE) .addGap(18, 18, 18)
.addComponent(showOnlyCurrentUserTagsCheckbox, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(showRejectedCheckBox) .addComponent(showRejectedCheckBox)
.addComponent(groupByDatasourceCheckBox)) .addComponent(groupByDatasourceCheckBox))
@ -256,7 +271,9 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addGap(5, 5, 5) .addGap(5, 5, 5)
.addComponent(showRejectedCheckBox) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(showRejectedCheckBox)
.addComponent(showOnlyCurrentUserTagsCheckbox))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(groupByDatasourceCheckBox)) .addComponent(groupByDatasourceCheckBox))
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
@ -323,10 +340,15 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
UserPreferences.setGroupItemsInTreeByDatasource(this.groupByDatasourceCheckBox.isSelected()); UserPreferences.setGroupItemsInTreeByDatasource(this.groupByDatasourceCheckBox.isSelected());
}//GEN-LAST:event_groupByDatasourceCheckBoxActionPerformed }//GEN-LAST:event_groupByDatasourceCheckBoxActionPerformed
private void showOnlyCurrentUserTagsCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_showOnlyCurrentUserTagsCheckboxActionPerformed
UserPreferences.setShowOnlyCurrentUserTags(this.showOnlyCurrentUserTagsCheckbox.isSelected());
}//GEN-LAST:event_showOnlyCurrentUserTagsCheckboxActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton backButton; private javax.swing.JButton backButton;
private javax.swing.JButton forwardButton; private javax.swing.JButton forwardButton;
private javax.swing.JCheckBox groupByDatasourceCheckBox; private javax.swing.JCheckBox groupByDatasourceCheckBox;
private javax.swing.JCheckBox showOnlyCurrentUserTagsCheckbox;
private javax.swing.JCheckBox showRejectedCheckBox; private javax.swing.JCheckBox showRejectedCheckBox;
private javax.swing.JScrollPane treeView; private javax.swing.JScrollPane treeView;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
@ -890,6 +912,29 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
SwingUtilities.invokeLater(this::rebuildTree); SwingUtilities.invokeLater(this::rebuildTree);
} }
/**
* Refresh only the tags subtree(s) of the tree view.
*/
private void refreshTagsTree() {
SwingUtilities.invokeLater(() -> {
// if no open case or has no data then there is no tree to rebuild
if (UserPreferences.groupItemsInTreeByDatasource()) {
for (Node dataSource : autopsyTreeChildren.getNodes()) {
Node tagsNode = dataSource.getChildren().findChild(Tags.getTagsDisplayName());
if (tagsNode != null) {
//Reports is at the same level as the data sources so we want to ignore it
((Tags.RootNode)tagsNode).refresh();
}
}
} else {
Node tagsNode = autopsyTreeChildren.findChild(Tags.getTagsDisplayName());
if (tagsNode != null) {
((Tags.RootNode)tagsNode).refresh();
}
}
});
}
/** /**
* Rebuilds the autopsy tree. * Rebuilds the autopsy tree.
* *

View File

@ -53,6 +53,8 @@ public final class ExtractAction extends AbstractAction {
private Logger logger = Logger.getLogger(ExtractAction.class.getName()); private Logger logger = Logger.getLogger(ExtractAction.class.getName());
private String userDefinedExportPath;
// This class is a singleton to support multi-selection of nodes, since // This class is a singleton to support multi-selection of nodes, since
// org.openide.nodes.NodeOp.findActions(Node[] nodes) will only pick up an Action if every // org.openide.nodes.NodeOp.findActions(Node[] nodes) will only pick up an Action if every
// node in the array returns a reference to the same action object from Node.getActions(boolean). // node in the array returns a reference to the same action object from Node.getActions(boolean).
@ -110,10 +112,12 @@ public final class ExtractAction extends AbstractAction {
return; return;
} }
JFileChooser fileChooser = new JFileChooser(); JFileChooser fileChooser = new JFileChooser();
fileChooser.setCurrentDirectory(new File(openCase.getExportDirectory())); fileChooser.setCurrentDirectory(new File(getExportDirectory(openCase)));
// If there is an attribute name, change the ":". Otherwise the extracted file will be hidden // If there is an attribute name, change the ":". Otherwise the extracted file will be hidden
fileChooser.setSelectedFile(new File(FileUtil.escapeFileName(selectedFile.getName()))); fileChooser.setSelectedFile(new File(FileUtil.escapeFileName(selectedFile.getName())));
if (fileChooser.showSaveDialog((Component) event.getSource()) == JFileChooser.APPROVE_OPTION) { if (fileChooser.showSaveDialog((Component) event.getSource()) == JFileChooser.APPROVE_OPTION) {
updateExportDirectory(fileChooser.getSelectedFile().getParent(), openCase);
ArrayList<FileExtractionTask> fileExtractionTasks = new ArrayList<>(); ArrayList<FileExtractionTask> fileExtractionTasks = new ArrayList<>();
fileExtractionTasks.add(new FileExtractionTask(selectedFile, fileChooser.getSelectedFile())); fileExtractionTasks.add(new FileExtractionTask(selectedFile, fileChooser.getSelectedFile()));
runExtractionTasks(event, fileExtractionTasks); runExtractionTasks(event, fileExtractionTasks);
@ -137,7 +141,7 @@ public final class ExtractAction extends AbstractAction {
} }
JFileChooser folderChooser = new JFileChooser(); JFileChooser folderChooser = new JFileChooser();
folderChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); folderChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
folderChooser.setCurrentDirectory(new File(openCase.getExportDirectory())); folderChooser.setCurrentDirectory(new File(getExportDirectory(openCase)));
if (folderChooser.showSaveDialog((Component) event.getSource()) == JFileChooser.APPROVE_OPTION) { if (folderChooser.showSaveDialog((Component) event.getSource()) == JFileChooser.APPROVE_OPTION) {
File destinationFolder = folderChooser.getSelectedFile(); File destinationFolder = folderChooser.getSelectedFile();
if (!destinationFolder.exists()) { if (!destinationFolder.exists()) {
@ -150,6 +154,7 @@ public final class ExtractAction extends AbstractAction {
return; return;
} }
} }
updateExportDirectory(destinationFolder.getPath(), openCase);
/* /*
* get the unique set of files from the list. A user once reported * get the unique set of files from the list. A user once reported
@ -169,6 +174,45 @@ public final class ExtractAction extends AbstractAction {
} }
} }
/**
* Get the export directory path.
*
* @param openCase The current case.
*
* @return The export directory path.
*/
private String getExportDirectory(Case openCase) {
String caseExportPath = openCase.getExportDirectory();
if (userDefinedExportPath == null) {
return caseExportPath;
}
File file = new File(userDefinedExportPath);
if (file.exists() == false || file.isDirectory() == false) {
return caseExportPath;
}
return userDefinedExportPath;
}
/**
* Update the default export directory. If the directory path matches the
* case export directory, then the directory used will always match the
* export directory of any given case. Otherwise, the path last used will be
* saved.
*
* @param exportPath The export path.
* @param openCase The current case.
*/
private void updateExportDirectory(String exportPath, Case openCase) {
if (exportPath.equalsIgnoreCase(openCase.getExportDirectory())) {
userDefinedExportPath = null;
} else {
userDefinedExportPath = exportPath;
}
}
/** /**
* Execute a series of file extraction tasks. * Execute a series of file extraction tasks.
* *

View File

@ -67,6 +67,7 @@ final class ExtractUnallocAction extends AbstractAction {
private final List<OutputFileData> filesToExtract = new ArrayList<>(); private final List<OutputFileData> filesToExtract = new ArrayList<>();
private static final Set<String> volumesInProgress = new HashSet<>(); private static final Set<String> volumesInProgress = new HashSet<>();
private static final Set<Long> imagesInProgress = new HashSet<>(); private static final Set<Long> imagesInProgress = new HashSet<>();
private static String userDefinedExportPath;
private long currentImage = 0L; private long currentImage = 0L;
private final boolean isImage; private final boolean isImage;
@ -159,7 +160,7 @@ final class ExtractUnallocAction extends AbstractAction {
} }
}; };
fileChooser.setCurrentDirectory(new File(openCase.getExportDirectory())); fileChooser.setCurrentDirectory(new File(getExportDirectory(openCase)));
fileChooser.setDialogTitle( fileChooser.setDialogTitle(
NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.dlgTitle.selectDirToSaveTo.msg")); NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.dlgTitle.selectDirToSaveTo.msg"));
fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
@ -167,6 +168,9 @@ final class ExtractUnallocAction extends AbstractAction {
int returnValue = fileChooser.showSaveDialog((Component) event.getSource()); int returnValue = fileChooser.showSaveDialog((Component) event.getSource());
if (returnValue == JFileChooser.APPROVE_OPTION) { if (returnValue == JFileChooser.APPROVE_OPTION) {
String destination = fileChooser.getSelectedFile().getPath(); String destination = fileChooser.getSelectedFile().getPath();
updateExportDirectory(destination, openCase);
for (OutputFileData outputFileData : filesToExtract) { for (OutputFileData outputFileData : filesToExtract) {
outputFileData.setPath(destination); outputFileData.setPath(destination);
@ -228,7 +232,45 @@ final class ExtractUnallocAction extends AbstractAction {
} }
} }
} }
}
/**
* Get the export directory path.
*
* @param openCase The current case.
*
* @return The export directory path.
*/
private String getExportDirectory(Case openCase) {
String caseExportPath = openCase.getExportDirectory();
if (userDefinedExportPath == null) {
return caseExportPath;
}
File file = new File(userDefinedExportPath);
if (file.exists() == false || file.isDirectory() == false) {
return caseExportPath;
}
return userDefinedExportPath;
}
/**
* Update the default export directory. If the directory path matches the
* case export directory, then the directory used will always match the
* export directory of any given case. Otherwise, the path last used will be
* saved.
*
* @param exportPath The export path.
* @param openCase The current case.
*/
private void updateExportDirectory(String exportPath, Case openCase) {
if (exportPath.equalsIgnoreCase(openCase.getExportDirectory())) {
userDefinedExportPath = null;
} else {
userDefinedExportPath = exportPath;
}
} }
/** /**

View File

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

View File

@ -784,8 +784,14 @@ class ReportHTML implements TableReportModule {
localFileFolder.mkdirs(); localFileFolder.mkdirs();
} }
// Construct a file tagName for the local file that incorporates the file id to ensure uniqueness. /*
String fileName = file.getName(); * Construct a file tagName for the local file that incorporates the
* file ID to ensure uniqueness.
*
* Note: File name is normalized to account for possible attribute name
* which will be separated by a ':' character.
*/
String fileName = org.sleuthkit.autopsy.coreutils.FileUtil.escapeFileName(file.getName());
String objectIdSuffix = "_" + file.getId(); String objectIdSuffix = "_" + file.getId();
int lastDotIndex = fileName.lastIndexOf("."); int lastDotIndex = fileName.lastIndexOf(".");
if (lastDotIndex != -1 && lastDotIndex != 0) { if (lastDotIndex != -1 && lastDotIndex != 0) {
@ -1294,9 +1300,24 @@ class ReportHTML implements TableReportModule {
return summary; return summary;
} }
/**
* Create a thumbnail of a given file.
*
* @param file The file from which to create the thumbnail.
*
* @return The path to the thumbnail file, or null if a thumbnail couldn't
* be created.
*/
private String prepareThumbnail(AbstractFile file) { private String prepareThumbnail(AbstractFile file) {
BufferedImage bufferedThumb = ImageUtils.getThumbnail(file, ImageUtils.ICON_SIZE_MEDIUM); BufferedImage bufferedThumb = ImageUtils.getThumbnail(file, ImageUtils.ICON_SIZE_MEDIUM);
File thumbFile = Paths.get(thumbsPath, file.getName() + ".png").toFile();
/*
* File name is normalized to account for possible attribute name which
* will be separated by a ':' character.
*/
String fileName = org.sleuthkit.autopsy.coreutils.FileUtil.escapeFileName(file.getName());
File thumbFile = Paths.get(thumbsPath, fileName + ".png").toFile();
if (bufferedThumb == null) { if (bufferedThumb == null) {
return null; return null;
} }

View File

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

View File

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

View File

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

View File

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