Merge branch 'artifacts_are_content' of https://github.com/sleuthkit/autopsy into 2689_artifacts_are_content

# Conflicts:
#	Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java
This commit is contained in:
Raman
2017-07-31 12:35:26 -04:00
23 changed files with 576 additions and 338 deletions
@@ -220,6 +220,7 @@ public class Metadata extends javax.swing.JPanel implements DataContentViewer {
@Override
public void resetComponent() {
setText("");
return;
}
@@ -493,18 +493,19 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
private final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
private String[][] rowData = null;
private final String artifactDisplayName;
private final Content content;
ResultsTableArtifact(BlackboardArtifact artifact) {
ResultsTableArtifact(BlackboardArtifact artifact, Content content) {
artifactDisplayName = artifact.getDisplayName();
this.content = content;
addRows(artifact);
}
ResultsTableArtifact(String errorMsg) {
artifactDisplayName = errorMsg;
rowData = new String[1][3];
rowData[0] = new String[]{"", errorMsg, ""};
content = null;
}
private String[][] getRows() {
@@ -514,7 +515,6 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
private void addRows(BlackboardArtifact artifact) {
List<String[]> rowsToAdd = new ArrayList<>();
try {
Content content = artifact.getSleuthkitCase().getContentById(artifact.getObjectID());
/*
* Add rows for each attribute.
*/
@@ -701,12 +701,15 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
if (contents.isEmpty()) {
return new ViewUpdate(getArtifactContents().size(), currentPage, ERROR_TEXT);
}
Content underlyingContent = null;
for (Content content : contents) {
if ( (content != null) && (!(content instanceof BlackboardArtifact)) ) {
// Get all of the blackboard artifacts associated with the content. These are what this
// viewer displays.
try {
artifacts = content.getAllArtifacts();
underlyingContent = content;
break;
} catch (TskException ex) {
logger.log(Level.SEVERE, "Couldn't get artifacts", ex); //NON-NLS
return new ViewUpdate(getArtifactContents().size(), currentPage, ERROR_TEXT);
@@ -721,7 +724,7 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
// Build the new artifact contents cache.
ArrayList<ResultsTableArtifact> artifactContents = new ArrayList<>();
for (BlackboardArtifact artifact : artifacts) {
artifactContents.add(new ResultsTableArtifact(artifact));
artifactContents.add(new ResultsTableArtifact(artifact, underlyingContent));
}
// If the node has an underlying blackboard artifact, show it. If not,
@@ -28,6 +28,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
@@ -104,11 +105,11 @@ class ThumbnailViewChildren extends Children.Keys<Integer> {
* children nodes (which might not be preloaded at this point).
*/
// get list of supported children sorted by persisted criteria
final List<Node> suppContent =
Stream.of(parent.getChildren().getNodes())
.filter(ThumbnailViewChildren::isSupported)
.sorted(getComparator())
.collect(Collectors.toList());
final List<Node> suppContent
= Stream.of(parent.getChildren().getNodes())
.filter(ThumbnailViewChildren::isSupported)
.sorted(getComparator())
.collect(Collectors.toList());
if (suppContent.isEmpty()) {
//if there are no images, there is nothing more to do
@@ -314,7 +315,12 @@ class ThumbnailViewChildren extends Children.Keys<Integer> {
private boolean cancelled = false;
ThumbnailLoadTask() {
super(() -> ImageUtils.getThumbnail(content, thumbSize));
super(new Callable<Image>() { //Does not work as lambda expression in dependent projects in IDE
public Image call() {
return ImageUtils.getThumbnail(content, thumbSize);
}
});
//super(() -> ImageUtils.getThumbnail(content, thumbSize));
progressText = Bundle.ThumbnailViewNode_progressHandle_text(content.getName());
progressHandle = ProgressHandleFactory.createSystemHandle(progressText);
@@ -18,6 +18,8 @@
*/
package org.sleuthkit.autopsy.datamodel;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.MessageFormat;
@@ -26,6 +28,8 @@ import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.stream.Collectors;
import javax.swing.Action;
@@ -35,12 +39,12 @@ 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.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagAddedEvent;
import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagDeletedEvent;
import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent;
import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import static org.sleuthkit.autopsy.datamodel.DisplayableItemNode.findLinked;
import org.sleuthkit.autopsy.timeline.actions.ViewArtifactInTimelineAction;
import org.sleuthkit.autopsy.timeline.actions.ViewFileInTimelineAction;
@@ -59,10 +63,15 @@ import org.sleuthkit.datamodel.TskCoreException;
*/
public class BlackboardArtifactNode extends DisplayableItemNode {
private static final Logger LOGGER = Logger.getLogger(BlackboardArtifactNode.class.getName());
private static Cache<Long, Content> contentCache = CacheBuilder.newBuilder()
.expireAfterWrite(1, TimeUnit.MINUTES).
build();
private final BlackboardArtifact artifact;
private Content associated = null;
private List<NodeProperty<? extends Object>> customProperties;
private static final Logger LOGGER = Logger.getLogger(BlackboardArtifactNode.class.getName());
/*
* Artifact types which should have the full unique path of the associated
* content as a property.
@@ -107,6 +116,7 @@ public class BlackboardArtifactNode extends DisplayableItemNode {
if (evt.getNewValue() == null) {
// case was closed. Remove listeners so that we don't get called with a stale case handle
removeListeners();
contentCache.invalidateAll();
}
}
}
@@ -297,7 +307,7 @@ public class BlackboardArtifactNode extends DisplayableItemNode {
// Do nothing since the display name will be set to the file name.
}
}
for (Map.Entry<String, Object> entry : map.entrySet()) {
ss.put(new NodeProperty<>(entry.getKey(),
entry.getKey(),
@@ -491,46 +501,46 @@ public class BlackboardArtifactNode extends DisplayableItemNode {
LOGGER.log(Level.SEVERE, "Getting attributes failed", ex); //NON-NLS
}
}
/**
* Fill map with EmailMsg properties, not all attributes are filled
*
* @param map map with preserved ordering, where property names/values
* are put
* @param attribute attribute to check/fill as property
*/
* Fill map with EmailMsg properties, not all attributes are filled
*
* @param map map with preserved ordering, where property names/values
* are put
* @param attribute attribute to check/fill as property
*/
private void addEmailMsgProperty(Map<String, Object> map, BlackboardAttribute attribute ) {
final int attributeTypeID = attribute.getAttributeType().getTypeID();
// Skip certain Email msg attributes
if (attributeTypeID == ATTRIBUTE_TYPE.TSK_DATETIME_SENT.getTypeID()
|| attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_HTML.getTypeID()
|| attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_RTF.getTypeID()
|| attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_BCC.getTypeID()
|| attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CC.getTypeID()
final int attributeTypeID = attribute.getAttributeType().getTypeID();
// Skip certain Email msg attributes
if (attributeTypeID == ATTRIBUTE_TYPE.TSK_DATETIME_SENT.getTypeID()
|| attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_HTML.getTypeID()
|| attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_RTF.getTypeID()
|| attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_BCC.getTypeID()
|| attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CC.getTypeID()
|| attributeTypeID == ATTRIBUTE_TYPE.TSK_HEADERS.getTypeID()
) {
// do nothing
// do nothing
}
else if (attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_PLAIN.getTypeID()) {
String value = attribute.getDisplayString();
if (value.length() > 160) {
value = value.substring(0, 160) + "...";
}
map.put(attribute.getAttributeType().getDisplayName(), value);
String value = attribute.getDisplayString();
if (value.length() > 160) {
value = value.substring(0, 160) + "...";
}
map.put(attribute.getAttributeType().getDisplayName(), value);
}
else if (attribute.getAttributeType().getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME) {
map.put(attribute.getAttributeType().getDisplayName(), ContentUtils.getStringTime(attribute.getValueLong(), associated));
map.put(attribute.getAttributeType().getDisplayName(), ContentUtils.getStringTime(attribute.getValueLong(), associated));
}
else {
map.put(attribute.getAttributeType().getDisplayName(), attribute.getDisplayString());
map.put(attribute.getAttributeType().getDisplayName(), attribute.getDisplayString());
}
}
}
@Override
public <T> T accept(DisplayableItemNodeVisitor<T> v) {
return v.visit(this);
@@ -539,31 +549,25 @@ public class BlackboardArtifactNode extends DisplayableItemNode {
/**
* Create a Lookup based on what is in the passed in artifact.
*
* @param artifact
* @param artifact The artifact to make a look up for.
*
* @return
* @return A lookup with the artifact and possibly any associated content in
* it.
*/
private static Lookup createLookup(BlackboardArtifact artifact) {
List<Object> forLookup = new ArrayList<>();
forLookup.add(artifact);
// Add the content the artifact is associated with
Content content = getAssociatedContent(artifact);
if (content != null) {
forLookup.add(content);
}
return Lookups.fixed(forLookup.toArray(new Object[forLookup.size()]));
}
private static Content getAssociatedContent(BlackboardArtifact artifact) {
final long objectID = artifact.getObjectID();
try {
return artifact.getSleuthkitCase().getContentById(artifact.getObjectID());
} catch (TskCoreException ex) {
LOGGER.log(Level.WARNING, "Getting file failed", ex); //NON-NLS
Content content = contentCache.get(objectID, () -> artifact.getSleuthkitCase().getContentById(objectID));
if (content == null) {
return Lookups.fixed(artifact);
} else {
return Lookups.fixed(artifact, content);
}
} catch (ExecutionException ex) {
LOGGER.log(Level.WARNING, "Getting associated content for artifact failed", ex); //NON-NLS
return Lookups.fixed(artifact);
}
throw new IllegalArgumentException(
NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.getAssocCont.exception.msg"));
}
@Override
@@ -42,7 +42,6 @@ BlackboardArtifactNode.createSheet.filePath.name=File Path
BlackboardArtifactNode.createSheet.filePath.displayName=File Path
BlackboardArtifactNode.createSheet.dataSrc.name=Data Source
BlackboardArtifactNode.createSheet.dataSrc.displayName=Data Source
BlackboardArtifactNode.getAssocCont.exception.msg=Could not get file from database
BlackboardArtifactTagNode.createSheet.srcFile.text=Source File
BlackboardArtifactTagNode.createSheet.unavail.text=Unavailable
BlackboardArtifactTagNode.createSheet.srcFilePath.text=Source File Path
@@ -87,8 +86,8 @@ DeletedContent.deletedContentsNode.name=Deleted Files
DeletedContent.createSheet.name.name=Name
DeletedContent.createSheet.name.displayName=Name
DeletedContent.createSheet.name.desc=no description
DeletedContent.createSheet.filterType.name=Filter Type
DeletedContent.createSheet.filterType.displayName=Filter Type
DeletedContent.createSheet.filterType.name=Type
DeletedContent.createSheet.filterType.displayName=Type
DeletedContent.createSheet.filterType.desc=no description
DeletedContent.createKeys.maxObjects.msg=There are more Deleted Files than can be displayed. Only the first {0} Deleted Files will be shown.
DeletedContent.createNodeForKey.typeNotSupported.msg=Not supported for this type of Displayable Item\: {0}
@@ -112,13 +111,13 @@ FileSize.fileSizeRootNode.name=File Size
FileSize.createSheet.name.name=Name
FileSize.createSheet.name.displayName=Name
FileSize.createSheet.name.desc=no description
FileSize.createSheet.filterType.name=Filter Type
FileSize.createSheet.filterType.displayName=Filter Type
FileSize.createSheet.filterType.name=Size Range
FileSize.createSheet.filterType.displayName=Size Range
FileSize.createSheet.filterType.desc=no description
FileSize.exception.notSupported.msg=Not supported for this type of Displayable Item\: {0}
FileTypeChildren.exception.notSupported.msg=Not supported for this type of Displayable Item\: {0}
FileTypesByExtNode.createSheet.filterType.name=Filter Type
FileTypesByExtNode.createSheet.filterType.displayName=Filter Type
FileTypesByExtNode.createSheet.filterType.name=File Type
FileTypesByExtNode.createSheet.filterType.displayName=File Type
FileTypesByExtNode.createSheet.filterType.desc=no description
FileTypesByExtNode.createSheet.fileExt.name=File Extensions
FileTypesByExtNode.createSheet.fileExt.displayName=File Extensions
@@ -32,7 +32,6 @@ BlackboardArtifactNode.createSheet.filePath.name=\u30d5\u30a1\u30a4\u30eb\u30d1\
BlackboardArtifactNode.createSheet.filePath.displayName=\u30d5\u30a1\u30a4\u30eb\u30d1\u30b9
BlackboardArtifactNode.createSheet.dataSrc.name=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9
BlackboardArtifactNode.createSheet.dataSrc.displayName=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9
BlackboardArtifactNode.getAssocCont.exception.msg=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304b\u3089\u30d5\u30a1\u30a4\u30eb\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f
BlackboardArtifactTagNode.createSheet.srcFile.text=\u30bd\u30fc\u30b9\u30d5\u30a1\u30a4\u30eb
BlackboardArtifactTagNode.createSheet.unavail.text=\u4f7f\u7528\u3067\u304d\u307e\u305b\u3093
BlackboardArtifactTagNode.createSheet.srcFilePath.text=\u30bd\u30fc\u30b9\u30d5\u30a1\u30a4\u30eb\u30d1\u30b9
@@ -64,8 +63,6 @@ DataSourcesNode.createSheet.name.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b
DeletedContent.fsDelFilter.text=\u30d5\u30a1\u30a4\u30eb\u30b7\u30b9\u30c6\u30e0
DeletedContent.allDelFilter.text=\u3059\u3079\u3066
DeletedContent.deletedContentsNode.name=\u524a\u9664\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb
DeletedContent.createSheet.name.name=\u540d\u524d
DeletedContent.createSheet.name.displayName=\u540d\u524d
DeletedContent.createSheet.name.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093
DeletedContent.createSheet.filterType.name=\u30d5\u30a3\u30eb\u30bf\u30fc\u30bf\u30a4\u30d7
DeletedContent.createSheet.filterType.displayName=\u30d5\u30a3\u30eb\u30bf\u30fc\u30bf\u30a4\u30d7
@@ -92,8 +89,6 @@ FileNode.getActions.viewInNewWin.text=\u65b0\u898f\u30a6\u30a3\u30f3\u30c9\u30a6
FileNode.getActions.openInExtViewer.text=\u5916\u90e8\u30d3\u30e5\u30fc\u30a2\u3067\u958b\u304f
FileNode.getActions.searchFilesSameMD5.text=\u540c\u3058MD5\u30cf\u30c3\u30b7\u30e5\u3092\u6301\u3064\u30d5\u30a1\u30a4\u30eb\u3092\u691c\u7d22
FileSize.fileSizeRootNode.name=\u30d5\u30a1\u30a4\u30eb\u30b5\u30a4\u30ba
FileSize.createSheet.name.name=\u540d\u524d
FileSize.createSheet.name.displayName=\u540d\u524d
FileSize.createSheet.name.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093
FileSize.createSheet.filterType.name=\u30d5\u30a3\u30eb\u30bf\u30fc\u30bf\u30a4\u30d7
FileSize.createSheet.filterType.displayName=\u30d5\u30a3\u30eb\u30bf\u30fc\u30bf\u30a4\u30d7
@@ -111,8 +106,6 @@ FileTypeExtensionFilters.autDocOfficeFilter.text=\u30aa\u30d5\u30a3\u30b9
FileTypeExtensionFilters.autoDocPdfFilter.text=PDF
FileTypeExtensionFilters.autDocTxtFilter.text=\u30d7\u30ec\u30fc\u30f3\u30c6\u30ad\u30b9\u30c8
FileTypeExtensionFilters.autDocRtfFilter.text=\u30ea\u30c3\u30c1\u30c6\u30ad\u30b9\u30c8
FileTypesByExtNode.createSheet.filterType.name=\u30d5\u30a3\u30eb\u30bf\u30fc\u30bf\u30a4\u30d7
FileTypesByExtNode.createSheet.filterType.displayName=\u30d5\u30a3\u30eb\u30bf\u30fc\u30bf\u30a4\u30d7
FileTypesByExtNode.createSheet.filterType.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093
FileTypesByExtNode.createSheet.fileExt.name=\u30d5\u30a1\u30a4\u30eb\u62e1\u5f35\u5b50
FileTypesByExtNode.createSheet.fileExt.displayName=\u30d5\u30a1\u30a4\u30eb\u62e1\u5f35\u5b50
@@ -30,7 +30,6 @@ 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.ImageUtils;
import org.sleuthkit.autopsy.directorytree.ExternalViewerAction;
import org.sleuthkit.autopsy.directorytree.ExtractAction;
import org.sleuthkit.autopsy.directorytree.HashSearchAction;
@@ -62,8 +61,7 @@ public class FileNode extends AbstractFsContentNode<AbstractFile> {
} else {
ext = "." + ext;
}
if (ImageUtils.isImageThumbnailSupported(file)
|| FileTypeExtensions.getImageExtensions().contains(ext)) {
if (FileTypeExtensions.getImageExtensions().contains(ext)) {
return "org/sleuthkit/autopsy/images/image-file.png"; //NON-NLS
}
if (FileTypeExtensions.getVideoExtensions().contains(ext)) {
@@ -45,6 +45,7 @@ import org.sleuthkit.datamodel.File;
import org.sleuthkit.datamodel.FsContent;
import org.sleuthkit.datamodel.LayoutFile;
import org.sleuthkit.datamodel.LocalFile;
import org.sleuthkit.datamodel.SlackFile;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
@@ -483,6 +484,11 @@ public class FileSize implements AutopsyVisitableItem {
return new FileNode(f, false);
}
@Override
public FileNode visit(SlackFile f) {
return new FileNode(f, false);
}
@Override
protected AbstractNode defaultVisit(Content di) {
throw new UnsupportedOperationException(
@@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2016 Basis Technology Corp.
* Copyright 2011-2017 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,9 +18,12 @@
*/
package org.sleuthkit.autopsy.datamodel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import javax.swing.SwingWorker;
@@ -31,6 +34,8 @@ import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.ContentVisitor;
import org.sleuthkit.datamodel.DerivedFile;
@@ -40,6 +45,7 @@ import org.sleuthkit.datamodel.LayoutFile;
import org.sleuthkit.datamodel.LocalFile;
import org.sleuthkit.datamodel.SlackFile;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.SleuthkitItemVisitor;
import org.sleuthkit.datamodel.TskCoreException;
/**
@@ -47,10 +53,24 @@ import org.sleuthkit.datamodel.TskCoreException;
*/
public final class FileTypes implements AutopsyVisitableItem {
private final static Logger logger = Logger.getLogger(FileTypes.class.getName());
private static final Logger logger = Logger.getLogger(FileTypes.class.getName());
@NbBundle.Messages("FileTypes.name.text=File Types")
private static final String NAME = Bundle.FileTypes_name_text();
/**
* Threshold used to limit db queries for child node counts. When the
* tsk_files table has more than this number of rows, we don't query for the
* child node counts, and since we don't have an accurate number we don't
* show the counts.
*/
private static final int NODE_COUNT_FILE_TABLE_THRESHOLD = 1_000_000;
/**
* Used to keep track of whether we have hit
* NODE_COUNT_FILE_TABLE_THRESHOLD. If we have, we stop querying for the
* number of rows in tsk_files, since it is already too large.
*/
private boolean showCounts = true;
private final SleuthkitCase skCase;
private boolean showCounts = true;
FileTypes(SleuthkitCase skCase) {
this.skCase = skCase;
@@ -66,15 +86,16 @@ public final class FileTypes implements AutopsyVisitableItem {
}
/**
* Should the nodes show counts?
*
*
* @return True, unless the DB has more than 200k rows.
* Check the db to determine if the nodes should show child counts.
*/
boolean shouldShowCounts() {
void updateShowCounts() {
/*
* once we have passed the threshold, we don't need to keep checking the
* number of rows in tsk_files
*/
if (showCounts) {
try {
if (skCase.countFilesWhere("1=1") > 200000) { //NON-NLS
if (skCase.countFilesWhere("1=1") > NODE_COUNT_FILE_TABLE_THRESHOLD) { //NON-NLS
showCounts = false;
}
} catch (TskCoreException tskCoreException) {
@@ -82,10 +103,7 @@ public final class FileTypes implements AutopsyVisitableItem {
logger.log(Level.SEVERE, "Error counting files.", tskCoreException); //NON-NLS
}
}
return showCounts;
}
@NbBundle.Messages("FileTypes.name.text=File Types")
static private final String NAME = Bundle.FileTypes_name_text();
/**
* Node which will contain By Mime Type and By Extension nodes.
@@ -97,8 +115,8 @@ public final class FileTypes implements AutopsyVisitableItem {
new FileTypesByExtension(FileTypes.this),
new FileTypesByMimeType(FileTypes.this))),
Lookups.singleton(NAME));
setName(NAME);
setDisplayName(NAME);
this.setName(NAME);
this.setDisplayName(NAME);
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file_types.png"); //NON-NLS
}
@@ -215,9 +233,9 @@ public final class FileTypes implements AutopsyVisitableItem {
* Updates the display name of the mediaSubTypeNode to include the count
* of files which it represents.
*/
@NbBundle.Messages("FileTypes.bgCounting.placeholder=(counting...)")
@NbBundle.Messages("FileTypes.bgCounting.placeholder= (counting...)")
void updateDisplayName() {
if (typesRoot.shouldShowCounts()) {
if (typesRoot.showCounts) {
//only show "(counting...)" the first time, otherwise it is distracting.
setDisplayName(getDisplayNameBase() + ((childCount < 0) ? Bundle.FileTypes_bgCounting_placeholder()
: ("(" + childCount + ")"))); //NON-NLS
@@ -239,8 +257,186 @@ public final class FileTypes implements AutopsyVisitableItem {
}
}.execute();
} else {
setDisplayName(getDisplayNameBase() + ((childCount < 0) ? "" : ("(" + childCount + "+)"))); //NON-NLS
setDisplayName(getDisplayNameBase() + ((childCount < 0) ? "" : (" (" + childCount + "+)"))); //NON-NLS
}
}
}
/**
* Class that is used as a key by NetBeans for creating result nodes. This
* is a wrapper around a Content object and is being put in place as an
* optimization to avoid the Content.hashCode() implementation which issues
* a database query to get the number of children when determining whether 2
* Content objects represent the same thing. TODO: This is a temporary
* solution that can hopefully be removed once we address the issue of
* determining how many children a Content has (JIRA-2823).
*/
static class FileTypesKey implements Content {
private final Content content;
public FileTypesKey(Content content) {
this.content = content;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final FileTypesKey other = (FileTypesKey) obj;
return this.content.getId() == other.content.getId();
}
@Override
public int hashCode() {
int hash = 7;
hash = 101 * hash + (int)(this.content.getId() ^ (this.content.getId() >>> 32));
return hash;
}
@Override
public <T> T accept(SleuthkitItemVisitor<T> v) {
return content.accept(v);
}
@Override
public int read(byte[] buf, long offset, long len) throws TskCoreException {
return content.read(buf, offset, len);
}
@Override
public void close() {
content.close();
}
@Override
public long getSize() {
return content.getSize();
}
@Override
public <T> T accept(ContentVisitor<T> v) {
return content.accept(v);
}
@Override
public String getName() {
return content.getName();
}
@Override
public String getUniquePath() throws TskCoreException {
return content.getUniquePath();
}
@Override
public long getId() {
return content.getId();
}
@Override
public Content getDataSource() throws TskCoreException {
return content.getDataSource();
}
@Override
public List<Content> getChildren() throws TskCoreException {
return content.getChildren();
}
@Override
public boolean hasChildren() throws TskCoreException {
return content.hasChildren();
}
@Override
public int getChildrenCount() throws TskCoreException {
return content.getChildrenCount();
}
@Override
public Content getParent() throws TskCoreException {
return content.getParent();
}
@Override
public List<Long> getChildrenIds() throws TskCoreException {
return content.getChildrenIds();
}
@Override
public BlackboardArtifact newArtifact(int artifactTypeID) throws TskCoreException {
return content.newArtifact(artifactTypeID);
}
@Override
public BlackboardArtifact newArtifact(BlackboardArtifact.ARTIFACT_TYPE type) throws TskCoreException {
return content.newArtifact(type);
}
@Override
public ArrayList<BlackboardArtifact> getArtifacts(String artifactTypeName) throws TskCoreException {
return content.getArtifacts(artifactTypeName);
}
@Override
public BlackboardArtifact getGenInfoArtifact() throws TskCoreException {
return content.getGenInfoArtifact();
}
@Override
public BlackboardArtifact getGenInfoArtifact(boolean create) throws TskCoreException {
return content.getGenInfoArtifact(create);
}
@Override
public ArrayList<BlackboardAttribute> getGenInfoAttributes(BlackboardAttribute.ATTRIBUTE_TYPE attr_type) throws TskCoreException {
return content.getGenInfoAttributes(attr_type);
}
@Override
public ArrayList<BlackboardArtifact> getArtifacts(int artifactTypeID) throws TskCoreException {
return content.getArtifacts(artifactTypeID);
}
@Override
public ArrayList<BlackboardArtifact> getArtifacts(BlackboardArtifact.ARTIFACT_TYPE type) throws TskCoreException {
return content.getArtifacts(type);
}
@Override
public ArrayList<BlackboardArtifact> getAllArtifacts() throws TskCoreException {
return content.getAllArtifacts();
}
@Override
public Set<String> getHashSetNames() throws TskCoreException {
return content.getHashSetNames();
}
@Override
public long getArtifactsCount(String artifactTypeName) throws TskCoreException {
return content.getArtifactsCount(artifactTypeName);
}
@Override
public long getArtifactsCount(int artifactTypeID) throws TskCoreException {
return content.getArtifactsCount(artifactTypeID);
}
@Override
public long getArtifactsCount(BlackboardArtifact.ARTIFACT_TYPE type) throws TskCoreException {
return content.getArtifactsCount(type);
}
@Override
public long getAllArtifactsCount() throws TskCoreException {
return content.getAllArtifactsCount();
}
}
}
@@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-17 Basis Technology Corp.
* Copyright 2011-2017 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,7 +24,10 @@ import java.util.Arrays;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import java.util.function.Function;
import java.util.logging.Level;
import java.util.stream.Collectors;
import org.apache.commons.lang.StringUtils;
import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
@@ -34,7 +37,9 @@ import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.FileTypes.FileTypesKey;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
@@ -87,7 +92,7 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
*/
try {
Case.getCurrentCase();
typesRoot.shouldShowCounts();
typesRoot.updateShowCounts();
update();
} catch (IllegalStateException notUsed) {
/**
@@ -147,8 +152,9 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
* provides updates on events
*/
private FileTypesByExtNode(SleuthkitCase skCase, FileTypesByExtension.RootFilter filter, FileTypesByExtObservable o) {
super(Children.create(new FileTypesByExtNodeChildren(skCase, filter, o), true),
Lookups.singleton(filter == null ? FNAME : filter.getName()));
Lookups.singleton(filter == null ? FNAME : filter.getDisplayName()));
this.filter = filter;
// root node of tree
@@ -157,7 +163,7 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
super.setDisplayName(FNAME);
} // sub-node in file tree (i.e. documents, exec, etc.)
else {
super.setName(filter.getName());
super.setName(filter.getDisplayName());
super.setDisplayName(filter.getDisplayName());
}
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file_types.png"); //NON-NLS
@@ -181,7 +187,16 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
ss = Sheet.createPropertiesSet();
s.put(ss);
}
ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.name.name"), NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.name.displayName"), NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.name.desc"), getName()));
if (filter != null && (filter.equals(FileTypesByExtension.RootFilter.TSK_DOCUMENT_FILTER) || filter.equals(FileTypesByExtension.RootFilter.TSK_EXECUTABLE_FILTER))) {
String extensions = "";
for (String ext : filter.getFilter()) {
extensions += "'" + ext + "', ";
}
extensions = extensions.substring(0, extensions.lastIndexOf(','));
ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.fileExt.name"), NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.fileExt.displayName"), NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.fileExt.desc"), extensions));
} else {
ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.name.name"), NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.name.displayName"), NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.name.desc"), getDisplayName()));
}
return s;
}
@@ -272,7 +287,7 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
super(typesRoot, Children.create(new FileExtensionNodeChildren(filter, skCase, o), true),
Lookups.singleton(filter.getDisplayName()));
this.filter = filter;
setName(filter.getName());
super.setName(filter.getDisplayName());
updateDisplayName();
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file-filter-icon.png"); //NON-NLS
@@ -330,24 +345,43 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
}
}
private static String createQuery(FileTypesByExtension.SearchFilterInterface filter) {
StringBuilder query = new StringBuilder();
query.append("(dir_type = ").append(TskData.TSK_FS_NAME_TYPE_ENUM.REG.getValue()).append(")"); //NON-NLS
if (UserPreferences.hideKnownFilesInViewsTree()) {
query.append(" AND (known IS NULL OR known != ").append(TskData.FileKnown.KNOWN.getFileKnownValue()).append(")"); //NON-NLS
private String createQuery(FileTypesByExtension.SearchFilterInterface filter) {
if (filter.getFilter().isEmpty()) {
// We should never be given a search filter without extensions
// but if we are it is clearly a programming error so we throw
// an IllegalArgumentException.
throw new IllegalArgumentException("Empty filter list passed to createQuery()"); // NON-NLS
}
query.append(" AND (NULL"); //NON-NLS
for (String s : filter.getFilter()) {
query.append(" OR LOWER(name) LIKE LOWER('%").append(s).append("')"); //NON-NLS
String query = "(dir_type = " + TskData.TSK_FS_NAME_TYPE_ENUM.REG.getValue() + ")"
+ (UserPreferences.hideKnownFilesInViewsTree() ? " AND (known IS NULL OR known != "
+ TskData.FileKnown.KNOWN.getFileKnownValue() + ")" : " ")
+ " AND (NULL "; //NON-NLS
if (skCase.getDatabaseType().equals(TskData.DbType.POSTGRESQL)) {
// For PostgreSQL we get a more efficient query by using builtin
// regular expression support and or'ing all extensions. We also
// escape the dot at the beginning of the extension.
// We will end up with a query that looks something like this:
// OR LOWER(name) ~ '(\.zip|\.rar|\.7zip|\.cab|\.jar|\.cpio|\.ar|\.gz|\.tgz|\.bz2)$')
query += "OR LOWER(name) ~ '(\\";
query += StringUtils.join(filter.getFilter().stream()
.map(String::toLowerCase).collect(Collectors.toList()), "|\\");
query += ")$'";
} else {
for (String s : filter.getFilter()) {
query += "OR LOWER(name) LIKE '%" + s.toLowerCase() + "'"; // NON-NLS
}
}
query.append(')');
return query.toString();
query += ')';
return query;
}
/**
* Child node factory for a specific file type - does the database query.
*/
private static class FileExtensionNodeChildren extends ChildFactory.Detachable<Content> implements Observer {
private class FileExtensionNodeChildren extends ChildFactory.Detachable<FileTypesKey> implements Observer {
private final SleuthkitCase skCase;
private final FileTypesByExtension.SearchFilterInterface filter;
@@ -387,9 +421,10 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
}
@Override
protected boolean createKeys(List<Content> list) {
protected boolean createKeys(List<FileTypesKey> list) {
try {
list.addAll(skCase.findAllFilesWhere(createQuery(filter)));
list.addAll(skCase.findAllFilesWhere(createQuery(filter))
.stream().map(f -> new FileTypesKey(f)).collect(Collectors.toList()));
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Couldn't get search results", ex); //NON-NLS
}
@@ -397,7 +432,7 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
}
@Override
protected Node createNodeForKey(Content key) {
protected Node createNodeForKey(FileTypesKey key) {
return key.accept(new FileTypes.FileNodeCreationVisitor());
}
}
@@ -419,10 +454,10 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
FileTypeExtensions.getArchiveExtensions()),
TSK_DOCUMENT_FILTER(3, "TSK_DOCUMENT_FILTER", //NON-NLS
NbBundle.getMessage(FileTypesByExtension.class, "FileTypeExtensionFilters.tskDocumentFilter.text"),
Arrays.asList(".doc", ".docx", ".pdf", ".xls", ".rtf", ".txt")), //NON-NLS
Arrays.asList(".htm", ".html", ".doc", ".docx", ".odt", ".xls", ".xlsx", ".ppt", ".pptx", ".pdf", ".txt", ".rtf")), //NON-NLS
TSK_EXECUTABLE_FILTER(3, "TSK_EXECUTABLE_FILTER", //NON-NLS
NbBundle.getMessage(FileTypesByExtension.class, "FileTypeExtensionFilters.tskExecFilter.text"),
Arrays.asList(".exe", ".dll", ".bat", ".cmd", ".com")); //NON-NLS
FileTypeExtensions.getExecutableExtensions()); //NON-NLS
private final int id;
private final String name;
@@ -30,16 +30,20 @@ import java.util.Map;
import java.util.Observable;
import java.util.Observer;
import java.util.logging.Level;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.nodes.Sheet;
import org.openide.util.NbBundle;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.core.UserPreferences;
import static org.sleuthkit.autopsy.core.UserPreferences.hideKnownFilesInViewsTree;
import static org.sleuthkit.autopsy.core.UserPreferences.hideSlackFilesInViewsTree;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.FileTypes.FileTypesKey;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
@@ -55,6 +59,7 @@ import org.sleuthkit.datamodel.TskData;
public final class FileTypesByMimeType extends Observable implements AutopsyVisitableItem {
private final static Logger logger = Logger.getLogger(FileTypesByMimeType.class.getName());
private final SleuthkitCase skCase;
/**
* The nodes of this tree will be determined dynamically by the mimetypes
@@ -62,36 +67,53 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
* type as the key and a Map, from media subtype to count, as the value.
*/
private final HashMap<String, Map<String, Long>> existingMimeTypeCounts = new HashMap<>();
private static final Logger LOGGER = Logger.getLogger(FileTypesByMimeType.class.getName());
/**
* Root of the File Types tree. Used to provide single answer to question:
* Should the child counts be shown next to the nodes?
*/
private final FileTypes typesRoot;
private void removeListeners() {
deleteObservers();
IngestManager.getInstance().removeIngestJobEventListener(pcl);
Case.removePropertyChangeListener(pcl);
}
/*
/**
* The pcl is in the class because it has the easiest mechanisms to add and
* remove itself during its life cycles.
*/
private final PropertyChangeListener pcl;
/**
* Performs the query on the database to get all distinct MIME types of
* files in it, and populate the hashmap with those results.
* Create the base expression used as the where clause in the queries for
* files by mime type. Filters out certain kinds of files and directories,
* and known/slack files based on user preferences.
*
* @return The base expression to be used in the where clause of queries for
* files by mime type.
*/
private void populateHashMap() {
String query = "SELECT mime_type,count(*) as count from tsk_files "
+ " where mime_type IS NOT null "
+ " AND dir_type = " + TskData.TSK_FS_NAME_TYPE_ENUM.REG.getValue()
static private String createBaseWhereExpr() {
return "(dir_type = " + TskData.TSK_FS_NAME_TYPE_ENUM.REG.getValue() + ")"
+ " AND (type IN ("
+ TskData.TSK_DB_FILES_TYPE_ENUM.FS.ordinal() + ","
+ TskData.TSK_DB_FILES_TYPE_ENUM.CARVED.ordinal() + ","
+ TskData.TSK_DB_FILES_TYPE_ENUM.DERIVED.ordinal() + ","
+ TskData.TSK_DB_FILES_TYPE_ENUM.LOCAL.ordinal()
+ ((UserPreferences.hideSlackFilesInViewsTree()) ? ""
: ("," + TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.ordinal()))
+ "))" + " GROUP BY mime_type";
+ (hideSlackFilesInViewsTree() ? "" : ("," + TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.ordinal()))
+ "))"
+ (hideKnownFilesInViewsTree() ? (" AND (known IS NULL OR known != " + TskData.FileKnown.KNOWN.getFileKnownValue() + ")") : "");
}
private void removeListeners() {
deleteObservers();
IngestManager.getInstance().removeIngestJobEventListener(pcl);
Case.removePropertyChangeListener(pcl);
}
/**
* Performs the query on the database to get all distinct MIME types of
* files in it, and populate the hashmap with those results.
*/
private void populateHashMap() {
String query = "SELECT mime_type, count(*) AS count FROM tsk_files "
+ " WHERE mime_type IS NOT null "
+ " AND " + createBaseWhereExpr()
+ " GROUP BY mime_type";
synchronized (existingMimeTypeCounts) {
existingMimeTypeCounts.clear();
@@ -114,7 +136,7 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
}
}
} catch (TskCoreException | SQLException ex) {
LOGGER.log(Level.SEVERE, "Unable to populate File Types by MIME Type tree view from DB: ", ex); //NON-NLS
logger.log(Level.SEVERE, "Unable to populate File Types by MIME Type tree view from DB: ", ex); //NON-NLS
}
}
@@ -138,7 +160,7 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
*/
try {
Case.getCurrentCase();
typesRoot.shouldShowCounts();
typesRoot.updateShowCounts();
populateHashMap();
} catch (IllegalStateException notUsed) {
/**
@@ -187,11 +209,12 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
*/
class ByMimeTypeNode extends DisplayableItemNode {
@NbBundle.Messages("FileTypesByMimeType.name.text=By MIME Type")
@NbBundle.Messages({"FileTypesByMimeType.name.text=By MIME Type"})
final String NAME = Bundle.FileTypesByMimeType_name_text();
ByMimeTypeNode() {
super(Children.create(new ByMimeTypeNodeChildren(), true));
super(Children.create(new ByMimeTypeNodeChildren(), true), Lookups.singleton(Bundle.FileTypesByMimeType_name_text()));
super.setName(NAME);
super.setDisplayName(NAME);
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file_types.png");
@@ -259,8 +282,12 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
*/
class MediaTypeNode extends DisplayableItemNode {
@NbBundle.Messages({"FileTypesByMimeTypeNode.createSheet.mediaType.name=Type",
"FileTypesByMimeTypeNode.createSheet.mediaType.displayName=Type",
"FileTypesByMimeTypeNode.createSheet.mediaType.desc=no description"})
MediaTypeNode(String name) {
super(Children.create(new MediaTypeNodeChildren(name), true));
super(Children.create(new MediaTypeNodeChildren(name), true), Lookups.singleton(name));
setName(name);
setDisplayName(name);
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file_types.png");
@@ -276,6 +303,18 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
return v.visit(this);
}
@Override
protected Sheet createSheet() {
Sheet s = super.createSheet();
Sheet.Set ss = s.get(Sheet.PROPERTIES);
if (ss == null) {
ss = Sheet.createPropertiesSet();
s.put(ss);
}
ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "FileTypesByMimeTypeNode.createSheet.mediaType.name"), NbBundle.getMessage(this.getClass(), "FileTypesByMimeTypeNode.createSheet.mediaType.displayName"), NbBundle.getMessage(this.getClass(), "FileTypesByMimeTypeNode.createSheet.mediaType.desc"), getDisplayName()));
return s;
}
@Override
public String getItemType() {
return getClass().getName();
@@ -316,19 +355,25 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
}
/**
* Node which represents the media sub type in the By MIME type tree, the
* media subtype is the portion of the MIME type following the /.
*/
class MediaSubTypeNode extends FileTypes.BGCountUpdatingNode {
@NbBundle.Messages({"FileTypesByMimeTypeNode.createSheet.mediaSubtype.name=Subtype",
"FileTypesByMimeTypeNode.createSheet.mediaSubtype.displayName=Subtype",
"FileTypesByMimeTypeNode.createSheet.mediaSubtype.desc=no description"})
private final String mimeType;
private final String subType;
private MediaSubTypeNode(String mimeType) {
super(typesRoot, Children.create(new MediaSubTypeNodeChildren(mimeType), true));
super(typesRoot, Children.create(new MediaSubTypeNodeChildren(mimeType), true), Lookups.singleton(mimeType));
this.mimeType = mimeType;
this.subType = StringUtils.substringAfter(mimeType, "/");
super.setName(mimeType);
super.setDisplayName(subType);
updateDisplayName();
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file-filter-icon.png"); //NON-NLS
addObserver(this);
}
@@ -347,6 +392,17 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
public <T> T accept(DisplayableItemNodeVisitor< T> v) {
return v.visit(this);
}
@Override
protected Sheet createSheet() {
Sheet s = super.createSheet();
Sheet.Set ss = s.get(Sheet.PROPERTIES);
if (ss == null) {
ss = Sheet.createPropertiesSet();
s.put(ss);
}
ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "FileTypesByMimeTypeNode.createSheet.mediaSubtype.name"), NbBundle.getMessage(this.getClass(), "FileTypesByMimeTypeNode.createSheet.mediaSubtype.displayName"), NbBundle.getMessage(this.getClass(), "FileTypesByMimeTypeNode.createSheet.mediaSubtype.desc"), getDisplayName()));
return s;
}
@Override
public String getItemType() {
@@ -369,39 +425,12 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
}
}
/**
* Create the portion of the query following WHERE for a query of the
* database for each file which matches the complete MIME type represented
* by this node. Matches against the mime_type column in tsk_files.
*
* @param mimeType - the complete mimetype of the file mediatype/subtype
*
* @return query.toString - portion of SQL query which will follow a WHERE
* clause.
*/
static private String createQuery(String mimeType) {
StringBuilder query = new StringBuilder();
query.append("(dir_type = ").append(TskData.TSK_FS_NAME_TYPE_ENUM.REG.getValue()).append(")"); //NON-NLS
query.append(" AND (type IN (").append(TskData.TSK_DB_FILES_TYPE_ENUM.FS.ordinal()).append(","); //NON-NLS
query.append(TskData.TSK_DB_FILES_TYPE_ENUM.CARVED.ordinal()).append(",");
query.append(TskData.TSK_DB_FILES_TYPE_ENUM.DERIVED.ordinal()).append(",");
if (!UserPreferences.hideSlackFilesInViewsTree()) {
query.append(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.ordinal()).append(",");
}
query.append(TskData.TSK_DB_FILES_TYPE_ENUM.LOCAL.ordinal()).append("))");
if (UserPreferences.hideKnownFilesInViewsTree()) {
query.append(" AND (known IS NULL OR known != ").append(TskData.FileKnown.KNOWN.getFileKnownValue()).append(")"); //NON-NLS
}
query.append(" AND mime_type = '").append(mimeType).append("'"); //NON-NLS
return query.toString();
}
/**
* Factory for populating the contents of the Media Sub Type Node with the
* files that match MimeType which is represented by this position in the
* tree.
*/
private class MediaSubTypeNodeChildren extends ChildFactory.Detachable<Content> implements Observer {
private class MediaSubTypeNodeChildren extends ChildFactory.Detachable<FileTypesKey> implements Observer {
private final String mimeType;
@@ -411,23 +440,13 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
this.mimeType = mimeType;
}
/**
* Uses the createQuery method to complete the query, Select * from
* tsk_files WHERE. The results from the database will contain the files
* which match this mime type and their information.
*
* @param list - will contain all files and their attributes from the
* tsk_files table where mime_type matches the one specified
*
* @return true
*/
@Override
protected boolean createKeys(List<Content> list) {
protected boolean createKeys(List<FileTypesKey> list) {
try {
List<AbstractFile> files = skCase.findAllFilesWhere(createQuery(mimeType));
list.addAll(files);
list.addAll(skCase.findAllFilesWhere(createBaseWhereExpr() + " AND mime_type = '" + mimeType + "'")
.stream().map(f -> new FileTypesKey(f)).collect(Collectors.toList())); //NON-NLS
} catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Couldn't get search results", ex); //NON-NLS
logger.log(Level.SEVERE, "Couldn't get search results", ex); //NON-NLS
}
return true;
}
@@ -437,19 +456,9 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
refresh(true);
}
/**
* Creates the content to populate the Directory Listing Table view for
* each file
*
* @param key
*
* @return
*/
@Override
protected Node createNodeForKey(Content key) {
protected Node createNodeForKey(FileTypesKey key) {
return key.accept(new FileTypes.FileNodeCreationVisitor());
}
}
}
@@ -24,7 +24,6 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
@@ -33,6 +32,7 @@ import java.util.Observable;
import java.util.Observer;
import java.util.Set;
import java.util.logging.Level;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children;
@@ -95,6 +95,10 @@ public class KeywordHits implements AutopsyVisitableItem {
+ " OR attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID()//NON-NLS
+ ")"; //NON-NLS
static private boolean isOnlyDefaultInstance(List<String> instances) {
return (instances.size() == 1) && (instances.get(0).equals(DEFAULT_INSTANCE_NAME));
}
public KeywordHits(SleuthkitCase skCase) {
this.skCase = skCase;
keywordResults = new KeywordResults();
@@ -265,7 +269,6 @@ public class KeywordHits implements AutopsyVisitableItem {
addRegExpToList(listMap, reg, word, id);
}
} else {//single term
if ("1".equals(kwType) || reg == null) { //literal, substring or exact
/*
* Substring, treated same as exact match. "1" is
@@ -626,9 +629,8 @@ public class KeywordHits implements AutopsyVisitableItem {
@Override
public boolean isLeafTypeNode() {
List<String> instances = keywordResults.getKeywordInstances(setName, keyword);
// is this an exact/substring match (i.e. did we use the DEFAULT name)?
return instances.size() == 1 && instances.get(0).equals(DEFAULT_INSTANCE_NAME);
return isOnlyDefaultInstance(keywordResults.getKeywordInstances(setName, keyword));
}
@Override
@@ -661,7 +663,6 @@ public class KeywordHits implements AutopsyVisitableItem {
return s;
}
}
/**
@@ -706,7 +707,6 @@ public class KeywordHits implements AutopsyVisitableItem {
private final String keyword;
private final String setName;
private final Map<RegExpInstanceKey, DisplayableItemNode> nodesMap = new HashMap<>();
private RegExpInstancesFactory(String setName, String keyword) {
super();
@@ -720,29 +720,20 @@ public class KeywordHits implements AutopsyVisitableItem {
// The keys are different depending on what we are displaying.
// regexp get another layer to show instances.
// Exact/substring matches don't.
if ((instances.size() == 1) && (instances.get(0).equals(DEFAULT_INSTANCE_NAME))) {
for (Long id : keywordResults.getArtifactIds(setName, keyword, DEFAULT_INSTANCE_NAME)) {
RegExpInstanceKey key = new RegExpInstanceKey(id);
nodesMap.computeIfAbsent(key, k -> createNode(k));
list.add(key);
}
if (isOnlyDefaultInstance(instances)) {
list.addAll(keywordResults.getArtifactIds(setName, keyword, DEFAULT_INSTANCE_NAME).stream()
.map(RegExpInstanceKey::new)
.collect(Collectors.toList()));
} else {
for (String instance : instances) {
RegExpInstanceKey key = new RegExpInstanceKey(instance);
nodesMap.computeIfAbsent(key, k -> createNode(k));
list.add(key);
}
list.addAll(instances.stream()
.map(RegExpInstanceKey::new)
.collect(Collectors.toList()));
}
return true;
}
@Override
protected Node createNodeForKey(RegExpInstanceKey key) {
return nodesMap.get(key);
}
private DisplayableItemNode createNode(RegExpInstanceKey key) {
if (key.isRegExp()) {
return new RegExpInstanceNode(setName, keyword, key.getRegExpKey());
} else {
@@ -750,6 +741,7 @@ public class KeywordHits implements AutopsyVisitableItem {
return createBlackboardArtifactNode(key.getIdKey());
}
}
}
/**
@@ -884,7 +876,6 @@ public class KeywordHits implements AutopsyVisitableItem {
private final String keyword;
private final String setName;
private final String instance;
private final Map<Long, BlackboardArtifactNode> nodesMap = new HashMap<>();
private HitsFactory(String setName, String keyword, String instance) {
super();
@@ -895,16 +886,13 @@ public class KeywordHits implements AutopsyVisitableItem {
@Override
protected boolean createKeys(List<Long> list) {
for (Long id : keywordResults.getArtifactIds(setName, keyword, instance)) {
nodesMap.computeIfAbsent(id, i -> createBlackboardArtifactNode(i));
list.add(id);
}
list.addAll(keywordResults.getArtifactIds(setName, keyword, instance));
return true;
}
@Override
protected Node createNodeForKey(Long artifactId) {
return nodesMap.get(artifactId);
return createBlackboardArtifactNode(artifactId);
}
}
}
@@ -417,7 +417,9 @@ final class ExtractUnallocAction extends AbstractAction {
try {
List<LayoutFile> lflst = new ArrayList<>();
for (Content layout : vd.getChildren()) {
lflst.add((LayoutFile) layout);
if(layout instanceof LayoutFile){
lflst.add((LayoutFile) layout);
}
}
return lflst;
} catch (TskCoreException tce) {
@@ -51,6 +51,7 @@ final class GetFilesCountVisitor extends ContentVisitor.Default<Long> {
queryB.append(") )");
queryB.append(" AND ( (meta_type = ").append(TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG.getValue()); //NON-NLS
queryB.append(") OR (meta_type = ").append(TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_DIR.getValue()); //NON-NLS
queryB.append(") OR (meta_type = ").append(TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_VIRT_DIR.getValue()); //NON-NLS
queryB.append(") OR (meta_type = ").append(TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_VIRT.getValue()); //NON-NLS
queryB.append(" AND (name != '.') AND (name != '..')"); //NON-NLS
queryB.append(") )");
@@ -537,10 +537,12 @@ public final class FilesSet implements Serializable {
case FILES:
return file.isFile();
case DIRECTORIES:
return file.getMetaType() == TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_DIR;
return file.getMetaType() == TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_DIR
|| file.getMetaType() == TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_VIRT_DIR;
case FILES_AND_DIRECTORIES:
return file.getMetaType() == TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG
|| file.getMetaType() == TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_DIR;
|| file.getMetaType() == TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_DIR
|| file.getMetaType() == TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_VIRT_DIR;
case ALL:
return true; //Effectively ignores the metatype condition when All is selected.
default: