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/ImageUtils.java b/Core/src/org/sleuthkit/autopsy/coreutils/ImageUtils.java
index 97922760c5..c2ed64987e 100644
--- a/Core/src/org/sleuthkit/autopsy/coreutils/ImageUtils.java
+++ b/Core/src/org/sleuthkit/autopsy/coreutils/ImageUtils.java
@@ -22,7 +22,6 @@
package org.sleuthkit.autopsy.coreutils;
import com.google.common.collect.ImmutableSortedSet;
-import com.google.common.io.Files;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
@@ -122,7 +121,7 @@ public class ImageUtils {
}
DEFAULT_THUMBNAIL = tempImage;
boolean tempFfmpegLoaded = false;
- if (OpenCvLoader.isOpenCvLoaded()) {
+ if (OpenCvLoader.hasOpenCvLoaded()) {
try {
if (System.getProperty("os.arch").equals("amd64") || System.getProperty("os.arch").equals("x86_64")) { //NON-NLS
System.loadLibrary("opencv_ffmpeg248_64"); //NON-NLS
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 fa4529e44b..eeacf491bf 100644
--- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java
@@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
- * Copyright 2011-2018 Basis Technology Corp.
+ * Copyright 2011-2019 Basis Technology Corp.
* Contact: carrier sleuthkit org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,7 +18,6 @@
*/
package org.sleuthkit.autopsy.datamodel;
-import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.ref.WeakReference;
@@ -27,8 +26,6 @@ import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.stream.Collectors;
import org.apache.commons.io.FilenameUtils;
@@ -65,6 +62,7 @@ import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.ContentTag;
+import org.sleuthkit.datamodel.Tag;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
@@ -76,15 +74,10 @@ import org.sleuthkit.datamodel.TskData;
public abstract class AbstractAbstractFileNode extends AbstractContentNode {
private static final Logger logger = Logger.getLogger(AbstractAbstractFileNode.class.getName());
- @NbBundle.Messages("AbstractAbstractFileNode.addFileProperty.desc=no description")
- private static final String NO_DESCR = AbstractAbstractFileNode_addFileProperty_desc();
private static final Set CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.CURRENT_CASE,
Case.Events.CONTENT_TAG_ADDED, Case.Events.CONTENT_TAG_DELETED, Case.Events.CR_COMMENT_CHANGED);
- private static final ExecutorService translationPool;
- private static final Integer MAX_POOL_SIZE = 10;
-
/**
* @param abstractFile file to wrap
*/
@@ -101,7 +94,7 @@ public abstract class AbstractAbstractFileNode extends A
}
if (UserPreferences.displayTranslatedFileNames()) {
- AbstractAbstractFileNode.translationPool.submit(new TranslationTask(
+ backgroundTasksPool.submit(new TranslationTask(
new WeakReference<>(this), weakPcl));
}
@@ -110,13 +103,6 @@ public abstract class AbstractAbstractFileNode extends A
Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, weakPcl);
}
- 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.
- translationPool = Executors.newFixedThreadPool(MAX_POOL_SIZE,
- new ThreadFactoryBuilder().setNameFormat("translation-task-thread-%d").build());
- }
-
/**
* The finalizer removes event listeners as the BlackboardArtifactNode is
* being garbage collected. Yes, we know that finalizers are considered to
@@ -137,16 +123,6 @@ public abstract class AbstractAbstractFileNode extends A
Case.removeEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, weakPcl);
}
- /**
- * Event signals to indicate the background tasks have completed processing.
- * Currently, we have one property task in the background:
- *
- * 1) Retreiving the translation of the file name
- */
- enum NodeSpecificEvents {
- TRANSLATION_AVAILABLE,
- }
-
private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> {
String eventType = evt.getPropertyName();
@@ -190,7 +166,7 @@ public abstract class AbstractAbstractFileNode extends A
} else if (eventType.equals(Case.Events.CONTENT_TAG_ADDED.toString())) {
ContentTagAddedEvent event = (ContentTagAddedEvent) evt;
if (event.getAddedTag().getContent().equals(content)) {
- List tags = getContentTagsFromDatabase();
+ List tags = this.getAllTagsFromDatabase();
Pair scorePropAndDescr = getScorePropertyAndDescription(tags);
Score value = scorePropAndDescr.getLeft();
String descr = scorePropAndDescr.getRight();
@@ -202,7 +178,7 @@ public abstract class AbstractAbstractFileNode extends A
} else if (eventType.equals(Case.Events.CONTENT_TAG_DELETED.toString())) {
ContentTagDeletedEvent event = (ContentTagDeletedEvent) evt;
if (event.getDeletedTagInfo().getContentID() == content.getId()) {
- List tags = getContentTagsFromDatabase();
+ List tags = getAllTagsFromDatabase();
Pair scorePropAndDescr = getScorePropertyAndDescription(tags);
Score value = scorePropAndDescr.getLeft();
String descr = scorePropAndDescr.getRight();
@@ -214,7 +190,7 @@ public abstract class AbstractAbstractFileNode extends A
} else if (eventType.equals(Case.Events.CR_COMMENT_CHANGED.toString())) {
CommentChangedEvent event = (CommentChangedEvent) evt;
if (event.getContentID() == content.getId()) {
- List tags = getContentTagsFromDatabase();
+ List tags = getAllTagsFromDatabase();
CorrelationAttributeInstance attribute = getCorrelationAttributeInstance();
updateSheet(new NodeProperty<>(COMMENT.toString(), COMMENT.toString(), NO_DESCR, getCommentProperty(tags, attribute)));
}
@@ -223,6 +199,17 @@ public abstract class AbstractAbstractFileNode extends A
//Set the tooltip
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()) && !UserPreferences.getHideSCOColumns()) {
+ 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) {
+ updateSheet(new NodeProperty<>(OCCURRENCES.toString(), OCCURRENCES.toString(), scoData.getCountAndDescription().getRight(), scoData.getCountAndDescription().getLeft()));
+ }
}
};
/**
@@ -235,38 +222,6 @@ public abstract class AbstractAbstractFileNode extends A
*/
private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(pcl, null);
- /**
- * 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
- * current sheet will it be applied. That way, we allow for subclasses to
- * add their own (or omit some!) properties and we will not accidentally
- * disrupt their UI.
- *
- * Race condition if not synchronized. Only one update should be applied at
- * a time.
- *
- * @param newProps New file property instances to be updated in the current
- * sheet.
- */
- private synchronized void updateSheet(NodeProperty>... newProps) {
- //Refresh ONLY those properties in the sheet currently. Subclasses may have
- //only added a subset of our properties or their own props.s
- Sheet visibleSheet = this.getSheet();
- Sheet.Set visibleSheetSet = visibleSheet.get(Sheet.PROPERTIES);
- Property>[] visibleProps = visibleSheetSet.getProperties();
- for (NodeProperty> newProp : newProps) {
- for (int i = 0; i < visibleProps.length; i++) {
- if (visibleProps[i].getName().equals(newProp.getName())) {
- visibleProps[i] = newProp;
- }
- }
- }
- visibleSheetSet.put(visibleProps);
- visibleSheet.put(visibleSheetSet);
- //setSheet() will notify Netbeans to update this node in the UI.
- this.setSheet(visibleSheet);
- }
-
/*
* This is called when the node is first initialized. Any new updates or
* changes happen by directly manipulating the sheet. That means we can fire
@@ -368,18 +323,19 @@ public abstract class AbstractAbstractFileNode extends A
properties.add(new NodeProperty<>(ORIGINAL_NAME.toString(), ORIGINAL_NAME.toString(), NO_DESCR, ""));
}
- //SCO column prereq info..
- List tags = getContentTagsFromDatabase();
- CorrelationAttributeInstance attribute = getCorrelationAttributeInstance();
-
- Pair scoreAndDescription = getScorePropertyAndDescription(tags);
- properties.add(new NodeProperty<>(SCORE.toString(), SCORE.toString(), scoreAndDescription.getRight(), scoreAndDescription.getLeft()));
- DataResultViewerTable.HasCommentStatus comment = getCommentProperty(tags, attribute);
- properties.add(new NodeProperty<>(COMMENT.toString(), COMMENT.toString(), NO_DESCR, comment));
- if (!UserPreferences.hideCentralRepoCommentsAndOccurrences()) {
- Pair countAndDescription = getCountPropertyAndDescription(attribute);
- properties.add(new NodeProperty<>(OCCURRENCES.toString(), OCCURRENCES.toString(), countAndDescription.getRight(), countAndDescription.getLeft()));
+ // Create place holders for S C O
+ if (!UserPreferences.getHideSCOColumns()) {
+ properties.add(new NodeProperty<>(SCORE.toString(), SCORE.toString(), VALUE_LOADING, ""));
+ properties.add(new NodeProperty<>(COMMENT.toString(), COMMENT.toString(), VALUE_LOADING, ""));
+ if (EamDb.isEnabled()) {
+ 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));
+
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)));
@@ -437,19 +393,20 @@ 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"})
- Pair getCountPropertyAndDescription(CorrelationAttributeInstance attribute) {
+ "# {0} - occurrenceCount",
+ "AbstractAbstractFileNode.createSheet.count.description=There were {0} datasource(s) found with occurrences of the MD5 correlation value"})
+ @Override
+ 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) {
@@ -457,7 +414,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);
}
@@ -468,7 +424,8 @@ public abstract class AbstractAbstractFileNode extends A
"AbstractAbstractFileNode.createSheet.taggedFile.description=File has been tagged.",
"AbstractAbstractFileNode.createSheet.notableTaggedFile.description=File tagged with notable tag.",
"AbstractAbstractFileNode.createSheet.noScore.description=No score"})
- Pair getScorePropertyAndDescription(List tags) {
+ @Override
+ protected Pair getScorePropertyAndDescription(List tags) {
DataResultViewerTable.Score score = DataResultViewerTable.Score.NO_SCORE;
String description = Bundle.AbstractAbstractFileNode_createSheet_noScore_description();
if (content.getKnown() == TskData.FileKnown.BAD) {
@@ -486,7 +443,7 @@ public abstract class AbstractAbstractFileNode extends A
if (!tags.isEmpty() && (score == DataResultViewerTable.Score.NO_SCORE || score == DataResultViewerTable.Score.INTERESTING_SCORE)) {
score = DataResultViewerTable.Score.INTERESTING_SCORE;
description = Bundle.AbstractAbstractFileNode_createSheet_taggedFile_description();
- for (ContentTag tag : tags) {
+ for (Tag tag : tags) {
if (tag.getName().getKnownStatus() == TskData.FileKnown.BAD) {
score = DataResultViewerTable.Score.NOTABLE_SCORE;
description = Bundle.AbstractAbstractFileNode_createSheet_notableTaggedFile_description();
@@ -499,11 +456,12 @@ public abstract class AbstractAbstractFileNode extends A
@NbBundle.Messages({
"AbstractAbstractFileNode.createSheet.comment.displayName=C"})
- HasCommentStatus getCommentProperty(List tags, CorrelationAttributeInstance attribute) {
+ @Override
+ protected HasCommentStatus getCommentProperty(List tags, CorrelationAttributeInstance attribute) {
DataResultViewerTable.HasCommentStatus status = !tags.isEmpty() ? DataResultViewerTable.HasCommentStatus.TAG_NO_COMMENT : DataResultViewerTable.HasCommentStatus.NO_COMMENT;
- for (ContentTag tag : tags) {
+ for (Tag tag : tags) {
if (!StringUtils.isBlank(tag.getComment())) {
//if the tag is null or empty or contains just white space it will indicate there is not a comment
status = DataResultViewerTable.HasCommentStatus.TAG_COMMENT;
@@ -571,9 +529,15 @@ public abstract class AbstractAbstractFileNode extends A
return tags;
}
- CorrelationAttributeInstance getCorrelationAttributeInstance() {
+ @Override
+ protected List getAllTagsFromDatabase() {
+ return new ArrayList<>(getContentTagsFromDatabase());
+ }
+
+ @Override
+ protected CorrelationAttributeInstance getCorrelationAttributeInstance() {
CorrelationAttributeInstance attribute = null;
- if (EamDb.isEnabled() && !UserPreferences.hideCentralRepoCommentsAndOccurrences()) {
+ if (EamDb.isEnabled() && !UserPreferences.getHideSCOColumns()) {
attribute = EamArtifactUtil.getInstanceFromContent(content);
}
return attribute;
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java
index 4f6f2e5f47..8b1d22d349 100644
--- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java
@@ -18,20 +18,30 @@
*/
package org.sleuthkit.autopsy.datamodel;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
import java.util.logging.Level;
+import org.apache.commons.lang3.tuple.Pair;
import org.openide.nodes.Children;
+import org.openide.nodes.Sheet;
import org.openide.util.lookup.Lookups;
import org.openide.util.Lookup;
+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;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.SleuthkitCase;
+import org.sleuthkit.datamodel.Tag;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.datamodel.TskException;
@@ -51,27 +61,60 @@ public abstract class AbstractContentNode extends ContentNode
private static final Logger logger = Logger.getLogger(AbstractContentNode.class.getName());
/**
- * Handles aspects that depend on the Content object
- *
- * @param content Underlying Content instances
+ * A pool of background tasks to run any long computation needed to populate
+ * this node.
*/
- AbstractContentNode(T content) {
- this(content, Lookups.singleton(content) );
+ 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"})
+ 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:
+ *
+ * 1) Retrieving the translation of the file name
+ */
+ enum NodeSpecificEvents {
+ 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
- * @param lookup The Lookup object for the node.
+ */
+ AbstractContentNode(T 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.
*/
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
*
@@ -100,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) {
@@ -142,7 +185,7 @@ public abstract class AbstractContentNode extends ContentNode
}
return false;
}
-
+
/**
* Return true if the underlying content object has children Useful for lazy
* loading.
@@ -162,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.
@@ -240,4 +283,83 @@ 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
+ * current sheet will it be applied. That way, we allow for subclasses to
+ * add their own (or omit some!) properties and we will not accidentally
+ * disrupt their UI.
+ *
+ * Race condition if not synchronized. Only one update should be applied at
+ * a time.
+ *
+ * @param newProps New file property instances to be updated in the current
+ * sheet.
+ */
+ protected synchronized void updateSheet(NodeProperty>... newProps) {
+ //Refresh ONLY those properties in the sheet currently. Subclasses may have
+ //only added a subset of our properties or their own props.s
+ Sheet visibleSheet = this.getSheet();
+ Sheet.Set visibleSheetSet = visibleSheet.get(Sheet.PROPERTIES);
+ Property>[] visibleProps = visibleSheetSet.getProperties();
+ for (NodeProperty> newProp : newProps) {
+ for (int i = 0; i < visibleProps.length; i++) {
+ if (visibleProps[i].getName().equals(newProp.getName())) {
+ visibleProps[i] = newProp;
+ }
+ }
+ }
+ visibleSheetSet.put(visibleProps);
+ visibleSheet.put(visibleSheetSet);
+ //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.
+ */
+ 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 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 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(Type attributeType, String attributeValue, String defaultDescription);
}
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java b/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java
index 4a1147494a..9727d83e39 100644
--- a/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java
@@ -286,7 +286,12 @@ public abstract class BaseChildFactory extends ChildFactory.D
* If pageSize is set split keys into pages, otherwise create a
* single page containing all keys.
*/
- pages = Lists.partition(keys, pageSize > 0 ? pageSize : keys.size());
+ if (keys.isEmpty()) {
+ pages.clear();
+ } else {
+ pages = Lists.partition(keys, pageSize > 0 ? pageSize : keys.size());
+ }
+
if (pages.size() != oldPageCount) {
try {
// Number of pages has changed so we need to send out a notification.
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java
index 55e81acd17..0ea8b04660 100644
--- a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java
@@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
- * Copyright 2011-2018 Basis Technology Corp.
+ * Copyright 2011-2019 Basis Technology Corp.
* Contact: carrier sleuthkit org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,6 +22,7 @@ import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
+import java.lang.ref.WeakReference;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
@@ -37,6 +38,7 @@ import java.util.logging.Level;
import java.util.stream.Collectors;
import javax.swing.Action;
import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.Pair;
import org.openide.nodes.Sheet;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
@@ -50,16 +52,19 @@ 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;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
import org.sleuthkit.autopsy.core.UserPreferences;
+import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable.Score;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import static org.sleuthkit.autopsy.datamodel.DisplayableItemNode.findLinked;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable.HasCommentStatus;
+import static org.sleuthkit.autopsy.datamodel.AbstractContentNode.backgroundTasksPool;
import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager;
import org.sleuthkit.autopsy.timeline.actions.ViewArtifactInTimelineAction;
import org.sleuthkit.autopsy.timeline.actions.ViewFileInTimelineAction;
@@ -96,9 +101,6 @@ public class BlackboardArtifactNode extends AbstractContentNode> customProperties;
- private final static String NO_DESCR = NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.noDesc.text");
-
-
/*
* Artifact types which should have the full unique path of the associated
* content as a property.
@@ -150,6 +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) {
+ updateSheet(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_count_name(), Bundle.BlackboardArtifactNode_createSheet_count_displayName(), scoData.getCountAndDescription().getRight(), scoData.getCountAndDescription().getLeft()));
+ }
}
}
};
@@ -319,7 +332,7 @@ public class BlackboardArtifactNode extends AbstractContentNode tags = getAllTagsFromDatabase();
-
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
if (sheetSet == null) {
sheetSet = Sheet.createPropertiesSet();
@@ -351,17 +362,19 @@ 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, ""));
+ if (EamDb.isEnabled()) {
+ sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_count_name(), Bundle.BlackboardArtifactNode_createSheet_count_displayName(), VALUE_LOADING, ""));
+ }
}
- addCommentProperty(sheetSet, tags, correlationAttribute);
- if (UserPreferences.hideCentralRepoCommentsAndOccurrences() == false) {
- addCountProperty(sheetSet, correlationAttribute);
- }
+ // Get the SCO columns data in a background task
+ backgroundTasksPool.submit(new GetSCOTask(
+ 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));
@@ -520,6 +533,7 @@ public class BlackboardArtifactNode extends AbstractContentNode getAllTagsFromDatabase() {
List tags = new ArrayList<>();
try {
@@ -569,6 +583,13 @@ 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;
if (EamDb.isEnabled()) {
@@ -581,16 +602,37 @@ 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,
+ status));
+ }
+
+ /**
+ * Gets the comment property for the node
+ *
+ * @param tags the list of tags associated with the file
+ * @param attribute the correlation attribute associated with this
+ * artifact's associated file, null if central repo is not
+ * enabled
+ *
+ * @return comment property
+ */
+ @Override
+ protected DataResultViewerTable.HasCommentStatus getCommentProperty(List tags, CorrelationAttributeInstance attribute) {
+
HasCommentStatus status = tags.size() > 0 ? HasCommentStatus.TAG_NO_COMMENT : HasCommentStatus.NO_COMMENT;
for (Tag tag : tags) {
if (!StringUtils.isBlank(tag.getComment())) {
@@ -609,17 +651,18 @@ public class BlackboardArtifactNode extends AbstractContentNode(Bundle.BlackboardArtifactNode_createSheet_comment_name(), Bundle.BlackboardArtifactNode_createSheet_comment_displayName(), NO_DESCR,
- status));
+ return status;
}
/**
* Used by (subclasses of) BlackboardArtifactNode to add the Score property
* to their sheets.
*
- * @param sheetSet the modifiable Sheet.Set returned by
- * Sheet.get(Sheet.PROPERTIES)
+ * @param sheetSet the modifiable Sheet.Set to add the property to
* @param tags the list of tags associated with the file
+ *
+ * @deprecated Use the GetSCOTask to get this data on a background
+ * thread..., and then update the property sheet asynchronously
*/
@NbBundle.Messages({"BlackboardArtifactNode.createSheet.score.name=S",
"BlackboardArtifactNode.createSheet.score.displayName=S",
@@ -628,7 +671,21 @@ public class BlackboardArtifactNode extends AbstractContentNode tags) {
+ @Deprecated
+ protected final void addScorePropertyAndDescription(Sheet.Set sheetSet, List tags) {
+ Pair 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
+ */
+ @Override
+ protected Pair getScorePropertyAndDescription(List tags) {
Score score = Score.NO_SCORE;
String description = Bundle.BlackboardArtifactNode_createSheet_noScore_description();
if (associated instanceof AbstractFile) {
@@ -673,34 +730,63 @@ public class BlackboardArtifactNode extends AbstractContentNode(Bundle.BlackboardArtifactNode_createSheet_score_name(), Bundle.BlackboardArtifactNode_createSheet_score_displayName(), description, score));
+
+ return Pair.of(score, description);
}
+ /**
+ * Used by (subclasses of) BlackboardArtifactNode to add the Occurrences
+ * property to their sheets.
+ *
+ * @param sheetSet the modifiable Sheet.Set to add the property to
+ * @param attribute correlation attribute instance
+ *
+ * @deprecated Use the GetSCOTask to get this data on a background
+ * thread..., and then update the property sheet asynchronously
+ */
@NbBundle.Messages({"BlackboardArtifactNode.createSheet.count.name=O",
"BlackboardArtifactNode.createSheet.count.displayName=O",
- "BlackboardArtifactNode.createSheet.count.noCentralRepo.description=Central repository was not enabled when this column was populated",
- "BlackboardArtifactNode.createSheet.count.hashLookupNotRun.description=Hash lookup had not been run on this artifact's associated file when the column was populated",
- "# {0} - occuranceCount",
- "BlackboardArtifactNode.createSheet.count.description=There were {0} datasource(s) found with occurances of the correlation value"})
-
+ "BlackboardArtifactNode.createSheet.count.noCorrelationAttributes.description=No correlation properties found",
+ "BlackboardArtifactNode.createSheet.count.noCorrelationValues.description=Unable to find other occurrences because no value exists for the available correlation property",
+ "# {0} - occurrenceCount",
+ "# {1} - attributeType",
+ "BlackboardArtifactNode.createSheet.count.description=There were {0} datasource(s) found with occurrences of the correlation value of type {1}"})
+ @Deprecated
protected final void addCountProperty(Sheet.Set sheetSet, CorrelationAttributeInstance attribute) {
- Long count = -1L; //The column renderer will not display negative values, negative value used when count unavailble to preserve sorting
- String description = Bundle.BlackboardArtifactNode_createSheet_count_noCentralRepo_description();
+ 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 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(Type attributeType, String attributeValue, String defaultDescription) {
+ Long count = -1L;
+ 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);
} catch (CorrelationAttributeNormalizationException ex) {
logger.log(Level.WARNING, "Unable to normalize data to get count of datasources with correlation attribute", ex);
}
- sheetSet.put(
- new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_count_name(), Bundle.BlackboardArtifactNode_createSheet_count_displayName(), description, count));
+ return Pair.of(count, description);
}
private void updateSheet() {
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED
index 1d8fb7eb14..57cdd1bf48 100755
--- a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED
@@ -1,16 +1,14 @@
AbstractAbstractFileNode.accessTimeColLbl=Access Time
-AbstractAbstractFileNode.addFileProperty.desc=no description
AbstractAbstractFileNode.attrAddrColLbl=Attr. Addr.
AbstractAbstractFileNode.changeTimeColLbl=Change Time
AbstractAbstractFileNode.createdTimeColLbl=Created Time
AbstractAbstractFileNode.createSheet.comment.displayName=C
AbstractAbstractFileNode.createSheet.comment.name=C
-# {0} - occuranceCount
-AbstractAbstractFileNode.createSheet.count.description=There were {0} datasource(s) found with occurances of the correlation value
+# {0} - occurrenceCount
+AbstractAbstractFileNode.createSheet.count.description=There were {0} datasource(s) found with occurrences of the MD5 correlation value
AbstractAbstractFileNode.createSheet.count.displayName=O
AbstractAbstractFileNode.createSheet.count.hashLookupNotRun.description=Hash lookup had not been run on this file when the column was populated
AbstractAbstractFileNode.createSheet.count.name=O
-AbstractAbstractFileNode.createSheet.count.noCentralRepo.description=Central repository was not enabled when this column was populated
AbstractAbstractFileNode.createSheet.interestingResult.description=File has interesting result associated with it.
AbstractAbstractFileNode.createSheet.noScore.description=No score
AbstractAbstractFileNode.createSheet.notableFile.description=File recognized as notable.
@@ -37,6 +35,8 @@ AbstractAbstractFileNode.tagsProperty.displayName=Tags
AbstractAbstractFileNode.typeDirColLbl=Type(Dir)
AbstractAbstractFileNode.typeMetaColLbl=Type(Meta)
AbstractAbstractFileNode.useridColLbl=UserID
+AbstractContentNode.nodescription=no description
+AbstractContentNode.valueLoading=value loading
AbstractFsContentNode.noDesc.text=no description
ArtifactStringContent.attrsTableHeader.sources=Source(s)
ArtifactStringContent.attrsTableHeader.type=Type
@@ -53,12 +53,13 @@ BlackboardArtifactNode.createSheet.artifactType.displayName=Result Type
BlackboardArtifactNode.createSheet.artifactType.name=Result Type
BlackboardArtifactNode.createSheet.comment.displayName=C
BlackboardArtifactNode.createSheet.comment.name=C
-# {0} - occuranceCount
-BlackboardArtifactNode.createSheet.count.description=There were {0} datasource(s) found with occurances of the correlation value
+# {0} - occurrenceCount
+# {1} - attributeType
+BlackboardArtifactNode.createSheet.count.description=There were {0} datasource(s) found with occurrences of the correlation value of type {1}
BlackboardArtifactNode.createSheet.count.displayName=O
-BlackboardArtifactNode.createSheet.count.hashLookupNotRun.description=Hash lookup had not been run on this artifact's associated file when the column was populated
BlackboardArtifactNode.createSheet.count.name=O
-BlackboardArtifactNode.createSheet.count.noCentralRepo.description=Central repository was not enabled when this column was populated
+BlackboardArtifactNode.createSheet.count.noCorrelationAttributes.description=No correlation properties found
+BlackboardArtifactNode.createSheet.count.noCorrelationValues.description=Unable to find other occurrences because no value exists for the available correlation property
BlackboardArtifactNode.createSheet.fileSize.displayName=Size
BlackboardArtifactNode.createSheet.fileSize.name=Size
BlackboardArtifactNode.createSheet.interestingResult.description=Result has an interesting result associated with it.
@@ -114,6 +115,8 @@ FileTypesByMimeTypeNode.createSheet.mediaSubtype.name=Subtype
FileTypesByMimeTypeNode.createSheet.mediaType.desc=no description
FileTypesByMimeTypeNode.createSheet.mediaType.displayName=Type
FileTypesByMimeTypeNode.createSheet.mediaType.name=Type
+GetSCOTask.occurrences.defaultDescription=No correlation properties found
+GetSCOTask.occurrences.multipleProperties=Multiple different correlation properties exist for this result
ImageNode.action.runIngestMods.text=Run Ingest Modules
ImageNode.createSheet.deviceId.desc=Device ID of the image
ImageNode.createSheet.deviceId.displayName=Device ID
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DataModelActionsFactory.java b/Core/src/org/sleuthkit/autopsy/datamodel/DataModelActionsFactory.java
index 13a0753ca1..bdb4a394a8 100644
--- a/Core/src/org/sleuthkit/autopsy/datamodel/DataModelActionsFactory.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/DataModelActionsFactory.java
@@ -36,6 +36,7 @@ import org.sleuthkit.autopsy.actions.ReplaceBlackboardArtifactTagAction;
import org.sleuthkit.autopsy.actions.ReplaceContentTagAction;
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
import org.sleuthkit.autopsy.datamodel.Reports.ReportNode;
+import org.sleuthkit.autopsy.directorytree.ExportCSVAction;
import org.sleuthkit.autopsy.directorytree.ExternalViewerAction;
import org.sleuthkit.autopsy.directorytree.ExternalViewerShortcutAction;
import org.sleuthkit.autopsy.directorytree.ExtractAction;
@@ -92,6 +93,7 @@ public class DataModelActionsFactory {
}
actionsList.add(null); // creates a menu separator
actionsList.add(ExtractAction.getInstance());
+ actionsList.add(ExportCSVAction.getInstance());
actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance());
if (isArtifactSource) {
@@ -119,6 +121,7 @@ public class DataModelActionsFactory {
actionsList.add(new NewWindowViewAction(VIEW_IN_NEW_WINDOW, slackFileNode));
actionsList.add(null); // creates a menu separator
actionsList.add(ExtractAction.getInstance());
+ actionsList.add(ExportCSVAction.getInstance());
actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance());
if (isArtifactSource) {
@@ -155,6 +158,7 @@ public class DataModelActionsFactory {
}
actionsList.add(null); // creates a menu separator
actionsList.add(ExtractAction.getInstance());//
+ actionsList.add(ExportCSVAction.getInstance());
actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance());
if (isArtifactSource) {
@@ -189,6 +193,7 @@ public class DataModelActionsFactory {
}
actionsList.add(null); // creates a menu separator
actionsList.add(ExtractAction.getInstance());
+ actionsList.add(ExportCSVAction.getInstance());
actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance());
if (isArtifactSource) {
@@ -223,6 +228,7 @@ public class DataModelActionsFactory {
}
actionsList.add(null); // creates a menu separator
actionsList.add(ExtractAction.getInstance());
+ actionsList.add(ExportCSVAction.getInstance());
actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance());
if (isArtifactSource) {
@@ -257,6 +263,7 @@ public class DataModelActionsFactory {
}
actionsList.add(null); // creates a menu separator
actionsList.add(ExtractAction.getInstance());
+ actionsList.add(ExportCSVAction.getInstance());
actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance());
if (isArtifactSource) {
@@ -291,6 +298,7 @@ public class DataModelActionsFactory {
}
actionsList.add(null); // creates a menu separator
actionsList.add(ExtractAction.getInstance());
+ actionsList.add(ExportCSVAction.getInstance());
actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance());
if (isArtifactSource) {
@@ -325,6 +333,7 @@ public class DataModelActionsFactory {
}
actionsList.add(null); // creates a menu separator
actionsList.add(ExtractAction.getInstance());
+ actionsList.add(ExportCSVAction.getInstance());
actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance());
if (isArtifactSource) {
@@ -379,6 +388,7 @@ public class DataModelActionsFactory {
}
actionsList.add(null); // creates a menu separator
actionsList.add(ExtractAction.getInstance());
+ actionsList.add(ExportCSVAction.getInstance());
actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance());
if (isArtifactSource) {
@@ -415,6 +425,7 @@ public class DataModelActionsFactory {
}
actionsList.add(null); // creates a menu separator
actionsList.add(ExtractAction.getInstance());
+ actionsList.add(ExportCSVAction.getInstance());
actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance());
if (isArtifactSource) {
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DataSourcesNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/DataSourcesNode.java
index 1265db5658..5ab29a5376 100644
--- a/Core/src/org/sleuthkit/autopsy/datamodel/DataSourcesNode.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/DataSourcesNode.java
@@ -57,7 +57,7 @@ public class DataSourcesNode extends DisplayableItemNode {
}
public DataSourcesNode(long dsObjId) {
- super(Children.create(new DataSourcesNodeChildren(dsObjId), true), Lookups.singleton(NAME));
+ super(Children.create(new DataSourcesNodeChildren(dsObjId), false), Lookups.singleton(NAME));
displayName = (dsObjId > 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/DirectoryNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/DirectoryNode.java
index 97b0fd8216..5a3476f896 100644
--- a/Core/src/org/sleuthkit/autopsy/datamodel/DirectoryNode.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/DirectoryNode.java
@@ -28,6 +28,7 @@ import org.openide.util.Utilities;
import org.sleuthkit.autopsy.actions.AddContentTagAction;
import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction;
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
+import org.sleuthkit.autopsy.directorytree.ExportCSVAction;
import org.sleuthkit.autopsy.directorytree.ExtractAction;
import org.sleuthkit.autopsy.directorytree.NewWindowViewAction;
import org.sleuthkit.autopsy.directorytree.ViewContextAction;
@@ -89,6 +90,7 @@ public class DirectoryNode extends AbstractFsContentNode {
actionsList.add(ViewFileInTimelineAction.createViewFileAction(content));
actionsList.add(null); // creates a menu separator
actionsList.add(ExtractAction.getInstance());
+ actionsList.add(ExportCSVAction.getInstance());
actionsList.add(null); // creates a menu separator
actionsList.add(new RunIngestModulesAction(content));
actionsList.add(null); // creates a menu separator
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/FileNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileNode.java
index 1695b253b0..7b3a55c20b 100644
--- a/Core/src/org/sleuthkit/autopsy/datamodel/FileNode.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileNode.java
@@ -32,6 +32,7 @@ import org.sleuthkit.autopsy.actions.AddContentTagAction;
import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction;
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
import org.sleuthkit.autopsy.coreutils.Logger;
+import org.sleuthkit.autopsy.directorytree.ExportCSVAction;
import org.sleuthkit.autopsy.directorytree.ExternalViewerAction;
import org.sleuthkit.autopsy.directorytree.ExternalViewerShortcutAction;
import org.sleuthkit.autopsy.directorytree.ExtractAction;
@@ -173,6 +174,7 @@ public class FileNode extends AbstractFsContentNode {
actionsList.add(null); // Creates an item separator
actionsList.add(ExtractAction.getInstance());
+ actionsList.add(ExportCSVAction.getInstance());
actionsList.add(null); // Creates an item separator
actionsList.add(AddContentTagAction.getInstance());
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 152e8a6bac..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()
@@ -390,6 +388,7 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
* @param skCase
* @param o Observable that will notify when there could be new
* data to display
+ * @param nodeName
*/
private FileExtensionNodeChildren(FileTypesByExtension.SearchFilterInterface filter, SleuthkitCase skCase, Observable o, String nodeName) {
super(nodeName, new ViewsKnownAndSlackFilter<>());
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
new file mode 100644
index 0000000000..c96dd582d5
--- /dev/null
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/GetSCOTask.java
@@ -0,0 +1,130 @@
+/*
+ * Autopsy Forensic Browser
+ *
+ * Copyright 2019 Basis Technology Corp.
+ * Contact: carrier sleuthkit org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.sleuthkit.autopsy.datamodel;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.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.EamDb;
+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.
+ *
+ */
+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;
+ }
+
+ // get the SCO column values
+ List tags = contentNode.getAllTagsFromDatabase();
+ CorrelationAttributeInstance fileAttribute = contentNode.getCorrelationAttributeInstance();
+
+ SCOData scoData = new SCOData();
+ scoData.setScoreAndDescription(contentNode.getScorePropertyAndDescription(tags));
+ scoData.setComment(contentNode.getCommentProperty(tags, fileAttribute));
+
+ if (EamDb.isEnabled() && !UserPreferences.getHideSCOColumns()) {
+ 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) {
+ listener.propertyChange(new PropertyChangeEvent(
+ AutopsyEvent.SourceType.LOCAL.toString(),
+ AbstractAbstractFileNode.NodeSpecificEvents.SCO_AVAILABLE.toString(),
+ null, scoData));
+ }
+ }
+}
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 584a405fcc..da82c315b5 100644
--- a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java
@@ -28,12 +28,15 @@ import java.util.EnumSet;
import java.util.List;
import java.util.logging.Level;
import javax.swing.Action;
+import org.apache.commons.lang3.tuple.Pair;
import org.openide.nodes.Sheet;
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.datasourcesummary.ViewSummaryInformationAction;
+import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
+import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.directorytree.ExplorerNodeActionVisitor;
import org.sleuthkit.autopsy.directorytree.FileSearchAction;
@@ -47,6 +50,7 @@ import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.VirtualDirectory;
import org.sleuthkit.autopsy.datamodel.BaseChildFactory.NoSuchEventBusException;
+import org.sleuthkit.datamodel.Tag;
/**
* This class is used to represent the "Node" for the image. The children of
@@ -126,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",
@@ -250,4 +254,75 @@ public class ImageNode extends AbstractContentNode {
}
};
+ /**
+ * Reads and returns a list of all tags associated with this content node.
+ *
+ * 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.
+ */
+ @Override
+ protected CorrelationAttributeInstance getCorrelationAttributeInstance() {
+ return null;
+ }
+
+ /**
+ * Returns Score property for the node.
+ *
+ * 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
+ * @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 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.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/LayoutFileNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/LayoutFileNode.java
index bfd8f7bd91..8f7f753db2 100644
--- a/Core/src/org/sleuthkit/autopsy/datamodel/LayoutFileNode.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/LayoutFileNode.java
@@ -23,24 +23,34 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
+import java.util.logging.Level;
import javax.swing.Action;
import org.openide.util.NbBundle;
import org.openide.util.Utilities;
import org.sleuthkit.autopsy.actions.AddContentTagAction;
import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction;
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
+import org.sleuthkit.autopsy.coreutils.Logger;
+import org.sleuthkit.autopsy.directorytree.ExportCSVAction;
import org.sleuthkit.autopsy.directorytree.ExternalViewerAction;
import org.sleuthkit.autopsy.directorytree.ExternalViewerShortcutAction;
import org.sleuthkit.autopsy.directorytree.ExtractAction;
import org.sleuthkit.autopsy.directorytree.NewWindowViewAction;
+import org.sleuthkit.autopsy.directorytree.ViewContextAction;
+import org.sleuthkit.autopsy.modules.embeddedfileextractor.ExtractArchiveWithPasswordAction;
+import org.sleuthkit.autopsy.timeline.actions.ViewFileInTimelineAction;
import org.sleuthkit.datamodel.AbstractFile;
+import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.LayoutFile;
+import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
/**
* Node for layout file
*/
public class LayoutFileNode extends AbstractAbstractFileNode {
+
+ private static final Logger logger = Logger.getLogger(LayoutFileNode.class.getName());
@Deprecated
public static enum LayoutContentPropertyType {
@@ -90,9 +100,14 @@ public class LayoutFileNode extends AbstractAbstractFileNode {
}
@Override
+ @NbBundle.Messages({
+ "LayoutFileNode.getActions.viewFileInDir.text=View File in Directory"})
public Action[] getActions(boolean context) {
List actionsList = new ArrayList<>();
actionsList.addAll(Arrays.asList(super.getActions(true)));
+ actionsList.add(new ViewContextAction(Bundle.LayoutFileNode_getActions_viewFileInDir_text(), this));
+ actionsList.add(null); // Creates an item separator
+
actionsList.add(new NewWindowViewAction(
NbBundle.getMessage(this.getClass(), "LayoutFileNode.getActions.viewInNewWin.text"), this));
final Collection selectedFilesList
@@ -103,8 +118,10 @@ public class LayoutFileNode extends AbstractAbstractFileNode {
} else {
actionsList.add(ExternalViewerShortcutAction.getInstance());
}
+ actionsList.add(ViewFileInTimelineAction.createViewFileAction(getContent()));
actionsList.add(null); // creates a menu separator
actionsList.add(ExtractAction.getInstance());
+ actionsList.add(ExportCSVAction.getInstance());
actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance());
@@ -113,6 +130,15 @@ public class LayoutFileNode extends AbstractAbstractFileNode {
}
actionsList.addAll(ContextMenuExtensionPoint.getActions());
+ if (FileTypeExtensions.getArchiveExtensions().contains("." + this.content.getNameExtension().toLowerCase())) {
+ try {
+ if (this.content.getArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED).size() > 0) {
+ actionsList.add(new ExtractArchiveWithPasswordAction(this.getContent()));
+ }
+ } catch (TskCoreException ex) {
+ logger.log(Level.WARNING, "Unable to add unzip with password action to context menus", ex);
+ }
+ }
return actionsList.toArray(new Action[actionsList.size()]);
}
diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/LocalFileNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/LocalFileNode.java
index 6d64aeb621..4a62cd9e4f 100644
--- a/Core/src/org/sleuthkit/autopsy/datamodel/LocalFileNode.java
+++ b/Core/src/org/sleuthkit/autopsy/datamodel/LocalFileNode.java
@@ -31,6 +31,7 @@ import org.sleuthkit.autopsy.actions.AddContentTagAction;
import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction;
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
import org.sleuthkit.autopsy.coreutils.Logger;
+import org.sleuthkit.autopsy.directorytree.ExportCSVAction;
import org.sleuthkit.autopsy.directorytree.ExternalViewerAction;
import org.sleuthkit.autopsy.directorytree.ExternalViewerShortcutAction;
import org.sleuthkit.autopsy.directorytree.ExtractAction;
@@ -82,6 +83,7 @@ public class LocalFileNode extends AbstractAbstractFileNode {
actionsList.add(null); // creates a menu separator
actionsList.add(ExtractAction.getInstance());
+ actionsList.add(ExportCSVAction.getInstance());
actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance());
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