mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-15 09:17:42 +00:00
Merge pull request #6949 from gdicristofaro/7561-treeHierarchy
7561 tree hierarchy
This commit is contained in:
commit
e39d8960ab
@ -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;
|
||||
}
|
||||
}
|
704
Core/src/org/sleuthkit/autopsy/datamodel/Artifacts.java
Normal file
704
Core/src/org/sleuthkit/autopsy/datamodel/Artifacts.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
95
Core/src/org/sleuthkit/autopsy/datamodel/DataArtifacts.java
Normal file
95
Core/src/org/sleuthkit/autopsy/datamodel/DataArtifacts.java
Normal 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;
|
||||
}
|
||||
}
|
@ -30,9 +30,8 @@ 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
|
||||
* Data source grouping node - an optional grouping node in the data tree view
|
||||
*
|
||||
*/
|
||||
class DataSourceGroupingNode extends DisplayableItemNode {
|
||||
@ -46,8 +45,8 @@ class DataSourceGroupingNode extends DisplayableItemNode {
|
||||
*/
|
||||
DataSourceGroupingNode(DataSource dataSource) {
|
||||
|
||||
super (Optional.ofNullable(createDSGroupingNodeChildren(dataSource))
|
||||
.orElse(new RootContentChildren(Arrays.asList(Collections.EMPTY_LIST))),
|
||||
super(Optional.ofNullable(createDSGroupingNodeChildren(dataSource))
|
||||
.orElse(new RootContentChildren(Arrays.asList(Collections.EMPTY_LIST))),
|
||||
Lookups.singleton(dataSource));
|
||||
|
||||
if (dataSource instanceof Image) {
|
||||
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,9 +304,12 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* 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) {
|
||||
return counts.get(accountType);
|
||||
@ -277,7 +317,8 @@ 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.
|
||||
*
|
||||
* @return An alphabetically organized list of all the account types.
|
||||
*/
|
||||
List<String> getTypes() {
|
||||
List<String> types = new ArrayList<>(counts.keySet());
|
||||
@ -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,10 +434,12 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* 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) {
|
||||
reviewStatusBus.register(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()
|
||||
|
@ -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,12 +201,14 @@ 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();
|
||||
Arrays.stream(resultsChildren.getNodes()).forEach(tree::expandNode);
|
||||
if (!Objects.isNull(categoryNode)) {
|
||||
tree.expandNode(categoryNode);
|
||||
Children resultsChildren = categoryNode.getChildren();
|
||||
Arrays.stream(resultsChildren.getNodes()).forEach(tree::expandNode);
|
||||
}
|
||||
}
|
||||
|
||||
Node views = rootChildren.findChild(ViewsNode.NAME);
|
||||
@ -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) {
|
||||
@ -307,7 +313,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
* Setter to determine if rejected results should be shown or not.
|
||||
*
|
||||
* @param showRejectedResults True if showing rejected results; otherwise
|
||||
* false.
|
||||
* false.
|
||||
*/
|
||||
public void setShowRejectedResults(boolean showRejectedResults) {
|
||||
this.showRejectedResults = showRejectedResults;
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
} 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());
|
||||
}
|
||||
}
|
||||
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();
|
||||
}
|
||||
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,207 +1246,34 @@ 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 hashsetRootChilds = hashsetRootNode.getChildren();
|
||||
try {
|
||||
String setName = null;
|
||||
List<BlackboardAttribute> attributes = art.getAttributes();
|
||||
for (BlackboardAttribute att : attributes) {
|
||||
int typeId = att.getAttributeType().getTypeID();
|
||||
if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()) {
|
||||
setName = att.getValueString();
|
||||
}
|
||||
}
|
||||
treeNode = hashsetRootChilds.findChild(setName);
|
||||
} catch (TskCoreException ex) {
|
||||
LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS
|
||||
}
|
||||
} else if (typeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()) {
|
||||
Node keywordRootNode = resultsChilds.findChild(typeName);
|
||||
Children keywordRootChilds = keywordRootNode.getChildren();
|
||||
try {
|
||||
String listName = null;
|
||||
String keywordName = null;
|
||||
String regex = null;
|
||||
List<BlackboardAttribute> attributes = art.getAttributes();
|
||||
for (BlackboardAttribute att : attributes) {
|
||||
int typeId = att.getAttributeType().getTypeID();
|
||||
if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()) {
|
||||
listName = att.getValueString();
|
||||
} else if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID()) {
|
||||
keywordName = att.getValueString();
|
||||
} else if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID()) {
|
||||
regex = att.getValueString();
|
||||
}
|
||||
}
|
||||
if (listName == null) {
|
||||
if (regex == null) { //using same labels used for creation
|
||||
listName = NbBundle.getMessage(KeywordHits.class, "KeywordHits.simpleLiteralSearch.text");
|
||||
} else {
|
||||
listName = NbBundle.getMessage(KeywordHits.class, "KeywordHits.singleRegexSearch.text");
|
||||
}
|
||||
}
|
||||
Node listNode = keywordRootChilds.findChild(listName);
|
||||
if (listNode == null) {
|
||||
return;
|
||||
}
|
||||
Children listChildren = listNode.getChildren();
|
||||
if (listChildren == null) {
|
||||
return;
|
||||
}
|
||||
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);
|
||||
if (regexNode == null) {
|
||||
return;
|
||||
}
|
||||
listChildren = regexNode.getChildren();
|
||||
if (listChildren == null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
Children typesChildren = categoryChildrenOpt.get();
|
||||
|
||||
treeNode = listChildren.findChild(keywordName);
|
||||
|
||||
} catch (TskCoreException ex) {
|
||||
LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS
|
||||
}
|
||||
} 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
|
||||
.getMessage(InterestingHits.class, "InterestingHits.interestingItems.text"));
|
||||
Children interestingItemsRootChildren = interestingItemsRootNode.getChildren();
|
||||
try {
|
||||
String setName = null;
|
||||
List<BlackboardAttribute> attributes = art.getAttributes();
|
||||
for (BlackboardAttribute att : attributes) {
|
||||
int typeId = att.getAttributeType().getTypeID();
|
||||
if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()) {
|
||||
setName = att.getValueString();
|
||||
}
|
||||
}
|
||||
Node setNode = interestingItemsRootChildren.findChild(setName);
|
||||
if (setNode == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Children fileArtifactChildren = setNode.getChildren();
|
||||
Node[] fileArtifactNodes = fileArtifactChildren == null ? null : fileArtifactChildren.getNodes();
|
||||
if (fileArtifactNodes == null || fileArtifactNodes.length != 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
treeNode = (typeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID())
|
||||
? fileArtifactNodes[0]
|
||||
: fileArtifactNodes[1];
|
||||
} catch (TskCoreException ex) {
|
||||
LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS
|
||||
}
|
||||
} else if (typeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()) {
|
||||
Node emailMsgRootNode = resultsChilds.findChild(typeName);
|
||||
Children emailMsgRootChilds = emailMsgRootNode.getChildren();
|
||||
Map<String, String> parsedPath = null;
|
||||
try {
|
||||
List<BlackboardAttribute> attributes = art.getAttributes();
|
||||
for (BlackboardAttribute att : attributes) {
|
||||
int typeId = att.getAttributeType().getTypeID();
|
||||
if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH.getTypeID()) {
|
||||
parsedPath = EmailExtracted.parsePath(att.getValueString());
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (parsedPath == null) {
|
||||
return;
|
||||
}
|
||||
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")));
|
||||
} catch (TskCoreException ex) {
|
||||
LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS
|
||||
}
|
||||
|
||||
} else if (typeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) {
|
||||
Node accountRootNode = resultsChilds.findChild(art.getDisplayName());
|
||||
Children accountRootChilds = accountRootNode.getChildren();
|
||||
List<BlackboardAttribute> attributes;
|
||||
String accountType = null;
|
||||
String ccNumberName = null;
|
||||
try {
|
||||
attributes = art.getAttributes();
|
||||
for (BlackboardAttribute att : attributes) {
|
||||
int typeId = att.getAttributeType().getTypeID();
|
||||
if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID()) {
|
||||
accountType = att.getValueString();
|
||||
}
|
||||
if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID()) {
|
||||
ccNumberName = att.getValueString();
|
||||
}
|
||||
}
|
||||
if (accountType == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (accountType.equals(Account.Type.CREDIT_CARD.getTypeName())) {
|
||||
Node accountNode = accountRootChilds.findChild(Account.Type.CREDIT_CARD.getDisplayName());
|
||||
if (accountNode == null) {
|
||||
return;
|
||||
}
|
||||
Children accountChildren = accountNode.getChildren();
|
||||
if (accountChildren == null) {
|
||||
return;
|
||||
}
|
||||
Node binNode = accountChildren.findChild(NbBundle.getMessage(Accounts.class, "Accounts.ByBINNode.name"));
|
||||
if (binNode == null) {
|
||||
return;
|
||||
}
|
||||
Children binChildren = binNode.getChildren();
|
||||
if (ccNumberName == null) {
|
||||
return;
|
||||
}
|
||||
//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
|
||||
String binName = StringUtils.rightPad(ccNumberName, 8, "0");
|
||||
binName = binName.substring(0, 8);
|
||||
int bin;
|
||||
try {
|
||||
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;
|
||||
}
|
||||
CreditCards.BankIdentificationNumber binInfo = CreditCards.getBINInfo(bin);
|
||||
if (binInfo != null) {
|
||||
int startBin = ((BINRange) binInfo).getBINstart();
|
||||
int endBin = ((BINRange) binInfo).getBINend();
|
||||
if (startBin != endBin) {
|
||||
binName = Integer.toString(startBin) + "-" + Integer.toString(endBin).substring(5); //if there is a range re-construct the name it appears as
|
||||
}
|
||||
}
|
||||
if (binName == null) {
|
||||
return;
|
||||
}
|
||||
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
|
||||
}
|
||||
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 {
|
||||
Node extractedContent = resultsChilds.findChild(ExtractedContent.NAME);
|
||||
Children extractedChilds = extractedContent.getChildren();
|
||||
if (extractedChilds == null) {
|
||||
return;
|
||||
}
|
||||
treeNode = extractedChilds.findChild(typeName);
|
||||
treeNode = typesChildren.findChild(typeName);
|
||||
}
|
||||
|
||||
if (treeNode == null) {
|
||||
@ -1419,6 +1296,270 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
// 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;
|
||||
List<BlackboardAttribute> attributes = art.getAttributes();
|
||||
for (BlackboardAttribute att : attributes) {
|
||||
int typeId = att.getAttributeType().getTypeID();
|
||||
if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()) {
|
||||
setName = att.getValueString();
|
||||
}
|
||||
}
|
||||
return hashsetRootChilds.findChild(setName);
|
||||
} catch (TskCoreException ex) {
|
||||
LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
String keywordName = null;
|
||||
String regex = null;
|
||||
List<BlackboardAttribute> attributes = art.getAttributes();
|
||||
for (BlackboardAttribute att : attributes) {
|
||||
int typeId = att.getAttributeType().getTypeID();
|
||||
if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()) {
|
||||
listName = att.getValueString();
|
||||
} else if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID()) {
|
||||
keywordName = att.getValueString();
|
||||
} else if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID()) {
|
||||
regex = att.getValueString();
|
||||
}
|
||||
}
|
||||
if (listName == null) {
|
||||
if (regex == null) { //using same labels used for creation
|
||||
listName = NbBundle.getMessage(KeywordHits.class, "KeywordHits.simpleLiteralSearch.text");
|
||||
} else {
|
||||
listName = NbBundle.getMessage(KeywordHits.class, "KeywordHits.singleRegexSearch.text");
|
||||
}
|
||||
}
|
||||
Node listNode = keywordRootChilds.findChild(listName);
|
||||
if (listNode == null) {
|
||||
return null;
|
||||
}
|
||||
Children listChildren = listNode.getChildren();
|
||||
if (listChildren == null) {
|
||||
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(listName);
|
||||
regexNode = (regexNode == null) ? listChildren.findChild(listName + "_" + regex) : regexNode;
|
||||
if (regexNode == null) {
|
||||
return null;
|
||||
}
|
||||
listChildren = regexNode.getChildren();
|
||||
if (listChildren == null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return listChildren.findChild(keywordName);
|
||||
} catch (TskCoreException ex) {
|
||||
LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
String setName = null;
|
||||
List<BlackboardAttribute> attributes = art.getAttributes();
|
||||
for (BlackboardAttribute att : attributes) {
|
||||
int typeId = att.getAttributeType().getTypeID();
|
||||
if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()) {
|
||||
setName = att.getValueString();
|
||||
}
|
||||
}
|
||||
Node setNode = interestingItemsRootChildren.findChild(setName);
|
||||
if (setNode == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Children fileArtifactChildren = setNode.getChildren();
|
||||
Node[] fileArtifactNodes = fileArtifactChildren == null ? null : fileArtifactChildren.getNodes();
|
||||
if (fileArtifactNodes == null || fileArtifactNodes.length != 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
List<BlackboardAttribute> attributes = art.getAttributes();
|
||||
for (BlackboardAttribute att : attributes) {
|
||||
int typeId = att.getAttributeType().getTypeID();
|
||||
if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH.getTypeID()) {
|
||||
parsedPath = EmailExtracted.parsePath(att.getValueString());
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (parsedPath == null) {
|
||||
return null;
|
||||
}
|
||||
Node defaultNode = emailMsgRootChilds.findChild(parsedPath.get(NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.defaultAcct.text")));
|
||||
Children defaultChildren = defaultNode.getChildren();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
String ccNumberName = null;
|
||||
try {
|
||||
attributes = art.getAttributes();
|
||||
for (BlackboardAttribute att : attributes) {
|
||||
int typeId = att.getAttributeType().getTypeID();
|
||||
if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID()) {
|
||||
accountType = att.getValueString();
|
||||
}
|
||||
if (typeId == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID()) {
|
||||
ccNumberName = att.getValueString();
|
||||
}
|
||||
}
|
||||
if (accountType == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (accountType.equals(Account.Type.CREDIT_CARD.getTypeName())) {
|
||||
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 null;
|
||||
}
|
||||
Children accountChildren = accountNode.getChildren();
|
||||
if (accountChildren == null) {
|
||||
return null;
|
||||
}
|
||||
Node binNode = accountChildren.findChild(NbBundle.getMessage(Accounts.class, "Accounts.ByBINNode.name"));
|
||||
if (binNode == null) {
|
||||
return null;
|
||||
}
|
||||
Children binChildren = binNode.getChildren();
|
||||
if (ccNumberName == null) {
|
||||
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
|
||||
String binName = StringUtils.rightPad(ccNumberName, 8, "0");
|
||||
binName = binName.substring(0, 8);
|
||||
int bin;
|
||||
try {
|
||||
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 null;
|
||||
}
|
||||
CreditCards.BankIdentificationNumber binInfo = CreditCards.getBINInfo(bin);
|
||||
if (binInfo != null) {
|
||||
int startBin = ((BINRange) binInfo).getBINstart();
|
||||
int endBin = ((BINRange) binInfo).getBINend();
|
||||
if (startBin != endBin) {
|
||||
binName = Integer.toString(startBin) + "-" + Integer.toString(endBin).substring(5); //if there is a range re-construct the name it appears as
|
||||
}
|
||||
}
|
||||
if (binName == null) {
|
||||
return null;
|
||||
}
|
||||
return binChildren.findChild(binName);
|
||||
}
|
||||
|
||||
public void viewArtifactContent(BlackboardArtifact art) {
|
||||
new ViewContextAction(
|
||||
NbBundle.getMessage(this.getClass(), "DirectoryTreeTopComponent.action.viewArtContent.text"),
|
||||
|
BIN
Core/src/org/sleuthkit/autopsy/images/analysis_result.png
Normal file
BIN
Core/src/org/sleuthkit/autopsy/images/analysis_result.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 443 B |
Loading…
x
Reference in New Issue
Block a user