Merge pull request #6949 from gdicristofaro/7561-treeHierarchy

7561 tree hierarchy
This commit is contained in:
Richard Cordovano 2021-05-05 16:04:31 -04:00 committed by GitHub
commit e39d8960ab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 1519 additions and 1065 deletions

View File

@ -0,0 +1,95 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.datamodel;
import org.openide.nodes.Children;
import org.openide.util.NbBundle;
import org.sleuthkit.datamodel.BlackboardArtifact;
/**
* Analysis Results node support.
*/
@NbBundle.Messages({
"AnalysisResults_name=Analysis Results",})
public class AnalysisResults implements AutopsyVisitableItem {
/**
* Returns the name of this node that is the key in the children object.
*
* @return The name of this node that is the key in the children object.
*/
public static String getName() {
return Bundle.AnalysisResults_name();
}
/**
* Parent node of all analysis results.
*/
static class RootNode extends Artifacts.BaseArtifactNode {
/**
* Main constructor.
*
* @param filteringDSObjId The data source object id for which results
* should be filtered. If no filtering should
* occur, this number should be less than or
* equal to 0.
*/
RootNode(long filteringDSObjId) {
super(Children.create(new Artifacts.TypeFactory(BlackboardArtifact.Category.ANALYSIS_RESULT, filteringDSObjId), true),
"org/sleuthkit/autopsy/images/analysis_result.png",
AnalysisResults.getName(),
AnalysisResults.getName());
}
}
private final long datasourceObjId;
/**
* Main constructor.
*/
public AnalysisResults() {
this(0);
}
/**
* Main constructor.
*
* @param dsObjId The data source object id.
*/
public AnalysisResults(long dsObjId) {
this.datasourceObjId = dsObjId;
}
@Override
public <T> T accept(AutopsyItemVisitor<T> visitor) {
return visitor.visit(this);
}
/**
* Returns whether or not there is a data source object for which results
* should be filtered.
*
* @return Whether or not there is a data source object for which results
* should be filtered.
*/
Long getFilteringDataSourceObjId() {
return datasourceObjId;
}
}

View File

@ -0,0 +1,704 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.datamodel;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.logging.Level;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.nodes.Sheet;
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.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.accounts.Accounts;
import org.sleuthkit.autopsy.datamodel.utils.IconsUtil;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.autopsy.guiutils.RefreshThrottler;
import org.sleuthkit.datamodel.BlackboardArtifact.Category;
import org.python.google.common.collect.Sets;
import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_ACCOUNT;
import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_DATA_SOURCE_USAGE;
import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_EMAIL_MSG;
import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_HASHSET_HIT;
import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT;
import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT;
import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_GEN_INFO;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE;
import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_TL_EVENT;
import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_ASSOCIATED_OBJECT;
import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_KEYWORD_HIT;
/**
* Classes for creating nodes for BlackboardArtifacts.
*/
public class Artifacts {
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST
= EnumSet.of(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED);
/**
* Base class for a parent node of artifacts.
*/
static class BaseArtifactNode extends DisplayableItemNode {
/**
* Main constructor.
*
* @param children The children of the node.
* @param icon The icon for the node.
* @param name The name identifier of the node.
* @param displayName The display name for the node.
*/
BaseArtifactNode(Children children, String icon, String name, String displayName) {
super(children, Lookups.singleton(name));
super.setName(name);
super.setDisplayName(displayName);
this.setIconBaseWithExtension(icon); //NON-NLS
}
@Override
public boolean isLeafTypeNode() {
return false;
}
@Override
public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
return visitor.visit(this);
}
@Override
protected Sheet createSheet() {
Sheet sheet = super.createSheet();
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
if (sheetSet == null) {
sheetSet = Sheet.createPropertiesSet();
sheet.put(sheetSet);
}
sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ExtractedContentNode.createSheet.name.name"),
NbBundle.getMessage(this.getClass(), "ExtractedContentNode.createSheet.name.displayName"),
NbBundle.getMessage(this.getClass(), "ExtractedContentNode.createSheet.name.desc"),
super.getDisplayName()));
return sheet;
}
@Override
public String getItemType() {
return getClass().getName();
}
}
/**
* A key to be used with the type factory.
*/
private static class TypeNodeKey {
private final UpdatableCountTypeNode node;
private final Set<BlackboardArtifact.Type> applicableTypes;
/**
* Constructor generating a generic TypeNode for a given artifact type.
*
* @param type The type for the key.
* @param dsObjId The data source object id if filtering should occur.
* If no filtering should occur, this number should be
* less than or equal to 0.
*/
TypeNodeKey(BlackboardArtifact.Type type, long dsObjId) {
this(new TypeNode(type, dsObjId), type);
}
/**
* Constructor for any UpdatableCountTypeNode.
*
* @param typeNode The UpdatableCountTypeNode.
* @param types The blackboard artifact types corresponding to this
* node.
*/
TypeNodeKey(UpdatableCountTypeNode typeNode, BlackboardArtifact.Type... types) {
this.node = typeNode;
this.applicableTypes = Stream.of(types)
.filter(t -> t != null)
.collect(Collectors.toSet());
}
/**
* Returns the node associated with this key.
*
* @return The node associated with this key.
*/
UpdatableCountTypeNode getNode() {
return node;
}
/**
* Returns the blackboard artifact types associated with this key.
*
* @return The blackboard artifact types associated with this key.
*/
Set<BlackboardArtifact.Type> getApplicableTypes() {
return applicableTypes;
}
@Override
public int hashCode() {
int hash = 3;
hash = 61 * hash + Objects.hashCode(this.applicableTypes);
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final TypeNodeKey other = (TypeNodeKey) obj;
if (!Objects.equals(this.applicableTypes, other.applicableTypes)) {
return false;
}
return true;
}
}
/**
* Factory for showing a list of artifact types (i.e. all the data artifact
* types).
*/
static class TypeFactory extends ChildFactory.Detachable<TypeNodeKey> implements RefreshThrottler.Refresher {
private static final Logger logger = Logger.getLogger(TypeNode.class.getName());
/**
* Types that should not be shown in the tree.
*/
@SuppressWarnings("deprecation")
private static final Set<BlackboardArtifact.Type> IGNORED_TYPES = Sets.newHashSet(
// these are shown in other parts of the UI (and different node types)
TSK_DATA_SOURCE_USAGE,
TSK_GEN_INFO,
new BlackboardArtifact.Type(TSK_DOWNLOAD_SOURCE),
TSK_TL_EVENT,
//This is not meant to be shown in the UI at all. It is more of a meta artifact.
TSK_ASSOCIATED_OBJECT
);
/**
* Returns a Children key to be use for a particular artifact type.
*
* @param type The artifact type.
* @param skCase The relevant Sleuthkit case in order to create the
* node.
* @param dsObjId The data source object id to use for filtering. If id
* is less than or equal to 0, no filtering will occur.
*
* @return The generated key.
*/
private static TypeNodeKey getTypeKey(BlackboardArtifact.Type type, SleuthkitCase skCase, long dsObjId) {
int typeId = type.getTypeID();
if (TSK_EMAIL_MSG.getTypeID() == typeId) {
EmailExtracted.RootNode emailNode = new EmailExtracted(skCase, dsObjId).new RootNode();
return new TypeNodeKey(emailNode, TSK_EMAIL_MSG);
} else if (TSK_ACCOUNT.getTypeID() == typeId) {
Accounts.AccountsRootNode accountsNode = new Accounts(skCase, dsObjId).new AccountsRootNode();
return new TypeNodeKey(accountsNode, TSK_ACCOUNT);
} else if (TSK_KEYWORD_HIT.getTypeID() == typeId) {
KeywordHits.RootNode keywordsNode = new KeywordHits(skCase, dsObjId).new RootNode();
return new TypeNodeKey(keywordsNode, TSK_KEYWORD_HIT);
} else if (TSK_INTERESTING_ARTIFACT_HIT.getTypeID() == typeId
|| TSK_INTERESTING_FILE_HIT.getTypeID() == typeId) {
InterestingHits.RootNode interestingHitsNode = new InterestingHits(skCase, dsObjId).new RootNode();
return new TypeNodeKey(interestingHitsNode,
TSK_INTERESTING_ARTIFACT_HIT,
TSK_INTERESTING_FILE_HIT);
} else if (TSK_HASHSET_HIT.getTypeID() == typeId) {
HashsetHits.RootNode hashsetHits = new HashsetHits(skCase, dsObjId).new RootNode();
return new TypeNodeKey(hashsetHits, TSK_HASHSET_HIT);
} else {
return new TypeNodeKey(type, dsObjId);
}
}
// maps the artifact type to its child node
private final Map<BlackboardArtifact.Type, TypeNodeKey> typeNodeMap = new HashMap<>();
private final long filteringDSObjId;
/**
* RefreshThrottler is used to limit the number of refreshes performed
* when CONTENT_CHANGED and DATA_ADDED ingest module events are
* received.
*/
private final RefreshThrottler refreshThrottler = new RefreshThrottler(this);
private final Category category;
/**
* Main constructor.
*
* @param category The category of types to be displayed.
* @param filteringDSObjId The data source object id to use for
* filtering. If id is less than or equal to 0,
* no filtering will occur.
*/
TypeFactory(Category category, long filteringDSObjId) {
super();
this.filteringDSObjId = filteringDSObjId;
this.category = category;
}
private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> {
String eventType = evt.getPropertyName();
if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
// case was closed. Remove listeners so that we don't get called with a stale case handle
if (evt.getNewValue() == null) {
removeNotify();
}
} else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
|| eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
/**
* This is a stop gap measure until a different way of handling
* the closing of cases is worked out. Currently, remote events
* may be received for a case that is already closed.
*/
try {
Case.getCurrentCaseThrows();
refresh(false);
} catch (NoCurrentCaseException notUsed) {
/**
* Case is closed, do nothing.
*/
}
}
};
@Override
protected void addNotify() {
refreshThrottler.registerForIngestModuleEvents();
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl);
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
}
@Override
protected void removeNotify() {
refreshThrottler.unregisterEventListener();
IngestManager.getInstance().removeIngestJobEventListener(pcl);
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
typeNodeMap.clear();
}
@Override
protected boolean createKeys(List<TypeNodeKey> list) {
try {
// Get all types in use
SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
List<BlackboardArtifact.Type> types = (this.filteringDSObjId > 0)
? skCase.getBlackboard().getArtifactTypesInUse(this.filteringDSObjId)
: skCase.getArtifactTypesInUse();
List<TypeNodeKey> allKeysSorted = types.stream()
// filter types by category and ensure they are not in the list of ignored types
.filter(tp -> category.equals(tp.getCategory()) && !IGNORED_TYPES.contains(tp))
.map(tp -> {
// if typeNodeMap already contains key, update the relevant node and return the node
if (typeNodeMap.containsKey(tp)) {
TypeNodeKey typeKey = typeNodeMap.get(tp);
typeKey.getNode().updateDisplayName();
return typeKey;
} else {
// if key is not in map, create the type key and add to map
TypeNodeKey newTypeKey = getTypeKey(tp, skCase, filteringDSObjId);
for (BlackboardArtifact.Type recordType : newTypeKey.getApplicableTypes()) {
typeNodeMap.put(recordType, newTypeKey);
}
return newTypeKey;
}
})
// ensure record is returned
.filter(record -> record != null)
// there are potentially multiple types that apply to the same node (i.e. Interesting Files / Artifacts)
// ensure the keys are distinct
.distinct()
// sort by display name
.sorted((a, b) -> {
String aSafe = (a.getNode() == null || a.getNode().getDisplayName() == null) ? "" : a.getNode().getDisplayName();
String bSafe = (b.getNode() == null || b.getNode().getDisplayName() == null) ? "" : b.getNode().getDisplayName();
return aSafe.compareToIgnoreCase(bSafe);
})
.collect(Collectors.toList());
list.addAll(allKeysSorted);
} catch (NoCurrentCaseException ex) {
logger.log(Level.WARNING, "Trying to access case when no case is open.", ex); //NON-NLS
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error getting list of artifacts in use: " + ex.getLocalizedMessage()); //NON-NLS
}
return true;
}
@Override
protected Node createNodeForKey(TypeNodeKey key) {
return key.getNode();
}
@Override
public void refresh() {
refresh(false);
}
@Override
public boolean isRefreshRequired(PropertyChangeEvent evt) {
String eventType = evt.getPropertyName();
if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
/**
* This is a stop gap measure until a different way of handling
* the closing of cases is worked out. Currently, remote events
* may be received for a case that is already closed.
*/
try {
Case.getCurrentCaseThrows();
/**
* Due to some unresolved issues with how cases are closed,
* it is possible for the event to have a null oldValue if
* the event is a remote event.
*/
final ModuleDataEvent event = (ModuleDataEvent) evt.getOldValue();
if (null != event && category.equals(event.getBlackboardArtifactType().getCategory())
&& !(IGNORED_TYPES.contains(event.getBlackboardArtifactType()))) {
return true;
}
} catch (NoCurrentCaseException notUsed) {
/**
* Case is closed, do nothing.
*/
}
}
return false;
}
}
/**
* Abstract class for type(s) nodes. This class allows for displaying a
* count artifacts with the type(s) associated with this node.
*/
public static abstract class UpdatableCountTypeNode extends DisplayableItemNode {
private static final Logger logger = Logger.getLogger(UpdatableCountTypeNode.class.getName());
private final Set<BlackboardArtifact.Type> types;
private final long filteringDSObjId;
private long childCount = 0;
private final String baseName;
/**
* Main constructor.
*
* @param children The Children to associated with this node.
* @param lookup The Lookup to use with this name.
* @param baseName The display name. The Node.displayName will
* be of format "[baseName] ([count])".
* @param filteringDSObjId The data source object id to use for
* filtering. If id is less than or equal to 0,
* no filtering will occur.
* @param types The types associated with this type node.
*/
public UpdatableCountTypeNode(Children children, Lookup lookup, String baseName,
long filteringDSObjId, BlackboardArtifact.Type... types) {
super(children, lookup);
this.types = Stream.of(types).collect(Collectors.toSet());
this.filteringDSObjId = filteringDSObjId;
this.baseName = baseName;
updateDisplayName();
}
/**
* Returns the count of artifacts associated with these type(s).
*
* @return The count of artifacts associated with these type(s).
*/
protected long getChildCount() {
return this.childCount;
}
/**
* Fetches the count to be displayed from the case.
*
* @param skCase The relevant SleuthkitCase.
*
* @return The count to be displayed.
*
* @throws TskCoreException
*/
protected long fetchChildCount(SleuthkitCase skCase) throws TskCoreException {
int count = 0;
for (BlackboardArtifact.Type type : this.types) {
if (filteringDSObjId > 0) {
count += skCase.getBlackboard().getArtifactsCount(type.getTypeID(), filteringDSObjId);
} else {
count += skCase.getBlackboardArtifactsTypeCount(type.getTypeID());
}
}
return count;
}
/**
* When this method is called, the count to be displayed will be
* updated.
*/
void updateDisplayName() {
try {
SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
this.childCount = fetchChildCount(skCase);
} catch (NoCurrentCaseException ex) {
logger.log(Level.WARNING, "Error fetching data when case closed.", ex);
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Error getting child count", ex); //NON-NLS
}
super.setDisplayName(this.baseName + " \u200E(\u200E" + this.childCount + ")\u200E");
}
}
/**
* Default node encapsulating a blackboard artifact type. This is used on
* the left-hand navigation side of the Autopsy UI as the parent node for
* all of the artifacts of a given type. Its children will be
* BlackboardArtifactNode objects.
*/
static class TypeNode extends UpdatableCountTypeNode {
private final BlackboardArtifact.Type type;
/**
* Main constructor.
*
* @param type The blackboard artifact type for this node.
* @param filteringDSObjId The data source object id to use for
* filtering. If id is less than or equal to 0,
* no filtering will occur.
*/
TypeNode(BlackboardArtifact.Type type, long filteringDSObjId) {
super(Children.create(new ArtifactFactory(type, filteringDSObjId), true),
Lookups.singleton(type.getDisplayName()),
type.getDisplayName(),
filteringDSObjId,
type);
super.setName(type.getTypeName());
this.type = type;
String iconPath = IconsUtil.getIconFilePath(type.getTypeID());
setIconBaseWithExtension(iconPath != null && iconPath.charAt(0) == '/' ? iconPath.substring(1) : iconPath);
}
@Override
protected Sheet createSheet() {
Sheet sheet = super.createSheet();
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
if (sheetSet == null) {
sheetSet = Sheet.createPropertiesSet();
sheet.put(sheetSet);
}
sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.artType.name"),
NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.artType.displayName"),
NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.artType.desc"),
type.getDisplayName()));
sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.childCnt.name"),
NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.childCnt.displayName"),
NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.childCnt.desc"),
getChildCount()));
return sheet;
}
@Override
public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
return visitor.visit(this);
}
@Override
public boolean isLeafTypeNode() {
return true;
}
@Override
public String getItemType() {
return getClass().getName() + type.getDisplayName();
}
}
/**
* Creates children for a given artifact type
*/
private static class ArtifactFactory extends BaseChildFactory<BlackboardArtifact> implements RefreshThrottler.Refresher {
private static final Logger logger = Logger.getLogger(ArtifactFactory.class.getName());
private final BlackboardArtifact.Type type;
/**
* RefreshThrottler is used to limit the number of refreshes performed
* when CONTENT_CHANGED and DATA_ADDED ingest module events are
* received.
*/
private final RefreshThrottler refreshThrottler = new RefreshThrottler(this);
private final long filteringDSObjId;
/**
* Main constructor.
*
* @param type The blackboard artifact type for this node.
* @param filteringDSObjId The data source object id to use for
* filtering. If id is less than or equal to 0,
* no filtering will occur.
*/
ArtifactFactory(BlackboardArtifact.Type type, long filteringDSObjId) {
super(type.getTypeName());
this.type = type;
this.filteringDSObjId = filteringDSObjId;
}
private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> {
String eventType = evt.getPropertyName();
if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
|| eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
/**
* Checking for a current case is a stop gap measure until a
* different way of handling the closing of cases is worked out.
* Currently, remote events may be received for a case that is
* already closed.
*/
try {
Case.getCurrentCaseThrows();
refresh(false);
} catch (NoCurrentCaseException notUsed) {
/**
* Case is closed, do nothing.
*/
}
}
};
@Override
protected void onAdd() {
refreshThrottler.registerForIngestModuleEvents();
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl);
}
@Override
protected void onRemove() {
refreshThrottler.unregisterEventListener();
IngestManager.getInstance().removeIngestJobEventListener(pcl);
}
@Override
protected Node createNodeForKey(BlackboardArtifact key) {
return new BlackboardArtifactNode(key);
}
@Override
protected List<BlackboardArtifact> makeKeys() {
try {
List<BlackboardArtifact> arts;
arts = (filteringDSObjId > 0)
? Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard().getArtifacts(type.getTypeID(), filteringDSObjId)
: Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboardArtifacts(type.getTypeID());
for (BlackboardArtifact art : arts) {
//Cache attributes while we are off the EDT.
//See JIRA-5969
art.getAttributes();
}
return arts;
} catch (NoCurrentCaseException ex) {
logger.log(Level.WARNING, "Trying to access case when no case is open.", ex); //NON-NLS
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Couldn't get blackboard artifacts from database", ex); //NON-NLS
}
return Collections.emptyList();
}
@Override
public void refresh() {
refresh(false);
}
@Override
public boolean isRefreshRequired(PropertyChangeEvent evt) {
String eventType = evt.getPropertyName();
if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
/**
* Checking for a current case is a stop gap measure until a
* different way of handling the closing of cases is worked out.
* Currently, remote events may be received for a case that is
* already closed.
*/
try {
Case.getCurrentCaseThrows();
/**
* Even with the check above, it is still possible that the
* case will be closed in a different thread before this
* code executes. If that happens, it is possible for the
* event to have a null oldValue.
*/
final ModuleDataEvent event = (ModuleDataEvent) evt.getOldValue();
if (null != event && event.getBlackboardArtifactType().equals(type)) {
return true;
}
} catch (NoCurrentCaseException notUsed) {
/**
* Case is closed, do nothing.
*/
}
}
return false;
}
}
}

View File

@ -53,8 +53,6 @@ public interface AutopsyItemVisitor<T> {
T visit(FileSize.FileSizeFilter fsf);
T visit(ExtractedContent ec);
T visit(KeywordHits kh);
T visit(HashsetHits hh);
@ -63,8 +61,6 @@ public interface AutopsyItemVisitor<T> {
T visit(InterestingHits ih);
T visit(Results r);
T visit(Tags tagsNodeKey);
T visit(Reports reportsItem);
@ -85,15 +81,15 @@ public interface AutopsyItemVisitor<T> {
T visit(DataSourcesByType aThis);
T visit(AnalysisResults aThis);
T visit(DataArtifacts aThis);
static abstract public class Default<T> implements AutopsyItemVisitor<T> {
protected abstract T defaultVisit(AutopsyVisitableItem ec);
@Override
public T visit(ExtractedContent ec) {
return defaultVisit(ec);
}
@Override
public T visit(FileTypesByExtension sf) {
return defaultVisit(sf);
@ -199,11 +195,6 @@ public interface AutopsyItemVisitor<T> {
return defaultVisit(personGrouping);
}
@Override
public T visit(Results r) {
return defaultVisit(r);
}
@Override
public T visit(FileTypes ft) {
return defaultVisit(ft);
@ -233,5 +224,15 @@ public interface AutopsyItemVisitor<T> {
public T visit(DataSourcesByType dataSourceHosts) {
return defaultVisit(dataSourceHosts);
}
@Override
public T visit(DataArtifacts aThis) {
return defaultVisit(aThis);
}
@Override
public T visit(AnalysisResults aThis) {
return defaultVisit(aThis);
}
}
}

View File

@ -111,24 +111,27 @@ public final class AutopsyTreeChildFactory extends ChildFactory.Detachable<Objec
if (CollectionUtils.isNotEmpty(personManager.getHostsForPerson(null))) {
list.add(new PersonGrouping(null));
}
return true;
} else {
// otherwise, just show host level
tskCase.getHostManager().getAllHosts().stream()
.map(HostGrouping::new)
.sorted()
.forEach(list::add);
return true;
}
list.add(new Reports());
return true;
} else {
// data source by type view
List<AutopsyVisitableItem> keys = new ArrayList<>(Arrays.asList(
new DataSourcesByType(),
new Views(tskCase),
new Results(tskCase),
new Views(Case.getCurrentCaseThrows().getSleuthkitCase()),
new DataArtifacts(),
new AnalysisResults(),
new OsAccounts(Case.getCurrentCaseThrows().getSleuthkitCase()),
new Tags(),
new Reports()));
new Reports()
));
list.addAll(keys);
}

View File

@ -171,7 +171,7 @@ TagNameNode.createSheet.name.displayName=Name
TagsNode.displayName.text=Tags
TagsNode.createSheet.name.name=Name
TagsNode.createSheet.name.displayName=Name
ViewsNode.name.text=Views
ViewsNode.name.text=File Views
ViewsNode.createSheet.name.name=Name
ViewsNode.createSheet.name.displayName=Name
ViewsNode.createSheet.name.desc=no description

View File

@ -39,6 +39,7 @@ AbstractAbstractFileNode.useridColLbl=UserID
AbstractContentNode.nodescription=no description
AbstractContentNode.valueLoading=value loading
AbstractFsContentNode.noDesc.text=no description
AnalysisResults_name=Analysis Results
ArtifactStringContent.attrsTableHeader.sources=Source(s)
ArtifactStringContent.attrsTableHeader.type=Type
ArtifactStringContent.attrsTableHeader.value=Value
@ -93,6 +94,7 @@ ContentTagNode.createSheet.artifactMD5.displayName=MD5 Hash
ContentTagNode.createSheet.artifactMD5.name=MD5 Hash
ContentTagNode.createSheet.origFileName=Original Name
ContentTagNode.createSheet.userName.text=User Name
DataArtifacts_name=Data Artifacts
DataSourcesHostsNode_name=Data Sources
DeletedContent.allDelFilter.text=All
DeletedContent.createSheet.filterType.desc=no description
@ -358,10 +360,6 @@ ReportNode.reportNameProperty.name=Report Name
ReportNode.reportNameProperty.displayName=Report Name
ReportNode.reportNameProperty.desc=Name of the report
ReportsListNode.displayName=Reports
ResultsNode.createSheet.name.desc=no description
ResultsNode.createSheet.name.displayName=Name
ResultsNode.createSheet.name.name=Name
ResultsNode.name.text=Results
SlackFileNode.getActions.viewInNewWin.text=View in New Window
SlackFileNode.getActions.viewFileInDir.text=View File in Directory
SpecialDirectoryNode.getActions.viewInNewWin.text=View in New Window
@ -380,7 +378,7 @@ UnsupportedContentNode.createSheet.name.desc=no description
UnsupportedContentNode.createSheet.name.displayName=Name
UnsupportedContentNode.createSheet.name.name=Name
UnsupportedContentNode.displayName=Unsupported Content
ViewsNode.name.text=Views
ViewsNode.name.text=File Views
ViewsNode.createSheet.name.name=Name
ViewsNode.createSheet.name.displayName=Name
ViewsNode.createSheet.name.desc=no description

View File

@ -0,0 +1,95 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.datamodel;
import org.openide.nodes.Children;
import org.openide.util.NbBundle;
import org.sleuthkit.datamodel.BlackboardArtifact;
/**
* Analysis Results node support.
*/
@NbBundle.Messages({
"DataArtifacts_name=Data Artifacts",})
public class DataArtifacts implements AutopsyVisitableItem {
/**
* Returns the name of this node that is the key in the children object.
*
* @return The name of this node that is the key in the children object.
*/
public static String getName() {
return Bundle.DataArtifacts_name();
}
/**
* Parent node of all data artifacts.
*/
static class RootNode extends Artifacts.BaseArtifactNode {
/**
* Main constructor.
*
* @param filteringDSObjId The data source object id for which results
* should be filtered. If no filtering should
* occur, this number should be less than or
* equal to 0.
*/
RootNode(long filteringDSObjId) {
super(Children.create(new Artifacts.TypeFactory(BlackboardArtifact.Category.DATA_ARTIFACT, filteringDSObjId), true),
"org/sleuthkit/autopsy/images/extracted_content.png",
DataArtifacts.getName(),
DataArtifacts.getName());
}
}
private final long datasourceObjId;
/**
* Main constructor.
*/
public DataArtifacts() {
this(0);
}
/**
* Main constructor.
*
* @param dsObjId The data source object id.
*/
public DataArtifacts(long dsObjId) {
this.datasourceObjId = dsObjId;
}
@Override
public <T> T accept(AutopsyItemVisitor<T> visitor) {
return visitor.visit(this);
}
/**
* Returns whether or not there is a data source object for which results
* should be filtered.
*
* @return Whether or not there is a data source object for which results
* should be filtered.
*/
Long getFilteringDataSourceObjId() {
return datasourceObjId;
}
}

View File

@ -30,7 +30,6 @@ import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.Image;
import org.sleuthkit.datamodel.LocalFilesDataSource;
/**
* Data source grouping node - an optional grouping node in the data tree view
*
@ -78,10 +77,12 @@ class DataSourceGroupingNode extends DisplayableItemNode {
return new RootContentChildren(Arrays.asList(
new DataSources(dsObjId),
new Views(Case.getCurrentCaseThrows().getSleuthkitCase(), dsObjId),
new Results(Case.getCurrentCaseThrows().getSleuthkitCase(), dsObjId),
new Tags(dsObjId) )
);
new DataArtifacts(dsObjId),
new AnalysisResults(dsObjId),
new OsAccounts(Case.getCurrentCaseThrows().getSleuthkitCase(), dsObjId),
new Tags(dsObjId),
new Reports()
));
} catch (NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "Error getting open case.", ex); //NON-NLS

View File

@ -88,16 +88,11 @@ public interface DisplayableItemNodeVisitor<T> {
T visit(RecentFilesFilterNode rffn);
/*
* Extracted Results Area
*/
T visit(ResultsNode rn);
T visit(BlackboardArtifactNode ban);
T visit(ExtractedContent.TypeNode atn);
T visit(Artifacts.TypeNode atn);
T visit(ExtractedContent.RootNode ecn);
T visit(Artifacts.BaseArtifactNode ecn);
T visit(KeywordHits.RootNode khrn);
@ -301,12 +296,12 @@ public interface DisplayableItemNodeVisitor<T> {
}
@Override
public T visit(ExtractedContent.TypeNode atn) {
public T visit(Artifacts.TypeNode atn) {
return defaultVisit(atn);
}
@Override
public T visit(ExtractedContent.RootNode ecn) {
public T visit(Artifacts.BaseArtifactNode ecn) {
return defaultVisit(ecn);
}
@ -405,11 +400,6 @@ public interface DisplayableItemNodeVisitor<T> {
return defaultVisit(dataSourceGroupingNode);
}
@Override
public T visit(ResultsNode rn) {
return defaultVisit(rn);
}
@Override
public T visit(FileTypesNode ft) {
return defaultVisit(ft);

View File

@ -32,7 +32,6 @@ import java.util.Observable;
import java.util.Observer;
import java.util.Set;
import java.util.logging.Level;
import org.apache.commons.lang3.tuple.Pair;
import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
@ -45,10 +44,12 @@ import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
import org.sleuthkit.datamodel.BlackboardArtifact;
import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_EMAIL_MSG;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.autopsy.datamodel.Artifacts.UpdatableCountTypeNode;
/**
* Support for TSK_EMAIL_MSG nodes and displaying emails in the directory tree.
@ -58,8 +59,7 @@ import org.sleuthkit.datamodel.TskCoreException;
*/
public class EmailExtracted implements AutopsyVisitableItem {
private static final String LABEL_NAME = BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getLabel();
private static final String DISPLAY_NAME = BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getDisplayName();
private static final String LABEL_NAME = BlackboardArtifact.Type.TSK_EMAIL_MSG.getTypeName();
private static final Logger logger = Logger.getLogger(EmailExtracted.class.getName());
private static final String MAIL_ACCOUNT = NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.mailAccount.text");
private static final String MAIL_FOLDER = NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.mailFolder.text");
@ -157,18 +157,18 @@ public class EmailExtracted implements AutopsyVisitableItem {
}
// get artifact id and path (if present) of all email artifacts
int emailArtifactId = BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID();
int emailArtifactId = BlackboardArtifact.Type.TSK_EMAIL_MSG.getTypeID();
int pathAttrId = BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH.getTypeID();
String query = "SELECT \n" +
" art.artifact_id AS artifact_id,\n" +
" (SELECT value_text FROM blackboard_attributes attr\n" +
" WHERE attr.artifact_id = art.artifact_id AND attr.attribute_type_id = " + pathAttrId + "\n" +
" LIMIT 1) AS value_text\n" +
"FROM \n" +
" blackboard_artifacts art\n" +
" WHERE art.artifact_type_id = " + emailArtifactId + "\n" +
((filteringDSObjId > 0) ? " AND art.data_source_obj_id = " + filteringDSObjId : "");
String query = "SELECT \n"
+ " art.artifact_id AS artifact_id,\n"
+ " (SELECT value_text FROM blackboard_attributes attr\n"
+ " WHERE attr.artifact_id = art.artifact_id AND attr.attribute_type_id = " + pathAttrId + "\n"
+ " LIMIT 1) AS value_text\n"
+ "FROM \n"
+ " blackboard_artifacts art\n"
+ " WHERE art.artifact_type_id = " + emailArtifactId + "\n"
+ ((filteringDSObjId > 0) ? " AND art.data_source_obj_id = " + filteringDSObjId : "");
// form hierarchy of account -> folder -> account id
Map<String, Map<String, List<Long>>> newMapping = new HashMap<>();
@ -189,7 +189,6 @@ public class EmailExtracted implements AutopsyVisitableItem {
logger.log(Level.WARNING, "Cannot initialize email extraction: ", ex); //NON-NLS
}
synchronized (accounts) {
accounts.clear();
accounts.putAll(newMapping);
@ -200,17 +199,20 @@ public class EmailExtracted implements AutopsyVisitableItem {
}
}
/**
* Mail root node grouping all mail accounts, supports account-> folder
* structure
*/
public class RootNode extends DisplayableItemNode {
public class RootNode extends UpdatableCountTypeNode {
public RootNode() {
super(Children.create(new AccountFactory(), true), Lookups.singleton(DISPLAY_NAME));
super(Children.create(new AccountFactory(), true),
Lookups.singleton(TSK_EMAIL_MSG.getDisplayName()),
TSK_EMAIL_MSG.getDisplayName(),
filteringDSObjId,
TSK_EMAIL_MSG);
//super(Children.create(new AccountFactory(), true), Lookups.singleton(DISPLAY_NAME));
super.setName(LABEL_NAME);
super.setDisplayName(DISPLAY_NAME);
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/mail-icon-16.png"); //NON-NLS
emailResults.update();
}
@ -277,7 +279,7 @@ public class EmailExtracted implements AutopsyVisitableItem {
* for the event to have a null oldValue.
*/
ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
if (null != eventData && eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()) {
if (null != eventData && eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.Type.TSK_EMAIL_MSG.getTypeID()) {
emailResults.update();
}
} catch (NoCurrentCaseException notUsed) {

View File

@ -1,492 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.datamodel;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
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.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.utils.IconsUtil;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
import org.sleuthkit.datamodel.Blackboard;
import org.sleuthkit.datamodel.BlackboardArtifact;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_TL_EVENT;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_DATA_SOURCE_USAGE;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_GEN_INFO;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.autopsy.guiutils.RefreshThrottler;
/**
* Parent of the "extracted content" artifacts to be displayed in the tree.
* Other artifacts are displayed under other more specific parents.
*/
public class ExtractedContent implements AutopsyVisitableItem {
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED);
public static final String NAME = NbBundle.getMessage(RootNode.class, "ExtractedContentNode.name.text");
private final long filteringDSObjId; // 0 if not filtering/grouping by data source
private SleuthkitCase skCase; // set to null after case has been closed
private Blackboard blackboard;
/**
* Constructs extracted content object
*
* @param skCase Case DB
*/
public ExtractedContent(SleuthkitCase skCase) {
this(skCase, 0);
}
/**
* Constructs extracted content object
*
* @param skCase Case DB
* @param objId Object id of the parent datasource
*/
public ExtractedContent(SleuthkitCase skCase, long objId) {
this.skCase = skCase;
this.filteringDSObjId = objId;
this.blackboard = skCase.getBlackboard();
}
@Override
public <T> T accept(AutopsyItemVisitor<T> visitor) {
return visitor.visit(this);
}
public SleuthkitCase getSleuthkitCase() {
return skCase;
}
public class RootNode extends DisplayableItemNode {
public RootNode(SleuthkitCase skCase) {
super(Children.create(new TypeFactory(), true), Lookups.singleton(NAME));
super.setName(NAME);
super.setDisplayName(NAME);
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/extracted_content.png"); //NON-NLS
}
@Override
public boolean isLeafTypeNode() {
return false;
}
@Override
public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
return visitor.visit(this);
}
@Override
protected Sheet createSheet() {
Sheet sheet = super.createSheet();
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
if (sheetSet == null) {
sheetSet = Sheet.createPropertiesSet();
sheet.put(sheetSet);
}
sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ExtractedContentNode.createSheet.name.name"),
NbBundle.getMessage(this.getClass(), "ExtractedContentNode.createSheet.name.displayName"),
NbBundle.getMessage(this.getClass(), "ExtractedContentNode.createSheet.name.desc"),
NAME));
return sheet;
}
@Override
public String getItemType() {
return getClass().getName();
}
}
/**
* Creates the children for the ExtractedContent area of the results tree.
* This area has all of the blackboard artifacts that are not displayed in a
* more specific form elsewhere in the tree.
*/
private class TypeFactory extends ChildFactory.Detachable<BlackboardArtifact.Type> implements RefreshThrottler.Refresher {
private final ArrayList<BlackboardArtifact.Type> doNotShow = new ArrayList<>();
// maps the artifact type to its child node
private final HashMap<BlackboardArtifact.Type, TypeNode> typeNodeList = new HashMap<>();
/**
* RefreshThrottler is used to limit the number of refreshes performed
* when CONTENT_CHANGED and DATA_ADDED ingest module events are
* received.
*/
private final RefreshThrottler refreshThrottler = new RefreshThrottler(this);
@SuppressWarnings("deprecation")
TypeFactory() {
super();
// these are shown in other parts of the UI
doNotShow.add(new BlackboardArtifact.Type(TSK_GEN_INFO));
doNotShow.add(new BlackboardArtifact.Type(TSK_EMAIL_MSG));
doNotShow.add(new BlackboardArtifact.Type(TSK_HASHSET_HIT));
doNotShow.add(new BlackboardArtifact.Type(TSK_KEYWORD_HIT));
doNotShow.add(new BlackboardArtifact.Type(TSK_INTERESTING_FILE_HIT));
doNotShow.add(new BlackboardArtifact.Type(TSK_INTERESTING_ARTIFACT_HIT));
doNotShow.add(new BlackboardArtifact.Type(TSK_ACCOUNT));
doNotShow.add(new BlackboardArtifact.Type(TSK_DATA_SOURCE_USAGE));
doNotShow.add(new BlackboardArtifact.Type(TSK_DOWNLOAD_SOURCE));
doNotShow.add(new BlackboardArtifact.Type(TSK_TL_EVENT));
//This is not meant to be shown in the UI at all. It is more of a meta artifact.
doNotShow.add(new BlackboardArtifact.Type(TSK_ASSOCIATED_OBJECT));
}
private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> {
String eventType = evt.getPropertyName();
if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
// case was closed. Remove listeners so that we don't get called with a stale case handle
if (evt.getNewValue() == null) {
removeNotify();
skCase = null;
}
} else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
|| eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
/**
* This is a stop gap measure until a different way of handling
* the closing of cases is worked out. Currently, remote events
* may be received for a case that is already closed.
*/
try {
Case.getCurrentCaseThrows();
refresh(false);
} catch (NoCurrentCaseException notUsed) {
/**
* Case is closed, do nothing.
*/
}
}
};
@Override
protected void addNotify() {
refreshThrottler.registerForIngestModuleEvents();
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl);
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
}
@Override
protected void removeNotify() {
refreshThrottler.unregisterEventListener();
IngestManager.getInstance().removeIngestJobEventListener(pcl);
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
typeNodeList.clear();
}
@Override
protected boolean createKeys(List<BlackboardArtifact.Type> list) {
if (skCase != null) {
try {
List<BlackboardArtifact.Type> types = (filteringDSObjId > 0)
? blackboard.getArtifactTypesInUse(filteringDSObjId)
: skCase.getArtifactTypesInUse();
types.removeAll(doNotShow);
Collections.sort(types,
new Comparator<BlackboardArtifact.Type>() {
@Override
public int compare(BlackboardArtifact.Type a, BlackboardArtifact.Type b) {
return a.getDisplayName().compareTo(b.getDisplayName());
}
});
list.addAll(types);
// the create node method will get called only for new types
// refresh the counts if we already created them from a previous update
for (BlackboardArtifact.Type art : types) {
TypeNode node = typeNodeList.get(art);
if (node != null) {
node.updateDisplayName();
}
}
} catch (TskCoreException ex) {
Logger.getLogger(TypeFactory.class.getName()).log(Level.SEVERE, "Error getting list of artifacts in use: " + ex.getLocalizedMessage()); //NON-NLS
}
}
return true;
}
@Override
protected Node createNodeForKey(BlackboardArtifact.Type key) {
TypeNode node = new TypeNode(key);
typeNodeList.put(key, node);
return node;
}
@Override
public void refresh() {
refresh(false);
}
@Override
public boolean isRefreshRequired(PropertyChangeEvent evt) {
String eventType = evt.getPropertyName();
if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
/**
* This is a stop gap measure until a different way of handling
* the closing of cases is worked out. Currently, remote events
* may be received for a case that is already closed.
*/
try {
Case.getCurrentCaseThrows();
/**
* Due to some unresolved issues with how cases are closed,
* it is possible for the event to have a null oldValue if
* the event is a remote event.
*/
final ModuleDataEvent event = (ModuleDataEvent) evt.getOldValue();
if (null != event && !(this.doNotShow.contains(event.getBlackboardArtifactType()))) {
return true;
}
} catch (NoCurrentCaseException notUsed) {
/**
* Case is closed, do nothing.
*/
}
}
return false;
}
}
/**
* Node encapsulating blackboard artifact type. This is used on the
* left-hand navigation side of the Autopsy UI as the parent node for all of
* the artifacts of a given type. Its children will be
* BlackboardArtifactNode objects.
*/
public class TypeNode extends DisplayableItemNode {
private final BlackboardArtifact.Type type;
private long childCount = 0;
TypeNode(BlackboardArtifact.Type type) {
super(Children.create(new ArtifactFactory(type), true), Lookups.singleton(type.getDisplayName()));
super.setName(type.getTypeName());
this.type = type;
String iconPath = IconsUtil.getIconFilePath(type.getTypeID());
setIconBaseWithExtension(iconPath != null && iconPath.charAt(0) == '/' ? iconPath.substring(1) : iconPath);
updateDisplayName();
}
final void updateDisplayName() {
if (skCase == null) {
return;
}
// NOTE: This completely destroys our lazy-loading ideal
// a performance increase might be had by adding a
// "getBlackboardArtifactCount()" method to skCase
try {
this.childCount = (filteringDSObjId > 0)
? blackboard.getArtifactsCount(type.getTypeID(), filteringDSObjId)
: skCase.getBlackboardArtifactsTypeCount(type.getTypeID());
} catch (TskCoreException ex) {
Logger.getLogger(TypeNode.class.getName())
.log(Level.WARNING, "Error getting child count", ex); //NON-NLS
}
super.setDisplayName(type.getDisplayName() + " \u200E(\u200E" + childCount + ")\u200E");
}
@Override
protected Sheet createSheet() {
Sheet sheet = super.createSheet();
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
if (sheetSet == null) {
sheetSet = Sheet.createPropertiesSet();
sheet.put(sheetSet);
}
sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.artType.name"),
NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.artType.displayName"),
NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.artType.desc"),
type.getDisplayName()));
sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.childCnt.name"),
NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.childCnt.displayName"),
NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.childCnt.desc"),
childCount));
return sheet;
}
@Override
public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
return visitor.visit(this);
}
@Override
public boolean isLeafTypeNode() {
return true;
}
@Override
public String getItemType() {
return getClass().getName() + type.getDisplayName();
}
}
/**
* Creates children for a given artifact type
*/
private class ArtifactFactory extends BaseChildFactory<BlackboardArtifact> implements RefreshThrottler.Refresher {
private final BlackboardArtifact.Type type;
/**
* RefreshThrottler is used to limit the number of refreshes performed
* when CONTENT_CHANGED and DATA_ADDED ingest module events are
* received.
*/
private final RefreshThrottler refreshThrottler = new RefreshThrottler(this);
ArtifactFactory(BlackboardArtifact.Type type) {
super(type.getTypeName());
this.type = type;
}
private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> {
String eventType = evt.getPropertyName();
if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
|| eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
/**
* Checking for a current case is a stop gap measure until a
* different way of handling the closing of cases is worked out.
* Currently, remote events may be received for a case that is
* already closed.
*/
try {
Case.getCurrentCaseThrows();
refresh(false);
} catch (NoCurrentCaseException notUsed) {
/**
* Case is closed, do nothing.
*/
}
}
};
@Override
protected void onAdd() {
refreshThrottler.registerForIngestModuleEvents();
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl);
}
@Override
protected void onRemove() {
refreshThrottler.unregisterEventListener();
IngestManager.getInstance().removeIngestJobEventListener(pcl);
}
@Override
protected Node createNodeForKey(BlackboardArtifact key) {
return new BlackboardArtifactNode(key);
}
@Override
protected List<BlackboardArtifact> makeKeys() {
if (skCase != null) {
try {
List<BlackboardArtifact> arts;
if (filteringDSObjId > 0) {
arts = blackboard.getArtifacts(type.getTypeID(), filteringDSObjId);
} else {
arts = skCase.getBlackboardArtifacts(type.getTypeID());
}
for (BlackboardArtifact art : arts) {
//Cache attributes while we are off the EDT.
//See JIRA-5969
art.getAttributes();
}
return arts;
} catch (TskCoreException ex) {
Logger.getLogger(ArtifactFactory.class.getName()).log(Level.SEVERE, "Couldn't get blackboard artifacts from database", ex); //NON-NLS
}
}
return Collections.emptyList();
}
@Override
public void refresh() {
refresh(false);
}
@Override
public boolean isRefreshRequired(PropertyChangeEvent evt) {
String eventType = evt.getPropertyName();
if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
/**
* Checking for a current case is a stop gap measure until a
* different way of handling the closing of cases is worked out.
* Currently, remote events may be received for a case that is
* already closed.
*/
try {
Case.getCurrentCaseThrows();
/**
* Even with the check above, it is still possible that the
* case will be closed in a different thread before this
* code executes. If that happens, it is possible for the
* event to have a null oldValue.
*/
final ModuleDataEvent event = (ModuleDataEvent) evt.getOldValue();
if (null != event && event.getBlackboardArtifactType().equals(type)) {
return true;
}
} catch (NoCurrentCaseException notUsed) {
/**
* Case is closed, do nothing.
*/
}
}
return false;
}
}
}

View File

@ -46,19 +46,20 @@ import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_HASHSET_HIT;
import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.autopsy.datamodel.Artifacts.UpdatableCountTypeNode;
/**
* Hash set hits node support. Inner classes have all of the nodes in the tree.
*/
public class HashsetHits implements AutopsyVisitableItem {
private static final String HASHSET_HITS = BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getLabel();
private static final String DISPLAY_NAME = BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getDisplayName();
private static final String HASHSET_HITS = BlackboardArtifact.Type.TSK_HASHSET_HIT.getTypeName();
private static final String DISPLAY_NAME = BlackboardArtifact.Type.TSK_HASHSET_HIT.getDisplayName();
private static final Logger logger = Logger.getLogger(HashsetHits.class.getName());
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED);
private static final Set<IngestManager.IngestModuleEvent> INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestModuleEvent.DATA_ADDED);
@ -134,7 +135,7 @@ public class HashsetHits implements AutopsyVisitableItem {
}
int setNameId = ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID();
int artId = ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID();
int artId = TSK_HASHSET_HIT.getTypeID();
String query = "SELECT value_text,blackboard_attributes.artifact_id,attribute_type_id " //NON-NLS
+ "FROM blackboard_attributes,blackboard_artifacts WHERE " //NON-NLS
+ "attribute_type_id=" + setNameId //NON-NLS
@ -168,10 +169,15 @@ public class HashsetHits implements AutopsyVisitableItem {
/**
* Top-level node for all hash sets
*/
public class RootNode extends DisplayableItemNode {
public class RootNode extends UpdatableCountTypeNode {
public RootNode() {
super(Children.create(new HashsetNameFactory(), true), Lookups.singleton(DISPLAY_NAME));
super(Children.create(new HashsetNameFactory(), true),
Lookups.singleton(DISPLAY_NAME),
DISPLAY_NAME,
filteringDSObjId,
TSK_HASHSET_HIT);
super.setName(HASHSET_HITS);
super.setDisplayName(DISPLAY_NAME);
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/hashset_hits.png"); //NON-NLS
@ -239,7 +245,7 @@ public class HashsetHits implements AutopsyVisitableItem {
* oldValue if the event is a remote event.
*/
ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
if (null != eventData && eventData.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID()) {
if (null != eventData && eventData.getBlackboardArtifactType().getTypeID() == TSK_HASHSET_HIT.getTypeID()) {
hashsetResults.update();
}
} catch (NoCurrentCaseException notUsed) {

View File

@ -50,6 +50,7 @@ import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.autopsy.datamodel.Artifacts.UpdatableCountTypeNode;
public class InterestingHits implements AutopsyVisitableItem {
@ -110,8 +111,8 @@ public class InterestingHits implements AutopsyVisitableItem {
synchronized (interestingItemsMap) {
interestingItemsMap.clear();
}
loadArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT);
loadArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT);
loadArtifacts(BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT);
loadArtifacts(BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT);
setChanged();
notifyObservers();
}
@ -121,7 +122,7 @@ public class InterestingHits implements AutopsyVisitableItem {
* the interestingItemsMap
*/
@SuppressWarnings("deprecation")
private void loadArtifacts(BlackboardArtifact.ARTIFACT_TYPE artType) {
private void loadArtifacts(BlackboardArtifact.Type artType) {
if (skCase == null) {
return;
}
@ -145,8 +146,8 @@ public class InterestingHits implements AutopsyVisitableItem {
long artifactId = resultSet.getLong("artifact_id"); //NON-NLS
if (!interestingItemsMap.containsKey(value)) {
interestingItemsMap.put(value, new LinkedHashMap<>());
interestingItemsMap.get(value).put(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getDisplayName(), new HashSet<>());
interestingItemsMap.get(value).put(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getDisplayName(), new HashSet<>());
interestingItemsMap.get(value).put(BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT.getDisplayName(), new HashSet<>());
interestingItemsMap.get(value).put(BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT.getDisplayName(), new HashSet<>());
}
interestingItemsMap.get(value).get(artType.getDisplayName()).add(artifactId);
}
@ -165,12 +166,16 @@ public class InterestingHits implements AutopsyVisitableItem {
/**
* Node for the interesting items
*/
public class RootNode extends DisplayableItemNode {
public class RootNode extends UpdatableCountTypeNode {
public RootNode() {
super(Children.create(new SetNameFactory(), true), Lookups.singleton(DISPLAY_NAME));
super(Children.create(new SetNameFactory(), true),
Lookups.singleton(DISPLAY_NAME),
DISPLAY_NAME,
filteringDSObjId,
BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT,
BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT);
super.setName(INTERESTING_ITEMS);
super.setDisplayName(DISPLAY_NAME);
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/interesting_item.png"); //NON-NLS
}
@ -232,8 +237,8 @@ public class InterestingHits implements AutopsyVisitableItem {
* event to have a null oldValue.
*/
ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
if (null != eventData && (eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()
|| eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID())) {
if (null != eventData && (eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()
|| eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT.getTypeID())) {
interestingResults.update();
}
} catch (NoCurrentCaseException notUsed) {
@ -314,8 +319,8 @@ public class InterestingHits implements AutopsyVisitableItem {
}
private void updateDisplayName() {
int sizeOfSet = interestingResults.getArtifactIds(setName, BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getDisplayName()).size()
+ interestingResults.getArtifactIds(setName, BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getDisplayName()).size();
int sizeOfSet = interestingResults.getArtifactIds(setName, BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT.getDisplayName()).size()
+ interestingResults.getArtifactIds(setName, BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT.getDisplayName()).size();
super.setDisplayName(setName + " (" + sizeOfSet + ")");
}
@ -374,8 +379,8 @@ public class InterestingHits implements AutopsyVisitableItem {
@Override
protected boolean createKeys(List<String> list) {
list.add(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getDisplayName());
list.add(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getDisplayName());
list.add(BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT.getDisplayName());
list.add(BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT.getDisplayName());
return true;
}
@ -400,8 +405,8 @@ public class InterestingHits implements AutopsyVisitableItem {
this.typeName = typeName;
this.setName = setName;
/**
* We use the combination of setName and typeName as the name of
* the node to ensure that nodes have a unique name. This comes into
* We use the combination of setName and typeName as the name of the
* node to ensure that nodes have a unique name. This comes into
* play when associating paging state with the node.
*/
super.setName(setName + "_" + typeName);
@ -462,9 +467,9 @@ public class InterestingHits implements AutopsyVisitableItem {
private HitFactory(String setName, String typeName) {
/**
* The node name passed to the parent constructor must be the
* same as the name set in the InterestingItemTypeNode constructor,
* i.e. setName underscore typeName
* The node name passed to the parent constructor must be the same
* as the name set in the InterestingItemTypeNode constructor, i.e.
* setName underscore typeName
*/
super(setName + "_" + typeName);
this.setName = setName;

View File

@ -55,6 +55,8 @@ import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery;
import org.sleuthkit.datamodel.TskCoreException;
import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_KEYWORD_HIT;
import org.sleuthkit.autopsy.datamodel.Artifacts.UpdatableCountTypeNode;
/**
* Keyword hits node support
@ -71,7 +73,7 @@ public class KeywordHits implements AutopsyVisitableItem {
@NbBundle.Messages("KeywordHits.singleRegexSearch.text=Single Regular Expression Search")
private static final String SIMPLE_REGEX_SEARCH = KeywordHits_singleRegexSearch_text();
public static final String NAME = BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getLabel();
public static final String NAME = BlackboardArtifact.Type.TSK_KEYWORD_HIT.getTypeName();
private SleuthkitCase skCase;
private final KeywordResults keywordResults;
@ -93,7 +95,7 @@ public class KeywordHits implements AutopsyVisitableItem {
+ "blackboard_attributes.attribute_type_id "//NON-NLS
+ "FROM blackboard_attributes, blackboard_artifacts "//NON-NLS
+ "WHERE blackboard_attributes.artifact_id = blackboard_artifacts.artifact_id "//NON-NLS
+ " AND blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID() //NON-NLS
+ " AND blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.Type.TSK_KEYWORD_HIT.getTypeID() //NON-NLS
+ " AND (attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()//NON-NLS
+ " OR attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID()//NON-NLS
+ " OR attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE.getTypeID()//NON-NLS
@ -375,12 +377,16 @@ public class KeywordHits implements AutopsyVisitableItem {
}
// Created by CreateAutopsyNodeVisitor
public class RootNode extends DisplayableItemNode {
public class RootNode extends UpdatableCountTypeNode {
public RootNode() {
super(Children.create(new ListFactory(), true), Lookups.singleton(KEYWORD_HITS));
super(Children.create(new ListFactory(), true),
Lookups.singleton(KEYWORD_HITS),
KEYWORD_HITS,
filteringDSObjId,
TSK_KEYWORD_HIT);
super.setName(NAME);
super.setDisplayName(KEYWORD_HITS);
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/keyword_hits.png"); //NON-NLS
}
@ -464,7 +470,7 @@ public class KeywordHits implements AutopsyVisitableItem {
* for the event to have a null oldValue.
*/
ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
if (null != eventData && eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()) {
if (null != eventData && eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.Type.TSK_KEYWORD_HIT.getTypeID()) {
keywordResults.update();
}
} catch (NoCurrentCaseException notUsed) {

View File

@ -1,52 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.datamodel;
import org.sleuthkit.datamodel.SleuthkitCase;
/**
* Results node support
*/
public class Results implements AutopsyVisitableItem {
private SleuthkitCase skCase;
private final long datasourceObjId;
public Results(SleuthkitCase skCase) {
this(skCase, 0);
}
public Results(SleuthkitCase skCase, long dsObjId) {
this.skCase = skCase;
this.datasourceObjId = dsObjId;
}
@Override
public <T> T accept(AutopsyItemVisitor<T> visitor) {
return visitor.visit(this);
}
public SleuthkitCase getSleuthkitCase() {
return skCase;
}
long filteringDataSourceObjId() {
return datasourceObjId;
}
}

View File

@ -1,97 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.datamodel;
import org.sleuthkit.autopsy.datamodel.accounts.Accounts;
import java.util.Arrays;
import org.openide.nodes.Sheet;
import org.openide.util.NbBundle;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.datamodel.SleuthkitCase;
/**
* Node for the results section of the tree.
*/
public class ResultsNode extends DisplayableItemNode {
@NbBundle.Messages("ResultsNode.name.text=Results")
private static final String NAME = Bundle.ResultsNode_name_text();
public static String getNameIdentifier() {
return NAME;
}
public ResultsNode(SleuthkitCase sleuthkitCase) {
this(sleuthkitCase, 0);
}
public ResultsNode(SleuthkitCase sleuthkitCase, long dsObjId) {
super(
new RootContentChildren(Arrays.asList(
new ExtractedContent(sleuthkitCase, dsObjId ),
new KeywordHits(sleuthkitCase, dsObjId),
new HashsetHits(sleuthkitCase, dsObjId),
new EmailExtracted(sleuthkitCase, dsObjId),
new InterestingHits(sleuthkitCase, dsObjId ),
new Accounts(sleuthkitCase, dsObjId),
new OsAccounts(sleuthkitCase, dsObjId))
),
Lookups.singleton(NAME));
setName(NAME);
setDisplayName(NAME);
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/results.png"); //NON-NLS
}
@Override
public boolean isLeafTypeNode() {
return false;
}
@Override
public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
return visitor.visit(this);
}
@Override
@NbBundle.Messages({
"ResultsNode.createSheet.name.name=Name",
"ResultsNode.createSheet.name.displayName=Name",
"ResultsNode.createSheet.name.desc=no description"})
protected Sheet createSheet() {
Sheet sheet = super.createSheet();
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
if (sheetSet == null) {
sheetSet = Sheet.createPropertiesSet();
sheet.put(sheetSet);
}
sheetSet.put(new NodeProperty<>(Bundle.ResultsNode_createSheet_name_name(),
Bundle.ResultsNode_createSheet_name_displayName(),
Bundle.ResultsNode_createSheet_name_desc(),
NAME
));
return sheet;
}
@Override
public String getItemType() {
return getClass().getName();
}
}

View File

@ -83,11 +83,6 @@ public class RootContentChildren extends Children.Keys<Object> {
*/
static class CreateAutopsyNodeVisitor extends AutopsyItemVisitor.Default<AbstractNode> {
@Override
public ExtractedContent.RootNode visit(ExtractedContent ec) {
return ec.new RootNode(ec.getSleuthkitCase());
}
@Override
public AbstractNode visit(FileTypesByExtension sf) {
return sf.new FileTypesByExtNode(sf.getSleuthkitCase(), null);
@ -148,11 +143,6 @@ public class RootContentChildren extends Children.Keys<Object> {
return new ViewsNode(v.getSleuthkitCase(), v.filteringDataSourceObjId());
}
@Override
public AbstractNode visit(Results results) {
return new ResultsNode(results.getSleuthkitCase(), results.filteringDataSourceObjId());
}
@Override
public AbstractNode visit(FileTypes ft) {
return ft.new FileTypesNode();
@ -204,5 +194,17 @@ public class RootContentChildren extends Children.Keys<Object> {
public AbstractNode visit(DataSourcesByType dataSourceHosts) {
return new DataSourcesByTypeNode();
}
@Override
public AbstractNode visit(AnalysisResults analysisResults) {
return new AnalysisResults.RootNode(
analysisResults.getFilteringDataSourceObjId());
}
@Override
public AbstractNode visit(DataArtifacts dataArtifacts) {
return new DataArtifacts.RootNode(
dataArtifacts.getFilteringDataSourceObjId());
}
}
}

View File

@ -69,6 +69,7 @@ import org.sleuthkit.autopsy.datamodel.CreditCards;
import org.sleuthkit.autopsy.datamodel.DataModelActionsFactory;
import org.sleuthkit.autopsy.datamodel.DisplayableItemNode;
import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor;
import org.sleuthkit.autopsy.datamodel.Artifacts.UpdatableCountTypeNode;
import org.sleuthkit.autopsy.datamodel.NodeProperty;
import org.sleuthkit.autopsy.directorytree.DirectoryTreeTopComponent;
import org.sleuthkit.autopsy.ingest.IngestManager;
@ -76,7 +77,8 @@ import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Account;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import org.sleuthkit.datamodel.BlackboardArtifact.Type;
import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_ACCOUNT;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.SleuthkitCase;
@ -93,6 +95,7 @@ final public class Accounts implements AutopsyVisitableItem {
private static final String ICON_BASE_PATH = "/org/sleuthkit/autopsy/images/"; //NON-NLS
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED);
private static final Set<IngestManager.IngestModuleEvent> INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestModuleEvent.DATA_ADDED);
private static final String DISPLAY_NAME = Bundle.Accounts_RootNode_displayName();
@NbBundle.Messages("AccountsRootNode.name=Accounts")
final public static String NAME = Bundle.AccountsRootNode_name();
@ -231,12 +234,16 @@ final public class Accounts implements AutopsyVisitableItem {
* Top-level node for the accounts tree
*/
@NbBundle.Messages({"Accounts.RootNode.displayName=Accounts"})
final public class AccountsRootNode extends DisplayableItemNode {
final public class AccountsRootNode extends UpdatableCountTypeNode {
public AccountsRootNode() {
super(Children.create(new AccountTypeFactory(), true), Lookups.singleton(Accounts.this));
super(Children.create(new AccountTypeFactory(), true),
Lookups.singleton(Accounts.this),
DISPLAY_NAME,
filteringDSObjId,
TSK_ACCOUNT);
setName(Accounts.NAME);
setDisplayName(Bundle.Accounts_RootNode_displayName());
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/accounts.png"); //NON-NLS
}
@ -254,12 +261,42 @@ final public class Accounts implements AutopsyVisitableItem {
public String getItemType() {
return getClass().getName();
}
@Override
protected long fetchChildCount(SleuthkitCase skCase) throws TskCoreException {
long count = 0;
String dataSourceFilterClause = (filteringDSObjId > 0)
? " AND " + filteringDSObjId + " IN (SELECT art.data_source_obj_id FROM blackboard_artifacts art WHERE art.artifact_id = attr.artifact_id)"
: "";
String accountTypesInUseQuery
= "SELECT COUNT(attr.value_text) AS count"
+ " FROM blackboard_attributes attr"
+ " WHERE attr.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID()
+ " AND attr.artifact_type_id = " + BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID()
+ dataSourceFilterClause
+ " GROUP BY attr.value_text";
try (SleuthkitCase.CaseDbQuery executeQuery = skCase.executeQuery(accountTypesInUseQuery);
ResultSet resultSet = executeQuery.getResultSet()) {
if (resultSet.next()) {
count = resultSet.getLong("count");
}
} catch (TskCoreException | SQLException ex) {
LOGGER.log(Level.SEVERE, "Error querying for count of all account types", ex);
}
return count;
}
}
/**
* Tracks the account types and the number of account types found.
*/
private class AccountTypeResults {
private final Map<String, Long> counts = new HashMap<>();
AccountTypeResults() {
@ -267,8 +304,11 @@ final public class Accounts implements AutopsyVisitableItem {
}
/**
* Given the type name of the Account.Type, provides the count of those type.
* Given the type name of the Account.Type, provides the count of those
* type.
*
* @param accountType The type name of the Account.Type.
*
* @return The number of results found for the given account type.
*/
Long getCount(String accountType) {
@ -277,6 +317,7 @@ final public class Accounts implements AutopsyVisitableItem {
/**
* Retrieves an alphabetically organized list of all the account types.
*
* @return An alphabetically organized list of all the account types.
*/
List<String> getTypes() {
@ -293,7 +334,7 @@ final public class Accounts implements AutopsyVisitableItem {
= "SELECT blackboard_attributes.value_text as account_type, COUNT(*) as count "
+ " FROM blackboard_artifacts " //NON-NLS
+ " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
+ " WHERE blackboard_artifacts.artifact_type_id = " + TSK_ACCOUNT.getTypeID() //NON-NLS
+ " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS
+ getFilterByDataSourceClause()
+ " GROUP BY blackboard_attributes.value_text ";
@ -343,7 +384,7 @@ final public class Accounts implements AutopsyVisitableItem {
*/
ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
if (null != eventData
&& eventData.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) {
&& eventData.getBlackboardArtifactType().getTypeID() == Type.TSK_ACCOUNT.getTypeID()) {
accountTypeResults.update();
reviewStatusBus.post(eventData);
}
@ -393,9 +434,11 @@ final public class Accounts implements AutopsyVisitableItem {
}
/**
* Registers the given node with the reviewStatusBus and returns
* the node wrapped in an array.
* Registers the given node with the reviewStatusBus and returns the
* node wrapped in an array.
*
* @param node The node to be wrapped.
*
* @return The array containing this node.
*/
private Node[] getNodeArr(Node node) {
@ -476,7 +519,7 @@ final public class Accounts implements AutopsyVisitableItem {
*/
ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
if (null != eventData
&& eventData.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) {
&& eventData.getBlackboardArtifactType().getTypeID() == Type.TSK_ACCOUNT.getTypeID()) {
reviewStatusBus.post(eventData);
}
} catch (NoCurrentCaseException notUsed) {
@ -529,7 +572,7 @@ final public class Accounts implements AutopsyVisitableItem {
= "SELECT blackboard_artifacts.artifact_id " //NON-NLS
+ " FROM blackboard_artifacts " //NON-NLS
+ " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID() //NON-NLS
+ " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS
+ " AND blackboard_attributes.value_text = '" + accountType.getTypeName() + "'" //NON-NLS
+ getFilterByDataSourceClause()
@ -576,6 +619,7 @@ final public class Accounts implements AutopsyVisitableItem {
* no special behavior.
*/
final public class DefaultAccountTypeNode extends DisplayableItemNode {
private final Account.Type accountType;
private DefaultAccountTypeNode(Account.Type accountType) {
@ -583,6 +627,7 @@ final public class Accounts implements AutopsyVisitableItem {
this.accountType = accountType;
String iconPath = getIconFilePath(accountType);
this.setIconBaseWithExtension(iconPath != null && iconPath.charAt(0) == '/' ? iconPath.substring(1) : iconPath); //NON-NLS
setName(accountType.getTypeName());
updateName();
}
@ -601,7 +646,6 @@ final public class Accounts implements AutopsyVisitableItem {
return getClass().getName();
}
@Subscribe
void handleReviewStatusChange(ReviewStatusChangeEvent event) {
updateName();
@ -613,10 +657,11 @@ final public class Accounts implements AutopsyVisitableItem {
}
/**
* Gets the latest counts for the account type and then updates the name.
* Gets the latest counts for the account type and then updates the
* name.
*/
public void updateName() {
setName(String.format("%s (%d)", accountType.getDisplayName(), accountTypeResults.getCount(accountType.getTypeName())));
setDisplayName(String.format("%s (%d)", accountType.getDisplayName(), accountTypeResults.getCount(accountType.getTypeName())));
}
}
@ -651,7 +696,7 @@ final public class Accounts implements AutopsyVisitableItem {
*/
ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
if (null != eventData
&& eventData.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) {
&& eventData.getBlackboardArtifactType().getTypeID() == Type.TSK_ACCOUNT.getTypeID()) {
reviewStatusBus.post(eventData);
}
} catch (NoCurrentCaseException notUsed) {
@ -749,7 +794,8 @@ final public class Accounts implements AutopsyVisitableItem {
}
/**
* Gets the latest counts for the account type and then updates the name.
* Gets the latest counts for the account type and then updates the
* name.
*/
public void updateName() {
setName(String.format("%s (%d)", Account.Type.CREDIT_CARD.getDisplayName(), accountTypeResults.getCount(Account.Type.CREDIT_CARD.getTypeName())));
@ -804,7 +850,7 @@ final public class Accounts implements AutopsyVisitableItem {
*/
ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
if (null != eventData
&& eventData.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) {
&& eventData.getBlackboardArtifactType().getTypeID() == Type.TSK_ACCOUNT.getTypeID()) {
reviewStatusBus.post(eventData);
}
} catch (NoCurrentCaseException notUsed) {
@ -882,7 +928,7 @@ final public class Accounts implements AutopsyVisitableItem {
+ " LEFT JOIN blackboard_attributes as account_type ON blackboard_artifacts.artifact_id = account_type.artifact_id " //NON-NLS
+ " AND account_type.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS
+ " AND account_type.value_text = '" + Account.Type.CREDIT_CARD.getTypeName() + "'" //NON-NLS
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID() //NON-NLS
+ getFilterByDataSourceClause()
+ getRejectedArtifactFilterClause()
+ " GROUP BY blackboard_artifacts.obj_id, solr_document_id " //NON-NLS
@ -951,7 +997,7 @@ final public class Accounts implements AutopsyVisitableItem {
+ " LEFT JOIN blackboard_attributes as account_type ON blackboard_artifacts.artifact_id = account_type.artifact_id " //NON-NLS
+ " AND account_type.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS
+ " AND account_type.value_text = '" + Account.Type.CREDIT_CARD.getTypeName() + "'" //NON-NLS
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID() //NON-NLS
+ getFilterByDataSourceClause()
+ getRejectedArtifactFilterClause()
+ " GROUP BY blackboard_artifacts.obj_id, solr_attribute.value_text ) AS foo";
@ -1019,7 +1065,7 @@ final public class Accounts implements AutopsyVisitableItem {
*/
ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
if (null != eventData
&& eventData.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) {
&& eventData.getBlackboardArtifactType().getTypeID() == Type.TSK_ACCOUNT.getTypeID()) {
reviewStatusBus.post(eventData);
}
} catch (NoCurrentCaseException notUsed) { //NOPMD empy catch clause
@ -1087,7 +1133,7 @@ final public class Accounts implements AutopsyVisitableItem {
+ " COUNT(blackboard_artifacts.artifact_id) AS count " //NON-NLS
+ " FROM blackboard_artifacts " //NON-NLS
+ " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id" //NON-NLS
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID() //NON-NLS
+ " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS
+ getFilterByDataSourceClause()
+ getRejectedArtifactFilterClause()
@ -1154,7 +1200,7 @@ final public class Accounts implements AutopsyVisitableItem {
= "SELECT count(distinct SUBSTR(blackboard_attributes.value_text,1,8)) AS BINs " //NON-NLS
+ " FROM blackboard_artifacts " //NON-NLS
+ " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id" //NON-NLS
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID() //NON-NLS
+ " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS
+ getFilterByDataSourceClause()
+ getRejectedArtifactFilterClause(); //NON-NLS
@ -1449,7 +1495,7 @@ final public class Accounts implements AutopsyVisitableItem {
= "SELECT blackboard_artifacts.artifact_id " //NON-NLS
+ " FROM blackboard_artifacts " //NON-NLS
+ " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID() //NON-NLS
+ " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS
+ " AND blackboard_attributes.value_text >= '" + bin.getBINStart() + "' AND blackboard_attributes.value_text < '" + (bin.getBINEnd() + 1) + "'" //NON-NLS
+ getFilterByDataSourceClause()
@ -1523,7 +1569,7 @@ final public class Accounts implements AutopsyVisitableItem {
= "SELECT count(blackboard_artifacts.artifact_id ) AS count" //NON-NLS
+ " FROM blackboard_artifacts " //NON-NLS
+ " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID() //NON-NLS
+ " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS
+ " AND blackboard_attributes.value_text >= '" + bin.getBINStart() + "' AND blackboard_attributes.value_text < '" + (bin.getBINEnd() + 1) + "'" //NON-NLS
+ getFilterByDataSourceClause()

View File

@ -32,6 +32,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.prefs.PreferenceChangeEvent;
@ -72,18 +73,18 @@ import org.sleuthkit.autopsy.corecomponents.TableFilterNode;
import org.sleuthkit.autopsy.corecomponents.ViewPreferencesPanel;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
import org.sleuthkit.autopsy.datamodel.AnalysisResults;
import org.sleuthkit.autopsy.datamodel.ArtifactNodeSelectionInfo;
import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode;
import org.sleuthkit.autopsy.datamodel.CreditCards;
import org.sleuthkit.autopsy.datamodel.DisplayableItemNode;
import org.sleuthkit.autopsy.datamodel.EmailExtracted;
import org.sleuthkit.autopsy.datamodel.EmptyNode;
import org.sleuthkit.autopsy.datamodel.ExtractedContent;
import org.sleuthkit.autopsy.datamodel.FileTypesByMimeType;
import org.sleuthkit.autopsy.datamodel.InterestingHits;
import org.sleuthkit.autopsy.datamodel.KeywordHits;
import org.sleuthkit.autopsy.datamodel.ResultsNode;
import org.sleuthkit.autopsy.datamodel.AutopsyTreeChildFactory;
import org.sleuthkit.autopsy.datamodel.DataArtifacts;
import org.sleuthkit.autopsy.datamodel.PersonGroupingNode;
import org.sleuthkit.autopsy.datamodel.Tags;
import org.sleuthkit.autopsy.datamodel.ViewsNode;
@ -91,6 +92,7 @@ import org.sleuthkit.autopsy.datamodel.accounts.Accounts;
import org.sleuthkit.autopsy.datamodel.accounts.BINRange;
import org.sleuthkit.datamodel.Account;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardArtifact.Category;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.DataSource;
@ -199,13 +201,15 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
*/
private void preExpandNodes(Children rootChildren) {
BeanTreeView tree = getTree();
for (String categoryKey : new String[]{AnalysisResults.getName(), DataArtifacts.getName()}) {
Node categoryNode = rootChildren.findChild(categoryKey);
Node results = rootChildren.findChild(ResultsNode.getNameIdentifier());
if (!Objects.isNull(results)) {
tree.expandNode(results);
Children resultsChildren = results.getChildren();
if (!Objects.isNull(categoryNode)) {
tree.expandNode(categoryNode);
Children resultsChildren = categoryNode.getChildren();
Arrays.stream(resultsChildren.getNodes()).forEach(tree::expandNode);
}
}
Node views = rootChildren.findChild(ViewsNode.NAME);
if (!Objects.isNull(views)) {
@ -224,10 +228,12 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
}
}
/**
* Returns all nodes including provided node that are parents of or are hosts.
* Returns all nodes including provided node that are parents of or are
* hosts.
*
* @param node The parent or possible host node.
*
* @return The descendant host nodes.
*/
private List<Node> getHostNodesAndParents(Node node) {
@ -1054,7 +1060,8 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
* Set the selected node using a path to a previously selected node.
*
* @param previouslySelectedNodePath Path to a previously selected node.
* @param rootNodeName Name of the root node to match, may be null.
* @param rootNodeName Name of the root node to match, may be
* null.
*/
private void setSelectedNode(final String[] previouslySelectedNodePath, final String rootNodeName) {
if (previouslySelectedNodePath == null) {
@ -1112,53 +1119,82 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
}
/**
* Does dfs search of node while nodes are Host, Person, or
* DataSourcesByType looking for the Results Node.
* Returns the node matching the given category that is an immediate child
* of the provided Children object or empty if no immediate child matches
* the given category.
*
* @param node The node.
* @param dataSourceId The data source id.
* @return The child nodes that are at the data source level.
* @param children The children to search.
* @param category The category to find.
*
* @return The node matching the given category
*/
private Node getResultsNodeSearch(Node node, long dataSourceId) {
if (node == null) {
return null;
} else if (node.getLookup().lookup(Host.class) != null
|| node.getLookup().lookup(Person.class) != null
|| PersonGroupingNode.getUnknownPersonId().equals(node.getLookup().lookup(String.class))) {
Children children = node.getChildren();
Node[] childNodes = children == null ? null : children.getNodes();
if (childNodes != null) {
for (Node child : childNodes) {
Node foundExtracted = getResultsNodeSearch(child, dataSourceId);
if (foundExtracted != null) {
return foundExtracted;
private Optional<Node> getCategoryNodeChild(Children children, Category category) {
switch (category) {
case DATA_ARTIFACT:
return Optional.ofNullable(children.findChild(DataArtifacts.getName()));
case ANALYSIS_RESULT:
return Optional.ofNullable(children.findChild(AnalysisResults.getName()));
default:
LOGGER.log(Level.WARNING, "Unbale to find category of type: " + category.name());
return Optional.empty();
}
}
}
} else {
DataSource dataSource = node.getLookup().lookup(DataSource.class);
if (dataSource != null && dataSource.getId() == dataSourceId) {
Children dsChildren = node.getChildren();
if (dsChildren != null) {
return dsChildren.findChild(ResultsNode.getNameIdentifier());
}
}
}
return null;
}
/**
* Finds the results node for the specific artifact.
* Does depth first search of node while nodes are Host, Person, or
* DataSourcesByType looking for the appropriate category Node (i.e. the
* Data Artifacts or Analysis Results nodes).
*
* @param art The artifact to find the relevant Results Node.
* @return THe Results Node or null.
* @param node The node.
* @param dataSourceId The data source id.
* @param category The artifact type category.
*
* @return The child nodes that are at the data source level.
*/
private Node getResultsNode(final BlackboardArtifact art) {
Children rootChilds = em.getRootContext().getChildren();
private Optional<Node> searchForCategoryNode(Node node, long dataSourceId, Category category) {
if (node == null) {
// if no node, no result
return Optional.empty();
} else if (node.getLookup().lookup(Host.class) != null
|| node.getLookup().lookup(Person.class) != null
|| PersonGroupingNode.getUnknownPersonId().equals(node.getLookup().lookup(String.class))) {
// if host or person node, recurse until we find correct data source node.
Children children = node.getChildren();
Node resultsNode = rootChilds.findChild(ResultsNode.getNameIdentifier());
if (resultsNode != null) {
return resultsNode;
Stream<Node> childNodeStream = children == null ? Stream.empty() : Stream.of(children.getNodes());
return childNodeStream
.map(childNode -> searchForCategoryNode(childNode, dataSourceId, category))
.filter(Optional::isPresent)
.map(Optional::get)
.findFirst();
} else {
DataSource dataSource = node.getLookup().lookup(DataSource.class);
// if data source node and the one we want, find the right category node.
if (dataSource != null && dataSource.getId() == dataSourceId) {
Children dsChildren = node.getChildren();
if (dsChildren != null) {
return getCategoryNodeChild(dsChildren, category);
}
}
return Optional.empty();
}
}
/**
* Finds the category node (i.e. Data Artifacts / Analysis Results) for the
* specific artifact and category.
*
* @param category The category of the artifact.
* @param art The artifact to find the relevant Results Node.
*
* @return The category node or empty.
*/
private Optional<Node> getCategoryNode(Category category, BlackboardArtifact art) {
Children rootChildren = em.getRootContext().getChildren();
Optional<Node> categoryNode = getCategoryNodeChild(rootChildren, category);
if (categoryNode.isPresent()) {
return categoryNode;
}
long dataSourceId;
@ -1169,17 +1205,31 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
return null;
}
Node[] rootNodes = rootChilds.getNodes();
if (rootNodes != null) {
for (Node rootNode : rootNodes) {
resultsNode = getResultsNodeSearch(rootNode, dataSourceId);
if (resultsNode != null) {
return resultsNode;
}
}
Node[] rootNodes = rootChildren.getNodes();
Stream<Node> rootNodesStream = rootNodes == null ? Stream.empty() : Stream.of(rootNodes);
return rootNodesStream
.map((rootNode) -> searchForCategoryNode(rootNode, dataSourceId, category))
.filter(Optional::isPresent)
.map(Optional::get)
.findFirst();
}
return null;
/**
* Attempts to retrieve the artifact type for the given artifact type id.
*
* @param artifactTypeId The artifact type id.
*
* @return The artifact type if present or empty if not found.
*/
private Optional<BlackboardArtifact.Type> getType(long artifactTypeId) {
try {
return Case.getCurrentCaseThrows().getSleuthkitCase().getArtifactTypesInUse().stream()
.filter(type -> type.getTypeID() == artifactTypeId)
.findFirst();
} catch (NoCurrentCaseException | TskCoreException ex) {
LOGGER.log(Level.WARNING, "Error occurred while looking up blackboard artifact type for: " + artifactTypeId, ex);
return Optional.empty();
}
}
/**
@ -1196,16 +1246,69 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
public void viewArtifact(final BlackboardArtifact art) {
int typeID = art.getArtifactTypeID();
String typeName = art.getArtifactTypeName();
Node treeNode = null;
Node resultsNode = getResultsNode(art);
if (resultsNode == null) {
Optional<BlackboardArtifact.Type> typeOpt = getType(typeID);
Optional<Children> categoryChildrenOpt = typeOpt
.flatMap(type -> getCategoryNode(type.getCategory(), art))
.flatMap(categoryNode -> Optional.ofNullable(categoryNode.getChildren()));
if (!categoryChildrenOpt.isPresent()) {
LOGGER.log(Level.WARNING, String.format("Category node children for artifact of typeID: %d and artifactID: %d not found.",
typeID, art.getArtifactID()));
return;
}
Children resultsChilds = resultsNode.getChildren();
if (typeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID()) {
Node hashsetRootNode = resultsChilds.findChild(typeName);
Children typesChildren = categoryChildrenOpt.get();
Node treeNode = null;
if (typeID == BlackboardArtifact.Type.TSK_HASHSET_HIT.getTypeID()) {
treeNode = getHashsetNode(typesChildren, art);
} else if (typeID == BlackboardArtifact.Type.TSK_KEYWORD_HIT.getTypeID()) {
treeNode = getKeywordHitNode(typesChildren, art);
} else if (typeID == BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT.getTypeID()
|| typeID == BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()) {
treeNode = getInterestingItemNode(typesChildren, art);
} else if (typeID == BlackboardArtifact.Type.TSK_EMAIL_MSG.getTypeID()) {
treeNode = getEmailNode(typesChildren, art);
} else if (typeID == BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID()) {
treeNode = getAccountNode(typesChildren, art);
} else {
treeNode = typesChildren.findChild(typeName);
}
if (treeNode == null) {
return;
}
DisplayableItemNode undecoratedParentNode = (DisplayableItemNode) ((DirectoryTreeFilterNode) treeNode).getOriginal();
undecoratedParentNode.setChildNodeSelectionInfo(new ArtifactNodeSelectionInfo(art));
getTree().expandNode(treeNode);
if (this.getSelectedNode().equals(treeNode)) {
this.setDirectoryListingActive();
this.respondSelection(em.getSelectedNodes(), new Node[]{treeNode});
} else {
try {
em.setExploredContextAndSelection(treeNode, new Node[]{treeNode});
} catch (PropertyVetoException ex) {
LOGGER.log(Level.WARNING, "Property Veto: ", ex); //NON-NLS
}
}
// Another thread is needed because we have to wait for dataResult to populate
}
/**
* Returns the hashset hit artifact's parent node or null if cannot be
* found.
*
* @param typesChildren The children object of the same category as hashset
* hits.
* @param art The artifact.
*
* @return The hashset hit artifact's parent node or null if cannot be
* found.
*/
private Node getHashsetNode(Children typesChildren, final BlackboardArtifact art) {
Node hashsetRootNode = typesChildren.findChild(art.getArtifactTypeName());
Children hashsetRootChilds = hashsetRootNode.getChildren();
try {
String setName = null;
@ -1216,12 +1319,26 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
setName = att.getValueString();
}
}
treeNode = hashsetRootChilds.findChild(setName);
return hashsetRootChilds.findChild(setName);
} catch (TskCoreException ex) {
LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS
return null;
}
} else if (typeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()) {
Node keywordRootNode = resultsChilds.findChild(typeName);
}
/**
* Returns the keyword hit artifact's parent node or null if cannot be
* found.
*
* @param typesChildren The children object of the same category as keyword
* hits.
* @param art The artifact.
*
* @return The keyword hit artifact's parent node or null if cannot be
* found.
*/
private Node getKeywordHitNode(Children typesChildren, BlackboardArtifact art) {
Node keywordRootNode = typesChildren.findChild(art.getArtifactTypeName());
Children keywordRootChilds = keywordRootNode.getChildren();
try {
String listName = null;
@ -1247,31 +1364,44 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
}
Node listNode = keywordRootChilds.findChild(listName);
if (listNode == null) {
return;
return null;
}
Children listChildren = listNode.getChildren();
if (listChildren == null) {
return;
return null;
}
if (regex != null) { //For support of regex nodes such as URLs, IPs, Phone Numbers, and Email Addrs as they are down another level
Node regexNode = listChildren.findChild(regex);
Node regexNode = listChildren.findChild(listName);
regexNode = (regexNode == null) ? listChildren.findChild(listName + "_" + regex) : regexNode;
if (regexNode == null) {
return;
return null;
}
listChildren = regexNode.getChildren();
if (listChildren == null) {
return;
return null;
}
}
treeNode = listChildren.findChild(keywordName);
return listChildren.findChild(keywordName);
} catch (TskCoreException ex) {
LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS
return null;
}
} else if (typeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID()
|| typeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()) {
Node interestingItemsRootNode = resultsChilds.findChild(NbBundle
}
/**
* Returns the interesting item artifact's parent node or null if cannot be
* found.
*
* @param typesChildren The children object of the same category as
* interesting item.
* @param art The artifact.
*
* @return The interesting item artifact's parent node or null if cannot be
* found.
*/
private Node getInterestingItemNode(Children typesChildren, BlackboardArtifact art) {
Node interestingItemsRootNode = typesChildren.findChild(NbBundle
.getMessage(InterestingHits.class, "InterestingHits.interestingItems.text"));
Children interestingItemsRootChildren = interestingItemsRootNode.getChildren();
try {
@ -1285,23 +1415,34 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
}
Node setNode = interestingItemsRootChildren.findChild(setName);
if (setNode == null) {
return;
return null;
}
Children fileArtifactChildren = setNode.getChildren();
Node[] fileArtifactNodes = fileArtifactChildren == null ? null : fileArtifactChildren.getNodes();
if (fileArtifactNodes == null || fileArtifactNodes.length != 2) {
return;
return null;
}
treeNode = (typeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID())
return (art.getArtifactTypeID() == BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT.getTypeID())
? fileArtifactNodes[0]
: fileArtifactNodes[1];
} catch (TskCoreException ex) {
LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS
return null;
}
} else if (typeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()) {
Node emailMsgRootNode = resultsChilds.findChild(typeName);
}
/**
* Returns the email artifact's parent node or null if cannot be found.
*
* @param typesChildren The children object of the same category as email.
* @param art The artifact.
*
* @return The email artifact's parent node or null if cannot be found.
*/
private Node getEmailNode(Children typesChildren, BlackboardArtifact art) {
Node emailMsgRootNode = typesChildren.findChild(art.getArtifactTypeName());
Children emailMsgRootChilds = emailMsgRootNode.getChildren();
Map<String, String> parsedPath = null;
try {
@ -1314,17 +1455,28 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
}
}
if (parsedPath == null) {
return;
return null;
}
Node defaultNode = emailMsgRootChilds.findChild(parsedPath.get(NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.defaultAcct.text")));
Children defaultChildren = defaultNode.getChildren();
treeNode = defaultChildren.findChild(parsedPath.get(NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.defaultFolder.text")));
return defaultChildren.findChild(parsedPath.get(NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.defaultFolder.text")));
} catch (TskCoreException ex) {
LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS
return null;
}
}
} else if (typeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) {
Node accountRootNode = resultsChilds.findChild(art.getDisplayName());
/**
* Returns the account artifact's parent node or null if cannot be found.
*
* @param typesChildren The children object of the same category as the
* account.
* @param art The artifact.
*
* @return The account artifact's parent node or null if cannot be found.
*/
private Node getAccountNode(Children typesChildren, BlackboardArtifact art) {
Node accountRootNode = typesChildren.findChild(art.getDisplayName());
Children accountRootChilds = accountRootNode.getChildren();
List<BlackboardAttribute> attributes;
String accountType = null;
@ -1341,25 +1493,47 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
}
}
if (accountType == null) {
return;
return null;
}
if (accountType.equals(Account.Type.CREDIT_CARD.getTypeName())) {
Node accountNode = accountRootChilds.findChild(Account.Type.CREDIT_CARD.getDisplayName());
return getCreditCardAccountNode(accountRootChilds, ccNumberName);
} else { //default account type
return accountRootChilds.findChild(accountType);
}
} catch (TskCoreException ex) {
LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS
return null;
}
}
/**
* Returns the credit card artifact's parent node or null if cannot be
* found.
*
* @param typesChildren The children object of the same category as credit
* card.
* @param art The artifact.
*
* @return The credit card artifact's parent node or null if cannot be
* found.
*/
private Node getCreditCardAccountNode(Children accountRootChildren, String ccNumberName) {
Node accountNode = accountRootChildren.findChild(Account.Type.CREDIT_CARD.getDisplayName());
if (accountNode == null) {
return;
return null;
}
Children accountChildren = accountNode.getChildren();
if (accountChildren == null) {
return;
return null;
}
Node binNode = accountChildren.findChild(NbBundle.getMessage(Accounts.class, "Accounts.ByBINNode.name"));
if (binNode == null) {
return;
return null;
}
Children binChildren = binNode.getChildren();
if (ccNumberName == null) {
return;
return null;
}
//right padded with 0s to 8 digits when single number
//when a range of numbers, the first 6 digits are rightpadded with 0s to 8 digits then a dash then 3 digits, the 6,7,8, digits of the end number right padded with 9s
@ -1370,7 +1544,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
bin = Integer.parseInt(binName);
} catch (NumberFormatException ex) {
LOGGER.log(Level.WARNING, "Unable to parseInt a BIN for node selection from string binName=" + binName, ex); //NON-NLS
return;
return null;
}
CreditCards.BankIdentificationNumber binInfo = CreditCards.getBINInfo(bin);
if (binInfo != null) {
@ -1381,42 +1555,9 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
}
}
if (binName == null) {
return;
return null;
}
treeNode = binChildren.findChild(binName);
} else { //default account type
treeNode = accountRootChilds.findChild(accountType);
}
} catch (TskCoreException ex) {
LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS
}
} else {
Node extractedContent = resultsChilds.findChild(ExtractedContent.NAME);
Children extractedChilds = extractedContent.getChildren();
if (extractedChilds == null) {
return;
}
treeNode = extractedChilds.findChild(typeName);
}
if (treeNode == null) {
return;
}
DisplayableItemNode undecoratedParentNode = (DisplayableItemNode) ((DirectoryTreeFilterNode) treeNode).getOriginal();
undecoratedParentNode.setChildNodeSelectionInfo(new ArtifactNodeSelectionInfo(art));
getTree().expandNode(treeNode);
if (this.getSelectedNode().equals(treeNode)) {
this.setDirectoryListingActive();
this.respondSelection(em.getSelectedNodes(), new Node[]{treeNode});
} else {
try {
em.setExploredContextAndSelection(treeNode, new Node[]{treeNode});
} catch (PropertyVetoException ex) {
LOGGER.log(Level.WARNING, "Property Veto: ", ex); //NON-NLS
}
}
// Another thread is needed because we have to wait for dataResult to populate
return binChildren.findChild(binName);
}
public void viewArtifactContent(BlackboardArtifact art) {

Binary file not shown.

After

Width:  |  Height:  |  Size: 443 B