Merge remote-tracking branch 'upstream/develop' into bulk_extractor_module
3
.gitignore
vendored
@ -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
|
||||
|
@ -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>
|
||||
|
38
Core/src/org/sleuthkit/autopsy/coreutils/ColorUtilities.java
Normal 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));
|
||||
}
|
||||
}
|
198
Core/src/org/sleuthkit/autopsy/coreutils/History.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
75
Core/src/org/sleuthkit/autopsy/coreutils/LoggedTask.java
Normal 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());
|
||||
}
|
||||
}
|
||||
}
|
22
Core/src/org/sleuthkit/autopsy/coreutils/ThreadConfined.java
Normal 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
|
||||
}
|
||||
}
|
@ -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:
|
||||
|
@ -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, "{key}")"/>
|
||||
</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>
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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
|
65
Core/src/org/sleuthkit/autopsy/timeline/FXMLConstructor.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/advancedtimeline/Bundle.properties" key="ProgressWindow.progressHeader.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JProgressBar" name="progressBar">
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
246
Core/src/org/sleuthkit/autopsy/timeline/ProgressWindow.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
690
Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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, "{key}")"/>
|
||||
</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]"/>
|
@ -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;
|
||||
}
|
||||
}
|
27
Core/src/org/sleuthkit/autopsy/timeline/TimeLineUI.java
Normal 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);
|
||||
}
|
35
Core/src/org/sleuthkit/autopsy/timeline/TimeLineView.java
Normal 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);
|
||||
|
||||
|
||||
}
|
@ -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
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
51
Core/src/org/sleuthkit/autopsy/timeline/actions/Back.java
Normal 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();
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
51
Core/src/org/sleuthkit/autopsy/timeline/actions/Forward.java
Normal 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();
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
56
Core/src/org/sleuthkit/autopsy/timeline/actions/ZoomOut.java
Normal 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();
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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());
|
||||
// }
|
||||
// }
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
1122
Core/src/org/sleuthkit/autopsy/timeline/events/db/EventDB.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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 "";
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
@ -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" : " ") + "]";
|
||||
}
|
||||
}
|
@ -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));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
81
Core/src/org/sleuthkit/autopsy/timeline/filters/Filter.java
Normal 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();
|
||||
|
||||
}
|
@ -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();
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
136
Core/src/org/sleuthkit/autopsy/timeline/filters/TypeFilter.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
@ -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());
|
||||
}
|
||||
|
||||
|
||||
}
|
After Width: | Height: | Size: 959 B |
After Width: | Height: | Size: 1.4 KiB |
BIN
Core/src/org/sleuthkit/autopsy/timeline/images/arrow-090.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
Core/src/org/sleuthkit/autopsy/timeline/images/arrow-180.png
Normal file
After Width: | Height: | Size: 1022 B |
BIN
Core/src/org/sleuthkit/autopsy/timeline/images/arrow-270.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 864 B |
BIN
Core/src/org/sleuthkit/autopsy/timeline/images/arrow-in.png
Normal file
After Width: | Height: | Size: 760 B |
BIN
Core/src/org/sleuthkit/autopsy/timeline/images/arrow-out.png
Normal file
After Width: | Height: | Size: 776 B |
After Width: | Height: | Size: 577 B |
BIN
Core/src/org/sleuthkit/autopsy/timeline/images/arrow-step.png
Normal file
After Width: | Height: | Size: 587 B |
BIN
Core/src/org/sleuthkit/autopsy/timeline/images/arrow.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
Core/src/org/sleuthkit/autopsy/timeline/images/arrow_in.png
Normal file
After Width: | Height: | Size: 600 B |
BIN
Core/src/org/sleuthkit/autopsy/timeline/images/arrow_out.png
Normal file
After Width: | Height: | Size: 594 B |
BIN
Core/src/org/sleuthkit/autopsy/timeline/images/block.png
Normal file
After Width: | Height: | Size: 609 B |
After Width: | Height: | Size: 643 B |
After Width: | Height: | Size: 642 B |
After Width: | Height: | Size: 638 B |
After Width: | Height: | Size: 654 B |
BIN
Core/src/org/sleuthkit/autopsy/timeline/images/blue-document.png
Normal file
After Width: | Height: | Size: 519 B |
After Width: | Height: | Size: 727 B |
BIN
Core/src/org/sleuthkit/autopsy/timeline/images/bookmarks.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
Core/src/org/sleuthkit/autopsy/timeline/images/btn_step_back.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.6 KiB |
BIN
Core/src/org/sleuthkit/autopsy/timeline/images/calllog.png
Normal file
After Width: | Height: | Size: 691 B |
After Width: | Height: | Size: 672 B |
BIN
Core/src/org/sleuthkit/autopsy/timeline/images/chart_bar.png
Normal file
After Width: | Height: | Size: 541 B |
BIN
Core/src/org/sleuthkit/autopsy/timeline/images/checker64.jpg
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
Core/src/org/sleuthkit/autopsy/timeline/images/checkerboard.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.0 KiB |
BIN
Core/src/org/sleuthkit/autopsy/timeline/images/clock-history.png
Normal file
After Width: | Height: | Size: 817 B |
BIN
Core/src/org/sleuthkit/autopsy/timeline/images/cookies.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 3.0 KiB |
BIN
Core/src/org/sleuthkit/autopsy/timeline/images/cross-circle.png
Normal file
After Width: | Height: | Size: 689 B |
BIN
Core/src/org/sleuthkit/autopsy/timeline/images/downloads.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
Core/src/org/sleuthkit/autopsy/timeline/images/funnel--minus.png
Normal file
After Width: | Height: | Size: 630 B |
BIN
Core/src/org/sleuthkit/autopsy/timeline/images/funnel.png
Normal file
After Width: | Height: | Size: 591 B |
BIN
Core/src/org/sleuthkit/autopsy/timeline/images/geolocation.png
Normal file
After Width: | Height: | Size: 436 B |
BIN
Core/src/org/sleuthkit/autopsy/timeline/images/gps-search.png
Normal file
After Width: | Height: | Size: 958 B |
After Width: | Height: | Size: 661 B |
BIN
Core/src/org/sleuthkit/autopsy/timeline/images/history.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
Core/src/org/sleuthkit/autopsy/timeline/images/image.png
Normal file
After Width: | Height: | Size: 516 B |
BIN
Core/src/org/sleuthkit/autopsy/timeline/images/info-icon-16.png
Normal file
After Width: | Height: | Size: 791 B |
BIN
Core/src/org/sleuthkit/autopsy/timeline/images/information.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 1.1 KiB |