mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-13 00:16:16 +00:00
Merge pull request #1724 from millmanorama/TL-consolidated-update-prompts
Tl consolidated update prompts
This commit is contained in:
commit
af2b9513a3
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.timeline;
|
||||
|
||||
import javafx.beans.property.ReadOnlyBooleanProperty;
|
||||
import javafx.concurrent.Task;
|
||||
|
||||
/**
|
||||
* An extension of Task that allows a client to request cancellation, with out
|
||||
* the Task entering the cancelled state immediately. This allows the task to
|
||||
* continue to report progress of eg its cleanup operations. Implementations
|
||||
* should use the {@link #isCancelRequested() } method to check for cancelation
|
||||
* and call cancel() before returning form the call() method.
|
||||
*/
|
||||
public abstract class CancellationProgressTask<X> extends Task<X> {
|
||||
|
||||
private boolean cancelRequested;
|
||||
|
||||
public synchronized boolean isCancelRequested() {
|
||||
return cancelRequested || isCancelled();
|
||||
}
|
||||
|
||||
public synchronized boolean requestCancel() {
|
||||
this.cancelRequested = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
abstract public ReadOnlyBooleanProperty cancellableProperty();
|
||||
|
||||
boolean isCancellable() {
|
||||
return cancellableProperty().get();
|
||||
}
|
||||
}
|
@ -73,12 +73,14 @@ public class OpenTimelineAction extends CallableSystemAction {
|
||||
LOGGER.log(Level.INFO, "Could not create timeline, there are no data sources.");// NON-NLS
|
||||
return;
|
||||
}
|
||||
if (timeLineController == null) {
|
||||
timeLineController = new TimeLineController(currentCase);
|
||||
} else if (timeLineController.getAutopsyCase() != currentCase) {
|
||||
timeLineController.closeTimeLine();
|
||||
timeLineController = new TimeLineController(currentCase);
|
||||
}
|
||||
|
||||
if (timeLineController == null) {
|
||||
timeLineController = new TimeLineController(currentCase);
|
||||
} else if (timeLineController.getAutopsyCase() != currentCase) {
|
||||
timeLineController.closeTimeLine();
|
||||
timeLineController = new TimeLineController(currentCase);
|
||||
}
|
||||
|
||||
timeLineController.openTimeLine();
|
||||
}
|
||||
|
||||
|
166
Core/src/org/sleuthkit/autopsy/timeline/PromptDialogManager.java
Normal file
166
Core/src/org/sleuthkit/autopsy/timeline/PromptDialogManager.java
Normal file
@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2015 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.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.logging.Level;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Alert;
|
||||
import javafx.scene.control.ButtonBar;
|
||||
import javafx.scene.control.ButtonType;
|
||||
import javafx.scene.control.Dialog;
|
||||
import javafx.scene.control.DialogPane;
|
||||
import javafx.scene.control.ListView;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.stage.Modality;
|
||||
import javafx.stage.Stage;
|
||||
import org.controlsfx.dialog.ProgressDialog;
|
||||
import org.controlsfx.tools.Borders;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
||||
|
||||
/**
|
||||
* Manager for the various prompts Timeline shows the user related to rebuilding
|
||||
* the database.
|
||||
*/
|
||||
public class PromptDialogManager {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(PromptDialogManager.class.getName());
|
||||
|
||||
@NbBundle.Messages("PrompDialogManager.buttonType.showTimeline=Show Timeline")
|
||||
private static final ButtonType SHOW_TIMELINE = new ButtonType(Bundle.PrompDialogManager_buttonType_showTimeline(), ButtonBar.ButtonData.OK_DONE);
|
||||
|
||||
@NbBundle.Messages("PrompDialogManager.buttonType.continueNoUpdate=Continue Without Updating")
|
||||
private static final ButtonType CONTINUE_NO_UPDATE = new ButtonType(Bundle.PrompDialogManager_buttonType_continueNoUpdate(), ButtonBar.ButtonData.CANCEL_CLOSE);
|
||||
|
||||
@NbBundle.Messages("PrompDialogManager.buttonType.update=Update")
|
||||
private static final ButtonType UPDATE = new ButtonType(Bundle.PrompDialogManager_buttonType_update(), ButtonBar.ButtonData.OK_DONE);
|
||||
|
||||
private static final Image LOGO;
|
||||
|
||||
static {
|
||||
Image x = null;
|
||||
try {
|
||||
x = new Image(new URL("nbresloc:/org/netbeans/core/startup/frame.gif").openStream()); //NOI18N
|
||||
} catch (IOException ex) {
|
||||
LOGGER.log(Level.WARNING, "Failed to load branded icon for progress dialog.", ex); //NOI18N
|
||||
}
|
||||
LOGO = x;
|
||||
}
|
||||
private Dialog<?> currentDialog;
|
||||
|
||||
private final TimeLineController controller;
|
||||
|
||||
PromptDialogManager(TimeLineController controller) {
|
||||
this.controller = controller;
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
||||
boolean bringCurrentDialogToFront() {
|
||||
if (currentDialog != null && currentDialog.isShowing()) {
|
||||
((Stage) currentDialog.getDialogPane().getScene().getWindow()).toFront();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@NbBundle.Messages({"PromptDialogManager.progressDialog.title=Populating Timeline Data"})
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
||||
public void showProgressDialog(CancellationProgressTask<?> task) {
|
||||
currentDialog = new ProgressDialog(task);
|
||||
currentDialog.headerTextProperty().bind(task.titleProperty());
|
||||
setDialogIcons(currentDialog);
|
||||
currentDialog.setTitle(Bundle.PromptDialogManager_progressDialog_title());
|
||||
|
||||
DialogPane dialogPane = currentDialog.getDialogPane();
|
||||
dialogPane.setPrefSize(400, 200); //override autosizing which fails for some reason
|
||||
|
||||
//co-ordinate task cancelation and dialog hiding.
|
||||
task.setOnCancelled(cancelled -> currentDialog.close());
|
||||
task.setOnSucceeded(succeeded -> currentDialog.close());
|
||||
dialogPane.getButtonTypes().setAll(ButtonType.CANCEL);
|
||||
final Node cancelButton = dialogPane.lookupButton(ButtonType.CANCEL);
|
||||
cancelButton.disableProperty().bind(task.cancellableProperty().not());
|
||||
currentDialog.setOnCloseRequest(closeRequest -> {
|
||||
if (task.isRunning()) {
|
||||
closeRequest.consume();
|
||||
}
|
||||
if (task.isCancellable() && task.isCancelRequested() == false) {
|
||||
task.requestCancel();
|
||||
}
|
||||
});
|
||||
|
||||
currentDialog.show();
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
||||
static private void setDialogIcons(Dialog<?> dialog) {
|
||||
Stage stage = (Stage) dialog.getDialogPane().getScene().getWindow();
|
||||
stage.getIcons().setAll(LOGO);
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
||||
static private void setDialogTitle(Dialog<?> dialog) {
|
||||
Stage stage = (Stage) dialog.getDialogPane().getScene().getWindow();
|
||||
stage.setTitle(Bundle.Timeline_confirmation_dialogs_title());
|
||||
}
|
||||
|
||||
/**
|
||||
* prompt the user that ingest is running and the db may not end up
|
||||
* complete.
|
||||
*
|
||||
* @return true if they want to continue anyways
|
||||
*/
|
||||
@NbBundle.Messages({"PromptDialogManager.confirmDuringIngest.headerText=You are trying to show a timeline before ingest has been completed.\nThe timeline may be incomplete.",
|
||||
"PromptDialogManager.confirmDuringIngest.contentText=Do you want to continue?"})
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
||||
boolean confirmDuringIngest() {
|
||||
currentDialog = new Alert(Alert.AlertType.CONFIRMATION, Bundle.PromptDialogManager_confirmDuringIngest_contentText(), SHOW_TIMELINE, ButtonType.CANCEL);
|
||||
currentDialog.initModality(Modality.APPLICATION_MODAL);
|
||||
currentDialog.setHeaderText(Bundle.PromptDialogManager_confirmDuringIngest_headerText());
|
||||
setDialogIcons(currentDialog);
|
||||
setDialogTitle(currentDialog);
|
||||
|
||||
return currentDialog.showAndWait().map(SHOW_TIMELINE::equals).orElse(false);
|
||||
}
|
||||
|
||||
@NbBundle.Messages({"PromptDialogManager.rebuildPrompt.headerText=The Timeline database is incomplete and/or out of date.\nSome events may be missing or inaccurate and some features may be unavailable.",
|
||||
"PromptDialogManager.rebuildPrompt.details=Details:"})
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
||||
boolean confirmRebuild(ArrayList<String> rebuildReasons) {
|
||||
currentDialog = new Alert(Alert.AlertType.CONFIRMATION, Bundle.TimeLinecontroller_updateNowQuestion(), UPDATE, CONTINUE_NO_UPDATE);
|
||||
currentDialog.initModality(Modality.APPLICATION_MODAL);
|
||||
currentDialog.setHeaderText(Bundle.PromptDialogManager_rebuildPrompt_headerText());
|
||||
setDialogIcons(currentDialog);
|
||||
setDialogTitle(currentDialog);
|
||||
|
||||
DialogPane dialogPane = currentDialog.getDialogPane();
|
||||
ListView<String> listView = new ListView<>(FXCollections.observableArrayList(rebuildReasons));
|
||||
listView.setCellFactory(lstView -> new WrappingListCell());
|
||||
listView.setMaxHeight(75);
|
||||
Node wrappedListView = Borders.wrap(listView).lineBorder().title(Bundle.PromptDialogManager_rebuildPrompt_details()).buildAll();
|
||||
dialogPane.setExpandableContent(wrappedListView);
|
||||
|
||||
return currentDialog.showAndWait().map(UPDATE::equals).orElse(false);
|
||||
}
|
||||
}
|
@ -21,12 +21,11 @@ package org.sleuthkit.autopsy.timeline;
|
||||
import java.awt.HeadlessException;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.text.NumberFormat;
|
||||
import java.time.ZoneId;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.MissingResourceException;
|
||||
@ -52,15 +51,11 @@ import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.concurrent.Task;
|
||||
import javafx.concurrent.Worker;
|
||||
import javafx.scene.control.ButtonType;
|
||||
import javafx.scene.control.DialogPane;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.scene.control.Dialog;
|
||||
import javax.annotation.concurrent.GuardedBy;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.SwingUtilities;
|
||||
import org.controlsfx.dialog.ProgressDialog;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeZone;
|
||||
import org.joda.time.Interval;
|
||||
@ -78,6 +73,7 @@ import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent;
|
||||
import org.sleuthkit.autopsy.coreutils.History;
|
||||
import org.sleuthkit.autopsy.coreutils.LoggedTask;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
||||
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||
import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel;
|
||||
@ -110,7 +106,8 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
* </li>
|
||||
* <ul>
|
||||
*/
|
||||
@NbBundle.Messages({"Timeline.confirmation.dialogs.title=Update Timeline database?"})
|
||||
@NbBundle.Messages({"Timeline.confirmation.dialogs.title=Update Timeline database?",
|
||||
"TimeLinecontroller.updateNowQuestion=Do you want to update the events database now?"})
|
||||
public class TimeLineController {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(TimeLineController.class.getName());
|
||||
@ -122,7 +119,7 @@ public class TimeLineController {
|
||||
}
|
||||
|
||||
public static DateTimeFormatter getZonedFormatter() {
|
||||
return DateTimeFormat.forPattern("YYYY-MM-dd HH:mm:ss").withZone(getJodaTimeZone()); // NON-NLS
|
||||
return DateTimeFormat.forPattern("YYYY-MM-dd HH:mm:ss").withZone(getJodaTimeZone()); // NON-NLS //NOI18N
|
||||
}
|
||||
|
||||
public static DateTimeZone getJodaTimeZone() {
|
||||
@ -137,16 +134,16 @@ public class TimeLineController {
|
||||
|
||||
private final ReadOnlyListWrapper<Task<?>> tasks = new ReadOnlyListWrapper<>(FXCollections.observableArrayList());
|
||||
|
||||
private final ReadOnlyDoubleWrapper progress = new ReadOnlyDoubleWrapper(-1);
|
||||
private final ReadOnlyDoubleWrapper taskProgress = new ReadOnlyDoubleWrapper(-1);
|
||||
|
||||
private final ReadOnlyStringWrapper message = new ReadOnlyStringWrapper();
|
||||
private final ReadOnlyStringWrapper taskMessage = new ReadOnlyStringWrapper();
|
||||
|
||||
private final ReadOnlyStringWrapper taskTitle = new ReadOnlyStringWrapper();
|
||||
|
||||
private final ReadOnlyStringWrapper status = new ReadOnlyStringWrapper();
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
||||
private ProgressDialog taskProgressDialog;
|
||||
private Dialog<?> currentDialog;
|
||||
|
||||
/**
|
||||
* status is a string that will be displayed in the status bar as a kind of
|
||||
@ -181,15 +178,15 @@ public class TimeLineController {
|
||||
return tasks.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
synchronized public ReadOnlyDoubleProperty getProgress() {
|
||||
return progress.getReadOnlyProperty();
|
||||
synchronized public ReadOnlyDoubleProperty taskProgressProperty() {
|
||||
return taskProgress.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
synchronized public ReadOnlyStringProperty getMessage() {
|
||||
return message.getReadOnlyProperty();
|
||||
synchronized public ReadOnlyStringProperty taskMessageProperty() {
|
||||
return taskMessage.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
synchronized public ReadOnlyStringProperty getTaskTitle() {
|
||||
synchronized public ReadOnlyStringProperty taskTitleProperty() {
|
||||
return taskTitle.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
@ -266,6 +263,8 @@ public class TimeLineController {
|
||||
}
|
||||
private final ReadOnlyBooleanWrapper newEventsFlag = new ReadOnlyBooleanWrapper(false);
|
||||
|
||||
private final PromptDialogManager promptDialogManager = new PromptDialogManager(this);
|
||||
|
||||
public TimeLineController(Case autoCase) {
|
||||
this.autoCase = autoCase;
|
||||
|
||||
@ -312,60 +311,52 @@ public class TimeLineController {
|
||||
/**
|
||||
* rebuld the repo.
|
||||
*
|
||||
* @return False if the repo was not rebuilt because of an error or because
|
||||
* the user aborted after prompt about ingest running. True if the
|
||||
* repo was rebuilt.
|
||||
* @return False if the repo was not rebuilt because because the user
|
||||
* aborted after prompt about ingest running. True if the repo was
|
||||
* rebuilt.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
boolean rebuildRepo() {
|
||||
if (IngestManager.getInstance().isIngestRunning()) {
|
||||
//confirm timeline during ingest
|
||||
if (confirmRebuildDuringIngest() == false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
||||
void rebuildRepo() {
|
||||
SwingUtilities.invokeLater(this::closeTimelineWindow);
|
||||
Platform.runLater(() -> {
|
||||
final Worker<Void> rebuildRepository = eventsRepository.rebuildRepository();
|
||||
rebuildRepository.stateProperty().addListener((stateProperty, oldState, newSate) -> {
|
||||
//this will be on JFX thread
|
||||
if (newSate == Worker.State.SUCCEEDED) {
|
||||
//TODO: this looks hacky. what is going on? should this be an event?
|
||||
needsHistogramRebuild.set(true);
|
||||
needsHistogramRebuild.set(false);
|
||||
SwingUtilities.invokeLater(TimeLineController.this::showWindow);
|
||||
final CancellationProgressTask<?> rebuildRepository = eventsRepository.rebuildRepository();
|
||||
rebuildRepository.stateProperty().addListener((stateProperty, oldState, newSate) -> {
|
||||
//this will be on JFX thread
|
||||
if (newSate == Worker.State.SUCCEEDED) {
|
||||
//TODO: this looks hacky. what is going on? should this be an event?
|
||||
needsHistogramRebuild.set(true);
|
||||
needsHistogramRebuild.set(false);
|
||||
SwingUtilities.invokeLater(TimeLineController.this::showWindow);
|
||||
|
||||
//TODO: should this be an event?
|
||||
newEventsFlag.set(false);
|
||||
historyManager.reset(filteredEvents.zoomParametersProperty().get());
|
||||
TimeLineController.this.showFullRange();
|
||||
//TODO: should this be an event?
|
||||
newEventsFlag.set(false);
|
||||
historyManager.reset(filteredEvents.zoomParametersProperty().get());
|
||||
TimeLineController.this.showFullRange();
|
||||
|
||||
}
|
||||
});
|
||||
showProgressDialog(rebuildRepository);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
promptDialogManager.showProgressDialog(rebuildRepository);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Since tags might have changed while TimeLine wasn't listening, drop the
|
||||
* tags table and rebuild it by querying for all the tags and inserting them
|
||||
* in to the TimeLine DB.
|
||||
*
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
||||
void rebuildTagsTable() {
|
||||
|
||||
SwingUtilities.invokeLater(this::closeTimelineWindow);
|
||||
Platform.runLater(() -> {
|
||||
Worker<Void> rebuildTags = eventsRepository.rebuildTags();
|
||||
rebuildTags.stateProperty().addListener((stateProperty, oldState, newSate) -> {
|
||||
//this will be on JFX thread
|
||||
if (newSate == Worker.State.SUCCEEDED) {
|
||||
SwingUtilities.invokeLater(TimeLineController.this::showWindow);
|
||||
showFullRange();
|
||||
}
|
||||
});
|
||||
showProgressDialog(rebuildTags);
|
||||
CancellationProgressTask<?> rebuildTags = eventsRepository.rebuildTags();
|
||||
rebuildTags.stateProperty().addListener((stateProperty, oldState, newSate) -> {
|
||||
//this will be on JFX thread
|
||||
if (newSate == Worker.State.SUCCEEDED) {
|
||||
SwingUtilities.invokeLater(TimeLineController.this::showWindow);
|
||||
showFullRange();
|
||||
}
|
||||
});
|
||||
promptDialogManager.showProgressDialog(rebuildTags);
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@ -375,32 +366,6 @@ public class TimeLineController {
|
||||
}
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
||||
@NbBundle.Messages({"Timeline.progressWindow.title=Populating Timeline Data"})
|
||||
private void showProgressDialog(Worker<Void> task) {
|
||||
taskProgressDialog = new ProgressDialog(task);
|
||||
taskProgressDialog.setTitle(Bundle.Timeline_progressWindow_title());
|
||||
DialogPane dialogPane = taskProgressDialog.getDialogPane();
|
||||
dialogPane.getButtonTypes().add(ButtonType.CANCEL);
|
||||
Stage dialogStage = (Stage) dialogPane.getScene().getWindow();
|
||||
dialogPane.setPrefWidth(400);
|
||||
taskProgressDialog.headerTextProperty().bind(task.titleProperty());
|
||||
dialogStage.getIcons().setAll(LOGO);
|
||||
taskProgressDialog.setOnCloseRequest(closeRequestEvent -> task.cancel());
|
||||
}
|
||||
|
||||
private static final Image LOGO;
|
||||
|
||||
static {
|
||||
Image x = null;
|
||||
try {
|
||||
x = new Image(new URL("nbresloc:/org/netbeans/core/startup/frame.gif").openStream());
|
||||
} catch (IOException ex) {
|
||||
LOGGER.log(Level.WARNING, "Failed to laod branded icon for progress dialog.", ex);
|
||||
}
|
||||
LOGO = x;
|
||||
}
|
||||
|
||||
public void showFullRange() {
|
||||
synchronized (filteredEvents) {
|
||||
pushTimeRange(filteredEvents.getSpanningInterval());
|
||||
@ -432,78 +397,96 @@ public class TimeLineController {
|
||||
listeningToAutopsy = true;
|
||||
}
|
||||
|
||||
try {
|
||||
if (eventsRepository.isRebuilding()) {
|
||||
Platform.runLater(() -> {
|
||||
if (taskProgressDialog != null) {
|
||||
((Stage) taskProgressDialog.getDialogPane().getScene().getWindow()).toFront();
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
boolean repoRebuilt = false; //has the repo been rebuilt
|
||||
long timeLineLastObjectId = eventsRepository.getLastObjID();
|
||||
|
||||
//if the repo is empty rebuild it
|
||||
if (timeLineLastObjectId == -1) {
|
||||
repoRebuilt = rebuildRepo();
|
||||
}
|
||||
|
||||
if (repoRebuilt == false) {
|
||||
//if ingest was running uring last rebuild, prompt to rebuild
|
||||
if (eventsRepository.getWasIngestRunning()) {
|
||||
if (confirmLastBuiltDuringIngestRebuild()) {
|
||||
repoRebuilt = rebuildRepo();
|
||||
Platform.runLater(() -> {
|
||||
try {
|
||||
if (promptDialogManager.bringCurrentDialogToFront()) {
|
||||
return;
|
||||
}
|
||||
if (IngestManager.getInstance().isIngestRunning()) {
|
||||
//confirm timeline during ingest
|
||||
if (promptDialogManager.confirmDuringIngest() == false) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (repoRebuilt == false) {
|
||||
final SleuthkitCase sleuthkitCase = autoCase.getSleuthkitCase();
|
||||
//if the last artifact and object ids don't match between skc and tldb, prompt to rebuild
|
||||
if (sleuthkitCase.getLastObjectId() != timeLineLastObjectId
|
||||
|| getCaseLastArtifactID(sleuthkitCase) != eventsRepository.getLastArtfactID()) {
|
||||
if (confirmOutOfDateRebuild()) {
|
||||
repoRebuilt = rebuildRepo();
|
||||
}
|
||||
/*
|
||||
* if the repo was not rebuilt at minimum rebuild the tags which
|
||||
* may have been updated without our knowing it, since we
|
||||
* can't/aren't checking them. This should at elast be quick.
|
||||
* //TODO: can we check the tags to see if we need to do this?
|
||||
*/
|
||||
if (checkAndPromptForRebuild() == false) {
|
||||
rebuildTagsTable();
|
||||
}
|
||||
}
|
||||
|
||||
if (repoRebuilt == false) {
|
||||
// if the TLDB schema has been upgraded since last time TL ran, prompt for rebuild
|
||||
if (eventsRepository.hasNewColumns() == false) {
|
||||
if (confirmDataSourceIDsMissingRebuild()) {
|
||||
repoRebuilt = rebuildRepo();
|
||||
}
|
||||
}
|
||||
} catch (HeadlessException | MissingResourceException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Unexpected error when generating timeline, ", ex); // NON-NLS //NOI18N
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* if the repo was not rebuilt at minimum rebuild the tags which may
|
||||
* have been updated without our knowing it.
|
||||
*/
|
||||
if (repoRebuilt == false) {
|
||||
rebuildTagsTable();
|
||||
}
|
||||
|
||||
} catch (TskCoreException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Error when generating timeline, ", ex); // NON-NLS
|
||||
} catch (HeadlessException | MissingResourceException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Unexpected error when generating timeline, ", ex); // NON-NLS
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
||||
private boolean checkAndPromptForRebuild() {
|
||||
//if the repo is empty just (r)ebuild it with out asking, they can always cancel part way through;
|
||||
if (eventsRepository.getLastObjID() == -1) {
|
||||
rebuildRepo();
|
||||
return true;
|
||||
}
|
||||
|
||||
ArrayList<String> rebuildReasons = getRebuildReasons();
|
||||
if (rebuildReasons.isEmpty() == false) {
|
||||
if (promptDialogManager.confirmRebuild(rebuildReasons)) {
|
||||
rebuildRepo();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.ANY)
|
||||
@NbBundle.Messages({"TimeLineController.errorTitle=Timeline error.",
|
||||
"TimeLineController.outOfDate.errorMessage=Error determing if the timeline is out of date. We will assume it should be updated. See the logs for more details.",
|
||||
"TimeLineController.rebuildReasons.outOfDateError=Could not determine if the timeline data is out of date.",
|
||||
"TimeLineController.rebuildReasons.outOfDate=The event data is out of date: Not all events will be visible.",
|
||||
"TimeLineController.rebuildReasons.ingestWasRunning=The Timeline events database was previously populated while ingest was running: Some events may be missing, incomplete, or inaccurate.",
|
||||
"TimeLineController.rebuildReasons.incompleteOldSchema=The Timeline events database was previously populated without incomplete information: Some features may be unavailable or non-functional unless you update the events database."})
|
||||
private ArrayList<String> getRebuildReasons() {
|
||||
ArrayList<String> rebuildReasons = new ArrayList<>();
|
||||
//if ingest was running during last rebuild, prompt to rebuild
|
||||
if (eventsRepository.getWasIngestRunning()) {
|
||||
rebuildReasons.add(Bundle.TimeLineController_rebuildReasons_ingestWasRunning());
|
||||
}
|
||||
final SleuthkitCase sleuthkitCase = autoCase.getSleuthkitCase();
|
||||
try {
|
||||
//if the last artifact and object ids don't match between skc and tldb, prompt to rebuild
|
||||
if (sleuthkitCase.getLastObjectId() != eventsRepository.getLastObjID()
|
||||
|| getCaseLastArtifactID(sleuthkitCase) != eventsRepository.getLastArtfactID()) {
|
||||
rebuildReasons.add(Bundle.TimeLineController_rebuildReasons_outOfDate());
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Error determing last object id from sleutkit case. We will assume the timeline is out of date.", ex); // NON-NLS
|
||||
MessageNotifyUtil.Notify.error(Bundle.TimeLineController_errorTitle(),
|
||||
Bundle.TimeLineController_outOfDate_errorMessage());
|
||||
rebuildReasons.add(Bundle.TimeLineController_rebuildReasons_outOfDateError());
|
||||
}
|
||||
// if the TLDB schema has been upgraded since last time TL ran, prompt for rebuild
|
||||
if (eventsRepository.hasNewColumns() == false) {
|
||||
rebuildReasons.add(Bundle.TimeLineController_rebuildReasons_incompleteOldSchema());
|
||||
}
|
||||
return rebuildReasons;
|
||||
}
|
||||
|
||||
public static long getCaseLastArtifactID(final SleuthkitCase sleuthkitCase) {
|
||||
//TODO: push this into sleuthkitCase
|
||||
long caseLastArtfId = -1;
|
||||
String query = "select Max(artifact_id) as max_id from blackboard_artifacts"; // NON-NLS
|
||||
String query = "select Max(artifact_id) as max_id from blackboard_artifacts"; // NON-NLS //NOI18N
|
||||
try (CaseDbQuery dbQuery = sleuthkitCase.executeQuery(query)) {
|
||||
ResultSet resultSet = dbQuery.getResultSet();
|
||||
while (resultSet.next()) {
|
||||
caseLastArtfId = resultSet.getLong("max_id"); // NON-NLS
|
||||
caseLastArtfId = resultSet.getLong("max_id"); // NON-NLS //NOI18N
|
||||
}
|
||||
} catch (TskCoreException | SQLException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Error getting last artifact id: ", ex); // NON-NLS
|
||||
LOGGER.log(Level.SEVERE, "Error getting last artifact id: ", ex); // NON-NLS //NOI18N
|
||||
}
|
||||
return caseLastArtfId;
|
||||
}
|
||||
@ -544,7 +527,7 @@ public class TimeLineController {
|
||||
}
|
||||
|
||||
public void selectEventIDs(Collection<Long> events) {
|
||||
final LoggedTask<Interval> selectEventIDsTask = new LoggedTask<Interval>("Select Event IDs", true) { // NON-NLS
|
||||
final LoggedTask<Interval> selectEventIDsTask = new LoggedTask<Interval>("Select Event IDs", true) { // NON-NLS //NOI18N
|
||||
@Override
|
||||
protected Interval call() throws Exception {
|
||||
return filteredEvents.getSpanningInterval(events);
|
||||
@ -557,13 +540,10 @@ public class TimeLineController {
|
||||
synchronized (TimeLineController.this) {
|
||||
selectedTimeRange.set(get());
|
||||
selectedEventIDs.setAll(events);
|
||||
|
||||
}
|
||||
} catch (InterruptedException ex) {
|
||||
Logger.getLogger(FilteredEventsModel.class
|
||||
.getName()).log(Level.SEVERE, getTitle() + " interrupted unexpectedly", ex); // NON-NLS
|
||||
} catch (ExecutionException ex) {
|
||||
Logger.getLogger(FilteredEventsModel.class
|
||||
.getName()).log(Level.SEVERE, getTitle() + " unexpectedly threw " + ex.getCause(), ex); // NON-NLS
|
||||
} catch (InterruptedException | ExecutionException ex) {
|
||||
LOGGER.log(Level.SEVERE, getTitle() + " Unexpected error", ex); // NON-NLS //NOI18N
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -607,8 +587,7 @@ public class TimeLineController {
|
||||
}
|
||||
|
||||
@NbBundle.Messages({"# {0} - the number of events",
|
||||
"Timeline.pushDescrLOD.confdlg.msg=You are about to show details for {0} events."
|
||||
+ " This might be very slow or even crash Autopsy.\n\nDo you want to continue?",
|
||||
"Timeline.pushDescrLOD.confdlg.msg=You are about to show details for {0} events. This might be very slow or even crash Autopsy.\n\nDo you want to continue?",
|
||||
"Timeline.pushDescrLOD.confdlg.title=Change description level of detail?"})
|
||||
synchronized public boolean pushDescrLOD(DescriptionLoD newLOD) {
|
||||
Map<EventType, Long> eventCounts = filteredEvents.getEventCounts(filteredEvents.zoomParametersProperty().get().getTimeRange());
|
||||
@ -675,7 +654,7 @@ public class TimeLineController {
|
||||
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) { // NON-NLS
|
||||
final LoggedTask<Collection<Long>> selectTimeAndTypeTask = new LoggedTask<Collection<Long>>("Select Time and Type", true) { // NON-NLS //NOI18N
|
||||
@Override
|
||||
protected Collection< Long> call() throws Exception {
|
||||
synchronized (TimeLineController.this) {
|
||||
@ -690,13 +669,10 @@ public class TimeLineController {
|
||||
synchronized (TimeLineController.this) {
|
||||
selectedTimeRange.set(timeRange);
|
||||
selectedEventIDs.setAll(get());
|
||||
|
||||
}
|
||||
} catch (InterruptedException ex) {
|
||||
Logger.getLogger(FilteredEventsModel.class
|
||||
.getName()).log(Level.SEVERE, getTitle() + " interrupted unexpectedly", ex);// NON-NLS
|
||||
} catch (ExecutionException ex) {
|
||||
Logger.getLogger(FilteredEventsModel.class
|
||||
.getName()).log(Level.SEVERE, getTitle() + " unexpectedly threw " + ex.getCause(), ex);// NON-NLS
|
||||
} catch (InterruptedException | ExecutionException ex) {
|
||||
LOGGER.log(Level.SEVERE, getTitle() + " Unexpected error", ex); // NON-NLS //NOI18N
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -711,6 +687,7 @@ public class TimeLineController {
|
||||
* @param task
|
||||
*/
|
||||
synchronized public void monitorTask(final Task<?> task) {
|
||||
//TODO: refactor this to use JavaFX Service? -jm
|
||||
if (task != null) {
|
||||
Platform.runLater(() -> {
|
||||
|
||||
@ -726,16 +703,16 @@ public class TimeLineController {
|
||||
case FAILED:
|
||||
tasks.remove(task);
|
||||
if (tasks.isEmpty() == false) {
|
||||
progress.bind(tasks.get(0).progressProperty());
|
||||
message.bind(tasks.get(0).messageProperty());
|
||||
taskProgress.bind(tasks.get(0).progressProperty());
|
||||
taskMessage.bind(tasks.get(0).messageProperty());
|
||||
taskTitle.bind(tasks.get(0).titleProperty());
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
tasks.add(task);
|
||||
progress.bind(task.progressProperty());
|
||||
message.bind(task.messageProperty());
|
||||
taskProgress.bind(task.progressProperty());
|
||||
taskMessage.bind(task.messageProperty());
|
||||
taskTitle.bind(task.titleProperty());
|
||||
switch (task.getState()) {
|
||||
case READY:
|
||||
@ -749,8 +726,8 @@ public class TimeLineController {
|
||||
case FAILED:
|
||||
tasks.remove(task);
|
||||
if (tasks.isEmpty() == false) {
|
||||
progress.bind(tasks.get(0).progressProperty());
|
||||
message.bind(tasks.get(0).messageProperty());
|
||||
taskProgress.bind(tasks.get(0).progressProperty());
|
||||
taskMessage.bind(tasks.get(0).messageProperty());
|
||||
taskTitle.bind(tasks.get(0).titleProperty());
|
||||
}
|
||||
break;
|
||||
@ -788,79 +765,11 @@ public class TimeLineController {
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
private void confirmOutOfDateRebuildIfWindowOpen() throws MissingResourceException, HeadlessException {
|
||||
if (isWindowOpen()) {
|
||||
if (confirmOutOfDateRebuild()) {
|
||||
rebuildRepo();
|
||||
}
|
||||
Platform.runLater(this::checkAndPromptForRebuild);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* prompt the user to rebuild the db because that datasource_ids are missing
|
||||
* from the database and that the datasource filter will not work
|
||||
*
|
||||
* @return true if they agree to rebuild
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@NbBundle.Messages({"datasource.missing.confirmation=The Timeline events database was previously populated with an old version of Autopsy."
|
||||
+ "\nThe data source filter will be unavailable unless you update the events database."
|
||||
+ "\nDo you want to update the events database now?"})
|
||||
synchronized boolean confirmDataSourceIDsMissingRebuild() {
|
||||
return JOptionPane.showConfirmDialog(mainFrame,
|
||||
Bundle.datasource_missing_confirmation(),
|
||||
Bundle.Timeline_confirmation_dialogs_title(),
|
||||
JOptionPane.YES_NO_OPTION,
|
||||
JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION;
|
||||
}
|
||||
|
||||
/**
|
||||
* prompt the user to rebuild the db because the db was last build during
|
||||
* ingest and may be incomplete
|
||||
*
|
||||
* @return true if they agree to rebuild
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@NbBundle.Messages({"Timeline.do_repopulate.msg=The Timeline events database was previously populated while ingest was running."
|
||||
+ "\nSome events may not have been populated or may have been populated inaccurately."
|
||||
+ "\nDo you want to repopulate the events database now?"})
|
||||
synchronized boolean confirmLastBuiltDuringIngestRebuild() {
|
||||
return JOptionPane.showConfirmDialog(mainFrame,
|
||||
Bundle.Timeline_do_repopulate_msg(),
|
||||
Bundle.Timeline_confirmation_dialogs_title(),
|
||||
JOptionPane.YES_NO_OPTION,
|
||||
JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION;
|
||||
}
|
||||
|
||||
/**
|
||||
* prompt the user to rebuild the db because the db is out of date and
|
||||
* doesn't include things from subsequent ingests
|
||||
*
|
||||
* @return true if they agree to rebuild
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@NbBundle.Messages({"Timeline.propChg.confDlg.timelineOOD.msg=The event data is out of date. Would you like to regenerate it?",})
|
||||
synchronized boolean confirmOutOfDateRebuild() throws MissingResourceException, HeadlessException {
|
||||
return JOptionPane.showConfirmDialog(mainFrame,
|
||||
Bundle.Timeline_propChg_confDlg_timelineOOD_msg(),
|
||||
Bundle.Timeline_confirmation_dialogs_title(),
|
||||
JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION;
|
||||
}
|
||||
|
||||
/**
|
||||
* prompt the user that ingest is running and the db may not end up
|
||||
* complete.
|
||||
*
|
||||
* @return true if they want to continue anyways
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
@NbBundle.Messages({"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?"})
|
||||
synchronized boolean confirmRebuildDuringIngest() throws MissingResourceException, HeadlessException {
|
||||
return JOptionPane.showConfirmDialog(mainFrame,
|
||||
Bundle.Timeline_initTimeline_confDlg_genBeforeIngest_msg(),
|
||||
Bundle.Timeline_confirmation_dialogs_title(),
|
||||
JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION;
|
||||
}
|
||||
|
||||
private class AutopsyIngestModuleListener implements PropertyChangeListener {
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
|
||||
package org.sleuthkit.autopsy.timeline;
|
||||
|
||||
import javafx.scene.control.ListCell;
|
||||
import javafx.scene.text.Text;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class WrappingListCell extends ListCell<String> {
|
||||
|
||||
@Override
|
||||
public void updateItem(String item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
if (item == null || isEmpty()) {
|
||||
setGraphic(null);
|
||||
} else {
|
||||
Text text = new Text(item);
|
||||
text.wrappingWidthProperty().bind(getListView().widthProperty().subtract(20));
|
||||
setGraphic(text);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -355,14 +355,14 @@ public final class FilteredEventsModel {
|
||||
synchronized public boolean handleContentTagAdded(ContentTagAddedEvent evt) {
|
||||
ContentTag contentTag = evt.getAddedTag();
|
||||
Content content = contentTag.getContent();
|
||||
Set<Long> updatedEventIDs = repo.addTag(content.getId(), null, contentTag);
|
||||
Set<Long> updatedEventIDs = repo.addTag(content.getId(), null, contentTag, null);
|
||||
return postTagsUpdated(updatedEventIDs);
|
||||
}
|
||||
|
||||
synchronized public boolean handleArtifactTagAdded(BlackBoardArtifactTagAddedEvent evt) {
|
||||
BlackboardArtifactTag artifactTag = evt.getAddedTag();
|
||||
BlackboardArtifact artifact = artifactTag.getArtifact();
|
||||
Set<Long> updatedEventIDs = repo.addTag(artifact.getObjectID(), artifact.getArtifactID(), artifactTag);;
|
||||
Set<Long> updatedEventIDs = repo.addTag(artifact.getObjectID(), artifact.getArtifactID(), artifactTag, null);
|
||||
return postTagsUpdated(updatedEventIDs);
|
||||
}
|
||||
|
||||
|
@ -810,7 +810,10 @@ public class EventDB {
|
||||
*
|
||||
* @return the event ids that match the object/artifact pair
|
||||
*/
|
||||
Set<Long> addTag(long objectID, @Nullable Long artifactID, Tag tag) {
|
||||
Set<Long> addTag(long objectID, @Nullable Long artifactID, Tag tag, EventTransaction transaction) {
|
||||
if (transaction != null && transaction.isClosed()) {
|
||||
throw new IllegalArgumentException("can't update database with closed transaction"); // NON-NLS
|
||||
}
|
||||
DBLock.lock();
|
||||
try {
|
||||
Set<Long> eventIDs = markEventsTagged(objectID, artifactID, true);
|
||||
@ -1265,7 +1268,6 @@ public class EventDB {
|
||||
DBLock.lock();
|
||||
try {
|
||||
con.setAutoCommit(false);
|
||||
|
||||
} catch (SQLException ex) {
|
||||
LOGGER.log(Level.SEVERE, "failed to set auto-commit to to false", ex); // NON-NLS
|
||||
}
|
||||
|
@ -31,21 +31,18 @@ import java.util.Map;
|
||||
import static java.util.Objects.isNull;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.FutureTask;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.ReadOnlyBooleanProperty;
|
||||
import javafx.beans.property.ReadOnlyBooleanWrapper;
|
||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.collections.ObservableMap;
|
||||
import javafx.concurrent.Service;
|
||||
import javafx.concurrent.Task;
|
||||
import javafx.concurrent.Worker;
|
||||
import javax.swing.JOptionPane;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.joda.time.Interval;
|
||||
@ -57,6 +54,7 @@ import org.sleuthkit.autopsy.casemodule.services.TagsManager;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
||||
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||
import org.sleuthkit.autopsy.timeline.CancellationProgressTask;
|
||||
import org.sleuthkit.autopsy.timeline.TimeLineController;
|
||||
import org.sleuthkit.autopsy.timeline.datamodel.EventCluster;
|
||||
import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel;
|
||||
@ -98,16 +96,14 @@ public class EventsRepository {
|
||||
|
||||
private final static Logger LOGGER = Logger.getLogger(EventsRepository.class.getName());
|
||||
|
||||
private final Executor workerExecutor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("eventrepository-worker-%d").build());
|
||||
private DBPopulationWorker dbWorker;
|
||||
private final EventDB eventDB;
|
||||
|
||||
private final DBPopulationService dbPopulationService = new DBPopulationService(this);
|
||||
|
||||
private final LoadingCache<Object, Long> maxCache;
|
||||
|
||||
private final LoadingCache<Object, Long> minCache;
|
||||
|
||||
private final Case autoCase;
|
||||
private final FilteredEventsModel modelInstance;
|
||||
|
||||
private final LoadingCache<Object, Long> maxCache;
|
||||
private final LoadingCache<Object, Long> minCache;
|
||||
private final LoadingCache<Long, TimeLineEvent> idToEventCache;
|
||||
private final LoadingCache<ZoomParams, Map<EventType, Long>> eventCountsCache;
|
||||
private final LoadingCache<ZoomParams, List<EventCluster>> eventClusterCache;
|
||||
@ -115,7 +111,6 @@ public class EventsRepository {
|
||||
private final ObservableMap<Long, String> datasourcesMap = FXCollections.observableHashMap();
|
||||
private final ObservableMap<Long, String> hashSetMap = FXCollections.observableHashMap();
|
||||
private final ObservableList<TagName> tagNames = FXCollections.observableArrayList();
|
||||
private final Case autoCase;
|
||||
|
||||
public Case getAutoCase() {
|
||||
return autoCase;
|
||||
@ -286,8 +281,8 @@ public class EventsRepository {
|
||||
}
|
||||
}
|
||||
|
||||
synchronized public Set<Long> addTag(long objID, Long artifactID, Tag tag) {
|
||||
Set<Long> updatedEventIDs = eventDB.addTag(objID, artifactID, tag);
|
||||
synchronized public Set<Long> addTag(long objID, Long artifactID, Tag tag, EventDB.EventTransaction trans) {
|
||||
Set<Long> updatedEventIDs = eventDB.addTag(objID, artifactID, tag, trans);
|
||||
if (!updatedEventIDs.isEmpty()) {
|
||||
invalidateCaches(updatedEventIDs);
|
||||
}
|
||||
@ -330,67 +325,6 @@ public class EventsRepository {
|
||||
}
|
||||
}
|
||||
|
||||
static private final Executor workerExecutor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("eventrepository-worker-%d").build());
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
||||
public Worker<Void> rebuildRepository() {
|
||||
return rebuildRepository(DBPopulationService.DBPopulationMode.FULL);
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
||||
public Worker<Void> rebuildTags() {
|
||||
return rebuildRepository(DBPopulationService.DBPopulationMode.TAGS_ONLY);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param mode the value of mode
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
||||
private Worker<Void> rebuildRepository(final DBPopulationService.DBPopulationMode mode) {
|
||||
LOGGER.log(Level.INFO, "(re) starting {0}db population task", mode);
|
||||
dbPopulationService.setDBPopulationMode(mode);
|
||||
dbPopulationService.restart();
|
||||
return dbPopulationService;
|
||||
}
|
||||
|
||||
private static class DBPopulationService extends Service<Void> {
|
||||
|
||||
enum DBPopulationMode {
|
||||
|
||||
FULL,
|
||||
TAGS_ONLY;
|
||||
}
|
||||
|
||||
private final EventsRepository eventRepo;
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
||||
private DBPopulationMode dbPopulationMode = DBPopulationMode.FULL;
|
||||
|
||||
DBPopulationService(EventsRepository eventRepo) {
|
||||
this.eventRepo = eventRepo;
|
||||
setExecutor(workerExecutor);
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
||||
public final void setDBPopulationMode(DBPopulationMode value) {
|
||||
dbPopulationMode = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Task<Void> createTask() {
|
||||
DBPopulationMode dbPopMode = dbPopulationMode;
|
||||
switch (dbPopMode) {
|
||||
case FULL:
|
||||
return eventRepo.new DBPopulationWorker();
|
||||
case TAGS_ONLY:
|
||||
return eventRepo.new RebuildTagsWorker();
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown db population mode: " + dbPopMode + ". Skipping db population.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param lastObjId the value of lastObjId
|
||||
@ -403,35 +337,69 @@ public class EventsRepository {
|
||||
recordWasIngestRunning(injestRunning);
|
||||
}
|
||||
|
||||
public boolean areFiltersEquivalent(RootFilter f1, RootFilter f2) {
|
||||
return SQLHelper.getSQLWhere(f1).equals(SQLHelper.getSQLWhere(f2));
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
||||
public boolean isRebuilding() {
|
||||
FutureTask<Boolean> task = new FutureTask<>(dbPopulationService::isRunning);
|
||||
Platform.runLater(task);
|
||||
try {
|
||||
return task.get();
|
||||
} catch (InterruptedException | ExecutionException exception) {
|
||||
LOGGER.log(Level.SEVERE, "There was an error determining the state of the db population service.", exception);
|
||||
}
|
||||
return false;
|
||||
return dbWorker.isRunning();
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
||||
public CancellationProgressTask<Void> rebuildRepository() {
|
||||
return rebuildRepository(DBPopulationMode.FULL);
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
||||
public CancellationProgressTask<Void> rebuildTags() {
|
||||
return rebuildRepository(DBPopulationMode.TAGS_ONLY);
|
||||
}
|
||||
|
||||
/**
|
||||
* A base class for Tasks that show a updates a {@link ProgressHandle} as it
|
||||
* performs its background work on the events DB.
|
||||
*
|
||||
* //TODO: I don't like the coupling to ProgressHandle, but the
|
||||
* alternatives I can think of seem even worse. -jm
|
||||
* @param mode the value of mode
|
||||
*/
|
||||
private abstract class DBProgressWorker extends Task<Void> {
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
||||
private CancellationProgressTask<Void> rebuildRepository(final DBPopulationMode mode) {
|
||||
LOGGER.log(Level.INFO, "(re)starting {0} db population task", mode);
|
||||
if (dbWorker != null) {
|
||||
dbWorker.cancel();
|
||||
}
|
||||
dbWorker = new DBPopulationWorker(mode);
|
||||
workerExecutor.execute(dbWorker);
|
||||
return dbWorker;
|
||||
}
|
||||
|
||||
final SleuthkitCase skCase;
|
||||
final TagsManager tagsManager;
|
||||
private enum DBPopulationMode {
|
||||
|
||||
volatile ProgressHandle progressHandle;
|
||||
FULL,
|
||||
TAGS_ONLY;
|
||||
}
|
||||
|
||||
DBProgressWorker(String initialProgressDisplayName) {
|
||||
progressHandle = ProgressHandleFactory.createHandle(initialProgressDisplayName, this::cancel);
|
||||
skCase = autoCase.getSleuthkitCase();
|
||||
tagsManager = autoCase.getServices().getTagsManager();
|
||||
/**
|
||||
* //TODO: I don't like the coupling to ProgressHandle in this task, but
|
||||
* the alternatives I can think of seem even worse. -jm
|
||||
*/
|
||||
private class DBPopulationWorker extends CancellationProgressTask<Void> {
|
||||
|
||||
private final ReadOnlyBooleanWrapper cancellable = new ReadOnlyBooleanWrapper(true);
|
||||
|
||||
private final DBPopulationMode dbPopulationMode;
|
||||
private final SleuthkitCase skCase;
|
||||
private final TagsManager tagsManager;
|
||||
|
||||
private ProgressHandle progressHandle;
|
||||
|
||||
@Override
|
||||
public ReadOnlyBooleanProperty cancellableProperty() {
|
||||
return cancellable.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requestCancel() {
|
||||
Platform.runLater(() -> cancellable.set(false));
|
||||
return super.requestCancel();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -462,157 +430,124 @@ public class EventsRepository {
|
||||
progressHandle.progress((int) workDone);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean areFiltersEquivalent(RootFilter f1, RootFilter f2) {
|
||||
return SQLHelper.getSQLWhere(f1).equals(SQLHelper.getSQLWhere(f2));
|
||||
}
|
||||
DBPopulationWorker(DBPopulationMode mode) {
|
||||
skCase = autoCase.getSleuthkitCase();
|
||||
tagsManager = autoCase.getServices().getTagsManager();
|
||||
this.dbPopulationMode = mode;
|
||||
}
|
||||
|
||||
private class RebuildTagsWorker extends DBProgressWorker {
|
||||
void restartProgressHandle(String title, String message, Double workDone, double total, Boolean cancellable) {
|
||||
if (progressHandle != null) {
|
||||
progressHandle.finish();
|
||||
}
|
||||
progressHandle = cancellable
|
||||
? ProgressHandleFactory.createHandle(title, this::requestCancel)
|
||||
: ProgressHandleFactory.createHandle(title);
|
||||
|
||||
@NbBundle.Messages("RebuildTagsWorker.task.displayName=refreshing tags")
|
||||
RebuildTagsWorker() {
|
||||
super(Bundle.RebuildTagsWorker_task_displayName());
|
||||
if (workDone < 0) {
|
||||
progressHandle.start();
|
||||
} else {
|
||||
progressHandle.start((int) total);
|
||||
}
|
||||
updateTitle(title);
|
||||
updateMessage(message);
|
||||
updateProgress(workDone, total);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NbBundle.Messages({"progressWindow.msg.refreshingFileTags=refreshing file tags",
|
||||
"progressWindow.msg.refreshingResultTags=refreshing result tags",
|
||||
"progressWindow.msg.commitingTags=committing tag changes"})
|
||||
@NbBundle.Messages({"progressWindow.msg.refreshingFileTags=Refreshing file tags",
|
||||
"progressWindow.msg.refreshingResultTags=Refreshing result tags",
|
||||
"progressWindow.msg.gatheringData=Gather event data",
|
||||
"progressWindow.msg.commitingDb=Committing events database"})
|
||||
protected Void call() throws Exception {
|
||||
progressHandle.start();
|
||||
EventDB.EventTransaction trans = eventDB.beginTransaction();
|
||||
LOGGER.log(Level.INFO, "dropping old tags"); // NON-NLS
|
||||
eventDB.reInitializeTags();
|
||||
EventDB.EventTransaction trans = null;
|
||||
|
||||
//save paramaters for recording later
|
||||
long lastObjId = skCase.getLastObjectId();
|
||||
long lastArtfID = TimeLineController.getCaseLastArtifactID(skCase);
|
||||
boolean injestRunning = IngestManager.getInstance().isIngestRunning();
|
||||
|
||||
if (dbPopulationMode == DBPopulationMode.FULL) {
|
||||
//drop old db, and add back MAC and artifact events
|
||||
LOGGER.log(Level.INFO, "Beginning population of timeline db."); // NON-NLS
|
||||
restartProgressHandle(Bundle.progressWindow_msg_gatheringData(), "", -1D, 1, true);
|
||||
//reset database //TODO: can we do more incremental updates? -jm
|
||||
eventDB.reInitializeDB();
|
||||
//grab ids of all files
|
||||
List<Long> fileIDs = skCase.findAllFileIdsWhere("name != '.' AND name != '..'");
|
||||
final int numFiles = fileIDs.size();
|
||||
|
||||
trans = eventDB.beginTransaction();
|
||||
insertMACTimeEvents(numFiles, fileIDs, trans);
|
||||
insertArtifactDerivedEvents(trans);
|
||||
}
|
||||
|
||||
//tags
|
||||
if (dbPopulationMode == DBPopulationMode.TAGS_ONLY) {
|
||||
trans = eventDB.beginTransaction();
|
||||
LOGGER.log(Level.INFO, "dropping old tags"); // NON-NLS
|
||||
eventDB.reInitializeTags();
|
||||
}
|
||||
|
||||
LOGGER.log(Level.INFO, "updating content tags"); // NON-NLS
|
||||
List<ContentTag> contentTags = tagsManager.getAllContentTags();
|
||||
progressHandle.finish();
|
||||
progressHandle = ProgressHandleFactory.createHandle(Bundle.progressWindow_msg_refreshingFileTags(), this::cancel);
|
||||
updateTitle(Bundle.progressWindow_msg_refreshingFileTags());
|
||||
int currentWorkTotal = contentTags.size();
|
||||
progressHandle.start(currentWorkTotal);
|
||||
|
||||
for (int i = 0; i < currentWorkTotal; i++) {
|
||||
if (isCancelled()) {
|
||||
break;
|
||||
}
|
||||
updateProgress(i, currentWorkTotal);
|
||||
ContentTag contentTag = contentTags.get(i);
|
||||
eventDB.addTag(contentTag.getContent().getId(), null, contentTag);
|
||||
}
|
||||
restartProgressHandle(Bundle.progressWindow_msg_refreshingFileTags(), "", 0D, currentWorkTotal, true);
|
||||
insertContentTags(currentWorkTotal, contentTags, trans);
|
||||
|
||||
LOGGER.log(Level.INFO, "updating artifact tags"); // NON-NLS
|
||||
List<BlackboardArtifactTag> artifactTags = tagsManager.getAllBlackboardArtifactTags();
|
||||
progressHandle.finish();
|
||||
progressHandle = ProgressHandleFactory.createHandle(Bundle.progressWindow_msg_refreshingResultTags(), this::cancel);
|
||||
updateTitle(Bundle.progressWindow_msg_refreshingResultTags());
|
||||
currentWorkTotal = artifactTags.size();
|
||||
progressHandle.start(currentWorkTotal);
|
||||
for (int i = 0; i < currentWorkTotal; i++) {
|
||||
if (isCancelled()) {
|
||||
break;
|
||||
}
|
||||
updateProgress(i, currentWorkTotal);
|
||||
BlackboardArtifactTag artifactTag = artifactTags.get(i);
|
||||
eventDB.addTag(artifactTag.getContent().getId(), artifactTag.getArtifact().getArtifactID(), artifactTag);
|
||||
restartProgressHandle(Bundle.progressWindow_msg_refreshingResultTags(), "", 0D, currentWorkTotal, true);
|
||||
insertArtifactTags(currentWorkTotal, artifactTags, trans);
|
||||
|
||||
LOGGER.log(Level.INFO, "committing db"); // NON-NLS
|
||||
Platform.runLater(() -> cancellable.set(false));
|
||||
restartProgressHandle(Bundle.progressWindow_msg_commitingDb(), "", -1D, 1, false);
|
||||
eventDB.commitTransaction(trans);
|
||||
if (isCancelRequested() == false) {
|
||||
recordDBPopulationState(lastObjId, lastArtfID, injestRunning);
|
||||
}
|
||||
|
||||
LOGGER.log(Level.INFO, "committing tags"); // NON-NLS
|
||||
progressHandle.finish();
|
||||
progressHandle = ProgressHandleFactory.createHandle(Bundle.progressWindow_msg_commitingTags());
|
||||
progressHandle.start();
|
||||
updateTitle(Bundle.progressWindow_msg_commitingTags());
|
||||
updateProgress(-.5, 1);
|
||||
|
||||
if (isCancelled()) {
|
||||
eventDB.rollBackTransaction(trans);
|
||||
} else {
|
||||
eventDB.commitTransaction(trans);
|
||||
}
|
||||
eventDB.analyze();
|
||||
populateFilterData(skCase);
|
||||
invalidateCaches();
|
||||
|
||||
progressHandle.finish();
|
||||
if (isCancelRequested()) {
|
||||
cancel();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NbBundle.Messages("msgdlg.tagsproblem.text=There was a problem refreshing the tagged events."
|
||||
+ " Some events may have inacurate tags. See the log for details.")
|
||||
protected void done() {
|
||||
super.done();
|
||||
try {
|
||||
get();
|
||||
} catch (CancellationException ex) {
|
||||
LOGGER.log(Level.WARNING, "Timeline database population was cancelled by the user. "
|
||||
+ "Not all events may be present or accurate."); // NON-NLS
|
||||
} catch (Exception ex) {
|
||||
LOGGER.log(Level.WARNING, "Unexpected exception while populating database.", ex); // NON-NLS
|
||||
JOptionPane.showMessageDialog(null, Bundle.msgdlg_tagsproblem_text());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class DBPopulationWorker extends DBProgressWorker {
|
||||
|
||||
@NbBundle.Messages("DBPopulationWorker.task.displayName=(re)initializing events database")
|
||||
DBPopulationWorker() {
|
||||
super(Bundle.DBPopulationWorker_task_displayName());
|
||||
}
|
||||
|
||||
@Override
|
||||
@NbBundle.Messages({"progressWindow.msg.populateMacEventsFiles=Populating MAC time events for files",
|
||||
"progressWindow.msg.reinit_db=(Re)Initializing events database",
|
||||
"progressWindow.msg.gatheringData=Gather event data",
|
||||
"progressWindow.msg.commitingDb=committing events db"})
|
||||
protected Void call() throws Exception {
|
||||
LOGGER.log(Level.INFO, "Beginning population of timeline db."); // NON-NLS
|
||||
progressHandle.start();
|
||||
updateProgress(-.5, 1);
|
||||
updateTitle(Bundle.progressWindow_msg_reinit_db());
|
||||
//reset database //TODO: can we do more incremental updates? -jm
|
||||
eventDB.reInitializeDB();
|
||||
|
||||
updateTitle(Bundle.progressWindow_msg_gatheringData());
|
||||
long lastObjId = skCase.getLastObjectId();
|
||||
long lastArtfID = TimeLineController.getCaseLastArtifactID(skCase);
|
||||
boolean injestRunning = IngestManager.getInstance().isIngestRunning();
|
||||
|
||||
//grab ids of all files
|
||||
List<Long> fileIDs = skCase.findAllFileIdsWhere("name != '.' AND name != '..'");
|
||||
final int numFiles = fileIDs.size();
|
||||
progressHandle.switchToDeterminate(numFiles);
|
||||
updateTitle(Bundle.progressWindow_msg_populateMacEventsFiles());
|
||||
|
||||
//insert file events into db
|
||||
EventDB.EventTransaction trans = eventDB.beginTransaction();
|
||||
for (int i = 0; i < numFiles; i++) {
|
||||
if (isCancelled()) {
|
||||
private void insertArtifactTags(int currentWorkTotal, List<BlackboardArtifactTag> artifactTags, EventDB.EventTransaction trans) {
|
||||
for (int i = 0; i < currentWorkTotal; i++) {
|
||||
if (isCancelRequested()) {
|
||||
break;
|
||||
} else {
|
||||
long fID = fileIDs.get(i);
|
||||
try {
|
||||
AbstractFile f = skCase.getAbstractFileById(fID);
|
||||
|
||||
if (isNull(f)) {
|
||||
LOGGER.log(Level.WARNING, "Failed to get data for file : {0}", fID); // NON-NLS
|
||||
} else {
|
||||
insertEventsForFile(f, trans);
|
||||
updateProgress(i, numFiles);
|
||||
updateMessage(f.getName());
|
||||
}
|
||||
} catch (TskCoreException tskCoreException) {
|
||||
LOGGER.log(Level.SEVERE, "Failed to insert MAC time events for file : " + fID, tskCoreException); // NON-NLS
|
||||
}
|
||||
}
|
||||
updateProgress(i, currentWorkTotal);
|
||||
BlackboardArtifactTag artifactTag = artifactTags.get(i);
|
||||
eventDB.addTag(artifactTag.getContent().getId(), artifactTag.getArtifact().getArtifactID(), artifactTag, trans);
|
||||
}
|
||||
}
|
||||
|
||||
private void insertContentTags(int currentWorkTotal, List<ContentTag> contentTags, EventDB.EventTransaction trans) {
|
||||
for (int i = 0; i < currentWorkTotal; i++) {
|
||||
if (isCancelRequested()) {
|
||||
break;
|
||||
}
|
||||
updateProgress(i, currentWorkTotal);
|
||||
ContentTag contentTag = contentTags.get(i);
|
||||
eventDB.addTag(contentTag.getContent().getId(), null, contentTag, trans);
|
||||
}
|
||||
}
|
||||
|
||||
private void insertArtifactDerivedEvents(EventDB.EventTransaction trans) {
|
||||
//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()) {
|
||||
if (isCancelRequested()) {
|
||||
break;
|
||||
}
|
||||
//skip file_system events, they are already handled above.
|
||||
@ -620,25 +555,30 @@ public class EventsRepository {
|
||||
populateEventType((ArtifactEventType) type, trans);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
progressHandle.finish();
|
||||
progressHandle = ProgressHandleFactory.createHandle(Bundle.progressWindow_msg_commitingDb());
|
||||
progressHandle.start();
|
||||
updateProgress(-0.5, 1);
|
||||
updateTitle(Bundle.progressWindow_msg_commitingDb());
|
||||
@NbBundle.Messages("progressWindow.msg.populateMacEventsFiles=Populating MAC time events for files")
|
||||
private void insertMACTimeEvents(final int numFiles, List<Long> fileIDs, EventDB.EventTransaction trans) {
|
||||
restartProgressHandle(Bundle.progressWindow_msg_populateMacEventsFiles(), "", 0D, numFiles, true);
|
||||
for (int i = 0; i < numFiles; i++) {
|
||||
if (isCancelRequested()) {
|
||||
break;
|
||||
}
|
||||
long fID = fileIDs.get(i);
|
||||
try {
|
||||
AbstractFile f = skCase.getAbstractFileById(fID);
|
||||
|
||||
if (isCancelled()) {
|
||||
eventDB.rollBackTransaction(trans);
|
||||
} else {
|
||||
eventDB.commitTransaction(trans);
|
||||
if (isNull(f)) {
|
||||
LOGGER.log(Level.WARNING, "Failed to get data for file : {0}", fID); // NON-NLS
|
||||
} else {
|
||||
insertEventsForFile(f, trans);
|
||||
updateProgress(i, numFiles);
|
||||
updateMessage(f.getName());
|
||||
}
|
||||
} catch (TskCoreException tskCoreException) {
|
||||
LOGGER.log(Level.SEVERE, "Failed to insert MAC time events for file : " + fID, tskCoreException); // NON-NLS
|
||||
}
|
||||
}
|
||||
eventDB.analyze();
|
||||
populateFilterData(skCase);
|
||||
invalidateCaches();
|
||||
|
||||
recordDBPopulationState(lastObjId, lastArtfID, injestRunning);
|
||||
progressHandle.finish();
|
||||
return null;
|
||||
}
|
||||
|
||||
private void insertEventsForFile(AbstractFile f, EventDB.EventTransaction trans) throws TskCoreException {
|
||||
@ -671,11 +611,8 @@ public class EventsRepository {
|
||||
List<ContentTag> tags = tagsManager.getContentTagsByContent(f);
|
||||
|
||||
for (Map.Entry<FileSystemTypes, Long> timeEntry : timeMap.entrySet()) {
|
||||
/*
|
||||
* if the time is legitimate ( greater than zero ) insert it
|
||||
* into the db
|
||||
*/
|
||||
if (timeEntry.getValue() > 0) {
|
||||
// if the time is legitimate ( greater than zero ) insert it
|
||||
eventDB.insertEvent(timeEntry.getValue(), timeEntry.getKey(),
|
||||
datasourceID, f.getId(), null, uniquePath, medDesc,
|
||||
shortDesc, known, hashSets, tags, trans);
|
||||
@ -713,10 +650,7 @@ public class EventsRepository {
|
||||
//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();
|
||||
progressHandle.finish();
|
||||
progressHandle = ProgressHandleFactory.createHandle(Bundle.progressWindow_populatingXevents(type.getDisplayName()), () -> cancel(true));
|
||||
progressHandle.start(numArtifacts);
|
||||
updateTitle(Bundle.progressWindow_populatingXevents(type.getDisplayName()));
|
||||
restartProgressHandle(Bundle.progressWindow_populatingXevents(type.getDisplayName()), "", 0D, numArtifacts, true);
|
||||
for (int i = 0; i < numArtifacts; i++) {
|
||||
try {
|
||||
//for each artifact, extract the relevant information for the descriptions
|
||||
@ -733,10 +667,8 @@ public class EventsRepository {
|
||||
|
||||
private void insertEventForArtifact(final ArtifactEventType type, BlackboardArtifact bbart, EventDB.EventTransaction trans) throws TskCoreException {
|
||||
ArtifactEventType.AttributeEventDescription eventDescription = ArtifactEventType.buildEventDescription(type, bbart);
|
||||
/*
|
||||
* if the time is legitimate ( greater than zero ) insert it into
|
||||
* the db
|
||||
*/
|
||||
|
||||
// if the time is legitimate ( greater than zero ) insert it into the db
|
||||
if (eventDescription != null && eventDescription.getTime() > 0) {
|
||||
long objectID = bbart.getObjectID();
|
||||
AbstractFile f = skCase.getAbstractFileById(objectID);
|
||||
|
@ -76,9 +76,9 @@ public class StatusBar extends ToolBar {
|
||||
|
||||
refreshLabel.visibleProperty().bind(this.controller.getNewEventsFlag());
|
||||
refreshLabel.managedProperty().bind(this.controller.getNewEventsFlag());
|
||||
taskLabel.textProperty().bind(this.controller.getTaskTitle());
|
||||
messageLabel.textProperty().bind(this.controller.getMessage());
|
||||
progressBar.progressProperty().bind(this.controller.getProgress());
|
||||
taskLabel.textProperty().bind(this.controller.taskTitleProperty());
|
||||
messageLabel.textProperty().bind(this.controller.taskMessageProperty());
|
||||
progressBar.progressProperty().bind(this.controller.taskProgressProperty());
|
||||
taskLabel.visibleProperty().bind(this.controller.getTasks().emptyProperty().not());
|
||||
|
||||
statusLabel.textProperty().bind(this.controller.getStatusProperty());
|
||||
|
Loading…
x
Reference in New Issue
Block a user