diff --git a/.gitignore b/.gitignore index 7fe49ad362..b89812646f 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/Core/build.xml b/Core/build.xml index 9dd720c2d6..2d313a2de7 100644 --- a/Core/build.xml +++ b/Core/build.xml @@ -6,33 +6,10 @@ Builds, tests, and runs the project org.sleuthkit.autopsy.core - - - - - - - - - - - - - - - - - - - - - - - - + @@ -53,12 +30,7 @@ - - - - - diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/ColorUtilities.java b/Core/src/org/sleuthkit/autopsy/coreutils/ColorUtilities.java new file mode 100644 index 0000000000..d51e919df7 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/coreutils/ColorUtilities.java @@ -0,0 +1,38 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2013 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.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)); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/History.java b/Core/src/org/sleuthkit/autopsy/coreutils/History.java new file mode 100644 index 0000000000..6ef81b1e38 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/coreutils/History.java @@ -0,0 +1,198 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2014 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * 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 . 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 the type of objects used to represent the + * current/historical/future states + */ +@ThreadSafe +public class History { + + @GuardedBy("this") + private final ObservableStack historyStack = new ObservableStack<>(); + + @GuardedBy("this") + private final ObservableStack forwardStack = new ObservableStack<>(); + + @GuardedBy("this") + private final ReadOnlyObjectWrapper 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 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 extends SimpleListProperty { + + public ObservableStack() { + super(FXCollections.synchronizedObservableList(FXCollections.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); + } + } + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/LoggedTask.java b/Core/src/org/sleuthkit/autopsy/coreutils/LoggedTask.java new file mode 100644 index 0000000000..9bc3f09d27 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/coreutils/LoggedTask.java @@ -0,0 +1,75 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2013-14 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.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 extends Task 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()); + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/ThreadConfined.java b/Core/src/org/sleuthkit/autopsy/coreutils/ThreadConfined.java new file mode 100644 index 0000000000..5527c01b26 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/coreutils/ThreadConfined.java @@ -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 + } +} diff --git a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties index 017c40bc15..74fbc9b59c 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties @@ -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: diff --git a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesIdentifierIngestJobSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesIdentifierIngestJobSettingsPanel.form index ec9fb17f55..f36b0434a9 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesIdentifierIngestJobSettingsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesIdentifierIngestJobSettingsPanel.form @@ -1,6 +1,15 @@
+ + + + + + + + + @@ -18,7 +27,7 @@ - + @@ -27,7 +36,7 @@ - + diff --git a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesIdentifierIngestJobSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesIdentifierIngestJobSettingsPanel.java index dda2a8ddf9..b96330a9da 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesIdentifierIngestJobSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesIdentifierIngestJobSettingsPanel.java @@ -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()) ); }// //GEN-END:initComponents diff --git a/Core/src/org/sleuthkit/autopsy/timeline/Bundle.properties b/Core/src/org/sleuthkit/autopsy/timeline/Bundle.properties index b320647823..a2a287e322 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/timeline/Bundle.properties @@ -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" diff --git a/Core/src/org/sleuthkit/autopsy/timeline/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/timeline/Bundle_ja.properties deleted file mode 100644 index b905bd1205..0000000000 --- a/Core/src/org/sleuthkit/autopsy/timeline/Bundle_ja.properties +++ /dev/null @@ -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 \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/timeline/FXMLConstructor.java b/Core/src/org/sleuthkit/autopsy/timeline/FXMLConstructor.java new file mode 100644 index 0000000000..d97551d00a --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/timeline/FXMLConstructor.java @@ -0,0 +1,65 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2013 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.timeline; + +import java.io.IOException; +import java.net.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); + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java b/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java new file mode 100644 index 0000000000..fd1f450370 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java @@ -0,0 +1,96 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2013 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.timeline; + +import java.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 + } +} diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimelineProgressDialog.form b/Core/src/org/sleuthkit/autopsy/timeline/ProgressWindow.form similarity index 81% rename from Core/src/org/sleuthkit/autopsy/timeline/TimelineProgressDialog.form rename to Core/src/org/sleuthkit/autopsy/timeline/ProgressWindow.form index 0a8a131d09..6c99e006c8 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimelineProgressDialog.form +++ b/Core/src/org/sleuthkit/autopsy/timeline/ProgressWindow.form @@ -1,6 +1,6 @@ - + @@ -28,7 +28,7 @@ - + @@ -40,23 +40,23 @@ - - + + - + - + + + - + - - diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ProgressWindow.java b/Core/src/org/sleuthkit/autopsy/timeline/ProgressWindow.java new file mode 100644 index 0000000000..e8f8239c72 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/timeline/ProgressWindow.java @@ -0,0 +1,246 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2013 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.timeline; + +import java.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") + // //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(); + }// //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; + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java new file mode 100644 index 0000000000..5dbc0800d0 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java @@ -0,0 +1,690 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2014 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * 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: