From ca5474ed025b9391cbea8772ac7aa328b71c1ceb Mon Sep 17 00:00:00 2001 From: millmanorama Date: Wed, 4 Nov 2015 00:45:00 -0500 Subject: [PATCH 1/3] make ProgressWindow non-modal, and add netbean ProgressHandle. WIP --- .../autopsy/timeline/ProgressWindow.java | 26 ++++++------- .../autopsy/timeline/db/EventsRepository.java | 37 +++++++++++++++---- 2 files changed, 43 insertions(+), 20 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ProgressWindow.java b/Core/src/org/sleuthkit/autopsy/timeline/ProgressWindow.java index 8f784263be..20c5a1fa51 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ProgressWindow.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ProgressWindow.java @@ -39,7 +39,7 @@ import org.sleuthkit.autopsy.coreutils.ThreadConfined; * Dialog with progress bar that pops up when timeline is being generated */ public class ProgressWindow extends JFrame { - + private final SwingWorker worker; /** @@ -50,25 +50,25 @@ public class ProgressWindow extends JFrame { 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()); }); - + setName(Bundle.Timeline_progressWindow_name()); setTitle(Bundle.Timeline_progressWindow_title()); // Close the dialog when Esc is pressed String cancelName = "cancel"; // NON-NLS InputMap inputMap = getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); - + inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), cancelName); ActionMap actionMap = getRootPane().getActionMap(); - + actionMap.put(cancelName, new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { @@ -149,7 +149,7 @@ public class ProgressWindow extends JFrame { } }); } - + public void close() { worker.cancel(false); setVisible(false); @@ -181,28 +181,28 @@ public class ProgressWindow extends JFrame { */ @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; diff --git a/Core/src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java b/Core/src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java index 3196f0baad..5607748d76 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java @@ -40,6 +40,8 @@ import javax.swing.JOptionPane; import javax.swing.SwingWorker; import org.apache.commons.lang3.StringUtils; import org.joda.time.Interval; +import org.netbeans.api.progress.ProgressHandle; +import org.netbeans.api.progress.ProgressHandleFactory; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.services.TagsManager; @@ -345,9 +347,11 @@ public class EventsRepository { private final Runnable postPopulationOperation; private final SleuthkitCase skCase; private final TagsManager tagsManager; + private final ProgressHandle progressHandle; public RebuildTagsWorker(Runnable postPopulationOperation) { - progressDialog = new ProgressWindow(null, true, this); + progressDialog = new ProgressWindow(null, false, this); + progressHandle = ProgressHandleFactory.createHandle("refreshing tags", () -> cancel(true)); progressDialog.setVisible(true); skCase = autoCase.getSleuthkitCase(); @@ -358,7 +362,7 @@ public class EventsRepository { @Override protected Void doInBackground() throws Exception { - + progressHandle.start(); EventDB.EventTransaction trans = eventDB.beginTransaction(); LOGGER.log(Level.INFO, "dropping old tags"); // NON-NLS eventDB.reInitializeTags(); @@ -411,6 +415,11 @@ public class EventsRepository { super.process(chunks); ProgressWindow.ProgressUpdate chunk = chunks.get(chunks.size() - 1); progressDialog.update(chunk); + + progressHandle.switchToDeterminate(chunk.getTotal()); + progressHandle.setDisplayName(chunk.getHeaderMessage()); + progressHandle.progress(chunk.getDetailMessage()); + progressHandle.progress(chunk.getProgress()); } @Override @@ -418,8 +427,10 @@ public class EventsRepository { + " Some events may have inacurate tags. See the log for details.") protected void done() { super.done(); + + progressHandle.finish(); + progressDialog.close(); try { - progressDialog.close(); get(); } catch (CancellationException ex) { LOGGER.log(Level.WARNING, "Database population was cancelled by the user. Not all events may be present or accurate. See the log for details.", ex); // NON-NLS @@ -443,11 +454,12 @@ public class EventsRepository { private final Runnable postPopulationOperation; private final SleuthkitCase skCase; private final TagsManager tagsManager; + private final ProgressHandle progressHandle; public DBPopulationWorker(Runnable postPopulationOperation) { - progressDialog = new ProgressWindow(null, true, this); + progressDialog = new ProgressWindow(null, false, this); progressDialog.setVisible(true); - + progressHandle = ProgressHandleFactory.createHandle("(re)initializing events database", () -> this.cancel(true)); skCase = autoCase.getSleuthkitCase(); tagsManager = autoCase.getServices().getTagsManager(); @@ -459,6 +471,7 @@ public class EventsRepository { "progressWindow.msg.reinit_db=(re)initializing events database", "progressWindow.msg.commitingDb=committing events db"}) protected Void doInBackground() throws Exception { + progressHandle.start(); publish(new ProgressWindow.ProgressUpdate(0, -1, Bundle.progressWindow_msg_reinit_db(), "")); //reset database //TODO: can we do more incremental updates? -jm @@ -561,6 +574,15 @@ public class EventsRepository { super.process(chunks); ProgressWindow.ProgressUpdate chunk = chunks.get(chunks.size() - 1); progressDialog.update(chunk); + + if (chunk.getTotal() >= 0) { + progressHandle.switchToDeterminate(chunk.getTotal()); + } else { + progressHandle.switchToIndeterminate(); + } + progressHandle.setDisplayName(chunk.getHeaderMessage()); + progressHandle.progress(chunk.getDetailMessage()); + progressHandle.progress(chunk.getProgress()); } @Override @@ -568,11 +590,12 @@ public class EventsRepository { + " Not all events may be present or accurate. See the log for details.") protected void done() { super.done(); + progressHandle.finish(); + progressDialog.close(); try { - progressDialog.close(); get(); } catch (CancellationException ex) { - LOGGER.log(Level.WARNING, "Database population was cancelled by the user. Not all events may be present or accurate. See the log for details.", ex); // NON-NLS + LOGGER.log(Level.WARNING, "Database population was cancelled by the user. Not all events may be present or accurate. See the log for details."); // NON-NLS } catch (InterruptedException | ExecutionException ex) { LOGGER.log(Level.WARNING, "Exception while populating database.", ex); // NON-NLS JOptionPane.showMessageDialog(null, Bundle.msgdlg_problem_text()); From 498322307a555ab71a42683f813a2afe8ef176f8 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Thu, 5 Nov 2015 15:28:00 -0500 Subject: [PATCH 2/3] refactor timeline database population code slightly and include more informative ProgressHandle updates. Cancellation works properly. --- .../autopsy/timeline/ProgressWindow.java | 31 +- .../eventtype/ArtifactEventType.java | 14 +- .../autopsy/timeline/db/EventsRepository.java | 338 +++++++++--------- 3 files changed, 199 insertions(+), 184 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ProgressWindow.java b/Core/src/org/sleuthkit/autopsy/timeline/ProgressWindow.java index 20c5a1fa51..9afa6f7c89 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ProgressWindow.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ProgressWindow.java @@ -39,7 +39,7 @@ import org.sleuthkit.autopsy.coreutils.ThreadConfined; * Dialog with progress bar that pops up when timeline is being generated */ public class ProgressWindow extends JFrame { - + private final SwingWorker worker; /** @@ -50,25 +50,25 @@ public class ProgressWindow extends JFrame { 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()); }); - + setName(Bundle.Timeline_progressWindow_name()); setTitle(Bundle.Timeline_progressWindow_title()); // Close the dialog when Esc is pressed String cancelName = "cancel"; // NON-NLS InputMap inputMap = getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); - + inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), cancelName); ActionMap actionMap = getRootPane().getActionMap(); - + actionMap.put(cancelName, new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { @@ -149,7 +149,7 @@ public class ProgressWindow extends JFrame { } }); } - + public void close() { worker.cancel(false); setVisible(false); @@ -181,34 +181,37 @@ public class ProgressWindow extends JFrame { */ @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; } + + public ProgressUpdate(int progress, int total, String headerMessage) { + this(progress, total, headerMessage, ""); + } } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/datamodel/eventtype/ArtifactEventType.java b/Core/src/org/sleuthkit/autopsy/timeline/datamodel/eventtype/ArtifactEventType.java index 4fc354fc54..d0029d305e 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/datamodel/eventtype/ArtifactEventType.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/datamodel/eventtype/ArtifactEventType.java @@ -131,21 +131,19 @@ public interface ArtifactEventType extends EventType { * Build a {@link AttributeEventDescription} derived from a * {@link BlackboardArtifact}. This is a template method that relies on each * {@link SubType}'s implementation of - * {@link SubType#parseAttributesHelper(org.sleuthkit.datamodel.BlackboardArtifact, java.util.Map)} - * know how to go from {@link BlackboardAttribute}s to the event - * description. + * {@link SubType#parseAttributesHelper()} to know how to go from + * {@link BlackboardAttribute}s to the event description. * * @param artf the {@link BlackboardArtifact} to derive the event * description from * * @return an {@link AttributeEventDescription} derived from the given - * artifact + * artifact, if the given artifact has no timestamp * * @throws TskCoreException is there is a problem accessing the blackboard * data */ - static public AttributeEventDescription buildEventDescription( - ArtifactEventType type, BlackboardArtifact artf) throws TskCoreException { + static public AttributeEventDescription buildEventDescription(ArtifactEventType type, BlackboardArtifact artf) throws TskCoreException { //if we got passed an artifact that doesn't correspond to the type of the event, //something went very wrong. throw an exception. if (type.getArtifactType().getTypeID() != artf.getArtifactTypeID()) { @@ -166,7 +164,7 @@ public interface ArtifactEventType extends EventType { } if (attrMap.get(type.getDateTimeAttrubuteType()) == null) { - Logger.getLogger(AttributeEventDescription.class.getName()).log(Level.WARNING, "Artifact {0} has no date/time attribute, skipping it.", artf.getArtifactID()); // NON-NLS + Logger.getLogger(AttributeEventDescription.class.getName()).log(Level.WARNING, "Artifact {0} has no date/time attribute, skipping it.", artf.getArtifactID()); // NON-NLS return null; } //use the hook provided by this subtype implementation @@ -188,8 +186,6 @@ public interface ArtifactEventType extends EventType { } } - - public static class EmptyExtractor implements BiFunction, String> { @Override diff --git a/Core/src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java b/Core/src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java index 5607748d76..b36c4a56c6 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java @@ -23,11 +23,13 @@ import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; +import java.util.EnumMap; import java.util.List; 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.TimeUnit; import java.util.logging.Level; import java.util.stream.Collectors; @@ -37,8 +39,10 @@ import javafx.collections.ObservableList; import javafx.collections.ObservableMap; import javax.annotation.concurrent.GuardedBy; import javax.swing.JOptionPane; +import javax.swing.SwingUtilities; import javax.swing.SwingWorker; import org.apache.commons.lang3.StringUtils; +import org.controlsfx.dialog.ProgressDialog; import org.joda.time.Interval; import org.netbeans.api.progress.ProgressHandle; import org.netbeans.api.progress.ProgressHandleFactory; @@ -338,30 +342,80 @@ public class EventsRepository { dbPopulationWorker.execute(); } - private class RebuildTagsWorker extends SwingWorker { - - private final ProgressWindow progressDialog; + /** + * A base class for swing workers that shows a {@link ProgressWorker} and + * updates a {@link ProgressHandle} as it performs its background work, and + * calls a call-back when finished. + * + * //TODO: I prefer the JavaFX task API as it has built in progress + * properties that can be bound to a javafx progress indicator. Convert + * these to a JavaFX implementation,and replace {@link ProgressWindow} with + * {@link ProgressDialog} + */ + private abstract class DBProgressWorker extends SwingWorker { //TODO: can we avoid this with a state listener? does it amount to the same thing? //post population operation to execute - private final Runnable postPopulationOperation; - private final SleuthkitCase skCase; - private final TagsManager tagsManager; - private final ProgressHandle progressHandle; + final Runnable postPopulationOperation; - public RebuildTagsWorker(Runnable postPopulationOperation) { + final SleuthkitCase skCase; + final TagsManager tagsManager; + + final ProgressWindow progressDialog; + volatile ProgressHandle progressHandle; + + DBProgressWorker(Runnable postPopulationOperation, String initialProgressDisplayName) { progressDialog = new ProgressWindow(null, false, this); - progressHandle = ProgressHandleFactory.createHandle("refreshing tags", () -> cancel(true)); progressDialog.setVisible(true); + progressHandle = ProgressHandleFactory.createHandle(initialProgressDisplayName, () -> cancel(true)); skCase = autoCase.getSleuthkitCase(); tagsManager = autoCase.getServices().getTagsManager(); - this.postPopulationOperation = postPopulationOperation; } + /** + * update progress UIs + * + * @param chunk + */ + final protected void update(ProgressWindow.ProgressUpdate chunk) { + SwingUtilities.invokeLater(() -> { + progressDialog.update(chunk); + }); + if (chunk.getTotal() >= 0) { + progressHandle.progress(chunk.getProgress()); + } + progressHandle.setDisplayName(chunk.getHeaderMessage()); + progressHandle.progress(chunk.getDetailMessage()); + } + @Override + protected void done() { + super.done(); + progressDialog.close(); + postPopulationOperation.run(); //execute post db population operation + } + } + + public boolean areFiltersEquivalent(RootFilter f1, RootFilter f2) { + return SQLHelper.getSQLWhere(f1).equals(SQLHelper.getSQLWhere(f2)); + } + + private class RebuildTagsWorker extends DBProgressWorker { + + @NbBundle.Messages("RebuildTagsWorker.task.displayName=refreshing tags") + RebuildTagsWorker(Runnable postPopulationOperation) { + super(postPopulationOperation, Bundle.RebuildTagsWorker_task_displayName()); + } + + @Override + @NbBundle.Messages({"progressWindow.msg.refreshingFileTags=refreshing file tags", + "progressWindow.msg.refreshingResultTags=refreshing result tags", + "progressWindow.msg.commitingTags=committing tag changes"}) protected Void doInBackground() throws Exception { + int currentWorkTotal; + progressHandle.start(); EventDB.EventTransaction trans = eventDB.beginTransaction(); LOGGER.log(Level.INFO, "dropping old tags"); // NON-NLS @@ -369,101 +423,77 @@ public class EventsRepository { LOGGER.log(Level.INFO, "updating content tags"); // NON-NLS List contentTags = tagsManager.getAllContentTags(); - int size = contentTags.size(); - for (int i = 0; i < size; i++) { + progressHandle.finish(); + progressHandle = ProgressHandleFactory.createHandle(Bundle.progressWindow_msg_refreshingFileTags(), + () -> cancel(true)); + progressHandle.start(currentWorkTotal = contentTags.size()); + + for (int i = 0; i < currentWorkTotal; i++) { if (isCancelled()) { break; } - publish(new ProgressWindow.ProgressUpdate(i, size, "refreshing file tags", "")); + update(new ProgressWindow.ProgressUpdate(i, currentWorkTotal, Bundle.progressWindow_msg_refreshingFileTags())); ContentTag contentTag = contentTags.get(i); eventDB.addTag(contentTag.getContent().getId(), null, contentTag); } + LOGGER.log(Level.INFO, "updating artifact tags"); // NON-NLS List artifactTags = tagsManager.getAllBlackboardArtifactTags(); - size = artifactTags.size(); - for (int i = 0; i < size; i++) { + progressHandle.finish(); + progressHandle = ProgressHandleFactory.createHandle(Bundle.progressWindow_msg_refreshingResultTags(), + () -> cancel(true)); + progressHandle.start(currentWorkTotal = artifactTags.size()); + + for (int i = 0; i < currentWorkTotal; i++) { if (isCancelled()) { break; } - publish(new ProgressWindow.ProgressUpdate(i, size, "refreshing result tags", "")); + update(new ProgressWindow.ProgressUpdate(i, currentWorkTotal, Bundle.progressWindow_msg_refreshingResultTags())); BlackboardArtifactTag artifactTag = artifactTags.get(i); eventDB.addTag(artifactTag.getContent().getId(), artifactTag.getArtifact().getArtifactID(), artifactTag); } LOGGER.log(Level.INFO, "committing tags"); // NON-NLS - publish(new ProgressWindow.ProgressUpdate(0, -1, "committing tag changes", "")); - eventDB.analyze(); + progressHandle.finish(); + progressHandle = ProgressHandleFactory.createHandle(Bundle.progressWindow_msg_commitingTags()); + progressHandle.start(); + update(new ProgressWindow.ProgressUpdate(0, -1, Bundle.progressWindow_msg_commitingTags())); + if (isCancelled()) { eventDB.rollBackTransaction(trans); } else { eventDB.commitTransaction(trans); } - + eventDB.analyze(); populateFilterData(skCase); invalidateCaches(); + progressHandle.finish(); return null; } - /** - * handle intermediate 'results': just update progress dialog - * - * @param chunks - */ - @Override - protected void process(List chunks) { - super.process(chunks); - ProgressWindow.ProgressUpdate chunk = chunks.get(chunks.size() - 1); - progressDialog.update(chunk); - - progressHandle.switchToDeterminate(chunk.getTotal()); - progressHandle.setDisplayName(chunk.getHeaderMessage()); - progressHandle.progress(chunk.getDetailMessage()); - progressHandle.progress(chunk.getProgress()); - } - @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(); - - progressHandle.finish(); - progressDialog.close(); try { get(); } catch (CancellationException ex) { - LOGGER.log(Level.WARNING, "Database population was cancelled by the user. Not all events may be present or accurate. See the log for details.", ex); // NON-NLS - } catch (InterruptedException | ExecutionException ex) { - LOGGER.log(Level.WARNING, "Exception while populating database.", ex); // NON-NLS - JOptionPane.showMessageDialog(null, Bundle.msgdlg_tagsproblem_text()); + 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()); } - postPopulationOperation.run(); //execute post db population operation } } - private class DBPopulationWorker extends SwingWorker { - - private final ProgressWindow progressDialog; - - //TODO: can we avoid this with a state listener? does it amount to the same thing? - //post population operation to execute - private final Runnable postPopulationOperation; - private final SleuthkitCase skCase; - private final TagsManager tagsManager; - private final ProgressHandle progressHandle; + private class DBPopulationWorker extends DBProgressWorker { + @NbBundle.Messages("DBPopulationWorker.task.displayName=(re)initializing events database") public DBPopulationWorker(Runnable postPopulationOperation) { - progressDialog = new ProgressWindow(null, false, this); - progressDialog.setVisible(true); - progressHandle = ProgressHandleFactory.createHandle("(re)initializing events database", () -> this.cancel(true)); - skCase = autoCase.getSleuthkitCase(); - tagsManager = autoCase.getServices().getTagsManager(); - - this.postPopulationOperation = postPopulationOperation; + super(postPopulationOperation, Bundle.DBPopulationWorker_task_displayName()); } @Override @@ -472,68 +502,37 @@ public class EventsRepository { "progressWindow.msg.commitingDb=committing events db"}) protected Void doInBackground() throws Exception { progressHandle.start(); - publish(new ProgressWindow.ProgressUpdate(0, -1, Bundle.progressWindow_msg_reinit_db(), "")); - //reset database - //TODO: can we do more incremental updates? -jm + update(new ProgressWindow.ProgressUpdate(0, -1, Bundle.progressWindow_msg_reinit_db())); + //reset database //TODO: can we do more incremental updates? -jm eventDB.reInitializeDB(); //grab ids of all files - List files = skCase.findAllFileIdsWhere("name != '.' AND name != '..'"); - - final int numFiles = files.size(); - publish(new ProgressWindow.ProgressUpdate(0, numFiles, Bundle.progressWindow_msg_populateMacEventsFiles(), "")); + List fileIDs = skCase.findAllFileIdsWhere("name != '.' AND name != '..'"); + final int numFiles = fileIDs.size(); + progressHandle.switchToDeterminate(numFiles); + update(new ProgressWindow.ProgressUpdate(0, numFiles, Bundle.progressWindow_msg_populateMacEventsFiles())); //insert file events into db - int i = 1; EventDB.EventTransaction trans = eventDB.beginTransaction(); - for (final Long fID : files) { + for (int i = 0; i < numFiles; i++) { if (isCancelled()) { break; } else { + long fID = fileIDs.get(i); try { AbstractFile f = skCase.getAbstractFileById(fID); - if (f == null) { + if (isNull(f)) { LOGGER.log(Level.WARNING, "Failed to get data for file : {0}", fID); // NON-NLS } else { - //TODO: This is broken for logical files? fix -jm - //TODO: logical files don't necessarily have valid timestamps, so ... -jm - final String uniquePath = f.getUniquePath(); - final String parentPath = f.getParentPath(); - long datasourceID = f.getDataSource().getId(); - String datasourceName = StringUtils.substringBeforeLast(uniquePath, parentPath); - - String rootFolder = StringUtils.substringBefore(StringUtils.substringAfter(parentPath, "/"), "/"); - String shortDesc = datasourceName + "/" + StringUtils.defaultString(rootFolder); - shortDesc = shortDesc.endsWith("/") ? shortDesc : shortDesc + "/"; - String medDesc = datasourceName + parentPath; - - final TskData.FileKnown known = f.getKnown(); - Set hashSets = f.getHashSetNames(); - List tags = tagsManager.getContentTagsByContent(f); - - //insert it into the db if time is > 0 => time is legitimate (drops logical files) - if (f.getAtime() > 0) { - eventDB.insertEvent(f.getAtime(), FileSystemTypes.FILE_ACCESSED, datasourceID, fID, null, uniquePath, medDesc, shortDesc, known, hashSets, tags, trans); - } - if (f.getMtime() > 0) { - eventDB.insertEvent(f.getMtime(), FileSystemTypes.FILE_MODIFIED, datasourceID, fID, null, uniquePath, medDesc, shortDesc, known, hashSets, tags, trans); - } - if (f.getCtime() > 0) { - eventDB.insertEvent(f.getCtime(), FileSystemTypes.FILE_CHANGED, datasourceID, fID, null, uniquePath, medDesc, shortDesc, known, hashSets, tags, trans); - } - if (f.getCrtime() > 0) { - eventDB.insertEvent(f.getCrtime(), FileSystemTypes.FILE_CREATED, datasourceID, fID, null, uniquePath, medDesc, shortDesc, known, hashSets, tags, trans); - } - - publish(new ProgressWindow.ProgressUpdate(i, numFiles, + insertEventsForFile(f, trans); + update(new ProgressWindow.ProgressUpdate(i, numFiles, Bundle.progressWindow_msg_populateMacEventsFiles(), f.getName())); } } catch (TskCoreException tskCoreException) { - LOGGER.log(Level.WARNING, "failed to insert mac event for file : " + fID, tskCoreException); // NON-NLS + LOGGER.log(Level.SEVERE, "Failed to insert MAC time events for file : " + fID, tskCoreException); // NON-NLS } } - i++; } //insert artifact based events @@ -548,62 +547,77 @@ public class EventsRepository { } } - publish(new ProgressWindow.ProgressUpdate(0, -1, Bundle.progressWindow_msg_commitingDb(), "")); - - eventDB.analyze(); + progressHandle.finish(); + progressHandle = ProgressHandleFactory.createHandle(Bundle.progressWindow_msg_commitingDb()); + progressHandle.start(); + update(new ProgressWindow.ProgressUpdate(0, -1, Bundle.progressWindow_msg_commitingDb())); if (isCancelled()) { eventDB.rollBackTransaction(trans); } else { eventDB.commitTransaction(trans); } - + eventDB.analyze(); populateFilterData(skCase); invalidateCaches(); + progressHandle.finish(); return null; } - /** - * handle intermediate 'results': just update progress dialog - * - * @param chunks - */ - @Override - protected void process(List chunks) { - super.process(chunks); - ProgressWindow.ProgressUpdate chunk = chunks.get(chunks.size() - 1); - progressDialog.update(chunk); + private void insertEventsForFile(AbstractFile f, EventDB.EventTransaction trans) throws TskCoreException { + //gather time stamps into map + EnumMap timeMap = new EnumMap<>(FileSystemTypes.class); + timeMap.put(FileSystemTypes.FILE_CREATED, f.getCrtime()); + timeMap.put(FileSystemTypes.FILE_ACCESSED, f.getAtime()); + timeMap.put(FileSystemTypes.FILE_CHANGED, f.getCtime()); + timeMap.put(FileSystemTypes.FILE_MODIFIED, f.getMtime()); - if (chunk.getTotal() >= 0) { - progressHandle.switchToDeterminate(chunk.getTotal()); - } else { - progressHandle.switchToIndeterminate(); + /* if there are no legitimate ( greater tan zero ) time stamps ( eg, + * logical/local files) skip the rest of the event generation: this + * should result in droping logical files, since they do not have + * legitimate time stamps. */ + if (Collections.max(timeMap.values()) > 0) { + final String uniquePath = f.getUniquePath(); + final String parentPath = f.getParentPath(); + long datasourceID = f.getDataSource().getId(); + String datasourceName = StringUtils.substringBeforeLast(uniquePath, parentPath); + + String rootFolder = StringUtils.substringBefore(StringUtils.substringAfter(parentPath, "/"), "/"); + String shortDesc = datasourceName + "/" + StringUtils.defaultString(rootFolder); + shortDesc = shortDesc.endsWith("/") ? shortDesc : shortDesc + "/"; + String medDesc = datasourceName + parentPath; + + final TskData.FileKnown known = f.getKnown(); + Set hashSets = f.getHashSetNames(); + List tags = tagsManager.getContentTagsByContent(f); + + for (Map.Entry timeEntry : timeMap.entrySet()) { + /* if the time is legitimate ( greater than zero ) insert it + * into the db */ + if (timeEntry.getValue() > 0) { + eventDB.insertEvent(timeEntry.getValue(), timeEntry.getKey(), + datasourceID, f.getId(), null, uniquePath, medDesc, + shortDesc, known, hashSets, tags, trans); + } + } } - progressHandle.setDisplayName(chunk.getHeaderMessage()); - progressHandle.progress(chunk.getDetailMessage()); - progressHandle.progress(chunk.getProgress()); } @Override @NbBundle.Messages("msgdlg.problem.text=There was a problem populating the timeline." - + " Not all events may be present or accurate. See the log for details.") + + " Not all events may be present or accurate.") protected void done() { super.done(); - progressHandle.finish(); - progressDialog.close(); try { get(); } catch (CancellationException ex) { - LOGGER.log(Level.WARNING, "Database population was cancelled by the user. Not all events may be present or accurate. See the log for details."); // NON-NLS - } catch (InterruptedException | ExecutionException ex) { - LOGGER.log(Level.WARNING, "Exception while populating database.", ex); // NON-NLS - JOptionPane.showMessageDialog(null, Bundle.msgdlg_problem_text()); + 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_problem_text()); } - postPopulationOperation.run(); //execute post db population operation } /** @@ -619,38 +633,40 @@ public class EventsRepository { //get all the blackboard artifacts corresponding to the given event sub_type final ArrayList 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); for (int i = 0; i < numArtifacts; i++) { - publish(new ProgressWindow.ProgressUpdate(i, numArtifacts, - Bundle.progressWindow_populatingXevents(type.getDisplayName()), "")); - - //for each artifact, extract the relevant information for the descriptions - BlackboardArtifact bbart = blackboardArtifacts.get(i); - ArtifactEventType.AttributeEventDescription eventDescription = ArtifactEventType.buildEventDescription(type, bbart); - - //insert it into the db if time is > 0 => time is legitimate - if (eventDescription != null && eventDescription.getTime() > 0L) { - long objectID = bbart.getObjectID(); - AbstractFile f = skCase.getAbstractFileById(objectID); - long datasourceID = f.getDataSource().getId(); - long artifactID = bbart.getArtifactID(); - Set hashSets = f.getHashSetNames(); - List tags = tagsManager.getBlackboardArtifactTagsByArtifact(bbart); - String fullDescription = eventDescription.getFullDescription(); - String medDescription = eventDescription.getMedDescription(); - String shortDescription = eventDescription.getShortDescription(); - - eventDB.insertEvent(eventDescription.getTime(), type, datasourceID, objectID, artifactID, fullDescription, medDescription, shortDescription, null, hashSets, tags, trans); + try { + //for each artifact, extract the relevant information for the descriptions + insertEventForArtifact(type, blackboardArtifacts.get(i), trans); + update(new ProgressWindow.ProgressUpdate(i, numArtifacts, + Bundle.progressWindow_populatingXevents(type.getDisplayName()))); + } catch (TskCoreException ex) { + LOGGER.log(Level.SEVERE, "There was a problem inserting event for artifact: " + blackboardArtifacts.get(i).getArtifactID(), ex); // NON-NLS } } } catch (TskCoreException ex) { - LOGGER.log(Level.SEVERE, "There was a problem getting events with sub type = " + type.toString() + ".", ex); // NON-NLS + LOGGER.log(Level.SEVERE, "There was a problem getting events with sub type " + type.toString() + ".", ex); // NON-NLS + } + } + + 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 (eventDescription != null && eventDescription.getTime() > 0) { + long objectID = bbart.getObjectID(); + AbstractFile f = skCase.getAbstractFileById(objectID); + long datasourceID = f.getDataSource().getId(); + long artifactID = bbart.getArtifactID(); + Set hashSets = f.getHashSetNames(); + List tags = tagsManager.getBlackboardArtifactTagsByArtifact(bbart); + String fullDescription = eventDescription.getFullDescription(); + String medDescription = eventDescription.getMedDescription(); + String shortDescription = eventDescription.getShortDescription(); + eventDB.insertEvent(eventDescription.getTime(), type, datasourceID, objectID, artifactID, fullDescription, medDescription, shortDescription, null, hashSets, tags, trans); } } } - - public boolean areFiltersEquivalent(RootFilter f1, RootFilter f2) { - return SQLHelper.getSQLWhere(f1).equals(SQLHelper.getSQLWhere(f2)); - - } } From b0138939258795e587e4b94d4a03ff6b9b44b9ef Mon Sep 17 00:00:00 2001 From: Karl Mortensen Date: Thu, 5 Nov 2015 15:58:57 -0500 Subject: [PATCH 3/3] Add index name to CaseMetadata, do not export Commons-logging from KWS --- .../sleuthkit/autopsy/casemodule/CaseMetadata.java | 11 +++++++++++ KeywordSearch/nbproject/project.xml | 1 - 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/CaseMetadata.java b/Core/src/org/sleuthkit/autopsy/casemodule/CaseMetadata.java index b2bbb54397..632939fe7c 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/CaseMetadata.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/CaseMetadata.java @@ -46,6 +46,7 @@ public final class CaseMetadata { private final String examiner; private final String caseDirectory; private final String caseDatabaseName; + private final String caseTextIndexName; /** * Constructs an object that provides access to case metadata. @@ -71,6 +72,7 @@ public final class CaseMetadata { throw new CaseMetadataException("Case directory missing"); } caseDatabaseName = metadata.getDatabaseName(); + caseTextIndexName = metadata.getTextIndexName(); if (Case.CaseType.MULTI_USER_CASE == caseType && caseDatabaseName.isEmpty()) { throw new CaseMetadataException("Case database name missing"); } @@ -132,5 +134,14 @@ public final class CaseMetadata { public String getCaseDatabaseName() { return caseDatabaseName; } + + /** + * Gets the text index name. + * + * @return The case text index name, will be empty for a single-user case. + */ + public String getTextIndexName() { + return caseTextIndexName; + } } diff --git a/KeywordSearch/nbproject/project.xml b/KeywordSearch/nbproject/project.xml index 0f823bd9be..43b6b9f390 100644 --- a/KeywordSearch/nbproject/project.xml +++ b/KeywordSearch/nbproject/project.xml @@ -108,7 +108,6 @@ org.apache.commons.lang.mutable org.apache.commons.lang.text org.apache.commons.lang.time - org.apache.commons.logging org.apache.commons.logging.impl org.apache.tika org.apache.tika.config