mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-06 21:00:22 +00:00
Merge branch 'develop' of github.com:sleuthkit/autopsy into 7317-dataArtifacts
This commit is contained in:
commit
423fcbc7ff
@ -44,7 +44,7 @@ import org.sleuthkit.autopsy.coreutils.PlatformUtil;
|
||||
*/
|
||||
@ActionID(category = "Tools", id = "org.sleuthkit.autopsy.apputils.ResetWindowsAction")
|
||||
@ActionReferences(value = {
|
||||
@ActionReference(path = "Menu/Window", position = 105)})
|
||||
@ActionReference(path = "Menu/Window", position = 205)})
|
||||
@ActionRegistration(displayName = "#CTL_ResetWindowsAction", lazy = false)
|
||||
@NbBundle.Messages({"CTL_ResetWindowsAction=Reset Windows"})
|
||||
public final class ResetWindowsAction extends CallableSystemAction {
|
||||
|
@ -250,18 +250,10 @@
|
||||
<file name="org-netbeans-core-windows-actions-ResetWindowsAction.shadow_hidden"/>
|
||||
<file name="testAction.shadow_hidden"/>
|
||||
<file name="org-netbeans-core-io-ui-IOWindowAction.shadow_hidden"/>
|
||||
<file name="org-sleuthkit-autopsy-menuactions-DataResultMenu-separatoBefore.instance">
|
||||
<attr name="instanceClass" stringvalue="javax.swing.JSeparator"/>
|
||||
<attr name="position" intvalue="75"/>
|
||||
</file>
|
||||
<file name="org-sleuthkit-autopsy-menuactions-DataResultMenu.instance">
|
||||
<attr name="originalFile" stringvalue="Actions/Tools/org-sleuthkit-autopsy-menuactions-DataResultMenu.instance"/>
|
||||
<attr name="position" intvalue="100"/>
|
||||
</file>
|
||||
<file name="org-sleuthkit-autopsy-menuactions-DataResultMenu-separatoAfter.instance">
|
||||
<attr name="instanceClass" stringvalue="javax.swing.JSeparator"/>
|
||||
<attr name="position" intvalue="125"/>
|
||||
</file>
|
||||
<file name="org-sleuthkit-autopsy-menuactions-DataContentMenu.instance">
|
||||
<attr name="originalFile" stringvalue="Actions/Tools/org-sleuthkit-autopsy-menuactions-DataContentMenu.instance"/>
|
||||
<attr name="position" intvalue="150"/>
|
||||
|
@ -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,24 +30,23 @@ 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 {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(DataSourceGroupingNode.class.getName());
|
||||
|
||||
/**
|
||||
* Creates a data source grouping node for the given data source.
|
||||
*
|
||||
* @param dataSource specifies the data source
|
||||
* Creates a data source grouping node for the given data source.
|
||||
*
|
||||
* @param dataSource specifies the data source
|
||||
*/
|
||||
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) {
|
||||
@ -70,7 +69,7 @@ class DataSourceGroupingNode extends DisplayableItemNode {
|
||||
public boolean isLeafTypeNode() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private static RootContentChildren createDSGroupingNodeChildren(DataSource dataSource) {
|
||||
|
||||
long dsObjId = dataSource.getId();
|
||||
@ -78,11 +77,13 @@ 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
|
||||
return null;
|
||||
|
@ -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");
|
||||
@ -147,7 +147,7 @@ public class EmailExtracted implements AutopsyVisitableItem {
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public void update() {
|
||||
public void update() {
|
||||
// clear cache if no case
|
||||
if (skCase == null) {
|
||||
synchronized (accounts) {
|
||||
@ -157,22 +157,22 @@ 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<>();
|
||||
|
||||
|
||||
try (CaseDbQuery dbQuery = skCase.executeQuery(query)) {
|
||||
ResultSet resultSet = dbQuery.getResultSet();
|
||||
while (resultSet.next()) {
|
||||
@ -180,7 +180,7 @@ public class EmailExtracted implements AutopsyVisitableItem {
|
||||
Map<String, String> accountFolderMap = parsePath(resultSet.getString("value_text"));
|
||||
String account = accountFolderMap.get(MAIL_ACCOUNT);
|
||||
String folder = accountFolderMap.get(MAIL_FOLDER);
|
||||
|
||||
|
||||
Map<String, List<Long>> folders = newMapping.computeIfAbsent(account, (str) -> new LinkedHashMap<>());
|
||||
List<Long> messages = folders.computeIfAbsent(folder, (str) -> new ArrayList<>());
|
||||
messages.add(artifactId);
|
||||
@ -188,29 +188,31 @@ public class EmailExtracted implements AutopsyVisitableItem {
|
||||
} catch (TskCoreException | SQLException ex) {
|
||||
logger.log(Level.WARNING, "Cannot initialize email extraction: ", ex); //NON-NLS
|
||||
}
|
||||
|
||||
|
||||
|
||||
synchronized (accounts) {
|
||||
accounts.clear();
|
||||
accounts.putAll(newMapping);
|
||||
}
|
||||
|
||||
|
||||
setChanged();
|
||||
notifyObservers();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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();
|
||||
@ -167,7 +157,7 @@ public class RootContentChildren extends Children.Keys<Object> {
|
||||
public AbstractNode visit(Accounts accountsItem) {
|
||||
return accountsItem.new AccountsRootNode();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public AbstractNode visit(OsAccounts osAccountsItem) {
|
||||
return osAccountsItem.new OsAccountListNode();
|
||||
@ -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,7 +95,8 @@ 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();
|
||||
|
||||
@ -109,7 +112,7 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
|
||||
private final RejectAccounts rejectActionInstance;
|
||||
private final ApproveAccounts approveActionInstance;
|
||||
|
||||
|
||||
// tracks the number of each account type found
|
||||
private final AccountTypeResults accountTypeResults;
|
||||
|
||||
@ -131,7 +134,7 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
public Accounts(SleuthkitCase skCase, long objId) {
|
||||
this.skCase = skCase;
|
||||
this.filteringDSObjId = objId;
|
||||
|
||||
|
||||
this.rejectActionInstance = new RejectAccounts();
|
||||
this.approveActionInstance = new ApproveAccounts();
|
||||
this.accountTypeResults = new AccountTypeResults();
|
||||
@ -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,37 +261,71 @@ 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() {
|
||||
update();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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());
|
||||
Collections.sort(types);
|
||||
return types;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Queries the database and updates the counts for each account type.
|
||||
*/
|
||||
@ -293,14 +334,14 @@ 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 ";
|
||||
|
||||
try (SleuthkitCase.CaseDbQuery executeQuery = skCase.executeQuery(accountTypesInUseQuery);
|
||||
ResultSet resultSet = executeQuery.getResultSet()) {
|
||||
|
||||
|
||||
counts.clear();
|
||||
while (resultSet.next()) {
|
||||
String accountType = resultSet.getString("account_type");
|
||||
@ -317,7 +358,7 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
* Creates child nodes for each account type in the db.
|
||||
*/
|
||||
private class AccountTypeFactory extends ObservingChildren<String> {
|
||||
|
||||
|
||||
/*
|
||||
* The pcl is in this class because it has the easiest mechanisms to add
|
||||
* and remove itself during its life cycles.
|
||||
@ -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);
|
||||
}
|
||||
@ -391,12 +432,14 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
list.addAll(accountTypeResults.getTypes());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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,13 +619,15 @@ 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) {
|
||||
super(Children.create(new DefaultAccountFactory(accountType), true), Lookups.singleton(accountType));
|
||||
this.accountType = accountType;
|
||||
String iconPath = getIconFilePath(accountType);
|
||||
this.setIconBaseWithExtension(iconPath != null && iconPath.charAt(0) == '/' ? iconPath.substring(1) : iconPath); //NON-NLS
|
||||
setName(accountType.getTypeName());
|
||||
updateName();
|
||||
}
|
||||
|
||||
@ -600,8 +645,7 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
public String getItemType() {
|
||||
return getClass().getName();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Subscribe
|
||||
void handleReviewStatusChange(ReviewStatusChangeEvent event) {
|
||||
updateName();
|
||||
@ -611,12 +655,13 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
void handleDataAdded(ModuleDataEvent event) {
|
||||
updateName();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
@ -747,14 +792,15 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
setName(Account.Type.CREDIT_CARD.getDisplayName());
|
||||
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/credit-cards.png"); //NON-NLS
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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())));
|
||||
}
|
||||
|
||||
|
||||
@Subscribe
|
||||
void handleReviewStatusChange(ReviewStatusChangeEvent event) {
|
||||
updateName();
|
||||
@ -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,19 +201,21 @@ 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);
|
||||
if (!Objects.isNull(views)) {
|
||||
tree.expandNode(views);
|
||||
}
|
||||
|
||||
|
||||
// expand all nodes parents of and including hosts if group by host/person
|
||||
if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) {
|
||||
Node[] rootNodes = rootChildren.getNodes();
|
||||
@ -223,11 +227,13 @@ 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 |
@ -1,3 +1,5 @@
|
||||
DataContentDynamicMenu.contentViewers.text=Content Viewers
|
||||
DataContentDynamicMenu.mainContentViewer.name=Main
|
||||
OpenIDE-Module-Name=MenuActions
|
||||
DataContentDynamicMenu.menu.dataContentWin.text=Data Content Windows
|
||||
DataContentMenu.getName.text=DataContent Menu
|
||||
@ -5,3 +7,4 @@ DataExplorerMenu.getName.text=DataExplorer Tools
|
||||
DataResultMenu.menu.dataResWin.text=DataResult Windows
|
||||
DataResultMenu.getName.text=DataResult Menu
|
||||
SearchResultMenu.menu.dataRes.text=Data Results
|
||||
SearchResultMenu.resultViewers.text=Result Viewers
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-2018 Basis Technology Corp.
|
||||
* Copyright 2011-2021 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -23,60 +23,53 @@ import javax.swing.JComponent;
|
||||
import javax.swing.JMenu;
|
||||
import javax.swing.JMenuItem;
|
||||
import org.openide.awt.DynamicMenuContent;
|
||||
import org.openide.util.Lookup;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.windows.TopComponent;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContent;
|
||||
import org.sleuthkit.autopsy.corecomponents.DataContentTopComponent;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jantonius
|
||||
* Class to provide menu access to the various instances of the content viewer
|
||||
* suite.
|
||||
*/
|
||||
class DataContentDynamicMenu extends JMenuItem implements DynamicMenuContent {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@NbBundle.Messages({"DataContentDynamicMenu.mainContentViewer.name=Main",
|
||||
"DataContentDynamicMenu.contentViewers.text=Content Viewers"})
|
||||
@Override
|
||||
public JComponent[] getMenuPresenters() {
|
||||
List<DataContentTopComponent> newWindowLists = DataContentTopComponent.getNewWindowList();
|
||||
JMenu submenu = new JMenu(Bundle.DataContentDynamicMenu_contentViewers_text());
|
||||
if (Case.isCaseOpen()) {
|
||||
|
||||
// Get DataContent provider to include in the menu
|
||||
int totalItems = newWindowLists.size() > 0 ? 2 : 1;
|
||||
JComponent[] comps = new JComponent[totalItems];
|
||||
int counter = 0;
|
||||
List<DataContentTopComponent> newWindowLists = DataContentTopComponent.getNewWindowList();
|
||||
|
||||
TopComponent contentWin = DataContentTopComponent.findInstance();
|
||||
JMenuItem defaultItem = new JMenuItem(contentWin.getName()); // set the main name
|
||||
|
||||
defaultItem.addActionListener(new OpenTopComponentAction(contentWin));
|
||||
|
||||
try {
|
||||
Case currentCase = Case.getCurrentCaseThrows();
|
||||
defaultItem.setEnabled(currentCase.hasData());
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
defaultItem.setEnabled(false); // disable the menu when no case is opened
|
||||
}
|
||||
|
||||
comps[counter++] = defaultItem;
|
||||
|
||||
// add the submenu
|
||||
if (newWindowLists != null) {
|
||||
if (newWindowLists.size() > 0) {
|
||||
|
||||
JMenu submenu = new JMenu(
|
||||
NbBundle.getMessage(this.getClass(), "DataContentDynamicMenu.menu.dataContentWin.text"));
|
||||
TopComponent contentWin = DataContentTopComponent.findInstance();
|
||||
JMenuItem defaultItem = new JMenuItem(Bundle.DataContentDynamicMenu_mainContentViewer_name()); // set the main name
|
||||
defaultItem.addActionListener(new OpenTopComponentAction(contentWin));
|
||||
try {
|
||||
Case currentCase = Case.getCurrentCaseThrows();
|
||||
defaultItem.setEnabled(currentCase.hasData());
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
defaultItem.setEnabled(false); // disable the menu when no case is opened
|
||||
}
|
||||
submenu.add(defaultItem);
|
||||
// add the submenu
|
||||
if (!newWindowLists.isEmpty()) {
|
||||
for (int i = 0; i < newWindowLists.size(); i++) {
|
||||
DataContentTopComponent dctc = newWindowLists.get(i);
|
||||
JMenuItem item = new JMenuItem(dctc.getName());
|
||||
item.addActionListener(new OpenTopComponentAction(dctc));
|
||||
submenu.add(item);
|
||||
}
|
||||
|
||||
comps[counter++] = submenu;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
submenu.setEnabled(submenu.getItemCount() > 0);
|
||||
JComponent[] comps = new JComponent[1];
|
||||
comps[0] = submenu;
|
||||
return comps;
|
||||
}
|
||||
|
||||
@ -84,4 +77,9 @@ class DataContentDynamicMenu extends JMenuItem implements DynamicMenuContent {
|
||||
public JComponent[] synchMenuPresenters(JComponent[] jcs) {
|
||||
return getMenuPresenters();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return Case.isCaseOpen();
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011 Basis Technology Corp.
|
||||
* Copyright 2011-2021 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -25,45 +25,34 @@ import javax.swing.JMenu;
|
||||
import javax.swing.JMenuItem;
|
||||
import org.openide.awt.DynamicMenuContent;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.corecomponents.DataResultTopComponent;
|
||||
import org.sleuthkit.autopsy.directorytree.DirectoryTreeTopComponent;
|
||||
|
||||
/**
|
||||
* Menu item lists DataResult tabs.
|
||||
* Class to provide menu access to the various result viewers.
|
||||
*/
|
||||
class SearchResultMenu extends JMenuItem implements DynamicMenuContent {
|
||||
|
||||
SearchResultMenu() {
|
||||
|
||||
}
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@NbBundle.Messages({"SearchResultMenu.resultViewers.text=Result Viewers"})
|
||||
@Override
|
||||
public JComponent[] getMenuPresenters() {
|
||||
JMenu submenu = new JMenu(Bundle.SearchResultMenu_resultViewers_text());
|
||||
List<String> dataResultsIds = DataResultTopComponent.getActiveComponentIds();
|
||||
DirectoryTreeTopComponent directoryTree = DirectoryTreeTopComponent.findInstance();
|
||||
DataResultTopComponent directoryListing = directoryTree.getDirectoryListing();
|
||||
|
||||
List<JComponent> menuItems = new ArrayList<JComponent>();
|
||||
|
||||
// add the main "DirectoryListing"
|
||||
JMenuItem dlItem = new JMenuItem(directoryListing.getName());
|
||||
dlItem.addActionListener(new OpenTopComponentAction(directoryListing));
|
||||
dlItem.setEnabled(directoryTree.isOpened());
|
||||
|
||||
menuItems.add(dlItem);
|
||||
|
||||
// add search results if there are any
|
||||
if (dataResultsIds.size() > 0) {
|
||||
JMenu submenu = new JMenu(NbBundle.getMessage(this.getClass(), "SearchResultMenu.menu.dataRes.text"));
|
||||
for (String resultTabId : dataResultsIds) {
|
||||
JMenuItem item = new JMenuItem(resultTabId);
|
||||
item.addActionListener(new OpenTopComponentAction(resultTabId));
|
||||
submenu.add(item);
|
||||
if (Case.isCaseOpen()) {
|
||||
// add search results if there are any
|
||||
if (!dataResultsIds.isEmpty()) {
|
||||
for (String resultTabId : dataResultsIds) {
|
||||
JMenuItem item = new JMenuItem(resultTabId);
|
||||
item.addActionListener(new OpenTopComponentAction(resultTabId));
|
||||
submenu.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
menuItems.add(submenu);
|
||||
}
|
||||
|
||||
submenu.setEnabled(!dataResultsIds.isEmpty());
|
||||
List<JComponent> menuItems = new ArrayList<>();
|
||||
menuItems.add(submenu);
|
||||
return menuItems.toArray(new JComponent[menuItems.size()]);
|
||||
}
|
||||
|
||||
@ -71,4 +60,10 @@ class SearchResultMenu extends JMenuItem implements DynamicMenuContent {
|
||||
public JComponent[] synchMenuPresenters(JComponent[] jcs) {
|
||||
return getMenuPresenters();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return Case.isCaseOpen();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2015-2019 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.imagegallery;
|
||||
|
||||
import org.netbeans.api.progress.ProgressHandle;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableDB;
|
||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
|
||||
/**
|
||||
* A task that queries the case database for all files with supported
|
||||
* image/video mime types or extensions and adds them to the drawables database.
|
||||
*/
|
||||
class AddDrawableFilesTask extends BulkDrawableFilesTask {
|
||||
|
||||
private final ImageGalleryController controller;
|
||||
private final DrawableDB taskDB;
|
||||
|
||||
AddDrawableFilesTask(long dataSourceObjId, ImageGalleryController controller) {
|
||||
super(dataSourceObjId, controller);
|
||||
this.controller = controller;
|
||||
this.taskDB = controller.getDrawablesDatabase();
|
||||
taskDB.buildFileMetaDataCache();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cleanup() {
|
||||
taskDB.freeFileMetaDataCache();
|
||||
// at the end of the task, set the stale status based on the
|
||||
// cumulative status of all data sources
|
||||
controller.setModelIsStale(controller.isDataSourcesTableStale());
|
||||
}
|
||||
|
||||
@Override
|
||||
void processFile(AbstractFile f, DrawableDB.DrawableTransaction tr, SleuthkitCase.CaseDbTransaction caseDbTransaction) throws TskCoreException {
|
||||
final boolean known = f.getKnown() == TskData.FileKnown.KNOWN;
|
||||
if (known) {
|
||||
taskDB.removeFile(f.getId(), tr); //remove known files
|
||||
} else {
|
||||
// NOTE: Files are being processed because they have the right MIME type,
|
||||
// so we do not need to worry about this calculating them
|
||||
if (FileTypeUtils.hasDrawableMIMEType(f)) {
|
||||
taskDB.updateFile(DrawableFile.create(f, true, false), tr, caseDbTransaction);
|
||||
} //unsupported mimtype => analyzed but shouldn't include
|
||||
else {
|
||||
taskDB.removeFile(f.getId(), tr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@NbBundle.Messages({
|
||||
"AddDrawableFilesTask.populatingDb.status=populating analyzed image/video database"
|
||||
})
|
||||
ProgressHandle getInitialProgressHandle() {
|
||||
return ProgressHandle.createHandle(Bundle.AddDrawableFilesTask_populatingDb_status(), this);
|
||||
}
|
||||
}
|
@ -1,12 +1,11 @@
|
||||
AddDrawableFilesTask.populatingDb.status=populating analyzed image/video database
|
||||
BulkDrawableFilesTask.committingDb.status=committing image/video database
|
||||
BulkDrawableFilesTask.errPopulating.errMsg=There was an error populating Image Gallery database.
|
||||
BulkDrawableFilesTask.populatingDb.status=populating analyzed image/video database
|
||||
BulkDrawableFilesTask.stopCopy.status=Stopping copy to drawable db task.
|
||||
CTL_ImageGalleryAction=Image/Video Gallery
|
||||
CTL_ImageGalleryTopComponent=Image/Video Gallery
|
||||
DrawableDbTask.InnerTask.message.name=status
|
||||
DrawableDbTask.InnerTask.progress.name=progress
|
||||
DrawableFileUpdateTask_committingDb.status=committing image/video database
|
||||
DrawableFileUpdateTask_errPopulating_errMsg=There was an error populating Image Gallery database.
|
||||
DrawableFileUpdateTask_populatingDb_status=populating analyzed image/video database
|
||||
DrawableFileUpdateTask_stopCopy_status=Stopping copy to drawable db task.
|
||||
ImageGallery.dialogTitle=Image Gallery
|
||||
ImageGallery.showTooManyFiles.contentText=There are too many files in the selected datasource(s) to ensure reasonable performance.
|
||||
ImageGallery.showTooManyFiles.headerText=
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2015-2019 Basis Technology Corp.
|
||||
* Copyright 2021 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -22,100 +22,159 @@ import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import org.netbeans.api.progress.ProgressHandle;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableDB;
|
||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
|
||||
/**
|
||||
* An abstract base class for tasks that add or modify the drawables database
|
||||
* records for multiple drawable files.
|
||||
* A bulk update task for adding images to the image gallery.
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"BulkDrawableFilesTask.committingDb.status=committing image/video database",
|
||||
"BulkDrawableFilesTask.stopCopy.status=Stopping copy to drawable db task.",
|
||||
"BulkDrawableFilesTask.errPopulating.errMsg=There was an error populating Image Gallery database."
|
||||
})
|
||||
abstract class BulkDrawableFilesTask extends DrawableDbTask {
|
||||
final class DrawableFileUpdateTask extends DrawableDbTask {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(DrawableFileUpdateTask.class.getName());
|
||||
|
||||
private static final Logger logger = Logger.getLogger(BulkDrawableFilesTask.class.getName());
|
||||
private static final String MIMETYPE_CLAUSE = "(mime_type LIKE '" //NON-NLS
|
||||
+ String.join("' OR mime_type LIKE '", FileTypeUtils.getAllSupportedMimeTypes()) //NON-NLS
|
||||
+ "') ";
|
||||
private final String drawableQuery;
|
||||
private final ImageGalleryController controller;
|
||||
private final DrawableDB taskDB;
|
||||
private final SleuthkitCase tskCase;
|
||||
private final long dataSourceObjId;
|
||||
|
||||
//NON-NLS
|
||||
BulkDrawableFilesTask(long dataSourceObjId, ImageGalleryController controller) {
|
||||
private final ImageGalleryController controller;
|
||||
|
||||
/**
|
||||
* Construct a new task.
|
||||
*
|
||||
* @param controller A handle to the IG controller.
|
||||
*/
|
||||
DrawableFileUpdateTask(ImageGalleryController controller) {
|
||||
this.controller = controller;
|
||||
this.taskDB = controller.getDrawablesDatabase();
|
||||
this.tskCase = controller.getCaseDatabase();
|
||||
this.dataSourceObjId = dataSourceObjId;
|
||||
drawableQuery = " (data_source_obj_id = " + dataSourceObjId + ") "
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
for (Long dataSourceObjId : controller.getStaleDataSourceIds()) {
|
||||
updateFileForDataSource(dataSourceObjId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the drawables database that is part of the model for the controller.
|
||||
*
|
||||
* @return The the drawable db object.
|
||||
*/
|
||||
private DrawableDB getDrawableDB() {
|
||||
return controller.getDrawablesDatabase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the sleuthkit case object for the open case.
|
||||
*
|
||||
* @return The case db object.
|
||||
*/
|
||||
private SleuthkitCase getCaseDB() {
|
||||
return controller.getCaseDatabase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of files to be processed by the task for the given
|
||||
* datasource.
|
||||
*
|
||||
* @param dataSourceObjId
|
||||
* @return
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
private List<AbstractFile> getFilesForDataSource(long dataSourceObjId) throws TskCoreException {
|
||||
List<AbstractFile> list = getCaseDB().findAllFilesWhere(getDrawableQuery(dataSourceObjId));
|
||||
return list;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a single file for the IG drawable db.
|
||||
*
|
||||
* @param file The file to process.
|
||||
* @param tr A valid DrawableTransaction object.
|
||||
* @param caseDbTransaction A valid caseDBTransaction object.
|
||||
*
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
void processFile(AbstractFile file, DrawableDB.DrawableTransaction tr, SleuthkitCase.CaseDbTransaction caseDbTransaction) throws TskCoreException {
|
||||
final boolean known = file.getKnown() == TskData.FileKnown.KNOWN;
|
||||
if (known) {
|
||||
getDrawableDB().removeFile(file.getId(), tr); //remove known files
|
||||
} else {
|
||||
// NOTE: Files are being processed because they have the right MIME type,
|
||||
// so we do not need to worry about this calculating them
|
||||
if (FileTypeUtils.hasDrawableMIMEType(file)) {
|
||||
getDrawableDB().updateFile(DrawableFile.create(file, true, false), tr, caseDbTransaction);
|
||||
} //unsupported mimtype => analyzed but shouldn't include
|
||||
else {
|
||||
getDrawableDB().removeFile(file.getId(), tr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the image query for the given data source.
|
||||
*
|
||||
* @param dataSourceObjId
|
||||
*
|
||||
* @return SQL query for given data source.
|
||||
*/
|
||||
private String getDrawableQuery(long dataSourceObjId) {
|
||||
return " (data_source_obj_id = " + dataSourceObjId + ") "
|
||||
+ " AND ( meta_type = " + TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG.getValue() + ")" + " AND ( " + MIMETYPE_CLAUSE //NON-NLS
|
||||
+ " OR mime_type LIKE 'video/%' OR mime_type LIKE 'image/%' )" //NON-NLS
|
||||
+ " ORDER BY parent_path ";
|
||||
}
|
||||
|
||||
/**
|
||||
* Do any cleanup for this task.
|
||||
*/
|
||||
abstract void cleanup();
|
||||
|
||||
abstract void processFile(final AbstractFile f, DrawableDB.DrawableTransaction tr, SleuthkitCase.CaseDbTransaction caseDBTransaction) throws TskCoreException;
|
||||
|
||||
/**
|
||||
* Gets a list of files to process.
|
||||
*
|
||||
* @return list of files to process
|
||||
*
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
List<AbstractFile> getFiles() throws TskCoreException {
|
||||
return tskCase.findAllFilesWhere(drawableQuery);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NbBundle.Messages({
|
||||
"BulkDrawableFilesTask.populatingDb.status=populating analyzed image/video database"
|
||||
@Messages({
|
||||
"DrawableFileUpdateTask_populatingDb_status=populating analyzed image/video database",
|
||||
"DrawableFileUpdateTask_committingDb.status=committing image/video database",
|
||||
"DrawableFileUpdateTask_stopCopy_status=Stopping copy to drawable db task.",
|
||||
"DrawableFileUpdateTask_errPopulating_errMsg=There was an error populating Image Gallery database."
|
||||
})
|
||||
public void run() {
|
||||
private void updateFileForDataSource(long dataSourceObjId) {
|
||||
ProgressHandle progressHandle = getInitialProgressHandle();
|
||||
progressHandle.start();
|
||||
updateMessage(Bundle.BulkDrawableFilesTask_populatingDb_status() + " (Data Source " + dataSourceObjId + ")");
|
||||
updateMessage(Bundle.DrawableFileUpdateTask_populatingDb_status() + " (Data Source " + dataSourceObjId + ")");
|
||||
|
||||
DrawableDB.DrawableTransaction drawableDbTransaction = null;
|
||||
SleuthkitCase.CaseDbTransaction caseDbTransaction = null;
|
||||
boolean hasFilesWithNoMime = true;
|
||||
boolean endedEarly = false;
|
||||
try {
|
||||
|
||||
getDrawableDB().buildFileMetaDataCache();
|
||||
// See if there are any files in the DS w/out a MIME TYPE
|
||||
hasFilesWithNoMime = controller.hasFilesWithNoMimeType(dataSourceObjId);
|
||||
|
||||
//grab all files with detected mime types
|
||||
final List<AbstractFile> files = getFiles();
|
||||
final List<AbstractFile> files = getFilesForDataSource(dataSourceObjId);
|
||||
progressHandle.switchToDeterminate(files.size());
|
||||
taskDB.insertOrUpdateDataSource(dataSourceObjId, DrawableDB.DrawableDbBuildStatusEnum.IN_PROGRESS);
|
||||
getDrawableDB().insertOrUpdateDataSource(dataSourceObjId, DrawableDB.DrawableDbBuildStatusEnum.IN_PROGRESS);
|
||||
updateProgress(0.0);
|
||||
int workDone = 0;
|
||||
// Cycle through all of the files returned and call processFile on each
|
||||
//do in transaction
|
||||
drawableDbTransaction = taskDB.beginTransaction();
|
||||
drawableDbTransaction = getDrawableDB().beginTransaction();
|
||||
/*
|
||||
* We are going to periodically commit the CaseDB transaction and
|
||||
* sleep so that the user can have Autopsy do other stuff while
|
||||
* these bulk tasks are ongoing.
|
||||
*/
|
||||
int caseDbCounter = 0;
|
||||
|
||||
for (final AbstractFile f : files) {
|
||||
updateMessage(f.getName());
|
||||
if (caseDbTransaction == null) {
|
||||
caseDbTransaction = tskCase.beginTransaction();
|
||||
caseDbTransaction = getCaseDB().beginTransaction();
|
||||
}
|
||||
|
||||
if (isCancelled() || Thread.interrupted()) {
|
||||
logger.log(Level.WARNING, "Task cancelled or interrupted: not all contents may be transfered to drawable database."); //NON-NLS
|
||||
endedEarly = true;
|
||||
@ -132,20 +191,19 @@ abstract class BulkDrawableFilesTask extends DrawableDbTask {
|
||||
if ((++caseDbCounter % 200) == 0) {
|
||||
caseDbTransaction.commit();
|
||||
caseDbTransaction = null;
|
||||
Thread.sleep(500); // 1/2 second
|
||||
Thread.sleep(500); // 1/2 millisecond
|
||||
}
|
||||
}
|
||||
progressHandle.finish();
|
||||
progressHandle = ProgressHandle.createHandle(Bundle.BulkDrawableFilesTask_committingDb_status());
|
||||
updateMessage(Bundle.BulkDrawableFilesTask_committingDb_status() + " (Data Source " + dataSourceObjId + ")");
|
||||
progressHandle = ProgressHandle.createHandle(Bundle.DrawableFileUpdateTask_committingDb_status());
|
||||
updateMessage(Bundle.DrawableFileUpdateTask_committingDb_status() + " (Data Source " + dataSourceObjId + ")");
|
||||
updateProgress(1.0);
|
||||
progressHandle.start();
|
||||
if (caseDbTransaction != null) {
|
||||
caseDbTransaction.commit();
|
||||
caseDbTransaction = null;
|
||||
}
|
||||
// pass true so that groupmanager is notified of the changes
|
||||
taskDB.commitTransaction(drawableDbTransaction, true);
|
||||
getDrawableDB().commitTransaction(drawableDbTransaction, true);
|
||||
drawableDbTransaction = null;
|
||||
} catch (TskCoreException | SQLException | InterruptedException ex) {
|
||||
if (null != caseDbTransaction) {
|
||||
@ -157,14 +215,14 @@ abstract class BulkDrawableFilesTask extends DrawableDbTask {
|
||||
}
|
||||
if (null != drawableDbTransaction) {
|
||||
try {
|
||||
taskDB.rollbackTransaction(drawableDbTransaction);
|
||||
getDrawableDB().rollbackTransaction(drawableDbTransaction);
|
||||
} catch (SQLException ex2) {
|
||||
logger.log(Level.SEVERE, String.format("Failed to roll back drawables db transaction after error: %s", ex.getMessage()), ex2); //NON-NLS
|
||||
}
|
||||
}
|
||||
progressHandle.progress(Bundle.BulkDrawableFilesTask_stopCopy_status());
|
||||
progressHandle.progress(Bundle.DrawableFileUpdateTask_stopCopy_status());
|
||||
logger.log(Level.WARNING, "Stopping copy to drawable db task. Failed to transfer all database contents", ex); //NON-NLS
|
||||
MessageNotifyUtil.Notify.warn(Bundle.BulkDrawableFilesTask_errPopulating_errMsg(), ex.getMessage());
|
||||
MessageNotifyUtil.Notify.warn(Bundle.DrawableFileUpdateTask_errPopulating_errMsg(), ex.getMessage());
|
||||
endedEarly = true;
|
||||
} finally {
|
||||
progressHandle.finish();
|
||||
@ -172,15 +230,27 @@ abstract class BulkDrawableFilesTask extends DrawableDbTask {
|
||||
// if there was cancellation or errors
|
||||
DrawableDB.DrawableDbBuildStatusEnum datasourceDrawableDBStatus = ((hasFilesWithNoMime == true) || (endedEarly == true)) ? DrawableDB.DrawableDbBuildStatusEnum.REBUILT_STALE : DrawableDB.DrawableDbBuildStatusEnum.COMPLETE;
|
||||
try {
|
||||
taskDB.insertOrUpdateDataSource(dataSourceObjId, datasourceDrawableDBStatus);
|
||||
getDrawableDB().insertOrUpdateDataSource(dataSourceObjId, datasourceDrawableDBStatus);
|
||||
} catch (SQLException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Error updating datasources table (data source object ID = %d, status = %s)", dataSourceObjId, datasourceDrawableDBStatus.toString(), ex)); //NON-NLS
|
||||
}
|
||||
updateMessage("");
|
||||
updateProgress(-1.0);
|
||||
|
||||
getDrawableDB().freeFileMetaDataCache();
|
||||
// at the end of the task, set the stale status based on the
|
||||
// cumulative status of all data sources
|
||||
controller.setModelIsStale(controller.isDataSourcesTableStale());
|
||||
}
|
||||
cleanup();
|
||||
|
||||
}
|
||||
|
||||
abstract ProgressHandle getInitialProgressHandle();
|
||||
/**
|
||||
* Returns a ProgressHandle.
|
||||
*
|
||||
* @return A new ProgressHandle.
|
||||
*/
|
||||
private ProgressHandle getInitialProgressHandle() {
|
||||
return ProgressHandle.createHandle(Bundle.DrawableFileUpdateTask_populatingDb_status(), this);
|
||||
}
|
||||
}
|
@ -486,8 +486,7 @@ public final class ImageGalleryController {
|
||||
*
|
||||
*/
|
||||
public void rebuildDrawablesDb() {
|
||||
// queue a rebuild task for each stale data source
|
||||
getStaleDataSourceIds().forEach(dataSourceObjId -> queueDBTask(new AddDrawableFilesTask(dataSourceObjId, this)));
|
||||
queueDBTask(new DrawableFileUpdateTask(this));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -670,7 +669,7 @@ public final class ImageGalleryController {
|
||||
*
|
||||
* @param bgTask
|
||||
*/
|
||||
public synchronized void queueDBTask(DrawableDbTask bgTask) {
|
||||
public synchronized void queueDBTask(Runnable bgTask) {
|
||||
if (!dbExecutor.isShutdown()) {
|
||||
incrementQueueSize();
|
||||
dbExecutor.submit(bgTask).addListener(this::decrementQueueSize, MoreExecutors.directExecutor());
|
||||
|
@ -37,7 +37,7 @@ OpenAction.noControllerDialog.text=An initialization error ocurred.\nPlease see
|
||||
OpenAction.notAnalyzedDlg.msg=No image/video files available to display yet.\nPlease run FileType and EXIF ingest modules.
|
||||
OpenAction.openTopComponent.error.message=An error occurred while attempting to open Image Gallery.
|
||||
OpenAction.openTopComponent.error.title=Failed to open Image Gallery
|
||||
OpenAction.stale.confDlg.msg=The image / video database may be out of date. Do you want to update and listen for further ingest results?\nChoosing 'yes' will update the database and enable listening to future ingests.
|
||||
OpenAction.stale.confDlg.msg=The image / video database may be out of date. Do you want to update and listen for further ingest results?\nChoosing 'yes' will update the database and enable listening to future ingests.\n\nDatabase update status will appear in the lower right corner of the application window.
|
||||
OpenAction.stale.confDlg.title=Image Gallery
|
||||
OpenExternalViewerAction.displayName=External Viewer
|
||||
RedoAction.name=Redo
|
||||
|
@ -40,6 +40,7 @@ import org.openide.awt.ActionID;
|
||||
import org.openide.awt.ActionReference;
|
||||
import org.openide.awt.ActionReferences;
|
||||
import org.openide.awt.ActionRegistration;
|
||||
import org.openide.util.Exceptions;
|
||||
import org.openide.util.HelpCtx;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
@ -69,7 +70,8 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
@Messages({"CTL_OpenAction=Images/Videos",
|
||||
"OpenAction.stale.confDlg.msg=The image / video database may be out of date. "
|
||||
+ "Do you want to update and listen for further ingest results?\n"
|
||||
+ "Choosing 'yes' will update the database and enable listening to future ingests.",
|
||||
+ "Choosing 'yes' will update the database and enable listening to future ingests.\n\n"
|
||||
+ "Database update status will appear in the lower right corner of the application window.",
|
||||
"OpenAction.notAnalyzedDlg.msg=No image/video files available to display yet.\n"
|
||||
+ "Please run FileType and EXIF ingest modules.",
|
||||
"OpenAction.stale.confDlg.title=Image Gallery"})
|
||||
@ -254,28 +256,7 @@ public final class OpenAction extends CallableSystemAction {
|
||||
// They don't want to rebuild. Just open the UI as is.
|
||||
// NOTE: There could be no data....
|
||||
} else if (answer == ButtonType.YES) {
|
||||
if (controller.getCase().getCaseType() == Case.CaseType.SINGLE_USER_CASE) {
|
||||
/*
|
||||
* For a single-user case, we favor user
|
||||
* experience, and rebuild the database as soon
|
||||
* as Image Gallery is enabled for the case.
|
||||
*
|
||||
* Turning listening off is necessary in order
|
||||
* to invoke the listener that will call
|
||||
* controller.rebuildDB();
|
||||
*/
|
||||
controller.setListeningEnabled(false);
|
||||
controller.setListeningEnabled(true);
|
||||
} else {
|
||||
/*
|
||||
* For a multi-user case, we favor overall
|
||||
* performance and user experience, not every
|
||||
* user may want to review images, so we rebuild
|
||||
* the database only when a user launches Image
|
||||
* Gallery.
|
||||
*/
|
||||
controller.rebuildDrawablesDb();
|
||||
}
|
||||
controller.rebuildDrawablesDb();
|
||||
}
|
||||
openTopComponent();
|
||||
return;
|
||||
|
@ -66,6 +66,7 @@ import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
||||
import org.sleuthkit.autopsy.imagegallery.actions.CategorizeGroupAction;
|
||||
import org.sleuthkit.autopsy.imagegallery.actions.TagGroupAction;
|
||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute;
|
||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableDB;
|
||||
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.DrawableGroup;
|
||||
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupSortBy;
|
||||
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupViewState;
|
||||
@ -236,8 +237,8 @@ public class Toolbar extends ToolBar {
|
||||
}
|
||||
|
||||
private void initDataSourceComboBox() {
|
||||
dataSourceComboBox.setCellFactory(param -> new DataSourceCell(dataSourcesViewable, controller.getAllDataSourcesDrawableDBStatus()));
|
||||
dataSourceComboBox.setButtonCell(new DataSourceCell(dataSourcesViewable, controller.getAllDataSourcesDrawableDBStatus()));
|
||||
dataSourceComboBox.setCellFactory(param -> new DataSourceCell(dataSourcesViewable, new HashMap<>()));
|
||||
dataSourceComboBox.setButtonCell(new DataSourceCell(dataSourcesViewable, new HashMap<>()));
|
||||
dataSourceComboBox.setConverter(new StringConverter<Optional<DataSource>>() {
|
||||
@Override
|
||||
public String toString(Optional<DataSource> object) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user