Converted DeletedContent, ExtractedContent, FileSize, FileTypesByExtension, FileTypesByMimeType, HashsetHits, InterestingHits and KeywordHits to new child factory approach that supports paging.

This commit is contained in:
esaunders 2019-04-17 13:05:50 -04:00
parent f54c9aa2ff
commit c4e81f2ea2
8 changed files with 273 additions and 270 deletions

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2018 Basis Technology Corp.
* Copyright 2011-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -39,7 +39,6 @@ import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.CasePreferences;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.datamodel.AbstractFile;
@ -358,7 +357,7 @@ public class DeletedContent implements AutopsyVisitableItem {
}
}
static class DeletedContentChildren extends ChildFactory.Detachable<AbstractFile> {
static class DeletedContentChildren extends BaseChildFactory<AbstractFile> {
private final SleuthkitCase skCase;
private final DeletedContent.DeletedContentFilter filter;
@ -368,6 +367,7 @@ public class DeletedContent implements AutopsyVisitableItem {
private final long datasourceObjId;
DeletedContentChildren(DeletedContent.DeletedContentFilter filter, SleuthkitCase skCase, Observable o, long datasourceObjId) {
super(filter.getName());
this.skCase = skCase;
this.filter = filter;
this.notifier = o;
@ -376,6 +376,11 @@ public class DeletedContent implements AutopsyVisitableItem {
private final Observer observer = new DeletedContentChildrenObserver();
@Override
protected List<AbstractFile> makeKeys() {
return runFsQuery();
}
// Cause refresh of children if there are changes
private class DeletedContentChildrenObserver implements Observer {
@ -386,25 +391,19 @@ public class DeletedContent implements AutopsyVisitableItem {
}
@Override
protected void addNotify() {
protected void onAdd() {
if (notifier != null) {
notifier.addObserver(observer);
}
}
@Override
protected void removeNotify() {
protected void onRemove() {
if (notifier != null) {
notifier.deleteObserver(observer);
}
}
@Override
protected boolean createKeys(List<AbstractFile> list) {
list.addAll(runFsQuery());
return true;
}
static private String makeQuery(DeletedContent.DeletedContentFilter filter, long filteringDSObjId) {
String query = "";
switch (filter) {
@ -440,11 +439,6 @@ public class DeletedContent implements AutopsyVisitableItem {
}
if (UserPreferences.hideKnownFilesInViewsTree()) {
query += " AND (known != " + TskData.FileKnown.KNOWN.getFileKnownValue() //NON-NLS
+ " OR known IS NULL)"; //NON-NLS
}
if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) {
query += " AND data_source_obj_id = " + filteringDSObjId;
}

View File

@ -423,12 +423,12 @@ public class ExtractedContent implements AutopsyVisitableItem {
/**
* Creates children for a given artifact type
*/
private class ArtifactFactory extends ChildFactory.Detachable<BlackboardArtifact> {
private class ArtifactFactory extends BaseChildFactory<BlackboardArtifact> {
private BlackboardArtifact.Type type;
public ArtifactFactory(BlackboardArtifact.Type type) {
super();
super(type.getTypeName());
this.type = type;
}
@ -481,36 +481,36 @@ public class ExtractedContent implements AutopsyVisitableItem {
};
@Override
protected void addNotify() {
protected void onAdd() {
IngestManager.getInstance().addIngestJobEventListener(pcl);
IngestManager.getInstance().addIngestModuleEventListener(pcl);
}
@Override
protected void removeNotify() {
protected void onRemove() {
IngestManager.getInstance().removeIngestJobEventListener(pcl);
IngestManager.getInstance().removeIngestModuleEventListener(pcl);
}
@Override
protected boolean createKeys(List<BlackboardArtifact> list) {
if (skCase != null) {
try {
List<BlackboardArtifact> arts =
Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true) ?
blackboard.getArtifacts(type.getTypeID(), datasourceObjId) :
skCase.getBlackboardArtifacts(type.getTypeID());
list.addAll(arts);
} catch (TskException ex) {
Logger.getLogger(ArtifactFactory.class.getName()).log(Level.SEVERE, "Couldn't get blackboard artifacts from database", ex); //NON-NLS
}
}
return true;
}
@Override
protected Node createNodeForKey(BlackboardArtifact key) {
return new BlackboardArtifactNode(key);
}
@Override
protected List<BlackboardArtifact> makeKeys() {
if (skCase != null) {
try {
List<BlackboardArtifact> arts =
Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true) ?
blackboard.getArtifacts(type.getTypeID(), datasourceObjId) :
skCase.getBlackboardArtifacts(type.getTypeID());
return arts;
} catch (TskException ex) {
Logger.getLogger(ArtifactFactory.class.getName()).log(Level.SEVERE, "Couldn't get blackboard artifacts from database", ex); //NON-NLS
}
}
return Collections.emptyList();
}
}
}

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2013-2018 Basis Technology Corp.
* Copyright 2013-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -361,7 +361,7 @@ public class FileSize implements AutopsyVisitableItem {
/*
* Makes children, which are nodes for files of a given range
*/
static class FileSizeChildren extends ChildFactory.Detachable<AbstractFile> {
static class FileSizeChildren extends BaseChildFactory<AbstractFile> {
private final SleuthkitCase skCase;
private final FileSizeFilter filter;
@ -377,6 +377,7 @@ public class FileSize implements AutopsyVisitableItem {
* added to case
*/
FileSizeChildren(FileSizeFilter filter, SleuthkitCase skCase, Observable o, long dsObjId) {
super(filter.getName());
this.skCase = skCase;
this.filter = filter;
this.notifier = o;
@ -385,14 +386,14 @@ public class FileSize implements AutopsyVisitableItem {
}
@Override
protected void addNotify() {
protected void onAdd() {
if (notifier != null) {
notifier.addObserver(observer);
}
}
@Override
protected void removeNotify() {
protected void onRemove() {
if (notifier != null) {
notifier.deleteObserver(observer);
}
@ -400,6 +401,11 @@ public class FileSize implements AutopsyVisitableItem {
private final Observer observer = new FileSizeChildrenObserver();
@Override
protected List<AbstractFile> makeKeys() {
return runFsQuery();
}
// Cause refresh of children if there are changes
private class FileSizeChildrenObserver implements Observer {
@ -409,12 +415,6 @@ public class FileSize implements AutopsyVisitableItem {
}
}
@Override
protected boolean createKeys(List<AbstractFile> list) {
list.addAll(runFsQuery());
return true;
}
private static String makeQuery(FileSizeFilter filter, long filteringDSObjId) {
String query;
switch (filter) {
@ -436,17 +436,6 @@ public class FileSize implements AutopsyVisitableItem {
// Ignore unallocated block files.
query = query + " AND (type != " + TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS.getFileType() + ")"; //NON-NLS
// Hide known files if indicated in the user preferences.
if(UserPreferences.hideKnownFilesInViewsTree()) {
query += " AND (known != " + TskData.FileKnown.KNOWN.getFileKnownValue() //NON-NLS
+ " OR known IS NULL)"; //NON-NLS
}
// Hide slack files if indicated in the user preferences.
if(UserPreferences.hideSlackFilesInViewsTree()) {
query += " AND (type != " + TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.getFileType() + ")"; //NON-NLS
}
// filter by datasource if indicated in case preferences
if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) {
query += " AND data_source_obj_id = " + filteringDSObjId;

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2018 Basis Technology Corp.
* Copyright 2011-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.datamodel;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Objects;
@ -292,7 +293,7 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
* should refresh
*/
FileExtensionNode(FileTypesByExtension.SearchFilterInterface filter, SleuthkitCase skCase, FileTypesByExtObservable o) {
super(typesRoot, Children.create(new FileExtensionNodeChildren(filter, skCase, o), true),
super(typesRoot, Children.create(new FileExtensionNodeChildren(filter, skCase, o, filter.getDisplayName()), true),
Lookups.singleton(filter.getDisplayName()));
this.filter = filter;
super.setName(filter.getDisplayName());
@ -377,7 +378,7 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
/**
* Child node factory for a specific file type - does the database query.
*/
private class FileExtensionNodeChildren extends ChildFactory.Detachable<FileTypesKey> implements Observer {
private class FileExtensionNodeChildren extends BaseChildFactory<FileTypesKey> implements Observer {
private final SleuthkitCase skCase;
private final FileTypesByExtension.SearchFilterInterface filter;
@ -390,22 +391,22 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
* @param o Observable that will notify when there could be new
* data to display
*/
private FileExtensionNodeChildren(FileTypesByExtension.SearchFilterInterface filter, SleuthkitCase skCase, Observable o) {
super();
private FileExtensionNodeChildren(FileTypesByExtension.SearchFilterInterface filter, SleuthkitCase skCase, Observable o, String nodeName) {
super(nodeName);
this.filter = filter;
this.skCase = skCase;
notifier = o;
}
@Override
protected void addNotify() {
protected void onAdd() {
if (notifier != null) {
notifier.addObserver(this);
}
}
@Override
protected void removeNotify() {
protected void onRemove() {
if (notifier != null) {
notifier.deleteObserver(this);
}
@ -417,19 +418,19 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
}
@Override
protected boolean createKeys(List<FileTypesKey> list) {
try {
list.addAll(skCase.findAllFilesWhere(createQuery(filter))
.stream().map(f -> new FileTypesKey(f)).collect(Collectors.toList()));
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Couldn't get search results", ex); //NON-NLS
}
return true;
protected Node createNodeForKey(FileTypesKey key) {
return key.accept(new FileTypes.FileNodeCreationVisitor());
}
@Override
protected Node createNodeForKey(FileTypesKey key) {
return key.accept(new FileTypes.FileNodeCreationVisitor());
protected List<FileTypesKey> makeKeys() {
try {
return skCase.findAllFilesWhere(createQuery(filter))
.stream().map(f -> new FileTypesKey(f)).collect(Collectors.toList());
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Couldn't get search results", ex); //NON-NLS
}
return Collections.emptyList();
}
}

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2018 Basis Technology Corp.
* Copyright 2011-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -445,27 +445,16 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
* files that match MimeType which is represented by this position in the
* tree.
*/
private class MediaSubTypeNodeChildren extends ChildFactory.Detachable<FileTypesKey> implements Observer {
private class MediaSubTypeNodeChildren extends BaseChildFactory<FileTypesKey> implements Observer {
private final String mimeType;
private MediaSubTypeNodeChildren(String mimeType) {
super();
super(mimeType);
addObserver(this);
this.mimeType = mimeType;
}
@Override
protected boolean createKeys(List<FileTypesKey> list) {
try {
list.addAll(skCase.findAllFilesWhere(createBaseWhereExpr() + " AND mime_type = '" + mimeType + "'")
.stream().map(f -> new FileTypesKey(f)).collect(Collectors.toList())); //NON-NLS
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Couldn't get search results", ex); //NON-NLS
}
return true;
}
@Override
public void update(Observable o, Object arg) {
refresh(true);
@ -475,5 +464,26 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
protected Node createNodeForKey(FileTypesKey key) {
return key.accept(new FileTypes.FileNodeCreationVisitor());
}
@Override
protected List<FileTypesKey> makeKeys() {
try {
return skCase.findAllFilesWhere(createBaseWhereExpr() + " AND mime_type = '" + mimeType + "'")
.stream().map(f -> new FileTypesKey(f)).collect(Collectors.toList()); //NON-NLS
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Couldn't get search results", ex); //NON-NLS
}
return Collections.emptyList();
}
@Override
protected void onAdd() {
// No-op
}
@Override
protected void onRemove() {
// No-op
}
}
}

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2018 Basis Technology Corp.
* Copyright 2011-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -35,6 +35,7 @@ import java.util.Observable;
import java.util.Observer;
import java.util.Set;
import java.util.logging.Level;
import java.util.stream.Collectors;
import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
@ -44,7 +45,6 @@ import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.CasePreferences;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
@ -378,60 +378,53 @@ public class HashsetHits implements AutopsyVisitableItem {
/**
* Creates the nodes for the hits in a given set.
*/
private class HitFactory extends ChildFactory.Detachable<Long> implements Observer {
private class HitFactory extends BaseChildFactory<BlackboardArtifact> implements Observer {
private String hashsetName;
private Map<Long, BlackboardArtifact> artifactHits = new HashMap<>();
private HitFactory(String hashsetName) {
super();
super(hashsetName);
this.hashsetName = hashsetName;
}
@Override
protected void addNotify() {
protected void onAdd() {
hashsetResults.addObserver(this);
}
@Override
protected void removeNotify() {
protected void onRemove() {
hashsetResults.deleteObserver(this);
}
@Override
protected boolean createKeys(List<Long> list) {
if (skCase == null) {
return true;
}
hashsetResults.getArtifactIds(hashsetName).forEach((id) -> {
try {
if (!artifactHits.containsKey(id)) {
BlackboardArtifact art = skCase.getBlackboardArtifact(id);
artifactHits.put(id, art);
}
} catch (TskException ex) {
logger.log(Level.SEVERE, "TSK Exception occurred", ex); //NON-NLS
}
});
// Adding all keys at once is more efficient than adding one at a
// time because Netbeans triggers internal processing each time an
// element is added to the list.
list.addAll(artifactHits.keySet());
return true;
}
@Override
protected Node createNodeForKey(Long id) {
BlackboardArtifact art = artifactHits.get(id);
return (null == art) ? null : new BlackboardArtifactNode(art);
protected Node createNodeForKey(BlackboardArtifact key) {
return new BlackboardArtifactNode(key);
}
@Override
public void update(Observable o, Object arg) {
refresh(true);
}
@Override
protected List<BlackboardArtifact> makeKeys() {
if (skCase != null) {
hashsetResults.getArtifactIds(hashsetName).forEach((id) -> {
try {
if (!artifactHits.containsKey(id)) {
BlackboardArtifact art = skCase.getBlackboardArtifact(id);
artifactHits.put(id, art);
}
} catch (TskException ex) {
logger.log(Level.SEVERE, "TSK Exception occurred", ex); //NON-NLS
}
});
return new ArrayList<>(artifactHits.values());
}
return Collections.emptyList();
}
}
}

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2018 Basis Technology Corp.
* Copyright 2011-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -449,51 +449,57 @@ public class InterestingHits implements AutopsyVisitableItem {
}
}
private class HitFactory extends ChildFactory<Long> implements Observer {
private class HitFactory extends BaseChildFactory<BlackboardArtifact> implements Observer {
private final String setName;
private final String typeName;
private final Map<Long, BlackboardArtifact> artifactHits = new HashMap<>();
private HitFactory(String setName, String typeName) {
super();
super(typeName);
this.setName = setName;
this.typeName = typeName;
interestingResults.addObserver(this);
}
@Override
protected boolean createKeys(List<Long> list) {
protected List<BlackboardArtifact> makeKeys() {
if (skCase == null) {
return true;
}
interestingResults.getArtifactIds(setName, typeName).forEach((id) -> {
try {
if (!artifactHits.containsKey(id)) {
BlackboardArtifact art = skCase.getBlackboardArtifact(id);
artifactHits.put(id, art);
if (skCase != null) {
interestingResults.getArtifactIds(setName, typeName).forEach((id) -> {
try {
if (!artifactHits.containsKey(id)) {
BlackboardArtifact art = skCase.getBlackboardArtifact(id);
artifactHits.put(id, art);
}
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "TSK Exception occurred", ex); //NON-NLS
}
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "TSK Exception occurred", ex); //NON-NLS
}
});
});
list.addAll(artifactHits.keySet());
return true;
return new ArrayList<>(artifactHits.values());
}
return Collections.emptyList();
}
@Override
protected Node createNodeForKey(Long l) {
BlackboardArtifact art = artifactHits.get(l);
return (null == art) ? null : new BlackboardArtifactNode(art);
protected Node createNodeForKey(BlackboardArtifact art) {
return new BlackboardArtifactNode(art);
}
@Override
public void update(Observable o, Object arg) {
refresh(true);
}
@Override
protected void onAdd() {
// No-op
}
@Override
protected void onRemove() {
// No-op
}
}
}

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2018 Basis Technology Corp.
* Copyright 2011-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -25,6 +25,7 @@ import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
@ -34,7 +35,6 @@ import java.util.Observable;
import java.util.Observer;
import java.util.Set;
import java.util.logging.Level;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children;
@ -46,7 +46,6 @@ import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.CasePreferences;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.coreutils.Logger;
import static org.sleuthkit.autopsy.datamodel.Bundle.*;
import org.sleuthkit.autopsy.ingest.IngestManager;
@ -85,7 +84,6 @@ public class KeywordHits implements AutopsyVisitableItem {
*/
private static final String DEFAULT_INSTANCE_NAME = "DEFAULT_INSTANCE_NAME";
/**
* query attributes table for the ones that we need for the tree
*/
@ -108,20 +106,20 @@ public class KeywordHits implements AutopsyVisitableItem {
/**
* Constructor
*
* @param skCase Case DB
*/
*
* @param skCase Case DB
*/
KeywordHits(SleuthkitCase skCase) {
this(skCase, 0);
}
/**
* Constructor
*
* @param skCase Case DB
* @param objId Object id of the data source
*
*/
*
* @param skCase Case DB
* @param objId Object id of the data source
*
*/
public KeywordHits(SleuthkitCase skCase, long objId) {
this.skCase = skCase;
this.datasourceObjId = objId;
@ -324,9 +322,9 @@ public class KeywordHits implements AutopsyVisitableItem {
String queryStr = KEYWORD_HIT_ATTRIBUTES_QUERY;
if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) {
queryStr += " AND blackboard_artifacts.data_source_obj_id = " + datasourceObjId;
queryStr += " AND blackboard_artifacts.data_source_obj_id = " + datasourceObjId;
}
try (CaseDbQuery dbQuery = skCase.executeQuery(queryStr)) {
ResultSet resultSet = dbQuery.getResultSet();
while (resultSet.next()) {
@ -530,7 +528,7 @@ public class KeywordHits implements AutopsyVisitableItem {
}
final void updateDisplayName() {
super.setDisplayName(getName() + " (" + countTotalDescendants() + ")");
super.setDisplayName(getDisplayName() + " (" + countTotalDescendants() + ")");
}
abstract int countTotalDescendants();
@ -631,6 +629,24 @@ public class KeywordHits implements AutopsyVisitableItem {
}
}
/**
* Create a ChildFactory object for the given set name and keyword.
*
* The type of ChildFactory we create is based on whether the node
* represents a regular expression keyword search or not. For regular
* expression keyword searches there will be an extra layer in the tree that
* represents each of the individual terms found by the regular expression.
* E.g., for an email regular expression search there will be a node in the
* tree for every email address hit.
*/
ChildFactory<?> createChildFactory(String setName, String keyword) {
if (isOnlyDefaultInstance(keywordResults.getKeywordInstances(setName, keyword))) {
return new HitsFactory(setName, keyword, DEFAULT_INSTANCE_NAME);
} else {
return new RegExpInstancesFactory(setName, keyword);
}
}
/**
* Represents the search term or regexp that user searched for
*/
@ -640,8 +656,17 @@ public class KeywordHits implements AutopsyVisitableItem {
private final String keyword;
private TermNode(String setName, String keyword) {
super(Children.create(new RegExpInstancesFactory(setName, keyword), true), Lookups.singleton(keyword));
super.setName(keyword);
super(Children.create(createChildFactory(setName, keyword), true), Lookups.singleton(keyword));
super.setDisplayName(keyword);
/**
* We differentiate between the programmatic name and the display
* name. The programmatic name is used to create an association with
* an event bus and must be the same as the node name passed by our
* ChildFactory to it's parent constructor. See the HitsFactory
* constructor for an example.
*/
super.setName(setName + "_" + keyword);
this.setName = setName;
this.keyword = keyword;
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/keyword_hits.png"); //NON-NLS
@ -694,45 +719,11 @@ public class KeywordHits implements AutopsyVisitableItem {
}
}
/**
* Allows us to pass in either longs or strings as they keys for different
* types of nodes at the same level. Probably a better way to do this, but
* it works.
*/
private class RegExpInstanceKey {
private final boolean isRegExp;
private String strKey;
private Long longKey;
RegExpInstanceKey(String key) {
isRegExp = true;
strKey = key;
}
RegExpInstanceKey(Long key) {
isRegExp = false;
longKey = key;
}
boolean isRegExp() {
return isRegExp;
}
Long getIdKey() {
return longKey;
}
String getRegExpKey() {
return strKey;
}
}
/**
* Creates the nodes for a given regexp that represent the specific terms
* that were found
*/
private class RegExpInstancesFactory extends DetachableObserverChildFactory<RegExpInstanceKey> {
private class RegExpInstancesFactory extends DetachableObserverChildFactory<String> {
private final String keyword;
private final String setName;
@ -744,33 +735,15 @@ public class KeywordHits implements AutopsyVisitableItem {
}
@Override
protected boolean createKeys(List<RegExpInstanceKey> list) {
List<String> instances = keywordResults.getKeywordInstances(setName, keyword);
// The keys are different depending on what we are displaying.
// regexp get another layer to show instances.
// Exact/substring matches don't.
if (isOnlyDefaultInstance(instances)) {
list.addAll(keywordResults.getArtifactIds(setName, keyword, DEFAULT_INSTANCE_NAME).stream()
.map(RegExpInstanceKey::new)
.collect(Collectors.toList()));
} else {
list.addAll(instances.stream()
.map(RegExpInstanceKey::new)
.collect(Collectors.toList()));
}
protected boolean createKeys(List<String> list) {
list.addAll(keywordResults.getKeywordInstances(setName, keyword));
return true;
}
@Override
protected Node createNodeForKey(RegExpInstanceKey key) {
if (key.isRegExp()) {
return new RegExpInstanceNode(setName, keyword, key.getRegExpKey());
} else {
// if it isn't a regexp, then skip the 'instance' layer of the tree
return createBlackboardArtifactNode(key.getIdKey());
}
protected Node createNodeForKey(String key) {
return new RegExpInstanceNode(setName, keyword, key);
}
}
/**
@ -784,7 +757,15 @@ public class KeywordHits implements AutopsyVisitableItem {
private RegExpInstanceNode(String setName, String keyword, String instance) {
super(Children.create(new HitsFactory(setName, keyword, instance), true), Lookups.singleton(instance));
super.setName(instance); //the instance represents the name of the keyword hit at this point as the keyword is the regex
super.setDisplayName(instance); //the instance represents the name of the keyword hit at this point as the keyword is the regex
/**
* We differentiate between the programmatic name and the display
* name. The programmatic name is used to create an association with
* an event bus and must be the same as the node name passed by our
* ChildFactory to it's parent constructor. See the HitsFactory
* constructor for an example.
*/
super.setName(setName + "_" + keyword + "_" + instance);
this.setName = setName;
this.keyword = keyword;
this.instance = instance;
@ -837,7 +818,7 @@ public class KeywordHits implements AutopsyVisitableItem {
/**
* Create a blackboard node for the given Keyword Hit artifact
*
* @param artifactId
* @param art
*
* @return Node or null on error
*/
@ -850,81 +831,110 @@ public class KeywordHits implements AutopsyVisitableItem {
"KeywordHits.createNodeForKey.chgTime.name=ChangeTime",
"KeywordHits.createNodeForKey.chgTime.displayName=Change Time",
"KeywordHits.createNodeForKey.chgTime.desc=Change Time"})
private BlackboardArtifactNode createBlackboardArtifactNode(Long artifactId) {
private BlackboardArtifactNode createBlackboardArtifactNode(BlackboardArtifact art) {
if (skCase == null) {
return null;
}
try {
BlackboardArtifact art = skCase.getBlackboardArtifact(artifactId);
BlackboardArtifactNode n = new BlackboardArtifactNode(art);
// The associated file should be available through the Lookup that
// gets created when the BlackboardArtifactNode is constructed.
AbstractFile file = n.getLookup().lookup(AbstractFile.class);
if (file == null) {
try {
file = skCase.getAbstractFileById(art.getObjectID());
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "TskCoreException while constructing BlackboardArtifact Node from KeywordHitsKeywordChildren", ex); //NON-NLS
return n;
}
}
/*
* It is possible to get a keyword hit on artifacts generated for
* the underlying image in which case MAC times are not
* available/applicable/useful.
*/
if (file == null) {
BlackboardArtifactNode n = new BlackboardArtifactNode(art); //NON-NLS
// The associated file should be available through the Lookup that
// gets created when the BlackboardArtifactNode is constructed.
AbstractFile file = n.getLookup().lookup(AbstractFile.class);
if (file == null) {
try {
file = skCase.getAbstractFileById(art.getObjectID());
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "TskCoreException while constructing BlackboardArtifact Node from KeywordHitsKeywordChildren", ex); //NON-NLS
return n;
}
n.addNodeProperty(new NodeProperty<>(
KeywordHits_createNodeForKey_modTime_name(),
KeywordHits_createNodeForKey_modTime_displayName(),
KeywordHits_createNodeForKey_modTime_desc(),
ContentUtils.getStringTime(file.getMtime(), file)));
n.addNodeProperty(new NodeProperty<>(
KeywordHits_createNodeForKey_accessTime_name(),
KeywordHits_createNodeForKey_accessTime_displayName(),
KeywordHits_createNodeForKey_accessTime_desc(),
ContentUtils.getStringTime(file.getAtime(), file)));
n.addNodeProperty(new NodeProperty<>(
KeywordHits_createNodeForKey_chgTime_name(),
KeywordHits_createNodeForKey_chgTime_displayName(),
KeywordHits_createNodeForKey_chgTime_desc(),
ContentUtils.getStringTime(file.getCtime(), file)));
return n;
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "TSK Exception occurred", ex); //NON-NLS
}
return null;
/*
* It is possible to get a keyword hit on artifacts generated for the
* underlying image in which case MAC times are not
* available/applicable/useful.
*/
if (file == null) {
return n;
}
n.addNodeProperty(new NodeProperty<>(
KeywordHits_createNodeForKey_modTime_name(),
KeywordHits_createNodeForKey_modTime_displayName(),
KeywordHits_createNodeForKey_modTime_desc(),
ContentUtils.getStringTime(file.getMtime(), file)));
n.addNodeProperty(new NodeProperty<>(
KeywordHits_createNodeForKey_accessTime_name(),
KeywordHits_createNodeForKey_accessTime_displayName(),
KeywordHits_createNodeForKey_accessTime_desc(),
ContentUtils.getStringTime(file.getAtime(), file)));
n.addNodeProperty(new NodeProperty<>(
KeywordHits_createNodeForKey_chgTime_name(),
KeywordHits_createNodeForKey_chgTime_displayName(),
KeywordHits_createNodeForKey_chgTime_desc(),
ContentUtils.getStringTime(file.getCtime(), file)));
return n;
}
/**
* Creates nodes for individual files that had hits
*/
private class HitsFactory extends DetachableObserverChildFactory<Long> {
private class HitsFactory extends BaseChildFactory<BlackboardArtifact> implements Observer {
private final String keyword;
private final String setName;
private final String instance;
private final Map<Long, BlackboardArtifact> artifactHits = new HashMap<>();
private HitsFactory(String setName, String keyword, String instance) {
super();
/**
* The node name passed to the parent constructor will consist of
* the set name, keyword and optionally the instance name (in the
* case of regular expression hits. This name must match the name
* set in the TermNode or RegExpInstanceNode constructors.
*/
super(setName + "_" + keyword + (DEFAULT_INSTANCE_NAME.equals(instance) ? "" : "_" + instance));
this.setName = setName;
this.keyword = keyword;
this.instance = instance;
}
@Override
protected boolean createKeys(List<Long> list) {
list.addAll(keywordResults.getArtifactIds(setName, keyword, instance));
return true;
protected List<BlackboardArtifact> makeKeys() {
if (skCase != null) {
keywordResults.getArtifactIds(setName, keyword, instance).forEach((id) -> {
try {
if (!artifactHits.containsKey(id)) {
BlackboardArtifact art = skCase.getBlackboardArtifact(id);
artifactHits.put(id, art);
}
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "TSK Exception occurred", ex); //NON-NLS
}
});
return new ArrayList<>(artifactHits.values());
}
return Collections.emptyList();
}
@Override
protected Node createNodeForKey(Long artifactId) {
return createBlackboardArtifactNode(artifactId);
protected Node createNodeForKey(BlackboardArtifact art) {
return createBlackboardArtifactNode(art);
}
@Override
protected void onAdd() {
keywordResults.addObserver(this);
}
@Override
protected void onRemove() {
keywordResults.deleteObserver(this);
}
@Override
public void update(Observable o, Object arg) {
refresh(true);
}
}
}