From 46e050370c2da71c7301035e0f21a4ab16261f16 Mon Sep 17 00:00:00 2001 From: jmillman Date: Mon, 19 Oct 2015 15:04:06 -0400 Subject: [PATCH 01/48] View in timeline action WIP --- .../sleuthkit/autopsy/datamodel/FileNode.java | 2 + .../datamodel/ViewInTimeLineAction.java | 52 +++++++++++++++ .../autopsy/timeline/OpenTimelineAction.java | 7 +- .../autopsy/timeline/TimeLineController.java | 65 +++++++++++++------ 4 files changed, 106 insertions(+), 20 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/datamodel/ViewInTimeLineAction.java diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileNode.java index 219d039970..dd778486d2 100755 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileNode.java @@ -86,6 +86,8 @@ public class FileNode extends AbstractFsContentNode { NbBundle.getMessage(this.getClass(), "FileNode.getActions.viewInNewWin.text"), this)); actionsList.add(new ExternalViewerAction( NbBundle.getMessage(this.getClass(), "FileNode.getActions.openInExtViewer.text"), this)); + actionsList.add( ViewInTimeLineAction.getInstance()); + actionsList.add(null); // creates a menu separator actionsList.add(ExtractAction.getInstance()); actionsList.add(new HashSearchAction( diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ViewInTimeLineAction.java b/Core/src/org/sleuthkit/autopsy/datamodel/ViewInTimeLineAction.java new file mode 100644 index 0000000000..4bbaad7f3b --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ViewInTimeLineAction.java @@ -0,0 +1,52 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.sleuthkit.autopsy.datamodel; + +import java.awt.event.ActionEvent; +import java.util.Collection; +import java.util.LongSummaryStatistics; +import java.util.stream.LongStream; +import javax.swing.AbstractAction; +import org.joda.time.Interval; +import org.openide.util.Utilities; +import org.openide.util.actions.SystemAction; +import org.sleuthkit.autopsy.timeline.OpenTimelineAction; +import org.sleuthkit.datamodel.AbstractFile; + +/** + * + */ +class ViewInTimeLineAction extends AbstractAction { + + // This class is a singleton to support multi-selection of nodes, since + // org.openide.nodes.NodeOp.findActions(Node[] nodes) will only pick up an Action if every + // node in the array returns a reference to the same action object from Node.getActions(boolean). + private static ViewInTimeLineAction instance; + + public static synchronized ViewInTimeLineAction getInstance() { + if (null == instance) { + instance = new ViewInTimeLineAction(); + } + return instance; + } + + private ViewInTimeLineAction() { + super("View in Timeline"); + } + + @Override + public void actionPerformed(ActionEvent e) { + Collection selectedFiles = Utilities.actionsGlobalContext().lookupAll(AbstractFile.class); + + LongSummaryStatistics summaryStatistics = selectedFiles.stream() + .flatMapToLong(file -> LongStream.of(file.getAtime(), file.getCrtime(), file.getCtime(), file.getMtime())) + .summaryStatistics(); + + Interval interval = new Interval(summaryStatistics.getMin() * 1000, 1 + summaryStatistics.getMax() * 1000); + + SystemAction.get(OpenTimelineAction.class).showTimeline(interval); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java b/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java index 3c5c8a6621..6bfbded091 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java @@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.timeline; import java.util.logging.Level; import javax.swing.JOptionPane; +import org.joda.time.Interval; import org.openide.awt.ActionID; import org.openide.awt.ActionReference; import org.openide.awt.ActionReferences; @@ -59,7 +60,10 @@ public class OpenTimelineAction extends CallableSystemAction { @Override public void performAction() { + showTimeline(null); + } + public void showTimeline(Interval interval) { //check case if (!Case.isCaseOpen()) { return; @@ -80,7 +84,7 @@ public class OpenTimelineAction extends CallableSystemAction { timeLineController = new TimeLineController(currentCase); } } - timeLineController.openTimeLine(); + timeLineController.openTimeLine(interval); } @Override @@ -97,4 +101,5 @@ public class OpenTimelineAction extends CallableSystemAction { public boolean asynchronous() { return false; // run on edt } + } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java index e7dd27a635..f4b4010c9a 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java @@ -64,13 +64,13 @@ import org.openide.windows.WindowManager; import org.sleuthkit.autopsy.casemodule.Case; import static org.sleuthkit.autopsy.casemodule.Case.Events.CURRENT_CASE; import static org.sleuthkit.autopsy.casemodule.Case.Events.DATA_SOURCE_ADDED; -import org.sleuthkit.autopsy.coreutils.History; -import org.sleuthkit.autopsy.coreutils.LoggedTask; -import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagAddedEvent; import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagDeletedEvent; import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent; import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent; +import org.sleuthkit.autopsy.coreutils.History; +import org.sleuthkit.autopsy.coreutils.LoggedTask; +import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel; @@ -291,7 +291,7 @@ public class TimeLineController { * the user aborted after prompt about ingest running. True if the * repo was rebuilt. */ - boolean rebuildRepo() { + boolean rebuildRepo(Interval interval) { if (IngestManager.getInstance().isIngestRunning()) { //confirm timeline during ingest if (confirmRebuildDuringIngest() == false) { @@ -328,7 +328,7 @@ public class TimeLineController { //TODO: should this be an event? newEventsFlag.set(false); historyManager.reset(filteredEvents.zoomParametersProperty().get()); - TimeLineController.this.showFullRange(); + TimeLineController.this.showRange(interval); }); }); } @@ -344,18 +344,18 @@ public class TimeLineController { * tags table and rebuild it by querying for all the tags and inserting them * in to the TimeLine DB. */ - void rebuildTagsTable() { + void rebuildTagsTable(Interval interval) { LOGGER.log(Level.INFO, "starting to rebuild tags table"); // NON-NLS - SwingUtilities.invokeLater(() -> { - if (isWindowOpen()) { - mainFrame.close(); - } - }); +// SwingUtilities.invokeLater(() -> { +// if (isWindowOpen()) { +// mainFrame.close(); +// } +// }); synchronized (eventsRepository) { eventsRepository.rebuildTags(() -> { showWindow(); Platform.runLater(() -> { - showFullRange(); + showRange(interval); }); }); } @@ -367,6 +367,17 @@ public class TimeLineController { } } + public void showRange(Interval interval) { + synchronized (filteredEvents) { + if (null == interval) { + showFullRange(); + } else { + System.out.println(interval); + pushTimeRange(interval); + } + } + } + synchronized public void closeTimeLine() { if (mainFrame != null) { listeningToAutopsy = false; @@ -382,7 +393,7 @@ public class TimeLineController { /** * show the timeline window and prompt for rebuilding database if necessary. */ - synchronized void openTimeLine() { + synchronized void openTimeLine(Interval interval) { // listen for case changes (specifically images being added, and case changes). if (Case.isCaseOpen() && !listeningToAutopsy) { IngestManager.getInstance().addIngestModuleEventListener(ingestModuleListener); @@ -397,14 +408,14 @@ public class TimeLineController { //if the repo is empty rebuild it if (timeLineLastObjectId == -1) { - repoRebuilt = rebuildRepo(); + repoRebuilt = rebuildRepo(interval); } if (repoRebuilt == false) { //if ingest was running uring last rebuild, prompt to rebuild if (eventsRepository.getWasIngestRunning()) { if (confirmLastBuiltDuringIngestRebuild()) { - repoRebuilt = rebuildRepo(); + repoRebuilt = rebuildRepo(interval); } } } @@ -415,7 +426,7 @@ public class TimeLineController { if (sleuthkitCase.getLastObjectId() != timeLineLastObjectId || getCaseLastArtifactID(sleuthkitCase) != eventsRepository.getLastArtfactID()) { if (confirmOutOfDateRebuild()) { - repoRebuilt = rebuildRepo(); + repoRebuilt = rebuildRepo(interval); } } } @@ -424,7 +435,7 @@ public class TimeLineController { // if the TLDB schema has been upgraded since last time TL ran, prompt for rebuild if (eventsRepository.hasNewColumns() == false) { if (confirmDataSourceIDsMissingRebuild()) { - repoRebuilt = rebuildRepo(); + repoRebuilt = rebuildRepo(interval); } } } @@ -434,7 +445,7 @@ public class TimeLineController { * have been updated without our knowing it. */ if (repoRebuilt == false) { - rebuildTagsTable(); + rebuildTagsTable(interval); } } catch (TskCoreException ex) { @@ -730,6 +741,22 @@ public class TimeLineController { return mainFrame != null && mainFrame.isOpened() && mainFrame.isVisible(); } + /** + * prompt the user to rebuild the db because the db is out of date and + * doesn't include things from subsequent ingests ONLY IF THE TIMELINE + * WINDOW IS OPEN + * + * @return true if they agree to rebuild + */ + @ThreadConfined(type = ThreadConfined.ThreadType.AWT) + private void confirmOutOfDateRebuildIfWindowOpen(Interval interval) throws MissingResourceException, HeadlessException { + if (isWindowOpen()) { + if (confirmOutOfDateRebuild()) { + rebuildRepo(interval); + } + } + } + /** * prompt the user to rebuild the db because the db is out of date and * doesn't include things from subsequent ingests ONLY IF THE TIMELINE @@ -741,7 +768,7 @@ public class TimeLineController { private void confirmOutOfDateRebuildIfWindowOpen() throws MissingResourceException, HeadlessException { if (isWindowOpen()) { if (confirmOutOfDateRebuild()) { - rebuildRepo(); + rebuildRepo(null); } } } From f98cf3e6007ff383ee76e7211e0fbece49fa657e Mon Sep 17 00:00:00 2001 From: jmillman Date: Mon, 19 Oct 2015 16:36:12 -0400 Subject: [PATCH 02/48] WIP towards view artifacts in timeline action --- .../datamodel/ViewInTimeLineAction.java | 39 ++++++++++++++----- .../directorytree/DataResultFilterNode.java | 18 +++++++++ .../eventtype/ArtifactEventType.java | 12 +++++- .../datamodel/eventtype/EventType.java | 6 ++- .../autopsy/timeline/db/EventsRepository.java | 2 +- 5 files changed, 64 insertions(+), 13 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ViewInTimeLineAction.java b/Core/src/org/sleuthkit/autopsy/datamodel/ViewInTimeLineAction.java index 4bbaad7f3b..64faf03508 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ViewInTimeLineAction.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ViewInTimeLineAction.java @@ -6,20 +6,26 @@ package org.sleuthkit.autopsy.datamodel; import java.awt.event.ActionEvent; -import java.util.Collection; -import java.util.LongSummaryStatistics; -import java.util.stream.LongStream; +import java.util.Set; +import java.util.TreeSet; +import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.swing.AbstractAction; import org.joda.time.Interval; +import org.openide.util.Exceptions; import org.openide.util.Utilities; import org.openide.util.actions.SystemAction; import org.sleuthkit.autopsy.timeline.OpenTimelineAction; +import org.sleuthkit.autopsy.timeline.datamodel.eventtype.ArtifactEventType; import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.TskCoreException; /** * */ -class ViewInTimeLineAction extends AbstractAction { +public class ViewInTimeLineAction extends AbstractAction { // This class is a singleton to support multi-selection of nodes, since // org.openide.nodes.NodeOp.findActions(Node[] nodes) will only pick up an Action if every @@ -39,13 +45,28 @@ class ViewInTimeLineAction extends AbstractAction { @Override public void actionPerformed(ActionEvent e) { - Collection selectedFiles = Utilities.actionsGlobalContext().lookupAll(AbstractFile.class); + TreeSet timestamps = Utilities.actionsGlobalContext().lookupAll(AbstractFile.class).stream() + .flatMap(file -> Stream.of(file.getAtime(), file.getCrtime(), file.getCtime(), file.getMtime())) + .collect(Collectors.toCollection(TreeSet::new)); - LongSummaryStatistics summaryStatistics = selectedFiles.stream() - .flatMapToLong(file -> LongStream.of(file.getAtime(), file.getCrtime(), file.getCtime(), file.getMtime())) - .summaryStatistics(); + //for each artifact, get all datetime attributes for that artifact type + for (BlackboardArtifact bbart : Utilities.actionsGlobalContext().lookupAll(BlackboardArtifact.class)) { + Set attributeTypes = ArtifactEventType.getAllArtifactEventTypes().stream() + .filter(artEventType -> bbart.getArtifactTypeID() == artEventType.getArtifactType().getTypeID()) + .map(ArtifactEventType::getDateTimeAttrubuteType) + .collect(Collectors.toSet()); - Interval interval = new Interval(summaryStatistics.getMin() * 1000, 1 + summaryStatistics.getMax() * 1000); + for (BlackboardAttribute.ATTRIBUTE_TYPE type : attributeTypes) { + try { + Set collect1 = bbart.getAttributes(type).stream().map(BlackboardAttribute::getValueLong).collect(Collectors.toSet()); + timestamps.addAll(collect1); + } catch (TskCoreException ex) { + Exceptions.printStackTrace(ex); + } + } + } + + Interval interval = new Interval(timestamps.first() * 1000, 1 + timestamps.last() * 1000); SystemAction.get(OpenTimelineAction.class).showTimeline(interval); } diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java index e4c34ebce9..be49934e5d 100755 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java @@ -66,8 +66,10 @@ import org.sleuthkit.autopsy.datamodel.RecentFilesFilterNode; import org.sleuthkit.autopsy.datamodel.RecentFilesNode; import org.sleuthkit.autopsy.datamodel.Reports; import org.sleuthkit.autopsy.datamodel.Tags; +import org.sleuthkit.autopsy.datamodel.ViewInTimeLineAction; import org.sleuthkit.autopsy.datamodel.VirtualDirectoryNode; import org.sleuthkit.autopsy.datamodel.VolumeNode; +import org.sleuthkit.autopsy.timeline.datamodel.eventtype.ArtifactEventType; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; @@ -77,6 +79,7 @@ import org.sleuthkit.datamodel.Directory; import org.sleuthkit.datamodel.File; import org.sleuthkit.datamodel.LayoutFile; import org.sleuthkit.datamodel.LocalFile; +import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskException; import org.sleuthkit.datamodel.VirtualDirectory; @@ -245,6 +248,21 @@ public class DataResultFilterNode extends FilterNode { actions.add(null); actions.add(AddBlackboardArtifactTagAction.getInstance()); } + + boolean hasTimeStamp = ArtifactEventType.getAllArtifactEventTypes().stream() + .filter(artEventType -> artEventType.getArtifactType().getTypeID() == ba.getArtifactTypeID()) + .filter(artEventType -> { + try { + return ba.getAttributes(artEventType.getDateTimeAttrubuteType()).isEmpty() == false; + } catch (TskCoreException ex) { + Logger.getLogger(DataResultFilterNode.class.getName()).log(Level.WARNING, "Error retreiving blackboard arttributes from blackboard artifact.", ex); + return false; + } + }).findAny().isPresent(); + if (hasTimeStamp){ + actions.add(ViewInTimeLineAction.getInstance()); + } + return actions; } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/datamodel/eventtype/ArtifactEventType.java b/Core/src/org/sleuthkit/autopsy/timeline/datamodel/eventtype/ArtifactEventType.java index fa17ab64e6..c11d3629f0 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/datamodel/eventtype/ArtifactEventType.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/datamodel/eventtype/ArtifactEventType.java @@ -21,8 +21,10 @@ package org.sleuthkit.autopsy.timeline.datamodel.eventtype; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.function.BiFunction; import java.util.logging.Level; +import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.BlackboardArtifact; @@ -34,6 +36,13 @@ import org.sleuthkit.datamodel.TskCoreException; */ public interface ArtifactEventType extends EventType { + public static Set getAllArtifactEventTypes() { + return allTypes.stream() + .filter((EventType t) -> t instanceof ArtifactEventType) + .map(ArtifactEventType.class::cast) + .collect(Collectors.toSet()); + } + /** * @return the Artifact type this event type is derived form, or null if * there is no artifact type (eg file system events) @@ -87,7 +96,8 @@ public interface ArtifactEventType extends EventType { /** * bundles the per event information derived from a BlackBoard Artifact into - * one object. Primarily used to have a single return value for {@link SubType#buildEventDescription(org.sleuthkit.datamodel.BlackboardArtifact). + * one object. Primarily used to have a single return value for null null + * null null {@link SubType#buildEventDescription(org.sleuthkit.datamodel.BlackboardArtifact). */ static class AttributeEventDescription { diff --git a/Core/src/org/sleuthkit/autopsy/timeline/datamodel/eventtype/EventType.java b/Core/src/org/sleuthkit/autopsy/timeline/datamodel/eventtype/EventType.java index 0758d804ec..54bd488d8f 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/datamodel/eventtype/EventType.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/datamodel/eventtype/EventType.java @@ -32,7 +32,7 @@ import org.sleuthkit.autopsy.timeline.zooming.EventTypeZoomLevel; */ public interface EventType { - final static List allTypes = RootEventType.getInstance().getSubTypesRecusive(); + final static List allTypes = RootEventType.getInstance().getSubTypesRecusive(); static Comparator getComparator() { return Comparator.comparing(EventType.allTypes::indexOf); @@ -47,7 +47,7 @@ public interface EventType { } } - default List getSubTypesRecusive() { + default List getSubTypesRecusive() { ArrayList flatList = new ArrayList<>(); for (EventType et : getSubTypes()) { @@ -56,6 +56,8 @@ public interface EventType { } return flatList; } + + /** * @return the color used to represent this event type visually diff --git a/Core/src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java b/Core/src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java index 73800b9b89..8450260cb3 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java @@ -443,7 +443,7 @@ public class EventsRepository { private final SleuthkitCase skCase; private final TagsManager tagsManager; - public DBPopulationWorker(Runnable postPopulationOperation) { + DBPopulationWorker(Runnable postPopulationOperation) { progressDialog = new ProgressWindow(null, true, this); progressDialog.setVisible(true); From 9bf8db616e9a32096be6bf96f2769b74434e4712 Mon Sep 17 00:00:00 2001 From: jmillman Date: Fri, 8 Apr 2016 12:30:49 -0400 Subject: [PATCH 03/48] fix end time calculation --- Core/src/org/sleuthkit/autopsy/datamodel/FileNode.java | 4 ++-- .../org/sleuthkit/autopsy/datamodel/ViewInTimeLineAction.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileNode.java index 7af6c01b72..5d89f6fe33 100755 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileNode.java @@ -87,7 +87,7 @@ public class FileNode extends AbstractFsContentNode { NbBundle.getMessage(this.getClass(), "FileNode.getActions.viewInNewWin.text"), this)); actionsList.add(new ExternalViewerAction( NbBundle.getMessage(this.getClass(), "FileNode.getActions.openInExtViewer.text"), this)); - actionsList.add( ViewInTimeLineAction.getInstance()); + actionsList.add(ViewInTimeLineAction.getInstance()); actionsList.add(null); // creates a menu separator actionsList.add(ExtractAction.getInstance()); @@ -96,7 +96,7 @@ public class FileNode extends AbstractFsContentNode { actionsList.add(null); // creates a menu separator actionsList.add(AddContentTagAction.getInstance()); actionsList.addAll(ContextMenuExtensionPoint.getActions()); - return actionsList.toArray(new Action[0]); + return actionsList.toArray(new Action[actionsList.size()]); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ViewInTimeLineAction.java b/Core/src/org/sleuthkit/autopsy/datamodel/ViewInTimeLineAction.java index 25881208f2..49ab0667d2 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ViewInTimeLineAction.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ViewInTimeLineAction.java @@ -70,7 +70,7 @@ public class ViewInTimeLineAction extends AbstractAction { } } - Interval interval = new Interval(timestamps.first() * 1000, 1 + timestamps.last() * 1000); + Interval interval = new Interval(timestamps.first() * 1000, (1 + timestamps.last()) * 1000); SystemAction.get(OpenTimelineAction.class).showTimeline(interval); } From 437927ec944f2600c7d04d674d51f499102b36c8 Mon Sep 17 00:00:00 2001 From: jmillman Date: Fri, 3 Jun 2016 17:57:31 -0400 Subject: [PATCH 04/48] fix merge conflicts, part way to working "view in timeline" action --- .../datamodel/ViewInTimeLineAction.java | 41 +++++------------ .../autopsy/timeline/OpenTimelineAction.java | 9 ++-- .../autopsy/timeline/TimeLineController.java | 45 ++++++++++--------- .../autopsy/timeline/actions/UpdateDB.java | 2 +- .../timeline/datamodel/CombinedEvent.java | 6 +-- .../datamodel/FilteredEventsModel.java | 4 ++ .../eventtype/ArtifactEventType.java | 4 ++ .../autopsy/timeline/db/EventDB.java | 19 ++++++++ .../autopsy/timeline/db/EventsRepository.java | 4 ++ .../timeline/ui/listvew/ListTimeline.java | 10 +++++ 10 files changed, 85 insertions(+), 59 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ViewInTimeLineAction.java b/Core/src/org/sleuthkit/autopsy/datamodel/ViewInTimeLineAction.java index 49ab0667d2..f1688dd725 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ViewInTimeLineAction.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ViewInTimeLineAction.java @@ -7,20 +7,14 @@ package org.sleuthkit.autopsy.datamodel; import java.awt.event.ActionEvent; import java.util.Set; -import java.util.TreeSet; import java.util.stream.Collectors; -import java.util.stream.Stream; import javax.swing.AbstractAction; -import org.joda.time.Interval; -import org.openide.util.Exceptions; import org.openide.util.Utilities; import org.openide.util.actions.SystemAction; import org.sleuthkit.autopsy.timeline.OpenTimelineAction; import org.sleuthkit.autopsy.timeline.datamodel.eventtype.ArtifactEventType; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; -import org.sleuthkit.datamodel.BlackboardAttribute; -import org.sleuthkit.datamodel.TskCoreException; /** * @@ -47,31 +41,20 @@ public class ViewInTimeLineAction extends AbstractAction { @Override public void actionPerformed(ActionEvent e) { - TreeSet timestamps = Utilities.actionsGlobalContext().lookupAll(AbstractFile.class).stream() - .flatMap(file -> Stream.of(file.getAtime(), file.getCrtime(), file.getCtime(), file.getMtime())) - .collect(Collectors.toCollection(TreeSet::new)); + Set fileIDs = Utilities.actionsGlobalContext().lookupAll(AbstractFile.class).stream() + .map(AbstractFile::getId) + .collect(Collectors.toSet()); + + final Set artifactEventTypeIDs = ArtifactEventType.getAllArtifactEventTypes().stream() + .map(ArtifactEventType::getArtifactTypeID) + .collect(Collectors.toSet()); //for each artifact, get all datetime attributes for that artifact type - for (BlackboardArtifact bbart : Utilities.actionsGlobalContext().lookupAll(BlackboardArtifact.class)) { - Set attributeTypes = ArtifactEventType.getAllArtifactEventTypes().stream() - .filter(artEventType -> bbart.getArtifactTypeID() == artEventType.getArtifactType().getTypeID()) - .map(ArtifactEventType::getDateTimeAttrubuteType) - .collect(Collectors.toSet()); + Set artifactIDs = Utilities.actionsGlobalContext().lookupAll(BlackboardArtifact.class).stream() + .filter(artifact -> artifactEventTypeIDs.contains(artifact.getArtifactTypeID())) + .map(BlackboardArtifact::getArtifactID) + .collect(Collectors.toSet()); - for (BlackboardAttribute.Type type : attributeTypes) { - try { - BlackboardAttribute attribute = bbart.getAttribute(type); - if (attribute != null) { - timestamps.add(attribute.getValueLong()); - } - } catch (TskCoreException ex) { - Exceptions.printStackTrace(ex); - } - } - } - - Interval interval = new Interval(timestamps.first() * 1000, (1 + timestamps.last()) * 1000); - - SystemAction.get(OpenTimelineAction.class).showTimeline(interval); + SystemAction.get(OpenTimelineAction.class).showTimeline(fileIDs, artifactIDs); } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java b/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java index 3e7c37af84..47b9054682 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java @@ -19,8 +19,9 @@ package org.sleuthkit.autopsy.timeline; import java.io.IOException; +import java.util.Collections; +import java.util.Set; import java.util.logging.Level; -import org.joda.time.Interval; import org.openide.awt.ActionID; import org.openide.awt.ActionReference; import org.openide.awt.ActionReferences; @@ -62,13 +63,13 @@ public class OpenTimelineAction extends CallableSystemAction { @Override @ThreadConfined(type = ThreadConfined.ThreadType.AWT) public void performAction() { - showTimeline(null); + showTimeline(Collections.emptySet(), Collections.emptySet()); } @NbBundle.Messages({ "OpenTimelineAction.settingsErrorMessage=Failed to initialize timeline settings.", "OpenTimeLineAction.msgdlg.text=Could not create timeline, there are no data sources."}) - public void showTimeline(Interval interval) { + public void showTimeline(Set fileIDs, Set artifactIDS) { //check case if (!Case.isCaseOpen()) { return; @@ -87,7 +88,7 @@ public class OpenTimelineAction extends CallableSystemAction { timeLineController.shutDownTimeLine(); timeLineController = new TimeLineController(currentCase); } - timeLineController.openTimeLine(interval); + timeLineController.openTimeLine(fileIDs, artifactIDS); } catch (IOException iOException) { MessageNotifyUtil.Message.error(Bundle.OpenTimelineAction_settingsErrorMessage()); LOGGER.log(Level.SEVERE, "Failed to initialize per case timeline settings.", iOException); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java index fe0d1e804f..8c0e5b74d1 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java @@ -26,6 +26,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Set; import java.util.TimeZone; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; @@ -400,7 +401,7 @@ public class TimeLineController { @NbBundle.Messages({ "TimeLineController.setIngestRunning.errMsgRunning=Failed to mark the timeline db as populated while ingest was running. Some results may be out of date or missing.", "TimeLinecontroller.setIngestRunning.errMsgNotRunning=Failed to mark the timeline db as populated while ingest was not running. Some results may be out of date or missing."}) - private void rebuildRepoHelper(Function, CancellationProgressTask> repoBuilder, Boolean markDBNotStale, Interval interval) { + private void rebuildRepoHelper(Function, CancellationProgressTask> repoBuilder, Boolean markDBNotStale, Set fileIDs, Set artifactIDS) { boolean ingestRunning = IngestManager.getInstance().isIngestRunning(); //if there is an existing prompt or progressdialog, just show that @@ -435,7 +436,7 @@ public class TimeLineController { filteredEvents.postDBUpdated(); } SwingUtilities.invokeLater(this::showWindow); - TimeLineController.this.showRange(interval); + TimeLineController.this.showEvents(fileIDs, artifactIDS); break; case FAILED: @@ -457,8 +458,8 @@ public class TimeLineController { * done. */ @ThreadConfined(type = ThreadConfined.ThreadType.JFX) - void rebuildRepo(Interval interval) { - rebuildRepoHelper(eventsRepository::rebuildRepository, true, interval); + public void rebuildRepo(Set fileIDs, Set artifactIDS) { + rebuildRepoHelper(eventsRepository::rebuildRepository, true, fileIDs, artifactIDS); } /** @@ -466,16 +467,8 @@ public class TimeLineController { * timeline when done. */ @ThreadConfined(type = ThreadConfined.ThreadType.JFX) - - void rebuildTagsTable(Interval interval) { - rebuildRepoHelper(eventsRepository::rebuildTags, false, interval); - } - - @ThreadConfined(type = ThreadConfined.ThreadType.AWT) - private void closeTimelineWindow() { - if (isWindowOpen()) { - mainFrame.close(); - } + void rebuildTagsTable(Set fileIDs, Set artifactIDS) { + rebuildRepoHelper(eventsRepository::rebuildTags, false, fileIDs, artifactIDS); } /** @@ -487,13 +480,21 @@ public class TimeLineController { } } - public void showRange(Interval interval) { - if (interval == null) { + public void showEvents(Set fileIDs, Set artifactIDS) { + if (fileIDs.isEmpty() && artifactIDS.isEmpty()) { showFullRange(); } else { + + setViewMode(ViewMode.LIST); + List eventIDs = filteredEvents.getDerivedEventIDs(fileIDs, artifactIDS); + + Interval interval = filteredEvents.getSpanningInterval(eventIDs); + synchronized (filteredEvents) { pushTimeRange(interval); } + + selectedEventIDs.setAll(eventIDs); } } @@ -519,7 +520,7 @@ public class TimeLineController { * necessary, and show the timeline window. */ @ThreadConfined(type = ThreadConfined.ThreadType.AWT) - void openTimeLine(Interval interval) { + void openTimeLine(Set fileIDs, Set artifactIDS) { // listen for case changes (specifically images being added, and case changes). if (Case.isCaseOpen() && !listeningToAutopsy) { IngestManager.getInstance().addIngestModuleEventListener(ingestModuleListener); @@ -528,7 +529,7 @@ public class TimeLineController { listeningToAutopsy = true; } - Platform.runLater(() -> promptForRebuild( interval)); + Platform.runLater(() -> promptForRebuild(fileIDs, artifactIDS)); } /** @@ -538,7 +539,7 @@ public class TimeLineController { * rebuild is done, or immediately if the rebuild is not confirmed. F */ @ThreadConfined(type = ThreadConfined.ThreadType.JFX) - private void promptForRebuild(@Nullable String dataSourceName, Interval interval) { + private void promptForRebuild(Set fileIDs, Set artifactIDS) { //if there is an existing prompt or progressdialog, just show that if (promptDialogManager.bringCurrentDialogToFront()) { return; @@ -546,7 +547,7 @@ public class TimeLineController { //if the repo is empty just (re)build it with out asking, the user can always cancel part way through if (eventsRepository.countAllEvents() == 0) { - rebuildRepo(interval); + rebuildRepo(fileIDs, artifactIDS); return; } @@ -554,7 +555,7 @@ public class TimeLineController { List rebuildReasons = getRebuildReasons(); if (false == rebuildReasons.isEmpty()) { if (promptDialogManager.confirmRebuild(rebuildReasons)) { - rebuildRepo(interval); + rebuildRepo(fileIDs, artifactIDS); return; } } @@ -566,7 +567,7 @@ public class TimeLineController { * * //TODO: can we check the tags to see if we need to do this? */ - rebuildTagsTable(interval); + rebuildTagsTable(fileIDs, artifactIDS); } /** diff --git a/Core/src/org/sleuthkit/autopsy/timeline/actions/UpdateDB.java b/Core/src/org/sleuthkit/autopsy/timeline/actions/UpdateDB.java index 599f029844..00f0192545 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/actions/UpdateDB.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/actions/UpdateDB.java @@ -44,7 +44,7 @@ public class UpdateDB extends Action { super(Bundle.RebuildDataBase_text()); setLongText(Bundle.RebuildDataBase_longText()); setGraphic(new ImageView(DB_REFRESH)); - setEventHandler(actionEvent -> controller.rebuildRepo()); + setEventHandler(actionEvent -> controller.rebuildRepo(null, null)); disabledProperty().bind(controller.eventsDBStaleProperty().not()); } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/datamodel/CombinedEvent.java b/Core/src/org/sleuthkit/autopsy/timeline/datamodel/CombinedEvent.java index 71e022e65b..bdd1f1eaec 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/datamodel/CombinedEvent.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/datamodel/CombinedEvent.java @@ -18,11 +18,11 @@ */ package org.sleuthkit.autopsy.timeline.datamodel; -import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.Set; +import org.python.google.common.collect.ImmutableSet; import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType; /** @@ -98,8 +98,8 @@ public class CombinedEvent { * * @return The event IDs of the combined events. */ - public Collection getEventIDs() { - return eventTypeMap.values(); + public ImmutableSet getEventIDs() { + return ImmutableSet.copyOf(eventTypeMap.values()); } /** diff --git a/Core/src/org/sleuthkit/autopsy/timeline/datamodel/FilteredEventsModel.java b/Core/src/org/sleuthkit/autopsy/timeline/datamodel/FilteredEventsModel.java index 6227256130..74964f7b0d 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/datamodel/FilteredEventsModel.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/datamodel/FilteredEventsModel.java @@ -429,6 +429,10 @@ public final class FilteredEventsModel { return false; } + public List getDerivedEventIDs(Set fileIDs, Set artifactIDS) { + return repo.getDerivedEventIDs(fileIDs, artifactIDS); + } + /** * Post a TagsAddedEvent to all registered subscribers, if the given set of * updated event IDs is not empty. diff --git a/Core/src/org/sleuthkit/autopsy/timeline/datamodel/eventtype/ArtifactEventType.java b/Core/src/org/sleuthkit/autopsy/timeline/datamodel/eventtype/ArtifactEventType.java index 309f2ff122..9602183efa 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/datamodel/eventtype/ArtifactEventType.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/datamodel/eventtype/ArtifactEventType.java @@ -52,6 +52,10 @@ public interface ArtifactEventType extends EventType { public BlackboardAttribute.Type getDateTimeAttrubuteType(); + public default int getArtifactTypeID(){ + return getArtifactType().getTypeID(); + }; + /** * given an artifact, pull out the time stamp, and compose the descriptions. * Each implementation of {@link ArtifactEventType} needs to implement diff --git a/Core/src/org/sleuthkit/autopsy/timeline/db/EventDB.java b/Core/src/org/sleuthkit/autopsy/timeline/db/EventDB.java index 8bbfc756af..eee3b05110 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/db/EventDB.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/db/EventDB.java @@ -667,6 +667,25 @@ public class EventDB { } } + List getDerivedEventIDs(Set fileIDs, Set artifactIDS) { + DBLock.lock(); + String query = "SELECT Group_Concat(event_id) FROM events" + + " WHERE file_id IN (" + StringUtils.join(fileIDs, ", ") + ")" + + " OR artifact_id IN (" + StringUtils.join(artifactIDS, ", ") + ")"; + + try (Statement stmt = con.createStatement(); + ResultSet rs = stmt.executeQuery(query);) { // NON-NLS + while (rs.next()) { + return SQLHelper.unGroupConcat(rs.getString("Group_Concat(event_id)"), Long::valueOf); + } + } catch (SQLException ex) { + LOGGER.log(Level.SEVERE, "Error executing get spanning interval query.", ex); // NON-NLS + } finally { + DBLock.unlock(); + } + return null; + } + /** * create the tags table if it doesn't already exist. This is broken out as * a separate method so it can be used by {@link #reInitializeTags() } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java b/Core/src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java index f4396e3d95..fea7ffc341 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java @@ -207,6 +207,10 @@ public class EventsRepository { return eventDB.countAllEvents(); } + public List getDerivedEventIDs(Set fileIDs, Set artifactIDS) { + return eventDB.getDerivedEventIDs(fileIDs, artifactIDS); + } + private void invalidateCaches() { minCache.invalidateAll(); maxCache.invalidateAll(); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListTimeline.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListTimeline.java index 4e07e031ee..6a49565fba 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListTimeline.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListTimeline.java @@ -18,6 +18,7 @@ */ package org.sleuthkit.autopsy.timeline.ui.listvew; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import java.util.ArrayList; import java.util.Arrays; @@ -62,6 +63,7 @@ import org.controlsfx.control.Notifications; import org.openide.awt.Actions; import org.openide.util.NbBundle; import org.openide.util.actions.Presenter; +import org.python.google.common.collect.Sets; import org.sleuthkit.autopsy.casemodule.services.TagsManager; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.ThreadConfined; @@ -195,6 +197,14 @@ class ListTimeline extends BorderPane { .map(CombinedEvent::getRepresentativeEventID) .collect(Collectors.toSet())); }); + + controller.getSelectedEventIDs().addListener((Observable observable) -> { + ImmutableSet selectedIDs = ImmutableSet.copyOf(controller.getSelectedEventIDs()); + + selectEvents(table.getItems().stream() + .filter(combinedEvent -> Sets.intersection(combinedEvent.getEventIDs(), selectedIDs).isEmpty() == false) + .collect(Collectors.toSet())); + }); } /** From 05416c603fc4b982c0698aad078cf49230bab230 Mon Sep 17 00:00:00 2001 From: jmillman Date: Mon, 6 Jun 2016 09:46:55 -0400 Subject: [PATCH 05/48] aborted atempt to break circular updates --- .../autopsy/timeline/TimeLineController.java | 2 +- .../timeline/TimeLineTopComponent.java | 2 +- .../timeline/ui/listvew/ListTimeline.java | 56 +++++++++---------- .../timeline/ui/listvew/ListViewPane.java | 6 +- 4 files changed, 28 insertions(+), 38 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java index 8c0e5b74d1..d895da9355 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java @@ -493,8 +493,8 @@ public class TimeLineController { synchronized (filteredEvents) { pushTimeRange(interval); } + Platform.runLater(() -> selectEventIDs(eventIDs)); - selectedEventIDs.setAll(eventIDs); } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java index 1ca594fa14..f934f28084 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java @@ -124,7 +124,7 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer LOGGER.log(Level.SEVERE, "Selecting the event node was vetoed.", ex); // NON-NLS } //if there is only one event selected push it into content viewer. - if (selectedEventIDs.size() == 1) { + if (childArray.length == 1) { contentViewerPanel.setNode(childArray[0]); } else { contentViewerPanel.setNode(null); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListTimeline.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListTimeline.java index 6a49565fba..ae228ff5aa 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListTimeline.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListTimeline.java @@ -18,7 +18,6 @@ */ package org.sleuthkit.autopsy.timeline.ui.listvew; -import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import java.util.ArrayList; import java.util.Arrays; @@ -37,7 +36,6 @@ import javafx.beans.Observable; import javafx.beans.binding.StringBinding; import javafx.beans.property.SimpleObjectProperty; import javafx.beans.value.ObservableValue; -import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.fxml.FXML; import javafx.geometry.Pos; @@ -59,11 +57,11 @@ import javafx.scene.layout.VBox; import javafx.util.Callback; import javax.swing.Action; import javax.swing.JMenuItem; +import org.apache.commons.lang3.ArrayUtils; import org.controlsfx.control.Notifications; import org.openide.awt.Actions; import org.openide.util.NbBundle; import org.openide.util.actions.Presenter; -import org.python.google.common.collect.Sets; import org.sleuthkit.autopsy.casemodule.services.TagsManager; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.ThreadConfined; @@ -115,11 +113,6 @@ class ListTimeline extends BorderPane { @FXML private TableColumn hashHitColumn; - /** - * Observable list used to track selected events. - */ - private final ObservableList selectedEventIDs = FXCollections.observableArrayList(); - private final TimeLineController controller; private final SleuthkitCase sleuthkitCase; private final TagsManager tagsManager; @@ -189,21 +182,23 @@ class ListTimeline extends BorderPane { } }); + table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); - table.getSelectionModel().getSelectedItems().addListener((Observable observable) -> { - //keep the selectedEventsIDs in sync with the table's selection model, via getRepresentitiveEventID(). - selectedEventIDs.setAll(table.getSelectionModel().getSelectedItems().stream() + //keep controller's list of selected event IDs in sync with this list's + table.getSelectionModel().getSelectedItems().addListener((Observable change) -> { +// keep the selectedEventsIDs in sync with the table's selection model, via getRepresentitiveEventID(). + controller.selectEventIDs(table.getSelectionModel().getSelectedItems().stream() .filter(Objects::nonNull) .map(CombinedEvent::getRepresentativeEventID) .collect(Collectors.toSet())); + }); - controller.getSelectedEventIDs().addListener((Observable observable) -> { - ImmutableSet selectedIDs = ImmutableSet.copyOf(controller.getSelectedEventIDs()); - - selectEvents(table.getItems().stream() - .filter(combinedEvent -> Sets.intersection(combinedEvent.getEventIDs(), selectedIDs).isEmpty() == false) - .collect(Collectors.toSet())); + controller.getSelectedEventIDs().addListener((Observable change) -> { + Set selectedCombinedEvents = table.getItems().stream() + .filter(combinedEvent -> combinedEvent.getEventIDs().stream().anyMatch(controller.getSelectedEventIDs()::contains)) + .collect(Collectors.toSet()); + selectEvents(selectedCombinedEvents); }); } @@ -225,16 +220,6 @@ class ListTimeline extends BorderPane { table.getItems().setAll(events); } - /** - * Get an ObservableList of IDs of events that are selected in this table. - * - * @return An ObservableList of IDs of events that are selected in this - * table. - */ - ObservableList getSelectedEventIDs() { - return selectedEventIDs; - } - /** * Get an ObservableList of combined events that are selected in this table. * @@ -251,11 +236,20 @@ class ListTimeline extends BorderPane { * @param selectedEvents The events that should be selected. */ void selectEvents(Collection selectedEvents) { - CombinedEvent firstSelected = selectedEvents.stream().min(Comparator.comparing(CombinedEvent::getStartMillis)).orElse(null); table.getSelectionModel().clearSelection(); - table.scrollTo(firstSelected); - selectedEvents.forEach(table.getSelectionModel()::select); - table.requestFocus(); + if (selectedEvents.isEmpty() == false) { + CombinedEvent firstSelected = selectedEvents.stream().filter(Objects::nonNull).min(Comparator.comparing(CombinedEvent::getStartMillis)).orElse(null); + table.scrollTo(firstSelected); + Set selectedIndices = selectedEvents.stream() + .map(table.getItems()::indexOf) + .filter(index -> index >= 0) + .collect(Collectors.toSet()); + Integer[] indices = selectedIndices.toArray(new Integer[selectedIndices.size()]); + if (indices.length >= 1) { + table.getSelectionModel().selectIndices(indices[0], ArrayUtils.toPrimitive(indices)); + table.requestFocus(); + } + } } /** diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewPane.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewPane.java index 06cba98a68..9a85968760 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewPane.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewPane.java @@ -21,7 +21,6 @@ package org.sleuthkit.autopsy.timeline.ui.listvew; import java.util.HashSet; import java.util.List; import javafx.application.Platform; -import javafx.beans.Observable; import javafx.concurrent.Task; import javafx.scene.Parent; import org.joda.time.Interval; @@ -50,10 +49,7 @@ public class ListViewPane extends AbstractTimeLineView { setCenter(listTimeline); setSettingsNodes(new ListViewPane.ListViewSettingsPane().getChildrenUnmodifiable()); - //keep controller's list of selected event IDs in sync with this list's - listTimeline.getSelectedEventIDs().addListener((Observable selectedIDs) -> { - controller.selectEventIDs(listTimeline.getSelectedEventIDs()); - }); + } @Override From e53469de17903eada8dd8b510184ff92f585f899 Mon Sep 17 00:00:00 2001 From: jmillman Date: Mon, 6 Jun 2016 11:02:28 -0400 Subject: [PATCH 06/48] there is still a timing issue preventing the selection from getting picked up in the ListView --- .../autopsy/timeline/TimeLineController.java | 17 ++++++++------- .../timeline/ui/listvew/ListTimeline.java | 21 ++++++++++++------- .../timeline/ui/listvew/ListViewPane.java | 2 +- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java index d895da9355..5c9f9526d3 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java @@ -485,15 +485,12 @@ public class TimeLineController { showFullRange(); } else { - setViewMode(ViewMode.LIST); - List eventIDs = filteredEvents.getDerivedEventIDs(fileIDs, artifactIDS); - - Interval interval = filteredEvents.getSpanningInterval(eventIDs); - synchronized (filteredEvents) { + List eventIDs = filteredEvents.getDerivedEventIDs(fileIDs, artifactIDS); + Interval interval = filteredEvents.getSpanningInterval(eventIDs); pushTimeRange(interval); + selectEventIDs(eventIDs, () -> setViewMode(ViewMode.LIST)); } - Platform.runLater(() -> selectEventIDs(eventIDs)); } } @@ -641,6 +638,12 @@ public class TimeLineController { } public void selectEventIDs(Collection events) { + selectEventIDs(events, () -> { + } + ); + } + + public void selectEventIDs(Collection events, Runnable andThen) { final LoggedTask selectEventIDsTask = new LoggedTask("Select Event IDs", true) { //NON-NLS @Override protected Interval call() throws Exception { @@ -654,7 +657,7 @@ public class TimeLineController { synchronized (TimeLineController.this) { selectedTimeRange.set(get()); selectedEventIDs.setAll(events); - + andThen.run(); } } catch (InterruptedException | ExecutionException ex) { LOGGER.log(Level.SEVERE, getTitle() + " Unexpected error", ex); //NON-NLS diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListTimeline.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListTimeline.java index ae228ff5aa..b2f047f1a9 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListTimeline.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListTimeline.java @@ -182,24 +182,29 @@ class ListTimeline extends BorderPane { } }); - table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); //keep controller's list of selected event IDs in sync with this list's table.getSelectionModel().getSelectedItems().addListener((Observable change) -> { -// keep the selectedEventsIDs in sync with the table's selection model, via getRepresentitiveEventID(). + //keep the selectedEventsIDs in sync with the table's selection model, via getRepresentitiveEventID(). controller.selectEventIDs(table.getSelectionModel().getSelectedItems().stream() .filter(Objects::nonNull) .map(CombinedEvent::getRepresentativeEventID) .collect(Collectors.toSet())); - }); + + Platform.runLater(new Runnable() { + + @Override + public void run() { + Set collect = table.getItems().stream() + .filter(combinedEvent -> combinedEvent.getEventIDs().stream().anyMatch(controller.getSelectedEventIDs()::contains)) + .collect(Collectors.toSet()); - controller.getSelectedEventIDs().addListener((Observable change) -> { - Set selectedCombinedEvents = table.getItems().stream() - .filter(combinedEvent -> combinedEvent.getEventIDs().stream().anyMatch(controller.getSelectedEventIDs()::contains)) - .collect(Collectors.toSet()); - selectEvents(selectedCombinedEvents); + selectEvents(collect); + } }); + + } /** diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewPane.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewPane.java index 9a85968760..28cc798f3b 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewPane.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewPane.java @@ -94,7 +94,7 @@ public class ListViewPane extends AbstractTimeLineView { Platform.runLater(() -> { //put the combined events into the table. listTimeline.setCombinedEvents(combinedEvents); - //restore the selected event + //restore the selected events listTimeline.selectEvents(selectedEvents); }); From 69c14aab46568284e429019947d42d323101af25 Mon Sep 17 00:00:00 2001 From: jmillman Date: Tue, 7 Jun 2016 19:58:45 -0400 Subject: [PATCH 07/48] broken rewrite of ViewInTimeLineAction --- .../datamodel/ViewInTimeLineAction.java | 17 +++++++++------- .../directorytree/DataResultFilterNode.java | 17 ---------------- .../autopsy/timeline/OpenTimelineAction.java | 20 ++++++++++++++++--- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ViewInTimeLineAction.java b/Core/src/org/sleuthkit/autopsy/datamodel/ViewInTimeLineAction.java index f1688dd725..3b2a85dd8a 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ViewInTimeLineAction.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ViewInTimeLineAction.java @@ -5,6 +5,7 @@ */ package org.sleuthkit.autopsy.datamodel; +import com.google.common.collect.Iterables; import java.awt.event.ActionEvent; import java.util.Set; import java.util.stream.Collectors; @@ -41,20 +42,22 @@ public class ViewInTimeLineAction extends AbstractAction { @Override public void actionPerformed(ActionEvent e) { - Set fileIDs = Utilities.actionsGlobalContext().lookupAll(AbstractFile.class).stream() - .map(AbstractFile::getId) + Set files = Utilities.actionsGlobalContext().lookupAll(AbstractFile.class).stream() .collect(Collectors.toSet()); final Set artifactEventTypeIDs = ArtifactEventType.getAllArtifactEventTypes().stream() .map(ArtifactEventType::getArtifactTypeID) .collect(Collectors.toSet()); - + //for each artifact, get all datetime attributes for that artifact type - Set artifactIDs = Utilities.actionsGlobalContext().lookupAll(BlackboardArtifact.class).stream() + Set artifacts = Utilities.actionsGlobalContext().lookupAll(BlackboardArtifact.class).stream() .filter(artifact -> artifactEventTypeIDs.contains(artifact.getArtifactTypeID())) - .map(BlackboardArtifact::getArtifactID) .collect(Collectors.toSet()); - - SystemAction.get(OpenTimelineAction.class).showTimeline(fileIDs, artifactIDs); + + if (files.size() > 1) { + return; + }else{ + SystemAction.get(OpenTimelineAction.class).showTimeline(Iterables.getOnlyElement(files,null), artifacts); + } } } diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java index ee51003160..96c82edf80 100755 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java @@ -66,10 +66,8 @@ import org.sleuthkit.autopsy.datamodel.RecentFilesFilterNode; import org.sleuthkit.autopsy.datamodel.RecentFilesNode; import org.sleuthkit.autopsy.datamodel.Reports; import org.sleuthkit.autopsy.datamodel.Tags; -import org.sleuthkit.autopsy.datamodel.ViewInTimeLineAction; import org.sleuthkit.autopsy.datamodel.VirtualDirectoryNode; import org.sleuthkit.autopsy.datamodel.VolumeNode; -import org.sleuthkit.autopsy.timeline.datamodel.eventtype.ArtifactEventType; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; @@ -79,7 +77,6 @@ import org.sleuthkit.datamodel.Directory; import org.sleuthkit.datamodel.File; import org.sleuthkit.datamodel.LayoutFile; import org.sleuthkit.datamodel.LocalFile; -import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskException; import org.sleuthkit.datamodel.VirtualDirectory; @@ -249,20 +246,6 @@ public class DataResultFilterNode extends FilterNode { actions.add(AddBlackboardArtifactTagAction.getInstance()); } - boolean hasTimeStamp = ArtifactEventType.getAllArtifactEventTypes().stream() - .filter(artEventType -> artEventType.getArtifactType().getTypeID() == ba.getArtifactTypeID()) - .filter(artEventType -> { - try { - return ba.getAttribute(artEventType.getDateTimeAttrubuteType()) != null; - } catch (TskCoreException ex) { - Logger.getLogger(DataResultFilterNode.class.getName()).log(Level.WARNING, "Error retreiving blackboard arttributes from blackboard artifact.", ex); - return false; - } - }).findAny().isPresent(); - if (hasTimeStamp) { - actions.add(ViewInTimeLineAction.getInstance()); - } - return actions; } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java b/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java index 47b9054682..d2566b3b7e 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java @@ -20,8 +20,11 @@ package org.sleuthkit.autopsy.timeline; import java.io.IOException; import java.util.Collections; +import java.util.Optional; import java.util.Set; import java.util.logging.Level; +import javafx.scene.control.ComboBox; +import javafx.scene.control.DialogPane; import org.openide.awt.ActionID; import org.openide.awt.ActionReference; import org.openide.awt.ActionReferences; @@ -34,6 +37,9 @@ import org.sleuthkit.autopsy.core.Installer; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.coreutils.ThreadConfined; +import org.sleuthkit.autopsy.timeline.datamodel.SingleEvent; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.BlackboardArtifact; @ActionID(category = "Tools", id = "org.sleuthkit.autopsy.timeline.Timeline") @ActionRegistration(displayName = "#CTL_MakeTimeline", lazy = false) @@ -63,13 +69,13 @@ public class OpenTimelineAction extends CallableSystemAction { @Override @ThreadConfined(type = ThreadConfined.ThreadType.AWT) public void performAction() { - showTimeline(Collections.emptySet(), Collections.emptySet()); + showTimeline(null, Collections.emptySet()); } @NbBundle.Messages({ "OpenTimelineAction.settingsErrorMessage=Failed to initialize timeline settings.", "OpenTimeLineAction.msgdlg.text=Could not create timeline, there are no data sources."}) - public void showTimeline(Set fileIDs, Set artifactIDS) { + public void showTimeline(AbstractFile file, Set artifactS) { //check case if (!Case.isCaseOpen()) { return; @@ -88,7 +94,15 @@ public class OpenTimelineAction extends CallableSystemAction { timeLineController.shutDownTimeLine(); timeLineController = new TimeLineController(currentCase); } - timeLineController.openTimeLine(fileIDs, artifactIDS); + + javafx.scene.control.Dialog d = new javafx.scene.control.Dialog<>(); + + DialogPane dp = d.getDialogPane(); + dp.setContent(new ComboBox); + + Optional result = d.showAndWait(); + + timeLineController.openTimeLine(file, artifactS); } catch (IOException iOException) { MessageNotifyUtil.Message.error(Bundle.OpenTimelineAction_settingsErrorMessage()); LOGGER.log(Level.SEVERE, "Failed to initialize per case timeline settings.", iOException); From 4e6eff56ca76f63744c9d740c611de64e9ddc1be Mon Sep 17 00:00:00 2001 From: jmillman Date: Wed, 8 Jun 2016 16:16:00 -0400 Subject: [PATCH 08/48] begin creation of ShowInTimelineDialog --- .../autopsy/timeline/FXMLConstructor.java | 2 +- .../autopsy/timeline/OpenTimelineAction.java | 13 +- .../timeline/ShowInTimelineDialog.fxml | 39 +++++ .../timeline/ShowInTimelineDialog.java | 141 ++++++++++++++++++ .../autopsy/timeline/TimeLineController.java | 26 ++-- 5 files changed, 199 insertions(+), 22 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/timeline/ShowInTimelineDialog.fxml create mode 100644 Core/src/org/sleuthkit/autopsy/timeline/ShowInTimelineDialog.java diff --git a/Core/src/org/sleuthkit/autopsy/timeline/FXMLConstructor.java b/Core/src/org/sleuthkit/autopsy/timeline/FXMLConstructor.java index 8d88a70630..05d560c460 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/FXMLConstructor.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/FXMLConstructor.java @@ -74,7 +74,7 @@ public class FXMLConstructor { * */ @ThreadConfined(type = ThreadConfined.ThreadType.JFX) - static public void construct(Node node, Class clazz, String fxmlFileName) { + static public void construct(Node node, Class clazz, String fxmlFileName) { final String name = "nbres:/" + StringUtils.replace(clazz.getPackage().getName(), ".", "/") + "/" + fxmlFileName; // NON-NLS try { diff --git a/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java b/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java index d2566b3b7e..a3733df630 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013-16 Basis Technology Corp. + * Copyright 2011-2016 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -23,8 +23,6 @@ import java.util.Collections; import java.util.Optional; import java.util.Set; import java.util.logging.Level; -import javafx.scene.control.ComboBox; -import javafx.scene.control.DialogPane; import org.openide.awt.ActionID; import org.openide.awt.ActionReference; import org.openide.awt.ActionReferences; @@ -75,7 +73,7 @@ public class OpenTimelineAction extends CallableSystemAction { @NbBundle.Messages({ "OpenTimelineAction.settingsErrorMessage=Failed to initialize timeline settings.", "OpenTimeLineAction.msgdlg.text=Could not create timeline, there are no data sources."}) - public void showTimeline(AbstractFile file, Set artifactS) { + public void showTimeline(AbstractFile file, Set artifacts) { //check case if (!Case.isCaseOpen()) { return; @@ -95,14 +93,11 @@ public class OpenTimelineAction extends CallableSystemAction { timeLineController = new TimeLineController(currentCase); } - javafx.scene.control.Dialog d = new javafx.scene.control.Dialog<>(); - - DialogPane dp = d.getDialogPane(); - dp.setContent(new ComboBox); + ShowInTimelineDialog d = new ShowInTimelineDialog(timeLineController, file, artifacts); Optional result = d.showAndWait(); - timeLineController.openTimeLine(file, artifactS); +// timeLineController.openTimeLine(result); } catch (IOException iOException) { MessageNotifyUtil.Message.error(Bundle.OpenTimelineAction_settingsErrorMessage()); LOGGER.log(Level.SEVERE, "Failed to initialize per case timeline settings.", iOException); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ShowInTimelineDialog.fxml b/Core/src/org/sleuthkit/autopsy/timeline/ShowInTimelineDialog.fxml new file mode 100644 index 0000000000..8426ea87b9 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/timeline/ShowInTimelineDialog.fxml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ShowInTimelineDialog.java b/Core/src/org/sleuthkit/autopsy/timeline/ShowInTimelineDialog.java new file mode 100644 index 0000000000..26ef2808a3 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/timeline/ShowInTimelineDialog.java @@ -0,0 +1,141 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2011-2016 Basis Technology Corp. + * Contact: carrier sleuthkit 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.timeline; + +import java.io.IOException; +import java.net.URL; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.logging.Level; +import java.util.stream.Collectors; +import javafx.beans.property.SimpleObjectProperty; +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.scene.control.ButtonBar; +import javafx.scene.control.ButtonType; +import javafx.scene.control.ChoiceBox; +import javafx.scene.control.Dialog; +import javafx.scene.control.DialogPane; +import javafx.scene.control.Spinner; +import javafx.scene.control.TableCell; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableView; +import javafx.scene.image.ImageView; +import javafx.scene.layout.VBox; +import org.apache.commons.lang3.StringUtils; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.timeline.datamodel.SingleEvent; +import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.BlackboardArtifact; + +/** + * + */ +public class ShowInTimelineDialog extends Dialog { + + private static final ButtonType show = new ButtonType("Show Timeline", ButtonBar.ButtonData.OK_DONE); + + private static final Logger LOGGER = Logger.getLogger(ShowInTimelineDialog.class.getName()); + + @FXML + private TableView eventTable; + + @FXML + private TableColumn typeColumn; + + @FXML + private TableColumn dateTimeColumn; + + @FXML + private Spinner amountsSpinner; + + @FXML + private ChoiceBox unitChoiceBox; + private final VBox contentRoot; + private final TimeLineController controller; + + public ShowInTimelineDialog(TimeLineController controller, AbstractFile file, Set artifacts) { + super(); + this.controller = controller; + contentRoot = new VBox(); + final String name = "nbres:/" + StringUtils.replace(ShowInTimelineDialog.class.getPackage().getName(), ".", "/") + "/ShowInTimelineDialog.fxml"; // NON-NLS + + try { + FXMLLoader fxmlLoader = new FXMLLoader(new URL(name)); + fxmlLoader.setRoot(contentRoot); + fxmlLoader.setController(this); + + fxmlLoader.load(); + } catch (IOException ex) { + LOGGER.log(Level.SEVERE, "Unable to load FXML, node initialization may not be complete.", ex); //NON-NLS + } + + assert eventTable != null : "fx:id=\"eventTable\" was not injected: check your FXML file 'ShowInTimelineDialog.fxml'."; + assert typeColumn != null : "fx:id=\"typeColumn\" was not injected: check your FXML file 'ShowInTimelineDialog.fxml'."; + assert dateTimeColumn != null : "fx:id=\"dateTimeColumn\" was not injected: check your FXML file 'ShowInTimelineDialog.fxml'."; + assert amountsSpinner != null : "fx:id=\"amountsSpinner\" was not injected: check your FXML file 'ShowInTimelineDialog.fxml'."; + assert unitChoiceBox != null : "fx:id=\"unitChoiceBox\" was not injected: check your FXML file 'ShowInTimelineDialog.fxml'."; + DialogPane dialogPane = getDialogPane(); + dialogPane.setContent(contentRoot); + dialogPane.getButtonTypes().setAll(show, ButtonType.CANCEL); + dialogPane.lookupButton(show).disableProperty().bind(eventTable.getSelectionModel().selectedItemProperty().isNull()); + + setResultConverter((ButtonType param) -> { + if (param == show) { + return eventTable.getSelectionModel().getSelectedItem(); + } + return null; + }); + + typeColumn.setCellValueFactory(param -> new SimpleObjectProperty<>(param.getValue().getEventType())); + typeColumn.setCellFactory((TableColumn param) -> new TableCell() { + @Override + protected void updateItem(EventType item, boolean empty) { + super.updateItem(item, empty); + + if (item == null || empty) { + setText(null); + setGraphic(null); + } else { + setText(item.getDisplayName()); + setGraphic(new ImageView(item.getFXImage())); + } + } + + }); + dateTimeColumn.setCellValueFactory(param -> new SimpleObjectProperty<>(param.getValue().getStartMillis())); + dateTimeColumn.setCellFactory((TableColumn param) -> new TableCell() { + @Override + protected void updateItem(Long item, boolean empty) { + super.updateItem(item, empty); + + if (item == null || empty) { + setText(null); + } else { + setText(TimeLineController.getZonedFormatter().print(item)); + } + } + + }); + List eventIDS = controller.getEventsModel().getDerivedEventIDs(Collections.singleton(file.getId()), artifacts.stream().map(BlackboardArtifact::getArtifactID).collect(Collectors.toSet())); + eventTable.getItems().setAll(eventIDS.stream().map(controller.getEventsModel()::getEventById).collect(Collectors.toSet())); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java index 5c9f9526d3..c42a508911 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java @@ -90,6 +90,8 @@ import org.sleuthkit.autopsy.timeline.zooming.DescriptionLoD; import org.sleuthkit.autopsy.timeline.zooming.EventTypeZoomLevel; import org.sleuthkit.autopsy.timeline.zooming.TimeUnits; import org.sleuthkit.autopsy.timeline.zooming.ZoomParams; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.BlackboardArtifact; /** * Controller in the MVC design along with FilteredEventsModel TimeLineView. @@ -401,7 +403,7 @@ public class TimeLineController { @NbBundle.Messages({ "TimeLineController.setIngestRunning.errMsgRunning=Failed to mark the timeline db as populated while ingest was running. Some results may be out of date or missing.", "TimeLinecontroller.setIngestRunning.errMsgNotRunning=Failed to mark the timeline db as populated while ingest was not running. Some results may be out of date or missing."}) - private void rebuildRepoHelper(Function, CancellationProgressTask> repoBuilder, Boolean markDBNotStale, Set fileIDs, Set artifactIDS) { + private void rebuildRepoHelper(Function, CancellationProgressTask> repoBuilder, Boolean markDBNotStale, AbstractFile file, Set artifacts) { boolean ingestRunning = IngestManager.getInstance().isIngestRunning(); //if there is an existing prompt or progressdialog, just show that @@ -436,7 +438,7 @@ public class TimeLineController { filteredEvents.postDBUpdated(); } SwingUtilities.invokeLater(this::showWindow); - TimeLineController.this.showEvents(fileIDs, artifactIDS); +// TimeLineController.this.showEvents(file, artifacts); break; case FAILED: @@ -458,8 +460,8 @@ public class TimeLineController { * done. */ @ThreadConfined(type = ThreadConfined.ThreadType.JFX) - public void rebuildRepo(Set fileIDs, Set artifactIDS) { - rebuildRepoHelper(eventsRepository::rebuildRepository, true, fileIDs, artifactIDS); + public void rebuildRepo(AbstractFile file, Set artifacts) { + rebuildRepoHelper(eventsRepository::rebuildRepository, true, file, artifacts); } /** @@ -467,8 +469,8 @@ public class TimeLineController { * timeline when done. */ @ThreadConfined(type = ThreadConfined.ThreadType.JFX) - void rebuildTagsTable(Set fileIDs, Set artifactIDS) { - rebuildRepoHelper(eventsRepository::rebuildTags, false, fileIDs, artifactIDS); + void rebuildTagsTable(AbstractFile file, Set artifacts) { + rebuildRepoHelper(eventsRepository::rebuildTags, false, file, artifacts); } /** @@ -517,7 +519,7 @@ public class TimeLineController { * necessary, and show the timeline window. */ @ThreadConfined(type = ThreadConfined.ThreadType.AWT) - void openTimeLine(Set fileIDs, Set artifactIDS) { + void openTimeLine(AbstractFile file, Set artifact) { // listen for case changes (specifically images being added, and case changes). if (Case.isCaseOpen() && !listeningToAutopsy) { IngestManager.getInstance().addIngestModuleEventListener(ingestModuleListener); @@ -526,7 +528,7 @@ public class TimeLineController { listeningToAutopsy = true; } - Platform.runLater(() -> promptForRebuild(fileIDs, artifactIDS)); + Platform.runLater(() -> promptForRebuild(file, artifact)); } /** @@ -536,7 +538,7 @@ public class TimeLineController { * rebuild is done, or immediately if the rebuild is not confirmed. F */ @ThreadConfined(type = ThreadConfined.ThreadType.JFX) - private void promptForRebuild(Set fileIDs, Set artifactIDS) { + private void promptForRebuild(AbstractFile file, Set artifacts) { //if there is an existing prompt or progressdialog, just show that if (promptDialogManager.bringCurrentDialogToFront()) { return; @@ -544,7 +546,7 @@ public class TimeLineController { //if the repo is empty just (re)build it with out asking, the user can always cancel part way through if (eventsRepository.countAllEvents() == 0) { - rebuildRepo(fileIDs, artifactIDS); + rebuildRepo(file, artifacts); return; } @@ -552,7 +554,7 @@ public class TimeLineController { List rebuildReasons = getRebuildReasons(); if (false == rebuildReasons.isEmpty()) { if (promptDialogManager.confirmRebuild(rebuildReasons)) { - rebuildRepo(fileIDs, artifactIDS); + rebuildRepo(file, artifacts); return; } } @@ -564,7 +566,7 @@ public class TimeLineController { * * //TODO: can we check the tags to see if we need to do this? */ - rebuildTagsTable(fileIDs, artifactIDS); + rebuildTagsTable(file, artifacts); } /** From 41658e76ad3941977624392f2f29118446ef4240 Mon Sep 17 00:00:00 2001 From: jmillman Date: Wed, 8 Jun 2016 18:10:46 -0400 Subject: [PATCH 09/48] mostly working --- .../datamodel/BlackboardArtifactNode.java | 36 +++- .../sleuthkit/autopsy/datamodel/FileNode.java | 2 +- ...java => ViewArtifactInTimelineAction.java} | 25 ++- .../datamodel/ViewFileInTimelineAction.java | 61 +++++++ .../autopsy/timeline/EventInTimeRange.java | 33 ++++ .../autopsy/timeline/OpenTimelineAction.java | 29 ++-- .../timeline/ShowInTimelineDialog.fxml | 17 +- .../timeline/ShowInTimelineDialog.java | 154 +++++++++++++----- .../autopsy/timeline/TimeLineController.java | 57 ++++--- .../datamodel/FilteredEventsModel.java | 10 +- .../datamodel/eventtype/MiscTypes.java | 6 +- .../autopsy/timeline/db/EventDB.java | 45 ++++- .../autopsy/timeline/db/EventsRepository.java | 18 +- .../autopsy/timeline/ui/ViewFrame.java | 9 +- .../autopsy/timeline/ui/ZoomRanges.java | 2 +- .../timeline/ui/listvew/ListTimeline.java | 2 +- .../autopsy/timeline/utils/IntervalUtils.java | 9 + 17 files changed, 390 insertions(+), 125 deletions(-) rename Core/src/org/sleuthkit/autopsy/datamodel/{ViewInTimeLineAction.java => ViewArtifactInTimelineAction.java} (72%) create mode 100644 Core/src/org/sleuthkit/autopsy/datamodel/ViewFileInTimelineAction.java create mode 100644 Core/src/org/sleuthkit/autopsy/timeline/EventInTimeRange.java diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java index afeacb9a46..a437da3d10 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java @@ -1,15 +1,15 @@ /* * Autopsy Forensic Browser - * + * * Copyright 2011-2014 Basis Technology Corp. * Contact: carrier sleuthkit 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. @@ -24,6 +24,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.logging.Level; +import javax.swing.Action; import org.openide.nodes.Children; import org.openide.nodes.Sheet; import org.openide.util.Lookup; @@ -31,6 +32,7 @@ import org.openide.util.NbBundle; import org.openide.util.lookup.Lookups; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.timeline.datamodel.eventtype.ArtifactEventType; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; @@ -100,6 +102,32 @@ public class BlackboardArtifactNode extends DisplayableItemNode { this.setIconBaseWithExtension(ExtractedContent.getIconFilePath(artifact.getArtifactTypeID())); //NON-NLS } + @Override + public Action[] getActions(boolean context) { + + List actionsList = new ArrayList<>(); + actionsList.addAll(Arrays.asList(super.getActions(context))); + + boolean hasTimeStamp = ArtifactEventType.getAllArtifactEventTypes().stream() + .filter(artEventType -> artEventType.getArtifactType().getTypeID() == artifact.getArtifactTypeID()) + .filter(artEventType -> { + try { + return artifact.getAttribute(artEventType.getDateTimeAttrubuteType()) != null; + } catch (TskCoreException ex) { + Logger.getLogger(BlackboardArtifactNode.class.getName()).log(Level.WARNING, "Error retreiving blackboard arttributes from blackboard artifact.", ex); + return false; + } + }).findAny().isPresent(); + if (hasTimeStamp) { + actionsList.add(ViewArtifactInTimelineAction.getInstance()); + } + + if (associated != null){ + actionsList.add(ViewFileInTimelineAction.getInstance()); + } + return actionsList.toArray(new Action[actionsList.size()]); + } + /** * Set the filter node display name. The value will either be the file name * or something along the lines of e.g. "Messages Artifact" for keyword hits diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileNode.java index 5d89f6fe33..1ef166bc6f 100755 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileNode.java @@ -87,7 +87,7 @@ public class FileNode extends AbstractFsContentNode { NbBundle.getMessage(this.getClass(), "FileNode.getActions.viewInNewWin.text"), this)); actionsList.add(new ExternalViewerAction( NbBundle.getMessage(this.getClass(), "FileNode.getActions.openInExtViewer.text"), this)); - actionsList.add(ViewInTimeLineAction.getInstance()); + actionsList.add(ViewFileInTimelineAction.getInstance()); actionsList.add(null); // creates a menu separator actionsList.add(ExtractAction.getInstance()); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ViewInTimeLineAction.java b/Core/src/org/sleuthkit/autopsy/datamodel/ViewArtifactInTimelineAction.java similarity index 72% rename from Core/src/org/sleuthkit/autopsy/datamodel/ViewInTimeLineAction.java rename to Core/src/org/sleuthkit/autopsy/datamodel/ViewArtifactInTimelineAction.java index 3b2a85dd8a..7a498f8e56 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ViewInTimeLineAction.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ViewArtifactInTimelineAction.java @@ -14,50 +14,47 @@ import org.openide.util.Utilities; import org.openide.util.actions.SystemAction; import org.sleuthkit.autopsy.timeline.OpenTimelineAction; import org.sleuthkit.autopsy.timeline.datamodel.eventtype.ArtifactEventType; -import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; /** * */ -public class ViewInTimeLineAction extends AbstractAction { +public class ViewArtifactInTimelineAction extends AbstractAction { private static final long serialVersionUID = 1L; // This class is a singleton to support multi-selection of nodes, since // org.openide.nodes.NodeOp.findActions(Node[] nodes) will only pick up an Action if every // node in the array returns a reference to the same action object from Node.getActions(boolean). - private static ViewInTimeLineAction instance; + private static ViewArtifactInTimelineAction instance; - public static synchronized ViewInTimeLineAction getInstance() { + public static synchronized ViewArtifactInTimelineAction getInstance() { if (null == instance) { - instance = new ViewInTimeLineAction(); + instance = new ViewArtifactInTimelineAction(); } return instance; } - private ViewInTimeLineAction() { - super("View in Timeline"); + private ViewArtifactInTimelineAction() { + super("View result in Timeline"); } @Override public void actionPerformed(ActionEvent e) { - Set files = Utilities.actionsGlobalContext().lookupAll(AbstractFile.class).stream() - .collect(Collectors.toSet()); final Set artifactEventTypeIDs = ArtifactEventType.getAllArtifactEventTypes().stream() .map(ArtifactEventType::getArtifactTypeID) .collect(Collectors.toSet()); - + //for each artifact, get all datetime attributes for that artifact type Set artifacts = Utilities.actionsGlobalContext().lookupAll(BlackboardArtifact.class).stream() .filter(artifact -> artifactEventTypeIDs.contains(artifact.getArtifactTypeID())) .collect(Collectors.toSet()); - - if (files.size() > 1) { + + if (artifacts.size() > 1) { return; - }else{ - SystemAction.get(OpenTimelineAction.class).showTimeline(Iterables.getOnlyElement(files,null), artifacts); + } else { + SystemAction.get(OpenTimelineAction.class).showArtifactInTimeline(Iterables.getOnlyElement(artifacts, null)); } } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ViewFileInTimelineAction.java b/Core/src/org/sleuthkit/autopsy/datamodel/ViewFileInTimelineAction.java new file mode 100644 index 0000000000..642b9b0d45 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ViewFileInTimelineAction.java @@ -0,0 +1,61 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.sleuthkit.autopsy.datamodel; + +import com.google.common.collect.Iterables; +import java.awt.event.ActionEvent; +import java.util.Set; +import java.util.stream.Collectors; +import javax.swing.AbstractAction; +import org.openide.util.Utilities; +import org.openide.util.actions.SystemAction; +import org.sleuthkit.autopsy.timeline.OpenTimelineAction; +import org.sleuthkit.datamodel.AbstractFile; + +/** + * + */ +public class ViewFileInTimelineAction extends AbstractAction { + + private static final long serialVersionUID = 1L; + + // This class is a singleton to support multi-selection of nodes, since + // org.openide.nodes.NodeOp.findActions(Node[] nodes) will only pick up an Action if every + // node in the array returns a reference to the same action object from Node.getActions(boolean). + private static ViewFileInTimelineAction instance; + + public static synchronized ViewFileInTimelineAction getInstance() { + if (null == instance) { + instance = new ViewFileInTimelineAction(); + } + return instance; + } + + private ViewFileInTimelineAction() { + super("View file in Timeline"); + } + + @Override + public void actionPerformed(ActionEvent e) { + Set files = Utilities.actionsGlobalContext().lookupAll(AbstractFile.class).stream() + .collect(Collectors.toSet()); + +// final Set artifactEventTypeIDs = ArtifactEventType.getAllArtifactEventTypes().stream() +// .map(ArtifactEventType::getArtifactTypeID) +// .collect(Collectors.toSet()); +// +// //for each artifact, get all datetime attributes for that artifact type +// Set artifacts = Utilities.actionsGlobalContext().lookupAll(BlackboardArtifact.class).stream() +// .filter(artifact -> artifactEventTypeIDs.contains(artifact.getArtifactTypeID())) +// .collect(Collectors.toSet()); + + if (files.size() > 1) { + return; + }else{ + SystemAction.get(OpenTimelineAction.class).showFileInTimeline(Iterables.getOnlyElement(files,null));//, artifacts); + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/timeline/EventInTimeRange.java b/Core/src/org/sleuthkit/autopsy/timeline/EventInTimeRange.java new file mode 100644 index 0000000000..145cecd867 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/timeline/EventInTimeRange.java @@ -0,0 +1,33 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package org.sleuthkit.autopsy.timeline; + +import java.util.Set; +import org.joda.time.Interval; + +/** + * + */ +public class EventInTimeRange { + + private final Set eventIDs; + private final Interval range; + + public EventInTimeRange(Set eventIDs, Interval range) { + this.eventIDs = eventIDs; + this.range = range; + } + + public Set getEventIDs() { + return eventIDs; + } + + public Interval getRange() { + return range; + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java b/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java index a3733df630..93f8b1e190 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java @@ -19,9 +19,6 @@ package org.sleuthkit.autopsy.timeline; import java.io.IOException; -import java.util.Collections; -import java.util.Optional; -import java.util.Set; import java.util.logging.Level; import org.openide.awt.ActionID; import org.openide.awt.ActionReference; @@ -35,7 +32,6 @@ import org.sleuthkit.autopsy.core.Installer; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.coreutils.ThreadConfined; -import org.sleuthkit.autopsy.timeline.datamodel.SingleEvent; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; @@ -67,13 +63,13 @@ public class OpenTimelineAction extends CallableSystemAction { @Override @ThreadConfined(type = ThreadConfined.ThreadType.AWT) public void performAction() { - showTimeline(null, Collections.emptySet()); + showTimeline(); } @NbBundle.Messages({ "OpenTimelineAction.settingsErrorMessage=Failed to initialize timeline settings.", "OpenTimeLineAction.msgdlg.text=Could not create timeline, there are no data sources."}) - public void showTimeline(AbstractFile file, Set artifacts) { + private void showTimeline(AbstractFile file, BlackboardArtifact artifact) { //check case if (!Case.isCaseOpen()) { return; @@ -92,12 +88,9 @@ public class OpenTimelineAction extends CallableSystemAction { timeLineController.shutDownTimeLine(); timeLineController = new TimeLineController(currentCase); } - - ShowInTimelineDialog d = new ShowInTimelineDialog(timeLineController, file, artifacts); - - Optional result = d.showAndWait(); - -// timeLineController.openTimeLine(result); + + timeLineController.openTimeLine(file, artifact); + } catch (IOException iOException) { MessageNotifyUtil.Message.error(Bundle.OpenTimelineAction_settingsErrorMessage()); LOGGER.log(Level.SEVERE, "Failed to initialize per case timeline settings.", iOException); @@ -107,6 +100,18 @@ public class OpenTimelineAction extends CallableSystemAction { } } + public void showTimeline() { + showTimeline(null, null); + } + + public void showFileInTimeline(AbstractFile file) { + showTimeline(file, null); + } + + public void showArtifactInTimeline(BlackboardArtifact artifact) { + showTimeline(null, artifact); + } + @Override public String getName() { return NbBundle.getMessage(OpenTimelineAction.class, "CTL_MakeTimeline"); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ShowInTimelineDialog.fxml b/Core/src/org/sleuthkit/autopsy/timeline/ShowInTimelineDialog.fxml index 8426ea87b9..57bd4b9c80 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ShowInTimelineDialog.fxml +++ b/Core/src/org/sleuthkit/autopsy/timeline/ShowInTimelineDialog.fxml @@ -1,7 +1,7 @@ - + @@ -9,13 +9,12 @@ - - + - + diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ShowInTimelineDialog.java b/Core/src/org/sleuthkit/autopsy/timeline/ShowInTimelineDialog.java index 26ef2808a3..4ad7d996e6 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ShowInTimelineDialog.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ShowInTimelineDialog.java @@ -20,9 +20,12 @@ package org.sleuthkit.autopsy.timeline; import java.io.IOException; import java.net.URL; +import java.time.Duration; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.Set; import java.util.logging.Level; import java.util.stream.Collectors; import javafx.beans.property.SimpleObjectProperty; @@ -30,28 +33,34 @@ import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.scene.control.ButtonBar; import javafx.scene.control.ButtonType; -import javafx.scene.control.ChoiceBox; +import javafx.scene.control.ComboBox; import javafx.scene.control.Dialog; import javafx.scene.control.DialogPane; +import javafx.scene.control.Label; +import javafx.scene.control.ListCell; import javafx.scene.control.Spinner; +import javafx.scene.control.SpinnerValueFactory; import javafx.scene.control.TableCell; import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; import javafx.scene.image.ImageView; import javafx.scene.layout.VBox; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.text.WordUtils; +import org.joda.time.Interval; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.timeline.datamodel.SingleEvent; import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType; +import org.sleuthkit.autopsy.timeline.utils.IntervalUtils; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; /** * */ -public class ShowInTimelineDialog extends Dialog { +public class ShowInTimelineDialog extends Dialog { - private static final ButtonType show = new ButtonType("Show Timeline", ButtonBar.ButtonData.OK_DONE); + private static final ButtonType SHOW = new ButtonType("Show Timeline", ButtonBar.ButtonData.OK_DONE); private static final Logger LOGGER = Logger.getLogger(ShowInTimelineDialog.class.getName()); @@ -65,14 +74,39 @@ public class ShowInTimelineDialog extends Dialog { private TableColumn dateTimeColumn; @FXML - private Spinner amountsSpinner; + private Spinner amountSpinner; @FXML - private ChoiceBox unitChoiceBox; + private ComboBox unitComboBox; + @FXML + private Label chooseEventLabel; + private final VBox contentRoot; private final TimeLineController controller; - public ShowInTimelineDialog(TimeLineController controller, AbstractFile file, Set artifacts) { + private static final List SCROLL_BY_UNITS = Arrays.asList( + ChronoUnit.YEARS, + ChronoUnit.MONTHS, + ChronoUnit.DAYS, + ChronoUnit.HOURS, + ChronoUnit.MINUTES, + ChronoUnit.SECONDS); + + static private class ChronoUnitListCell extends ListCell { + + @Override + protected void updateItem(ChronoUnit item, boolean empty) { + super.updateItem(item, empty); + + if (empty || item == null) { + setText(null); + } else { + setText(WordUtils.capitalizeFully(item.toString())); + } + } + } + + public ShowInTimelineDialog(TimeLineController controller, AbstractFile file, BlackboardArtifact artifact) { super(); this.controller = controller; contentRoot = new VBox(); @@ -91,51 +125,89 @@ public class ShowInTimelineDialog extends Dialog { assert eventTable != null : "fx:id=\"eventTable\" was not injected: check your FXML file 'ShowInTimelineDialog.fxml'."; assert typeColumn != null : "fx:id=\"typeColumn\" was not injected: check your FXML file 'ShowInTimelineDialog.fxml'."; assert dateTimeColumn != null : "fx:id=\"dateTimeColumn\" was not injected: check your FXML file 'ShowInTimelineDialog.fxml'."; - assert amountsSpinner != null : "fx:id=\"amountsSpinner\" was not injected: check your FXML file 'ShowInTimelineDialog.fxml'."; - assert unitChoiceBox != null : "fx:id=\"unitChoiceBox\" was not injected: check your FXML file 'ShowInTimelineDialog.fxml'."; + assert amountSpinner != null : "fx:id=\"amountsSpinner\" was not injected: check your FXML file 'ShowInTimelineDialog.fxml'."; + assert unitComboBox != null : "fx:id=\"unitChoiceBox\" was not injected: check your FXML file 'ShowInTimelineDialog.fxml'."; DialogPane dialogPane = getDialogPane(); dialogPane.setContent(contentRoot); - dialogPane.getButtonTypes().setAll(show, ButtonType.CANCEL); - dialogPane.lookupButton(show).disableProperty().bind(eventTable.getSelectionModel().selectedItemProperty().isNull()); + dialogPane.getButtonTypes().setAll(SHOW, ButtonType.CANCEL); + - setResultConverter((ButtonType param) -> { - if (param == show) { - return eventTable.getSelectionModel().getSelectedItem(); + setResultConverter(buttonType -> { + if (buttonType == SHOW) { + SingleEvent selectedEvent = eventTable.getSelectionModel().getSelectedItem(); + + if (file == null) { + selectedEvent = eventTable.getItems().get(0); + } + Duration selectedDuration = Duration.of(amountSpinner.getValue(), unitComboBox.getSelectionModel().getSelectedItem()); + + Interval range = IntervalUtils.getIntervalAround(Instant.ofEpochMilli(selectedEvent.getStartMillis()), selectedDuration); + return new EventInTimeRange(Collections.singleton(selectedEvent.getEventID()), range); + } else { + return null; } - return null; }); + amountSpinner.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(1, 1000)); + + unitComboBox.setButtonCell(new ChronoUnitListCell()); + unitComboBox.setCellFactory(comboBox -> new ChronoUnitListCell()); + unitComboBox.getItems().setAll(SCROLL_BY_UNITS); + unitComboBox.getSelectionModel().select(ChronoUnit.MINUTES); + typeColumn.setCellValueFactory(param -> new SimpleObjectProperty<>(param.getValue().getEventType())); - typeColumn.setCellFactory((TableColumn param) -> new TableCell() { - @Override - protected void updateItem(EventType item, boolean empty) { - super.updateItem(item, empty); + typeColumn.setCellFactory(param -> new TypeTableCell<>()); - if (item == null || empty) { - setText(null); - setGraphic(null); - } else { - setText(item.getDisplayName()); - setGraphic(new ImageView(item.getFXImage())); - } - } - - }); dateTimeColumn.setCellValueFactory(param -> new SimpleObjectProperty<>(param.getValue().getStartMillis())); - dateTimeColumn.setCellFactory((TableColumn param) -> new TableCell() { - @Override - protected void updateItem(Long item, boolean empty) { - super.updateItem(item, empty); + dateTimeColumn.setCellFactory(param -> new DateTimeTableCell<>()); - if (item == null || empty) { - setText(null); - } else { - setText(TimeLineController.getZonedFormatter().print(item)); - } - } + List eventIDS; + if (file != null) { + eventIDS = controller.getEventsModel().getEventIDsForFile(file, false); + dialogPane.lookupButton(SHOW).disableProperty().bind(eventTable.getSelectionModel().selectedItemProperty().isNull()); + } else if (artifact != null) { - }); - List eventIDS = controller.getEventsModel().getDerivedEventIDs(Collections.singleton(file.getId()), artifacts.stream().map(BlackboardArtifact::getArtifactID).collect(Collectors.toSet())); + eventIDS = controller.getEventsModel().getEventIDsForArtifact(artifact); + } else { + throw new IllegalArgumentException(); + } + setResizable(true); eventTable.getItems().setAll(eventIDS.stream().map(controller.getEventsModel()::getEventById).collect(Collectors.toSet())); + if (eventIDS.size() == 1) { + chooseEventLabel.setVisible(false); + chooseEventLabel.setManaged(false); + eventTable.getSelectionModel().select(0); + } + eventTable.setPrefHeight(Math.min(200, 24 * eventTable.getItems().size() + 28)); + } + + static private class DateTimeTableCell extends TableCell { + + @Override + protected void updateItem(Long item, boolean empty) { + super.updateItem(item, empty); + + if (item == null || empty) { + setText(null); + } else { + setText(TimeLineController.getZonedFormatter().print(item)); + } + } + } + + static private class TypeTableCell extends TableCell { + + @Override + protected void updateItem(EventType item, boolean empty) { + super.updateItem(item, empty); + + if (item == null || empty) { + setText(null); + setGraphic(null); + } else { + setText(item.getDisplayName()); + setGraphic(new ImageView(item.getFXImage())); + } + } } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java index c42a508911..fcc95938e3 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java @@ -26,6 +26,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Optional; import java.util.Set; import java.util.TimeZone; import java.util.concurrent.ExecutionException; @@ -403,7 +404,7 @@ public class TimeLineController { @NbBundle.Messages({ "TimeLineController.setIngestRunning.errMsgRunning=Failed to mark the timeline db as populated while ingest was running. Some results may be out of date or missing.", "TimeLinecontroller.setIngestRunning.errMsgNotRunning=Failed to mark the timeline db as populated while ingest was not running. Some results may be out of date or missing."}) - private void rebuildRepoHelper(Function, CancellationProgressTask> repoBuilder, Boolean markDBNotStale, AbstractFile file, Set artifacts) { + private void rebuildRepoHelper(Function, CancellationProgressTask> repoBuilder, Boolean markDBNotStale, AbstractFile file, BlackboardArtifact artifact) { boolean ingestRunning = IngestManager.getInstance().isIngestRunning(); //if there is an existing prompt or progressdialog, just show that @@ -437,8 +438,19 @@ public class TimeLineController { setEventsDBStale(false); filteredEvents.postDBUpdated(); } - SwingUtilities.invokeLater(this::showWindow); -// TimeLineController.this.showEvents(file, artifacts); + + if (file == null && artifact==null) { + SwingUtilities.invokeLater(this::showWindow); + TimeLineController.this.showFullRange(); + } else { + ShowInTimelineDialog d = new ShowInTimelineDialog(this, file, artifact); + + Optional result = d.showAndWait(); + result.ifPresent((EventInTimeRange t) -> { + SwingUtilities.invokeLater(this::showWindow); + showEvents(t.getEventIDs(), t.getRange()); + }); + } break; case FAILED: @@ -460,8 +472,8 @@ public class TimeLineController { * done. */ @ThreadConfined(type = ThreadConfined.ThreadType.JFX) - public void rebuildRepo(AbstractFile file, Set artifacts) { - rebuildRepoHelper(eventsRepository::rebuildRepository, true, file, artifacts); + public void rebuildRepo(AbstractFile file, BlackboardArtifact artifact) { + rebuildRepoHelper(eventsRepository::rebuildRepository, true, file, artifact); } /** @@ -469,31 +481,31 @@ public class TimeLineController { * timeline when done. */ @ThreadConfined(type = ThreadConfined.ThreadType.JFX) - void rebuildTagsTable(AbstractFile file, Set artifacts) { - rebuildRepoHelper(eventsRepository::rebuildTags, false, file, artifacts); + void rebuildTagsTable(AbstractFile file,BlackboardArtifact artifact) { + rebuildRepoHelper(eventsRepository::rebuildTags, false, file, artifact); } /** * Show the entire range of the timeline. */ - public boolean showFullRange() { + private boolean showFullRange() { synchronized (filteredEvents) { return pushTimeRange(filteredEvents.getSpanningInterval()); } } - public void showEvents(Set fileIDs, Set artifactIDS) { - if (fileIDs.isEmpty() && artifactIDS.isEmpty()) { + public void showEvents(Set eventIDs, Interval interval) { + if (eventIDs == null && interval == null) { showFullRange(); } else { - synchronized (filteredEvents) { - List eventIDs = filteredEvents.getDerivedEventIDs(fileIDs, artifactIDS); - Interval interval = filteredEvents.getSpanningInterval(eventIDs); - pushTimeRange(interval); - selectEventIDs(eventIDs, () -> setViewMode(ViewMode.LIST)); + if (interval != null) { + pushTimeRange(interval); + } + if (eventIDs != null) { + selectEventIDs(eventIDs, () -> setViewMode(ViewMode.LIST)); + } } - } } @@ -519,7 +531,7 @@ public class TimeLineController { * necessary, and show the timeline window. */ @ThreadConfined(type = ThreadConfined.ThreadType.AWT) - void openTimeLine(AbstractFile file, Set artifact) { + void openTimeLine(AbstractFile file, BlackboardArtifact artifact) { // listen for case changes (specifically images being added, and case changes). if (Case.isCaseOpen() && !listeningToAutopsy) { IngestManager.getInstance().addIngestModuleEventListener(ingestModuleListener); @@ -538,7 +550,7 @@ public class TimeLineController { * rebuild is done, or immediately if the rebuild is not confirmed. F */ @ThreadConfined(type = ThreadConfined.ThreadType.JFX) - private void promptForRebuild(AbstractFile file, Set artifacts) { + private void promptForRebuild(AbstractFile file, BlackboardArtifact artifact) { //if there is an existing prompt or progressdialog, just show that if (promptDialogManager.bringCurrentDialogToFront()) { return; @@ -546,7 +558,7 @@ public class TimeLineController { //if the repo is empty just (re)build it with out asking, the user can always cancel part way through if (eventsRepository.countAllEvents() == 0) { - rebuildRepo(file, artifacts); + rebuildRepo(file, artifact); return; } @@ -554,7 +566,7 @@ public class TimeLineController { List rebuildReasons = getRebuildReasons(); if (false == rebuildReasons.isEmpty()) { if (promptDialogManager.confirmRebuild(rebuildReasons)) { - rebuildRepo(file, artifacts); + rebuildRepo(file, artifact); return; } } @@ -566,7 +578,7 @@ public class TimeLineController { * * //TODO: can we check the tags to see if we need to do this? */ - rebuildTagsTable(file, artifacts); + rebuildTagsTable(file, artifact); } /** @@ -618,8 +630,7 @@ public class TimeLineController { */ synchronized public void pushPeriod(ReadablePeriod period) { synchronized (filteredEvents) { - final DateTime middleOf = IntervalUtils.middleOf(filteredEvents.timeRangeProperty().get()); - pushTimeRange(IntervalUtils.getIntervalAround(middleOf, period)); + pushTimeRange(IntervalUtils.getIntervalAroundMiddle(filteredEvents.getTimeRange(), period)); } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/datamodel/FilteredEventsModel.java b/Core/src/org/sleuthkit/autopsy/timeline/datamodel/FilteredEventsModel.java index 74964f7b0d..11c9ab9b68 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/datamodel/FilteredEventsModel.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/datamodel/FilteredEventsModel.java @@ -62,6 +62,7 @@ import org.sleuthkit.autopsy.timeline.filters.TypeFilter; import org.sleuthkit.autopsy.timeline.zooming.DescriptionLoD; import org.sleuthkit.autopsy.timeline.zooming.EventTypeZoomLevel; import org.sleuthkit.autopsy.timeline.zooming.ZoomParams; +import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifactTag; import org.sleuthkit.datamodel.Content; @@ -429,8 +430,13 @@ public final class FilteredEventsModel { return false; } - public List getDerivedEventIDs(Set fileIDs, Set artifactIDS) { - return repo.getDerivedEventIDs(fileIDs, artifactIDS); + + public List getEventIDsForFile(AbstractFile file, boolean includedDerivedArtifacts) { + return repo.getEventIDsForFile(file,includedDerivedArtifacts); + } + + public List getEventIDsForArtifact(BlackboardArtifact artifact) { + return repo.getEventIDsForArtifact( artifact); } /** diff --git a/Core/src/org/sleuthkit/autopsy/timeline/datamodel/eventtype/MiscTypes.java b/Core/src/org/sleuthkit/autopsy/timeline/datamodel/eventtype/MiscTypes.java index ce7f009ceb..4f1e4b430a 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/datamodel/eventtype/MiscTypes.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/datamodel/eventtype/MiscTypes.java @@ -76,7 +76,7 @@ public enum MiscTypes implements EventType, ArtifactEventType { final BlackboardAttribute latitude = getAttributeSafe(artf, new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_GEO_LATITUDE)); return stringValueOf(latitude) + " " + stringValueOf(longitude); // NON-NLS }, - EMPTY_EXTRACTOR), + new EmptyExtractor()), CALL_LOG(NbBundle.getMessage(MiscTypes.class, "MiscTypes.Calls.name"), "calllog.png", // NON-NLS new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_CALLLOG), new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DATETIME_START), @@ -120,8 +120,8 @@ public enum MiscTypes implements EventType, ArtifactEventType { new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_INSTALLED_PROG), new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DATETIME), new AttributeExtractor(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_PROG_NAME)), - EMPTY_EXTRACTOR, - EMPTY_EXTRACTOR), + new EmptyExtractor(), + new EmptyExtractor()), EXIF(NbBundle.getMessage(MiscTypes.class, "MiscTypes.exif.name"), "camera-icon-16.png", // NON-NLS new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_METADATA_EXIF), new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED), diff --git a/Core/src/org/sleuthkit/autopsy/timeline/db/EventDB.java b/Core/src/org/sleuthkit/autopsy/timeline/db/EventDB.java index eee3b05110..7882e266c6 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/db/EventDB.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/db/EventDB.java @@ -71,6 +71,8 @@ import org.sleuthkit.autopsy.timeline.utils.RangeDivisionInfo; import org.sleuthkit.autopsy.timeline.zooming.DescriptionLoD; import org.sleuthkit.autopsy.timeline.zooming.EventTypeZoomLevel; import org.sleuthkit.autopsy.timeline.zooming.ZoomParams; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.Tag; import org.sleuthkit.datamodel.TskData; @@ -670,7 +672,7 @@ public class EventDB { List getDerivedEventIDs(Set fileIDs, Set artifactIDS) { DBLock.lock(); String query = "SELECT Group_Concat(event_id) FROM events" - + " WHERE file_id IN (" + StringUtils.join(fileIDs, ", ") + ")" + + " WHERE ( file_id IN (" + StringUtils.join(fileIDs, ", ") + ") AND artifact_id IS NULL)" + " OR artifact_id IN (" + StringUtils.join(artifactIDS, ", ") + ")"; try (Statement stmt = con.createStatement(); @@ -686,6 +688,47 @@ public class EventDB { return null; } + List getEventIDsForArtifact(BlackboardArtifact artifact) { + DBLock.lock(); + String query = "SELECT event_id FROM events WHERE artifact_id == " + artifact.getArtifactID() ; + + ArrayList results = new ArrayList<>(); + + try (Statement stmt = con.createStatement(); + ResultSet rs = stmt.executeQuery(query);) { // NON-NLS + while (rs.next()) { + results.add(rs.getLong("event_id")); + } + + } catch (SQLException ex) { + LOGGER.log(Level.SEVERE, "Error executing getEventIDsForArtifact query.", ex); // NON-NLS + } finally { + DBLock.unlock(); + } + return results; + } + + List getEventIDsForFile(AbstractFile file, boolean includeDerivedArtifacts) { + DBLock.lock(); + String query = "SELECT event_id FROM events WHERE file_id == " + file.getId() + + (includeDerivedArtifacts ? "" : " AND artifact_id IS NULL"); + + ArrayList results = new ArrayList<>(); + + try (Statement stmt = con.createStatement(); + ResultSet rs = stmt.executeQuery(query);) { // NON-NLS + while (rs.next()) { + results.add(rs.getLong("event_id")); + } + + } catch (SQLException ex) { + LOGGER.log(Level.SEVERE, "Error executing getEventIDsForFile query.", ex); // NON-NLS + } finally { + DBLock.unlock(); + } + return results; + } + /** * create the tags table if it doesn't already exist. This is broken out as * a separate method so it can be used by {@link #reInitializeTags() } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java b/Core/src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java index 27339c6448..a3af11eb2a 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java @@ -207,8 +207,14 @@ public class EventsRepository { return eventDB.countAllEvents(); } - public List getDerivedEventIDs(Set fileIDs, Set artifactIDS) { - return eventDB.getDerivedEventIDs(fileIDs, artifactIDS); + + + public List getEventIDsForFile(AbstractFile file, boolean includedDerivedArtifacts) { + return eventDB.getEventIDsForFile(file, includedDerivedArtifacts); + } + + public List getEventIDsForArtifact(BlackboardArtifact artifact) { + return eventDB.getEventIDsForArtifact(artifact); } private void invalidateCaches() { @@ -601,10 +607,10 @@ public class EventsRepository { timeMap.put(FileSystemTypes.FILE_MODIFIED, f.getMtime()); /* - * if there are no legitimate ( greater than zero ) time stamps ( eg, - * logical/local files) skip the rest of the event generation: this - * should result in droping logical files, since they do not have - * legitimate time stamps. + * if there are no legitimate ( greater than zero ) time stamps ( + * eg, logical/local files) skip the rest of the event generation: + * this should result in droping logical files, since they do not + * have legitimate time stamps. */ if (Collections.max(timeMap.values()) > 0) { final String uniquePath = f.getUniquePath(); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/ViewFrame.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/ViewFrame.java index 679f28e7b0..4b4fe6093e 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/ViewFrame.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/ViewFrame.java @@ -394,13 +394,8 @@ final public class ViewFrame extends BorderPane { zoomMenuButton.getItems().clear(); for (ZoomRanges zoomRange : ZoomRanges.values()) { zoomMenuButton.getItems().add(ActionUtils.createMenuItem( - new Action(zoomRange.getDisplayName(), event -> { - if (zoomRange != ZoomRanges.ALL) { - controller.pushPeriod(zoomRange.getPeriod()); - } else { - controller.showFullRange(); - } - }))); + new Action(zoomRange.getDisplayName(), event -> controller.pushPeriod(zoomRange.getPeriod())) + )); } zoomMenuButton.setText(Bundle.ViewFrame_zoomMenuButton_text()); ActionUtils.configureButton(new ZoomOut(controller), zoomOutButton); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/ZoomRanges.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/ZoomRanges.java index fe96e83a29..83c337197f 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/ZoomRanges.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/ZoomRanges.java @@ -30,7 +30,7 @@ public enum ZoomRanges { THREE_YEARS(NbBundle.getMessage(ZoomRanges.class, "Timeline.ui.ZoomRanges.threeyears.text"), Years.THREE), FIVE_YEARS(NbBundle.getMessage(ZoomRanges.class, "Timeline.ui.ZoomRanges.fiveyears.text"), Years.years(5)), TEN_YEARS(NbBundle.getMessage(ZoomRanges.class, "Timeline.ui.ZoomRanges.tenyears.text"), Years.years(10)), - ALL(NbBundle.getMessage(ZoomRanges.class, "Timeline.ui.ZoomRanges.all.text"), Minutes.ONE); + ALL(NbBundle.getMessage(ZoomRanges.class, "Timeline.ui.ZoomRanges.all.text"), Years.years(1000000)); private ZoomRanges(String displayName, ReadablePeriod period) { this.displayName = displayName; diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListTimeline.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListTimeline.java index f8a0c4cd72..28f2e2f517 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListTimeline.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListTimeline.java @@ -659,7 +659,7 @@ class ListTimeline extends BorderPane { } } - private class ChronoFieldListCell extends ListCell { + static private class ChronoFieldListCell extends ListCell { @Override protected void updateItem(ChronoField item, boolean empty) { diff --git a/Core/src/org/sleuthkit/autopsy/timeline/utils/IntervalUtils.java b/Core/src/org/sleuthkit/autopsy/timeline/utils/IntervalUtils.java index 7cc8205c65..256c8deac0 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/utils/IntervalUtils.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/utils/IntervalUtils.java @@ -18,6 +18,8 @@ */ package org.sleuthkit.autopsy.timeline.utils; +import java.time.Duration; +import java.time.Instant; import java.util.Collection; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; @@ -68,6 +70,13 @@ public class IntervalUtils { return newInterval; } + static public Interval getIntervalAround(Instant aroundInstant, Duration period) { + long start = aroundInstant.minus(period).toEpochMilli(); + long end = aroundInstant.plus(period).toEpochMilli(); + final Interval newInterval = new Interval(start, Math.max(start + 1, end)); + return newInterval; + } + /** * Get an interval the length of the given period, centered around the * center of the given interval. From 64048b014fc3393f96d05bd54a2de15451b837ce Mon Sep 17 00:00:00 2001 From: jmillman Date: Thu, 9 Jun 2016 16:06:21 -0400 Subject: [PATCH 10/48] view in timeline selection appears to be working --- .../autopsy/timeline/TimeLineController.java | 39 ++++--------------- .../timeline/ui/AbstractTimeLineView.java | 2 +- .../timeline/ui/listvew/ListTimeline.java | 34 +++++++--------- .../timeline/ui/listvew/ListViewPane.java | 7 ++-- 4 files changed, 27 insertions(+), 55 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java index fcc95938e3..cf6d1ac690 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java @@ -438,8 +438,8 @@ public class TimeLineController { setEventsDBStale(false); filteredEvents.postDBUpdated(); } - - if (file == null && artifact==null) { + + if (file == null && artifact == null) { SwingUtilities.invokeLater(this::showWindow); TimeLineController.this.showFullRange(); } else { @@ -481,7 +481,7 @@ public class TimeLineController { * timeline when done. */ @ThreadConfined(type = ThreadConfined.ThreadType.JFX) - void rebuildTagsTable(AbstractFile file,BlackboardArtifact artifact) { + void rebuildTagsTable(AbstractFile file, BlackboardArtifact artifact) { rebuildRepoHelper(eventsRepository::rebuildTags, false, file, artifact); } @@ -503,7 +503,8 @@ public class TimeLineController { pushTimeRange(interval); } if (eventIDs != null) { - selectEventIDs(eventIDs, () -> setViewMode(ViewMode.LIST)); + setViewMode(ViewMode.LIST); + selectEventIDs(eventIDs); } } } @@ -651,34 +652,8 @@ public class TimeLineController { } public void selectEventIDs(Collection events) { - selectEventIDs(events, () -> { - } - ); - } - - public void selectEventIDs(Collection events, Runnable andThen) { - final LoggedTask selectEventIDsTask = new LoggedTask("Select Event IDs", true) { //NON-NLS - @Override - protected Interval call() throws Exception { - return filteredEvents.getSpanningInterval(events); - } - - @Override - protected void succeeded() { - super.succeeded(); - try { - synchronized (TimeLineController.this) { - selectedTimeRange.set(get()); - selectedEventIDs.setAll(events); - andThen.run(); - } - } catch (InterruptedException | ExecutionException ex) { - LOGGER.log(Level.SEVERE, getTitle() + " Unexpected error", ex); //NON-NLS - } - } - }; - - monitorTask(selectEventIDsTask); + selectedTimeRange.set(filteredEvents.getSpanningInterval(events)); + selectedEventIDs.setAll(events); } /** diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractTimeLineView.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractTimeLineView.java index 7f6a2efe14..6f4920f2bc 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractTimeLineView.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractTimeLineView.java @@ -74,7 +74,7 @@ public abstract class AbstractTimeLineView extends BorderPane { */ private Task updateTask; - private final TimeLineController controller; + public final TimeLineController controller; private final FilteredEventsModel filteredEvents; /** diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListTimeline.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListTimeline.java index 28f2e2f517..0c5351c684 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListTimeline.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListTimeline.java @@ -251,20 +251,8 @@ class ListTimeline extends BorderPane { .map(CombinedEvent::getRepresentativeEventID) .collect(Collectors.toSet())); }); - - Platform.runLater(new Runnable() { - - @Override - public void run() { - Set collect = table.getItems().stream() - .filter(combinedEvent -> combinedEvent.getEventIDs().stream().anyMatch(controller.getSelectedEventIDs()::contains)) - .collect(Collectors.toSet()); - selectEvents(collect); - } - }); - - + selectEvents(controller.getSelectedEventIDs()); } /** @@ -298,17 +286,25 @@ class ListTimeline extends BorderPane { /** * Set the combined events that are selected in this view. * - * @param selectedEvents The events that should be selected. + * @param selectedEventIDs The events that should be selected. */ - void selectEvents(Collection selectedEvents) { + void selectEvents(Collection selectedEventIDs) { table.getSelectionModel().clearSelection(); - if (selectedEvents.isEmpty() == false) { - CombinedEvent firstSelected = selectedEvents.stream().filter(Objects::nonNull).min(Comparator.comparing(CombinedEvent::getStartMillis)).orElse(null); - table.scrollTo(firstSelected); - Set selectedIndices = selectedEvents.stream() + if (selectedEventIDs.isEmpty() == false) { + List selectedCombinedEvents = table.getItems().stream() + .filter(combinedEvent -> combinedEvent.getEventIDs().stream().anyMatch(selectedEventIDs::contains)) + .sorted(Comparator.comparing(CombinedEvent::getStartMillis)) + .collect(Collectors.toList()); + + if (selectedCombinedEvents.size() > 0) { + CombinedEvent firstSelected = selectedCombinedEvents.get(0); + table.scrollTo(firstSelected); + } + Set selectedIndices = selectedCombinedEvents.stream() .map(table.getItems()::indexOf) .filter(index -> index >= 0) .collect(Collectors.toSet()); + Integer[] indices = selectedIndices.toArray(new Integer[selectedIndices.size()]); if (indices.length >= 1) { table.getSelectionModel().selectIndices(indices[0], ArrayUtils.toPrimitive(indices)); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewPane.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewPane.java index b7688f0d82..882fe00770 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewPane.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewPane.java @@ -19,9 +19,9 @@ package org.sleuthkit.autopsy.timeline.ui.listvew; import com.google.common.collect.ImmutableList; -import java.util.HashSet; import java.util.List; import javafx.application.Platform; +import javafx.collections.ObservableList; import javafx.concurrent.Task; import javafx.scene.Node; import org.joda.time.Interval; @@ -46,6 +46,7 @@ public class ListViewPane extends AbstractTimeLineView { */ public ListViewPane(TimeLineController controller) { super(controller); + listTimeline = new ListTimeline(controller); //initialize chart; @@ -104,7 +105,7 @@ public class ListViewPane extends AbstractTimeLineView { FilteredEventsModel eventsModel = getEventsModel(); //grab the currently selected event - HashSet selectedEvents = new HashSet<>(listTimeline.getSelectedEvents()); + ObservableList selectedEventIDs = getController().getSelectedEventIDs(); //clear the chart and set the time range. resetView(eventsModel.getTimeRange()); @@ -118,7 +119,7 @@ public class ListViewPane extends AbstractTimeLineView { //put the combined events into the table. listTimeline.setCombinedEvents(combinedEvents); //restore the selected events - listTimeline.selectEvents(selectedEvents); + listTimeline.selectEvents(selectedEventIDs); }); return combinedEvents.isEmpty() == false; From 52effbca62707dd71cb8400d1fee069ff3ee6484 Mon Sep 17 00:00:00 2001 From: jmillman Date: Thu, 9 Jun 2016 16:32:03 -0400 Subject: [PATCH 11/48] restore mysteriously mising photorec files --- Core/release/photorec_exec/AUTHORS.txt | 4 + Core/release/photorec_exec/COPYING.txt | 339 ++++++++++++++++++ Core/release/photorec_exec/THANKS.txt | 6 + Core/release/photorec_exec/documentation.html | 12 + .../plugins/BartPE/Get_Files.cmd | 143 ++++++++ .../photorec_exec/plugins/BartPE/Help.htm | 69 ++++ .../photorec_exec/plugins/BartPE/RESET.cmd | 6 + .../photorec_exec/plugins/BartPE/ReadMe.txt | 19 + .../plugins/BartPE/SCRIPTS/Start_INF.dat | 20 ++ .../plugins/BartPE/SCRIPTS/StaticINF.dat | 36 ++ .../photorec_exec/plugins/BartPE/start.inf | 20 ++ .../plugins/BartPE/testdisk_nu2menu.xml | 7 + .../photorec_exec/plugins/WinBuilder/Help.htm | 66 ++++ .../plugins/WinBuilder/ReadMe.txt | 20 ++ .../plugins/WinBuilder/TestDisk.script | 52 +++ Core/release/photorec_exec/readme.txt | 23 ++ 16 files changed, 842 insertions(+) create mode 100755 Core/release/photorec_exec/AUTHORS.txt create mode 100755 Core/release/photorec_exec/COPYING.txt create mode 100755 Core/release/photorec_exec/THANKS.txt create mode 100755 Core/release/photorec_exec/documentation.html create mode 100755 Core/release/photorec_exec/plugins/BartPE/Get_Files.cmd create mode 100755 Core/release/photorec_exec/plugins/BartPE/Help.htm create mode 100755 Core/release/photorec_exec/plugins/BartPE/RESET.cmd create mode 100755 Core/release/photorec_exec/plugins/BartPE/ReadMe.txt create mode 100755 Core/release/photorec_exec/plugins/BartPE/SCRIPTS/Start_INF.dat create mode 100755 Core/release/photorec_exec/plugins/BartPE/SCRIPTS/StaticINF.dat create mode 100755 Core/release/photorec_exec/plugins/BartPE/start.inf create mode 100755 Core/release/photorec_exec/plugins/BartPE/testdisk_nu2menu.xml create mode 100755 Core/release/photorec_exec/plugins/WinBuilder/Help.htm create mode 100755 Core/release/photorec_exec/plugins/WinBuilder/ReadMe.txt create mode 100755 Core/release/photorec_exec/plugins/WinBuilder/TestDisk.script create mode 100755 Core/release/photorec_exec/readme.txt diff --git a/Core/release/photorec_exec/AUTHORS.txt b/Core/release/photorec_exec/AUTHORS.txt new file mode 100755 index 0000000000..fa1fc0ee7a --- /dev/null +++ b/Core/release/photorec_exec/AUTHORS.txt @@ -0,0 +1,4 @@ + +TestDisk and PhotoRec are written and maintained by Christophe GRENIER +TestDisk logos has been created by Simone Brandt and by Dmitri Zdorov in 2001, +PhotoRec logo by Marcel Bruins in 2006. diff --git a/Core/release/photorec_exec/COPYING.txt b/Core/release/photorec_exec/COPYING.txt new file mode 100755 index 0000000000..d511905c16 --- /dev/null +++ b/Core/release/photorec_exec/COPYING.txt @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/Core/release/photorec_exec/THANKS.txt b/Core/release/photorec_exec/THANKS.txt new file mode 100755 index 0000000000..3dda34e63d --- /dev/null +++ b/Core/release/photorec_exec/THANKS.txt @@ -0,0 +1,6 @@ +TestDisk & PhotoRec are mainly written by Christophe GRENIER. +Many people further contributed to TestDisk, directly or indirectly, by +reporting problems, helping with the documentation, suggesting various +improvements, sending me gifts using my Amazon whish-list... + +Thanks to the thousands of people who have provided support for the project! diff --git a/Core/release/photorec_exec/documentation.html b/Core/release/photorec_exec/documentation.html new file mode 100755 index 0000000000..e4f0877567 --- /dev/null +++ b/Core/release/photorec_exec/documentation.html @@ -0,0 +1,12 @@ + + +TestDisk & PhotoRec documentation + + +TestDisk & PhotoRec documentation can be found online: + + + diff --git a/Core/release/photorec_exec/plugins/BartPE/Get_Files.cmd b/Core/release/photorec_exec/plugins/BartPE/Get_Files.cmd new file mode 100755 index 0000000000..2bf66f4748 --- /dev/null +++ b/Core/release/photorec_exec/plugins/BartPE/Get_Files.cmd @@ -0,0 +1,143 @@ +@echo off &MODE CON: COLS=75 LINES=20 &color 1e +:: All my thanks to hilander999 and Siegfried for their help +:: Original code from hilander999 +: edit and modified to fit this plugin by Xtreme + +SETLOCAL ENABLEEXTENSIONS +cd /d %~dp0 + +set INFname=Testdisk.inf +::SET NOW=2 +::SET TOPBOX=? +::SET MIDBOX= ????????????????????????????????????????????????????????????????????????¸ +::SET NUMROW= 0%% 25%% 50%% 75%% 100%% + +echo ===================================== > "%cd%\File_Grabber.log" +echo Testdisk and PhotoRec 6.14 Plugin >> File_Grabber.log +echo Plugin by: Xtreme (Ahmed Hossam) >> File_Grabber.log +echo Collector by: Xtreme >> File_Grabber.log +echo Plugin for: Windows Xpire Rd CD >> File_Grabber.log +echo Website: http://xtreme.boot-land.net >> File_Grabber.log +echo -mirror: http://xtremee.orgfree.com >> File_Grabber.log +echo Copyright © Windows Xpire Tech Center. All rights reserved. >> File_Grabber.log +echo ===================================== >> File_Grabber.log + +IF NOT EXIST cd ..\..\63\cygwin GOTO :error1 + +cls&CALL :BRANDH +echo Please wait till Grabber finish Testdisk and PhotoRec grabbing process... + +IF NOT EXIST "%cd%\TestDisk_PE\files" md "TestDisk_PE\files" +IF NOT EXIST "%cd%\TestDisk_PE\files\63" md "TestDisk_PE\files\63" + +echo.&echo -Grabbing file: Copy Testdisk files... &FOR %%A IN ( +..\..\cygwin1.dll +..\..\photorec_win.exe +..\..\testdisk_win.exe +..\..\fidentify_win.exe + )DO (ECHO. Grabbing file:%%~A >>"%cd%\File_Grabber.log" + Copy /Y "%%~A" TestDisk_PE\files >NUL + if errorlevel 1 (SET ERRORLEVEL=1&echo. *ERROR: File_Grabber can't find %%~A >>"%cd%\File_Grabber.log") + ) + +FOR %%B IN ( +..\..\63\cygwin + )DO (ECHO. Grabbing file:%%~B >>"%cd%\File_Grabber.log" + Copy /Y "%%~B" TestDisk_PE\files\63 >NUL + if errorlevel 1 (SET ERRORLEVEL=1&echo. *ERROR: File_Grabber can't find %%~B >>"%cd%\File_Grabber.log") + ) + +CALL :PROGRESS + +::pause +MODE CON: COLS=76 LINES=23 +IF "%ERRORLEVEL%"=="1" (GOTO :error2)else goto :done +GOTO :END + +:PROGRESS +::SET /A NOW+=1 +::IF %NOW% LEQ 2 GOTO :EOF +cls&CALL :BRANDH +::echo.&echo. File: %~1&echo.&echo.%TOPBOX%&echo.%MIDBOX%&echo.%NUMROW% +::SET TOPBOX=%TOPBOX%U +::SET NOW=1 +copy SCRIPTS\StaticINF.dat TestDisk_PE\"%INFname%" +copy testdisk_nu2menu.xml TestDisk_PE\ +if exist start.inf del start.inf +GOTO :EOF + +:error1 +MODE CON: COLS=78 LINES=28 &COLOR 4F &cls + +echo.&echo. >> File_Grabber.log +echo TestDisk and PhotoRes can't be localized on your system >> File_Grabber.log +echo. >> File_Grabber.log +echo You can download TestDisk and PhotoRes from here >> File_Grabber.log +echo.&echo -TestDisk Official website: >> File_Grabber.log +echo http://www.cgsecurity.org/wiki/TestDisk_Download/ >> File_Grabber.log +echo. >> File_Grabber.log +echo For help you can check Help.html >> File_Grabber.log + +CALL :BRANDH +echo.&echo. TestDisk and PhotoRes can't be localized on your system +echo.&echo. You need to download TestDisk and DON'T change the download folders structure then try again +echo.&echo. You can download TestDisk and PhotoRes from here +echo.&echo. -TestDisk Official website: +echo http://www.cgsecurity.org/wiki/TestDisk_Download/&echo. +CALL :BRAND2 +GOTO :END + + +:error2 +cls&COLOR 4F +CALL :BRANDH +echo.&echo One or more required files was not found. +echo.&echo. Check the log for details...&echo. "%cd%\File_Grabber.log"&echo. +CALL :BRAND2 +pause +IF EXIST %systemroot%\system32\notepad.exe (start %systemroot%\system32\notepad.exe "%cd%\File_Grabber.log") +endlocal +exit + +:done +cls&CALL :BRANDH +echo.&echo. TestDisk and PhotoRes Files have been succesfully collected +echo.&echo. You can now start build your PE version &echo. +CALL :BRAND2 +GOTO :END + +:BRANDH +MODE CON: COLS=75 LINES=35 &color 1e +echo. +ECHO ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ» +ECHO º º +ECHO º TestDisk and PhotoRes plugin Files Collector º +ECHO º º +ECHO º Plugin by Xtreme ( Xtremesony_xp@yahoo.com ) º +ECHO º º +ECHO ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ +echo. +GOTO :EOF + +:BRAND2 +echo. - For more help and update you Can join us in our group +echo. http://groups.yahoo.com/group/Windows-Xpire/ +echo. +echo. - For more BartPE Plugin go to our website: +echo. http://xtreme.boot-land.net +echo. (mirror) http://xtremee.orgfree.com +echo. +echo. - Send all comments about plugin to Xtreme Xtremesony_xp@yahoo.com +echo. +echo. +ECHO ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ» +ECHO º Copyright (C) Windows Xpire Tech Center º +ECHO ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ + +GOTO :EOF + + +:END +ENDLOCAL +echo. +PAUSE&EXIT diff --git a/Core/release/photorec_exec/plugins/BartPE/Help.htm b/Core/release/photorec_exec/plugins/BartPE/Help.htm new file mode 100755 index 0000000000..6af4d81bcc --- /dev/null +++ b/Core/release/photorec_exec/plugins/BartPE/Help.htm @@ -0,0 +1,69 @@ + + + + Help| TestDisk + + + +PE Builder v3 plugin +
+

TestDisk and PhotoRec
+

+
+

Overview

+
+

TestDisk is a powerful free data recovery software! It was primarily designed to help recover lost partitions and/or make non-booting disks bootable again when these symptoms are caused by faulty software, certain types of viruses or human error (such as accidentally deleting a Partition Table). Partition table recovery using TestDisk is really easy.

+

PhotoRec is file data recovery software designed to recover lost files including video, documents and archives from Hard Disks and CDRom and lost pictures (thus, its 'Photo Recovery' name) from digital camera memory. PhotoRec ignores the filesystem and goes after the underlying data, so it will still work even if your media's filesystem has been severely damaged or re-formatted.

+

System Requirements

+
+
    +
  • BartPE v3.x and XPE (recommended).
  • +
  • Your VGA Card Driver (recommended).
  • +
  • Windows XP SP2 on your host OS.
  • +
+
+

Instructions

+
+
    +
  • 1- Download TestDisk and PhotoRec from Here
  • +
  • 2- Uncompress the download.
  • +
  • 3- Run the Get_Files.cmd to collect the needed files.
  • +
  • 4- Copy this folder to Plugin directory.
  • +
  • 5- Start your build and enjoy !
  • +
+

Support

+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

 

+
+

All trademarks mentioned on this page are the property of their respective owners

+

© 2007-2008 Windows Xpire Tech Center
+

+ + diff --git a/Core/release/photorec_exec/plugins/BartPE/RESET.cmd b/Core/release/photorec_exec/plugins/BartPE/RESET.cmd new file mode 100755 index 0000000000..6a80758df2 --- /dev/null +++ b/Core/release/photorec_exec/plugins/BartPE/RESET.cmd @@ -0,0 +1,6 @@ +@echo off +rd files /s /q +del File_Grabber.log +del *.inf +copy SCRIPTS\Start_INF.dat start.inf +pause \ No newline at end of file diff --git a/Core/release/photorec_exec/plugins/BartPE/ReadMe.txt b/Core/release/photorec_exec/plugins/BartPE/ReadMe.txt new file mode 100755 index 0000000000..911ee64667 --- /dev/null +++ b/Core/release/photorec_exec/plugins/BartPE/ReadMe.txt @@ -0,0 +1,19 @@ +TestDisk and PhotoRec v6.14 Plugin +===================================== +Plugin by: Xtreme - Ahmed Hossam +Plugin for: Christophe GRENIER (www.cgsecurity.org) +Website: http://xtreme.boot-land.net + -mirror: http://xtremee.orgfree.com +===================================== + +How to use the plugin ? + * 1- Run the Get_Files.cmd to collect the needed files. + * 2- Copy TestDisk_PE folder to Plugin directory. + * 3- Start your build and enjoy ! + +How to clean ? + * Delete the BUILD folder +------------------------------------------------------------------------- +Copyright © Windows Xpire Tech Center. +--> It is published under GNU Public License 2 or later. +------------------------------------------------------------------------- diff --git a/Core/release/photorec_exec/plugins/BartPE/SCRIPTS/Start_INF.dat b/Core/release/photorec_exec/plugins/BartPE/SCRIPTS/Start_INF.dat new file mode 100755 index 0000000000..6bfc8a2dd3 --- /dev/null +++ b/Core/release/photorec_exec/plugins/BartPE/SCRIPTS/Start_INF.dat @@ -0,0 +1,20 @@ +; Testdisk.inf +; PE Builder v3 plugin INF file for Testdisk and PhotoRec 6.14 +; Plugin by Ahmed Hossam/Xtreme +; Made For Christophe GRENIER (www.cgsecurity.org) +; © Windows Xpire Tech Center +; Windows Xpire Tech Center officially represented by Xtreme +; CodeName: Radw + +[Version] +Signature= "$Windows NT$" + +[PEBuilder] +Name="(Xtreme) Disk Tools: TestDisk and PhotoRec -Press CONFIG to Enable-" +Enable=1 +Help=Help.htm +config=Get_Files.cmd + + +[SourceDisksFiles] +"please run autoHelp to configure your plugin"=a,,1 diff --git a/Core/release/photorec_exec/plugins/BartPE/SCRIPTS/StaticINF.dat b/Core/release/photorec_exec/plugins/BartPE/SCRIPTS/StaticINF.dat new file mode 100755 index 0000000000..16f6dbfd0b --- /dev/null +++ b/Core/release/photorec_exec/plugins/BartPE/SCRIPTS/StaticINF.dat @@ -0,0 +1,36 @@ +; Testdisk.inf +; PE Builder v3 plugin INF file for Testdisk and PhotoRec 6.14 +; Plugin by Ahmed Hossam/Xtreme +; Made For Christophe GRENIER (www.cgsecurity.org) +; © Windows Xpire Tech Center +; Windows Xpire Tech Center is represented by Xtreme +; CodeName: Radw + +[Version] +Signature= "$Windows NT$" + +[PEBuilder] +Name="(Xtreme) Disk Tools: TestDisk and PhotoRec -Press CONFIG to Enable-" +Enable=1 +Help=Help.htm +config=Get_Files.cmd + +[WinntDirectories] +a="Programs\testdisk",2 +b="Programs\testdisk\63",2 + +[SourceDisksFiles] +files\testdisk_win.exe=a,,1 +files\photorec_win.exe=a,,1 +files\fidentify_win.exe=a,,1 +files\cygwin1.dll=a,,1 +files\63\cygwin=b,,1 + +[Software.AddReg] +0x2, "Sherpya\XPEinit\Programs","Disk Tools\TestDisk - Fix MBR and Recover lost partitions","%systemdrive%\Programs\testdisk\testdisk_win.exe" +0x2, "Sherpya\XPEinit\Programs","Disk Tools\PhotoRec - Recover lost files (Doc, Pic and Video)","%systemdrive%\Programs\testdisk\photorec_win.exe" +0x2, "Sherpya\XPEinit\Programs","Disk Tools\files Identify","%systemdrive%\Programs\testdisk\fidentify_win.exe" + +; Remove the comment ";" from the below lines if you are going to use NU2 menu +;[Append] +;nu2menu.xml, testdisk_nu2menu.xml diff --git a/Core/release/photorec_exec/plugins/BartPE/start.inf b/Core/release/photorec_exec/plugins/BartPE/start.inf new file mode 100755 index 0000000000..6bfc8a2dd3 --- /dev/null +++ b/Core/release/photorec_exec/plugins/BartPE/start.inf @@ -0,0 +1,20 @@ +; Testdisk.inf +; PE Builder v3 plugin INF file for Testdisk and PhotoRec 6.14 +; Plugin by Ahmed Hossam/Xtreme +; Made For Christophe GRENIER (www.cgsecurity.org) +; © Windows Xpire Tech Center +; Windows Xpire Tech Center officially represented by Xtreme +; CodeName: Radw + +[Version] +Signature= "$Windows NT$" + +[PEBuilder] +Name="(Xtreme) Disk Tools: TestDisk and PhotoRec -Press CONFIG to Enable-" +Enable=1 +Help=Help.htm +config=Get_Files.cmd + + +[SourceDisksFiles] +"please run autoHelp to configure your plugin"=a,,1 diff --git a/Core/release/photorec_exec/plugins/BartPE/testdisk_nu2menu.xml b/Core/release/photorec_exec/plugins/BartPE/testdisk_nu2menu.xml new file mode 100755 index 0000000000..9e2495f367 --- /dev/null +++ b/Core/release/photorec_exec/plugins/BartPE/testdisk_nu2menu.xml @@ -0,0 +1,7 @@ + + + + Testdisk - Fix MBR and Recover lost partitions + PhotoRec - Recover lost files (Doc, Pic and Video) + + diff --git a/Core/release/photorec_exec/plugins/WinBuilder/Help.htm b/Core/release/photorec_exec/plugins/WinBuilder/Help.htm new file mode 100755 index 0000000000..40b652a326 --- /dev/null +++ b/Core/release/photorec_exec/plugins/WinBuilder/Help.htm @@ -0,0 +1,66 @@ + + + + Help| TestDisk + + + +WinBuilder v075b1 +
+

TestDisk and PhotoRec
+

+
+

Overview

+
+

TestDisk is a powerful free data recovery software! It was primarily designed to help recover lost partitions and/or make non-booting disks bootable again when these symptoms are caused by faulty software, certain types of viruses or human error (such as accidentally deleting a Partition Table). Partition table recovery using TestDisk is really easy.

+

PhotoRec is file data recovery software designed to recover lost files including video, documents and archives from Hard Disks and CDRom and lost pictures (thus, its 'Photo Recovery' name) from digital camera memory. PhotoRec ignores the filesystem and goes after the underlying data, so it will still work even if your media's filesystem has been severely damaged or re-formatted.

+

System Requirements

+
+
    +
  • WinBuilder v075 [beta1] from Here.
  • +
  • Your VGA Card Driver (recommended).
  • +
  • Windows XP SP2 on your host OS.
  • +
+
+

Instructions

+
+
    +
  • 1- Download TestDisk and PhotoRec from Here
  • +
  • 2- Uncompress the download.
  • +
  • 3- Copy TestDisk.script to the WinBuilder App project folder.
  • +
  • 4- Start your build and enjoy !
  • +
+

Support

+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

 

+
+

All trademarks mentioned on this page are the property of their respective owners

+

© 2007-2008 Windows Xpire Tech Center
+

+ + diff --git a/Core/release/photorec_exec/plugins/WinBuilder/ReadMe.txt b/Core/release/photorec_exec/plugins/WinBuilder/ReadMe.txt new file mode 100755 index 0000000000..0ac9e4b000 --- /dev/null +++ b/Core/release/photorec_exec/plugins/WinBuilder/ReadMe.txt @@ -0,0 +1,20 @@ +TestDisk and PhotoRec v6.14 Plugin +===================================== +Plugin by: Xtreme - Ahmed Hossam +Plugin for: Christophe GRENIER (www.cgsecurity.org) +Website: http://xtreme.boot-land.net + -mirror: http://xtremee.orgfree.com +===================================== + +How to use the plugin ? + * 1- Download WinBuilder v075 [beta_1] + From here: http://winbuilder.net/download.php?list.12 + * 2- Copy TestDisk.script to the WinBuilder App project folder. + * 3- Start your build and enjoy ! + +How to clean ? + * Delete the BUILD folder +------------------------------------------------------------------------- +Copyright © Windows Xpire Tech Center. +--> It is published under GNU Public License 2 or later. +------------------------------------------------------------------------- diff --git a/Core/release/photorec_exec/plugins/WinBuilder/TestDisk.script b/Core/release/photorec_exec/plugins/WinBuilder/TestDisk.script new file mode 100755 index 0000000000..ccbc52fd1d --- /dev/null +++ b/Core/release/photorec_exec/plugins/WinBuilder/TestDisk.script @@ -0,0 +1,52 @@ +[main] +Title=TestDisk & PhotoRec 6.14 +Description=A powerful free data recovery software utility. +Selected=True +Level=5 +Version=1 +Author=Xtreme +Contact=http://www.boot-land.net/forums/ +Credits=Christophe GRENIER (http://www.cgsecurity.org) +Date=Friday, May 16,2008 + +[Interface] +pTextLabel1=" Thanks a lot for Galapo for his support and helping me learning WinBuilder Script.",1,1,19,25,463,54,8,Normal +pBevel1=pBevel1,1,12,12,18,455,63 + +[variables] +%ProgramTitle%=TestDisk +%ProgramEXE%=testdisk_win.exe +%ProgramFolder%=TestDisk + +[process] +StrFormat,path,%scriptdir%,%one_folder_up% +StrFormat,CTrim,"%one_folder_up%","\",%one_folder_up% +StrFormat,path,%one_folder_up%,%two_folders_up% +StrFormat,CTrim,"%two_folders_up%","\",%two_folders_up% +If,NotExistFile,"%two_folders_up%\testdisk_win.exe",Run,%ScriptFile%,Halt +DirMake,"%Target_Prog%\%ProgramFolder%" +DirMake,"%Target_Prog%\%ProgramFolder%\63" +FileCopy,"%two_folders_up%\cygwin1.dll","%Target_Prog%\%ProgramFolder%" +FileCopy,"%two_folders_up%\fidentify_win.exe","%Target_Prog%\%ProgramFolder%" +FileCopy,"%two_folders_up%\photorec_win.exe","%Target_Prog%\%ProgramFolder%" +FileCopy,"%two_folders_up%\readme.txt","%Target_Prog%\%ProgramFolder%" +FileCopy,"%two_folders_up%\testdisk_win.exe","%Target_Prog%\%ProgramFolder%" +DirCopy,"%two_folders_up%\63\*.*","%Target_Prog%\%ProgramFolder%\63" +::DirCopy,"%two_folders_up%\TestDisk\*.*","%Target_Prog%\%ProgramFolder%" +Add_Shortcut,StartMenu,"%TestDisk & PhotoRec" +Add_Shortcut,StartMenu,"TestDisk & PhotoRec","%PE_Programs%\%ProgramFolder%\photorec_win.exe","PhotoRec" +Add_Shortcut,StartMenu,"TestDisk & PhotoRec","%PE_Programs%\%ProgramFolder%\testdisk_win.exe","Testdisk" +Add_Shortcut,StartMenu,"TestDisk & PhotoRec","%PE_Programs%\%ProgramFolder%\fidentify_win.exe","Files Identify" + +[Halt] +Message,"testdisk_win.exe could not be located in the following folder: '%two_folders_up%'. Program will not be available in the PE.",Error +Exit,"" + +[EncodedFile-AuthorEncoded-testdisklogo-clear-100.gif] +lines=0 +0=eJy1lfs/0wsDgL+b7y5mZi5jLtnmFhmfqcj9HRVa6SA0utBoNF3EEpMajUnurYhNbqtFLk3rLXLe704lFCWXI0V0wpxUqJC30857/oj3+e358fnp8d/h5+p2KBaIBYbVgFqtXllZmZ2dnZiY6O/v7+zsVIx0NvUrpFIpBEGiTrFIJBJ05gsEgvT09ARFekxMTKQiISQkhCGNZDAYblKGk5OTnciNInWjUChEsRORSMSLKGgxBRATAREe+D9D+Q7CAEAToP4j/zTRkWpIkBhANSnqrk/qOep+jfHCD5BFtozeSnn68TjCFP/VTgqHX8zh9bEtnx1sAQaaG40yNzA9j19pjD+FiIAhUoS5HHQaEzyWyjuUcXYP/wgnMSzngliSx8wsBtIvaZJD9LQQV/fnhxdWnY/MlMjDEbja+h1kQznGiGT+8NHjrifdD0km2Jaq51q3KrXbS7eRDcJQZr+Zk9aBMN2JnumZbpIOTwt3agCYqtvPTRk+cx0fRhjvIRGax7olpgHzGjmeMBWVmbqjDwC0bQPutFYzHvLyU66WVheRlFlqOV41WVLtIzDS0fYl35buBOscX9lbZAN8mNL7z6Gb9MFN6i0LRw0ZaevrFCEPNkgoa20393D8PlMkDEVD2MYbcs54r6Lx95Zo5UGvpbi5wa1Yo6oMg1PzztI78w++h/Dn6u7vU91P+BAe8Oqe9tGIgQ7DgQ77QfayM2KlNDr1ufk+XbyxluPjXfVmuMToLOQJ6wHNP4J8YGawQYxlVpq1QVSFIX43ujN5FoVDjtRIS01kAXdL6rOivUwPPLdHJo14n/Y4LYbr2RdaHF11zkXGLc/9SJyqISBJ8a+9VYMLgWUO9nBHkx+R3oDOo03ngAUb5HdVe2WG32MYD6O3mY7gBCn8r3BPWsmiDYdu5owkHL+smBQY2MgGWES2rCaKEaZSTwn37ncdiUpxi7dg67osG8AXbQPo58XVrrV5F6iTEiPYe+ZbI2tD195gu4rbE+tuhTrRi5zHtqGDNx7czjJlWLBryRFYyO9SuNfNrhjNwAjJ4fm/bIfzfXtBTGmC7F2v2b+2RyDg9+muw+GmsUFsDZzniKmFx8Q7zYR/PzpCNjZ1nWQDSLMFOpjv0qvSQBGD3lcJJ79hKjj3NAEx87frsQA2Hj+R1HVGX1nQmmp8/Zp3Xqp2Aha9rWEHaRvUwCmVjm0oPrujWAh7k8pXgvJ+92Ja3yNud8G9NzU3rdO1OQykS8H7EhoKqOXLVo0/KE82zZWXgLh5W3pR+bdSNQo+c4bmY/g4LX1XE0ZuMF4pqf9ATxl3TM5EvYqsgWRLvK3mgKiZQgfw1TeqDqwt7TM9vSWpCwKtSnghablY+Pg5Awhktpt6culd+slBVsBk4C1cbNVXVNLO80w7SzRBm9zQtY7O4cMJ1JWCZM065zxQzyeIbq6Bz5lOrAvlK4dC9x77NsyMyziFYBiOa8IPfna3CTyUbW1V1dAwZhdmdX16+XXiE4eVN4lP3VgeR2bHczRiTqhW3vJRjquTgiLXL1O565tX3+l9kxwWkv121zQWXXOBpsvjmr/PiE9/+j57Y/FPqwKex71lcb2HFAPTQLe2Fjbr0Okw2kyC+vT0C9+2XqHs0AaXnEibV7NCkJzmHqJ8qpCbt1kuuIx2nAjuLmPBr61dttdXiaRNwbwzdj0ti/NfcX4kFuPtL3q/2u4d+nXEAnswLT2sp+1E7Jy2Z+PetQzwXPmmVS2sYT/BpnegiFvxkvBz82HpWwe/ncsa/M8GwXyMezYBc5X0Oh6U/dfrj4jKBiwLDOtEvQz45ceU76i3k+9Pio+9cTVdA/Lj73q2mxCddxWjjjXWM6FhZMXYbJPylstNiTu9uFN2Z3Rr+xMtPYPyWakBD/kgiwNz5o7rEnKysYyLRsbAEGBBLz1+oUuDgCY+WeqC4HmFNkMb9W4/Wy1Z2K+G3Gh7hmPQKrr8UclhW/vRB54LwKajt/W3mOyqmEFlJb0SFi61B0I5Q8iVvlZvy451/Q0QH5dKLs6iCjuIJbS54iLtmUrs2nurbJ/c8ySdaCiD7NZ4mBtoHV5uvpI05xXOWg8shuS4u7bpRO6eh7OJwMcJtv6s9mfRwIaaqIlNrWlhAA4JlTt9cM2cOqSEGcACFlmxklJlTNyXl88JSPydIEOriKR4ZndSbM92HAY0g9HB9+cdqGaApOarv9/+srrRMJr/Uj1gXn2hJP2TFjDm5k8YRgMIcGex6VbmE/xMqxnB4cB/8m2Tr/gEXzyuvw71ZYzxsy2ImBnK+vr6Pt8BvgC8EIca200LAn88DOHB/SvIqUUBMWtUOFZRiGq8wYdDg/8bRVFGqNFTzhwMj7xUyVvY1o9fGdpcjebAgwqPQTXZGY5CZ8ZfIAy3HmbtAPqSu6Gt2OhbXIGqwSbNWDdGGVzmChUvi65HyWGkEwV50XfTyuQVS3g19Zj7AJfg3Uij2UUuy8v7qOyO+8wbtkfEOmu1Uaxk5Mmry3dkgU2cx9JKoNlBlnBEaM/eqxwbW76rCGyc25zb7oFopTkKfFfPvhZN6mFwjYHNSWWVb3oeLPInRGJu3OIb6uWP7aN9zUzMk/FPvN8dfADA42/UG1J5eJyTKkktLknJLM7OyU/P103OSU0s0jU0MNBLz0xjGAXDH1xnh9Cr2LHLx92yk2ZgAgBOAAyvxhMHbwEAAAACAAAANgAAAKoHAAAAAAAAAQAAAAAAAAAAAAAA + +[AuthorEncoded] +testdisklogo-clear-100.gif=2Kb,2Kb +Logo=testdisklogo-clear-100.gif + diff --git a/Core/release/photorec_exec/readme.txt b/Core/release/photorec_exec/readme.txt new file mode 100755 index 0000000000..57cd3b6f3a --- /dev/null +++ b/Core/release/photorec_exec/readme.txt @@ -0,0 +1,23 @@ +The Windows version of TestDisk & PhotoRec should work under +- Windows NT 4 +- Windows 2000 +- Windows XP +- Windows 2003 +- Windows Vista +- Windows Server 2008 +- Windows 7 +On Windows 64-bit, WoW64 (Windows 32-bit On Windows 64-bit) is required to run +these 32-bit executables. +For Windows 64-bit without WoW64, use the Windows 64-bit version of TestDisk +& PhotoRec. + +If you are using an older version of Windows, run the DOS version of TestDisk. +You can download it from http://www.cgsecurity.org/wiki/TestDisk_Download + +TestDisk doesn't need to be installed, you only need to +- extract the files +- run testdisk_win.exe or photorec_win.exe + +TestDisk & PhotoRec documentation can be found online: +- http://www.cgsecurity.org/wiki/TestDisk +- http://www.cgsecurity.org/wiki/PhotoRec From 9eb12d3f064845c6db9db05ffb532be04ede12ec Mon Sep 17 00:00:00 2001 From: jmillman Date: Thu, 9 Jun 2016 16:50:15 -0400 Subject: [PATCH 12/48] cleanup BlackboardArtifactNode.java --- .../datamodel/BlackboardArtifactNode.java | 77 +++++++++++-------- 1 file changed, 44 insertions(+), 33 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java index a437da3d10..0abeca56b4 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2014 Basis Technology Corp. + * Copyright 2011-2016 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -25,6 +25,7 @@ import java.util.List; import java.util.Map; import java.util.logging.Level; import javax.swing.Action; +import org.apache.commons.lang3.StringUtils; import org.openide.nodes.Children; import org.openide.nodes.Sheet; import org.openide.util.Lookup; @@ -32,6 +33,8 @@ import org.openide.util.NbBundle; import org.openide.util.lookup.Lookups; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; +import static org.sleuthkit.autopsy.datamodel.Bundle.*; import org.sleuthkit.autopsy.timeline.datamodel.eventtype.ArtifactEventType; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; @@ -40,7 +43,6 @@ import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TskCoreException; -import org.sleuthkit.datamodel.TskException; /** * Node wrapping a blackboard artifact object. This is generated from several @@ -51,7 +53,7 @@ public class BlackboardArtifactNode extends DisplayableItemNode { private final BlackboardArtifact artifact; private final Content associated; private List> customProperties; - static final Logger logger = Logger.getLogger(BlackboardArtifactNode.class.getName()); + private static final Logger LOGGER = Logger.getLogger(BlackboardArtifactNode.class.getName()); /* * Artifact types which should have the full unique path of the associated * content as a property. @@ -103,28 +105,37 @@ public class BlackboardArtifactNode extends DisplayableItemNode { } @Override + @NbBundle.Messages({"BlackboardArtifactNode.getAction.errorTitle=Error getting action", + "BlackboardArtifactNode.getAction.errorMessage=There was a problem getting actions for the selected result."}) public Action[] getActions(boolean context) { - List actionsList = new ArrayList<>(); actionsList.addAll(Arrays.asList(super.getActions(context))); - boolean hasTimeStamp = ArtifactEventType.getAllArtifactEventTypes().stream() - .filter(artEventType -> artEventType.getArtifactType().getTypeID() == artifact.getArtifactTypeID()) - .filter(artEventType -> { - try { - return artifact.getAttribute(artEventType.getDateTimeAttrubuteType()) != null; - } catch (TskCoreException ex) { - Logger.getLogger(BlackboardArtifactNode.class.getName()).log(Level.WARNING, "Error retreiving blackboard arttributes from blackboard artifact.", ex); - return false; + //see if this artifact has a timestamp in any of the supported artifacts/attributes + boolean hasTimeStamp = false; + for (ArtifactEventType artEventType : ArtifactEventType.getAllArtifactEventTypes()) { + if (artEventType.getArtifactTypeID() == artifact.getArtifactTypeID()) { + try { + if (artifact.getAttribute(artEventType.getDateTimeAttrubuteType()) != null) { + hasTimeStamp = true; + break; } - }).findAny().isPresent(); + } catch (TskCoreException ex) { + LOGGER.log(Level.SEVERE, "Error retreiving blackboard arttributes from blackboard artifact.", ex); //NON-NLS + MessageNotifyUtil.Notify.error(BlackboardArtifactNode_getAction_errorTitle(), BlackboardArtifactNode_getAction_errorMessage()); + } + } + } if (hasTimeStamp) { + //if this artifact has a time stamp add the action to view it in the timeline actionsList.add(ViewArtifactInTimelineAction.getInstance()); } - - if (associated != null){ + + if (associated != null) { + //if this artifact has associated content, add the action to view the content in the timeline actionsList.add(ViewFileInTimelineAction.getInstance()); } + return actionsList.toArray(new Action[actionsList.size()]); } @@ -134,7 +145,7 @@ public class BlackboardArtifactNode extends DisplayableItemNode { * on artifacts. */ private void setDisplayName() { - String displayName = ""; + String displayName = ""; //NON-NLS if (associated != null) { displayName = associated.getName(); } @@ -148,7 +159,7 @@ public class BlackboardArtifactNode extends DisplayableItemNode { if (attribute.getAttributeType().getTypeID() == ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT.getTypeID()) { BlackboardArtifact associatedArtifact = Case.getCurrentCase().getSleuthkitCase().getBlackboardArtifact(attribute.getValueLong()); if (associatedArtifact != null) { - displayName = associatedArtifact.getDisplayName() + " Artifact"; // NON-NLS + displayName = associatedArtifact.getDisplayName() + " Artifact"; } } } @@ -159,6 +170,7 @@ public class BlackboardArtifactNode extends DisplayableItemNode { this.setDisplayName(displayName); } + @Override protected Sheet createSheet() { Sheet s = super.createSheet(); Sheet.Set ss = s.get(Sheet.PROPERTIES); @@ -193,14 +205,14 @@ public class BlackboardArtifactNode extends DisplayableItemNode { // If mismatch, add props for extension and file type if (artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_EXT_MISMATCH_DETECTED.getTypeID()) { - String ext = ""; - String actualMimeType = ""; + String ext = ""; //NON-NLS + String actualMimeType = ""; //NON-NLS if (associated instanceof AbstractFile) { AbstractFile af = (AbstractFile) associated; ext = af.getNameExtension(); actualMimeType = af.getMIMEType(); if (actualMimeType == null) { - actualMimeType = ""; + actualMimeType = ""; //NON-NLS } } ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "BlackboardArtifactNode.createSheet.ext.name"), @@ -215,11 +227,11 @@ public class BlackboardArtifactNode extends DisplayableItemNode { } if (Arrays.asList(SHOW_UNIQUE_PATH).contains(artifactTypeId)) { - String sourcePath = ""; + String sourcePath = ""; //NON-NLS try { sourcePath = associated.getUniquePath(); } catch (TskCoreException ex) { - logger.log(Level.WARNING, "Failed to get unique path from: {0}", associated.getName()); //NON-NLS + LOGGER.log(Level.WARNING, "Failed to get unique path from: {0}", associated.getName()); //NON-NLS } if (sourcePath.isEmpty() == false) { @@ -263,7 +275,7 @@ public class BlackboardArtifactNode extends DisplayableItemNode { dataSourceStr = getRootParentName(); } } catch (TskCoreException ex) { - logger.log(Level.WARNING, "Failed to get image name from {0}", associated.getName()); //NON-NLS + LOGGER.log(Level.WARNING, "Failed to get image name from {0}", associated.getName()); //NON-NLS } if (dataSourceStr.isEmpty() == false) { @@ -286,7 +298,7 @@ public class BlackboardArtifactNode extends DisplayableItemNode { parentName = parent.getName(); } } catch (TskCoreException ex) { - logger.log(Level.WARNING, "Failed to get parent name from {0}", associated.getName()); //NON-NLS + LOGGER.log(Level.WARNING, "Failed to get parent name from {0}", associated.getName()); //NON-NLS return ""; } return parentName; @@ -298,7 +310,7 @@ public class BlackboardArtifactNode extends DisplayableItemNode { * * @param np NodeProperty to add */ - public void addNodeProperty(NodeProperty np) { + public void addNodeProperty(NodeProperty np) { if (null == customProperties) { //lazy create the list customProperties = new ArrayList<>(); @@ -324,7 +336,6 @@ public class BlackboardArtifactNode extends DisplayableItemNode { || attributeTypeID == ATTRIBUTE_TYPE.TSK_TAGGED_ARTIFACT.getTypeID() || attributeTypeID == ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT.getTypeID() || attributeTypeID == ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()) { - continue; } else if (attribute.getAttributeType().getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME) { map.put(attribute.getAttributeType().getDisplayName(), ContentUtils.getStringTime(attribute.getValueLong(), associated)); } else if (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_TOOL_OUTPUT.getTypeID() @@ -345,8 +356,8 @@ public class BlackboardArtifactNode extends DisplayableItemNode { map.put(attribute.getAttributeType().getDisplayName(), attribute.getDisplayString()); } } - } catch (TskException ex) { - logger.log(Level.SEVERE, "Getting attributes failed", ex); //NON-NLS + } catch (TskCoreException ex) { + LOGGER.log(Level.SEVERE, "Getting attributes failed", ex); //NON-NLS } } @@ -385,8 +396,8 @@ public class BlackboardArtifactNode extends DisplayableItemNode { private static Content getAssociatedContent(BlackboardArtifact artifact) { try { return artifact.getSleuthkitCase().getContentById(artifact.getObjectID()); - } catch (TskException ex) { - logger.log(Level.WARNING, "Getting file failed", ex); //NON-NLS + } catch (TskCoreException ex) { + LOGGER.log(Level.WARNING, "Getting file failed", ex); //NON-NLS } throw new IllegalArgumentException( NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.getAssocCont.exception.msg")); @@ -416,7 +427,7 @@ public class BlackboardArtifactNode extends DisplayableItemNode { } } if (keyword != null) { - boolean isRegexp = (regexp != null && !regexp.equals("")); + boolean isRegexp = StringUtils.isNotBlank(regexp); String origQuery; if (isRegexp) { origQuery = regexp; @@ -425,8 +436,8 @@ public class BlackboardArtifactNode extends DisplayableItemNode { } return highlightFactory.createInstance(objectId, keyword, isRegexp, origQuery); } - } catch (TskException ex) { - logger.log(Level.WARNING, "Failed to retrieve Blackboard Attributes", ex); //NON-NLS + } catch (TskCoreException ex) { + LOGGER.log(Level.WARNING, "Failed to retrieve Blackboard Attributes", ex); //NON-NLS } return null; } From 74a4ee9fce648e626074da021c482a9738c5e2a3 Mon Sep 17 00:00:00 2001 From: jmillman Date: Fri, 10 Jun 2016 10:49:21 -0400 Subject: [PATCH 13/48] move ShowArtifactInTimelineAction and ShowFileInTimelineAction to timeline.actions package; remove partial/poor multiselect support from them --- .../datamodel/BlackboardArtifactNode.java | 17 ++++-- .../autopsy/datamodel/Bundle.properties | 4 -- .../autopsy/datamodel/Bundle_ja.properties | 2 +- .../sleuthkit/autopsy/datamodel/FileNode.java | 38 ++++++------ .../ViewArtifactInTimelineAction.java | 60 ------------------ .../datamodel/ViewFileInTimelineAction.java | 61 ------------------- .../actions/ShowArtifactInTimelineAction.java | 46 ++++++++++++++ .../actions/ShowFileInTimelineAction.java | 47 ++++++++++++++ 8 files changed, 123 insertions(+), 152 deletions(-) delete mode 100644 Core/src/org/sleuthkit/autopsy/datamodel/ViewArtifactInTimelineAction.java delete mode 100644 Core/src/org/sleuthkit/autopsy/datamodel/ViewFileInTimelineAction.java create mode 100644 Core/src/org/sleuthkit/autopsy/timeline/actions/ShowArtifactInTimelineAction.java create mode 100644 Core/src/org/sleuthkit/autopsy/timeline/actions/ShowFileInTimelineAction.java diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java index 0abeca56b4..eb3bc57f46 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java @@ -35,6 +35,8 @@ import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import static org.sleuthkit.autopsy.datamodel.Bundle.*; +import org.sleuthkit.autopsy.timeline.actions.ShowArtifactInTimelineAction; +import org.sleuthkit.autopsy.timeline.actions.ShowFileInTimelineAction; import org.sleuthkit.autopsy.timeline.datamodel.eventtype.ArtifactEventType; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; @@ -105,18 +107,19 @@ public class BlackboardArtifactNode extends DisplayableItemNode { } @Override - @NbBundle.Messages({"BlackboardArtifactNode.getAction.errorTitle=Error getting action", + @NbBundle.Messages({"BlackboardArtifactNode.getAction.errorTitle=Error getting actions", "BlackboardArtifactNode.getAction.errorMessage=There was a problem getting actions for the selected result."}) public Action[] getActions(boolean context) { List actionsList = new ArrayList<>(); actionsList.addAll(Arrays.asList(super.getActions(context))); - //see if this artifact has a timestamp in any of the supported artifacts/attributes + //see if this artifact has a timestamp in any of the supported attributes boolean hasTimeStamp = false; + for (ArtifactEventType artEventType : ArtifactEventType.getAllArtifactEventTypes()) { if (artEventType.getArtifactTypeID() == artifact.getArtifactTypeID()) { try { - if (artifact.getAttribute(artEventType.getDateTimeAttrubuteType()) != null) { + if (null != artifact.getAttribute(artEventType.getDateTimeAttrubuteType())) { hasTimeStamp = true; break; } @@ -128,12 +131,14 @@ public class BlackboardArtifactNode extends DisplayableItemNode { } if (hasTimeStamp) { //if this artifact has a time stamp add the action to view it in the timeline - actionsList.add(ViewArtifactInTimelineAction.getInstance()); + actionsList.add(new ShowArtifactInTimelineAction(artifact)); } - if (associated != null) { + AbstractFile file = getLookup().lookup(AbstractFile.class); + + if (null != file) { //if this artifact has associated content, add the action to view the content in the timeline - actionsList.add(ViewFileInTimelineAction.getInstance()); + actionsList.add(new ShowFileInTimelineAction(file)); } return actionsList.toArray(new Action[actionsList.size()]); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties index 8f1a007aab..f933ce4738 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties @@ -109,10 +109,6 @@ ExtractedContentNode.createSheet.name.name=Name ExtractedContentNode.createSheet.name.displayName=Name ExtractedContentNode.createSheet.name.desc=no description LocalFileNode.viewFileInDir.text=View File in Directory -FileNode.viewFileInDir.text=View File in Directory -FileNode.getActions.viewInNewWin.text=View in New Window -FileNode.getActions.openInExtViewer.text=Open in External Viewer -FileNode.getActions.searchFilesSameMD5.text=Search for files with the same MD5 hash FileSize.fileSizeRootNode.name=File Size FileSize.createSheet.name.name=Name FileSize.createSheet.name.displayName=Name diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle_ja.properties index 6b27e2b4eb..036ff29159 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle_ja.properties @@ -88,7 +88,7 @@ ExtractedContentNode.name.text=\u62bd\u51fa\u3055\u308c\u305f\u30b3\u30f3\u30c6\ ExtractedContentNode.createSheet.name.name=\u540d\u524d ExtractedContentNode.createSheet.name.displayName=\u540d\u524d ExtractedContentNode.createSheet.name.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093 -FileNode.viewFileInDir.text=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u5185\u306e\u30d5\u30a1\u30a4\u30eb\u3092\u8868\u793a +FileNode.getActions.viewFileInDir.text=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u5185\u306e\u30d5\u30a1\u30a4\u30eb\u3092\u8868\u793a FileNode.getActions.viewInNewWin.text=\u65b0\u898f\u30a6\u30a3\u30f3\u30c9\u30a6\u306b\u8868\u793a FileNode.getActions.openInExtViewer.text=\u5916\u90e8\u30d3\u30e5\u30fc\u30a2\u3067\u958b\u304f FileNode.getActions.searchFilesSameMD5.text=\u540c\u3058MD5\u30cf\u30c3\u30b7\u30e5\u3092\u6301\u3064\u30d5\u30a1\u30a4\u30eb\u3092\u691c\u7d22 diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileNode.java index 1ef166bc6f..1eee85ec0c 100755 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileNode.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011 - 2013 Basis Technology Corp. + * Copyright 2011-2016 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -31,17 +31,20 @@ import org.sleuthkit.autopsy.directorytree.ExtractAction; import org.sleuthkit.autopsy.directorytree.HashSearchAction; import org.sleuthkit.autopsy.directorytree.NewWindowViewAction; import org.sleuthkit.autopsy.directorytree.ViewContextAction; +import org.sleuthkit.autopsy.timeline.actions.ShowFileInTimelineAction; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.TskData.TSK_DB_FILES_TYPE_ENUM; import org.sleuthkit.datamodel.TskData.TSK_FS_NAME_FLAG_ENUM; /** - * This class is used to represent the "Node" for the file. It may have derived - * files children. + * This class is the Node for a AbstractFile. It may have derived files + * children. */ public class FileNode extends AbstractFsContentNode { /** + * Constructor + * * @param file underlying Content */ public FileNode(AbstractFile file) { @@ -69,30 +72,25 @@ public class FileNode extends AbstractFsContentNode { } } - /** - * Right click action for this node - * - * @param popup - * - * @return - */ @Override + @NbBundle.Messages({ + "FileNode.getActions.viewFileInDir.text=View File in Directory", + "FileNode.getActions.viewInNewWin.text=View in New Window", + "FileNode.getActions.openInExtViewer.text=Open in External Viewer", + "FileNode.getActions.searchFilesSameMD5.text=Search for files with the same MD5 hash"}) public Action[] getActions(boolean popup) { List actionsList = new ArrayList<>(); if (!this.getDirectoryBrowseMode()) { - actionsList.add(new ViewContextAction(NbBundle.getMessage(this.getClass(), "FileNode.viewFileInDir.text"), this)); + actionsList.add(new ViewContextAction(Bundle.FileNode_getActions_viewFileInDir_text(), this)); actionsList.add(null); // creates a menu separator } - actionsList.add(new NewWindowViewAction( - NbBundle.getMessage(this.getClass(), "FileNode.getActions.viewInNewWin.text"), this)); - actionsList.add(new ExternalViewerAction( - NbBundle.getMessage(this.getClass(), "FileNode.getActions.openInExtViewer.text"), this)); - actionsList.add(ViewFileInTimelineAction.getInstance()); + actionsList.add(new NewWindowViewAction(Bundle.FileNode_getActions_viewInNewWin_text(), this)); + actionsList.add(new ExternalViewerAction(Bundle.FileNode_getActions_openInExtViewer_text(), this)); + actionsList.add(new ShowFileInTimelineAction(getContent())); actionsList.add(null); // creates a menu separator actionsList.add(ExtractAction.getInstance()); - actionsList.add(new HashSearchAction( - NbBundle.getMessage(this.getClass(), "FileNode.getActions.searchFilesSameMD5.text"), this)); + actionsList.add(new HashSearchAction(Bundle.FileNode_getActions_searchFilesSameMD5_text(), this)); actionsList.add(null); // creates a menu separator actionsList.add(AddContentTagAction.getInstance()); actionsList.addAll(ContextMenuExtensionPoint.getActions()); @@ -100,12 +98,12 @@ public class FileNode extends AbstractFsContentNode { } @Override - public T accept(ContentNodeVisitor< T> v) { + public T accept(ContentNodeVisitor v) { return v.visit(this); } @Override - public T accept(DisplayableItemNodeVisitor< T> v) { + public T accept(DisplayableItemNodeVisitor v) { return v.visit(this); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ViewArtifactInTimelineAction.java b/Core/src/org/sleuthkit/autopsy/datamodel/ViewArtifactInTimelineAction.java deleted file mode 100644 index 7a498f8e56..0000000000 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ViewArtifactInTimelineAction.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.sleuthkit.autopsy.datamodel; - -import com.google.common.collect.Iterables; -import java.awt.event.ActionEvent; -import java.util.Set; -import java.util.stream.Collectors; -import javax.swing.AbstractAction; -import org.openide.util.Utilities; -import org.openide.util.actions.SystemAction; -import org.sleuthkit.autopsy.timeline.OpenTimelineAction; -import org.sleuthkit.autopsy.timeline.datamodel.eventtype.ArtifactEventType; -import org.sleuthkit.datamodel.BlackboardArtifact; - -/** - * - */ -public class ViewArtifactInTimelineAction extends AbstractAction { - - private static final long serialVersionUID = 1L; - - // This class is a singleton to support multi-selection of nodes, since - // org.openide.nodes.NodeOp.findActions(Node[] nodes) will only pick up an Action if every - // node in the array returns a reference to the same action object from Node.getActions(boolean). - private static ViewArtifactInTimelineAction instance; - - public static synchronized ViewArtifactInTimelineAction getInstance() { - if (null == instance) { - instance = new ViewArtifactInTimelineAction(); - } - return instance; - } - - private ViewArtifactInTimelineAction() { - super("View result in Timeline"); - } - - @Override - public void actionPerformed(ActionEvent e) { - - final Set artifactEventTypeIDs = ArtifactEventType.getAllArtifactEventTypes().stream() - .map(ArtifactEventType::getArtifactTypeID) - .collect(Collectors.toSet()); - - //for each artifact, get all datetime attributes for that artifact type - Set artifacts = Utilities.actionsGlobalContext().lookupAll(BlackboardArtifact.class).stream() - .filter(artifact -> artifactEventTypeIDs.contains(artifact.getArtifactTypeID())) - .collect(Collectors.toSet()); - - if (artifacts.size() > 1) { - return; - } else { - SystemAction.get(OpenTimelineAction.class).showArtifactInTimeline(Iterables.getOnlyElement(artifacts, null)); - } - } -} diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ViewFileInTimelineAction.java b/Core/src/org/sleuthkit/autopsy/datamodel/ViewFileInTimelineAction.java deleted file mode 100644 index 642b9b0d45..0000000000 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ViewFileInTimelineAction.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.sleuthkit.autopsy.datamodel; - -import com.google.common.collect.Iterables; -import java.awt.event.ActionEvent; -import java.util.Set; -import java.util.stream.Collectors; -import javax.swing.AbstractAction; -import org.openide.util.Utilities; -import org.openide.util.actions.SystemAction; -import org.sleuthkit.autopsy.timeline.OpenTimelineAction; -import org.sleuthkit.datamodel.AbstractFile; - -/** - * - */ -public class ViewFileInTimelineAction extends AbstractAction { - - private static final long serialVersionUID = 1L; - - // This class is a singleton to support multi-selection of nodes, since - // org.openide.nodes.NodeOp.findActions(Node[] nodes) will only pick up an Action if every - // node in the array returns a reference to the same action object from Node.getActions(boolean). - private static ViewFileInTimelineAction instance; - - public static synchronized ViewFileInTimelineAction getInstance() { - if (null == instance) { - instance = new ViewFileInTimelineAction(); - } - return instance; - } - - private ViewFileInTimelineAction() { - super("View file in Timeline"); - } - - @Override - public void actionPerformed(ActionEvent e) { - Set files = Utilities.actionsGlobalContext().lookupAll(AbstractFile.class).stream() - .collect(Collectors.toSet()); - -// final Set artifactEventTypeIDs = ArtifactEventType.getAllArtifactEventTypes().stream() -// .map(ArtifactEventType::getArtifactTypeID) -// .collect(Collectors.toSet()); -// -// //for each artifact, get all datetime attributes for that artifact type -// Set artifacts = Utilities.actionsGlobalContext().lookupAll(BlackboardArtifact.class).stream() -// .filter(artifact -> artifactEventTypeIDs.contains(artifact.getArtifactTypeID())) -// .collect(Collectors.toSet()); - - if (files.size() > 1) { - return; - }else{ - SystemAction.get(OpenTimelineAction.class).showFileInTimeline(Iterables.getOnlyElement(files,null));//, artifacts); - } - } -} diff --git a/Core/src/org/sleuthkit/autopsy/timeline/actions/ShowArtifactInTimelineAction.java b/Core/src/org/sleuthkit/autopsy/timeline/actions/ShowArtifactInTimelineAction.java new file mode 100644 index 0000000000..cb069431b5 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/timeline/actions/ShowArtifactInTimelineAction.java @@ -0,0 +1,46 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2011-2016 Basis Technology Corp. + * Contact: carrier sleuthkit 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.timeline.actions; + +import java.awt.event.ActionEvent; +import javax.swing.AbstractAction; +import org.openide.util.NbBundle; +import org.openide.util.actions.SystemAction; +import org.sleuthkit.autopsy.timeline.OpenTimelineAction; +import org.sleuthkit.datamodel.BlackboardArtifact; + +/** + * An action that shows the given artifact in the Timeline List View. + */ +public final class ShowArtifactInTimelineAction extends AbstractAction { + + private static final long serialVersionUID = 1L; + private final BlackboardArtifact artifact; + + @NbBundle.Messages({"ShowArtifactInTimelineAction.displayName=Show Result in Timeline... "}) + public ShowArtifactInTimelineAction(BlackboardArtifact artifact) { + super(Bundle.ShowArtifactInTimelineAction_displayName()); + this.artifact = artifact; + } + + @Override + public void actionPerformed(ActionEvent e) { + SystemAction.get(OpenTimelineAction.class).showArtifactInTimeline(artifact); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/timeline/actions/ShowFileInTimelineAction.java b/Core/src/org/sleuthkit/autopsy/timeline/actions/ShowFileInTimelineAction.java new file mode 100644 index 0000000000..5ab02ce5df --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/timeline/actions/ShowFileInTimelineAction.java @@ -0,0 +1,47 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2011-2016 Basis Technology Corp. + * Contact: carrier sleuthkit 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.timeline.actions; + +import java.awt.event.ActionEvent; +import javax.swing.AbstractAction; +import org.openide.util.NbBundle; +import org.openide.util.actions.SystemAction; +import org.sleuthkit.autopsy.timeline.OpenTimelineAction; +import org.sleuthkit.datamodel.AbstractFile; + +/** + * An action to prompt the user to pick an timestamp/event associated with the + * given file and show it in the Timeline List View + */ +public final class ShowFileInTimelineAction extends AbstractAction { + + private static final long serialVersionUID = 1L; + private final AbstractFile file; + + @NbBundle.Messages({"ShowFileInTimelineAction.displayName=Show File in Timeline... "}) + public ShowFileInTimelineAction(AbstractFile file) { + super(Bundle.ShowFileInTimelineAction_displayName()); + this.file = file; + } + + @Override + public void actionPerformed(ActionEvent e) { + SystemAction.get(OpenTimelineAction.class).showFileInTimeline(file); + } +} From 907c6dedc8057339ba666162c4ace8894cb31922 Mon Sep 17 00:00:00 2001 From: jmillman Date: Fri, 10 Jun 2016 11:11:08 -0400 Subject: [PATCH 14/48] move EventInTimeRange to inner class of ShowInTimelineDialog --- .../directorytree/DataResultFilterNode.java | 1 - .../autopsy/timeline/EventInTimeRange.java | 33 ------------------- .../timeline/ShowInTimelineDialog.java | 29 ++++++++++++++-- .../autopsy/timeline/TimeLineController.java | 6 ++-- 4 files changed, 29 insertions(+), 40 deletions(-) delete mode 100644 Core/src/org/sleuthkit/autopsy/timeline/EventInTimeRange.java diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java index 96c82edf80..52b1954188 100755 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java @@ -245,7 +245,6 @@ public class DataResultFilterNode extends FilterNode { actions.add(null); actions.add(AddBlackboardArtifactTagAction.getInstance()); } - return actions; } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/EventInTimeRange.java b/Core/src/org/sleuthkit/autopsy/timeline/EventInTimeRange.java deleted file mode 100644 index 145cecd867..0000000000 --- a/Core/src/org/sleuthkit/autopsy/timeline/EventInTimeRange.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ - -package org.sleuthkit.autopsy.timeline; - -import java.util.Set; -import org.joda.time.Interval; - -/** - * - */ -public class EventInTimeRange { - - private final Set eventIDs; - private final Interval range; - - public EventInTimeRange(Set eventIDs, Interval range) { - this.eventIDs = eventIDs; - this.range = range; - } - - public Set getEventIDs() { - return eventIDs; - } - - public Interval getRange() { - return range; - } - -} diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ShowInTimelineDialog.java b/Core/src/org/sleuthkit/autopsy/timeline/ShowInTimelineDialog.java index 4ad7d996e6..4f769030fe 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ShowInTimelineDialog.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ShowInTimelineDialog.java @@ -26,6 +26,7 @@ import java.time.temporal.ChronoUnit; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Set; import java.util.logging.Level; import java.util.stream.Collectors; import javafx.beans.property.SimpleObjectProperty; @@ -58,7 +59,7 @@ import org.sleuthkit.datamodel.BlackboardArtifact; /** * */ -public class ShowInTimelineDialog extends Dialog { +public class ShowInTimelineDialog extends Dialog { private static final ButtonType SHOW = new ButtonType("Show Timeline", ButtonBar.ButtonData.OK_DONE); @@ -130,7 +131,6 @@ public class ShowInTimelineDialog extends Dialog { DialogPane dialogPane = getDialogPane(); dialogPane.setContent(contentRoot); dialogPane.getButtonTypes().setAll(SHOW, ButtonType.CANCEL); - setResultConverter(buttonType -> { if (buttonType == SHOW) { @@ -164,7 +164,7 @@ public class ShowInTimelineDialog extends Dialog { List eventIDS; if (file != null) { eventIDS = controller.getEventsModel().getEventIDsForFile(file, false); - dialogPane.lookupButton(SHOW).disableProperty().bind(eventTable.getSelectionModel().selectedItemProperty().isNull()); + dialogPane.lookupButton(SHOW).disableProperty().bind(eventTable.getSelectionModel().selectedItemProperty().isNull()); } else if (artifact != null) { eventIDS = controller.getEventsModel().getEventIDsForArtifact(artifact); @@ -210,4 +210,27 @@ public class ShowInTimelineDialog extends Dialog { } } } + + /** + * Encapsulates the result of the ShowIntimelineDialog. + */ + static final class EventInTimeRange { + + private final Set eventIDs; + private final Interval range; + + EventInTimeRange(Set eventIDs, Interval range) { + this.eventIDs = eventIDs; + this.range = range; + } + + public Set getEventIDs() { + return eventIDs; + } + + public Interval getRange() { + return range; + } + + } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java index cf6d1ac690..c715ed7a20 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java @@ -445,10 +445,10 @@ public class TimeLineController { } else { ShowInTimelineDialog d = new ShowInTimelineDialog(this, file, artifact); - Optional result = d.showAndWait(); - result.ifPresent((EventInTimeRange t) -> { + Optional result = d.showAndWait(); + result.ifPresent(eventInTimeRange -> { SwingUtilities.invokeLater(this::showWindow); - showEvents(t.getEventIDs(), t.getRange()); + showEvents(eventInTimeRange.getEventIDs(), eventInTimeRange.getRange()); }); } break; From 5703a1c37f19c74d2142feff66db7bbc2e08dea6 Mon Sep 17 00:00:00 2001 From: jmillman Date: Fri, 10 Jun 2016 11:15:15 -0400 Subject: [PATCH 15/48] revert unneeded change to FXMLConstructor --- Core/src/org/sleuthkit/autopsy/timeline/FXMLConstructor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/FXMLConstructor.java b/Core/src/org/sleuthkit/autopsy/timeline/FXMLConstructor.java index 05d560c460..8d88a70630 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/FXMLConstructor.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/FXMLConstructor.java @@ -74,7 +74,7 @@ public class FXMLConstructor { * */ @ThreadConfined(type = ThreadConfined.ThreadType.JFX) - static public void construct(Node node, Class clazz, String fxmlFileName) { + static public void construct(Node node, Class clazz, String fxmlFileName) { final String name = "nbres:/" + StringUtils.replace(clazz.getPackage().getName(), ".", "/") + "/" + fxmlFileName; // NON-NLS try { From e6c9274cfef04db774ea90e47c0c9ae5253721e3 Mon Sep 17 00:00:00 2001 From: jmillman Date: Fri, 10 Jun 2016 11:30:14 -0400 Subject: [PATCH 16/48] cleanup OpenTimelineAction --- .../autopsy/timeline/OpenTimelineAction.java | 41 +++++++++++++++---- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java b/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java index 93f8b1e190..f27c159e73 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java @@ -35,18 +35,27 @@ import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; +/** + * An Action that opens the Timeline window. Has methods to open the window in + * various specific states (e.g., showing a specific artifact in the List View) + */ @ActionID(category = "Tools", id = "org.sleuthkit.autopsy.timeline.Timeline") @ActionRegistration(displayName = "#CTL_MakeTimeline", lazy = false) @ActionReferences(value = { @ActionReference(path = "Menu/Tools", position = 100)}) public class OpenTimelineAction extends CallableSystemAction { + private static final long serialVersionUID = 1L; private static final Logger LOGGER = Logger.getLogger(OpenTimelineAction.class.getName()); - private static final boolean fxInited = Installer.isJavaFxInited(); + private static final boolean FX_INITED = Installer.isJavaFxInited(); private static TimeLineController timeLineController = null; + /** + * Invalidate the reference to the controller so that a new will will be + * instantiated the next time this action is invoked + */ synchronized static void invalidateController() { timeLineController = null; } @@ -57,7 +66,7 @@ public class OpenTimelineAction extends CallableSystemAction { * we disabled the check to hasData() because if it is executed while a * data source is being added, it blocks the edt */ - return Case.isCaseOpen() && fxInited;// && Case.getCurrentCase().hasData(); + return Case.isCaseOpen() && FX_INITED;// && Case.getCurrentCase().hasData(); } @Override @@ -69,11 +78,7 @@ public class OpenTimelineAction extends CallableSystemAction { @NbBundle.Messages({ "OpenTimelineAction.settingsErrorMessage=Failed to initialize timeline settings.", "OpenTimeLineAction.msgdlg.text=Could not create timeline, there are no data sources."}) - private void showTimeline(AbstractFile file, BlackboardArtifact artifact) { - //check case - if (!Case.isCaseOpen()) { - return; - } + synchronized private void showTimeline(AbstractFile file, BlackboardArtifact artifact) { try { Case currentCase = Case.getCurrentCase(); if (currentCase.hasData() == false) { @@ -100,21 +105,41 @@ public class OpenTimelineAction extends CallableSystemAction { } } + /** + * Open the Timeline window with the default initial view. + */ + @ThreadConfined(type = ThreadConfined.ThreadType.AWT) public void showTimeline() { showTimeline(null, null); } + /** + * Open the Timeline window with the given file selected in ListView. The + * user will be prompted to choose which timestamp to use for the file, and + * how much time to show around it. + * + * @param file The AbstractFile to show in the Timeline. + */ + @ThreadConfined(type = ThreadConfined.ThreadType.AWT) public void showFileInTimeline(AbstractFile file) { showTimeline(file, null); } + /** + * Open the Timeline window with the given artifact selected in ListView. + * The how much time to show around it. + * + * @param artifact The BlackboardArtifact to show in the Timeline. + */ + @ThreadConfined(type = ThreadConfined.ThreadType.AWT) public void showArtifactInTimeline(BlackboardArtifact artifact) { showTimeline(null, artifact); } @Override + @NbBundle.Messages("OpenTimelineAction.displayName=Timeline") public String getName() { - return NbBundle.getMessage(OpenTimelineAction.class, "CTL_MakeTimeline"); + return Bundle.OpenTimelineAction_displayName(); } @Override From ac3d88e8088d791e8c9b793901ae97d91136be2a Mon Sep 17 00:00:00 2001 From: jmillman Date: Fri, 10 Jun 2016 12:30:17 -0400 Subject: [PATCH 17/48] rename ShowFileInTimelineAction an ShowArtifactInTimelineAction to View... ; split file and artifact constructors for ShowInTimelineDialog --- .../datamodel/BlackboardArtifactNode.java | 8 +- .../sleuthkit/autopsy/datamodel/FileNode.java | 4 +- .../timeline/ShowInTimelineDialog.java | 125 ++++++++++-------- .../autopsy/timeline/TimeLineController.java | 7 +- ...java => ViewArtifactInTimelineAction.java} | 8 +- ...ion.java => ViewFileInTimelineAction.java} | 11 +- 6 files changed, 93 insertions(+), 70 deletions(-) rename Core/src/org/sleuthkit/autopsy/timeline/actions/{ShowArtifactInTimelineAction.java => ViewArtifactInTimelineAction.java} (82%) rename Core/src/org/sleuthkit/autopsy/timeline/actions/{ShowFileInTimelineAction.java => ViewFileInTimelineAction.java} (72%) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java index eb3bc57f46..f9d2f34e0e 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java @@ -35,8 +35,8 @@ import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import static org.sleuthkit.autopsy.datamodel.Bundle.*; -import org.sleuthkit.autopsy.timeline.actions.ShowArtifactInTimelineAction; -import org.sleuthkit.autopsy.timeline.actions.ShowFileInTimelineAction; +import org.sleuthkit.autopsy.timeline.actions.ViewArtifactInTimelineAction; +import org.sleuthkit.autopsy.timeline.actions.ViewFileInTimelineAction; import org.sleuthkit.autopsy.timeline.datamodel.eventtype.ArtifactEventType; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; @@ -131,14 +131,14 @@ public class BlackboardArtifactNode extends DisplayableItemNode { } if (hasTimeStamp) { //if this artifact has a time stamp add the action to view it in the timeline - actionsList.add(new ShowArtifactInTimelineAction(artifact)); + actionsList.add(new ViewArtifactInTimelineAction(artifact)); } AbstractFile file = getLookup().lookup(AbstractFile.class); if (null != file) { //if this artifact has associated content, add the action to view the content in the timeline - actionsList.add(new ShowFileInTimelineAction(file)); + actionsList.add(new ViewFileInTimelineAction(file, true)); } return actionsList.toArray(new Action[actionsList.size()]); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileNode.java index 1eee85ec0c..34f0006a4c 100755 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileNode.java @@ -31,7 +31,7 @@ import org.sleuthkit.autopsy.directorytree.ExtractAction; import org.sleuthkit.autopsy.directorytree.HashSearchAction; import org.sleuthkit.autopsy.directorytree.NewWindowViewAction; import org.sleuthkit.autopsy.directorytree.ViewContextAction; -import org.sleuthkit.autopsy.timeline.actions.ShowFileInTimelineAction; +import org.sleuthkit.autopsy.timeline.actions.ViewFileInTimelineAction; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.TskData.TSK_DB_FILES_TYPE_ENUM; import org.sleuthkit.datamodel.TskData.TSK_FS_NAME_FLAG_ENUM; @@ -86,7 +86,7 @@ public class FileNode extends AbstractFsContentNode { } actionsList.add(new NewWindowViewAction(Bundle.FileNode_getActions_viewInNewWin_text(), this)); actionsList.add(new ExternalViewerAction(Bundle.FileNode_getActions_openInExtViewer_text(), this)); - actionsList.add(new ShowFileInTimelineAction(getContent())); + actionsList.add(new ViewFileInTimelineAction(getContent(), false)); actionsList.add(null); // creates a menu separator actionsList.add(ExtractAction.getInstance()); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ShowInTimelineDialog.java b/Core/src/org/sleuthkit/autopsy/timeline/ShowInTimelineDialog.java index 4f769030fe..2a1bd31eab 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ShowInTimelineDialog.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ShowInTimelineDialog.java @@ -49,6 +49,7 @@ import javafx.scene.layout.VBox; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.text.WordUtils; import org.joda.time.Interval; +import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.timeline.datamodel.SingleEvent; import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType; @@ -57,14 +58,17 @@ import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; /** - * + * A Dialog that, given a AbstractFile OR BlackBoardArtifact, allows the user to + * choose a specific event and a time range around it to show in the Timeline + * List View. */ -public class ShowInTimelineDialog extends Dialog { - - private static final ButtonType SHOW = new ButtonType("Show Timeline", ButtonBar.ButtonData.OK_DONE); +final class ShowInTimelineDialog extends Dialog { private static final Logger LOGGER = Logger.getLogger(ShowInTimelineDialog.class.getName()); + @NbBundle.Messages({"ShowInTimelineDialog.showTimelineButtonType.text=Show Timeline"}) + private static final ButtonType SHOW = new ButtonType(Bundle.ShowInTimelineDialog_showTimelineButtonType_text(), ButtonBar.ButtonData.OK_DONE); + @FXML private TableView eventTable; @@ -79,12 +83,18 @@ public class ShowInTimelineDialog extends Dialog unitComboBox; + @FXML private Label chooseEventLabel; - private final VBox contentRoot; + private final VBox contentRoot = new VBox(); + private final TimeLineController controller; + /** + * List of ChronoUnits the user can select from when choosing a time range + * to show. + */ private static final List SCROLL_BY_UNITS = Arrays.asList( ChronoUnit.YEARS, ChronoUnit.MONTHS, @@ -93,24 +103,9 @@ public class ShowInTimelineDialog extends Dialog { - - @Override - protected void updateItem(ChronoUnit item, boolean empty) { - super.updateItem(item, empty); - - if (empty || item == null) { - setText(null); - } else { - setText(WordUtils.capitalizeFully(item.toString())); - } - } - } - - public ShowInTimelineDialog(TimeLineController controller, AbstractFile file, BlackboardArtifact artifact) { - super(); + private ShowInTimelineDialog(TimeLineController controller, List eventIDS) { this.controller = controller; - contentRoot = new VBox(); + final String name = "nbres:/" + StringUtils.replace(ShowInTimelineDialog.class.getPackage().getName(), ".", "/") + "/ShowInTimelineDialog.fxml"; // NON-NLS try { @@ -132,22 +127,6 @@ public class ShowInTimelineDialog extends Dialog { - if (buttonType == SHOW) { - SingleEvent selectedEvent = eventTable.getSelectionModel().getSelectedItem(); - - if (file == null) { - selectedEvent = eventTable.getItems().get(0); - } - Duration selectedDuration = Duration.of(amountSpinner.getValue(), unitComboBox.getSelectionModel().getSelectedItem()); - - Interval range = IntervalUtils.getIntervalAround(Instant.ofEpochMilli(selectedEvent.getStartMillis()), selectedDuration); - return new EventInTimeRange(Collections.singleton(selectedEvent.getEventID()), range); - } else { - return null; - } - }); - amountSpinner.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(1, 1000)); unitComboBox.setButtonCell(new ChronoUnitListCell()); @@ -161,26 +140,64 @@ public class ShowInTimelineDialog extends Dialog new SimpleObjectProperty<>(param.getValue().getStartMillis())); dateTimeColumn.setCellFactory(param -> new DateTimeTableCell<>()); - List eventIDS; - if (file != null) { - eventIDS = controller.getEventsModel().getEventIDsForFile(file, false); - dialogPane.lookupButton(SHOW).disableProperty().bind(eventTable.getSelectionModel().selectedItemProperty().isNull()); - } else if (artifact != null) { - - eventIDS = controller.getEventsModel().getEventIDsForArtifact(artifact); - } else { - throw new IllegalArgumentException(); - } - setResizable(true); eventTable.getItems().setAll(eventIDS.stream().map(controller.getEventsModel()::getEventById).collect(Collectors.toSet())); - if (eventIDS.size() == 1) { - chooseEventLabel.setVisible(false); - chooseEventLabel.setManaged(false); - eventTable.getSelectionModel().select(0); - } eventTable.setPrefHeight(Math.min(200, 24 * eventTable.getItems().size() + 28)); } + ShowInTimelineDialog(TimeLineController controller, BlackboardArtifact artifact) { + this(controller, + controller.getEventsModel().getEventIDsForArtifact(artifact)); + chooseEventLabel.setVisible(false); + chooseEventLabel.setManaged(false); + eventTable.getSelectionModel().select(0); + + setResultConverter(buttonType -> { + if (buttonType == SHOW) { + SingleEvent selectedEvent = eventTable.getSelectionModel().getSelectedItem(); + if (selectedEvent == null) { + selectedEvent = eventTable.getItems().get(0); + } + return makeEventInTimeRange(selectedEvent); + } else { + return null; + } + }); + } + + ShowInTimelineDialog(TimeLineController controller, AbstractFile file) { + this(controller, + controller.getEventsModel().getEventIDsForFile(file, false)); + getDialogPane().lookupButton(SHOW).disableProperty().bind(eventTable.getSelectionModel().selectedItemProperty().isNull()); + + setResultConverter(buttonType -> { + if (buttonType == SHOW) { + return makeEventInTimeRange(eventTable.getSelectionModel().getSelectedItem()); + } else { + return null; + } + }); + } + + private EventInTimeRange makeEventInTimeRange(SingleEvent selectedEvent) { + Duration selectedDuration = Duration.of(amountSpinner.getValue(), unitComboBox.getSelectionModel().getSelectedItem()); + Interval range = IntervalUtils.getIntervalAround(Instant.ofEpochMilli(selectedEvent.getStartMillis()), selectedDuration); + return new EventInTimeRange(Collections.singleton(selectedEvent.getEventID()), range); + } + + static private class ChronoUnitListCell extends ListCell { + + @Override + protected void updateItem(ChronoUnit item, boolean empty) { + super.updateItem(item, empty); + + if (empty || item == null) { + setText(null); + } else { + setText(WordUtils.capitalizeFully(item.toString())); + } + } + } + static private class DateTimeTableCell extends TableCell { @Override diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java index c715ed7a20..c29c803239 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java @@ -418,7 +418,8 @@ public class TimeLineController { } //get a task that rebuilds the repo with the bellow state listener attached - final CancellationProgressTask rebuildRepositoryTask = repoBuilder.apply(newSate -> { + final CancellationProgressTask rebuildRepositoryTask; + rebuildRepositoryTask = repoBuilder.apply(newSate -> { //this will be on JFX thread switch (newSate) { case SUCCEEDED: @@ -443,7 +444,9 @@ public class TimeLineController { SwingUtilities.invokeLater(this::showWindow); TimeLineController.this.showFullRange(); } else { - ShowInTimelineDialog d = new ShowInTimelineDialog(this, file, artifact); + ShowInTimelineDialog d = (file == null) + ? new ShowInTimelineDialog(this, artifact) + : new ShowInTimelineDialog(this, file); Optional result = d.showAndWait(); result.ifPresent(eventInTimeRange -> { diff --git a/Core/src/org/sleuthkit/autopsy/timeline/actions/ShowArtifactInTimelineAction.java b/Core/src/org/sleuthkit/autopsy/timeline/actions/ViewArtifactInTimelineAction.java similarity index 82% rename from Core/src/org/sleuthkit/autopsy/timeline/actions/ShowArtifactInTimelineAction.java rename to Core/src/org/sleuthkit/autopsy/timeline/actions/ViewArtifactInTimelineAction.java index cb069431b5..0f7504ab35 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/actions/ShowArtifactInTimelineAction.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/actions/ViewArtifactInTimelineAction.java @@ -28,14 +28,14 @@ import org.sleuthkit.datamodel.BlackboardArtifact; /** * An action that shows the given artifact in the Timeline List View. */ -public final class ShowArtifactInTimelineAction extends AbstractAction { +public final class ViewArtifactInTimelineAction extends AbstractAction { private static final long serialVersionUID = 1L; private final BlackboardArtifact artifact; - @NbBundle.Messages({"ShowArtifactInTimelineAction.displayName=Show Result in Timeline... "}) - public ShowArtifactInTimelineAction(BlackboardArtifact artifact) { - super(Bundle.ShowArtifactInTimelineAction_displayName()); + @NbBundle.Messages({"ViewArtifactInTimelineAction.displayName=View Result in Timeline... "}) + public ViewArtifactInTimelineAction(BlackboardArtifact artifact) { + super(Bundle.ViewArtifactInTimelineAction_displayName()); this.artifact = artifact; } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/actions/ShowFileInTimelineAction.java b/Core/src/org/sleuthkit/autopsy/timeline/actions/ViewFileInTimelineAction.java similarity index 72% rename from Core/src/org/sleuthkit/autopsy/timeline/actions/ShowFileInTimelineAction.java rename to Core/src/org/sleuthkit/autopsy/timeline/actions/ViewFileInTimelineAction.java index 5ab02ce5df..376d260765 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/actions/ShowFileInTimelineAction.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/actions/ViewFileInTimelineAction.java @@ -29,14 +29,17 @@ import org.sleuthkit.datamodel.AbstractFile; * An action to prompt the user to pick an timestamp/event associated with the * given file and show it in the Timeline List View */ -public final class ShowFileInTimelineAction extends AbstractAction { +public final class ViewFileInTimelineAction extends AbstractAction { private static final long serialVersionUID = 1L; private final AbstractFile file; - @NbBundle.Messages({"ShowFileInTimelineAction.displayName=Show File in Timeline... "}) - public ShowFileInTimelineAction(AbstractFile file) { - super(Bundle.ShowFileInTimelineAction_displayName()); + @NbBundle.Messages({"ViewFileInTimelineAction.fileSource.displayName=View File in Timeline... ", + "ViewFileInTimelineAction.artifactSource.displayName=View Source File in Timeline... "}) + public ViewFileInTimelineAction(AbstractFile file, boolean isArtifactSource) { + super(isArtifactSource + ? Bundle.ViewFileInTimelineAction_artifactSource_displayName() + : Bundle.ViewFileInTimelineAction_fileSource_displayName()); this.file = file; } From 3a3af6647e62b02bf65186a44d912b2afd3dbf6e Mon Sep 17 00:00:00 2001 From: jmillman Date: Fri, 10 Jun 2016 13:29:32 -0400 Subject: [PATCH 18/48] set ShowInTimelineDialog icon --- .../autopsy/timeline/PromptDialogManager.java | 2 +- .../timeline/ShowInTimelineDialog.fxml | 2 +- .../timeline/ShowInTimelineDialog.java | 108 +++++++++++++++--- .../autopsy/timeline/TimeLineController.java | 24 ++-- .../autopsy/timeline/utils/IntervalUtils.java | 2 +- 5 files changed, 108 insertions(+), 30 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/PromptDialogManager.java b/Core/src/org/sleuthkit/autopsy/timeline/PromptDialogManager.java index 0e1996456e..45b4bfe1ff 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/PromptDialogManager.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/PromptDialogManager.java @@ -143,7 +143,7 @@ class PromptDialogManager { * @param dialog The dialog to set the title bar icon for. */ @ThreadConfined(type = ThreadConfined.ThreadType.JFX) - static private void setDialogIcons(Dialog dialog) { + static void setDialogIcons(Dialog dialog) { ((Stage) dialog.getDialogPane().getScene().getWindow()).getIcons().setAll(AUTOPSY_ICON); } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ShowInTimelineDialog.fxml b/Core/src/org/sleuthkit/autopsy/timeline/ShowInTimelineDialog.fxml index 57bd4b9c80..df2088a935 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ShowInTimelineDialog.fxml +++ b/Core/src/org/sleuthkit/autopsy/timeline/ShowInTimelineDialog.fxml @@ -21,7 +21,7 @@ -