add tag actions to user created events.

cleanup related files
This commit is contained in:
millmanorama 2019-03-12 14:31:04 +01:00
parent f8134a3d82
commit 671f95c0e7
5 changed files with 76 additions and 97 deletions

View File

@ -51,7 +51,7 @@ public abstract class DisplayableItemNode extends AbstractNode {
* *
* @throws TskCoreException * @throws TskCoreException
*/ */
static AbstractFile findLinked(BlackboardArtifact artifact) throws TskCoreException { protected static AbstractFile findLinked(BlackboardArtifact artifact) throws TskCoreException {
BlackboardAttribute pathIDAttribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID)); BlackboardAttribute pathIDAttribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID));
if (pathIDAttribute != null) { if (pathIDAttribute != null) {
long contentID = pathIDAttribute.getValueLong(); long contentID = pathIDAttribute.getValueLong();

View File

@ -59,7 +59,6 @@ import org.openide.windows.RetainLocation;
import org.openide.windows.TopComponent; import org.openide.windows.TopComponent;
import org.openide.windows.WindowManager; import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.actions.AddBookmarkTagAction; import org.sleuthkit.autopsy.actions.AddBookmarkTagAction;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContent; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContent;
import org.sleuthkit.autopsy.corecomponents.DataContentPanel; import org.sleuthkit.autopsy.corecomponents.DataContentPanel;
import org.sleuthkit.autopsy.corecomponents.DataResultPanel; import org.sleuthkit.autopsy.corecomponents.DataResultPanel;
@ -168,7 +167,7 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer
// make a copy because this list gets updated as the user navigates around // make a copy because this list gets updated as the user navigates around
// and causes concurrent access exceptions // and causes concurrent access exceptions
List<Long> selectedEventIDs = ImmutableList.copyOf(controller.getSelectedEventIDs()); List<Long> selectedEventIDs = ImmutableList.copyOf(controller.getSelectedEventIDs());
//depending on the active view mode, we either update the dataResultPanel, or update the contentViewerPanel directly. //depending on the active view mode, we either update the dataResultPanel, or update the contentViewerPanel directly.
switch (controller.getViewMode()) { switch (controller.getViewMode()) {
case LIST: case LIST:
@ -198,9 +197,6 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer
contentViewerPanel.setNode(null); contentViewerPanel.setNode(null);
} }
}); });
} catch (NoCurrentCaseException ex) {
//Since the case is closed, the user probably doesn't care about this, just log it as a precaution.
logger.log(Level.SEVERE, "There was no case open to lookup the Sleuthkit object backing a SingleEvent.", ex); // NON-NLS
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Failed to lookup Sleuthkit object backing a SingleEvent.", ex); // NON-NLS logger.log(Level.SEVERE, "Failed to lookup Sleuthkit object backing a SingleEvent.", ex); // NON-NLS
Platform.runLater(() -> { Platform.runLater(() -> {

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2018 Basis Technology Corp. * Copyright 2011-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -21,9 +21,12 @@ package org.sleuthkit.autopsy.timeline.explorernodes;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
import javax.annotation.Nonnull;
import javax.swing.Action; import javax.swing.Action;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.joda.time.DateTimeZone; import org.joda.time.DateTimeZone;
@ -31,9 +34,11 @@ import org.openide.nodes.Children;
import org.openide.nodes.PropertySupport; import org.openide.nodes.PropertySupport;
import org.openide.nodes.Sheet; import org.openide.nodes.Sheet;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.Utilities;
import org.openide.util.lookup.Lookups; import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.actions.AddBlackboardArtifactTagAction;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.actions.DeleteFileBlackboardArtifactTagAction;
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.datamodel.DataModelActionsFactory; import org.sleuthkit.autopsy.datamodel.DataModelActionsFactory;
@ -46,7 +51,6 @@ import org.sleuthkit.autopsy.timeline.actions.ViewFileInTimelineAction;
import org.sleuthkit.autopsy.timeline.ui.EventTypeUtils; import org.sleuthkit.autopsy.timeline.ui.EventTypeUtils;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
@ -59,17 +63,32 @@ public class EventNode extends DisplayableItemNode {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static final Logger LOGGER = Logger.getLogger(EventNode.class.getName()); private static final Logger logger = Logger.getLogger(EventNode.class.getName());
private final TimelineEvent event; private final TimelineEvent event;
EventNode(TimelineEvent event, Content file, BlackboardArtifact artifact) { /**
* Construct an EvetNode for an event with a Content and a
* BlackboardArtifact in its lookup.
*
* @param event The event this node is for.
* @param file The Content the artifact for this event is derived form.
* Not Null.
* @param artifact The artifact this event is derived from. Not Null.
*/
EventNode(@Nonnull TimelineEvent event, @Nonnull Content file, @Nonnull BlackboardArtifact artifact) {
super(Children.LEAF, Lookups.fixed(event, file, artifact)); super(Children.LEAF, Lookups.fixed(event, file, artifact));
this.event = event; this.event = event;
this.setIconBaseWithExtension(EventTypeUtils.getImagePath(event.getEventType())); // NON-NLS this.setIconBaseWithExtension(EventTypeUtils.getImagePath(event.getEventType())); // NON-NLS
} }
EventNode(TimelineEvent event, Content file) { /**
* Construct an EvetNode for an event with a Content in its lookup.
*
* @param event The event this node is for.
* @param file The Content this event is derived directly from. Not Null.
*/
EventNode(@Nonnull TimelineEvent event, @Nonnull Content file) {
super(Children.LEAF, Lookups.fixed(event, file)); super(Children.LEAF, Lookups.fixed(event, file));
this.event = event; this.event = event;
this.setIconBaseWithExtension(EventTypeUtils.getImagePath(event.getEventType())); // NON-NLS this.setIconBaseWithExtension(EventTypeUtils.getImagePath(event.getEventType())); // NON-NLS
@ -117,40 +136,43 @@ public class EventNode extends DisplayableItemNode {
"EventNode.getAction.linkedFileMessage=There was a problem getting actions for the selected result. " "EventNode.getAction.linkedFileMessage=There was a problem getting actions for the selected result. "
+ " The 'View File in Timeline' action will not be available."}) + " The 'View File in Timeline' action will not be available."})
public Action[] getActions(boolean context) { public Action[] getActions(boolean context) {
Action[] superActions = super.getActions(context);
List<Action> actionsList = new ArrayList<>(); List<Action> actionsList = new ArrayList<>();
actionsList.addAll(Arrays.asList(superActions)); Collections.addAll(actionsList, super.getActions(context));
final Content sourceFile = getLookup().lookup(Content.class);
/* /*
* if this event is derived from an artifact, add actions to view the * If this event is derived from an artifact, add actions to view the
* source file and a "linked" file, if present. * source file and a "linked" file, if present.
*/ */
final BlackboardArtifact artifact = getLookup().lookup(BlackboardArtifact.class); final BlackboardArtifact artifact = getLookup().lookup(BlackboardArtifact.class);
final Content sourceFile = getLookup().lookup(Content.class);
if (artifact != null) { if (artifact != null) {
try { try {
//find a linked file such as a downloaded file.
AbstractFile linkedfile = findLinked(artifact); AbstractFile linkedfile = findLinked(artifact);
if (linkedfile != null) { if (linkedfile != null) {
actionsList.add(ViewFileInTimelineAction.createViewFileAction(linkedfile)); actionsList.add(ViewFileInTimelineAction.createViewFileAction(linkedfile));
} }
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, MessageFormat.format("Error getting linked file from blackboard artifact{0}.", artifact.getArtifactID()), ex); //NON-NLS logger.log(Level.SEVERE, MessageFormat.format("Error getting linked file from blackboard artifact{0}.", artifact.getArtifactID()), ex); //NON-NLS
MessageNotifyUtil.Notify.error(Bundle.EventNode_getAction_errorTitle(), Bundle.EventNode_getAction_linkedFileMessage()); MessageNotifyUtil.Notify.error(Bundle.EventNode_getAction_errorTitle(), Bundle.EventNode_getAction_linkedFileMessage());
} }
//if this event has associated content, add the action to view the content in the timeline //add the action to view the content in the timeline, only for abstract files ( ie with times)
if (null != sourceFile) { if (sourceFile instanceof AbstractFile) {
if (sourceFile instanceof AbstractFile) { actionsList.add(ViewFileInTimelineAction.createViewSourceFileAction((AbstractFile) sourceFile));
actionsList.add(ViewFileInTimelineAction.createViewSourceFileAction((AbstractFile) sourceFile));
}
} }
} }
//get default actions for the source file //get default actions for the source file
final List<Action> factoryActions = DataModelActionsFactory.getActions(sourceFile, artifact != null); List<Action> factoryActions = DataModelActionsFactory.getActions(sourceFile, artifact != null);
actionsList.addAll(factoryActions); actionsList.addAll(factoryActions);
if (factoryActions.isEmpty()) { // if there were no factory supplied actions, at least add the tagging actions.
actionsList.add(AddBlackboardArtifactTagAction.getInstance());
if (isExactlyOneArtifactSelected()) {
actionsList.add(DeleteFileBlackboardArtifactTagAction.getInstance());
}
actionsList.addAll(ContextMenuExtensionPoint.getActions());
}
return actionsList.toArray(new Action[actionsList.size()]); return actionsList.toArray(new Action[actionsList.size()]);
} }
@ -190,7 +212,7 @@ public class EventNode extends DisplayableItemNode {
try { try {
setValue(getDateTimeString()); setValue(getDateTimeString());
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
LOGGER.log(Level.SEVERE, "Unexpected error setting date/time property on EventNode explorer node", ex); //NON-NLS logger.log(Level.SEVERE, "Unexpected error setting date/time property on EventNode explorer node", ex); //NON-NLS
} }
}); });
@ -202,10 +224,10 @@ public class EventNode extends DisplayableItemNode {
} }
@Override @Override
public void setValue(String t) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { public void setValue(String newValue) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
String oldValue = getValue(); String oldValue = getValue();
value = t; value = newValue;
firePropertyChange("time", oldValue, t); // NON-NLS firePropertyChange("time", oldValue, newValue); // NON-NLS
} }
} }
@ -216,55 +238,31 @@ public class EventNode extends DisplayableItemNode {
* @param eventID The ID of the event this node is for. * @param eventID The ID of the event this node is for.
* @param eventsModel The model that provides access to the events DB. * @param eventsModel The model that provides access to the events DB.
* *
* @return An EventNode with the file (and artifact) backing this event in * @return An EventNode with the content (and possible artifact) backing
* its lookup. * this event in its lookup.
*/ */
public static EventNode createEventNode(final Long eventID, FilteredEventsModel eventsModel) throws TskCoreException, NoCurrentCaseException { public static EventNode createEventNode(final Long eventID, FilteredEventsModel eventsModel) throws TskCoreException {
SleuthkitCase sleuthkitCase = Case.getCurrentCaseThrows().getSleuthkitCase(); SleuthkitCase sleuthkitCase = eventsModel.getSleuthkitCase();
try {
/*
* Look up the event by id and creata an EventNode with the
* appropriate data in the lookup.
*/
final TimelineEvent eventById = eventsModel.getEventById(eventID);
Content file = sleuthkitCase.getContentById(eventById.getFileObjID()); /*
* Look up the event by id and creata an EventNode with the appropriate
* data in the lookup.
*/
final TimelineEvent eventById = eventsModel.getEventById(eventID);
Content file = sleuthkitCase.getContentById(eventById.getFileObjID());
if (eventById.getArtifactID().isPresent()) { if (eventById.getArtifactID().isPresent()) {
BlackboardArtifact blackboardArtifact = sleuthkitCase.getBlackboardArtifact(eventById.getArtifactID().get()); BlackboardArtifact blackboardArtifact = sleuthkitCase.getBlackboardArtifact(eventById.getArtifactID().get());
return new EventNode(eventById, file, blackboardArtifact); return new EventNode(eventById, file, blackboardArtifact);
} else { } else {
return new EventNode(eventById, file); return new EventNode(eventById, file);
}
} catch (TskCoreException ex) {
throw new TskCoreException("Error getting event by id.", ex);
} }
} }
/** private static boolean isExactlyOneArtifactSelected() {
* this code started as a cut and past of final Collection<BlackboardArtifact> selectedArtifactsList
* DataResultFilterNode.GetPopupActionsDisplayableItemNodeVisitor.findLinked(BlackboardArtifactNode = new HashSet<>(Utilities.actionsGlobalContext().lookupAll(BlackboardArtifact.class));
* ba) return selectedArtifactsList.size() == 1;
*
* It is now in DisplayableItemNode too, but is not accesible across
* packages
*
* @param artifact
*
* @return
*/
static AbstractFile findLinked(BlackboardArtifact artifact) throws TskCoreException {
BlackboardAttribute pathIDAttribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID));
if (pathIDAttribute != null) {
long contentID = pathIDAttribute.getValueLong();
if (contentID != -1) {
return artifact.getSleuthkitCase().getAbstractFileById(contentID);
}
}
return null;
} }
} }

View File

@ -29,7 +29,6 @@ import org.openide.nodes.Children;
import org.openide.nodes.Node; import org.openide.nodes.Node;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.lookup.Lookups; import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; import org.sleuthkit.autopsy.datamodel.DisplayableItemNode;
import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor; import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor;
@ -85,8 +84,7 @@ public class EventRootNode extends DisplayableItemNode {
* filteredEvents is used to lookup the events from their IDs * filteredEvents is used to lookup the events from their IDs
*/ */
private final FilteredEventsModel filteredEvents; private final FilteredEventsModel filteredEvents;
private Map<Long, Node > nodesMap = new HashMap<>(); private final Map<Long, Node> nodesMap = new HashMap<>();
EventNodeChildFactory(Collection<Long> eventIds, FilteredEventsModel filteredEvents) { EventNodeChildFactory(Collection<Long> eventIds, FilteredEventsModel filteredEvents) {
this.eventIDs = eventIds; this.eventIDs = eventIds;
@ -100,16 +98,12 @@ public class EventRootNode extends DisplayableItemNode {
* indicate this. * indicate this.
*/ */
if (eventIDs.size() < MAX_EVENTS_TO_DISPLAY) { if (eventIDs.size() < MAX_EVENTS_TO_DISPLAY) {
for (Long eventId: eventIDs){ for (Long eventId : eventIDs) {
if (!nodesMap.containsKey(eventId)) { nodesMap.computeIfAbsent(eventId, this::createNode);
nodesMap.put(eventId, createNode(eventId));
}
toPopulate.add(eventId); toPopulate.add(eventId);
} }
} else { } else {
if (!nodesMap.containsKey(-1L)) { nodesMap.computeIfAbsent(-1L, this::createNode);
nodesMap.put(-1L, createNode(-1L));
}
toPopulate.add(-1L); toPopulate.add(-1L);
} }
return true; return true;
@ -119,9 +113,8 @@ public class EventRootNode extends DisplayableItemNode {
protected Node createNodeForKey(Long eventID) { protected Node createNodeForKey(Long eventID) {
return nodesMap.get(eventID); return nodesMap.get(eventID);
} }
private Node createNode(Long eventID) { private Node createNode(Long eventID) {
if (eventID < 0) { if (eventID < 0) {
/* /*
* If the eventId is a the special value ( -1 ), return a node * If the eventId is a the special value ( -1 ), return a node
@ -131,17 +124,13 @@ public class EventRootNode extends DisplayableItemNode {
} else { } else {
try { try {
return EventNode.createEventNode(eventID, filteredEvents); return EventNode.createEventNode(eventID, filteredEvents);
} catch (NoCurrentCaseException ex) {
//Since the case is closed, the user probably doesn't care about this, just log it as a precaution.
LOGGER.log(Level.SEVERE, "There was no case open to lookup the Sleuthkit object backing a SingleEvent.", ex); // NON-NLS
return null;
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
/* /*
* Just log it: There might be lots of these errors, and we * Just log it: There might be lots of these errors, and we
* don't want to flood the user with notifications. It will * don't want to flood the user with notifications. It will
* be obvious the UI is broken anyways * be obvious the UI is broken anyways
*/ */
LOGGER.log(Level.SEVERE, "Failed to lookup Sleuthkit object backing a SingleEvent.", ex); // NON-NLS LOGGER.log(Level.SEVERE, "Error creating explorer node for event id " + eventID + ".", ex); // NON-NLS
return null; return null;
} }
} }

View File

@ -78,7 +78,6 @@ import org.controlsfx.control.action.ActionUtils;
import org.openide.awt.Actions; import org.openide.awt.Actions;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.actions.Presenter; import org.openide.util.actions.Presenter;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.casemodule.services.TagsManager; import org.sleuthkit.autopsy.casemodule.services.TagsManager;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.coreutils.ThreadConfined;
@ -214,7 +213,7 @@ class ListTimeline extends BorderPane {
assert dateTimeColumn != null : "fx:id=\"dateTimeColumn\" was not injected: check your FXML file 'ListViewPane.fxml'."; //NON-NLS assert dateTimeColumn != null : "fx:id=\"dateTimeColumn\" was not injected: check your FXML file 'ListViewPane.fxml'."; //NON-NLS
assert descriptionColumn != null : "fx:id=\"descriptionColumn\" was not injected: check your FXML file 'ListViewPane.fxml'."; //NON-NLS assert descriptionColumn != null : "fx:id=\"descriptionColumn\" was not injected: check your FXML file 'ListViewPane.fxml'."; //NON-NLS
assert typeColumn != null : "fx:id=\"typeColumn\" was not injected: check your FXML file 'ListViewPane.fxml'."; //NON-NLS assert typeColumn != null : "fx:id=\"typeColumn\" was not injected: check your FXML file 'ListViewPane.fxml'."; //NON-NLS
//configure scroll controls //configure scroll controls
scrollInrementComboBox.setButtonCell(new ChronoFieldListCell()); scrollInrementComboBox.setButtonCell(new ChronoFieldListCell());
scrollInrementComboBox.setCellFactory(comboBox -> new ChronoFieldListCell()); scrollInrementComboBox.setCellFactory(comboBox -> new ChronoFieldListCell());
@ -676,14 +675,11 @@ class ListTimeline extends BorderPane {
} }
} }
} }
}; }
//show new context menu. //show new context menu.
new ContextMenu(menuItems.toArray(new MenuItem[menuItems.size()])) new ContextMenu(menuItems.toArray(new MenuItem[menuItems.size()]))
.show(this, contextMenuEvent.getScreenX(), contextMenuEvent.getScreenY()); .show(this, contextMenuEvent.getScreenX(), contextMenuEvent.getScreenY());
} catch (NoCurrentCaseException ex) {
//Since the case is closed, the user probably doesn't care about this, just log it as a precaution.
logger.log(Level.SEVERE, "There was no case open to lookup the Sleuthkit object backing a TimelineEvent.", ex); //NON-NLS
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Failed to lookup Sleuthkit object backing a TimelineEvent.", ex); //NON-NLS logger.log(Level.SEVERE, "Failed to lookup Sleuthkit object backing a TimelineEvent.", ex); //NON-NLS
Platform.runLater(() -> { Platform.runLater(() -> {