org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.sleuthkit.autopsy.corecomponents;
+
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.Insets;
+import javax.swing.JScrollPane;
+import javax.swing.SwingUtilities;
+
+/**
+* FlowLayout subclass that fully supports wrapping of components.
+*
+* Originally written by Rob Camick
+* https://tips4java.wordpress.com/2008/11/06/wrap-layout/
+*/
+class WrapLayout extends FlowLayout {
+
+ /**
+ * Constructs a new WrapLayout
with a left alignment and a
+ * default 5-unit horizontal and vertical gap.
+ */
+ public WrapLayout() {
+ super();
+ }
+
+ /**
+ * Constructs a new FlowLayout
with the specified alignment
+ * and a default 5-unit horizontal and vertical gap. The value of the
+ * alignment argument must be one of WrapLayout
,
+ * WrapLayout
, or WrapLayout
.
+ *
+ * @param align the alignment value
+ */
+ public WrapLayout(int align) {
+ super(align);
+ }
+
+ /**
+ * Creates a new flow layout manager with the indicated alignment and
+ * the indicated horizontal and vertical gaps.
+ *
+ * The value of the alignment argument must be one of
+ * WrapLayout
, WrapLayout
, or
+ * WrapLayout
.
+ *
+ * @param align the alignment value
+ * @param hgap the horizontal gap between components
+ * @param vgap the vertical gap between components
+ */
+ public WrapLayout(int align, int hgap, int vgap) {
+ super(align, hgap, vgap);
+ }
+
+ /**
+ * Returns the preferred dimensions for this layout given the
+ * visible components in the specified target container.
+ *
+ * @param target the component which needs to be laid out
+ *
+ * @return the preferred dimensions to lay out the subcomponents of the
+ * specified container
+ */
+ @Override
+ public Dimension preferredLayoutSize(Container target) {
+ return layoutSize(target, true);
+ }
+
+ /**
+ * Returns the minimum dimensions needed to layout the visible
+ * components contained in the specified target container.
+ *
+ * @param target the component which needs to be laid out
+ *
+ * @return the minimum dimensions to lay out the subcomponents of the
+ * specified container
+ */
+ @Override
+ public Dimension minimumLayoutSize(Container target) {
+ Dimension minimum = layoutSize(target, false);
+ minimum.width -= (getHgap() + 1);
+ return minimum;
+ }
+
+ /**
+ * Returns the minimum or preferred dimension needed to layout the
+ * target container.
+ *
+ * @param target target to get layout size for
+ * @param preferred should preferred size be calculated
+ *
+ * @return the dimension to layout the target container
+ */
+ private Dimension layoutSize(Container target, boolean preferred) {
+ synchronized (target.getTreeLock()) {
+ // Each row must fit with the width allocated to the containter.
+ // When the container width = 0, the preferred width of the container
+ // has not yet been calculated so lets ask for the maximum.
+
+ int targetWidth = target.getSize().width;
+ Container container = target;
+
+ while (container.getSize().width == 0 && container.getParent() != null) {
+ container = container.getParent();
+ }
+
+ targetWidth = container.getSize().width;
+
+ if (targetWidth == 0) {
+ targetWidth = Integer.MAX_VALUE;
+ }
+
+ int hgap = getHgap();
+ int vgap = getVgap();
+ Insets insets = target.getInsets();
+ int horizontalInsetsAndGap = insets.left + insets.right + (hgap * 2);
+ int maxWidth = targetWidth - horizontalInsetsAndGap;
+
+ // Fit components into the allowed width
+ Dimension dim = new Dimension(0, 0);
+ int rowWidth = 0;
+ int rowHeight = 0;
+
+ int nmembers = target.getComponentCount();
+
+ for (int i = 0; i < nmembers; i++) {
+ Component m = target.getComponent(i);
+
+ if (m.isVisible()) {
+ Dimension d = preferred ? m.getPreferredSize() : m.getMinimumSize();
+
+ // Can't add the component to current row. Start a new row.
+ if (rowWidth + d.width > maxWidth) {
+ addRow(dim, rowWidth, rowHeight);
+ rowWidth = 0;
+ rowHeight = 0;
+ }
+
+ // Add a horizontal gap for all components after the first
+ if (rowWidth != 0) {
+ rowWidth += hgap;
+ }
+
+ rowWidth += d.width;
+ rowHeight = Math.max(rowHeight, d.height);
+ }
+ }
+
+ addRow(dim, rowWidth, rowHeight);
+
+ dim.width += horizontalInsetsAndGap;
+ dim.height += insets.top + insets.bottom + vgap * 2;
+
+ // When using a scroll pane or the DecoratedLookAndFeel we need to
+ // make sure the preferred size is less than the size of the
+ // target containter so shrinking the container size works
+ // correctly. Removing the horizontal gap is an easy way to do this.
+ Container scrollPane = SwingUtilities.getAncestorOfClass(JScrollPane.class, target);
+
+ if (scrollPane != null && target.isValid()) {
+ dim.width -= (hgap + 1);
+ }
+
+ return dim;
+ }
+ }
+
+ /*
+ * A new row has been completed. Use the dimensions of this row to
+ * update the preferred size for the container.
+ *
+ * @param dim update the width and height when appropriate @param
+ * rowWidth the width of the row to add @param rowHeight the height of
+ * the row to add
+ */
+ private void addRow(Dimension dim, int rowWidth, int rowHeight) {
+ dim.width = Math.max(dim.width, rowWidth);
+
+ if (dim.height > 0) {
+ dim.height += getVgap();
+ }
+
+ dim.height += rowHeight;
+ }
+ }
\ No newline at end of file
diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/StringExtract.java b/Core/src/org/sleuthkit/autopsy/coreutils/StringExtract.java
index aded120ac7..0f89eb2291 100644
--- a/Core/src/org/sleuthkit/autopsy/coreutils/StringExtract.java
+++ b/Core/src/org/sleuthkit/autopsy/coreutils/StringExtract.java
@@ -221,7 +221,8 @@ public class StringExtract {
StringExtractResult resWin = null;
if (enableUTF8 && resUTF16 != null) {
resWin = runUTF16 && resUTF16.numChars > resUTF8.numChars ? resUTF16 : resUTF8;
- } else if (enableUTF16) {
+ } else if (runUTF16) {
+ //Only let resUTF16 "win" if it was actually run.
resWin = resUTF16;
} else if (enableUTF8) {
resWin = resUTF8;
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java
index 158f2ef4d9..e9b395f292 100644
--- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java
@@ -102,7 +102,7 @@ public abstract class AbstractAbstractFileNode extends A
// or when tags are added.
Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, weakPcl);
}
-
+
/**
* The finalizer removes event listeners as the BlackboardArtifactNode is
* being garbage collected. Yes, we know that finalizers are considered to
@@ -123,7 +123,6 @@ public abstract class AbstractAbstractFileNode extends A
Case.removeEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, weakPcl);
}
-
private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> {
String eventType = evt.getPropertyName();
@@ -201,16 +200,16 @@ public abstract class AbstractAbstractFileNode extends A
this.setShortDescription(content.getName());
updateSheet(new NodeProperty<>(ORIGINAL_NAME.toString(), ORIGINAL_NAME.toString(), NO_DESCR, content.getName()));
} else if (eventType.equals(NodeSpecificEvents.SCO_AVAILABLE.toString())) {
- SCOData scoData = (SCOData)evt.getNewValue();
+ SCOData scoData = (SCOData) evt.getNewValue();
if (scoData.getScoreAndDescription() != null) {
updateSheet(new NodeProperty<>(SCORE.toString(), SCORE.toString(), scoData.getScoreAndDescription().getRight(), scoData.getScoreAndDescription().getLeft()));
}
if (scoData.getComment() != null) {
updateSheet(new NodeProperty<>(COMMENT.toString(), COMMENT.toString(), NO_DESCR, scoData.getComment()));
}
- if (scoData.getCountAndDescription() != null &&
- !UserPreferences.hideCentralRepoCommentsAndOccurrences()) {
- updateSheet(new NodeProperty<>(OCCURRENCES.toString(), OCCURRENCES.toString(), scoData.getCountAndDescription().getRight(), scoData.getCountAndDescription().getLeft()));
+ if (scoData.getCountAndDescription() != null
+ && !UserPreferences.hideCentralRepoCommentsAndOccurrences()) {
+ updateSheet(new NodeProperty<>(OCCURRENCES.toString(), OCCURRENCES.toString(), scoData.getCountAndDescription().getRight(), scoData.getCountAndDescription().getLeft()));
}
}
};
@@ -331,12 +330,11 @@ public abstract class AbstractAbstractFileNode extends A
if (UserPreferences.hideCentralRepoCommentsAndOccurrences() == false) {
properties.add(new NodeProperty<>(OCCURRENCES.toString(), OCCURRENCES.toString(), VALUE_LOADING, ""));
}
-
-
+
// Get the SCO columns data in a background task
backgroundTasksPool.submit(new GetSCOTask(
- new WeakReference<>(this), weakPcl));
-
+ new WeakReference<>(this), weakPcl));
+
properties.add(new NodeProperty<>(LOCATION.toString(), LOCATION.toString(), NO_DESCR, getContentPath(content)));
properties.add(new NodeProperty<>(MOD_TIME.toString(), MOD_TIME.toString(), NO_DESCR, ContentUtils.getStringTime(content.getMtime(), content)));
properties.add(new NodeProperty<>(CHANGED_TIME.toString(), CHANGED_TIME.toString(), NO_DESCR, ContentUtils.getStringTime(content.getCtime(), content)));
@@ -394,20 +392,19 @@ public abstract class AbstractAbstractFileNode extends A
@NbBundle.Messages({
"AbstractAbstractFileNode.createSheet.count.displayName=O",
- "AbstractAbstractFileNode.createSheet.count.noCentralRepo.description=Central repository was not enabled when this column was populated",
"AbstractAbstractFileNode.createSheet.count.hashLookupNotRun.description=Hash lookup had not been run on this file when the column was populated",
- "# {0} - occuranceCount",
- "AbstractAbstractFileNode.createSheet.count.description=There were {0} datasource(s) found with occurances of the correlation value"})
+ "# {0} - occurenceCount",
+ "AbstractAbstractFileNode.createSheet.count.description=There were {0} datasource(s) found with occurences of the MD5 correlation value"})
@Override
- protected Pair getCountPropertyAndDescription(CorrelationAttributeInstance attribute) {
+ protected Pair getCountPropertyAndDescription(CorrelationAttributeInstance.Type attributeType, String attributeValue, String defaultDescription) {
Long count = -1L; //The column renderer will not display negative values, negative value used when count unavailble to preserve sorting
- String description = Bundle.AbstractAbstractFileNode_createSheet_count_noCentralRepo_description();
+ String description = defaultDescription;
try {
//don't perform the query if there is no correlation value
- if (attribute != null && StringUtils.isNotBlank(attribute.getCorrelationValue())) {
- count = EamDb.getInstance().getCountUniqueCaseDataSourceTuplesHavingTypeValue(attribute.getCorrelationType(), attribute.getCorrelationValue());
+ if (attributeType != null && StringUtils.isNotBlank(attributeValue)) {
+ count = EamDb.getInstance().getCountUniqueCaseDataSourceTuplesHavingTypeValue(attributeType, attributeValue);
description = Bundle.AbstractAbstractFileNode_createSheet_count_description(count);
- } else if (attribute != null) {
+ } else if (attributeType != null) {
description = Bundle.AbstractAbstractFileNode_createSheet_count_hashLookupNotRun_description();
}
} catch (EamDbException ex) {
@@ -415,7 +412,6 @@ public abstract class AbstractAbstractFileNode extends A
} catch (CorrelationAttributeNormalizationException ex) {
logger.log(Level.WARNING, "Unable to normalize data to get count of datasources with correlation attribute", ex);
}
-
return Pair.of(count, description);
}
@@ -535,7 +531,7 @@ public abstract class AbstractAbstractFileNode extends A
protected List getAllTagsFromDatabase() {
return new ArrayList<>(getContentTagsFromDatabase());
}
-
+
@Override
protected CorrelationAttributeInstance getCorrelationAttributeInstance() {
CorrelationAttributeInstance attribute = null;
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java
index b9e012e6af..8b1d22d349 100644
--- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java
@@ -35,6 +35,7 @@ import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
+import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance.Type;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
@@ -60,21 +61,21 @@ public abstract class AbstractContentNode extends ContentNode
private static final Logger logger = Logger.getLogger(AbstractContentNode.class.getName());
/**
- * A pool of background tasks to run any long computation needed to
- * populate this node.
+ * A pool of background tasks to run any long computation needed to populate
+ * this node.
*/
static final ExecutorService backgroundTasksPool;
private static final Integer MAX_POOL_SIZE = 10;
-
+
/**
* Default no description string
*/
@NbBundle.Messages({"AbstractContentNode.nodescription=no description",
- "AbstractContentNode.valueLoading=value loading"})
+ "AbstractContentNode.valueLoading=value loading"})
protected static final String NO_DESCR = Bundle.AbstractContentNode_nodescription();
protected static final String VALUE_LOADING = Bundle.AbstractContentNode_valueLoading();
-
- /**
+
+ /**
* Event signals to indicate the background tasks have completed processing.
* Currently, we have one property task in the background:
*
@@ -84,35 +85,36 @@ public abstract class AbstractContentNode extends ContentNode
TRANSLATION_AVAILABLE,
SCO_AVAILABLE
}
-
+
static {
//Initialize this pool only once! This will be used by every instance of AAFN
//to do their heavy duty SCO column and translation updates.
backgroundTasksPool = Executors.newFixedThreadPool(MAX_POOL_SIZE,
new ThreadFactoryBuilder().setNameFormat("content-node-background-task-%d").build());
}
+
/**
* Handles aspects that depend on the Content object
*
* @param content Underlying Content instances
*/
AbstractContentNode(T content) {
- this(content, Lookups.singleton(content) );
+ this(content, Lookups.singleton(content));
}
/**
* Handles aspects that depend on the Content object
*
* @param content Underlying Content instances
- * @param lookup The Lookup object for the node.
+ * @param lookup The Lookup object for the node.
*/
AbstractContentNode(T content, Lookup lookup) {
- super(Children.create(new ContentChildren(content), true), lookup);
+ super(Children.create(new ContentChildren(content), false), lookup);
this.content = content;
//super.setName(ContentUtils.getSystemName(content));
super.setName("content_" + Long.toString(content.getId())); //NON-NLS
}
-
+
/**
* Return the content data associated with this node
*
@@ -141,40 +143,40 @@ public abstract class AbstractContentNode extends ContentNode
public boolean hasVisibleContentChildren() {
return contentHasVisibleContentChildren(content);
}
-
+
/**
* Return true if the given content object has children. Useful for lazy
* loading.
- *
+ *
* @param c The content object to look for children on
+ *
* @return true if has children
*/
- public static boolean contentHasVisibleContentChildren(Content c){
+ public static boolean contentHasVisibleContentChildren(Content c) {
if (c != null) {
-
+
try {
- if( ! c.hasChildren()) {
+ if (!c.hasChildren()) {
return false;
}
} catch (TskCoreException ex) {
-
+
logger.log(Level.SEVERE, "Error checking if the node has children, for content: " + c, ex); //NON-NLS
return false;
}
-
+
String query = "SELECT COUNT(obj_id) AS count FROM "
- + " ( SELECT obj_id FROM tsk_objects WHERE par_obj_id = " + c.getId() + " AND type = "
- + TskData.ObjectType.ARTIFACT.getObjectType()
- + " INTERSECT SELECT artifact_obj_id FROM blackboard_artifacts WHERE obj_id = " + c.getId()
- + " AND (artifact_type_id = " + ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()
- + " OR artifact_type_id = " + ARTIFACT_TYPE.TSK_MESSAGE.getTypeID() + ") "
- + " UNION SELECT obj_id FROM tsk_objects WHERE par_obj_id = " + c.getId()
- + " AND type = " + TskData.ObjectType.ABSTRACTFILE.getObjectType() + ") AS OBJECT_IDS"; //NON-NLS;
-
-
+ + " ( SELECT obj_id FROM tsk_objects WHERE par_obj_id = " + c.getId() + " AND type = "
+ + TskData.ObjectType.ARTIFACT.getObjectType()
+ + " INTERSECT SELECT artifact_obj_id FROM blackboard_artifacts WHERE obj_id = " + c.getId()
+ + " AND (artifact_type_id = " + ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()
+ + " OR artifact_type_id = " + ARTIFACT_TYPE.TSK_MESSAGE.getTypeID() + ") "
+ + " UNION SELECT obj_id FROM tsk_objects WHERE par_obj_id = " + c.getId()
+ + " AND type = " + TskData.ObjectType.ABSTRACTFILE.getObjectType() + ") AS OBJECT_IDS"; //NON-NLS;
+
try (SleuthkitCase.CaseDbQuery dbQuery = Case.getCurrentCaseThrows().getSleuthkitCase().executeQuery(query)) {
ResultSet resultSet = dbQuery.getResultSet();
- if(resultSet.next()){
+ if (resultSet.next()) {
return (0 < resultSet.getInt("count"));
}
} catch (TskCoreException | SQLException | NoCurrentCaseException ex) {
@@ -183,7 +185,7 @@ public abstract class AbstractContentNode extends ContentNode
}
return false;
}
-
+
/**
* Return true if the underlying content object has children Useful for lazy
* loading.
@@ -203,7 +205,7 @@ public abstract class AbstractContentNode extends ContentNode
return hasChildren;
}
-
+
/**
* Return ids of children of the underlying content. The ids can be treated
* as keys - useful for lazy loading.
@@ -281,8 +283,7 @@ public abstract class AbstractContentNode extends ContentNode
public int read(byte[] buf, long offset, long len) throws TskException {
return content.read(buf, offset, len);
}
-
-
+
/**
* Updates the values of the properties in the current property sheet with
* the new properties being passed in. Only if that property exists in the
@@ -314,46 +315,51 @@ public abstract class AbstractContentNode extends ContentNode
//setSheet() will notify Netbeans to update this node in the UI.
this.setSheet(visibleSheet);
}
-
+
/**
* Reads and returns a list of all tags associated with this content node.
- *
+ *
* @return list of tags associated with the node.
- */
+ */
abstract protected List getAllTagsFromDatabase();
-
- /**
- * Returns correlation attribute instance for the underlying content of the node.
- *
- * @return correlation attribute instance for the underlying content of the node.
- */
+
+ /**
+ * Returns correlation attribute instance for the underlying content of the
+ * node.
+ *
+ * @return correlation attribute instance for the underlying content of the
+ * node.
+ */
abstract protected CorrelationAttributeInstance getCorrelationAttributeInstance();
-
+
/**
* Returns Score property for the node.
- *
+ *
* @param tags list of tags.
- *
+ *
* @return Score property for the underlying content of the node.
- */
+ */
abstract protected Pair getScorePropertyAndDescription(List tags);
-
+
/**
* Returns comment property for the node.
- *
- * @param tags list of tags
+ *
+ * @param tags list of tags
* @param attribute correlation attribute instance
- *
+ *
* @return Comment property for the underlying content of the node.
- */
+ */
abstract protected DataResultViewerTable.HasCommentStatus getCommentProperty(List tags, CorrelationAttributeInstance attribute);
-
+
/**
* Returns occurrences/count property for the node.
- *
- * @param attribute correlation attribute instance
- *
+ *
+ * @param attributeType the type of the attribute to count
+ * @param attributeValue the value of the attribute to count
+ * @param defaultDescription a description to use when none is determined by
+ * the getCountPropertyAndDescription method
+ *
* @return count property for the underlying content of the node.
- */
- abstract protected Pair getCountPropertyAndDescription(CorrelationAttributeInstance attribute);
+ */
+ abstract protected Pair getCountPropertyAndDescription(Type attributeType, String attributeValue, String defaultDescription);
}
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java
index 23ab78fa01..ef96097391 100644
--- a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java
@@ -52,6 +52,7 @@ import org.sleuthkit.autopsy.casemodule.events.CommentChangedEvent;
import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent;
import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
+import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance.Type;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNormalizationException;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamArtifactUtil;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
@@ -99,7 +100,7 @@ public class BlackboardArtifactNode extends AbstractContentNode> customProperties;
-
+
/*
* Artifact types which should have the full unique path of the associated
* content as a property.
@@ -151,18 +152,17 @@ public class BlackboardArtifactNode extends AbstractContentNode(Bundle.BlackboardArtifactNode_createSheet_score_name(), Bundle.BlackboardArtifactNode_createSheet_score_displayName(), scoData.getScoreAndDescription().getRight(), scoData.getScoreAndDescription().getLeft()));
}
if (scoData.getComment() != null) {
updateSheet(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_comment_name(), Bundle.BlackboardArtifactNode_createSheet_comment_displayName(), NO_DESCR, scoData.getComment()));
}
- if (scoData.getCountAndDescription() != null &&
- !UserPreferences.hideCentralRepoCommentsAndOccurrences()) {
- updateSheet(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_count_name(), Bundle.BlackboardArtifactNode_createSheet_count_displayName(), scoData.getCountAndDescription().getRight(), scoData.getCountAndDescription().getLeft()));
+ if (scoData.getCountAndDescription() != null
+ && !UserPreferences.hideCentralRepoCommentsAndOccurrences()) {
+ updateSheet(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_count_name(), Bundle.BlackboardArtifactNode_createSheet_count_displayName(), scoData.getCountAndDescription().getRight(), scoData.getCountAndDescription().getLeft()));
}
}
}
@@ -333,7 +333,7 @@ public class BlackboardArtifactNode extends AbstractContentNode(Bundle.BlackboardArtifactNode_createSheet_score_name(), Bundle.BlackboardArtifactNode_createSheet_score_displayName(), VALUE_LOADING, ""));
- sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_comment_name(), Bundle.BlackboardArtifactNode_createSheet_comment_displayName(), VALUE_LOADING, ""));
+ sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_comment_name(), Bundle.BlackboardArtifactNode_createSheet_comment_displayName(), VALUE_LOADING, ""));
if (UserPreferences.hideCentralRepoCommentsAndOccurrences() == false) {
sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_count_name(), Bundle.BlackboardArtifactNode_createSheet_count_displayName(), VALUE_LOADING, ""));
}
-
+
// Get the SCO columns data in a background task
backgroundTasksPool.submit(new GetSCOTask(
- new WeakReference<>(this), weakPcl));
-
+ new WeakReference<>(this), weakPcl));
+
if (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()) {
try {
BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT));
@@ -582,6 +582,12 @@ public class BlackboardArtifactNode extends AbstractContentNode t.getName().getDisplayName()).collect(Collectors.joining(", "))));
}
+ /**
+ * Gets the correlation attribute for the associated file
+ *
+ * @return the correlation attribute for the file associated with this
+ * BlackboardArtifactNode
+ */
@Override
protected final CorrelationAttributeInstance getCorrelationAttributeInstance() {
CorrelationAttributeInstance correlationAttribute = null;
@@ -600,19 +606,19 @@ public class BlackboardArtifactNode extends AbstractContentNode tags, CorrelationAttributeInstance attribute) {
- HasCommentStatus status = getCommentProperty(tags, attribute );
- sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_comment_name(), Bundle.BlackboardArtifactNode_createSheet_comment_displayName(), NO_DESCR,
+ HasCommentStatus status = getCommentProperty(tags, attribute);
+ sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_comment_name(), Bundle.BlackboardArtifactNode_createSheet_comment_displayName(), NO_DESCR,
status));
}
-
+
/**
* Gets the comment property for the node
*
@@ -620,6 +626,7 @@ public class BlackboardArtifactNode extends AbstractContentNode scoreAndDescription = getScorePropertyAndDescription(tags);
sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_score_name(), Bundle.BlackboardArtifactNode_createSheet_score_displayName(), scoreAndDescription.getRight(), scoreAndDescription.getLeft()));
}
-
+
/**
* Get the score property for the node.
*
- * @param tags the list of tags associated with the file
- *
- * @return score property and description
+ * @param tags the list of tags associated with the file
+ *
+ * @return score property and description
*/
@Override
protected Pair getScorePropertyAndDescription(List tags) {
- Score score = Score.NO_SCORE;
+ Score score = Score.NO_SCORE;
String description = Bundle.BlackboardArtifactNode_createSheet_noScore_description();
if (associated instanceof AbstractFile) {
if (((AbstractFile) associated).getKnown() == TskData.FileKnown.BAD) {
@@ -721,51 +729,56 @@ public class BlackboardArtifactNode extends AbstractContentNode countAndDescription = getCountPropertyAndDescription(attribute);
+ Pair countAndDescription = getCountPropertyAndDescription(attribute.getCorrelationType(), attribute.getCorrelationValue(), Bundle.BlackboardArtifactNode_createSheet_count_noCorrelationAttributes_description());
sheetSet.put(
new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_count_name(), Bundle.BlackboardArtifactNode_createSheet_count_displayName(), countAndDescription.getRight(), countAndDescription.getLeft()));
- }
-
+ }
+
/**
* Gets the Occurrences property for the node.
*
- * @param attribute correlation attribute instance
- *
+ * @param attributeType the type of the attribute to count
+ * @param attributeValue the value of the attribute to count
+ * @param defaultDescription a description to use when none is determined by
+ * the getCountPropertyAndDescription method
+ *
* @return count and description
- *
+ *
*/
@Override
- protected Pair getCountPropertyAndDescription(CorrelationAttributeInstance attribute) {
+ protected Pair getCountPropertyAndDescription(Type attributeType, String attributeValue, String defaultDescription) {
Long count = -1L;
- String description = Bundle.BlackboardArtifactNode_createSheet_count_noCentralRepo_description();
+ String description = defaultDescription;
try {
//don't perform the query if there is no correlation value
- if (attribute != null && StringUtils.isNotBlank(attribute.getCorrelationValue())) {
- count = EamDb.getInstance().getCountUniqueCaseDataSourceTuplesHavingTypeValue(attribute.getCorrelationType(), attribute.getCorrelationValue());
- description = Bundle.BlackboardArtifactNode_createSheet_count_description(count);
- } else if (attribute != null) {
- description = Bundle.BlackboardArtifactNode_createSheet_count_hashLookupNotRun_description();
+ if (attributeType != null && StringUtils.isNotBlank(attributeValue)) {
+ count = EamDb.getInstance().getCountUniqueCaseDataSourceTuplesHavingTypeValue(attributeType, attributeValue);
+ description = Bundle.BlackboardArtifactNode_createSheet_count_description(count, attributeType.getDisplayName());
+ } else if (attributeType != null) {
+ description = Bundle.BlackboardArtifactNode_createSheet_count_noCorrelationValues_description();
}
} catch (EamDbException ex) {
logger.log(Level.WARNING, "Error getting count of datasources with correlation attribute", ex);
@@ -773,12 +786,12 @@ public class BlackboardArtifactNode extends AbstractContentNode 0) ? NbBundle.getMessage(DataSourcesNode.class, "DataSourcesNode.group_by_datasource.name") : NAME;
init();
}
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DeletedContent.java b/Core/src/org/sleuthkit/autopsy/datamodel/DeletedContent.java
index 25069d9564..adc5a34fa7 100644
--- a/Core/src/org/sleuthkit/autopsy/datamodel/DeletedContent.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/DeletedContent.java
@@ -24,7 +24,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
-import java.util.Objects;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;
@@ -37,7 +36,6 @@ import org.openide.nodes.Sheet;
import org.openide.util.NbBundle;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case;
-import org.sleuthkit.autopsy.casemodule.CasePreferences;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.ingest.IngestManager;
@@ -59,7 +57,7 @@ import org.sleuthkit.datamodel.VirtualDirectory;
public class DeletedContent implements AutopsyVisitableItem {
private SleuthkitCase skCase;
- private final long datasourceObjId;
+ private final long filteringDSObjId; // 0 if not filtering/grouping by data source
@NbBundle.Messages({"DeletedContent.fsDelFilter.text=File System",
"DeletedContent.allDelFilter.text=All"})
@@ -105,11 +103,11 @@ public class DeletedContent implements AutopsyVisitableItem {
public DeletedContent(SleuthkitCase skCase, long dsObjId) {
this.skCase = skCase;
- this.datasourceObjId = dsObjId;
+ this.filteringDSObjId = dsObjId;
}
long filteringDataSourceObjId() {
- return this.datasourceObjId;
+ return this.filteringDSObjId;
}
@Override
@@ -439,7 +437,7 @@ public class DeletedContent implements AutopsyVisitableItem {
}
- if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) {
+ if (filteringDSObjId > 0) {
query += " AND data_source_obj_id = " + filteringDSObjId;
}
return query;
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/EmailExtracted.java b/Core/src/org/sleuthkit/autopsy/datamodel/EmailExtracted.java
index 83fcee1b65..7285b2cb8d 100644
--- a/Core/src/org/sleuthkit/autopsy/datamodel/EmailExtracted.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/EmailExtracted.java
@@ -28,7 +28,6 @@ import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;
@@ -40,7 +39,6 @@ import org.openide.nodes.Sheet;
import org.openide.util.NbBundle;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case;
-import org.sleuthkit.autopsy.casemodule.CasePreferences;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.ingest.IngestManager;
@@ -88,7 +86,7 @@ public class EmailExtracted implements AutopsyVisitableItem {
}
private SleuthkitCase skCase;
private final EmailResults emailResults;
- private final long datasourceObjId;
+ private final long filteringDSObjId; // 0 if not filtering/grouping by data source
@@ -110,7 +108,7 @@ public class EmailExtracted implements AutopsyVisitableItem {
*/
public EmailExtracted(SleuthkitCase skCase, long objId) {
this.skCase = skCase;
- this.datasourceObjId = objId;
+ this.filteringDSObjId = objId;
emailResults = new EmailResults();
}
@@ -162,8 +160,8 @@ public class EmailExtracted implements AutopsyVisitableItem {
+ "attribute_type_id=" + pathAttrId //NON-NLS
+ " AND blackboard_attributes.artifact_id=blackboard_artifacts.artifact_id" //NON-NLS
+ " AND blackboard_artifacts.artifact_type_id=" + artId; //NON-NLS
- if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) {
- query += " AND blackboard_artifacts.data_source_obj_id = " + datasourceObjId;
+ if (filteringDSObjId > 0) {
+ query += " AND blackboard_artifacts.data_source_obj_id = " + filteringDSObjId;
}
try (CaseDbQuery dbQuery = skCase.executeQuery(query)) {
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java b/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java
index a21afff190..a51e36eb87 100644
--- a/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java
@@ -26,7 +26,6 @@ import java.util.Comparator;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
-import java.util.Objects;
import java.util.logging.Level;
import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children;
@@ -35,7 +34,6 @@ import org.openide.nodes.Sheet;
import org.openide.util.NbBundle;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case;
-import org.sleuthkit.autopsy.casemodule.CasePreferences;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.ingest.IngestManager;
@@ -64,7 +62,7 @@ public class ExtractedContent implements AutopsyVisitableItem {
private SleuthkitCase skCase; // set to null after case has been closed
private Blackboard blackboard;
public static final String NAME = NbBundle.getMessage(RootNode.class, "ExtractedContentNode.name.text");
- private final long datasourceObjId;
+ private final long filteringDSObjId; // 0 if not filtering/grouping by data source
/**
* Constructs extracted content object
@@ -83,7 +81,7 @@ public class ExtractedContent implements AutopsyVisitableItem {
*/
public ExtractedContent(SleuthkitCase skCase, long objId) {
this.skCase = skCase;
- this.datasourceObjId = objId;
+ this.filteringDSObjId = objId;
this.blackboard = skCase.getBlackboard();
}
@@ -307,8 +305,8 @@ public class ExtractedContent implements AutopsyVisitableItem {
protected boolean createKeys(List list) {
if (skCase != null) {
try {
- List types = Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true) ?
- blackboard.getArtifactTypesInUse(datasourceObjId) :
+ List types = (filteringDSObjId > 0) ?
+ blackboard.getArtifactTypesInUse(filteringDSObjId) :
skCase.getArtifactTypesInUse() ;
types.removeAll(doNotShow);
@@ -372,8 +370,8 @@ public class ExtractedContent implements AutopsyVisitableItem {
// a performance increase might be had by adding a
// "getBlackboardArtifactCount()" method to skCase
try {
- this.childCount = Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true) ?
- blackboard.getArtifactsCount(type.getTypeID(), datasourceObjId) :
+ this.childCount = (filteringDSObjId > 0) ?
+ blackboard.getArtifactsCount(type.getTypeID(), filteringDSObjId) :
skCase.getBlackboardArtifactsTypeCount(type.getTypeID());
} catch (TskException ex) {
Logger.getLogger(TypeNode.class.getName())
@@ -501,8 +499,8 @@ public class ExtractedContent implements AutopsyVisitableItem {
protected List makeKeys() {
if (skCase != null) {
try {
- return Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)
- ? blackboard.getArtifacts(type.getTypeID(), datasourceObjId)
+ return (filteringDSObjId > 0)
+ ? blackboard.getArtifacts(type.getTypeID(), filteringDSObjId)
: skCase.getBlackboardArtifacts(type.getTypeID());
} catch (TskException ex) {
Logger.getLogger(ArtifactFactory.class.getName()).log(Level.SEVERE, "Couldn't get blackboard artifacts from database", ex); //NON-NLS
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileSize.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileSize.java
index e9c49c596e..9cceeb5a29 100644
--- a/Core/src/org/sleuthkit/autopsy/datamodel/FileSize.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileSize.java
@@ -24,7 +24,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
-import java.util.Objects;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;
@@ -37,9 +36,7 @@ import org.openide.nodes.Sheet;
import org.openide.util.NbBundle;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case;
-import org.sleuthkit.autopsy.casemodule.CasePreferences;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
-import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.datamodel.AbstractFile;
@@ -63,7 +60,7 @@ import org.sleuthkit.datamodel.VirtualDirectory;
public class FileSize implements AutopsyVisitableItem {
private SleuthkitCase skCase;
- private final long datasourceObjId;
+ private final long filteringDSObjId; // 0 if not filtering/grouping by data source
public enum FileSizeFilter implements AutopsyVisitableItem {
@@ -105,7 +102,7 @@ public class FileSize implements AutopsyVisitableItem {
public FileSize(SleuthkitCase skCase, long dsObjId) {
this.skCase = skCase;
- this.datasourceObjId = dsObjId;
+ this.filteringDSObjId = dsObjId;
}
@Override
@@ -118,7 +115,7 @@ public class FileSize implements AutopsyVisitableItem {
}
long filteringDataSourceObjId() {
- return this.datasourceObjId;
+ return this.filteringDSObjId;
}
/*
* Root node. Children are nodes for specific sizes.
@@ -437,7 +434,7 @@ public class FileSize implements AutopsyVisitableItem {
query = query + " AND (type != " + TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS.getFileType() + ")"; //NON-NLS
// filter by datasource if indicated in case preferences
- if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) {
+ if (filteringDSObjId > 0) {
query += " AND data_source_obj_id = " + filteringDSObjId;
}
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java
index b3005c28b4..34ec74280a 100644
--- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java
@@ -24,7 +24,6 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
-import java.util.Objects;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;
@@ -39,7 +38,6 @@ import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case;
-import org.sleuthkit.autopsy.casemodule.CasePreferences;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.coreutils.Logger;
@@ -366,7 +364,7 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
+ (UserPreferences.hideKnownFilesInViewsTree()
? " AND (known IS NULL OR known != " + TskData.FileKnown.KNOWN.getFileKnownValue() + ")"
: " ")
- + (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)
+ + (filteringDataSourceObjId() > 0
? " AND data_source_obj_id = " + filteringDataSourceObjId()
: " ")
+ " AND (extension IN (" + filter.getFilter().stream()
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java
index 1e4f61fc87..86cc42aa8c 100644
--- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java
@@ -28,7 +28,6 @@ import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;
@@ -42,7 +41,6 @@ import org.openide.nodes.Sheet;
import org.openide.util.NbBundle;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case;
-import org.sleuthkit.autopsy.casemodule.CasePreferences;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import static org.sleuthkit.autopsy.core.UserPreferences.hideKnownFilesInViewsTree;
import static org.sleuthkit.autopsy.core.UserPreferences.hideSlackFilesInViewsTree;
@@ -103,7 +101,7 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
+ TskData.TSK_DB_FILES_TYPE_ENUM.LOCAL.ordinal()
+ (hideSlackFilesInViewsTree() ? "" : ("," + TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.ordinal()))
+ "))"
- + ( Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true) ? " AND data_source_obj_id = " + this.filteringDataSourceObjId() : " ")
+ + ( (filteringDataSourceObjId() > 0) ? " AND data_source_obj_id = " + this.filteringDataSourceObjId() : " ")
+ (hideKnownFilesInViewsTree() ? (" AND (known IS NULL OR known != " + TskData.FileKnown.KNOWN.getFileKnownValue() + ")") : "");
}
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/GetSCOTask.java b/Core/src/org/sleuthkit/autopsy/datamodel/GetSCOTask.java
index b7f4e38a5e..bcdca8632e 100644
--- a/Core/src/org/sleuthkit/autopsy/datamodel/GetSCOTask.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/GetSCOTask.java
@@ -22,30 +22,42 @@ import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.ref.WeakReference;
import java.util.List;
+import java.util.logging.Level;
+import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
+import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance.Type;
+import org.sleuthkit.autopsy.centralrepository.datamodel.EamArtifactUtil;
+import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
import org.sleuthkit.autopsy.core.UserPreferences;
+import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.events.AutopsyEvent;
+import org.sleuthkit.datamodel.AbstractFile;
+import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.Tag;
+import org.sleuthkit.datamodel.TskCoreException;
/**
- * Background task to get Score, Comment and Occurrences values for an
- * Abstract content node.
- *
+ * Background task to get Score, Comment and Occurrences values for an Abstract
+ * content node.
+ *
*/
class GetSCOTask implements Runnable {
private final WeakReference> weakNodeRef;
private final PropertyChangeListener listener;
+ private static final Logger logger = Logger.getLogger(GetSCOTask.class.getName());
GetSCOTask(WeakReference> weakContentRef, PropertyChangeListener listener) {
this.weakNodeRef = weakContentRef;
this.listener = listener;
}
+ @Messages({"GetSCOTask.occurrences.defaultDescription=No correlation properties found",
+ "GetSCOTask.occurrences.multipleProperties=Multiple different correlation properties exist for this result"})
@Override
public void run() {
AbstractContentNode> contentNode = weakNodeRef.get();
-
+
//Check for stale reference
if (contentNode == null) {
return;
@@ -53,17 +65,61 @@ class GetSCOTask implements Runnable {
// get the SCO column values
List tags = contentNode.getAllTagsFromDatabase();
- CorrelationAttributeInstance attribute = contentNode.getCorrelationAttributeInstance();
+ CorrelationAttributeInstance fileAttribute = contentNode.getCorrelationAttributeInstance();
SCOData scoData = new SCOData();
scoData.setScoreAndDescription(contentNode.getScorePropertyAndDescription(tags));
- scoData.setComment(contentNode.getCommentProperty(tags, attribute));
+ scoData.setComment(contentNode.getCommentProperty(tags, fileAttribute));
+
if (!UserPreferences.hideCentralRepoCommentsAndOccurrences()) {
- scoData.setCountAndDescription(contentNode.getCountPropertyAndDescription(attribute));
+ Type type = null;
+ String value = null;
+ String description = Bundle.GetSCOTask_occurrences_defaultDescription();
+ if (contentNode instanceof BlackboardArtifactNode) {
+ BlackboardArtifact bbArtifact = ((BlackboardArtifactNode) contentNode).getArtifact();
+ //for specific artifact types we still want to display information for the file instance correlation attribute
+ if (bbArtifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED.getTypeID()
+ || bbArtifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_SUSPECTED.getTypeID()
+ || bbArtifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID()
+ || bbArtifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF.getTypeID()
+ || bbArtifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()
+ || bbArtifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_OBJECT_DETECTED.getTypeID()
+ || bbArtifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_EXT_MISMATCH_DETECTED.getTypeID()
+ || bbArtifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID()) {
+ try {
+ if (bbArtifact.getParent() instanceof AbstractFile) {
+ type = CorrelationAttributeInstance.getDefaultCorrelationTypes().get(CorrelationAttributeInstance.FILES_TYPE_ID);
+ value = ((AbstractFile) bbArtifact.getParent()).getMd5Hash();
+ }
+ } catch (TskCoreException | EamDbException ex) {
+ logger.log(Level.WARNING, "Unable to get correlation type or value to determine value for O column for artifact", ex);
+ }
+ } else {
+ List listOfPossibleAttributes = EamArtifactUtil.makeInstancesFromBlackboardArtifact(bbArtifact, false);
+ if (listOfPossibleAttributes.size() > 1) {
+ //Don't display anything if there is more than 1 correlation property for an artifact but let the user know
+ description = Bundle.GetSCOTask_occurrences_multipleProperties();
+ } else if (!listOfPossibleAttributes.isEmpty()) {
+ //there should only be one item in the list
+ type = listOfPossibleAttributes.get(0).getCorrelationType();
+ value = listOfPossibleAttributes.get(0).getCorrelationValue();
+ }
+ }
+ } else if (contentNode.getContent() instanceof AbstractFile) {
+ //use the file instance correlation attribute if the node is not a BlackboardArtifactNode
+ try {
+ type = CorrelationAttributeInstance.getDefaultCorrelationTypes().get(CorrelationAttributeInstance.FILES_TYPE_ID);
+ value = ((AbstractFile) contentNode.getContent()).getMd5Hash();
+ } catch (EamDbException ex) {
+ logger.log(Level.WARNING, "Unable to get correlation type to determine value for O column for file", ex);
+ }
+ }
+ scoData.setCountAndDescription(contentNode.getCountPropertyAndDescription(type, value, description));
}
-
+
// signal SCO data is available.
- if (listener != null) {
+ if (listener
+ != null) {
listener.propertyChange(new PropertyChangeEvent(
AutopsyEvent.SourceType.LOCAL.toString(),
AbstractAbstractFileNode.NodeSpecificEvents.SCO_AVAILABLE.toString(),
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java b/Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java
index 9d6ed53431..03c72d2d2e 100644
--- a/Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java
@@ -30,7 +30,6 @@ import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;
@@ -42,7 +41,6 @@ import org.openide.nodes.Sheet;
import org.openide.util.NbBundle;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case;
-import org.sleuthkit.autopsy.casemodule.CasePreferences;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.ingest.IngestManager;
@@ -65,7 +63,7 @@ public class HashsetHits implements AutopsyVisitableItem {
private static final Logger logger = Logger.getLogger(HashsetHits.class.getName());
private SleuthkitCase skCase;
private final HashsetResults hashsetResults;
- private final long datasourceObjId;
+ private final long filteringDSObjId; // 0 if not filtering/grouping by data source
/**
@@ -87,7 +85,7 @@ public class HashsetHits implements AutopsyVisitableItem {
*/
public HashsetHits(SleuthkitCase skCase, long objId) {
this.skCase = skCase;
- this.datasourceObjId = objId;
+ this.filteringDSObjId = objId;
hashsetResults = new HashsetResults();
}
@@ -142,8 +140,8 @@ public class HashsetHits implements AutopsyVisitableItem {
+ "attribute_type_id=" + setNameId //NON-NLS
+ " AND blackboard_attributes.artifact_id=blackboard_artifacts.artifact_id" //NON-NLS
+ " AND blackboard_artifacts.artifact_type_id=" + artId; //NON-NLS
- if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) {
- query += " AND blackboard_artifacts.data_source_obj_id = " + datasourceObjId;
+ if (filteringDSObjId > 0) {
+ query += " AND blackboard_artifacts.data_source_obj_id = " + filteringDSObjId;
}
try (CaseDbQuery dbQuery = skCase.executeQuery(query)) {
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java
index b2c80f1322..da82c315b5 100644
--- a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java
@@ -130,7 +130,7 @@ public class ImageNode extends AbstractContentNode {
"ImageNode.createSheet.type.text=Image",
"ImageNode.createSheet.sectorSize.name=Sector Size (Bytes)",
"ImageNode.createSheet.sectorSize.displayName=Sector Size (Bytes)",
- "ImageNode.createSheet.sectorSize.desc=Sector size of the image in bytes.",
+ "ImageNode.createSheet.sectorSize.desc=Sector size of the image in bytes.",
"ImageNode.createSheet.timezone.name=Timezone",
"ImageNode.createSheet.timezone.displayName=Timezone",
"ImageNode.createSheet.timezone.desc=Timezone of the image",
@@ -257,64 +257,72 @@ public class ImageNode extends AbstractContentNode {
/**
* Reads and returns a list of all tags associated with this content node.
*
- * Null implementation of an abstract method.
- *
+ * Null implementation of an abstract method.
+ *
* @return list of tags associated with the node.
- */
+ */
@Override
protected List getAllTagsFromDatabase() {
return new ArrayList<>();
}
+
/**
- * Returns correlation attribute instance for the underlying content of the node.
- *
- * Null implementation of an abstract method.
- *
- * @return correlation attribute instance for the underlying content of the node.
- */
+ * Returns correlation attribute instance for the underlying content of the
+ * node.
+ *
+ * Null implementation of an abstract method.
+ *
+ * @return correlation attribute instance for the underlying content of the
+ * node.
+ */
@Override
protected CorrelationAttributeInstance getCorrelationAttributeInstance() {
return null;
}
-
+
/**
* Returns Score property for the node.
- *
- * Null implementation of an abstract method.
- *
+ *
+ * Null implementation of an abstract method.
+ *
* @param tags list of tags.
- *
+ *
* @return Score property for the underlying content of the node.
- */
+ */
@Override
protected Pair getScorePropertyAndDescription(List tags) {
return Pair.of(DataResultViewerTable.Score.NO_SCORE, NO_DESCR);
}
+
/**
* Returns comment property for the node.
- *
- * Null implementation of an abstract method.
- *
- * @param tags list of tags
+ *
+ * Null implementation of an abstract method.
+ *
+ * @param tags list of tags
* @param attribute correlation attribute instance
- *
+ *
* @return Comment property for the underlying content of the node.
- */
+ */
@Override
protected DataResultViewerTable.HasCommentStatus getCommentProperty(List tags, CorrelationAttributeInstance attribute) {
return DataResultViewerTable.HasCommentStatus.NO_COMMENT;
}
+
/**
* Returns occurrences/count property for the node.
*
- * Null implementation of an abstract method.
- *
- * @param attribute correlation attribute instance
- *
+ * Null implementation of an abstract method.
+ *
+ * @param attributeType the type of the attribute to count
+ * @param attributeValue the value of the attribute to coun
+ * @param defaultDescription a description to use when none is determined by
+ * the getCountPropertyAndDescription method
+ *
* @return count property for the underlying content of the node.
- */
+ */
@Override
- protected Pair getCountPropertyAndDescription(CorrelationAttributeInstance attribute) {
+ protected Pair getCountPropertyAndDescription(CorrelationAttributeInstance.Type attributeType, String attributeValue, String defaultDescription) {
return Pair.of(-1L, NO_DESCR);
}
}
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java b/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java
index c36833992c..482ebf1558 100644
--- a/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java
@@ -30,7 +30,6 @@ import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;
@@ -42,7 +41,6 @@ import org.openide.nodes.Sheet;
import org.openide.util.NbBundle;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case;
-import org.sleuthkit.autopsy.casemodule.CasePreferences;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.ingest.IngestManager;
@@ -61,7 +59,7 @@ public class InterestingHits implements AutopsyVisitableItem {
private static final Logger logger = Logger.getLogger(InterestingHits.class.getName());
private SleuthkitCase skCase;
private final InterestingResults interestingResults = new InterestingResults();
- private final long datasourceObjId;
+ private final long filteringDSObjId; // 0 if not filtering/grouping by data source
/**
* Constructor
@@ -82,7 +80,7 @@ public class InterestingHits implements AutopsyVisitableItem {
*/
public InterestingHits(SleuthkitCase skCase, long objId) {
this.skCase = skCase;
- this.datasourceObjId = objId;
+ this.filteringDSObjId = objId;
interestingResults.update();
}
@@ -133,8 +131,8 @@ public class InterestingHits implements AutopsyVisitableItem {
+ "attribute_type_id=" + setNameId //NON-NLS
+ " AND blackboard_attributes.artifact_id=blackboard_artifacts.artifact_id" //NON-NLS
+ " AND blackboard_artifacts.artifact_type_id=" + artId; //NON-NLS
- if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) {
- query += " AND blackboard_artifacts.data_source_obj_id = " + datasourceObjId;
+ if (filteringDSObjId > 0) {
+ query += " AND blackboard_artifacts.data_source_obj_id = " + filteringDSObjId;
}
try (CaseDbQuery dbQuery = skCase.executeQuery(query)) {
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java b/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java
index cb24744b0c..655f0c1973 100644
--- a/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java
@@ -31,7 +31,6 @@ import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;
@@ -45,7 +44,6 @@ import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case;
-import org.sleuthkit.autopsy.casemodule.CasePreferences;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger;
import static org.sleuthkit.autopsy.datamodel.Bundle.*;
@@ -76,7 +74,7 @@ public class KeywordHits implements AutopsyVisitableItem {
private SleuthkitCase skCase;
private final KeywordResults keywordResults;
- private final long datasourceObjId;
+ private final long filteringDSObjId; // 0 if not filtering/grouping by data source
/**
* String used in the instance MAP so that exact matches and substring can
@@ -123,7 +121,7 @@ public class KeywordHits implements AutopsyVisitableItem {
*/
public KeywordHits(SleuthkitCase skCase, long objId) {
this.skCase = skCase;
- this.datasourceObjId = objId;
+ this.filteringDSObjId = objId;
keywordResults = new KeywordResults();
}
@@ -344,8 +342,8 @@ public class KeywordHits implements AutopsyVisitableItem {
}
String queryStr = KEYWORD_HIT_ATTRIBUTES_QUERY;
- if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) {
- queryStr += " AND blackboard_artifacts.data_source_obj_id = " + datasourceObjId;
+ if (filteringDSObjId > 0) {
+ queryStr += " AND blackboard_artifacts.data_source_obj_id = " + filteringDSObjId;
}
try (CaseDbQuery dbQuery = skCase.executeQuery(queryStr)) {
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/RootContentChildren.java b/Core/src/org/sleuthkit/autopsy/datamodel/RootContentChildren.java
index 66291955a8..c5d77692c8 100644
--- a/Core/src/org/sleuthkit/autopsy/datamodel/RootContentChildren.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/RootContentChildren.java
@@ -25,6 +25,7 @@ import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.datamodel.accounts.Accounts;
+import org.sleuthkit.datamodel.SleuthkitVisitableItem;
/**
* Children implementation for the root node of a ContentNode tree. Accepts a
@@ -34,6 +35,7 @@ public class RootContentChildren extends Children.Keys {
private final Collection extends Object> contentKeys;
private final CreateAutopsyNodeVisitor createAutopsyNodeVisitor = new CreateAutopsyNodeVisitor();
+ private final CreateSleuthkitNodeVisitor createSleuthkitNodeVisitor = new CreateSleuthkitNodeVisitor();
/**
* @param contentKeys root Content objects for the Node tree
@@ -68,7 +70,7 @@ public class RootContentChildren extends Children.Keys {
if (key instanceof AutopsyVisitableItem) {
return new Node[] {((AutopsyVisitableItem)key).accept(createAutopsyNodeVisitor)};
} else {
- return null;
+ return new Node[] {((SleuthkitVisitableItem)key).accept(createSleuthkitNodeVisitor)};
}
}
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Tags.java b/Core/src/org/sleuthkit/autopsy/datamodel/Tags.java
index abf77350d2..2dfc02678b 100644
--- a/Core/src/org/sleuthkit/autopsy/datamodel/Tags.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/Tags.java
@@ -23,7 +23,6 @@ import java.beans.PropertyChangeListener;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
-import java.util.Objects;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;
@@ -35,7 +34,6 @@ import org.openide.nodes.Sheet;
import org.openide.util.NbBundle;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case;
-import org.sleuthkit.autopsy.casemodule.CasePreferences;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.casemodule.services.TagsManager;
import org.sleuthkit.autopsy.core.UserPreferences;
@@ -61,14 +59,14 @@ public class Tags implements AutopsyVisitableItem {
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 long datasourceObjId;
+ private final long filteringDSObjId; // 0 if not filtering/grouping by data source
Tags() {
this(0);
}
Tags(long dsObjId) {
- this.datasourceObjId = dsObjId;
+ this.filteringDSObjId = dsObjId;
}
/**
@@ -81,7 +79,7 @@ public class Tags implements AutopsyVisitableItem {
}
long filteringDataSourceObjId() {
- return this.datasourceObjId;
+ return this.filteringDSObjId;
}
@Override
@@ -156,7 +154,7 @@ public class Tags implements AutopsyVisitableItem {
private class TagNameNodeFactory extends ChildFactory.Detachable implements Observer {
- private final long datasourceObjId;
+ private final long filteringDSObjId; // 0 if not filtering/grouping by data source
private final Set CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED,
Case.Events.BLACKBOARD_ARTIFACT_TAG_DELETED,
@@ -219,7 +217,7 @@ public class Tags implements AutopsyVisitableItem {
* @param objId data source object id
*/
TagNameNodeFactory(long objId) {
- this.datasourceObjId = objId;
+ this.filteringDSObjId = objId;
}
@@ -246,12 +244,12 @@ public class Tags implements AutopsyVisitableItem {
List tagNamesInUse;
if (UserPreferences.showOnlyCurrentUserTags()) {
String userName = System.getProperty(USER_NAME_PROPERTY);
- tagNamesInUse = Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)
- ? Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUseForUser(datasourceObjId, userName)
+ tagNamesInUse = (filteringDSObjId > 0)
+ ? Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUseForUser(filteringDSObjId, userName)
: Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUseForUser(userName);
} else {
- tagNamesInUse = Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)
- ? Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUse(datasourceObjId)
+ tagNamesInUse = (filteringDSObjId > 0)
+ ? Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUse(filteringDSObjId)
: Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUse();
}
Collections.sort(tagNamesInUse);
@@ -303,17 +301,17 @@ public class Tags implements AutopsyVisitableItem {
TagsManager tm = Case.getCurrentCaseThrows().getServices().getTagsManager();
if (UserPreferences.showOnlyCurrentUserTags()) {
String userName = System.getProperty(USER_NAME_PROPERTY);
- if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) {
- tagsCount = tm.getContentTagsCountByTagNameForUser(tagName, datasourceObjId, userName);
- tagsCount += tm.getBlackboardArtifactTagsCountByTagNameForUser(tagName, datasourceObjId, userName);
+ if (filteringDSObjId > 0) {
+ tagsCount = tm.getContentTagsCountByTagNameForUser(tagName, filteringDSObjId, userName);
+ tagsCount += tm.getBlackboardArtifactTagsCountByTagNameForUser(tagName, filteringDSObjId, userName);
} else {
tagsCount = tm.getContentTagsCountByTagNameForUser(tagName, userName);
tagsCount += tm.getBlackboardArtifactTagsCountByTagNameForUser(tagName, userName);
}
} else {
- if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) {
- tagsCount = tm.getContentTagsCountByTagName(tagName, datasourceObjId);
- tagsCount += tm.getBlackboardArtifactTagsCountByTagName(tagName, datasourceObjId);
+ if (filteringDSObjId > 0) {
+ tagsCount = tm.getContentTagsCountByTagName(tagName, filteringDSObjId);
+ tagsCount += tm.getBlackboardArtifactTagsCountByTagName(tagName, filteringDSObjId);
} else {
tagsCount = tm.getContentTagsCountByTagName(tagName);
tagsCount += tm.getBlackboardArtifactTagsCountByTagName(tagName);
@@ -424,12 +422,12 @@ public class Tags implements AutopsyVisitableItem {
if (UserPreferences.showOnlyCurrentUserTags()) {
String userName = System.getProperty(USER_NAME_PROPERTY);
- tagsCount = Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)
- ? Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsCountByTagNameForUser(tagName, datasourceObjId, userName)
+ tagsCount = (filteringDSObjId > 0)
+ ? Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsCountByTagNameForUser(tagName, filteringDSObjId, userName)
: Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsCountByTagNameForUser(tagName, userName);
} else {
- tagsCount = Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)
- ? Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsCountByTagName(tagName, datasourceObjId)
+ tagsCount = (filteringDSObjId > 0)
+ ? Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsCountByTagName(tagName, filteringDSObjId)
: Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsCountByTagName(tagName);
}
} catch (TskCoreException | NoCurrentCaseException ex) {
@@ -486,8 +484,8 @@ public class Tags implements AutopsyVisitableItem {
protected boolean createKeys(List keys) {
// Use the content tags bearing the specified tag name as the keys.
try {
- List contentTags = Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)
- ? Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByTagName(tagName, datasourceObjId)
+ List contentTags = (filteringDSObjId > 0)
+ ? Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByTagName(tagName, filteringDSObjId)
: Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByTagName(tagName);
if (UserPreferences.showOnlyCurrentUserTags()) {
String userName = System.getProperty(USER_NAME_PROPERTY);
@@ -544,12 +542,12 @@ public class Tags implements AutopsyVisitableItem {
try {
if (UserPreferences.showOnlyCurrentUserTags()) {
String userName = System.getProperty(USER_NAME_PROPERTY);
- tagsCount = Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)
- ? Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagNameForUser(tagName, datasourceObjId, userName)
+ tagsCount = (filteringDSObjId > 0)
+ ? Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagNameForUser(tagName, filteringDSObjId, userName)
: Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagNameForUser(tagName, userName);
} else {
- tagsCount = Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)
- ? Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagName(tagName, datasourceObjId)
+ tagsCount = (filteringDSObjId > 0)
+ ? Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagName(tagName, filteringDSObjId)
: Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagName(tagName);
}
} catch (TskCoreException | NoCurrentCaseException ex) {
@@ -606,8 +604,8 @@ public class Tags implements AutopsyVisitableItem {
protected boolean createKeys(List keys) {
try {
// Use the blackboard artifact tags bearing the specified tag name as the keys.
- List artifactTags = Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)
- ? Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsByTagName(tagName, datasourceObjId)
+ List artifactTags = (filteringDSObjId > 0)
+ ? Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsByTagName(tagName, filteringDSObjId)
: Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsByTagName(tagName);
if (UserPreferences.showOnlyCurrentUserTags()) {
String userName = System.getProperty(USER_NAME_PROPERTY);
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/VolumeNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/VolumeNode.java
index 233d098071..307013136f 100644
--- a/Core/src/org/sleuthkit/autopsy/datamodel/VolumeNode.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/VolumeNode.java
@@ -50,6 +50,7 @@ import org.sleuthkit.datamodel.Tag;
* root directory of a file system
*/
public class VolumeNode extends AbstractContentNode {
+
private static final Logger logger = Logger.getLogger(VolumeNode.class.getName());
/**
@@ -217,67 +218,76 @@ public class VolumeNode extends AbstractContentNode {
public String getItemType() {
return DisplayableItemNode.FILE_PARENT_NODE_KEY;
}
+
/**
* Reads and returns a list of all tags associated with this content node.
*
- * Null implementation of an abstract method.
- *
+ * Null implementation of an abstract method.
+ *
* @return list of tags associated with the node.
- */
+ */
@Override
protected List getAllTagsFromDatabase() {
return new ArrayList<>();
}
+
/**
- * Returns correlation attribute instance for the underlying content of the node.
- *
- * Null implementation of an abstract method.
- *
- * @return correlation attribute instance for the underlying content of the node.
- */
+ * Returns correlation attribute instance for the underlying content of the
+ * node.
+ *
+ * Null implementation of an abstract method.
+ *
+ * @return correlation attribute instance for the underlying content of the
+ * node.
+ */
@Override
protected CorrelationAttributeInstance getCorrelationAttributeInstance() {
return null;
}
-
+
/**
* Returns Score property for the node.
- *
- * Null implementation of an abstract method.
- *
+ *
+ * Null implementation of an abstract method.
+ *
* @param tags list of tags.
- *
+ *
* @return Score property for the underlying content of the node.
- */
+ */
@Override
protected Pair getScorePropertyAndDescription(List tags) {
return Pair.of(DataResultViewerTable.Score.NO_SCORE, NO_DESCR);
}
+
/**
* Returns comment property for the node.
- *
- * Null implementation of an abstract method.
- *
- * @param tags list of tags
+ *
+ * Null implementation of an abstract method.
+ *
+ * @param tags list of tags
* @param attribute correlation attribute instance
- *
+ *
* @return Comment property for the underlying content of the node.
- */
+ */
@Override
protected DataResultViewerTable.HasCommentStatus getCommentProperty(List tags, CorrelationAttributeInstance attribute) {
return DataResultViewerTable.HasCommentStatus.NO_COMMENT;
}
+
/**
* Returns occurrences/count property for the node.
*
- * Null implementation of an abstract method.
- *
- * @param attribute correlation attribute instance
- *
+ * Null implementation of an abstract method.
+ *
+ * @param attributeType the type of the attribute to count
+ * @param attributeValue the value of the attribute to coun
+ * @param defaultDescription a description to use when none is determined by
+ * the getCountPropertyAndDescription method
+ *
* @return count property for the underlying content of the node.
- */
+ */
@Override
- protected Pair getCountPropertyAndDescription(CorrelationAttributeInstance attribute) {
+ protected Pair getCountPropertyAndDescription(CorrelationAttributeInstance.Type attributeType, String attributeValue, String defaultDescription) {
return Pair.of(-1L, NO_DESCR);
}
}
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Accounts.java b/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Accounts.java
index d8e40d8a3e..4cd6e330e0 100644
--- a/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Accounts.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Accounts.java
@@ -57,9 +57,7 @@ import org.openide.util.NbBundle;
import org.openide.util.Utilities;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case;
-import org.sleuthkit.autopsy.casemodule.CasePreferences;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
-import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.corecomponents.DataResultTopComponent;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.AutopsyItemVisitor;
@@ -96,7 +94,7 @@ final public class Accounts implements AutopsyVisitableItem {
final public static String NAME = Bundle.AccountsRootNode_name();
private SleuthkitCase skCase;
- private final long datasourceObjId;
+ private final long filteringDSObjId; // 0 if not filtering/grouping by data source
private final EventBus reviewStatusBus = new EventBus("ReviewStatusBus");
@@ -123,7 +121,7 @@ final public class Accounts implements AutopsyVisitableItem {
*/
public Accounts(SleuthkitCase skCase, long objId) {
this.skCase = skCase;
- this.datasourceObjId = objId;
+ this.filteringDSObjId = objId;
this.rejectActionInstance = new RejectAccounts();
this.approveActionInstance = new ApproveAccounts();
@@ -153,8 +151,8 @@ final public class Accounts implements AutopsyVisitableItem {
* based on the CasePreferences groupItemsInTreeByDataSource setting
*/
private String getFilterByDataSourceClause() {
- if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) {
- return " AND blackboard_artifacts.data_source_obj_id = " + datasourceObjId + " ";
+ if (filteringDSObjId > 0) {
+ return " AND blackboard_artifacts.data_source_obj_id = " + filteringDSObjId + " ";
}
return " ";
diff --git a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties
index 35378ab1a3..b3732ce149 100644
--- a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties
+++ b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties
@@ -6,7 +6,7 @@ OptionsCategory_Name_InterestingItemDefinitions=Interesting Files
OptionsCategory_Keywords_InterestingItemDefinitions=InterestingItemDefinitions
InterestingItemsIdentifierIngestModule.moduleName=Interesting Files Identifier
InterestingItemsIdentifierIngestModule.moduleDescription=Identifies interesting items as defined by interesting item rule sets.
-FilesSetPanel.interesting.title=Interesting Files Set
+FilesSetPanel.interesting.title=Interesting Files Set Rule
FilesSetPanel.interesting.messages.filesSetsMustBeNamed=Interesting files sets must be named.
FilesSetPanel.messages.filesSetsReservedName=You have chosen a name reserved by the software, please choose a different name.
FilesSetPanel.ignoreKnownFilesCheckbox.text=Ignore Known Files
@@ -31,13 +31,13 @@ FilesSetRulePanel.messages.invalidCharInName=The name cannot contain \\, /, :, *
FilesSetRulePanel.messages.invalidCharInPath=The path cannot contain \\, :, *, ?, \", <, or > unless it is a regular expression.
FilesSetRulePanel.messages.invalidPathRegex=The path regular expression is not valid:\n\n{0}
FilesSetDefsPanel.doFileSetsDialog.duplicateRuleSet.text=Rule set with name {0} already exists.
-FilesSetRulePanel.pathSeparatorInfoLabel.text=Use / as path separator
+FilesSetRulePanel.pathSeparatorInfoLabel.text=Folder must be in parent path. Use '/' to give consecutive names
FilesIdentifierIngestJobSettingsPanel.border.title=Select interesting files sets to enable during ingest:
FilesSetRulePanel.jLabel1.text=Type:
FilesSetRulePanel.interesting.jLabel5.text=Enter information about files that you want to find.
FilesSetRulePanel.ingest.jLabel5.text=Enter information about files that you want to run ingest on.
FilesSetRulePanel.nameCheck.text=Name:
-FilesSetRulePanel.pathCheck.text=Path Substring:
+FilesSetRulePanel.pathCheck.text=Folder Name:
FilesSetRulePanel.filesRadioButton.text=Files
FilesSetRulePanel.dirsRadioButton.text=Directories
FilesSetDefsPanel.interesting.setsListLabel.text=Rule Sets:
diff --git a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties-MERGED
index 7ca4901b1b..ad6fe6f1c9 100755
--- a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties-MERGED
+++ b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties-MERGED
@@ -37,6 +37,8 @@ FilesSetPanel.ingest.createNewFilter=Create/edit file ingest filters...
FilesSetPanel.ingest.messages.filtersMustBeNamed=File ingest filters must be named.
FilesSetPanel.rule.title=File Filter Rule
FilesSetRulePanel.bytes=Bytes
+#{0} - regex
+FilesSetRulePanel.CommaInRegexWarning=Warning: Comma(s) in the file extension field will be interpreted as part of a regex and will not split the entry into multiple extensions (Entered: "{0}")
FilesSetRulePanel.DaysIncludedEmptyError=Number of days included cannot be empty.
FilesSetRulePanel.DaysIncludedInvalidError=Number of days included must be a positive integer.
FilesSetRulePanel.gigaBytes=Gigabytes
@@ -60,7 +62,7 @@ OptionsCategory_Name_InterestingItemDefinitions=Interesting Files
OptionsCategory_Keywords_InterestingItemDefinitions=InterestingItemDefinitions
InterestingItemsIdentifierIngestModule.moduleName=Interesting Files Identifier
InterestingItemsIdentifierIngestModule.moduleDescription=Identifies interesting items as defined by interesting item rule sets.
-FilesSetPanel.interesting.title=Interesting Files Set
+FilesSetPanel.interesting.title=Interesting Files Set Rule
FilesSetPanel.interesting.messages.filesSetsMustBeNamed=Interesting files sets must be named.
FilesSetPanel.messages.filesSetsReservedName=You have chosen a name reserved by the software, please choose a different name.
FilesSetPanel.ignoreKnownFilesCheckbox.text=Ignore Known Files
@@ -85,13 +87,13 @@ FilesSetRulePanel.messages.invalidCharInName=The name cannot contain \\, /, :, *
FilesSetRulePanel.messages.invalidCharInPath=The path cannot contain \\, :, *, ?, \", <, or > unless it is a regular expression.
FilesSetRulePanel.messages.invalidPathRegex=The path regular expression is not valid:\n\n{0}
FilesSetDefsPanel.doFileSetsDialog.duplicateRuleSet.text=Rule set with name {0} already exists.
-FilesSetRulePanel.pathSeparatorInfoLabel.text=Use / as path separator
+FilesSetRulePanel.pathSeparatorInfoLabel.text=Folder must be in parent path. Use '/' to give consecutive names
FilesIdentifierIngestJobSettingsPanel.border.title=Select interesting files sets to enable during ingest:
FilesSetRulePanel.jLabel1.text=Type:
FilesSetRulePanel.interesting.jLabel5.text=Enter information about files that you want to find.
FilesSetRulePanel.ingest.jLabel5.text=Enter information about files that you want to run ingest on.
FilesSetRulePanel.nameCheck.text=Name:
-FilesSetRulePanel.pathCheck.text=Path Substring:
+FilesSetRulePanel.pathCheck.text=Folder Name:
FilesSetRulePanel.filesRadioButton.text=Files
FilesSetRulePanel.dirsRadioButton.text=Directories
FilesSetDefsPanel.interesting.setsListLabel.text=Rule Sets:
diff --git a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetDefsPanel.form b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetDefsPanel.form
index 416aad285c..7497f936bb 100644
--- a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetDefsPanel.form
+++ b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetDefsPanel.form
@@ -832,6 +832,7 @@
+
diff --git a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetDefsPanel.java b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetDefsPanel.java
index 25db05decb..5ff9d2a172 100644
--- a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetDefsPanel.java
+++ b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetDefsPanel.java
@@ -811,6 +811,7 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
org.openide.awt.Mnemonics.setLocalizedText(jLabel7, org.openide.util.NbBundle.getMessage(FilesSetDefsPanel.class, "FilesSetDefsPanel.jLabel7.text")); // NOI18N
mimeTypeComboBox.setBackground(new java.awt.Color(240, 240, 240));
+ mimeTypeComboBox.setEditable(true);
mimeTypeComboBox.setModel(new javax.swing.DefaultComboBoxModel(new String[] {""}));
mimeTypeComboBox.setEnabled(false);
mimeTypeComboBox.setMinimumSize(new java.awt.Dimension(0, 20));
diff --git a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetRulePanel.java b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetRulePanel.java
index 9744b0c5bf..90cc881ad8 100644
--- a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetRulePanel.java
+++ b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetRulePanel.java
@@ -57,7 +57,9 @@ final class FilesSetRulePanel extends javax.swing.JPanel {
"FilesSetRulePanel.NoPathError=Path cannot be empty",
"FilesSetRulePanel.DaysIncludedEmptyError=Number of days included cannot be empty.",
"FilesSetRulePanel.DaysIncludedInvalidError=Number of days included must be a positive integer.",
- "FilesSetRulePanel.ZeroFileSizeError=File size condition value must not be 0 (Unless = is selected)."
+ "FilesSetRulePanel.ZeroFileSizeError=File size condition value must not be 0 (Unless = is selected).",
+ "#{0} - regex",
+ "FilesSetRulePanel.CommaInRegexWarning=Warning: Comma(s) in the file extension field will be interpreted as part of a regex and will not split the entry into multiple extensions (Entered: \"{0}\")",
})
private static final long serialVersionUID = 1L;
@@ -130,6 +132,7 @@ final class FilesSetRulePanel extends javax.swing.JPanel {
this.setButtons(okButton, cancelButton);
updateNameTextFieldPrompt();
+ setComponentsForSearchType();
}
/**
@@ -358,6 +361,16 @@ final class FilesSetRulePanel extends javax.swing.JPanel {
return false;
}
if (this.nameRegexCheckbox.isSelected()) {
+
+ // If extension is also selected and the regex contains a comma, display a warning
+ // since it is unclear whether the comma is part of a regex or is separating extensions.
+ if (this.extensionRadioButton.isSelected() && this.nameTextField.getText().contains(",")) {
+ NotifyDescriptor notifyDesc = new NotifyDescriptor.Message(
+ Bundle.FilesSetRulePanel_CommaInRegexWarning(this.nameTextField.getText()),
+ NotifyDescriptor.WARNING_MESSAGE);
+ DialogDisplayer.getDefault().notify(notifyDesc);
+ }
+
try {
Pattern.compile(this.nameTextField.getText());
} catch (PatternSyntaxException ex) {
diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java b/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java
index b54af094ad..be1ec45965 100644
--- a/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java
+++ b/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java
@@ -48,18 +48,18 @@ import java.util.TreeMap;
import java.util.logging.Level;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
+import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringEscapeUtils;
import org.openide.filesystems.FileUtil;
import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
-import org.sleuthkit.autopsy.casemodule.services.Services;
import org.sleuthkit.autopsy.casemodule.services.TagsManager;
-import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase;
-import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
-import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
-import org.sleuthkit.autopsy.centralrepository.datamodel.EamOrganization;
+import org.sleuthkit.autopsy.casemodule.services.contentviewertags.ContentViewerTagManager;
+import org.sleuthkit.autopsy.casemodule.services.contentviewertags.ContentViewerTagManager.ContentViewerTag;
+import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagRegion;
+import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagsUtility;
import org.sleuthkit.autopsy.coreutils.EscapeUtil;
import org.sleuthkit.autopsy.coreutils.ImageUtils;
import org.sleuthkit.autopsy.coreutils.Logger;
@@ -731,6 +731,29 @@ class ReportHTML implements TableReportModule {
logger.log(Level.SEVERE, "Output writer is null. Page was not initialized before writing.", ex); //NON-NLS
}
}
+
+ /**
+ * Finds all associated image tags.
+ *
+ * @param contentTags
+ * @return
+ */
+ private List getTaggedRegions(List contentTags) {
+ ArrayList tagRegions = new ArrayList<>();
+ contentTags.forEach((contentTag) -> {
+ try {
+ ContentViewerTag contentViewerTag = ContentViewerTagManager
+ .getTag(contentTag, ImageTagRegion.class);
+ if (contentViewerTag != null) {
+ tagRegions.add(contentViewerTag.getDetails());
+ }
+ } catch (TskCoreException | NoCurrentCaseException ex) {
+ logger.log(Level.WARNING, "Could not get content viewer tag "
+ + "from case db for content_tag with id %d", contentTag.getId());
+ }
+ });
+ return tagRegions;
+ }
/**
* Add the body of the thumbnails table.
@@ -770,13 +793,54 @@ class ReportHTML implements TableReportModule {
}
AbstractFile file = (AbstractFile) content;
+ List contentTags = new ArrayList<>();
+
+ String thumbnailPath = null;
+ String imageWithTagsFullPath = null;
+ try {
+ //Get content tags and all image tags
+ contentTags = Case.getCurrentCase().getServices()
+ .getTagsManager().getContentTagsByContent(file);
+ List imageTags = getTaggedRegions(contentTags);
+
+ if(!imageTags.isEmpty()) {
+ //Write the tags to the fullsize and thumbnail images
+ BufferedImage fullImageWithTags = ImageTagsUtility.writeTags(file, imageTags, "png");
+
+ BufferedImage thumbnailImageWithTags = ImageTagsUtility.makeThumbnail(file,
+ imageTags, ImageTagsUtility.IconSize.MEDIUM, "png");
+
+ String fileName = org.sleuthkit.autopsy.coreutils.FileUtil.escapeFileName(file.getName());
+
+ //Create paths in report to write tagged images
+ File thumbnailImageWithTagsFile = Paths.get(thumbsPath, FilenameUtils.removeExtension(fileName) + ".png").toFile();
+ String fullImageWithTagsPath = makeCustomUniqueFilePath(file, "thumbs_fullsize");
+ fullImageWithTagsPath = FilenameUtils.removeExtension(fullImageWithTagsPath) + ".png";
+ File fullImageWithTagsFile = Paths.get(fullImageWithTagsPath).toFile();
+
+ //Save images
+ ImageIO.write(thumbnailImageWithTags, "png", thumbnailImageWithTagsFile);
+ ImageIO.write(fullImageWithTags, "png", fullImageWithTagsFile);
+
+ thumbnailPath = THUMBS_REL_PATH + thumbnailImageWithTagsFile.getName();
+ //Relative path
+ imageWithTagsFullPath = fullImageWithTagsPath.substring(subPath.length());
+ }
+ } catch (TskCoreException ex) {
+ logger.log(Level.WARNING, "Could not get tags for file.", ex); //NON-NLS
+ } catch (IOException ex) {
+ logger.log(Level.WARNING, "Could make marked up thumbnail.", ex); //NON-NLS
+ }
// save copies of the orginal image and thumbnail image
- String thumbnailPath = prepareThumbnail(file);
+ if(thumbnailPath == null) {
+ thumbnailPath = prepareThumbnail(file);
+ }
+
if (thumbnailPath == null) {
continue;
}
- String contentPath = saveContent(file, "thumbs_fullsize"); //NON-NLS
+ String contentPath = saveContent(file, "original"); //NON-NLS
String nameInImage;
try {
nameInImage = file.getUniquePath();
@@ -785,32 +849,27 @@ class ReportHTML implements TableReportModule {
}
StringBuilder linkToThumbnail = new StringBuilder();
- linkToThumbnail.append("");
- linkToThumbnail.append("
");
- linkToThumbnail.append(" "); //NON-NLS
- linkToThumbnail.append(" "); //NON-NLS
- linkToThumbnail.append(file.getName()).append("
"); //NON-NLS
-
- Services services = currentCase.getServices();
- TagsManager tagsManager = services.getTagsManager();
- try {
- List
tags = tagsManager.getContentTagsByContent(content);
- if (tags.size() > 0) {
- linkToThumbnail.append(NbBundle.getMessage(this.getClass(), "ReportHTML.thumbLink.tags"));
- }
- for (int i = 0; i < tags.size(); i++) {
- ContentTag tag = tags.get(i);
- String notableString = tag.getName().getKnownStatus() == TskData.FileKnown.BAD ? TagsManager.getNotableTagLabel() : "";
- linkToThumbnail.append(tag.getName().getDisplayName()).append(notableString);
- if (i != tags.size() - 1) {
- linkToThumbnail.append(", ");
- }
- }
- } catch (TskCoreException ex) {
- logger.log(Level.WARNING, "Could not find get tags for file.", ex); //NON-NLS
+ linkToThumbnail.append("") //NON-NLS
+ .append(file.getName()).append("
"); //NON-NLS
+ if(imageWithTagsFullPath != null) {
+ linkToThumbnail.append("
View Original ");
}
+
+ if (!contentTags.isEmpty()) {
+ linkToThumbnail.append(NbBundle.getMessage(this.getClass(), "ReportHTML.thumbLink.tags"));
+ }
+ for (int i = 0; i < contentTags.size(); i++) {
+ ContentTag tag = contentTags.get(i);
+ String notableString = tag.getName().getKnownStatus() == TskData.FileKnown.BAD ? TagsManager.getNotableTagLabel() : "";
+ linkToThumbnail.append(tag.getName().getDisplayName()).append(notableString);
+ if (i != contentTags.size() - 1) {
+ linkToThumbnail.append(", ");
+ }
+ }
+
linkToThumbnail.append("
");
currentRow.add(linkToThumbnail.toString());
@@ -839,17 +898,8 @@ class ReportHTML implements TableReportModule {
|| file.getType() == TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS
|| file.getType() == TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS;
}
-
- /**
- * Save a local copy of the given file in the reports folder.
- *
- * @param file File to save
- * @param dirName Custom top-level folder to use to store the files in (tag
- * name, etc.)
- *
- * @return Path to where file was stored (relative to root of HTML folder)
- */
- public String saveContent(AbstractFile file, String dirName) {
+
+ private String makeCustomUniqueFilePath(AbstractFile file, String dirName) {
// clean up the dir name passed in
String dirName2 = org.sleuthkit.autopsy.coreutils.FileUtil.escapeFileName(dirName);
@@ -883,16 +933,32 @@ class ReportHTML implements TableReportModule {
}
localFilePath.append(File.separator);
localFilePath.append(fileName);
+
+ return localFilePath.toString();
+ }
+
+ /**
+ * Save a local copy of the given file in the reports folder.
+ *
+ * @param file File to save
+ * @param dirName Custom top-level folder to use to store the files in (tag
+ * name, etc.)
+ *
+ * @return Path to where file was stored (relative to root of HTML folder)
+ */
+ public String saveContent(AbstractFile file, String dirName) {
+
+ String localFilePath = makeCustomUniqueFilePath(file, dirName);
// If the local file doesn't already exist, create it now.
// The existence check is necessary because it is possible to apply multiple tags with the same tagName to a file.
- File localFile = new File(localFilePath.toString());
+ File localFile = new File(localFilePath);
if (!localFile.exists()) {
ExtractFscContentVisitor.extract(file, localFile, null, null);
}
// get the relative path
- return localFilePath.toString().substring(subPath.length());
+ return localFilePath.substring(subPath.length());
}
/**
diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java
index 66d0803812..fe1e9083dd 100644
--- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java
+++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java
@@ -217,10 +217,10 @@ class QueryResults {
SleuthkitCase tskCase = Case.getCurrentCaseThrows().getSleuthkitCase();
content = tskCase.getContentById(hit.getContentID());
} catch (TskCoreException | NoCurrentCaseException tskCoreException) {
- logger.log(Level.SEVERE, "Failed to get text source object for ", tskCoreException); //NON-NLS
+ logger.log(Level.SEVERE, "Failed to get text source object for keyword hit", tskCoreException); //NON-NLS
}
- if (saveResults) {
+ if ((content != null) && saveResults) {
/*
* Post an artifact for the hit to the blackboard.
*/
diff --git a/docs/doxygen-user/interesting_files.dox b/docs/doxygen-user/interesting_files.dox
index 080b1072af..7e4147a66f 100644
--- a/docs/doxygen-user/interesting_files.dox
+++ b/docs/doxygen-user/interesting_files.dox
@@ -42,7 +42,7 @@ The top line allows you to choose whether you want to match only files, only dir
Each rule must have at least one condition. To create conditions, check the box to the left of the condition you want to enable. The following is a description of each condition, with some full examples after.
-Name - Enter either the full file name or one or more extensions, and select whether this is an exact match or a substring/regex match. If substring/regex match is enabled, it will automatically add wildcards to the beginning and end of the text. If you're only matching directories, this will match the directory name. If you're using a comma-separated list of extensions, make sure the regex checkbox is disabled - the two features do not work together. The following table shows some examples of what the different combinations can be used for.
+Name - Enter either the full file name or one or more extensions, and select whether this is an exact match or a substring/regex match. If substring/regex match is enabled, it will automatically add wildcards to the beginning and end of the text. If you're only matching directories, this will match the directory name. If you're using a comma-separated list of extensions, make sure the regex checkbox is disabled - the entire contents will be interpreted as one regex when the checkbox is selected. The following table shows some examples of what the different combinations can be used for.
Type Substring/Regex Text Description Sample match