Merge remote-tracking branch 'upstream/develop' into bulk_extractor_module

This commit is contained in:
Richard Cordovano 2014-09-06 09:06:35 -04:00
commit 81872519cb
355 changed files with 26864 additions and 2125 deletions

3
.gitignore vendored
View File

@ -66,6 +66,7 @@ genfiles.properties
*~
/netbeans-plat
/docs/doxygen/doxygen_docs
/docs/doxygen-user/user-docs
/jdiff-javadocs/*
/jdiff-logs/*
/gen_version.txt
@ -74,3 +75,5 @@ Core/src/org/sleuthkit/autopsy/casemodule/docs/QuickStart.html
Core/src/org/sleuthkit/autopsy/casemodule/docs/screenshot.png
/test/script/myconfig.xml
/test/script/*/*.xml
.DS_Store
.*.swp

View File

@ -6,33 +6,10 @@
<description>Builds, tests, and runs the project org.sleuthkit.autopsy.core</description>
<import file="nbproject/build-impl.xml"/>
<target name="quickstart-add-builddir">
<!-- additional docs for quickstart -->
<echo message="building quick start guide" />
<mkdir dir="build/classes/org/sleuthkit/autopsy/casemodule/docs" />
<copy overwrite="true" file="${basedir}/../docs/QuickStartGuide/index.html" tofile="build/classes/org/sleuthkit/autopsy/casemodule/docs/QuickStart.html"/>
<copy overwrite="true" file="${basedir}/../docs/QuickStartGuide/screenshot.png" tofile="build/classes/org/sleuthkit/autopsy/casemodule/docs/screenshot.png"/>
</target>
<target name="quickstart-add-src">
<!-- additional docs for quickstart -->
<echo message="building quick start guide 1" />
<mkdir dir="src/org/sleuthkit/autopsy/casemodule/docs" />
<copy overwrite="true" file="${basedir}/../docs/QuickStartGuide/index.html" tofile="src/org/sleuthkit/autopsy/casemodule/docs/QuickStart.html"/>
<copy overwrite="true" file="${basedir}/../docs/QuickStartGuide/screenshot.png" tofile="src/org/sleuthkit/autopsy/casemodule/docs/screenshot.png"/>
</target>
<target name="quickstart-remove-src">
<!-- cleanup additional docs for quickstart -->
<echo message="building quick start guide 2" />
<delete file="src/org/sleuthkit/autopsy/casemodule/docs/QuickStart.html"/>
<delete file="src/org/sleuthkit/autopsy/casemodule/docs/screenshot.png"/>
</target>
<target name="javahelp">
<antcall target="quickstart-remove-src" />
</target>
<!-- Verify that the TSK_HOME env variable is set -->
@ -53,12 +30,7 @@
<target name="init" depends="basic-init,files-init,build-init,-javac-init">
<!-- get additional deps -->
<antcall target="getTSKJars" />
<antcall target="quickstart-add-src" />
</target>
</project>

View File

@ -0,0 +1,38 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2013 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.coreutils;
import javafx.scene.paint.Color;
/**
*
*/
public class ColorUtilities {
private ColorUtilities() {
}
public static String getRGBCode(Color color) {
return String.format("#%02X%02X%02X%02X",
(int) (color.getRed() * 255),
(int) (color.getGreen() * 255),
(int) (color.getBlue() * 255),
(int) (color.getOpacity() * 255));
}
}

View File

@ -0,0 +1,198 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.coreutils;
import java.util.Objects;
import javafx.beans.property.Property;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.property.SimpleListProperty;
import javafx.collections.FXCollections;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;
/**
* A basic history implementation. Keeps a history (and forward) stack of state
* objects of type <T>. exposes current state and availability of
* advance/retreat operations via methods and JFX {@link Property}s. Null is not
* a valid state, and will only be the current state before the first call to
* advance.
*
* @param <T> the type of objects used to represent the
* current/historical/future states
*/
@ThreadSafe
public class History<T> {
@GuardedBy("this")
private final ObservableStack<T> historyStack = new ObservableStack<>();
@GuardedBy("this")
private final ObservableStack<T> forwardStack = new ObservableStack<>();
@GuardedBy("this")
private final ReadOnlyObjectWrapper<T> currentState = new ReadOnlyObjectWrapper<>();
@GuardedBy("this")
private final ReadOnlyBooleanWrapper canAdvance = new ReadOnlyBooleanWrapper();
@GuardedBy("this")
private final ReadOnlyBooleanWrapper canRetreat = new ReadOnlyBooleanWrapper();
synchronized public T getCurrentState() {
return currentState.get();
}
synchronized public boolean canAdvance() {
return canAdvance.get();
}
synchronized public boolean canRetreat() {
return canRetreat.get();
}
synchronized public ReadOnlyObjectProperty<T> currentState() {
return currentState.getReadOnlyProperty();
}
synchronized public ReadOnlyBooleanProperty getCanAdvance() {
return canAdvance.getReadOnlyProperty();
}
synchronized public ReadOnlyBooleanProperty getCanRetreat() {
return canRetreat.getReadOnlyProperty();
}
public History(T initialState) {
this();
currentState.set(initialState);
}
public History() {
canAdvance.bind(forwardStack.emptyProperty().not());
canRetreat.bind(historyStack.emptyProperty().not());
}
/**
* advance through the forward states by one, and put the current state in
* the history. Is a no-op if there are no forward states.
*
* @return the state advanced to, or null if there were no forward states.
*/
synchronized public T advance() {
final T peek = forwardStack.peek();
if (peek != null && peek.equals(currentState.get()) == false) {
historyStack.push(currentState.get());
currentState.set(peek);
forwardStack.pop();
}
return peek;
}
/**
* retreat through the history states by one, and add the current state to
* the forward states. Is a no-op if there are no history states.
*
* @return the state retreated to, or null if there were no history states.
*/
synchronized public T retreat() {
final T pop = historyStack.pop();
if (pop != null && pop.equals(currentState.get()) == false) {
forwardStack.push(currentState.get());
currentState.set(pop);
return pop;
} else if (pop != null && pop.equals(currentState.get())) {
return retreat();
}
return pop;
}
/**
* put the current state in the history and advance to the given state. It
* is a no-op if the current state is equal to the given state as determined
* by invoking the equals method. Throws away any forward states.
*
* @param newState the new state to advance to
* @throws IllegalArgumentException if newState == null
*/
synchronized public void advance(T newState) throws IllegalArgumentException {
if (newState != null && Objects.equals(currentState.get(), newState) == false) {
if (currentState.get() != null) {
historyStack.push(currentState.get());
}
currentState.set(newState);
if (newState.equals(forwardStack.peek())) {
forwardStack.pop();
} else {
forwardStack.clear();
}
}
}
public void clear() {
historyStack.clear();
forwardStack.clear();
currentState.set(null);
}
/**
* A simple extension to SimpleListProperty to add a stack api
*
* TODO: this really should not extend SimpleListProperty but should
* delegate to an appropriate observable implementation while implementing
* the {@link Deque} interface
*/
private static class ObservableStack<T> extends SimpleListProperty<T> {
public ObservableStack() {
super(FXCollections.<T>synchronizedObservableList(FXCollections.<T>observableArrayList()));
}
public void push(T item) {
synchronized (this) {
add(0, item);
}
}
public T pop() {
synchronized (this) {
if (isEmpty()) {
return null;
} else {
return remove(0);
}
}
}
public T peek() {
synchronized (this) {
if (isEmpty()) {
return null;
} else {
return get(0);
}
}
}
}
}

View File

@ -0,0 +1,75 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2013-14 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.coreutils;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import javafx.concurrent.Task;
import org.openide.util.Cancellable;
/**
* extension of Task that logs state changes
*/
public abstract class LoggedTask<T> extends Task<T> implements Cancellable {
private static final Logger LOGGER = Logger.getLogger(LoggedTask.class.getName());
private final boolean logStateChanges;
public LoggedTask(String taskName, boolean logStateChanges) {
updateTitle(taskName);
this.logStateChanges = logStateChanges;
}
@Override
protected void cancelled() {
super.cancelled();
if (logStateChanges) {
LOGGER.log(Level.WARNING, "{0} cancelled!", getTitle());
}
}
@Override
protected void failed() {
super.failed();
LOGGER.log(Level.SEVERE, getTitle() + " failed!", getException());
}
@Override
protected void scheduled() {
super.scheduled();
if (logStateChanges) {
LOGGER.log(Level.INFO, "{0} scheduled", getTitle());
}
}
@Override
protected void succeeded() {
super.succeeded();
try {
get();
} catch (InterruptedException | ExecutionException ex) {
LOGGER.log(Level.SEVERE, getTitle() + " threw unexpected exception: ", ex);
}
if (logStateChanges) {
LOGGER.log(Level.INFO, "{0} succeeded", getTitle());
}
}
}

View File

@ -0,0 +1,22 @@
package org.sleuthkit.autopsy.coreutils;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.FIELD})
@Inherited
@Documented
public @interface ThreadConfined {
ThreadType type();
public enum ThreadType {
ANY, UI, JFX, AWT, NOT_UI
}
}

View File

@ -62,3 +62,4 @@ InterestingItemDefsPanel.rulePathFilterTextField.text=
InterestingItemDefsPanel.rulePathPanel.border.title=Path
InterestingItemDefsPanel.rulePathFilterRegexCheckBox.text=Regex
InterestingItemDefsPanel.ignoreKnownFilesCheckbox.text=Ignore Known Files
FilesIdentifierIngestJobSettingsPanel.border.title=Select interesting files sets to enable during ingest:

View File

@ -1,6 +1,15 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo">
<TitledBorder title="Select interesting files sets to enable during ingest:">
<ResourceString PropertyName="titleX" bundle="org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties" key="FilesIdentifierIngestJobSettingsPanel.border.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</TitledBorder>
</Border>
</Property>
</Properties>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
@ -18,7 +27,7 @@
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="filesSetScrollPane" pref="254" max="32767" attributes="0"/>
<Component id="filesSetScrollPane" pref="242" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
@ -27,7 +36,7 @@
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="filesSetScrollPane" pref="188" max="32767" attributes="0"/>
<Component id="filesSetScrollPane" pref="165" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>

View File

@ -264,6 +264,8 @@ final class FilesIdentifierIngestJobSettingsPanel extends IngestModuleIngestJobS
filesSetScrollPane = new javax.swing.JScrollPane();
filesSetTable = new javax.swing.JTable();
setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(FilesIdentifierIngestJobSettingsPanel.class, "FilesIdentifierIngestJobSettingsPanel.border.title"))); // NOI18N
filesSetTable.setBackground(new java.awt.Color(240, 240, 240));
filesSetTable.setModel(new javax.swing.table.DefaultTableModel(
new Object [][] {
@ -283,14 +285,14 @@ final class FilesIdentifierIngestJobSettingsPanel extends IngestModuleIngestJobS
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(filesSetScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 254, Short.MAX_VALUE)
.addComponent(filesSetScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 242, Short.MAX_VALUE)
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap()
.addComponent(filesSetScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 188, Short.MAX_VALUE)
.addComponent(filesSetScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 165, Short.MAX_VALUE)
.addContainerGap())
);
}// </editor-fold>//GEN-END:initComponents

View File

@ -1,20 +1,8 @@
OpenIDE-Module-Display-Category=External Viewers
OpenIDE-Module-Long-Description=\
Displays user activity as an interactive timeline chart with year, month and day granularity. \n\
Events for a selected day are viewable in the built-in result and content viewers.
OpenIDE-Module-Name=Timeline
CTL_MakeTimeline="Make Timeline (Beta)"
CTL_TimelineView=Generate Timeline
OpenIDE-Module-Short-Description=Displays user activity timeline
TimelineProgressDialog.jLabel1.text=Creating timeline . . .
TimelineFrame.title=Timeline
Timeline.frameName.text={0} - Autopsy Timeline (Beta)
CTL_MakeTimeline="Timeline"
Timeline.frameName.text={0} - Autopsy Timeline
Timeline.resultsPanel.title=Timeline Results
Timeline.getName=Make Timeline (Beta)
Timeline.runJavaFxThread.progress.creating=Creating timeline . . .
Timeline.runJavaFxThread.progress.genBodyFile=Generating Bodyfile
Timeline.runJavaFxThread.progress.genMacTime=Generating Mactime
Timeline.runJavaFxThread.progress.parseMacTime=Parsing Mactime
Timeline.zoomOutButton.text=Zoom Out
Timeline.goToButton.text=Go To\:
Timeline.yearBarChart.x.years=Years
@ -26,8 +14,24 @@ Timeline.eventsByMoBarChart.y.numEvents=Number of Events
Timeline.node.emptyRoot=Empty Root
Timeline.resultPanel.loading=Loading...
Timeline.node.root=Root
Timeline.propChg.confDlg.timelineOOD.msg=Timeline is out of date. Would you like to regenerate it?
Timeline.propChg.confDlg.timelineOOD.msg=The event data is out of date. Would you like to regenerate it?
Timeline.propChg.confDlg.timelineOOD.details=Select an option
Timeline.initTimeline.confDlg.genBeforeIngest.msg=You are trying to generate a timeline before ingest has been completed. The timeline may be incomplete. Do you want to continue?
Timeline.initTimeline.confDlg.genBeforeIngest.deails=Timeline
TimelineProgressDialog.setName.text=Make Timeline (Beta)
Timeline.initTimeline.confDlg.genBeforeIngest.details=Timeline
TimelineFrame.title=Timeline
TimelinePanel.jButton1.text=6m
TimelinePanel.jButton13.text=all
TimelinePanel.jButton10.text=1h
TimelinePanel.jButton9.text=12h
TimelinePanel.jButton11.text=5y
TimelinePanel.jButton12.text=10y
TimelinePanel.jButton6.text=1w
TimelinePanel.jButton5.text=1y
TimelinePanel.jButton8.text=1d
TimelinePanel.jButton7.text=3d
TimelinePanel.jButton2.text=1m
TimelinePanel.jButton3.text=3m
TimelinePanel.jButton4.text=2w
ProgressWindow.progressHeader.text=\
AggregateEvent.differentTypes="aggregate events are not compatible they have different types"
AggregateEvent.differentDescriptions="aggregate events are not compatible they have different descriptions"

View File

@ -1,33 +0,0 @@
OpenIDE-Module-Display-Category=\u5916\u90E8\u30D3\u30E5\u30FC\u30A2
OpenIDE-Module-Long-Description=\
\u30E6\u30FC\u30B6\u30A2\u30AF\u30C6\u30A3\u30D3\u30C6\u30A3\u3092\u5E74\u3001\u6708\u3001\u65E5\u306E\u5358\u4F4D\u3067\u3001\u30A4\u30F3\u30BF\u30E9\u30AF\u30C6\u30A3\u30D6\u306A\u30BF\u30A4\u30E0\u30E9\u30A4\u30F3\u30C1\u30E3\u30FC\u30C8\u3068\u3057\u3066\u8868\u793A\u3057\u307E\u3059\u3002\n\
\u9078\u629E\u3057\u305F\u65E5\u306E\u30A4\u30D9\u30F3\u30C8\u306F\u5185\u8535\u306E\u7D50\u679C\u304A\u3088\u3073\u30B3\u30F3\u30C6\u30F3\u30C4\u30D3\u30E5\u30FC\u30A2\u3067\u78BA\u8A8D\u3067\u304D\u307E\u3059\u3002
OpenIDE-Module-Name=\u30BF\u30A4\u30E0\u30E9\u30A4\u30F3
CTL_MakeTimeline="\u30BF\u30A4\u30E0\u30E9\u30A4\u30F3\u4F5C\u6210\uFF08\u30D9\u30FC\u30BF\uFF09"
CTL_TimelineView=\u30BF\u30A4\u30E0\u30E9\u30A4\u30F3\u4F5C\u6210
OpenIDE-Module-Short-Description=\u30E6\u30FC\u30B6\u30A2\u30AF\u30C6\u30A3\u30D3\u30C6\u30A3\u30BF\u30A4\u30E0\u30E9\u30A4\u30F3\u3092\u8868\u793A
TimelineProgressDialog.jLabel1.text=\u30BF\u30A4\u30E0\u30E9\u30A4\u30F3\u3092\u4F5C\u6210\u4E2D\u2026
TimelineFrame.title=\u30BF\u30A4\u30E0\u30E9\u30A4\u30F3
Timeline.frameName.text={0} - Autopsy\u30BF\u30A4\u30E0\u30E9\u30A4\u30F3\uFF08\u30D9\u30FC\u30BF\uFF09
Timeline.resultsPanel.title=\u30BF\u30A4\u30E0\u30E9\u30A4\u30F3\u7D50\u679C
Timeline.getName=\u30BF\u30A4\u30E0\u30E9\u30A4\u30F3\u4F5C\u6210\uFF08\u30D9\u30FC\u30BF\uFF09
Timeline.runJavaFxThread.progress.creating=\u30BF\u30A4\u30E0\u30E9\u30A4\u30F3\u3092\u4F5C\u6210\u4E2D\u2026
Timeline.runJavaFxThread.progress.genBodyFile=Bodyfile\u3092\u4F5C\u6210\u4E2D
Timeline.runJavaFxThread.progress.genMacTime=MAC\u30BF\u30A4\u30E0\u3092\u4F5C\u6210\u4E2D
Timeline.runJavaFxThread.progress.parseMacTime=MAC\u30BF\u30A4\u30E0\u3092\u30D1\u30FC\u30B9\u4E2D
Timeline.zoomOutButton.text=\u7E2E\u5C0F
Timeline.goToButton.text=\u4E0B\u8A18\u3078\u79FB\u52D5\uFF1A
Timeline.yearBarChart.x.years=\u5E74
Timeline.yearBarChart.y.numEvents=\u30A4\u30D9\u30F3\u30C8\u6570
Timeline.MonthsBarChart.x.monthYY=\u6708 ({0})
Timeline.MonthsBarChart.y.numEvents=\u30A4\u30D9\u30F3\u30C8\u6570
Timeline.eventsByMoBarChart.x.dayOfMo=\u65E5
Timeline.eventsByMoBarChart.y.numEvents=\u30A4\u30D9\u30F3\u30C8\u6570
Timeline.node.emptyRoot=\u7A7A\u306E\u30EB\u30FC\u30C8
Timeline.resultPanel.loading=\u30ED\u30FC\u30C9\u4E2D\u2026
Timeline.node.root=\u30EB\u30FC\u30C8
Timeline.propChg.confDlg.timelineOOD.msg=\u30BF\u30A4\u30E0\u30E9\u30A4\u30F3\u304C\u6700\u65B0\u3067\u306F\u3042\u308A\u307E\u305B\u3093\u3002\u518D\u5EA6\u4F5C\u6210\u3057\u307E\u3059\u304B\uFF1F
Timeline.propChg.confDlg.timelineOOD.details=\u30AA\u30D7\u30B7\u30E7\u30F3\u3092\u9078\u629E\u3057\u3066\u4E0B\u3055\u3044
Timeline.initTimeline.confDlg.genBeforeIngest.msg=\u30A4\u30F3\u30B8\u30A7\u30B9\u30C8\u304C\u5B8C\u4E86\u3059\u308B\u524D\u306B\u30BF\u30A4\u30E0\u30E9\u30A4\u30F3\u3092\u4F5C\u6210\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059\u3002\u30BF\u30A4\u30E0\u30E9\u30A4\u30F3\u304C\u4E0D\u5B8C\u5168\u306B\u306A\u308B\u304B\u3082\u3057\u308C\u307E\u305B\u3093\u3002\u7D9A\u884C\u3057\u307E\u3059\u304B\uFF1F
Timeline.initTimeline.confDlg.genBeforeIngest.deails=\u30BF\u30A4\u30E0\u30E9\u30A4\u30F3
TimelineProgressDialog.setName.text=\u30BF\u30A4\u30E0\u30E9\u30A4\u30F3\u3092\u4F5C\u6210\uFF08\u30D9\u30FC\u30BF\uFF09

View File

@ -0,0 +1,65 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2013 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.timeline;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import org.apache.commons.lang3.StringUtils;
import org.openide.util.Exceptions;
/**
* This class supports programmer productivity by abstracting frequently used
* code to load FXML-defined GUI components,
*
* TODO? improve performance by implementing a caching FXMLLoader as described
* at
* http://stackoverflow.com/questions/11734885/javafx2-very-poor-performance-when-adding-custom-made-fxmlpanels-to-gridpane.
*
* TODO: find a way to move this to CoreUtils and remove duplicate verison in
* image analyzer
*/
public class FXMLConstructor {
static public void construct(Node n, String fxmlFileName) {
final String name = "nbres:/" + StringUtils.replace(n.getClass().getPackage().getName(), ".", "/") + "/" + fxmlFileName;
System.out.println(name);
try {
FXMLLoader fxmlLoader = new FXMLLoader(new URL(name));
fxmlLoader.setRoot(n);
fxmlLoader.setController(n);
try {
fxmlLoader.load();
} catch (IOException exception) {
try {
fxmlLoader.setClassLoader(FXMLLoader.getDefaultClassLoader());
fxmlLoader.load();
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
}
}
} catch (MalformedURLException ex) {
Exceptions.printStackTrace(ex);
}
}
}

View File

@ -0,0 +1,96 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2013 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.timeline;
import java.util.logging.Level;
import javax.swing.JOptionPane;
import org.openide.awt.ActionID;
import org.openide.awt.ActionReference;
import org.openide.awt.ActionReferences;
import org.openide.awt.ActionRegistration;
import org.openide.util.HelpCtx;
import org.openide.util.actions.CallableSystemAction;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.core.Installer;
import org.sleuthkit.autopsy.coreutils.Logger;
@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 Logger LOGGER = Logger.getLogger(OpenTimelineAction.class.getName());
private static final boolean fxInited = Installer.isJavaFxInited();
synchronized static void invalidateController() {
timeLineController = null;
}
private static TimeLineController timeLineController = null;
public OpenTimelineAction() {
super();
}
@Override
public boolean isEnabled() {
return Case.isCaseOpen() && fxInited && Case.getCurrentCase().hasData();
}
@Override
public void performAction() {
//check case
if (!Case.existsCurrentCase()) {
return;
}
final Case currentCase = Case.getCurrentCase();
if (currentCase.hasData() == false) {
JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), "Error creating timeline, there are no data sources.");
LOGGER.log(Level.INFO, "Error creating timeline, there are no data sources.");
return;
}
synchronized (OpenTimelineAction.class) {
if (timeLineController == null) {
timeLineController = new TimeLineController();
LOGGER.log(Level.WARNING, "Failed to get TimeLineController from lookup. Instantiating one directly.S");
}
}
timeLineController.openTimeLine();
}
@Override
public String getName() {
return "Timeline";
}
@Override
public HelpCtx getHelpCtx() {
return HelpCtx.DEFAULT_HELP;
}
@Override
public boolean asynchronous() {
return false; // run on edt
}
}

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JDialogFormInfo">
<Form version="1.5" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JFrameFormInfo">
<SyntheticProperties>
<SyntheticProperty name="formSizePolicy" type="int" value="1"/>
<SyntheticProperty name="generateCenter" type="boolean" value="false"/>
@ -28,7 +28,7 @@
<Group type="103" groupAlignment="0" attributes="0">
<Component id="progressBar" pref="504" max="32767" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Component id="jLabel1" min="-2" max="-2" attributes="0"/>
<Component id="progressHeader" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
</Group>
@ -40,23 +40,23 @@
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="jLabel1" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="7" max="-2" attributes="0"/>
<Component id="progressHeader" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="progressBar" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="16" max="32767" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="jLabel1">
<Component class="javax.swing.JProgressBar" name="progressBar">
</Component>
<Component class="javax.swing.JLabel" name="progressHeader">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/timeline/Bundle.properties" key="TimelineProgressDialog.jLabel1.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/advancedtimeline/Bundle.properties" key="ProgressWindow.progressHeader.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JProgressBar" name="progressBar">
</Component>
</SubComponents>
</Form>

View File

@ -0,0 +1,246 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2013 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.timeline;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.annotation.concurrent.Immutable;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.GroupLayout;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JProgressBar;
import javax.swing.KeyStroke;
import javax.swing.LayoutStyle;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import org.openide.awt.Mnemonics;
import org.openide.util.NbBundle;
import org.openide.windows.WindowManager;
/**
* Dialog with progress bar that pops up when timeline is being generated
*/
public class ProgressWindow extends JFrame {
private final SwingWorker<?, ?> worker;
/**
* Creates new form TimelineProgressDialog
*/
public ProgressWindow(Component parent, boolean modal, SwingWorker<?, ?> worker) {
super();
initComponents();
setLocationRelativeTo(parent);
setAlwaysOnTop(modal);
//set icon the same as main app
SwingUtilities.invokeLater(() -> {
setIconImage(WindowManager.getDefault().getMainWindow().getIconImage());
});
//progressBar.setIndeterminate(true);
setName("Timeline");
setTitle("Generating Timeline data");
// Close the dialog when Esc is pressed
String cancelName = "cancel";
InputMap inputMap = getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), cancelName);
ActionMap actionMap = getRootPane().getActionMap();
actionMap.put(cancelName, new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
cancel();
}
});
this.worker = worker;
}
public void updateProgress(final int progress) {
SwingUtilities.invokeLater(() -> {
progressBar.setValue(progress);
});
}
public void updateProgress(final int progress, final String message) {
SwingUtilities.invokeLater(() -> {
progressBar.setValue(progress);
progressBar.setString(message);
});
}
public void updateProgress(final String message) {
SwingUtilities.invokeLater(() -> {
progressBar.setString(message);
});
}
public void setProgressTotal(final int total) {
SwingUtilities.invokeLater(() -> {
progressBar.setIndeterminate(false);
progressBar.setMaximum(total);
progressBar.setStringPainted(true);
});
}
public void updateHeaderMessage(final String headerMessage) {
SwingUtilities.invokeLater(() -> {
progressHeader.setText(headerMessage);
});
}
public void setIndeterminate() {
SwingUtilities.invokeLater(() -> {
progressBar.setIndeterminate(true);
progressBar.setStringPainted(true);
});
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
progressBar = new JProgressBar();
progressHeader = new JLabel();
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent evt) {
closeDialog(evt);
}
});
Mnemonics.setLocalizedText(progressHeader, NbBundle.getMessage(ProgressWindow.class, "ProgressWindow.progressHeader.text")); // NOI18N
GroupLayout layout = new GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addComponent(progressBar, GroupLayout.DEFAULT_SIZE, 504, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup()
.addComponent(progressHeader)
.addGap(0, 0, Short.MAX_VALUE)))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(progressHeader)
.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
.addComponent(progressBar, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
pack();
}// </editor-fold>//GEN-END:initComponents
/**
* Closes the dialog
*/
private void closeDialog(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_closeDialog
cancel();
}//GEN-LAST:event_closeDialog
public void cancel() {
SwingUtilities.invokeLater(() -> {
if (isVisible()) {
int showConfirmDialog = JOptionPane.showConfirmDialog(ProgressWindow.this, "Do you want to cancel time line creation?", "Cancel timeline creation?", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
if (showConfirmDialog == JOptionPane.YES_OPTION) {
close();
}
} else {
close();
}
});
}
public void close() {
worker.cancel(false);
setVisible(false);
dispose();
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private JProgressBar progressBar;
private JLabel progressHeader;
// End of variables declaration//GEN-END:variables
public void update(ProgressUpdate chunk) {
updateHeaderMessage(chunk.getHeaderMessage());
if (chunk.getTotal() >= 0) {
setProgressTotal(chunk.getTotal());
updateProgress(chunk.getProgress(), chunk.getDetailMessage());
} else {
setIndeterminate();
updateProgress(chunk.getDetailMessage());
}
}
/** bundles up progress information to be shown in the progress dialog */
@Immutable
public static class ProgressUpdate {
private final int progress;
private final int total;
private final String headerMessage;
private final String detailMessage;
public int getProgress() {
return progress;
}
public int getTotal() {
return total;
}
public String getHeaderMessage() {
return headerMessage;
}
public String getDetailMessage() {
return detailMessage;
}
public ProgressUpdate(int progress, int total, String headerMessage, String detailMessage) {
super();
this.progress = progress;
this.total = total;
this.headerMessage = headerMessage;
this.detailMessage = detailMessage;
}
}
}

View File

@ -0,0 +1,690 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.timeline;
import org.sleuthkit.autopsy.coreutils.LoggedTask;
import java.awt.HeadlessException;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.ZoneId;
import java.util.Collection;
import java.util.MissingResourceException;
import java.util.TimeZone;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import javafx.application.Platform;
import javafx.beans.Observable;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper;
import javafx.beans.property.ReadOnlyDoubleProperty;
import javafx.beans.property.ReadOnlyDoubleWrapper;
import javafx.beans.property.ReadOnlyListProperty;
import javafx.beans.property.ReadOnlyListWrapper;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.property.ReadOnlyStringProperty;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.Immutable;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.Interval;
import org.joda.time.ReadablePeriod;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle;
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.Logger;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.timeline.events.FilteredEventsModel;
import org.sleuthkit.autopsy.timeline.events.db.EventsRepository;
import org.sleuthkit.autopsy.timeline.events.type.EventType;
import org.sleuthkit.autopsy.timeline.filters.Filter;
import org.sleuthkit.autopsy.timeline.filters.TypeFilter;
import org.sleuthkit.autopsy.timeline.utils.IntervalUtils;
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.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
/** Controller in the MVC design along with model = {@link FilteredEventsModel}
* and views = {@link TimeLineView}. Forwards interpreted user gestures form
* views to model. Provides model to view. Is entry point for timeline module.
*
* Concurrency Policy:<ul>
* <li>Since filteredEvents is internally synchronized, only compound access to
* it needs external synchronization</li>
* * <li>Since eventsRepository is internally synchronized, only compound
* access to it needs external synchronization <li>
* <li>Other state including listeningToAutopsy, mainFrame, viewMode, and the
* listeners should only be accessed with this object's intrinsic lock held
* </li>
* <ul>
*/
public class TimeLineController {
private static final Logger LOGGER = Logger.getLogger(TimeLineController.class.getName());
private static final String DO_REPOPULATE_MESSAGE = "The events databse was prevously populated while ingest was running.\n" + "Some events may not have been populated or may have been populated inaccurately.\n" + "Do you want to repopulate the events database now?";
private static final ReadOnlyObjectWrapper<TimeZone> timeZone = new ReadOnlyObjectWrapper<>(TimeZone.getDefault());
public static ZoneId getTimeZoneID() {
return timeZone.get().toZoneId();
}
public static DateTimeFormatter getZonedFormatter() {
return DateTimeFormat.forPattern("YYYY-MM-dd HH:mm:ss").withZone(getJodaTimeZone());
}
public static DateTimeZone getJodaTimeZone() {
return DateTimeZone.forTimeZone(getTimeZone().get());
}
public static ReadOnlyObjectProperty<TimeZone> getTimeZone() {
return timeZone.getReadOnlyProperty();
}
private final ExecutorService executor = Executors.newSingleThreadExecutor();
private final ReadOnlyListWrapper<Task<?>> tasks = new ReadOnlyListWrapper<>(FXCollections.observableArrayList());
private final ReadOnlyDoubleWrapper progress = new ReadOnlyDoubleWrapper(-1);
private final ReadOnlyStringWrapper message = new ReadOnlyStringWrapper();
private final ReadOnlyStringWrapper taskTitle = new ReadOnlyStringWrapper();
synchronized public ReadOnlyListProperty<Task<?>> getTasks() {
return tasks.getReadOnlyProperty();
}
synchronized public ReadOnlyDoubleProperty getProgress() {
return progress.getReadOnlyProperty();
}
synchronized public ReadOnlyStringProperty getMessage() {
return message.getReadOnlyProperty();
}
synchronized public ReadOnlyStringProperty getTaskTitle() {
return taskTitle.getReadOnlyProperty();
}
@GuardedBy("this")
private TimeLineTopComponent mainFrame;
//are the listeners currently attached
@GuardedBy("this")
private boolean listeningToAutopsy = false;
private final PropertyChangeListener caseListener;
private final PropertyChangeListener ingestJobListener = new AutopsyIngestJobListener();
private final PropertyChangeListener ingestModuleListener = new AutopsyIngestModuleListener();
@GuardedBy("this")
private final ReadOnlyObjectWrapper<VisualizationMode> viewMode = new ReadOnlyObjectWrapper<>(VisualizationMode.COUNTS);
synchronized public ReadOnlyObjectProperty<VisualizationMode> getViewMode() {
return viewMode.getReadOnlyProperty();
}
@GuardedBy("filteredEvents")
private final FilteredEventsModel filteredEvents;
@GuardedBy("eventsRepository")
private final EventsRepository eventsRepository;
@GuardedBy("this")
private final ZoomParams InitialZoomState;
@GuardedBy("this")
private final History<ZoomParams> historyManager = new History<>();
//all members should be access with the intrinsict lock of this object held
//selected events (ie shown in the result viewer)
@GuardedBy("this")
private final ObservableList<Long> selectedEventIDs = FXCollections.<Long>synchronizedObservableList(FXCollections.<Long>observableArrayList());
/**
* @return an unmodifiable list of the selected event ids
*/
synchronized public ObservableList<Long> getSelectedEventIDs() {
return selectedEventIDs;
}
@GuardedBy("this")
private final ReadOnlyObjectWrapper<Interval> selectedTimeRange = new ReadOnlyObjectWrapper<>();
/**
* @return a read only view of the selected interval.
*/
synchronized public ReadOnlyObjectProperty<Interval> getSelectedTimeRange() {
return selectedTimeRange.getReadOnlyProperty();
}
public ReadOnlyBooleanProperty getNewEventsFlag() {
return newEventsFlag.getReadOnlyProperty();
}
private final ReadOnlyBooleanWrapper needsHistogramRebuild = new ReadOnlyBooleanWrapper(false);
public ReadOnlyBooleanProperty getNeedsHistogramRebuild() {
return needsHistogramRebuild.getReadOnlyProperty();
}
synchronized public ReadOnlyBooleanProperty getCanAdvance() {
return historyManager.getCanAdvance();
}
synchronized public ReadOnlyBooleanProperty getCanRetreat() {
return historyManager.getCanRetreat();
}
private final ReadOnlyBooleanWrapper newEventsFlag = new ReadOnlyBooleanWrapper(false);
public TimeLineController() {
//initalize repository and filteredEvents on creation
eventsRepository = new EventsRepository(historyManager.currentState());
filteredEvents = eventsRepository.getEventsModel();
InitialZoomState = new ZoomParams(filteredEvents.getSpanningInterval(),
EventTypeZoomLevel.BASE_TYPE,
Filter.getDefaultFilter(),
DescriptionLOD.SHORT);
historyManager.advance(InitialZoomState);
//persistent listener instances
caseListener = new AutopsyCaseListener();
}
/** @return a shared events model */
public FilteredEventsModel getEventsModel() {
return filteredEvents;
}
public void applyDefaultFilters() {
pushFilters(Filter.getDefaultFilter());
}
public void zoomOutToActivity() {
Interval boundingEventsInterval = filteredEvents.getBoundingEventsInterval();
advance(filteredEvents.getRequestedZoomParamters().get().withTimeRange(boundingEventsInterval));
}
boolean rebuildRepo() {
if (IngestManager.getInstance().isIngestRunning()) {
//confirm timeline during ingest
if (showIngestConfirmation() != JOptionPane.YES_OPTION) {
return false;
}
}
LOGGER.log(Level.INFO, "Beginning generation of timeline");
try {
SwingUtilities.invokeLater(() -> {
if (mainFrame != null) {
mainFrame.close();
}
});
final SleuthkitCase sleuthkitCase = Case.getCurrentCase().getSleuthkitCase();
final long lastObjId = sleuthkitCase.getLastObjectId();
final long lastArtfID = getCaseLastArtifactID(sleuthkitCase);
final Boolean injestRunning = IngestManager.getInstance().isIngestRunning();
//TODO: verify this locking is correct? -jm
synchronized (eventsRepository) {
eventsRepository.rebuildRepository(() -> {
synchronized (eventsRepository) {
eventsRepository.recordLastObjID(lastObjId);
eventsRepository.recordLastArtifactID(lastArtfID);
eventsRepository.recordWasIngestRunning(injestRunning);
}
synchronized (TimeLineController.this) {
needsHistogramRebuild.set(true);
needsHistogramRebuild.set(false);
showWindow();
}
Platform.runLater(() -> {
newEventsFlag.set(false);
TimeLineController.this.showFullRange();
});
});
}
} catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Error when generating timeline, ", ex);
return false;
}
return true;
}
public void showFullRange() {
synchronized (filteredEvents) {
pushTimeRange(filteredEvents.getSpanningInterval());
}
}
synchronized public void closeTimeLine() {
if (mainFrame != null) {
listeningToAutopsy = false;
IngestManager.getInstance().removeIngestModuleEventListener(ingestModuleListener);
IngestManager.getInstance().removeIngestJobEventListener(ingestJobListener);
Case.removePropertyChangeListener(caseListener);
mainFrame.close();
mainFrame.setVisible(false);
mainFrame = null;
}
}
/** show the timeline window and prompt for rebuilding database */
synchronized void openTimeLine() {
// listen for case changes (specifically images being added, and case changes).
if (Case.isCaseOpen() && !listeningToAutopsy) {
IngestManager.getInstance().addIngestModuleEventListener(ingestModuleListener);
IngestManager.getInstance().addIngestJobEventListener(ingestJobListener);
Case.addPropertyChangeListener(caseListener);
listeningToAutopsy = true;
}
try {
long timeLineLastObjectId = eventsRepository.getLastObjID();
boolean rebuildingRepo = false;
if (timeLineLastObjectId == -1) {
rebuildingRepo = rebuildRepo();
}
if (rebuildingRepo == false
&& eventsRepository.getWasIngestRunning()) {
if (showLastPopulatedWhileIngestingConfirmation() == JOptionPane.YES_OPTION) {
rebuildingRepo = rebuildRepo();
}
}
final SleuthkitCase sleuthkitCase = Case.getCurrentCase().getSleuthkitCase();
if ((rebuildingRepo == false)
&& (sleuthkitCase.getLastObjectId() != timeLineLastObjectId
|| getCaseLastArtifactID(sleuthkitCase) != eventsRepository.getLastArtfactID())) {
rebuildingRepo = outOfDatePromptAndRebuild();
}
if (rebuildingRepo == false) {
showWindow();
showFullRange();
}
} catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Error when generating timeline, ", ex);
} catch (HeadlessException | MissingResourceException ex) {
LOGGER.log(Level.SEVERE, "Unexpected error when generating timeline, ", ex);
}
}
@SuppressWarnings("deprecation")
private long getCaseLastArtifactID(final SleuthkitCase sleuthkitCase) {
long caseLastArtfId = -1;
try (ResultSet runQuery = sleuthkitCase.runQuery("select Max(artifact_id) as max_id from blackboard_artifacts")) {
while (runQuery.next()) {
caseLastArtfId = runQuery.getLong("max_id");
}
sleuthkitCase.closeRunQuery(runQuery);
} catch (SQLException ex) {
Exceptions.printStackTrace(ex);
}
return caseLastArtfId;
}
/**
* request a time range the same length as the given period and centered
* around the middle of the currently selected range
*
* @param period
*/
synchronized public void pushPeriod(ReadablePeriod period) {
synchronized (filteredEvents) {
final DateTime middleOf = IntervalUtils.middleOf(filteredEvents.timeRange().get());
pushTimeRange(IntervalUtils.getIntervalAround(middleOf, period));
}
}
synchronized public void pushZoomOutTime() {
final Interval timeRange = filteredEvents.timeRange().get();
long toDurationMillis = timeRange.toDurationMillis() / 4;
DateTime start = timeRange.getStart().minus(toDurationMillis);
DateTime end = timeRange.getEnd().plus(toDurationMillis);
pushTimeRange(new Interval(start, end));
}
synchronized public void pushZoomInTime() {
final Interval timeRange = filteredEvents.timeRange().get();
long toDurationMillis = timeRange.toDurationMillis() / 4;
DateTime start = timeRange.getStart().plus(toDurationMillis);
DateTime end = timeRange.getEnd().minus(toDurationMillis);
pushTimeRange(new Interval(start, end));
}
synchronized public void setViewMode(VisualizationMode visualizationMode) {
if (viewMode.get() != visualizationMode) {
viewMode.set(visualizationMode);
}
}
public void selectEventIDs(Collection<Long> events) {
final LoggedTask<Interval> selectEventIDsTask = new LoggedTask<Interval>("Select Event IDs", true) {
@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);
}
} catch (InterruptedException ex) {
Logger.getLogger(FilteredEventsModel.class
.getName()).log(Level.SEVERE, getTitle() + " interrupted unexpectedly", ex);
} catch (ExecutionException ex) {
Logger.getLogger(FilteredEventsModel.class
.getName()).log(Level.SEVERE, getTitle() + " unexpectedly threw " + ex.getCause(), ex);
}
}
};
monitorTask(selectEventIDsTask);
}
/**
* private method to build gui if necessary and make it visible.
*/
synchronized private void showWindow() {
if (mainFrame == null) {
LOGGER.log(Level.WARNING, "Tried to show timeline with invalid window. Rebuilding GUI.");
mainFrame = (TimeLineTopComponent) WindowManager.getDefault().findTopComponent("TimeLineTopComponent");
if (mainFrame == null) {
mainFrame = new TimeLineTopComponent();
}
mainFrame.setController(this);
}
SwingUtilities.invokeLater(() -> {
mainFrame.open();
mainFrame.setVisible(true);
mainFrame.toFront();
});
}
synchronized public void pushEventTypeZoom(EventTypeZoomLevel typeZoomeLevel) {
ZoomParams currentZoom = filteredEvents.getRequestedZoomParamters().get();
if (currentZoom == null) {
advance(InitialZoomState.withTypeZoomLevel(typeZoomeLevel));
} else if (currentZoom.hasTypeZoomLevel(typeZoomeLevel) == false) {
advance(currentZoom.withTypeZoomLevel(typeZoomeLevel));
}
}
synchronized public void pushTimeRange(Interval timeRange) {
timeRange = this.filteredEvents.getSpanningInterval().overlap(timeRange);
ZoomParams currentZoom = filteredEvents.getRequestedZoomParamters().get();
if (currentZoom == null) {
advance(InitialZoomState.withTimeRange(timeRange));
} else if (currentZoom.hasTimeRange(timeRange) == false) {
advance(currentZoom.withTimeRange(timeRange));
}
}
synchronized public void pushDescrLOD(DescriptionLOD newLOD) {
ZoomParams currentZoom = filteredEvents.getRequestedZoomParamters().get();
if (currentZoom == null) {
advance(InitialZoomState.withDescrLOD(newLOD));
} else if (currentZoom.hasDescrLOD(newLOD) == false) {
advance(currentZoom.withDescrLOD(newLOD));
}
}
synchronized public void pushTimeAndType(Interval timeRange, EventTypeZoomLevel typeZoom) {
timeRange = this.filteredEvents.getSpanningInterval().overlap(timeRange);
ZoomParams currentZoom = filteredEvents.getRequestedZoomParamters().get();
if (currentZoom == null) {
advance(InitialZoomState.withTimeAndType(timeRange, typeZoom));
} else if (currentZoom.hasTimeRange(timeRange) == false && currentZoom.hasTypeZoomLevel(typeZoom) == false) {
advance(currentZoom.withTimeAndType(timeRange, typeZoom));
} else if (currentZoom.hasTimeRange(timeRange) == false) {
advance(currentZoom.withTimeRange(timeRange));
} else if (currentZoom.hasTypeZoomLevel(typeZoom) == false) {
advance(currentZoom.withTypeZoomLevel(typeZoom));
}
}
synchronized public void pushFilters(Filter filter) {
ZoomParams currentZoom = filteredEvents.getRequestedZoomParamters().get();
if (currentZoom == null) {
advance(InitialZoomState.withFilter(filter.copyOf()));
} else if (currentZoom.hasFilter(filter) == false) {
advance(currentZoom.withFilter(filter.copyOf()));
}
}
synchronized public ZoomParams advance() {
return historyManager.advance();
}
synchronized public ZoomParams retreat() {
return historyManager.retreat();
}
synchronized private void advance(ZoomParams newState) {
historyManager.advance(newState);
}
public void selectTimeAndType(Interval interval, EventType type) {
final Interval timeRange = filteredEvents.getSpanningInterval().overlap(interval);
final LoggedTask<Collection<Long>> selectTimeAndTypeTask = new LoggedTask<Collection<Long>>("Select Time and Type", true) {
@Override
protected Collection< Long> call() throws Exception {
synchronized (TimeLineController.this) {
return filteredEvents.getEventIDs(timeRange, new TypeFilter(type));
}
}
@Override
protected void succeeded() {
super.succeeded();
try {
synchronized (TimeLineController.this) {
selectedTimeRange.set(timeRange);
selectedEventIDs.setAll(get());
}
} catch (InterruptedException ex) {
Logger.getLogger(FilteredEventsModel.class
.getName()).log(Level.SEVERE, getTitle() + " interrupted unexpectedly", ex);
} catch (ExecutionException ex) {
Logger.getLogger(FilteredEventsModel.class
.getName()).log(Level.SEVERE, getTitle() + " unexpectedly threw " + ex.getCause(), ex);
}
}
};
monitorTask(selectTimeAndTypeTask);
}
synchronized public void monitorTask(final Task<?> task) {
if (task != null) {
Platform.runLater(() -> {
//is this actually threadsafe, could we get a finished task stuck in the list?
task.stateProperty().addListener((Observable observable) -> {
switch (task.getState()) {
case READY:
case RUNNING:
case SCHEDULED:
break;
case SUCCEEDED:
case CANCELLED:
case FAILED:
tasks.remove(task);
if (tasks.isEmpty() == false) {
progress.bind(tasks.get(0).progressProperty());
message.bind(tasks.get(0).messageProperty());
taskTitle.bind(tasks.get(0).titleProperty());
}
break;
}
});
tasks.add(task);
progress.bind(task.progressProperty());
message.bind(task.messageProperty());
taskTitle.bind(task.titleProperty());
switch (task.getState()) {
case READY:
executor.submit(task);
break;
case SCHEDULED:
case RUNNING:
case SUCCEEDED:
case CANCELLED:
case FAILED:
break;
}
});
}
}
static synchronized public void setTimeZone(TimeZone timeZone) {
TimeLineController.timeZone.set(timeZone);
}
Interval getSpanningInterval(Collection<Long> eventIDs) {
return filteredEvents.getSpanningInterval(eventIDs);
}
/**
* prompt the user to rebuild and then rebuild if the user chooses to
*/
synchronized public boolean outOfDatePromptAndRebuild() {
return showOutOfDateConfirmation() == JOptionPane.YES_OPTION
? rebuildRepo()
: false;
}
synchronized int showLastPopulatedWhileIngestingConfirmation() {
return JOptionPane.showConfirmDialog(mainFrame,
DO_REPOPULATE_MESSAGE,
"re populate events?",
JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE);
}
synchronized int showOutOfDateConfirmation() throws MissingResourceException, HeadlessException {
return JOptionPane.showConfirmDialog(mainFrame,
NbBundle.getMessage(TimeLineController.class,
"Timeline.propChg.confDlg.timelineOOD.msg"),
NbBundle.getMessage(TimeLineController.class,
"Timeline.propChg.confDlg.timelineOOD.details"),
JOptionPane.YES_NO_OPTION);
}
synchronized int showIngestConfirmation() throws MissingResourceException, HeadlessException {
return JOptionPane.showConfirmDialog(mainFrame,
NbBundle.getMessage(TimeLineController.class,
"Timeline.initTimeline.confDlg.genBeforeIngest.msg"),
NbBundle.getMessage(TimeLineController.class,
"Timeline.initTimeline.confDlg.genBeforeIngest.details"),
JOptionPane.YES_NO_OPTION);
}
private class AutopsyIngestModuleListener implements PropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent evt) {
switch (IngestManager.IngestModuleEvent.valueOf(evt.getPropertyName())) {
case CONTENT_CHANGED:
// ((ModuleContentEvent)evt.getOldValue())????
//ModuleContentEvent doesn't seem to provide any usefull information...
break;
case DATA_ADDED:
// Collection<BlackboardArtifact> artifacts = ((ModuleDataEvent) evt.getOldValue()).getArtifacts();
//new artifacts, insert them into db
break;
case FILE_DONE:
// Long fileID = (Long) evt.getOldValue();
//update file (known status) for file with id
Platform.runLater(() -> {
newEventsFlag.set(true);
});
break;
}
}
}
@Immutable
private class AutopsyIngestJobListener implements PropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent evt) {
switch (IngestManager.IngestJobEvent.valueOf(evt.getPropertyName())) {
case CANCELLED:
case COMPLETED:
//if we are doing incremental updates, drop this
outOfDatePromptAndRebuild();
break;
}
}
}
@Immutable
class AutopsyCaseListener implements PropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent evt) {
switch (Case.Events.valueOf(evt.getPropertyName())) {
case DATA_SOURCE_ADDED:
// Content content = (Content) evt.getNewValue();
//if we are doing incremental updates, drop this
outOfDatePromptAndRebuild();
break;
case CURRENT_CASE:
OpenTimelineAction.invalidateController();
closeTimeLine();
break;
}
}
}
}

View File

@ -0,0 +1,33 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.timeline;
/**
*
*/
public class TimeLineException extends Exception {
public TimeLineException(String string, Exception e) {
super(string, e);
}
public TimeLineException(String string) {
super(string);
}
}

View File

@ -1,19 +1,6 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JFrameFormInfo">
<Properties>
<Property name="defaultCloseOperation" type="int" value="2"/>
<Property name="title" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/timeline/Bundle.properties" key="TimelineFrame.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[1200, 700]"/>
</Property>
</Properties>
<SyntheticProperties>
<SyntheticProperty name="formSizePolicy" type="int" value="1"/>
<SyntheticProperty name="generateCenter" type="boolean" value="false"/>
</SyntheticProperties>
<Form version="1.5" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
@ -29,21 +16,37 @@
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="splitYPane" alignment="1" max="32767" attributes="0"/>
<Component id="splitYPane" alignment="0" pref="972" max="32767" attributes="0"/>
<Component id="jFXstatusPanel" alignment="0" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="splitYPane" alignment="1" max="32767" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Component id="splitYPane" pref="482" max="32767" attributes="0"/>
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
<Component id="jFXstatusPanel" min="-2" pref="29" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javafx.embed.swing.JFXPanel" name="jFXstatusPanel">
<Properties>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[100, 16]"/>
</Property>
</Properties>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout">
<Property name="useNullLayout" type="boolean" value="true"/>
</Layout>
</Container>
<Container class="javax.swing.JSplitPane" name="splitYPane">
<Properties>
<Property name="dividerLocation" type="int" value="420"/>
<Property name="orientation" type="int" value="0"/>
<Property name="resizeWeight" type="double" value="0.5"/>
<Property name="resizeWeight" type="double" value="0.9"/>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[1024, 400]"/>
</Property>
@ -51,21 +54,18 @@
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
<SubComponents>
<Container class="javax.swing.JPanel" name="topPane">
<Properties>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[1200, 400]"/>
</Property>
</Properties>
<Container class="javafx.embed.swing.JFXPanel" name="jFXVizPanel">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
<JSplitPaneConstraints position="left"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout">
<Property name="useNullLayout" type="boolean" value="true"/>
</Layout>
</Container>
<Container class="javax.swing.JSplitPane" name="splitXPane">
<Container class="javax.swing.JSplitPane" name="lowerSplitXPane">
<Properties>
<Property name="dividerLocation" type="int" value="600"/>
<Property name="resizeWeight" type="double" value="0.5"/>
@ -82,7 +82,7 @@
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
<SubComponents>
<Container class="javax.swing.JPanel" name="leftPane">
<Container class="javax.swing.JPanel" name="resultContainerPanel">
<Properties>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[700, 300]"/>
@ -96,7 +96,7 @@
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
</Container>
<Container class="javax.swing.JPanel" name="rightPane">
<Container class="javax.swing.JPanel" name="contentViewerContainerPanel">
<Properties>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[500, 300]"/>

View File

@ -0,0 +1,287 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2013 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.timeline;
import java.awt.BorderLayout;
import java.util.Collections;
import java.util.List;
import javafx.application.Platform;
import javafx.beans.Observable;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.SplitPane;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import org.netbeans.api.settings.ConvertAsProperties;
import org.openide.explorer.ExplorerManager;
import org.openide.explorer.ExplorerUtils;
import org.openide.util.NbBundle.Messages;
import org.openide.windows.Mode;
import org.openide.windows.TopComponent;
import static org.openide.windows.TopComponent.PROP_UNDOCKING_DISABLED;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.corecomponents.DataContentPanel;
import org.sleuthkit.autopsy.corecomponents.DataResultPanel;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.timeline.actions.Back;
import org.sleuthkit.autopsy.timeline.actions.Forward;
import org.sleuthkit.autopsy.timeline.ui.StatusBar;
import org.sleuthkit.autopsy.timeline.ui.TimeLineResultView;
import org.sleuthkit.autopsy.timeline.ui.TimeZonePanel;
import org.sleuthkit.autopsy.timeline.ui.VisualizationPanel;
import org.sleuthkit.autopsy.timeline.ui.detailview.tree.NavPanel;
import org.sleuthkit.autopsy.timeline.ui.filtering.FilterSetPanel;
import org.sleuthkit.autopsy.timeline.zooming.ZoomSettingsPane;
/**
* TopComponent for the timeline feature.
*/
@ConvertAsProperties(
dtd = "-//org.sleuthkit.autopsy.timeline//TimeLine//EN",
autostore = false)
@TopComponent.Description(
preferredID = "TimeLineTopComponent",
//iconBase="SET/PATH/TO/ICON/HERE",
persistenceType = TopComponent.PERSISTENCE_NEVER)
@TopComponent.Registration(mode = "timeline", openAtStartup = false)
@Messages({
"CTL_TimeLineTopComponentAction=TimeLineTopComponent",
"CTL_TimeLineTopComponent=Timeline Window",
"HINT_TimeLineTopComponent=This is a Timeline window"
})
public final class TimeLineTopComponent extends TopComponent implements ExplorerManager.Provider, TimeLineUI {
private static final Logger LOGGER = Logger.getLogger(TimeLineTopComponent.class.getName());
private DataContentPanel dataContentPanel;
private TimeLineResultView tlrv;
private final ExplorerManager em = new ExplorerManager();
private TimeLineController controller;
////jfx componenets that make up the interface
private final FilterSetPanel filtersPanel = new FilterSetPanel();
private final Tab eventsTab = new Tab("Events");
private final Tab filterTab = new Tab("Filters");
private final VBox leftVBox = new VBox(5);
private final NavPanel navPanel = new NavPanel();
private final StatusBar statusBar = new StatusBar();
private final TabPane tabPane = new TabPane();
private final ZoomSettingsPane zoomSettingsPane = new ZoomSettingsPane();
private final VisualizationPanel visualizationPanel = new VisualizationPanel(navPanel);
private final SplitPane splitPane = new SplitPane();
private final TimeZonePanel timeZonePanel = new TimeZonePanel();
public TimeLineTopComponent() {
initComponents();
associateLookup(ExplorerUtils.createLookup(em, getActionMap()));
setName(Bundle.CTL_TimeLineTopComponent());
setToolTipText(Bundle.HINT_TimeLineTopComponent());
setIcon(WindowManager.getDefault().getMainWindow().getIconImage()); //use the same icon as main application
customizeComponents();
}
synchronized private void customizeComponents() {
dataContentPanel = DataContentPanel.createInstance();
this.contentViewerContainerPanel.add(dataContentPanel, BorderLayout.CENTER);
tlrv = new TimeLineResultView(dataContentPanel);
DataResultPanel dataResultPanel = tlrv.getDataResultPanel();
this.resultContainerPanel.add(dataResultPanel, BorderLayout.CENTER);
dataResultPanel.open();
Platform.runLater(() -> {
//assemble ui componenets together
jFXstatusPanel.setScene(new Scene(statusBar));
splitPane.setDividerPositions(0);
jFXVizPanel.setScene(new Scene(splitPane));
filterTab.setClosable(false);
filterTab.setContent(filtersPanel);
filterTab.setGraphic(new ImageView("org/sleuthkit/autopsy/timeline/images/funnel.png"));
eventsTab.setClosable(false);
eventsTab.setContent(navPanel);
eventsTab.setGraphic(new ImageView("org/sleuthkit/autopsy/timeline/images/timeline_marker.png"));
tabPane.getTabs().addAll(filterTab, eventsTab);
VBox.setVgrow(tabPane, Priority.ALWAYS);
VBox.setVgrow(timeZonePanel, Priority.SOMETIMES);
leftVBox.getChildren().addAll(timeZonePanel, zoomSettingsPane, tabPane);
SplitPane.setResizableWithParent(leftVBox, Boolean.FALSE);
splitPane.getItems().addAll(leftVBox, visualizationPanel);
});
}
public synchronized void setController(TimeLineController controller) {
this.controller = controller;
tlrv.setController(controller);
Platform.runLater(() -> {
jFXVizPanel.getScene().addEventFilter(KeyEvent.KEY_PRESSED, (
KeyEvent event) -> {
if (new KeyCodeCombination(KeyCode.LEFT, KeyCodeCombination.ALT_DOWN).match(event)) {
new Back(controller).handle(new ActionEvent());
} else if (new KeyCodeCombination(KeyCode.BACK_SPACE).match(event)) {
new Back(controller).handle(new ActionEvent());
} else if (new KeyCodeCombination(KeyCode.RIGHT, KeyCodeCombination.ALT_DOWN).match(event)) {
new Forward(controller).handle(new ActionEvent());
} else if (new KeyCodeCombination(KeyCode.BACK_SPACE, KeyCodeCombination.SHIFT_DOWN).match(event)) {
new Forward(controller).handle(new ActionEvent());
}
});
controller.getViewMode().addListener((Observable observable) -> {
if (controller.getViewMode().get().equals(VisualizationMode.COUNTS)) {
tabPane.getSelectionModel().select(filterTab);
}
});
eventsTab.disableProperty().bind(controller.getViewMode().isEqualTo(VisualizationMode.COUNTS));
visualizationPanel.setController(controller);
navPanel.setController(controller);
filtersPanel.setController(controller);
zoomSettingsPane.setController(controller);
statusBar.setController(controller);
});
}
@Override
public List<Mode> availableModes(List<Mode> modes) {
return Collections.emptyList();
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
jFXstatusPanel = new javafx.embed.swing.JFXPanel();
splitYPane = new javax.swing.JSplitPane();
jFXVizPanel = new javafx.embed.swing.JFXPanel();
lowerSplitXPane = new javax.swing.JSplitPane();
resultContainerPanel = new javax.swing.JPanel();
contentViewerContainerPanel = new javax.swing.JPanel();
jFXstatusPanel.setPreferredSize(new java.awt.Dimension(100, 16));
splitYPane.setDividerLocation(420);
splitYPane.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);
splitYPane.setResizeWeight(0.9);
splitYPane.setPreferredSize(new java.awt.Dimension(1024, 400));
splitYPane.setLeftComponent(jFXVizPanel);
lowerSplitXPane.setDividerLocation(600);
lowerSplitXPane.setResizeWeight(0.5);
lowerSplitXPane.setPreferredSize(new java.awt.Dimension(1200, 300));
lowerSplitXPane.setRequestFocusEnabled(false);
resultContainerPanel.setPreferredSize(new java.awt.Dimension(700, 300));
resultContainerPanel.setLayout(new java.awt.BorderLayout());
lowerSplitXPane.setLeftComponent(resultContainerPanel);
contentViewerContainerPanel.setPreferredSize(new java.awt.Dimension(500, 300));
contentViewerContainerPanel.setLayout(new java.awt.BorderLayout());
lowerSplitXPane.setRightComponent(contentViewerContainerPanel);
splitYPane.setRightComponent(lowerSplitXPane);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(splitYPane, javax.swing.GroupLayout.DEFAULT_SIZE, 972, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup()
.addGap(0, 0, 0)
.addComponent(jFXstatusPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGap(0, 0, 0))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(splitYPane, javax.swing.GroupLayout.DEFAULT_SIZE, 482, Short.MAX_VALUE)
.addGap(0, 0, 0)
.addComponent(jFXstatusPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 29, javax.swing.GroupLayout.PREFERRED_SIZE))
);
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JPanel contentViewerContainerPanel;
private javafx.embed.swing.JFXPanel jFXVizPanel;
private javafx.embed.swing.JFXPanel jFXstatusPanel;
private javax.swing.JSplitPane lowerSplitXPane;
private javax.swing.JPanel resultContainerPanel;
private javax.swing.JSplitPane splitYPane;
// End of variables declaration//GEN-END:variables
@Override
public void componentOpened() {
WindowManager.getDefault().setTopComponentFloating(this, true);
putClientProperty(PROP_UNDOCKING_DISABLED, true);
}
@Override
public void componentClosed() {
// TODO add custom code on component closing
}
void writeProperties(java.util.Properties p) {
// better to version settings since initial version as advocated at
// http://wiki.apidesign.org/wiki/PropertyFiles
p.setProperty("version", "1.0");
// TODO store your settings
}
void readProperties(java.util.Properties p) {
String version = p.getProperty("version");
// TODO read your settings according to their version
}
@Override
public ExplorerManager getExplorerManager() {
return em;
}
}

View File

@ -0,0 +1,27 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.timeline;
/**
*
*/
public interface TimeLineUI {
void setController(TimeLineController controller);
}

View File

@ -0,0 +1,35 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2013 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.timeline;
/** Interface to be implemented by views of the data.
*
* Most implementations should install the relevant listeners in their
* {@link #setController} and {@link #setModel} methods */
import org.sleuthkit.autopsy.timeline.events.FilteredEventsModel;
public interface TimeLineView extends TimeLineUI {
@Override
void setController(TimeLineController controller);
void setModel(final FilteredEventsModel filteredEvents);
}

File diff suppressed because it is too large Load Diff

View File

@ -1,169 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2013 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.timeline;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Image;
/**
*
* Ready timeline frame with layout to hold dynamic components
*/
class TimelineFrame extends javax.swing.JFrame {
/**
* Creates new form TimelineFrame
*/
public TimelineFrame() {
initComponents();
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
splitYPane = new javax.swing.JSplitPane();
topPane = new javax.swing.JPanel();
splitXPane = new javax.swing.JSplitPane();
leftPane = new javax.swing.JPanel();
rightPane = new javax.swing.JPanel();
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
setTitle(org.openide.util.NbBundle.getMessage(TimelineFrame.class, "TimelineFrame.title")); // NOI18N
setPreferredSize(new java.awt.Dimension(1200, 700));
splitYPane.setDividerLocation(420);
splitYPane.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);
splitYPane.setResizeWeight(0.5);
splitYPane.setPreferredSize(new java.awt.Dimension(1024, 400));
topPane.setPreferredSize(new java.awt.Dimension(1200, 400));
topPane.setLayout(new java.awt.BorderLayout());
splitYPane.setLeftComponent(topPane);
splitXPane.setDividerLocation(600);
splitXPane.setResizeWeight(0.5);
splitXPane.setPreferredSize(new java.awt.Dimension(1200, 300));
splitXPane.setRequestFocusEnabled(false);
leftPane.setPreferredSize(new java.awt.Dimension(700, 300));
leftPane.setLayout(new java.awt.BorderLayout());
splitXPane.setLeftComponent(leftPane);
rightPane.setPreferredSize(new java.awt.Dimension(500, 300));
rightPane.setLayout(new java.awt.BorderLayout());
splitXPane.setRightComponent(rightPane);
splitYPane.setRightComponent(splitXPane);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(splitYPane, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(splitYPane, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
);
pack();
}// </editor-fold>//GEN-END:initComponents
void setTopPanel(Component component) {
this.topPane.add(component, BorderLayout.CENTER);
}
void setBottomLeftPanel(Component component) {
this.leftPane.add(component, BorderLayout.CENTER);
}
void setBottomRightPanel(Component component) {
this.rightPane.add(component, BorderLayout.CENTER);
}
void setFrameName(String name) {
this.setTitle(name);
}
void setFrameIcon(Image iconImage) {
this.setIconImage(iconImage);
}
void setFrameSize(Dimension size) {
this.setSize(size);
}
void setTopComponentCursor(Cursor cursor) {
this.topPane.setCursor(cursor);
}
/**
* @param args the command line arguments
*/
public static void main(String args[]) {
/* Set the Nimbus look and feel */
//<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
/* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
* For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
*/
try {
for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) { //NON-NLS
javax.swing.UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (ClassNotFoundException ex) {
java.util.logging.Logger.getLogger(TimelineFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (InstantiationException ex) {
java.util.logging.Logger.getLogger(TimelineFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
java.util.logging.Logger.getLogger(TimelineFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (javax.swing.UnsupportedLookAndFeelException ex) {
java.util.logging.Logger.getLogger(TimelineFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
//</editor-fold>
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new TimelineFrame().setVisible(true);
}
});
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JPanel leftPane;
private javax.swing.JPanel rightPane;
private javax.swing.JSplitPane splitXPane;
private javax.swing.JSplitPane splitYPane;
private javax.swing.JPanel topPane;
// End of variables declaration//GEN-END:variables
}

View File

@ -1,184 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2013 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.timeline;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.KeyStroke;
import org.openide.util.NbBundle;
import org.openide.windows.WindowManager;
/**
* Dialog with progress bar that pops up when timeline is being generated
*/
class TimelineProgressDialog extends javax.swing.JDialog {
/**
* A return status code - returned if Cancel button has been pressed
*/
public static final int RET_CANCEL = 0;
/**
* A return status code - returned if OK button has been pressed
*/
public static final int RET_OK = 1;
/**
* Creates new form TimelineProgressDialog
*/
public TimelineProgressDialog(java.awt.Frame parent, boolean modal) {
super(parent, modal);
initComponents();
setLocationRelativeTo(null);
//set icon the same as main app
setIconImage(WindowManager.getDefault().getMainWindow().getIconImage());
//progressBar.setIndeterminate(true);
setName(NbBundle.getMessage(this.getClass(), "TimelineProgressDialog.setName.text"));
// Close the dialog when Esc is pressed
String cancelName = "cancel"; //NON-NLS
InputMap inputMap = getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), cancelName);
ActionMap actionMap = getRootPane().getActionMap();
actionMap.put(cancelName, new AbstractAction() {
public void actionPerformed(ActionEvent e) {
doClose(RET_CANCEL);
}
});
}
/**
* @return the return status of this dialog - one of RET_OK or RET_CANCEL
*/
public int getReturnStatus() {
return returnStatus;
}
void updateProgressBar(final int progress) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
progressBar.setValue(progress);
}
});
}
void updateProgressBar(final String message) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
progressBar.setString(message);
}
});
}
void setProgressTotal(final int total) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
//progressBar.setIndeterminate(false);
progressBar.setMaximum(total);
//progressBar.setValue(0);
progressBar.setStringPainted(true);
progressBar.setVisible(true);
}
});
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
jLabel1 = new javax.swing.JLabel();
progressBar = new javax.swing.JProgressBar();
addWindowListener(new java.awt.event.WindowAdapter() {
public void windowClosing(java.awt.event.WindowEvent evt) {
closeDialog(evt);
}
});
org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(TimelineProgressDialog.class, "TimelineProgressDialog.jLabel1.text")); // NOI18N
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(progressBar, javax.swing.GroupLayout.DEFAULT_SIZE, 504, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup()
.addComponent(jLabel1)
.addGap(0, 0, Short.MAX_VALUE)))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(jLabel1)
.addGap(7, 7, 7)
.addComponent(progressBar, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(16, Short.MAX_VALUE))
);
pack();
}// </editor-fold>//GEN-END:initComponents
/**
* Closes the dialog
*/
private void closeDialog(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_closeDialog
doClose(RET_CANCEL);
}//GEN-LAST:event_closeDialog
void doClose(final int retStatus) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
returnStatus = retStatus;
setVisible(false);
dispose();
}
});
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel jLabel1;
private javax.swing.JProgressBar progressBar;
// End of variables declaration//GEN-END:variables
private int returnStatus = RET_CANCEL;
}

View File

@ -0,0 +1,27 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2013 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.timeline;
/**
*
*/
public enum VisualizationMode {
COUNTS, DETAIL;
}

View File

@ -0,0 +1,51 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.timeline.actions;
import javafx.event.ActionEvent;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
import org.controlsfx.control.action.AbstractAction;
import org.sleuthkit.autopsy.timeline.TimeLineController;
/**
*
*/
//TODO: This and the corresponding imageanalyzer action are identical except for the type of the controller... abstract something! -jm
public class Back extends AbstractAction {
private static final Image BACK_IMAGE = new Image("/org/sleuthkit/autopsy/timeline/images/arrow-180.png", 16, 16, true, true, true);
private final TimeLineController controller;
public Back(TimeLineController controller) {
super("Back");
setGraphic(new ImageView(BACK_IMAGE));
setAccelerator(new KeyCodeCombination(KeyCode.LEFT, KeyCodeCombination.ALT_DOWN));
this.controller = controller;
disabledProperty().bind(controller.getCanRetreat().not());
}
@Override
public void handle(ActionEvent ae) {
controller.retreat();
}
}

View File

@ -0,0 +1,57 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.timeline.actions;
import javafx.beans.binding.BooleanBinding;
import javafx.event.ActionEvent;
import org.controlsfx.control.action.AbstractAction;
import org.sleuthkit.autopsy.timeline.TimeLineController;
import org.sleuthkit.autopsy.timeline.events.FilteredEventsModel;
import org.sleuthkit.autopsy.timeline.filters.Filter;
/**
*
*/
public class DefaultFilters extends AbstractAction {
private final TimeLineController controller;
private FilteredEventsModel eventsModel;
public DefaultFilters(final TimeLineController controller) {
super("apply default filters");
this.controller = controller;
eventsModel = controller.getEventsModel();
disabledProperty().bind(new BooleanBinding() {
{
bind(eventsModel.getRequestedZoomParamters());
}
@Override
protected boolean computeValue() {
return eventsModel.getRequestedZoomParamters().getValue().getFilter().equals(Filter.getDefaultFilter());
}
});
}
@Override
public void handle(ActionEvent event) {
controller.applyDefaultFilters();
}
}

View File

@ -0,0 +1,51 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.timeline.actions;
import javafx.event.ActionEvent;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
import org.controlsfx.control.action.AbstractAction;
import org.sleuthkit.autopsy.timeline.TimeLineController;
/**
*
*/
//TODO: This and the corresponding imageanalyzer action are identical except for the type of the controller... abstract something! -jm
public class Forward extends AbstractAction {
private static final Image BACK_IMAGE = new Image("/org/sleuthkit/autopsy/timeline/images/arrow.png", 16, 16, true, true, true);
private final TimeLineController controller;
public Forward(TimeLineController controller) {
super("Forward");
setGraphic(new ImageView(BACK_IMAGE));
setAccelerator(new KeyCodeCombination(KeyCode.RIGHT, KeyCodeCombination.ALT_DOWN));
this.controller = controller;
disabledProperty().bind(controller.getCanAdvance().not());
}
@Override
public void handle(ActionEvent ae) {
controller.advance();
}
}

View File

@ -0,0 +1,134 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.timeline.actions;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import javafx.embed.swing.SwingFXUtils;
import javafx.event.ActionEvent;
import javafx.scene.image.WritableImage;
import javafx.stage.DirectoryChooser;
import javafx.util.Pair;
import javax.imageio.ImageIO;
import org.controlsfx.control.action.AbstractAction;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.timeline.TimeLineController;
import org.sleuthkit.autopsy.timeline.zooming.ZoomParams;
import org.sleuthkit.datamodel.TskCoreException;
/**
*/
public class SaveSnapshot extends AbstractAction {
private static final String HTML_EXT = ".html";
private static final String REPORT_IMAGE_EXTENSION = ".png";
private static final Logger LOGGER = Logger.getLogger(SaveSnapshot.class.getName());
private final TimeLineController controller;
private final WritableImage snapshot;
public SaveSnapshot(TimeLineController controller, WritableImage snapshot) {
super("save snapshot");
this.controller = controller;
this.snapshot = snapshot;
}
@Override
public void handle(ActionEvent event) {
//choose location/name
DirectoryChooser fileChooser = new DirectoryChooser();
fileChooser.setTitle("Save snapshot to");
fileChooser.setInitialDirectory(new File(Case.getCurrentCase().getCaseDirectory() + File.separator + "Reports"));
File outFolder = fileChooser.showDialog(null);
if (outFolder == null) {
return;
}
outFolder.mkdir();
String name = outFolder.getName();
//gather metadata
List<Pair<String, String>> reportMetaData = new ArrayList<>();
reportMetaData.add(new Pair<>("Case", Case.getCurrentCase().getName()));
ZoomParams get = controller.getEventsModel().getRequestedZoomParamters().get();
reportMetaData.add(new Pair<>("Time Range", get.getTimeRange().toString()));
reportMetaData.add(new Pair<>("Description Level of Detail", get.getDescrLOD().getDisplayName()));
reportMetaData.add(new Pair<>("Event Type Zoom Level", get.getTypeZoomLevel().getDisplayName()));
reportMetaData.add(new Pair<>("Filters", get.getFilter().getHTMLReportString()));
//save snapshot as png
try {
ImageIO.write(SwingFXUtils.fromFXImage(snapshot, null), "png", new File(outFolder.getPath() + File.separator + outFolder.getName() + REPORT_IMAGE_EXTENSION));
} catch (IOException ex) {
LOGGER.log(Level.WARNING, "failed to write snapshot to disk", ex);
return;
}
//build html string
StringBuilder wrapper = new StringBuilder();
wrapper.append("<html>\n<head>\n\t<title>").append("timeline snapshot").append("</title>\n\t<link rel=\"stylesheet\" type=\"text/css\" href=\"index.css\" />\n</head>\n<body>\n");
wrapper.append("<div id=\"content\">\n<h1>").append(outFolder.getName()).append("</h1>\n");
wrapper.append("<img src = \"").append(outFolder.getName()).append(REPORT_IMAGE_EXTENSION + "\" alt = \"snaphot\">");
wrapper.append("<table>\n");
for (Pair<String, String> pair : reportMetaData) {
wrapper.append("<tr><td>").append(pair.getKey()).append(": </td><td>").append(pair.getValue()).append("</td></tr>\n");
}
wrapper.append("</table>\n");
wrapper.append("</div>\n</body>\n</html>");
//write html wrapper
try (Writer htmlWriter = new FileWriter(new File(outFolder, name + HTML_EXT))) {
htmlWriter.write(wrapper.toString());
} catch (FileNotFoundException ex) {
LOGGER.log(Level.WARNING, "failed to open html wrapper file for writing ", ex);
return;
} catch (IOException ex) {
LOGGER.log(Level.WARNING, "failed to write html wrapper file", ex);
return;
}
//copy css
try (InputStream resource = this.getClass().getResourceAsStream("/org/sleuthkit/autopsy/timeline/index.css")) {
Files.copy(resource, Paths.get(outFolder.getPath(), "index.css"));
} catch (IOException ex) {
LOGGER.log(Level.WARNING, "failed to copy css file", ex);
}
//add html file as report to case
try {
Case.getCurrentCase().addReport(outFolder.getPath() + File.separator + outFolder.getName() + HTML_EXT, "Timeline", outFolder.getName() + HTML_EXT);
} catch (TskCoreException ex) {
LOGGER.log(Level.WARNING, "failed add html wrapper as a report", ex);
}
}
}

View File

@ -0,0 +1,56 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.timeline.actions;
import javafx.beans.binding.BooleanBinding;
import javafx.event.ActionEvent;
import org.controlsfx.control.action.AbstractAction;
import org.sleuthkit.autopsy.timeline.TimeLineController;
import org.sleuthkit.autopsy.timeline.events.FilteredEventsModel;
/**
*
*/
public class ZoomOut extends AbstractAction {
private final TimeLineController controller;
private final FilteredEventsModel eventsModel;
public ZoomOut(final TimeLineController controller) {
super("apply default filters");
this.controller = controller;
eventsModel = controller.getEventsModel();
disabledProperty().bind(new BooleanBinding() {
{
bind(eventsModel.getRequestedZoomParamters());
}
@Override
protected boolean computeValue() {
return eventsModel.getRequestedZoomParamters().getValue().getTimeRange().contains(eventsModel.getSpanningInterval());
}
});
}
@Override
public void handle(ActionEvent event) {
controller.zoomOutToActivity();
}
}

View File

@ -0,0 +1,114 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2013 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.timeline.events;
import com.google.common.collect.Collections2;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.annotation.concurrent.Immutable;
import org.joda.time.Interval;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.timeline.events.type.EventType;
import org.sleuthkit.autopsy.timeline.utils.IntervalUtils;
import org.sleuthkit.autopsy.timeline.zooming.DescriptionLOD;
/** An event that represent a set of other events aggregated together. All the
* sub events should have the same type and matching descriptions at the
* designated 'zoom level'.
*/
@Immutable
public class AggregateEvent {
final private Interval span;
final private EventType type;
final private Set<Long> eventIDs;
final private String description;
private final DescriptionLOD lod;
public AggregateEvent(Interval spanningInterval, EventType type, Set<Long> eventIDs, String description, DescriptionLOD lod) {
this.span = spanningInterval;
this.type = type;
this.description = description;
this.eventIDs = eventIDs;
this.lod = lod;
}
public AggregateEvent(Interval spanningInterval, EventType type, List<String> events, String description, DescriptionLOD lod) {
this.span = spanningInterval;
this.type = type;
this.description = description;
this.eventIDs = new HashSet<>(Collections2.transform(events, Long::valueOf));
this.lod = lod;
}
/** @return the actual interval from the first event to the last event */
public Interval getSpan() {
return span;
}
public Set<Long> getEventIDs() {
return Collections.unmodifiableSet(eventIDs);
}
public String getDescription() {
return description;
}
public EventType getType() {
return type;
}
/**
* merge two aggregate events into one new aggregate event.
*
* @param ag1
* @param ag2
*
* @return
*/
public static AggregateEvent merge(AggregateEvent ag1, AggregateEvent ag2) {
if (ag1.getType() != ag2.getType()) {
throw new IllegalArgumentException(NbBundle.getMessage(AggregateEvent.class, "AggregateEvent.differentTypes"));
}
if (!ag1.getDescription().equals(ag2.getDescription())) {
throw new IllegalArgumentException(NbBundle.getMessage(AggregateEvent.class, "AggregateEvent.differentDescriptions"));
}
HashSet<Long> ids = new HashSet<>(ag1.getEventIDs());
ids.addAll(ag2.getEventIDs());
//TODO: check that types/descriptions are actually the same -jm
return new AggregateEvent(IntervalUtils.span(ag1.span, ag2.span), ag1.getType(), ids, ag1.getDescription(), ag1.lod);
}
DescriptionLOD getLOD() {
return lod;
}
}

View File

@ -0,0 +1,262 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.timeline.events;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javafx.beans.Observable;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javax.annotation.concurrent.GuardedBy;
import org.joda.time.DateTimeZone;
import org.joda.time.Interval;
import org.sleuthkit.autopsy.timeline.TimeLineView;
import org.sleuthkit.autopsy.timeline.events.db.EventsRepository;
import org.sleuthkit.autopsy.timeline.events.type.EventType;
import org.sleuthkit.autopsy.timeline.filters.Filter;
import org.sleuthkit.autopsy.timeline.filters.IntersectionFilter;
import org.sleuthkit.autopsy.timeline.zooming.DescriptionLOD;
import org.sleuthkit.autopsy.timeline.zooming.EventTypeZoomLevel;
import org.sleuthkit.autopsy.timeline.zooming.ZoomParams;
/**
* This class acts as the model for a {@link TimeLineView}
*
* Views can register listeners on properties returned by methods.
*
* This class is implemented as a filtered view into an underlying
* {@link EventsRepository}.
*
* TODO: as many methods as possible should cache their results so as to avoid
* unnecessary db calls through the {@link EventsRepository} -jm
*
* Concurrency Policy: repo is internally synchronized, so methods that only
* access the repo atomicaly do not need further synchronization
*
* all other member state variables should only be accessed with intrinsic lock
* of containing FilteredEventsModel held. Many methods delegate to a task
* submitted to the dbQueryThread executor. These methods should synchronize on
* this object, and the tasks should too. Since the tasks execute asynchronously
* from the invoking methods, the methods will return and release the lock for
* the tasks to obtain.
*
*/
public class FilteredEventsModel {
/**
* time range that spans the filtered events
*/
//requested time range, filter, event_type zoom, and description level of detail. if specifics are not passed to methods, the values of these members are used to query repository.
@GuardedBy("this")
private final ReadOnlyObjectWrapper<Interval> requestedTimeRange = new ReadOnlyObjectWrapper<>();
@GuardedBy("this")
private final ReadOnlyObjectWrapper<Filter> requestedFilter = new ReadOnlyObjectWrapper<>(Filter.getDefaultFilter());
@GuardedBy("this")
private final ReadOnlyObjectWrapper< EventTypeZoomLevel> requestedTypeZoom = new ReadOnlyObjectWrapper<>(EventTypeZoomLevel.BASE_TYPE);
@GuardedBy("this")
private final ReadOnlyObjectWrapper< DescriptionLOD> requestedLOD = new ReadOnlyObjectWrapper<>(DescriptionLOD.SHORT);
@GuardedBy("this")
private final ReadOnlyObjectWrapper<ZoomParams> requestedZoomParamters = new ReadOnlyObjectWrapper<>();
/**
* The underlying repo for events. Atomic access to repo is synchronized
* internally, but compound access should be done with the intrinsic lock of
* this FilteredEventsModel object
*/
@GuardedBy("this")
private final EventsRepository repo;
public FilteredEventsModel(EventsRepository repo, ReadOnlyObjectProperty<ZoomParams> currentStateProperty) {
this.repo = repo;
requestedZoomParamters.addListener((Observable observable) -> {
final ZoomParams zoomParams = requestedZoomParamters.get();
if (zoomParams != null) {
if (zoomParams.getTypeZoomLevel().equals(requestedTypeZoom.get()) == false
|| zoomParams.getDescrLOD().equals(requestedLOD.get()) == false
|| zoomParams.getFilter().equals(requestedFilter.get()) == false
|| zoomParams.getTimeRange().equals(requestedTimeRange.get()) == false) {
requestedTypeZoom.set(zoomParams.getTypeZoomLevel());
requestedFilter.set(zoomParams.getFilter().copyOf());
requestedTimeRange.set(zoomParams.getTimeRange());
requestedLOD.set(zoomParams.getDescrLOD());
}
}
});
requestedZoomParamters.bind(currentStateProperty);
}
public Interval getBoundingEventsInterval() {
return repo.getBoundingEventsInterval(getRequestedZoomParamters().get().getTimeRange(), getRequestedZoomParamters().get().getFilter());
}
synchronized public ReadOnlyObjectProperty<ZoomParams> getRequestedZoomParamters() {
return requestedZoomParamters.getReadOnlyProperty();
}
public TimeLineEvent getEventById(Long eventID) {
return repo.getEventById(eventID);
}
public Set<Long> getEventIDs(Interval timeRange, Filter filter) {
final Interval overlap;
final IntersectionFilter intersect;
synchronized (this) {
overlap = getSpanningInterval().overlap(timeRange);
intersect = Filter.intersect(new Filter[]{filter, requestedFilter.get()});
}
return repo.getEventIDs(overlap, intersect);
}
/**
* return the number of events that pass the requested filter and are within
* the given time range.
*
* NOTE: this method does not change the requested time range
*
* @param timeRange
*
* @return
*/
public Map<EventType, Long> getEventCounts(Interval timeRange) {
final Filter filter;
final EventTypeZoomLevel typeZoom;
synchronized (this) {
filter = requestedFilter.get();
typeZoom = requestedTypeZoom.get();
}
return repo.countEvents(new ZoomParams(timeRange, typeZoom, filter, null));
}
/**
* @return a read only view of the time range requested via
* {@link #requestTimeRange(org.joda.time.Interval)}
*/
synchronized public ReadOnlyObjectProperty<Interval> timeRange() {
if (requestedTimeRange.get() == null) {
requestedTimeRange.set(getSpanningInterval());
}
return requestedTimeRange.getReadOnlyProperty();
}
synchronized public ReadOnlyObjectProperty<DescriptionLOD> descriptionLOD() {
return requestedLOD.getReadOnlyProperty();
}
synchronized public ReadOnlyObjectProperty<Filter> filter() {
return requestedFilter.getReadOnlyProperty();
}
/**
* @return the smallest interval spanning all the events from the
* repository, ignoring any filters or requested ranges
*/
public final Interval getSpanningInterval() {
return new Interval(getMinTime() * 1000, 1000 + getMaxTime() * 1000, DateTimeZone.UTC);
}
/**
* @return the smallest interval spanning all the given events
*/
public Interval getSpanningInterval(Collection<Long> eventIDs) {
return repo.getSpanningInterval(eventIDs);
}
/**
* @return the time (in seconds from unix epoch) of the absolutely first
* event available from the repository, ignoring any filters or requested
* ranges
*/
public final Long getMinTime() {
return repo.getMinTime();
}
/**
* @return the time (in seconds from unix epoch) of the absolutely last
* event available from the repository, ignoring any filters or requested
* ranges
*/
public final Long getMaxTime() {
return repo.getMaxTime();
}
/**
* @param aggregation
*
* @return a list of aggregated events that are within the requested time
* range and pass the requested filter, using the given aggregation to
* control the grouping of events
*/
public List<AggregateEvent> getAggregatedEvents() {
final Interval range;
final Filter filter;
final EventTypeZoomLevel zoom;
final DescriptionLOD lod;
synchronized (this) {
range = requestedTimeRange.get();
filter = requestedFilter.get();
zoom = requestedTypeZoom.get();
lod = requestedLOD.get();
}
return repo.getAggregatedEvents(new ZoomParams(range, zoom, filter, lod));
}
/**
* @param aggregation
*
* @return a list of aggregated events that are within the requested time
* range and pass the requested filter, using the given aggregation to
* control the grouping of events
*/
public List<AggregateEvent> getAggregatedEvents(ZoomParams params) {
return repo.getAggregatedEvents(params);
}
synchronized public ReadOnlyObjectProperty<EventTypeZoomLevel> eventTypeZoom() {
return requestedTypeZoom.getReadOnlyProperty();
}
synchronized public EventTypeZoomLevel getEventTypeZoom() {
return requestedTypeZoom.get();
}
// synchronized public void requestZoomState(ZoomParams zCrumb, boolean force) {
// if (force
// || zCrumb.getTypeZoomLevel().equals(requestedTypeZoom.get()) == false
// || zCrumb.getDescrLOD().equals(requestedLOD.get()) == false
// || zCrumb.getFilter().equals(requestedFilter.get()) == false
// || zCrumb.getTimeRange().equals(requestedTimeRange.get()) == false) {
//
// requestedZoomParamters.set(zCrumb);
// requestedTypeZoom.set(zCrumb.getTypeZoomLevel());
// requestedFilter.set(zCrumb.getFilter().copyOf());
// requestedTimeRange.set(zCrumb.getTimeRange());
// requestedLOD.set(zCrumb.getDescrLOD());
// }
// }
}

View File

@ -0,0 +1,91 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.timeline.events;
import org.sleuthkit.autopsy.timeline.events.type.EventType;
import org.sleuthkit.datamodel.TskData;
/**
*
*/
public class TimeLineEvent {
private final Long eventID;
private final Long fileID, time;
private final Long artifactID;
private final EventType subType;
private final String fullDescription, medDescription, shortDescription;
private final TskData.FileKnown known;
public Long getArtifactID() {
return artifactID;
}
public Long getEventID() {
return eventID;
}
public Long getFileID() {
return fileID;
}
/** @return the time in seconds from unix epoch */
public Long getTime() {
return time;
}
public EventType getType() {
return subType;
}
public String getFullDescription() {
return fullDescription;
}
public String getMedDescription() {
return medDescription;
}
public String getShortDescription() {
return shortDescription;
}
public TimeLineEvent(Long eventID, Long objID, Long artifactID, Long time,
EventType type, String fullDescription, String medDescription, String shortDescription, TskData.FileKnown known) {
this.eventID = eventID;
this.fileID = objID;
this.artifactID = artifactID;
this.time = time;
this.subType = type;
this.fullDescription = fullDescription;
this.medDescription = medDescription;
this.shortDescription = shortDescription;
this.known = known;
}
public TskData.FileKnown getKnown() {
return known;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,361 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2013-14 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.timeline.events.db;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.cache.RemovalNotification;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import javafx.beans.property.ReadOnlyObjectProperty;
import javax.annotation.concurrent.GuardedBy;
import javax.swing.JOptionPane;
import javax.swing.SwingWorker;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.Interval;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.timeline.ProgressWindow;
import org.sleuthkit.autopsy.timeline.events.AggregateEvent;
import org.sleuthkit.autopsy.timeline.events.FilteredEventsModel;
import org.sleuthkit.autopsy.timeline.events.TimeLineEvent;
import org.sleuthkit.autopsy.timeline.events.type.ArtifactEventType;
import org.sleuthkit.autopsy.timeline.events.type.EventType;
import org.sleuthkit.autopsy.timeline.events.type.FileSystemTypes;
import org.sleuthkit.autopsy.timeline.events.type.RootEventType;
import org.sleuthkit.autopsy.timeline.filters.Filter;
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.TskCoreException;
/**
* Provides public API (over EventsDB) to access events. In theory this
* insulates the rest of the timeline module form the details of the db
* implementation. Since there are no other implementations of the database or
* clients of this class, and no Java Interface defined yet, in practice this
* just delegates everything to the eventDB
*
* Concurrency Policy:
*
* Since almost everything just delegates to the EventDB, which is internally
* synchronized, we only have to worry about rebuildRepository() which we
* synchronize on our intrinsic lock.
*
*/
public class EventsRepository {
private static final String FILES_AND_DIRS_WHERE_CLAUSE = "name != '.' AND name != '..'";
private final EventDB eventDB;
private final static Logger LOGGER = Logger.getLogger(EventsRepository.class.getName());
@GuardedBy("this")
private SwingWorker<Void, ProgressWindow.ProgressUpdate> dbPopulationWorker;
private final LoadingCache<Object, Long> maxCache;
private final LoadingCache<Object, Long> minCache;
private final FilteredEventsModel modelInstance;
private final LoadingCache<Long, TimeLineEvent> idToEventCache;
private final LoadingCache<ZoomParams, Map<EventType, Long>> eventCountsCache;
private final LoadingCache<ZoomParams, List<AggregateEvent>> aggregateEventsCache;
public Interval getBoundingEventsInterval(Interval timeRange, Filter filter) {
return eventDB.getBoundingEventsInterval(timeRange, filter);
}
/**
* @return a FilteredEvetns object with this repository as underlying source
* of events
*/
public FilteredEventsModel getEventsModel() {
return modelInstance;
}
public EventsRepository(ReadOnlyObjectProperty<ZoomParams> currentStateProperty) {
//TODO: we should check that case is open, or get passed a case object/directory -jm
this.eventDB = EventDB.getEventDB(Case.getCurrentCase().getCaseDirectory());
idToEventCache = CacheBuilder.newBuilder().maximumSize(5000L).expireAfterAccess(10, TimeUnit.MINUTES).removalListener((RemovalNotification<Long, TimeLineEvent> rn) -> {
LOGGER.log(Level.INFO, "evicting event: {0}", rn.toString());
}).build(CacheLoader.from(eventDB::getEventById));
eventCountsCache = CacheBuilder.newBuilder().maximumSize(1000L).expireAfterAccess(10, TimeUnit.MINUTES).removalListener((RemovalNotification<ZoomParams, Map<EventType, Long>> rn) -> {
LOGGER.log(Level.INFO, "evicting counts: {0}", rn.toString());
}).build(CacheLoader.from(eventDB::countEvents));
aggregateEventsCache = CacheBuilder.newBuilder().maximumSize(1000L).expireAfterAccess(10, TimeUnit.MINUTES).removalListener((RemovalNotification<ZoomParams, List<AggregateEvent>> rn) -> {
LOGGER.log(Level.INFO, "evicting aggregated events: {0}", rn.toString());
}).build(CacheLoader.from(eventDB::getAggregatedEvents));
maxCache = CacheBuilder.newBuilder().build(CacheLoader.from(eventDB::getMaxTime));
minCache = CacheBuilder.newBuilder().build(CacheLoader.from(eventDB::getMinTime));
this.modelInstance = new FilteredEventsModel(this, currentStateProperty);
}
/** @return min time (in seconds from unix epoch) */
public Long getMaxTime() {
return maxCache.getUnchecked("max");
// return eventDB.getMaxTime();
}
/** @return max tie (in seconds from unix epoch) */
public Long getMinTime() {
return minCache.getUnchecked("min");
// return eventDB.getMinTime();
}
public void recordLastArtifactID(long lastArtfID) {
eventDB.recordLastArtifactID(lastArtfID);
}
public void recordWasIngestRunning(Boolean wasIngestRunning) {
eventDB.recordWasIngestRunning(wasIngestRunning);
}
public void recordLastObjID(Long lastObjID) {
eventDB.recordLastObjID(lastObjID);
}
public boolean getWasIngestRunning() {
return eventDB.getWasIngestRunning();
}
public Long getLastObjID() {
return eventDB.getLastObjID();
}
public long getLastArtfactID() {
return eventDB.getLastArtfactID();
}
public TimeLineEvent getEventById(Long eventID) {
return idToEventCache.getUnchecked(eventID);
}
public List<AggregateEvent> getAggregatedEvents(ZoomParams params) {
return aggregateEventsCache.getUnchecked(params);
}
public Map<EventType, Long> countEvents(ZoomParams params) {
return eventCountsCache.getUnchecked(params);
}
private void invalidateCaches() {
minCache.invalidateAll();
maxCache.invalidateAll();
eventCountsCache.invalidateAll();
aggregateEventsCache.invalidateAll();
}
public Set<Long> getEventIDs(Interval timeRange, Filter filter) {
return eventDB.getEventIDs(timeRange, filter);
}
public Interval getSpanningInterval(Collection<Long> eventIDs) {
return eventDB.getSpanningInterval(eventIDs);
}
synchronized public void rebuildRepository(Runnable r) {
if (dbPopulationWorker != null) {
dbPopulationWorker.cancel(true);
}
dbPopulationWorker = new DBPopulationWorker(r);
dbPopulationWorker.execute();
}
/**
* SwingWorker to populate event db with data from main autopsy database.
*
* has only local state. accesses eventDB but that is internally
* synchronized/ thread-safe.
*/
private class DBPopulationWorker extends SwingWorker<Void, ProgressWindow.ProgressUpdate> {
private final ProgressWindow progressDialog;
//TODO: can we avoid this with a state listener? does it amount to the same thing?
//post population operation to execute
private final Runnable r;
public DBPopulationWorker(Runnable r) {
progressDialog = new ProgressWindow(null, true, this);
progressDialog.setVisible(true);
this.r = r;
}
@Override
protected Void doInBackground() throws Exception {
process(Arrays.asList(new ProgressWindow.ProgressUpdate(0, -1, "(re)initializing events database", "")));
//reset database
//TODO: can we do more incremental updates? -jm
eventDB.dropTable();
eventDB.initializeDB();
//grab ids of all files
SleuthkitCase skCase = Case.getCurrentCase().getSleuthkitCase();
List<Long> files = skCase.findAllFileIdsWhere(FILES_AND_DIRS_WHERE_CLAUSE);
final int numFiles = files.size();
process(Arrays.asList(new ProgressWindow.ProgressUpdate(0, numFiles, "populating mac events for files: ", "")));
//insert file events into db
int i = 1;
EventDB.EventTransaction trans = eventDB.beginTransaction();
for (final Long fID : files) {
if (isCancelled()) {
break;
} else {
try {
AbstractFile f = skCase.getAbstractFileById(fID);
//TODO: This is broken for logical files? fix -jm
//TODO: logical files don't necessarily have valid timestamps, so ... -jm
final String uniquePath = f.getUniquePath();
final String parentPath = f.getParentPath();
String datasourceName = StringUtils.substringBefore(StringUtils.stripStart(uniquePath, "/"), parentPath);
String rootFolder = StringUtils.substringBetween(parentPath, "/", "/");
String shortDesc = datasourceName + "/" + StringUtils.defaultIfBlank(rootFolder, "");
String medD = datasourceName + parentPath;
//insert it into the db if time is > 0 => time is legitimate (drops logical files)
if (f.getAtime() > 0) {
eventDB.insertEvent(f.getAtime(), FileSystemTypes.FILE_ACCESSED, fID, null, uniquePath, medD, shortDesc, f.getKnown(), trans);
}
if (f.getMtime() > 0) {
eventDB.insertEvent(f.getMtime(), FileSystemTypes.FILE_MODIFIED, fID, null, uniquePath, medD, shortDesc, f.getKnown(), trans);
}
if (f.getCtime() > 0) {
eventDB.insertEvent(f.getCtime(), FileSystemTypes.FILE_CHANGED, fID, null, uniquePath, medD, shortDesc, f.getKnown(), trans);
}
if (f.getCrtime() > 0) {
eventDB.insertEvent(f.getCrtime(), FileSystemTypes.FILE_CREATED, fID, null, uniquePath, medD, shortDesc, f.getKnown(), trans);
}
process(Arrays.asList(new ProgressWindow.ProgressUpdate(i, numFiles, "populating mac events for files: ", f.getName())));
} catch (TskCoreException tskCoreException) {
LOGGER.log(Level.WARNING, "failed to insert mac event for file : " + fID, tskCoreException);
}
}
i++;
}
//insert artifact based events
//TODO: use (not-yet existing api) to grab all artifacts with timestamps, rather than the hardcoded lists in EventType -jm
for (EventType type : RootEventType.allTypes) {
if (isCancelled()) {
break;
}
//skip file_system events, they are already handled above.
if (type instanceof ArtifactEventType) {
populateEventType((ArtifactEventType) type, trans, skCase);
}
}
process(Arrays.asList(new ProgressWindow.ProgressUpdate(0, -1, "commiting events db", "")));
if (isCancelled()) {
eventDB.rollBackTransaction(trans);
} else {
eventDB.commitTransaction(trans, true);
}
invalidateCaches();
return null;
}
/**
* handle intermediate 'results': just update progress dialog
*
* @param chunks
*/
@Override
protected void process(List<ProgressWindow.ProgressUpdate> chunks) {
super.process(chunks);
ProgressWindow.ProgressUpdate chunk = chunks.get(chunks.size() - 1);
progressDialog.update(chunk);
}
@Override
protected void done() {
super.done();
try {
progressDialog.close();
get();
} catch (CancellationException ex) {
LOGGER.log(Level.INFO, "Database population was cancelled by the user. Not all events may be present or accurate. See the log for details.", ex);
} catch (InterruptedException | ExecutionException ex) {
LOGGER.log(Level.WARNING, "Exception while populating database.", ex);
JOptionPane.showMessageDialog(null, "There was a problem populating the timeline. Not all events may be present or accurate. See the log for details.");
} catch (Exception ex) {
LOGGER.log(Level.WARNING, "Unexpected exception while populating database.", ex);
JOptionPane.showMessageDialog(null, "There was a problem populating the timeline. Not all events may be present or accurate. See the log for details.");
}
r.run(); //execute post db population operation
}
/**
* populate all the events of one subtype
*
* @param subType the subtype to populate
* @param trans the db transaction to use
* @param skCase a reference to the sleuthkit case
*/
private void populateEventType(final ArtifactEventType type, EventDB.EventTransaction trans, SleuthkitCase skCase) {
try {
//get all the blackboard artifacts corresponding to the given event sub_type
final ArrayList<BlackboardArtifact> blackboardArtifacts = skCase.getBlackboardArtifacts(type.getArtifactType());
final int numArtifacts = blackboardArtifacts.size();
process(Arrays.asList(new ProgressWindow.ProgressUpdate(0, numArtifacts, "populating " + type.toString() + " events", "")));
int i = 0;
for (final BlackboardArtifact bbart : blackboardArtifacts) {
//for each artifact, extract the relevant information for the descriptions
ArtifactEventType.AttributeEventDescription eventDescription = ArtifactEventType.AttributeEventDescription.buildEventDescription(type, bbart);
if (eventDescription != null && eventDescription.getTime() > 0L) { //insert it into the db if time is > 0 => time is legitimate
eventDB.insertEvent(eventDescription.getTime(), type, bbart.getObjectID(), bbart.getArtifactID(), eventDescription.getFullDescription(), eventDescription.getMedDescription(), eventDescription.getShortDescription(), null, trans);
}
i++;
process(Arrays.asList(new ProgressWindow.ProgressUpdate(i, numArtifacts, "populating " + type.toString() + " events", "")));
}
} catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, "There was a problem getting events with sub type = " + type.toString() + ".", ex);
}
}
}
}

View File

@ -0,0 +1,189 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.timeline.events.type;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.logging.Level;
import org.apache.commons.lang3.StringUtils;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.TskCoreException;
/**
*
*/
public interface ArtifactEventType extends EventType {
/**
* @return the Artifact type this event type is derived form, or null if
* there is no artifact type (eg file system events)
*/
public BlackboardArtifact.ARTIFACT_TYPE getArtifactType();
public BlackboardAttribute.ATTRIBUTE_TYPE getDateTimeAttrubuteType();
/** given an artifact, and a map from attribute types to attributes, pull
* out the time stamp, and compose the descriptions. Each implementation
* of {@link ArtifactEventType} needs to implement parseAttributesHelper()
* as hook for {@link buildEventDescription(org.sleuthkit.datamodel.BlackboardArtifact)
* to invoke. Most subtypes can use this default implementation.
*
* @param artf
* @param attrMap
*
* @return an {@link AttributeEventDescription} containing the timestamp
* and description information
*
* @throws TskCoreException
*/
default AttributeEventDescription parseAttributesHelper(BlackboardArtifact artf, Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> attrMap) throws TskCoreException {
final BlackboardAttribute dateTimeAttr = attrMap.get(getDateTimeAttrubuteType());
long time = dateTimeAttr.getValueLong();
String shortDescription = getShortExtractor().apply(artf, attrMap);
String medDescription = shortDescription + " : " + getMedExtractor().apply(artf, attrMap);
String fullDescription = medDescription + " : " + getFullExtractor().apply(artf, attrMap);
return new AttributeEventDescription(time, shortDescription, medDescription, fullDescription);
}
/** @return a function from an artifact and a map of its attributes, to a
* String to use as part of the full event description */
BiFunction<BlackboardArtifact, Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute>, String> getFullExtractor();
/** @return a function from an artifact and a map of its attributes, to a
* String to use as part of the medium event description */
BiFunction<BlackboardArtifact, Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute>, String> getMedExtractor();
/** @return a function from an artifact and a map of its attributes, to a
* String to use as part of the short event description */
BiFunction<BlackboardArtifact, Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute>, String> getShortExtractor();
/**
* 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).
*/
static class AttributeEventDescription {
final private long time;
public long getTime() {
return time;
}
public String getShortDescription() {
return shortDescription;
}
public String getMedDescription() {
return medDescription;
}
public String getFullDescription() {
return fullDescription;
}
final private String shortDescription;
final private String medDescription;
final private String fullDescription;
public AttributeEventDescription(long time, String shortDescription,
String medDescription,
String fullDescription) {
this.time = time;
this.shortDescription = shortDescription;
this.medDescription = medDescription;
this.fullDescription = fullDescription;
}
/**
* Build a {@link AttributeEventDescription} derived from a
* {@link BlackboardArtifact}. This is a template method that relies on
* each {@link SubType}'s implementation of
* {@link SubType#parseAttributesHelper(org.sleuthkit.datamodel.BlackboardArtifact, java.util.Map)}
* know how to go from {@link BlackboardAttribute}s to the event
* description.
*
* @param artf the {@link BlackboardArtifact} to derive the event
* description from
*
* @return an {@link AttributeEventDescription} derived from the given
* artifact
*
* @throws TskCoreException is there is a problem accessing the
* blackboard data
*/
static public AttributeEventDescription buildEventDescription(
ArtifactEventType type, BlackboardArtifact artf) throws TskCoreException {
//if we got passed an artifact that doesn't correspond to the type of the event,
//something went very wrong. throw an exception.
if (type.getArtifactType().getTypeID() != artf.getArtifactTypeID()) {
throw new IllegalArgumentException();
}
/* build a map from attribute type to attribute, this makes
* implementing the parseAttributeHelper easier but could be
* ineffecient if we don't need most of the attributes. This would
* be unnessecary if there was an api on Blackboard artifacts to get
* specific attributes by type */
List<BlackboardAttribute> attributes = artf.getAttributes();
Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> attrMap = new HashMap<>();
for (BlackboardAttribute attr : attributes) {
attrMap.put(BlackboardAttribute.ATTRIBUTE_TYPE.fromLabel(attr.
getAttributeTypeName()), attr);
}
if (attrMap.get(type.getDateTimeAttrubuteType()) == null) {
Logger.getLogger(AttributeEventDescription.class.getName()).log(Level.WARNING, "Artifact {0} has no date/time attribute, skipping it.", artf.getArtifactID());
return null;
}
//use the hook provided by this subtype implementation
return type.parseAttributesHelper(artf, attrMap);
}
}
public static class AttributeExtractor implements BiFunction<BlackboardArtifact, Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute>, String> {
@Override
public String apply(BlackboardArtifact artf, Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> attrMap) {
final BlackboardAttribute attr = attrMap.get(attribute);
return (attr != null) ? StringUtils.defaultString(attr.getValueString()) : " ";
}
private final BlackboardAttribute.ATTRIBUTE_TYPE attribute;
public AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE attribute) {
this.attribute = attribute;
}
}
public static class EmptyExtractor implements BiFunction<BlackboardArtifact, Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute>, String> {
@Override
public String apply(BlackboardArtifact t, Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> u) {
return "";
}
}
}

View File

@ -0,0 +1,108 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.timeline.events.type;
import java.util.Arrays;
import java.util.List;
import javafx.scene.image.Image;
import org.sleuthkit.autopsy.timeline.zooming.EventTypeZoomLevel;
/**
* RootTypes are event types that have no super type.
*/
public enum BaseTypes implements EventType {
FILE_SYSTEM("File System", "blue-document.png") {
@Override
public List<? extends EventType> getSubTypes() {
return Arrays.asList(FileSystemTypes.values());
}
@Override
public EventType getSubType(String string) {
return FileSystemTypes.valueOf(string);
}
},
WEB_ACTIVITY("Web Activity", "web-file.png") {
@Override
public List<? extends EventType> getSubTypes() {
return Arrays.asList(WebTypes.values());
}
@Override
public EventType getSubType(String string) {
return WebTypes.valueOf(string);
}
},
MISC_TYPES("Misc Types", "block.png") {
@Override
public List<? extends EventType> getSubTypes() {
return Arrays.asList(MiscTypes.values());
}
@Override
public EventType getSubType(String string) {
return MiscTypes.valueOf(string);
}
};
private final String displayName;
private final String iconBase;
private Image image;
public Image getFXImage() {
return image;
}
@Override
public String getIconBase() {
return iconBase;
}
@Override
public EventTypeZoomLevel getZoomLevel() {
return EventTypeZoomLevel.BASE_TYPE;
}
@Override
public String getDisplayName() {
return displayName;
}
private BaseTypes(String displayName, String iconBase) {
this.displayName = displayName;
this.iconBase = iconBase;
this.image = new Image("org/sleuthkit/autopsy/timeline/images/" + iconBase, true);
}
@Override
public EventType getSuperType() {
return RootEventType.getInstance();
}
@Override
public EventType getSubType(String string) {
return BaseTypes.valueOf(string);
}
}

View File

@ -0,0 +1,108 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.timeline.events.type;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import javafx.scene.image.Image;
import javafx.scene.paint.Color;
import org.sleuthkit.autopsy.timeline.zooming.EventTypeZoomLevel;
/**
* An Event Type represents a distinct kind of event ie file system or web
* activity. An EventType may have an optional super-type and 0 or more
* subtypes, allowing events to be organized in a type hierarchy.
*/
public interface EventType {
final static List<? extends EventType> allTypes = RootEventType.getInstance().getSubTypesRecusive();
static Comparator<EventType> getComparator() {
return Comparator.comparing(EventType.allTypes::indexOf);
}
default BaseTypes getBaseType() {
if (this instanceof BaseTypes) {
return (BaseTypes) this;
} else {
return getSuperType().getBaseType();
}
}
default List<? extends EventType> getSubTypesRecusive() {
ArrayList<EventType> flatList = new ArrayList<>();
for (EventType et : getSubTypes()) {
flatList.add(et);
flatList.addAll(et.getSubTypesRecusive());
}
return flatList;
}
/**
* @return the color used to represent this event type visually
*/
default Color getColor() {
Color baseColor = this.getSuperType().getColor();
int siblings = getSuperType().getSiblingTypes().stream().max((
EventType t, EventType t1)
-> Integer.compare(t.getSubTypes().size(), t1.getSubTypes().size()))
.get().getSubTypes().size() + 1;
int superSiblings = this.getSuperType().getSiblingTypes().size();
double offset = (360.0 / superSiblings) / siblings;
final Color deriveColor = baseColor.deriveColor(ordinal() * offset, 1, 1, 1);
return Color.hsb(deriveColor.getHue(), deriveColor.getSaturation(), deriveColor.getBrightness());
}
default List<? extends EventType> getSiblingTypes() {
return this.getSuperType().getSubTypes();
}
/**
* @return the super type of this event
*/
public EventType getSuperType();
public EventTypeZoomLevel getZoomLevel();
/**
* @return a list of event types, one for each subtype of this eventype, or
* an empty list if this event type has no subtypes
*/
public List<? extends EventType> getSubTypes();
/* return the name of the icon file for this type, it will be resolved in
* the org/sleuthkit/autopsy/timeline/images */
public String getIconBase();
public String getDisplayName();
public EventType getSubType(String string);
public Image getFXImage();
public int ordinal();
}

View File

@ -0,0 +1,82 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.timeline.events.type;
import java.util.Collections;
import java.util.List;
import javafx.scene.image.Image;
import org.sleuthkit.autopsy.timeline.zooming.EventTypeZoomLevel;
/**
*
*/
public enum FileSystemTypes implements EventType {
FILE_MODIFIED("File Modified", "blue-document-attribute-m.png"),
FILE_ACCESSED("File Accessed", "blue-document-attribute-a.png"),
FILE_CREATED("File Created", "blue-document-attribute-b.png"),
FILE_CHANGED("File Changed", "blue-document-attribute-c.png");
private final String iconBase;
private final Image image;
@Override
public Image getFXImage() {
return image;
}
@Override
public String getIconBase() {
return iconBase;
}
@Override
public EventTypeZoomLevel getZoomLevel() {
return EventTypeZoomLevel.SUB_TYPE;
}
private final String displayName;
@Override
public EventType getSubType(String string) {
return FileSystemTypes.valueOf(string);
}
@Override
public EventType getSuperType() {
return BaseTypes.FILE_SYSTEM;
}
@Override
public List<? extends EventType> getSubTypes() {
return Collections.emptyList();
}
private FileSystemTypes(String displayName, String iconBase) {
this.displayName = displayName;
this.iconBase = iconBase;
this.image = new Image("org/sleuthkit/autopsy/timeline/images/" + iconBase, true);
}
@Override
public String getDisplayName() {
return displayName;
}
}

View File

@ -0,0 +1,253 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.timeline.events.type;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
import javafx.scene.image.Image;
import org.apache.commons.lang3.StringUtils;
import org.openide.util.Exceptions;
import org.sleuthkit.autopsy.timeline.zooming.EventTypeZoomLevel;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.TskCoreException;
/**
*
*/
public enum MiscTypes implements EventType, ArtifactEventType {
MESSAGE("Messages", "message.png",
BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE,
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME,
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MESSAGE_TYPE),
(artf, attrMap) -> {
final BlackboardAttribute dir = attrMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION);
final BlackboardAttribute name = attrMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME);
final BlackboardAttribute phoneNumber = attrMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER);
final BlackboardAttribute subject = attrMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SUBJECT);
List<String> asList = Arrays.asList(stringValueOf(dir), name != null || phoneNumber != null ? toFrom(dir) : "", stringValueOf(name != null ? name : phoneNumber), (subject == null ? "" : stringValueOf(subject)));
return StringUtils.join(asList, " ");
},
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT)),
GPS_ROUTE("GPS Routes", "gps-search.png",
BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_ROUTE,
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME,
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME),
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_LOCATION),
(BlackboardArtifact artf, Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> attrMap) -> {
final BlackboardAttribute latStart = attrMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_START);
final BlackboardAttribute longStart = attrMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_START);
final BlackboardAttribute latEnd = attrMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_END);
final BlackboardAttribute longEnd = attrMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_END);
return String.format("from %1$g %2$g to %3$g %4$g", latStart.getValueDouble(), longStart.getValueDouble(), latEnd.getValueDouble(), longEnd.getValueDouble());
}),
GPS_TRACKPOINT("Location History", "gps-trackpoint.png",
BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACKPOINT,
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME,
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME),
(artf, attrMap) -> {
final BlackboardAttribute longitude = attrMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE);
final BlackboardAttribute latitude = attrMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE);
return (latitude != null ? latitude.getValueDouble() : "") + " " + (longitude != null ? longitude.getValueDouble() : "");
},
(artf, attrMap) -> ""),
CALL_LOG("Calls", "calllog.png",
BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG,
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START,
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME),
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER),
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION)),
EMAIL("Email", "mail-icon-16.png",
BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG,
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_SENT,
(artifact, attrMap) -> {
final BlackboardAttribute emailFrom = attrMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL_FROM);
final BlackboardAttribute emailTo = attrMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL_TO);
return (emailFrom != null ? emailFrom.getValueString() : "") + " to " + (emailTo != null ? emailTo.getValueString() : "");
},
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SUBJECT),
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_PLAIN)),
RECENT_DOCUMENTS("Recent Documents", "recent_docs.png",
BlackboardArtifact.ARTIFACT_TYPE.TSK_RECENT_OBJECT,
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME,
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH).andThen(
(String t) -> (StringUtils.substringBeforeLast(StringUtils.substringBeforeLast(t, "\\"), "\\"))),
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH).andThen(
(String t) -> StringUtils.substringBeforeLast(t, "\\")),
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH)) {
/** Override
* {@link ArtifactEventType#parseAttributesHelper(org.sleuthkit.datamodel.BlackboardArtifact, java.util.Map)}
* with non-default description construction */
@Override
public AttributeEventDescription parseAttributesHelper(BlackboardArtifact artf, Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> attrMap) throws TskCoreException {
final BlackboardAttribute dateTimeAttr = attrMap.get(getDateTimeAttrubuteType());
long time = dateTimeAttr.getValueLong();
//Non-default description construction
String shortDescription = getShortExtractor().apply(artf, attrMap);
String medDescription = getMedExtractor().apply(artf, attrMap);
String fullDescription = getFullExtractor().apply(artf, attrMap);
return new AttributeEventDescription(time, shortDescription, medDescription, fullDescription);
}
},
INSTALLED_PROGRAM("Installed Programs", "programs.png",
BlackboardArtifact.ARTIFACT_TYPE.TSK_INSTALLED_PROG,
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME,
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME),
new EmptyExtractor(),
new EmptyExtractor()),
EXIF("Exif", "camera-icon-16.png",
BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF,
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_MAKE),
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_MODEL),
(BlackboardArtifact t,
Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> u) -> {
try {
return t.getSleuthkitCase().getAbstractFileById(t.getObjectID()).getName();
} catch (TskCoreException ex) {
Exceptions.printStackTrace(ex);
return " error loading file name";
}
}),
DEVICES_ATTACHED("Devices Attached", "usb_devices.png",
BlackboardArtifact.ARTIFACT_TYPE.TSK_DEVICE_ATTACHED,
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME,
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_MAKE),
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_MODEL),
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_ID));
static public String stringValueOf(BlackboardAttribute attr) {
return attr != null ? attr.getValueString() : "";
}
public static String toFrom(BlackboardAttribute dir) {
if (dir == null) {
return "";
} else {
switch (dir.getValueString()) {
case "Incoming":
return "from";
case "Outgoing":
return "to";
default:
return "";
}
}
}
private final BlackboardAttribute.ATTRIBUTE_TYPE dateTimeAttributeType;
private final String iconBase;
private final Image image;
@Override
public Image getFXImage() {
return image;
}
private final BiFunction<BlackboardArtifact, Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute>, String> longExtractor;
private final BiFunction<BlackboardArtifact, Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute>, String> medExtractor;
private final BiFunction<BlackboardArtifact, Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute>, String> shortExtractor;
@Override
public BiFunction<BlackboardArtifact, Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute>, String> getFullExtractor() {
return longExtractor;
}
@Override
public BiFunction<BlackboardArtifact, Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute>, String> getMedExtractor() {
return medExtractor;
}
@Override
public BiFunction<BlackboardArtifact, Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute>, String> getShortExtractor() {
return shortExtractor;
}
@Override
public BlackboardAttribute.ATTRIBUTE_TYPE getDateTimeAttrubuteType() {
return dateTimeAttributeType;
}
@Override
public EventTypeZoomLevel getZoomLevel() {
return EventTypeZoomLevel.SUB_TYPE;
}
private final String displayName;
private final BlackboardArtifact.ARTIFACT_TYPE artifactType;
@Override
public String getDisplayName() {
return displayName;
}
@Override
public String getIconBase() {
return iconBase;
}
@Override
public EventType getSubType(String string) {
return MiscTypes.valueOf(string);
}
private MiscTypes(String displayName, String iconBase, BlackboardArtifact.ARTIFACT_TYPE artifactType,
BlackboardAttribute.ATTRIBUTE_TYPE dateTimeAttributeType,
BiFunction<BlackboardArtifact, Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute>, String> shortExtractor,
BiFunction<BlackboardArtifact, Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute>, String> medExtractor,
BiFunction<BlackboardArtifact, Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute>, String> longExtractor) {
this.displayName = displayName;
this.iconBase = iconBase;
this.artifactType = artifactType;
this.dateTimeAttributeType = dateTimeAttributeType;
this.shortExtractor = shortExtractor;
this.medExtractor = medExtractor;
this.longExtractor = longExtractor;
this.image = new Image("org/sleuthkit/autopsy/timeline/images/" + iconBase, true);
}
@Override
public EventType getSuperType() {
return BaseTypes.MISC_TYPES;
}
@Override
public List<? extends EventType> getSubTypes() {
return Collections.emptyList();
}
@Override
public BlackboardArtifact.ARTIFACT_TYPE getArtifactType() {
return artifactType;
}
}

View File

@ -0,0 +1,95 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2013 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.timeline.events.type;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javafx.scene.image.Image;
import javafx.scene.paint.Color;
import org.sleuthkit.autopsy.timeline.zooming.EventTypeZoomLevel;
/** A singleton {@link } EventType to represent the root type of all event
* types. */
public class RootEventType implements EventType {
@Override
public List<RootEventType> getSiblingTypes() {
return Collections.singletonList(this);
}
@Override
public EventTypeZoomLevel getZoomLevel() {
return EventTypeZoomLevel.ROOT_TYPE;
}
private RootEventType() {
}
public static RootEventType getInstance() {
return RootEventTypeHolder.INSTANCE;
}
@Override
public EventType getSubType(String string) {
return BaseTypes.valueOf(string);
}
@Override
public int ordinal() {
return 0;
}
private static class RootEventTypeHolder {
private static final RootEventType INSTANCE = new RootEventType();
}
@Override
public Color getColor() {
return Color.hsb(359, .9, .9, 0);
}
@Override
public RootEventType getSuperType() {
return this;
}
@Override
public List<BaseTypes> getSubTypes() {
return Arrays.asList(BaseTypes.values());
}
@Override
public String getIconBase() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public String getDisplayName() {
return "Event Types";
}
@Override
public Image getFXImage() {
return null;
}
}

View File

@ -0,0 +1,186 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.timeline.events.type;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
import javafx.scene.image.Image;
import org.apache.commons.lang3.StringUtils;
import org.sleuthkit.autopsy.timeline.zooming.EventTypeZoomLevel;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
/**
*
*/
public enum WebTypes implements EventType, ArtifactEventType {
WEB_DOWNLOADS("Web Downloads",
"downloads.png",
BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD,
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN),
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH),
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_URL)) {
/** Override
* {@link ArtifactEventType#parseAttributesHelper(org.sleuthkit.datamodel.BlackboardArtifact, java.util.Map)}
* with non default descritpion construction */
@Override
public AttributeEventDescription parseAttributesHelper(BlackboardArtifact artf, Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> attrMap) {
long time = attrMap.get(getDateTimeAttrubuteType()).getValueLong();
String domain = getShortExtractor().apply(artf, attrMap);
String path = getMedExtractor().apply(artf, attrMap);
String fileName = StringUtils.substringAfterLast(path, "/");
String url = getFullExtractor().apply(artf, attrMap);
//TODO: review non default descritpion construction
String shortDescription = fileName + " from " + domain;
String medDescription = fileName + " from " + url;
String fullDescription = path + " from " + url;
return new AttributeEventDescription(time, shortDescription, medDescription, fullDescription);
}
},
//TODO: review description seperators
WEB_COOKIE("Web Cookies",
"cookies.png",
BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_COOKIE,
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME,
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN),
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME),
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_VALUE)),
//TODO: review description seperators
WEB_BOOKMARK("Web Bookmarks",
"bookmarks.png",
BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_BOOKMARK,
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN),
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_URL),
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TITLE)),
//TODO: review description seperators
WEB_HISTORY("Web History",
"history.png",
BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY,
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN),
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_URL),
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TITLE)),
//TODO: review description seperators
WEB_SEARCH("Web Searches",
"searchquery.png",
BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY,
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT),
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN),
new AttributeExtractor(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME));
private final BlackboardAttribute.ATTRIBUTE_TYPE dateTimeAttributeType;
private final String iconBase;
private final Image image;
@Override
public Image getFXImage() {
return image;
}
@Override
public BlackboardAttribute.ATTRIBUTE_TYPE getDateTimeAttrubuteType() {
return dateTimeAttributeType;
}
@Override
public EventTypeZoomLevel getZoomLevel() {
return EventTypeZoomLevel.SUB_TYPE;
}
private final BiFunction<BlackboardArtifact, Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute>, String> longExtractor;
private final BiFunction<BlackboardArtifact, Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute>, String> medExtractor;
private final BiFunction<BlackboardArtifact, Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute>, String> shortExtractor;
@Override
public BiFunction<BlackboardArtifact, Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute>, String> getFullExtractor() {
return longExtractor;
}
@Override
public BiFunction<BlackboardArtifact, Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute>, String> getMedExtractor() {
return medExtractor;
}
@Override
public BiFunction<BlackboardArtifact, Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute>, String> getShortExtractor() {
return shortExtractor;
}
private final String displayName;
BlackboardArtifact.ARTIFACT_TYPE artifactType;
@Override
public String getIconBase() {
return iconBase;
}
@Override
public BlackboardArtifact.ARTIFACT_TYPE getArtifactType() {
return artifactType;
}
private WebTypes(String displayName, String iconBase, BlackboardArtifact.ARTIFACT_TYPE artifactType,
BlackboardAttribute.ATTRIBUTE_TYPE dateTimeAttributeType,
BiFunction<BlackboardArtifact, Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute>, String> shortExtractor,
BiFunction<BlackboardArtifact, Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute>, String> medExtractor,
BiFunction<BlackboardArtifact, Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute>, String> longExtractor) {
this.displayName = displayName;
this.iconBase = iconBase;
this.artifactType = artifactType;
this.dateTimeAttributeType = dateTimeAttributeType;
this.shortExtractor = shortExtractor;
this.medExtractor = medExtractor;
this.longExtractor = longExtractor;
this.image = new Image("org/sleuthkit/autopsy/timeline/images/" + iconBase, true);
}
@Override
public EventType getSuperType() {
return BaseTypes.WEB_ACTIVITY;
}
@Override
public String getDisplayName() {
return displayName;
}
@Override
public EventType getSubType(String string) {
return WebTypes.valueOf(string);
}
@Override
public List<? extends EventType> getSubTypes() {
return Collections.emptyList();
}
}

View File

@ -0,0 +1,151 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.timeline.explorernodes;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javafx.beans.Observable;
import javax.swing.Action;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.openide.nodes.Children;
import org.openide.nodes.PropertySupport;
import org.openide.nodes.Sheet;
import org.openide.util.Exceptions;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.timeline.TimeLineController;
import org.sleuthkit.autopsy.timeline.events.TimeLineEvent;
import org.sleuthkit.autopsy.datamodel.DataModelActionsFactory;
import org.sleuthkit.autopsy.datamodel.DisplayableItemNode;
import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor;
import org.sleuthkit.autopsy.datamodel.NodeProperty;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.Content;
/** * Node for {@link TimeLineEvent}s. */
class EventNode extends DisplayableItemNode {
private final TimeLineEvent e;
EventNode(TimeLineEvent eventById, AbstractFile file, BlackboardArtifact artifact) {
super(Children.LEAF, Lookups.fixed(eventById, file, artifact));
this.e = eventById;
this.setIconBaseWithExtension("org/sleuthkit/autopsy/timeline/images/" + e.getType().getIconBase());
}
EventNode(TimeLineEvent eventById, AbstractFile file) {
super(Children.LEAF, Lookups.fixed(eventById, file));
this.e = eventById;
this.setIconBaseWithExtension("org/sleuthkit/autopsy/timeline/images/" + e.getType().getIconBase());
}
@Override
protected Sheet createSheet() {
Sheet s = super.createSheet();
Sheet.Set properties = s.get(Sheet.PROPERTIES);
if (properties == null) {
properties = Sheet.createPropertiesSet();
s.put(properties);
}
final TimeProperty timePropery = new TimeProperty("time", "Date/Time", "time ", getDateTimeString());
TimeLineController.getTimeZone().addListener((Observable observable) -> {
try {
timePropery.setValue(getDateTimeString());
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
Exceptions.printStackTrace(ex);
}
});
// String name, String displayName, String desc, T value
properties.put(new NodeProperty<>("icon", "Icon", "icon", true)); //gets overriden with icon
properties.put(timePropery);
properties.put(new NodeProperty<>("eventID", "Event ID", "event id", e.getEventID()));
properties.put(new NodeProperty<>("fileID", "File ID", "File id", e.getFileID()));
properties.put(new NodeProperty<>("artifactID", "Result ID", "result id", e.getArtifactID() == 0 ? "" : e.getArtifactID()));
properties.put(new NodeProperty<>("eventBaseType", "Base Type", "base type", e.getType().getSuperType().getDisplayName()));
properties.put(new NodeProperty<>("eventSubType", "Sub Type", "sub type", e.getType().getDisplayName()));
properties.put(new NodeProperty<>("Known", "Known", "known", e.getKnown().toString()));
properties.put(new NodeProperty<>("description", "Description", "description", e.getFullDescription()));
return s;
}
private String getDateTimeString() {
return new DateTime(e.getTime() * 1000, DateTimeZone.UTC).toString(TimeLineController.getZonedFormatter());
}
@Override
public Action[] getActions(boolean context) {
Action[] superActions = super.getActions(context);
List<Action> actionsList = new ArrayList<>();
actionsList.addAll(Arrays.asList(superActions));
final Content content = getLookup().lookup(Content.class);
final BlackboardArtifact artifact = getLookup().lookup(BlackboardArtifact.class);
final List<Action> factoryActions = DataModelActionsFactory.getActions(content, artifact != null);
actionsList.addAll(factoryActions);
return actionsList.toArray(new Action[0]);
}
@Override
public boolean isLeafTypeNode() {
return true;
}
@Override
public <T> T accept(DisplayableItemNodeVisitor<T> dinv) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
class TimeProperty extends PropertySupport.ReadWrite<String> {
private String value;
@Override
public boolean canWrite() {
return false;
}
public TimeProperty(String name, String displayName, String shortDescription, String value) {
super(name, String.class, displayName, shortDescription);
setValue("suppressCustomEditor", Boolean.TRUE); // remove the "..." (editing) button NON-NLS
this.value = value;
}
@Override
public String getValue() throws IllegalAccessException, InvocationTargetException {
return value;
}
@Override
public void setValue(String t) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
String oldValue = getValue();
value = t;
firePropertyChange("time", oldValue, t);
}
}
}

View File

@ -0,0 +1,130 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.timeline.explorernodes;
import java.util.Collection;
import java.util.List;
import java.util.logging.Level;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.timeline.events.FilteredEventsModel;
import org.sleuthkit.autopsy.timeline.events.TimeLineEvent;
import org.sleuthkit.autopsy.timeline.events.type.BaseTypes;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.DisplayableItemNode;
import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.TskCoreException;
/**
*
*/
public class EventRootNode extends DisplayableItemNode {
public static final int MAX_EVENTS_TO_DISPLAY = 5000;
private final int childCount;
public EventRootNode(String NAME, Collection<Long> fileIds, FilteredEventsModel filteredEvents) {
super(Children.create(new EventNodeChildFactory(fileIds, filteredEvents), true), Lookups.singleton(fileIds));
super.setName(NAME);
super.setDisplayName(NAME);
childCount = fileIds.size();
}
@Override
public boolean isLeafTypeNode() {
return false;
}
@Override
public <T> T accept(DisplayableItemNodeVisitor<T> v) {
return null;
}
public int getChildCount() {
return childCount;
}
/** The node factories used to make lists of files to send to the result
* viewer using the lazy loading (rather than background) loading option to
* facilitate */
private static class EventNodeChildFactory extends ChildFactory<Long> {
private static final Logger LOGGER = Logger.getLogger(EventNodeChildFactory.class.getName());
private final Collection<Long> eventIDs;
private final FilteredEventsModel filteredEvents;
EventNodeChildFactory(Collection<Long> fileIds, FilteredEventsModel filteredEvents) {
this.eventIDs = fileIds;
this.filteredEvents = filteredEvents;
}
@Override
protected boolean createKeys(List<Long> toPopulate) {
if (eventIDs.size() < MAX_EVENTS_TO_DISPLAY) {
toPopulate.addAll(eventIDs);
} else {
toPopulate.add(-1l);
}
return true;
}
@Override
protected Node createNodeForKey(Long eventID) {
if (eventID >= 0) {
final TimeLineEvent eventById = filteredEvents.getEventById(eventID);
try {
if (eventById.getType().getSuperType() == BaseTypes.FILE_SYSTEM) {
return new EventNode(eventById, Case.getCurrentCase().getSleuthkitCase().getAbstractFileById(eventById.getFileID()));
} else {
AbstractFile file = Case.getCurrentCase().getSleuthkitCase().getAbstractFileById(eventById.getFileID());
BlackboardArtifact blackboardArtifact = Case.getCurrentCase().getSleuthkitCase().getBlackboardArtifact(eventById.getArtifactID());
return new EventNode(eventById, file, blackboardArtifact);
}
} catch (TskCoreException tskCoreException) {
LOGGER.log(Level.WARNING, "Failed to lookup sleuthkit object backing TimeLineEvent.", tskCoreException);
return null;
}
} else {
return new TooManyNode(eventIDs.size());
}
}
}
private static class TooManyNode extends AbstractNode {
public TooManyNode(int size) {
super(Children.LEAF);
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/info-icon-16.png");
setDisplayName("Too many events to display. Maximum = " + MAX_EVENTS_TO_DISPLAY + ". But there are " + size + " to display.");
}
}
}

View File

@ -0,0 +1,53 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.sleuthkit.autopsy.timeline.filters;
import javafx.beans.property.SimpleBooleanProperty;
/**
* Base implementation of a {@link Filter}. Implements active property.
*
*/
public abstract class AbstractFilter implements Filter {
private final SimpleBooleanProperty active = new SimpleBooleanProperty(true);
private final SimpleBooleanProperty disabled = new SimpleBooleanProperty(false);
@Override
public SimpleBooleanProperty getActiveProperty() {
return active;
}
@Override
public SimpleBooleanProperty getDisabledProperty() {
return disabled;
}
@Override
public void setActive(Boolean act) {
active.set(act);
}
@Override
public boolean isActive() {
return active.get();
}
@Override
public void setDisabled(Boolean act) {
disabled.set(act);
}
@Override
public boolean isdisabled() {
return disabled.get();
}
@Override
public String getStringCheckBox() {
return "[" + (isActive() ? "x" : " ") + "]";
}
}

View File

@ -0,0 +1,106 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.sleuthkit.autopsy.timeline.filters;
import java.util.List;
import javafx.beans.Observable;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
/**
* A Filter with a collection of {@link Filter} sub-filters. If this
* filter is not active than none of its sub-filters are applied either.
* Concrete implementations can decide how to combine the sub-filters.
*
* a {@link CompoundFilter} uses listeners to enforce the following
* relationships between it and its sub-filters:
* <ol>
* <le>if a filter becomes active, and all its sub-filters were inactive, make
* them all active</le>
* <le>if a filter becomes inactive and all its sub-filters were active, make
* them all inactive</le>
* <le>if a sub-filter changes active state set the parent filter active if any
* of its sub-filters are active.</le>
* </ol>
*/
public abstract class CompoundFilter extends AbstractFilter {
/** the list of sub-filters that make up this filter */
private final ObservableList<Filter> subFilters;
public final ObservableList<Filter> getSubFilters() {
return subFilters;
}
/** construct a compound filter from a list of other filters to combine.
*
* @param subFilters
*/
public CompoundFilter(ObservableList<Filter> subFilters) {
super();
this.subFilters = FXCollections.<Filter>synchronizedObservableList(subFilters);
//listen to changes in list of subfilters and add active state listener to newly added filters
this.subFilters.addListener((ListChangeListener.Change<? extends Filter> c) -> {
while (c.next()) {
addListeners(c.getAddedSubList());
//TODO: remove listeners from removed subfilters
}
});
//add listeners to subfilters
addListeners(subFilters);
//disable subfilters if this filter is disabled
getDisabledProperty().addListener((ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) -> {
if (newValue) {
getSubFilters().forEach((Filter t) -> {
t.setDisabled(true);
});
} else {
final boolean isActive = !isActive();
getSubFilters().forEach((Filter t) -> {
t.setDisabled(isActive);
});
}
});
//listen to active property and adjust subfilters active property
getActiveProperty().addListener((ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) -> {
if (newValue) {
// if this filter become active, and all its subfilters were inactive, make them all active
if (getSubFilters().stream().noneMatch(Filter::isActive)) {
getSubFilters().forEach((Filter filter) -> {
filter.setActive(true);
});
}
} else {
//if this filter beceoms inactive and all its subfilters where active, make them inactive
if (getSubFilters().stream().allMatch(Filter::isActive)) {
getSubFilters().forEach((Filter filter) -> {
filter.setActive(false);
});
}
}
//disabled subfilters if this filter is not active
getSubFilters().forEach((Filter t) -> {
t.setDisabled(!newValue);
});
});
}
private void addListeners(List<? extends Filter> newSubfilters) {
for (Filter sf : newSubfilters) {
//if a subfilter changes active state
sf.getActiveProperty().addListener((Observable observable) -> {
//set this filter acttive af any of the subfilters are active.
setActive(getSubFilters().parallelStream().anyMatch(Filter::isActive));
});
}
}
}

View File

@ -0,0 +1,81 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.timeline.filters;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import org.sleuthkit.autopsy.timeline.events.type.RootEventType;
/** Interface for Filters */
public interface Filter {
/** @return the default filter used at startup */
static Filter getDefaultFilter() {
return Filter.intersect(new Filter[]{new HideKnownFilter(), new TextFilter(), new TypeFilter(RootEventType.getInstance())});
}
/** @param <S> the type of the given filters
* @param filters a set of filters to intersect
*
* @return a filter that is the intersection of the given filters */
public static IntersectionFilter intersect(ObservableList<Filter> filters) {
return new IntersectionFilter(filters);
}
/** @param <S> the type of the given filters
* @param filters a set of filters to intersect
*
* @return a filter that is the intersection of the given filters */
public static IntersectionFilter intersect(Filter[] filters) {
return new IntersectionFilter(FXCollections.observableArrayList(filters));
}
/** since filters have mutable state (active) and are observed in various
* places, we need a mechanism to copy the current state to keep in history.
*
* Concrete subtasks should implement this in a way that preserves the
* active state and any subfilters.
*
* @return a copy of this filter. */
Filter copyOf();
String getDisplayName();
String getHTMLReportString();
String getStringCheckBox();
boolean isActive();
void setActive(Boolean act);
SimpleBooleanProperty getActiveProperty();
/* TODO: disabled state only affects the state of the checkboxes in the ui
* and not the actual filters and shouldn't be implemented here, but it
* was too hard to figure out how it should be implemented without intruding
* on the ui-ignorant filters */
void setDisabled(Boolean act);
SimpleBooleanProperty getDisabledProperty();
boolean isdisabled();
}

View File

@ -0,0 +1,69 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.timeline.filters;
/**
* Filter to hide known files
*/
public class HideKnownFilter extends AbstractFilter {
@Override
public String getDisplayName() {
return "Hide Known Files";
}
public HideKnownFilter() {
super();
getActiveProperty().set(false);
}
@Override
public HideKnownFilter copyOf() {
HideKnownFilter hideKnownFilter = new HideKnownFilter();
hideKnownFilter.setActive(isActive());
hideKnownFilter.setDisabled(isdisabled());
return hideKnownFilter;
}
@Override
public String getHTMLReportString() {
return "hide known" + getStringCheckBox();
}
@Override
public int hashCode() {
int hash = 7;
return hash;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final HideKnownFilter other = (HideKnownFilter) obj;
return isActive() == other.isActive();
}
}

View File

@ -0,0 +1,70 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.sleuthkit.autopsy.timeline.filters;
import java.util.stream.Collectors;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
/** Intersection(And) filter */
public class IntersectionFilter extends CompoundFilter {
public IntersectionFilter(ObservableList<Filter> subFilters) {
super(subFilters);
}
public IntersectionFilter() {
super(FXCollections.<Filter>observableArrayList());
}
@Override
public IntersectionFilter copyOf() {
IntersectionFilter filter = new IntersectionFilter(FXCollections.observableArrayList(
this.getSubFilters().stream()
.map(Filter::copyOf)
.collect(Collectors.toList())));
filter.setActive(isActive());
filter.setDisabled(isdisabled());
return filter;
}
@Override
public String getDisplayName() {
return "Intersection";
}
@Override
public String getHTMLReportString() {
return getSubFilters().stream().filter(Filter::isActive).map(Filter::getHTMLReportString).collect(Collectors.joining("</li><li>", "<ul><li>", "</li></ul>"));
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final IntersectionFilter other = (IntersectionFilter) obj;
if (isActive() != other.isActive()) {
return false;
}
for (int i = 0; i < getSubFilters().size(); i++) {
if (getSubFilters().get(i).equals(other.getSubFilters().get(i)) == false) {
return false;
}
}
return true;
}
@Override
public int hashCode() {
int hash = 7;
return hash;
}
}

View File

@ -0,0 +1,91 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2013 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.timeline.filters;
import java.util.Objects;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleStringProperty;
import org.apache.commons.lang3.StringUtils;
/** Filter for text matching */
public class TextFilter extends AbstractFilter {
public TextFilter() {
}
public TextFilter(String text) {
this.text.set(text);
}
private final SimpleStringProperty text = new SimpleStringProperty();
synchronized public void setText(String text) {
this.text.set(text);
}
@Override
public String getDisplayName() {
return "Text Filter";
}
synchronized public String getText() {
return text.getValue();
}
public Property<String> textProperty() {
return text;
}
@Override
synchronized public TextFilter copyOf() {
TextFilter textFilter = new TextFilter(getText());
textFilter.setActive(isActive());
textFilter.setDisabled(isdisabled());
return textFilter;
}
@Override
public String getHTMLReportString() {
return "text like \"" + StringUtils.defaultIfBlank(text.getValue(), "") + "\"" + getStringCheckBox();
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final TextFilter other = (TextFilter) obj;
if (isActive() != other.isActive()) {
return false;
}
return Objects.equals(text.get(), other.text.get());
}
@Override
public int hashCode() {
int hash = 5;
hash = 29 * hash + Objects.hashCode(this.text.get());
return hash;
}
}

View File

@ -0,0 +1,136 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2013 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.timeline.filters;
import java.util.Objects;
import java.util.stream.Collectors;
import javafx.collections.FXCollections;
import javafx.scene.image.Image;
import javafx.scene.paint.Color;
import org.sleuthkit.autopsy.timeline.events.type.EventType;
import org.sleuthkit.autopsy.timeline.events.type.RootEventType;
/** Event Type Filter. An instance of TypeFilter is usually a tree that
* parallels the event type hierarchy with one filter/node for each event type. */
public class TypeFilter extends UnionFilter {
/** the event type this filter passes */
private final EventType eventType;
/** private constructor that enables non recursive/tree construction of the
* filter hierarchy for use in {@link TypeFilter#copyOf()}.
*
* @param et the event type this filter passes
* @param recursive true if subfilters should be added for each subtype.
* False if no subfilters should be added. */
private TypeFilter(EventType et, boolean recursive) {
super(FXCollections.observableArrayList());
this.eventType = et;
if (recursive) { // add subfilters for each subtype
for (EventType subType : et.getSubTypes()) {
this.getSubFilters().add(new TypeFilter(subType));
}
}
}
/** public constructor. creates a subfilter for each subtype of the given
* event type
*
* @param et the event type this filter will pass */
public TypeFilter(EventType et) {
this(et, true);
}
public EventType getEventType() {
return eventType;
}
@Override
public String getDisplayName() {
return eventType == RootEventType.getInstance() ? "Event Type Filter" : eventType.getDisplayName();
}
/** @return a color to use in GUI components representing this filter */
public Color getColor() {
return eventType.getColor();
}
/** @return an image to use in GUI components representing this filter */
public Image getFXImage() {
return eventType.getFXImage();
}
@Override
public TypeFilter copyOf() {
//make a nonrecursive copy of this filter
final TypeFilter typeFilter = new TypeFilter(eventType, false);
typeFilter.setActive(isActive());
typeFilter.setDisabled(isdisabled());
//add a copy of each subfilter
this.getSubFilters().forEach((Filter t) -> {
typeFilter.getSubFilters().add(t.copyOf());
});
return typeFilter;
}
@Override
public String getHTMLReportString() {
String string = getEventType().getDisplayName() + getStringCheckBox();
if (getSubFilters().isEmpty() == false) {
string = string + " : " + getSubFilters().stream().filter(Filter::isActive).map(Filter::getHTMLReportString).collect(Collectors.joining("</li><li>", "<ul><li>", "</li></ul>"));
}
return string;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final TypeFilter other = (TypeFilter) obj;
if (isActive() != other.isActive()) {
return false;
}
if (this.eventType != other.eventType) {
return false;
}
for (int i = 0; i < getSubFilters().size(); i++) {
if (getSubFilters().get(i).equals(other.getSubFilters().get(i)) == false) {
return false;
}
}
return true;
}
@Override
public int hashCode() {
int hash = 7;
hash = 67 * hash + Objects.hashCode(this.eventType);
return hash;
}
}

View File

@ -0,0 +1,25 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.sleuthkit.autopsy.timeline.filters;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
/**
*
* Union(or) filter
*/
abstract public class UnionFilter extends CompoundFilter{
public UnionFilter(ObservableList<Filter> subFilters) {
super(subFilters);
}
public UnionFilter() {
super(FXCollections.<Filter>observableArrayList());
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 959 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1022 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 864 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 760 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 776 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 577 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 587 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 600 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 594 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 609 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 643 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 642 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 638 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 654 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 519 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 727 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 691 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 672 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 541 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 817 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 689 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 591 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 436 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 958 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 661 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 516 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 791 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Some files were not shown because too many files have changed in this diff Show More