From 22bb45f80f281673084d1954063f6bee6ee083bf Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Mon, 13 Apr 2020 11:56:40 -0400 Subject: [PATCH 01/20] added open file systems method to case open method --- .../sleuthkit/autopsy/casemodule/Case.java | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index b70845e3df..4ca47bd658 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -60,6 +60,7 @@ import javax.annotation.concurrent.GuardedBy; import javax.annotation.concurrent.ThreadSafe; import javax.swing.JOptionPane; import javax.swing.SwingUtilities; +import org.openide.util.Exceptions; import org.openide.util.Lookup; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; @@ -125,6 +126,7 @@ import org.sleuthkit.datamodel.CaseDbConnectionInfo; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.ContentTag; import org.sleuthkit.datamodel.DataSource; +import org.sleuthkit.datamodel.FileSystem; import org.sleuthkit.datamodel.Image; import org.sleuthkit.datamodel.Report; import org.sleuthkit.datamodel.SleuthkitCase; @@ -1979,6 +1981,8 @@ public class Case { openAppServiceCaseResources(progressIndicator); checkForCancellation(); openCommunicationChannels(progressIndicator); + checkForCancellation(); + openFileSystems(); return null; } catch (CaseActionException ex) { @@ -1996,6 +2000,51 @@ public class Case { throw ex; } } + + /** + * Reads a sector from each file system of each image of a case to do an eager open of the filesystems in case. + * @throws CaseActionCancelledException Exception thrown if task is cancelled. + */ + private void openFileSystems() throws CaseActionCancelledException { + String caseName = (this.caseDb != null) ? this.caseDb.getDatabaseName() : "null"; + + List images = null; + try { + images = this.caseDb.getImages(); + } catch (TskCoreException ex) { + logger.log( + Level.SEVERE, + String.format("Could not obtain images while opening case: %s.", caseName), + ex); + + return; + } + + checkForCancellation(); + byte[] tempBuff = new byte[512]; + + for (Image image : images) { + Collection fileSystems = this.caseDb.getFileSystems(image); + checkForCancellation(); + for (FileSystem fileSystem : fileSystems) { + try { + fileSystem.read(tempBuff, 0, 512); + } + catch (TskCoreException ex) { + String imageStr = image.getName(); + String fileSysStr = fileSystem.getName(); + + logger.log( + Level.WARNING, + String.format("Could not open filesystem: %s in image: %s for case: %s.", fileSysStr, imageStr, caseName), + ex); + } + + checkForCancellation(); + } + + } + } /** * A case action (interface CaseAction) that opens a case, deletes From 3be19e717e4c26d34de75ba7c1750549dac09dce Mon Sep 17 00:00:00 2001 From: Ethan Roseman Date: Mon, 13 Apr 2020 15:37:19 -0400 Subject: [PATCH 02/20] 6160: Dot rendering --- .../autopsy/geolocation/MapPanel.java | 45 ++++++++++++++++--- .../autopsy/geolocation/MapWaypoint.java | 7 +++ 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java b/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java index 46600f2c86..4134e09190 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java @@ -19,11 +19,13 @@ package org.sleuthkit.autopsy.geolocation; import java.awt.AlphaComposite; +import java.awt.BasicStroke; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Rectangle; +import java.awt.RenderingHints; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ComponentAdapter; @@ -71,6 +73,7 @@ import org.sleuthkit.autopsy.geolocation.datamodel.GeoLocationDataException; import org.sleuthkit.datamodel.TskCoreException; import javax.imageio.ImageIO; import javax.swing.SwingUtilities; +import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; /** * The map panel. This panel contains the jxmapviewer MapViewer @@ -691,7 +694,8 @@ final public class MapPanel extends javax.swing.JPanel { */ private class MapWaypointRenderer implements WaypointRenderer { - private final Map imageCache = new HashMap<>(); + private final Map dotImageCache = new HashMap<>(); + private final Map waypointImageCache = new HashMap<>(); /** * @@ -710,6 +714,28 @@ final public class MapPanel extends javax.swing.JPanel { } } + /** + * Creates a dot image with the specified color + * + * @param color the color of the new image + * @return the new dot image + */ + private BufferedImage createTrackDotImage(Color color) { + int w = 10; + int h = 10; + + BufferedImage ret = new BufferedImage(w + 2, h + 2, BufferedImage.TYPE_INT_ARGB); + Graphics2D g = ret.createGraphics(); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g.setColor(color); + g.fillOval(1, 1, w, h); + g.setColor(Color.BLACK); + g.setStroke(new BasicStroke(1)); + g.drawOval(1, 1, w, h); + g.dispose(); + return ret; + } + /** * Creates a waypoint image with the specified color * @@ -736,11 +762,20 @@ final public class MapPanel extends javax.swing.JPanel { @Override public void paintWaypoint(Graphics2D gd, JXMapViewer jxmv, MapWaypoint waypoint) { Color color = getColor(waypoint, currentlySelectedWaypoint); + BufferedImage image; + int artifactType = waypoint.getArtifactTypeID(); - // Store computed images in cache for later use - BufferedImage image = imageCache.computeIfAbsent(color, k -> { - return createWaypointImage(color); - }); + if (artifactType == ARTIFACT_TYPE.TSK_GPS_TRACKPOINT.getTypeID() || + artifactType == ARTIFACT_TYPE.TSK_GPS_TRACK.getTypeID()) { + image = dotImageCache.computeIfAbsent(color, k -> { + return createTrackDotImage(color); + }); + } else { + // Store computed images in cache for later use + image = waypointImageCache.computeIfAbsent(color, k -> { + return createWaypointImage(color); + }); + } Point2D point = jxmv.getTileFactory().geoToPixel(waypoint.getPosition(), jxmv.getZoom()); diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/MapWaypoint.java b/Core/src/org/sleuthkit/autopsy/geolocation/MapWaypoint.java index f9e4ed86ee..1bf9e1be0c 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/MapWaypoint.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/MapWaypoint.java @@ -199,6 +199,13 @@ final class MapWaypoint extends KdTree.XYZPoint implements org.jxmapviewer.viewe return getFormattedDetails(dataModelWaypoint); } + /** + * Returns the artifact type for this waypoint's data source + */ + int getArtifactTypeID() { + return dataModelWaypoint.getArtifact().getArtifactTypeID(); + } + /** * Returns a list of JMenuItems for the waypoint. The list list may contain * nulls which should be removed or replaced with JSeparators. From a87bea71d488b7ffff91dfe3f803d00f51fb005b Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Mon, 13 Apr 2020 15:57:45 -0400 Subject: [PATCH 03/20] adding progress updates --- .../org/sleuthkit/autopsy/casemodule/Case.java | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 4ca47bd658..9071880d0b 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -1982,7 +1982,7 @@ public class Case { checkForCancellation(); openCommunicationChannels(progressIndicator); checkForCancellation(); - openFileSystems(); + openFileSystems(progressIndicator); return null; } catch (CaseActionException ex) { @@ -2001,13 +2001,20 @@ public class Case { } } + /** * Reads a sector from each file system of each image of a case to do an eager open of the filesystems in case. + * @param progressIndicator The progress indicator for the operation. * @throws CaseActionCancelledException Exception thrown if task is cancelled. */ - private void openFileSystems() throws CaseActionCancelledException { - String caseName = (this.caseDb != null) ? this.caseDb.getDatabaseName() : "null"; + @Messages({ + "# {0} - case", "Case.openFileSystems.retrievingImages=Retrieving images for case: {0}...", + "# {0} - image", "Case.openFileSystems.openingImage=Opening all filesystems for image: {0}..." + }) + private void openFileSystems(ProgressIndicator progressIndicator) throws CaseActionCancelledException { + String caseName = (this.caseDb != null) ? this.caseDb.getDatabaseName() : ""; + progressIndicator.progress(Bundle.Case_openFileSystems_retrievingImages(caseName)); List images = null; try { images = this.caseDb.getImages(); @@ -2024,6 +2031,10 @@ public class Case { byte[] tempBuff = new byte[512]; for (Image image : images) { + String imageStr = image.getName(); + + progressIndicator.progress(Bundle.Case_openFileSystems_openingImage(imageStr)); + Collection fileSystems = this.caseDb.getFileSystems(image); checkForCancellation(); for (FileSystem fileSystem : fileSystems) { @@ -2031,7 +2042,6 @@ public class Case { fileSystem.read(tempBuff, 0, 512); } catch (TskCoreException ex) { - String imageStr = image.getName(); String fileSysStr = fileSystem.getName(); logger.log( From e135994242144644889b0ce3455151191b3e1d46 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Tue, 14 Apr 2020 08:17:30 -0400 Subject: [PATCH 04/20] open background task thread --- .../sleuthkit/autopsy/casemodule/Case.java | 133 ++++++++++++------ 1 file changed, 92 insertions(+), 41 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 9071880d0b..d351efd5dd 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.casemodule; import org.sleuthkit.autopsy.featureaccess.FeatureAccessUtils; import com.google.common.annotations.Beta; import com.google.common.eventbus.Subscribe; +import com.google.common.util.concurrent.ThreadFactoryBuilder; import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData; import java.awt.Frame; import java.awt.event.ActionEvent; @@ -1982,7 +1983,7 @@ public class Case { checkForCancellation(); openCommunicationChannels(progressIndicator); checkForCancellation(); - openFileSystems(progressIndicator); + openFileSystemsInBackground(); return null; } catch (CaseActionException ex) { @@ -2001,59 +2002,109 @@ public class Case { } } - + private static Future backgroundOpenFileSystemsFuture = null; + private static final ExecutorService startIngestJobsExecutor = + Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("Case-open-file-systems-%d").build()); + /** - * Reads a sector from each file system of each image of a case to do an eager open of the filesystems in case. - * @param progressIndicator The progress indicator for the operation. + * Starts a background task that reads a sector from each file system of each image of a case to do an eager open of + * the filesystems in case. If this method is called before another background file system read has finished the earlier + * one will be cancelled. + * * @throws CaseActionCancelledException Exception thrown if task is cancelled. */ @Messages({ "# {0} - case", "Case.openFileSystems.retrievingImages=Retrieving images for case: {0}...", "# {0} - image", "Case.openFileSystems.openingImage=Opening all filesystems for image: {0}..." }) - private void openFileSystems(ProgressIndicator progressIndicator) throws CaseActionCancelledException { - String caseName = (this.caseDb != null) ? this.caseDb.getDatabaseName() : ""; + private void openFileSystemsInBackground() throws CaseActionCancelledException { + if (backgroundOpenFileSystemsFuture != null && !backgroundOpenFileSystemsFuture.isDone()) + backgroundOpenFileSystemsFuture.cancel(true); - progressIndicator.progress(Bundle.Case_openFileSystems_retrievingImages(caseName)); - List images = null; - try { - images = this.caseDb.getImages(); - } catch (TskCoreException ex) { - logger.log( - Level.SEVERE, - String.format("Could not obtain images while opening case: %s.", caseName), - ex); - - return; + BackgroundOpenFileSystemsTask backgroundTask = new BackgroundOpenFileSystemsTask(this.caseDb, new LoggingProgressIndicator()); + backgroundOpenFileSystemsFuture = startIngestJobsExecutor.submit(backgroundTask); + } + + + + /** + * This task opens all the filesystems of all images in the case in the background. + * It also responds to cancellation events. + */ + private static class BackgroundOpenFileSystemsTask implements Runnable { + private final SleuthkitCase tskCase; + private final String caseName; + private final ProgressIndicator progressIndicator; + + + BackgroundOpenFileSystemsTask(SleuthkitCase tskCase, ProgressIndicator progressIndicator) { + this.tskCase = tskCase; + this.progressIndicator = progressIndicator; + caseName = (this.tskCase != null) ? this.tskCase.getDatabaseName() : ""; } - checkForCancellation(); - byte[] tempBuff = new byte[512]; + private List getImages() { + progressIndicator.progress(Bundle.Case_openFileSystems_retrievingImages(caseName)); + List images = null; + try { + images = this.tskCase.getImages(); + } catch (TskCoreException ex) { + logger.log( + Level.SEVERE, + String.format("Could not obtain images while opening case: %s.", caseName), + ex); - for (Image image : images) { - String imageStr = image.getName(); - - progressIndicator.progress(Bundle.Case_openFileSystems_openingImage(imageStr)); - - Collection fileSystems = this.caseDb.getFileSystems(image); - checkForCancellation(); - for (FileSystem fileSystem : fileSystems) { - try { - fileSystem.read(tempBuff, 0, 512); - } - catch (TskCoreException ex) { - String fileSysStr = fileSystem.getName(); - - logger.log( - Level.WARNING, - String.format("Could not open filesystem: %s in image: %s for case: %s.", fileSysStr, imageStr, caseName), - ex); - } - - checkForCancellation(); + return null; } - + return images; } + + private void openFileSystems(List images) throws CaseActionCancelledException { + byte[] tempBuff = new byte[512]; + + for (Image image : images) { + String imageStr = image.getName(); + + progressIndicator.progress(Bundle.Case_openFileSystems_openingImage(imageStr)); + + Collection fileSystems = this.tskCase.getFileSystems(image); + checkForCancellation(); + for (FileSystem fileSystem : fileSystems) { + try { + fileSystem.read(tempBuff, 0, 512); + } + catch (TskCoreException ex) { + String fileSysStr = fileSystem.getName(); + + logger.log( + Level.WARNING, + String.format("Could not open filesystem: %s in image: %s for case: %s.", fileSysStr, imageStr, caseName), + ex); + } + + checkForCancellation(); + } + + } + } + + @Override + public void run() { + try { + checkForCancellation(); + List images = getImages(); + if (images == null) + return; + + checkForCancellation(); + openFileSystems(images); + } + catch (CaseActionCancelledException ex) { + // EMPTY + // No action needs to be taken when this task is cancelled. + } + } + } /** From 7d186a78bded940c74a972adf53582a925bfd569 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Tue, 14 Apr 2020 15:49:13 -0400 Subject: [PATCH 05/20] commenting --- .../sleuthkit/autopsy/casemodule/Case.java | 119 +++++++++++------- 1 file changed, 77 insertions(+), 42 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index d351efd5dd..248525ccab 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -159,6 +159,9 @@ public class Case { private static final Logger logger = Logger.getLogger(Case.class.getName()); private static final AutopsyEventPublisher eventPublisher = new AutopsyEventPublisher(); private static final Object caseActionSerializationLock = new Object(); + private static Future backgroundOpenFileSystemsFuture = null; + private static final ExecutorService startIngestJobsExecutor + = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("Case-open-file-systems-%d").build()); private static volatile Frame mainFrame; private static volatile Case currentCase; private final CaseMetadata metadata; @@ -2001,48 +2004,71 @@ public class Case { throw ex; } } - - private static Future backgroundOpenFileSystemsFuture = null; - private static final ExecutorService startIngestJobsExecutor = - Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("Case-open-file-systems-%d").build()); - + /** - * Starts a background task that reads a sector from each file system of each image of a case to do an eager open of - * the filesystems in case. If this method is called before another background file system read has finished the earlier - * one will be cancelled. - * - * @throws CaseActionCancelledException Exception thrown if task is cancelled. + * Starts a background task that reads a sector from each file system of + * each image of a case to do an eager open of the filesystems in case. If + * this method is called before another background file system read has + * finished the earlier one will be cancelled. + * + * @throws CaseActionCancelledException Exception thrown if task is + * cancelled. */ @Messages({ "# {0} - case", "Case.openFileSystems.retrievingImages=Retrieving images for case: {0}...", "# {0} - image", "Case.openFileSystems.openingImage=Opening all filesystems for image: {0}..." }) private void openFileSystemsInBackground() throws CaseActionCancelledException { - if (backgroundOpenFileSystemsFuture != null && !backgroundOpenFileSystemsFuture.isDone()) + if (backgroundOpenFileSystemsFuture != null && !backgroundOpenFileSystemsFuture.isDone()) { backgroundOpenFileSystemsFuture.cancel(true); - + } + BackgroundOpenFileSystemsTask backgroundTask = new BackgroundOpenFileSystemsTask(this.caseDb, new LoggingProgressIndicator()); backgroundOpenFileSystemsFuture = startIngestJobsExecutor.submit(backgroundTask); } - - - + /** - * This task opens all the filesystems of all images in the case in the background. - * It also responds to cancellation events. + * This task opens all the filesystems of all images in the case in the + * background. It also responds to cancellation events. */ private static class BackgroundOpenFileSystemsTask implements Runnable { + private final SleuthkitCase tskCase; private final String caseName; private final ProgressIndicator progressIndicator; - + /** + * Main constructor for the BackgroundOpenFileSystemsTask. + * + * @param tskCase The case database to query for filesystems + * to open. + * @param progressIndicator The progress indicator for file systems + * opened. + */ BackgroundOpenFileSystemsTask(SleuthkitCase tskCase, ProgressIndicator progressIndicator) { this.tskCase = tskCase; this.progressIndicator = progressIndicator; caseName = (this.tskCase != null) ? this.tskCase.getDatabaseName() : ""; } - + + /** + * Checks if thread has been cancelled and throws an + * InterruptedException if it has. + * + * @throws InterruptedException The exception thrown if the operation + * has been cancelled. + */ + private void checkIfCancelled() throws InterruptedException { + if (Thread.interrupted()) { + throw new InterruptedException(); + } + } + + /** + * Retrieves all images present in the sleuthkit case. + * + * @return All images present in the sleuthkit case. + */ private List getImages() { progressIndicator.progress(Bundle.Case_openFileSystems_retrievingImages(caseName)); List images = null; @@ -2050,61 +2076,70 @@ public class Case { images = this.tskCase.getImages(); } catch (TskCoreException ex) { logger.log( - Level.SEVERE, - String.format("Could not obtain images while opening case: %s.", caseName), - ex); + Level.SEVERE, + String.format("Could not obtain images while opening case: %s.", caseName), + ex); return null; } return images; } - - private void openFileSystems(List images) throws CaseActionCancelledException { + + /** + * Opens all file systems in the list of images provided. + * + * @param images The images whose file systems will be opened. + * + * @throws CaseActionCancelledException The exception thrown in the + * event that the operation is + * cancelled prior to completion. + */ + private void openFileSystems(List images) throws InterruptedException { byte[] tempBuff = new byte[512]; - + for (Image image : images) { String imageStr = image.getName(); progressIndicator.progress(Bundle.Case_openFileSystems_openingImage(imageStr)); Collection fileSystems = this.tskCase.getFileSystems(image); - checkForCancellation(); + checkIfCancelled(); for (FileSystem fileSystem : fileSystems) { try { - fileSystem.read(tempBuff, 0, 512); - } - catch (TskCoreException ex) { + fileSystem.read(tempBuff, 0, 512); + } catch (TskCoreException ex) { String fileSysStr = fileSystem.getName(); logger.log( - Level.WARNING, - String.format("Could not open filesystem: %s in image: %s for case: %s.", fileSysStr, imageStr, caseName), - ex); + Level.WARNING, + String.format("Could not open filesystem: %s in image: %s for case: %s.", fileSysStr, imageStr, caseName), + ex); } - checkForCancellation(); + checkIfCancelled(); } } } - + @Override public void run() { try { - checkForCancellation(); + checkIfCancelled(); List images = getImages(); - if (images == null) + if (images == null) { return; + } - checkForCancellation(); + checkIfCancelled(); openFileSystems(images); - } - catch (CaseActionCancelledException ex) { - // EMPTY - // No action needs to be taken when this task is cancelled. + } catch (InterruptedException ex) { + logger.log( + Level.INFO, + String.format("Background operation opening all file systems in %s has been cancelled.", caseName)); } } - + } /** From 0e2520d5c0ea3dbd4839ab351d66ad573e52d39d Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Tue, 14 Apr 2020 15:53:47 -0400 Subject: [PATCH 06/20] commenting --- Core/src/org/sleuthkit/autopsy/casemodule/Case.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 248525ccab..917ddf6210 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -2071,9 +2071,8 @@ public class Case { */ private List getImages() { progressIndicator.progress(Bundle.Case_openFileSystems_retrievingImages(caseName)); - List images = null; try { - images = this.tskCase.getImages(); + return this.tskCase.getImages(); } catch (TskCoreException ex) { logger.log( Level.SEVERE, @@ -2082,7 +2081,6 @@ public class Case { return null; } - return images; } /** From 52ded97f8db4ce0c0b030b4175c1f6e9432bea44 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Wed, 15 Apr 2020 15:49:00 -0400 Subject: [PATCH 07/20] beginnings of gpx file ingest --- .../GPX_Module/GPX_Parser_Module.py | 268 ++++++++++-------- 1 file changed, 145 insertions(+), 123 deletions(-) diff --git a/InternalPythonModules/GPX_Module/GPX_Parser_Module.py b/InternalPythonModules/GPX_Module/GPX_Parser_Module.py index 3d202a963b..73952a341d 100644 --- a/InternalPythonModules/GPX_Module/GPX_Parser_Module.py +++ b/InternalPythonModules/GPX_Module/GPX_Parser_Module.py @@ -60,12 +60,17 @@ import gpxpy import gpxpy.gpx import gpxpy.parser +# to get a random filename to prevent race conditions +import uuid + # Factory that defines the name and details of the module and allows Autopsy # to create instances of the modules that will do the analysis. + + class GPXParserDataSourceIngestModuleFactory(IngestModuleFactoryAdapter): moduleName = "GPX Parser" - + def getModuleDisplayName(self): return self.moduleName @@ -75,158 +80,175 @@ class GPXParserDataSourceIngestModuleFactory(IngestModuleFactoryAdapter): def getModuleVersionNumber(self): return "1.2" - def isDataSourceIngestModuleFactory(self): + def isFileIngestModuleFactory(self): return True - def createDataSourceIngestModule(self, ingestOptions): + def createFileIngestModule(self, ingestOptions): return GPXParserDataSourceIngestModule() - -# Data Source-level ingest module. One gets created per data source. -class GPXParserDataSourceIngestModule(DataSourceIngestModule): - logger = Logger.getLogger(GPXParserDataSourceIngestModuleFactory.moduleName) +# Data Source-level ingest module. One gets created per data source. +class GPXParserDataSourceIngestModule(FileIngestModule): + + logger = Logger.getLogger( + GPXParserDataSourceIngestModuleFactory.moduleName) writeDebugMsgs = False def log(self, level, msg): - self.logger.logp(level, self.__class__.__name__, inspect.stack()[1][3], msg) + self.logger.logp(level, self.__class__.__name__, + inspect.stack()[1][3], msg) def __init__(self): self.context = None - - # Where any setup and configuration is done. - def startUp(self, context): - self.context = context - - # Where the analysis is done. - def process(self, dataSource, progressBar): - - # We don't know how much work there is yet. - progressBar.switchToIndeterminate() - - # Get the case database and its blackboard. - skCase = Case.getCurrentCase().getSleuthkitCase() - blackboard = skCase.getBlackboard() - - # Get any files with a .gpx extension. - # It would perhaps be better to get these files by MIME type instead. - # RC: It would also be better if this were a file level ingest module so it could process files extracted from archives. - fileManager = Case.getCurrentCase().getServices().getFileManager() - files = fileManager.findFiles(dataSource, "%.gpx") - - # Update the progress bar now that we know how much work there is to do. - numFiles = len(files) - if self.writeDebugMsgs: self.log(Level.INFO, "Found " + str(numFiles) + " GPX files") - progressBar.switchToDeterminate(numFiles) + self.fileCount = 0 # Get the module name, it will be needed for adding attributes - moduleName = GPXParserDataSourceIngestModuleFactory.moduleName + self.moduleName = GPXParserDataSourceIngestModuleFactory.moduleName - # Check if a folder for this module is present in the case Temp directory. + # Check if a folder for this module is present in the case Temp directory. # If not, create it. - dirName = os.path.join(Case.getCurrentCase().getTempDirectory(), "GPX_Parser_Module") + self.dirName = os.path.join( + Case.getCurrentCase().getTempDirectory(), "GPX_Parser_Module") try: - os.stat(dirName) + os.stat(self.dirName) except: - os.mkdir(dirName) + os.mkdir(self.dirName) - # Create a temp file name. It appears that we cannot close and delete + # Where any setup and configuration is done. + + def startUp(self, context): + self.context = context + self.fileFound = 0 + + # Where the file analysis is done. + def process(self, file): + if not file.getName().lower().endswith(".gpx"): + return IngestModule.ProcessResult.OK + + # Create a temp file name. It appears that we cannot close and delete # this file, but we can overwrite it for each file we need to process. - fileName = os.path.join(dirName, "tmp.gpx") - - fileCount = 0; - for file in files: + fileName = os.path.join(self.dirName, uuid.uuid4().hex + ".gpx") - # Create a GeoArtifactsHelper for this file. - geoArtifactHelper = GeoArtifactsHelper(skCase, moduleName, None, file) - - # Check if the user pressed cancel while we were busy. - if self.context.isJobCancelled(): - return IngestModule.ProcessResult.OK + # Create a GeoArtifactsHelper for this file. + geoArtifactHelper = GeoArtifactsHelper(skCase, moduleName, None, file) - if self.writeDebugMsgs: self.log(Level.INFO, "Processing " + file.getUniquePath() + " (objID = " + str(file.getId()) + ")") - fileCount += 1 + if self.writeDebugMsgs: + self.log(Level.INFO, "Processing " + file.getUniquePath() + + " (objID = " + str(file.getId()) + ")") - # Write the file so that it can be parsed by gpxpy. - localFile = File(fileName) - ContentUtils.writeToFile(file, localFile) + self.fileCount += 1 - # Send the file to gpxpy for parsing. - gpxfile = open(fileName) - try: - gpx = gpxpy.parse(gpxfile) - if self.writeDebugMsgs: self.log(Level.INFO, "Parsed " + file.getUniquePath() + " (objID = " + str(file.getId()) + ")") - except Exception as e: - self.log(Level.WARNING, "Error parsing file " + file.getUniquePath() + " (objID = " + str(file.getId()) + "):" + str(e)) - continue - - if gpx: - if self.writeDebugMsgs: self.log(Level.INFO, "Processing tracks from " + file.getUniquePath() + " (objID = " + str(file.getId()) + ")") - for track in gpx.tracks: - for segment in track.segments: - geoPointList = GeoTrackPoints() - for point in segment.points: + # Write the file so that it can be parsed by gpxpy. + localFile = File(fileName) + ContentUtils.writeToFile(file, localFile) - elevation = 0 - if point.elevation != None: - elevation = point.elevation - - timeStamp = 0 - try: - if (point.time != None): - timeStamp = long(time.mktime(point.time.timetuple())) - except Exception as e: - self.log(Level.WARNING, "Error getting track timestamp from " + file.getUniquePath() + " (objID = " + str(file.getId()) + "):" + str(e)) + # Send the file to gpxpy for parsing. + gpxfile = open(fileName) + try: + gpx = gpxpy.parse(gpxfile) + if self.writeDebugMsgs: + self.log(Level.INFO, "Parsed " + file.getUniquePath() + + " (objID = " + str(file.getId()) + ")") + except Exception as e: + self.log(Level.WARNING, "Error parsing file " + file.getUniquePath() + + " (objID = " + str(file.getId()) + "):" + str(e)) + continue - geoPointList.addPoint(TrackPoint(point.latitude, point.longitude, elevation, None, 0, 0, 0, timeStamp)) - + if gpx: + if self.writeDebugMsgs: + self.log(Level.INFO, "Processing tracks from " + + file.getUniquePath() + " (objID = " + str(file.getId()) + ")") + + for track in gpx.tracks: + for segment in track.segments: + geoPointList = GeoTrackPoints() + for point in segment.points: + + elevation = 0 + if point.elevation != None: + elevation = point.elevation + + timeStamp = 0 try: - geoArtifactHelper.addTrack("Track", geoPointList, None) - except Blackboard.BlackboardException as e: - self.log(Level.SEVERE, "Error posting GPS track artifact for " + file.getUniquePath() + " (objID = " + str(file.getId()) + "):" + e.getMessage()) - except TskCoreException as e: - self.log(Level.SEVERE, "Error creating GPS track artifact for " + file.getUniquePath() + " (objID = " + str(file.getId()) + "):" + e.getMessage()) - - if self.writeDebugMsgs: self.log(Level.INFO, "Processing waypoints from " + file.getUniquePath() + " (objID = " + str(file.getId()) + ")") - for waypoint in gpx.waypoints: - + if (point.time != None): + timeStamp = long(time.mktime( + point.time.timetuple())) + except Exception as e: + self.log(Level.WARNING, "Error getting track timestamp from " + + file.getUniquePath() + " (objID = " + str(file.getId()) + "):" + str(e)) + + geoPointList.addPoint(TrackPoint( + point.latitude, point.longitude, elevation, None, 0, 0, 0, timeStamp)) + try: - art = file.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_BOOKMARK) - - attributes = ArrayList() - attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE.getTypeID(), moduleName, waypoint.latitude)) - attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE.getTypeID(), moduleName, waypoint.longitude)) - attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_FLAG.getTypeID(), moduleName, "Waypoint")) - attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME.getTypeID(), moduleName, waypoint.name)) - attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME.getTypeID(), moduleName, "GPXParser")) - art.addAttributes(attributes) - - blackboard.postArtifact(art, moduleName) - + geoArtifactHelper.addTrack("Track", geoPointList, None) except Blackboard.BlackboardException as e: - self.log(Level.SEVERE, "Error posting GPS bookmark artifact for " + file.getUniquePath() + " (objID = " + str(file.getId()) + "):" + e.getMessage()) + self.log(Level.SEVERE, "Error posting GPS track artifact for " + + file.getUniquePath() + " (objID = " + str(file.getId()) + "):" + e.getMessage()) except TskCoreException as e: - self.log(Level.SEVERE, "Error creating GPS bookmark artifact for " + file.getUniquePath() + " (objID = " + str(file.getId()) + "):" + e.getMessage()) + self.log(Level.SEVERE, "Error creating GPS track artifact for " + + file.getUniquePath() + " (objID = " + str(file.getId()) + "):" + e.getMessage()) - if self.writeDebugMsgs: self.log(Level.INFO, "Processing routes from " + file.getUniquePath() + " (objID = " + str(file.getId()) + ")") - for route in gpx.routes: + if self.writeDebugMsgs: + self.log(Level.INFO, "Processing waypoints from " + + file.getUniquePath() + " (objID = " + str(file.getId()) + ")") - geoWaypoints = GeoWaypoints() + for waypoint in gpx.waypoints: - for point in route.points: - geoWaypoints.addPoint(Waypoint(point.latitude, point.longitude, point.elevation, point.name)) - - try: - geoArtifactHelper.addRoute(None, None, geoWaypoints, None) - except Blackboard.BlackboardException as e: - self.log("Error posting GPS route artifact for " + file.getUniquePath() + " (objID = " + str(file.getId()) + "):" + e.getMessage()) - except TskCoreException as e: - self.log(Level.SEVERE, "Error creating GPS route artifact for " + file.getUniquePath() + " (objID = " + str(file.getId()) + "):" + e.getMessage()) - - # Update the progress bar. - progressBar.progress(fileCount) + try: + art = file.newArtifact( + BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_BOOKMARK) - # Post a message to the ingest messages inbox. - message = IngestMessage.createMessage(IngestMessage.MessageType.DATA, moduleName, "Processed %d files" % fileCount) - IngestServices.getInstance().postMessage(message) - return IngestModule.ProcessResult.OK; + attributes = ArrayList() + attributes.add(BlackboardAttribute( + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE.getTypeID(), moduleName, waypoint.latitude)) + attributes.add(BlackboardAttribute( + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE.getTypeID(), moduleName, waypoint.longitude)) + attributes.add(BlackboardAttribute( + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_FLAG.getTypeID(), moduleName, "Waypoint")) + attributes.add(BlackboardAttribute( + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME.getTypeID(), moduleName, waypoint.name)) + attributes.add(BlackboardAttribute( + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME.getTypeID(), moduleName, "GPXParser")) + art.addAttributes(attributes) + + blackboard.postArtifact(art, moduleName) + + except Blackboard.BlackboardException as e: + self.log(Level.SEVERE, "Error posting GPS bookmark artifact for " + + file.getUniquePath() + " (objID = " + str(file.getId()) + "):" + e.getMessage()) + except TskCoreException as e: + self.log(Level.SEVERE, "Error creating GPS bookmark artifact for " + + file.getUniquePath() + " (objID = " + str(file.getId()) + "):" + e.getMessage()) + + if self.writeDebugMsgs: + self.log(Level.INFO, "Processing routes from " + + file.getUniquePath() + " (objID = " + str(file.getId()) + ")") + + for route in gpx.routes: + + geoWaypoints = GeoWaypoints() + + for point in route.points: + geoWaypoints.addPoint( + Waypoint(point.latitude, point.longitude, point.elevation, point.name)) + + try: + geoArtifactHelper.addRoute(None, None, geoWaypoints, None) + except Blackboard.BlackboardException as e: + self.log("Error posting GPS route artifact for " + file.getUniquePath() + + " (objID = " + str(file.getId()) + "):" + e.getMessage()) + except TskCoreException as e: + self.log(Level.SEVERE, "Error creating GPS route artifact for " + + file.getUniquePath() + " (objID = " + str(file.getId()) + "):" + e.getMessage()) + + return IngestModule.ProcessResult.OK + + # Where any shutdown code is run and resources are freed. + + def shutDown(self): + # As a final part of this example, we'll send a message to the ingest inbox with the number of files found (in this thread) + message = IngestMessage.createMessage( + IngestMessage.MessageType.DATA, SampleJythonFileIngestModuleFactory.moduleName, + str(self.filesFound) + " files found") + ingestServices = IngestServices.getInstance().postMessage(message) From f6be62c09097fb0e7b9d62a6212d1c78e204bb42 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Wed, 15 Apr 2020 16:27:03 -0400 Subject: [PATCH 08/20] syntax updates --- InternalPythonModules/GPX_Module/GPX_Parser_Module.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/InternalPythonModules/GPX_Module/GPX_Parser_Module.py b/InternalPythonModules/GPX_Module/GPX_Parser_Module.py index 73952a341d..f4a83c1995 100644 --- a/InternalPythonModules/GPX_Module/GPX_Parser_Module.py +++ b/InternalPythonModules/GPX_Module/GPX_Parser_Module.py @@ -92,7 +92,7 @@ class GPXParserDataSourceIngestModule(FileIngestModule): logger = Logger.getLogger( GPXParserDataSourceIngestModuleFactory.moduleName) - writeDebugMsgs = False + writeDebugMsgs = True def log(self, level, msg): self.logger.logp(level, self.__class__.__name__, @@ -136,8 +136,6 @@ class GPXParserDataSourceIngestModule(FileIngestModule): self.log(Level.INFO, "Processing " + file.getUniquePath() + " (objID = " + str(file.getId()) + ")") - self.fileCount += 1 - # Write the file so that it can be parsed by gpxpy. localFile = File(fileName) ContentUtils.writeToFile(file, localFile) @@ -152,7 +150,7 @@ class GPXParserDataSourceIngestModule(FileIngestModule): except Exception as e: self.log(Level.WARNING, "Error parsing file " + file.getUniquePath() + " (objID = " + str(file.getId()) + "):" + str(e)) - continue + return IngestModule.ProcessResult.ERROR if gpx: if self.writeDebugMsgs: @@ -242,6 +240,7 @@ class GPXParserDataSourceIngestModule(FileIngestModule): self.log(Level.SEVERE, "Error creating GPS route artifact for " + file.getUniquePath() + " (objID = " + str(file.getId()) + "):" + e.getMessage()) + self.fileCount += 1 return IngestModule.ProcessResult.OK # Where any shutdown code is run and resources are freed. From 43152e56f3fc768827ed6cecb174c4cfda2f1d8f Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 16 Apr 2020 09:16:22 -0400 Subject: [PATCH 09/20] bug fixes --- .../GPX_Module/GPX_Parser_Module.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/InternalPythonModules/GPX_Module/GPX_Parser_Module.py b/InternalPythonModules/GPX_Module/GPX_Parser_Module.py index f4a83c1995..d73a55555e 100644 --- a/InternalPythonModules/GPX_Module/GPX_Parser_Module.py +++ b/InternalPythonModules/GPX_Module/GPX_Parser_Module.py @@ -105,6 +105,10 @@ class GPXParserDataSourceIngestModule(FileIngestModule): # Get the module name, it will be needed for adding attributes self.moduleName = GPXParserDataSourceIngestModuleFactory.moduleName + # Get the case database and its blackboard. + self.skCase = Case.getCurrentCase().getSleuthkitCase() + self.blackboard = self.skCase.getBlackboard() + # Check if a folder for this module is present in the case Temp directory. # If not, create it. self.dirName = os.path.join( @@ -118,7 +122,7 @@ class GPXParserDataSourceIngestModule(FileIngestModule): def startUp(self, context): self.context = context - self.fileFound = 0 + self.fileCount = 0 # Where the file analysis is done. def process(self, file): @@ -130,7 +134,7 @@ class GPXParserDataSourceIngestModule(FileIngestModule): fileName = os.path.join(self.dirName, uuid.uuid4().hex + ".gpx") # Create a GeoArtifactsHelper for this file. - geoArtifactHelper = GeoArtifactsHelper(skCase, moduleName, None, file) + geoArtifactHelper = GeoArtifactsHelper(self.skCase, self.moduleName, None, file) if self.writeDebugMsgs: self.log(Level.INFO, "Processing " + file.getUniquePath() + @@ -210,7 +214,7 @@ class GPXParserDataSourceIngestModule(FileIngestModule): BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME.getTypeID(), moduleName, "GPXParser")) art.addAttributes(attributes) - blackboard.postArtifact(art, moduleName) + self.blackboard.postArtifact(art, self.moduleName) except Blackboard.BlackboardException as e: self.log(Level.SEVERE, "Error posting GPS bookmark artifact for " + @@ -248,6 +252,6 @@ class GPXParserDataSourceIngestModule(FileIngestModule): def shutDown(self): # As a final part of this example, we'll send a message to the ingest inbox with the number of files found (in this thread) message = IngestMessage.createMessage( - IngestMessage.MessageType.DATA, SampleJythonFileIngestModuleFactory.moduleName, - str(self.filesFound) + " files found") + IngestMessage.MessageType.DATA, GPXParserDataSourceIngestModuleFactory.moduleName, + str(self.fileCount) + " files found") ingestServices = IngestServices.getInstance().postMessage(message) From 1d5b9d4089fa9cd7df00e91c12c9d0c1a77b266e Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 16 Apr 2020 09:17:38 -0400 Subject: [PATCH 10/20] turn off debug --- InternalPythonModules/GPX_Module/GPX_Parser_Module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InternalPythonModules/GPX_Module/GPX_Parser_Module.py b/InternalPythonModules/GPX_Module/GPX_Parser_Module.py index d73a55555e..f8e3f04522 100644 --- a/InternalPythonModules/GPX_Module/GPX_Parser_Module.py +++ b/InternalPythonModules/GPX_Module/GPX_Parser_Module.py @@ -92,7 +92,7 @@ class GPXParserDataSourceIngestModule(FileIngestModule): logger = Logger.getLogger( GPXParserDataSourceIngestModuleFactory.moduleName) - writeDebugMsgs = True + writeDebugMsgs = False def log(self, level, msg): self.logger.logp(level, self.__class__.__name__, From 7e35e479f986e7bcb2745f87c2aac0e42b78ac47 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 16 Apr 2020 09:34:28 -0400 Subject: [PATCH 11/20] more bug fixes --- InternalPythonModules/GPX_Module/GPX_Parser_Module.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/InternalPythonModules/GPX_Module/GPX_Parser_Module.py b/InternalPythonModules/GPX_Module/GPX_Parser_Module.py index f8e3f04522..35b18847d1 100644 --- a/InternalPythonModules/GPX_Module/GPX_Parser_Module.py +++ b/InternalPythonModules/GPX_Module/GPX_Parser_Module.py @@ -203,15 +203,15 @@ class GPXParserDataSourceIngestModule(FileIngestModule): attributes = ArrayList() attributes.add(BlackboardAttribute( - BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE.getTypeID(), moduleName, waypoint.latitude)) + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE.getTypeID(), self.moduleName, waypoint.latitude)) attributes.add(BlackboardAttribute( - BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE.getTypeID(), moduleName, waypoint.longitude)) + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE.getTypeID(), self.moduleName, waypoint.longitude)) attributes.add(BlackboardAttribute( - BlackboardAttribute.ATTRIBUTE_TYPE.TSK_FLAG.getTypeID(), moduleName, "Waypoint")) + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_FLAG.getTypeID(), self.moduleName, "Waypoint")) attributes.add(BlackboardAttribute( - BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME.getTypeID(), moduleName, waypoint.name)) + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME.getTypeID(), self.moduleName, waypoint.name)) attributes.add(BlackboardAttribute( - BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME.getTypeID(), moduleName, "GPXParser")) + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME.getTypeID(), self.moduleName, "GPXParser")) art.addAttributes(attributes) self.blackboard.postArtifact(art, self.moduleName) From 65bd10afe12623e7014c9c560682d106ae15f4cf Mon Sep 17 00:00:00 2001 From: Ethan Roseman Date: Thu, 16 Apr 2020 10:43:41 -0400 Subject: [PATCH 12/20] 6160 track rendering --- .../geolocation/AbstractWaypointFetcher.java | 50 ++++--- .../geolocation/GeolocationTopComponent.java | 9 +- .../autopsy/geolocation/MapPanel.java | 122 ++++++++++++++---- .../autopsy/geolocation/MapWaypoint.java | 2 +- .../datamodel/WaypointBuilder.java | 10 +- 5 files changed, 144 insertions(+), 49 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/AbstractWaypointFetcher.java b/Core/src/org/sleuthkit/autopsy/geolocation/AbstractWaypointFetcher.java index bc56968f99..e923d6dbd6 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/AbstractWaypointFetcher.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/AbstractWaypointFetcher.java @@ -22,6 +22,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.logging.Level; +import javafx.util.Pair; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.geolocation.datamodel.GeoLocationDataException; @@ -75,11 +76,11 @@ abstract class AbstractWaypointFetcher implements WaypointBuilder.WaypointFilter * * @param mapWaypoints List of filtered MapWaypoints. */ - abstract void handleFilteredWaypointSet(Set mapWaypoints); + abstract void handleFilteredWaypointSet(Set mapWaypoints, List> tracks); @Override public void process(List waypoints) { - List tracks = null; + List tracks = new ArrayList<>(); if (filters.getArtifactTypes().contains(ARTIFACT_TYPE.TSK_GPS_TRACK)) { try { tracks = Track.getTracks(Case.getCurrentCase().getSleuthkitCase(), filters.getDataSources()); @@ -87,11 +88,15 @@ abstract class AbstractWaypointFetcher implements WaypointBuilder.WaypointFilter logger.log(Level.WARNING, "Exception thrown while retrieving list of Tracks", ex); } } + Pair, List>> waypointsAndTracks = createWaypointList(waypoints, tracks); + + final Set pointSet = MapWaypoint.getWaypoints(waypointsAndTracks.getKey()); + final List> trackSets = new ArrayList<>(); + for (List t : waypointsAndTracks.getValue()) { + trackSets.add(MapWaypoint.getWaypoints(t)); + } - List completeList = createWaypointList(waypoints, tracks); - final Set pointSet = MapWaypoint.getWaypoints(completeList); - - handleFilteredWaypointSet(pointSet); + handleFilteredWaypointSet(pointSet, trackSets); } /** @@ -104,8 +109,9 @@ abstract class AbstractWaypointFetcher implements WaypointBuilder.WaypointFilter * @return A list of waypoints including the tracks based on the current * filters. */ - private List createWaypointList(List waypoints, List tracks) { + private Pair, List>> createWaypointList(List waypoints, List tracks) { final List completeList = new ArrayList<>(); + List> filteredTracks = new ArrayList<>(); if (tracks != null) { Long timeRangeEnd; @@ -117,19 +123,22 @@ abstract class AbstractWaypointFetcher implements WaypointBuilder.WaypointFilter timeRangeStart = timeRangeEnd - (86400 * filters.getMostRecentNumDays()); completeList.addAll(getWaypointsInRange(timeRangeStart, timeRangeEnd, waypoints)); - completeList.addAll(getTracksInRange(timeRangeStart, timeRangeEnd, tracks)); - + + filteredTracks = getTracksInRange(timeRangeStart, timeRangeEnd, tracks); + for (List filteredTrack : filteredTracks) { + completeList.addAll(filteredTrack); + } } else { completeList.addAll(waypoints); for (Track track : tracks) { completeList.addAll(track.getPath()); + filteredTracks.add(track.getPath()); } } } else { completeList.addAll(waypoints); } - - return completeList; + return new Pair<>(completeList, filteredTracks); } /** @@ -158,31 +167,30 @@ abstract class AbstractWaypointFetcher implements WaypointBuilder.WaypointFilter } /** - * Return a list of waypoints from the given tracks that fall into for - * tracks that fall into the given time range. The track start time will - * used for determining if the whole track falls into the range. + * Return a list of lists of waypoints from the given tracks that fall into + * the given time range. The track start time will used for determining if + * the whole track falls into the range. * * @param timeRangeStart start timestamp of range (seconds from java epoch) * @param timeRangeEnd start timestamp of range (seconds from java epoch) * @param tracks Track list. * - * @return A list of waypoints that that belong to tracks that fall into the - * time range. + * @return A list of lists of waypoints corresponding to belong to tracks + * that exist within the time range. */ - private List getTracksInRange(Long timeRangeStart, Long timeRangeEnd, List tracks) { - List completeList = new ArrayList<>(); + private List> getTracksInRange(Long timeRangeStart, Long timeRangeEnd, List tracks) { + List> ret = new ArrayList<>(); if (tracks != null) { for (Track track : tracks) { Long trackTime = track.getStartTime(); if ((trackTime == null && filters.showWaypointsWithoutTimeStamp()) || (trackTime != null && (trackTime >= timeRangeStart && trackTime <= timeRangeEnd))) { - - completeList.addAll(track.getPath()); + ret.add(track.getPath()); } } } - return completeList; + return ret; } /** diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.java b/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.java index da8e14a65b..7973218de9 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.java @@ -50,6 +50,7 @@ import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.geolocation.GeoFilterPanel.GeoFilter; import org.sleuthkit.autopsy.geolocation.datamodel.GeoLocationDataException; +import org.sleuthkit.autopsy.geolocation.datamodel.Track; import org.sleuthkit.autopsy.ingest.IngestManager; import static org.sleuthkit.autopsy.ingest.IngestManager.IngestModuleEvent.DATA_ADDED; import org.sleuthkit.autopsy.ingest.ModuleDataEvent; @@ -330,7 +331,7 @@ public final class GeolocationTopComponent extends TopComponent { * * @param waypointList */ - void addWaypointsToMap(Set waypointList) { + void addWaypointsToMap(Set waypointList, List> tracks) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { @@ -347,6 +348,8 @@ public final class GeolocationTopComponent extends TopComponent { } mapPanel.clearWaypoints(); mapPanel.setWaypoints(waypointList); + mapPanel.setTracks(tracks); + mapPanel.initializePainter(); setWaypointLoading(false); geoFilterPanel.setEnabled(true); } @@ -499,8 +502,8 @@ public final class GeolocationTopComponent extends TopComponent { } @Override - void handleFilteredWaypointSet(Set mapWaypoints) { - addWaypointsToMap(mapWaypoints); + void handleFilteredWaypointSet(Set mapWaypoints, List> tracks) { + addWaypointsToMap(mapWaypoints, tracks); } } } diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java b/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java index 4134e09190..3fe66cbd66 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java @@ -73,6 +73,9 @@ import org.sleuthkit.autopsy.geolocation.datamodel.GeoLocationDataException; import org.sleuthkit.datamodel.TskCoreException; import javax.imageio.ImageIO; import javax.swing.SwingUtilities; +import org.jxmapviewer.painter.CompoundPainter; +import org.jxmapviewer.painter.Painter; +import org.sleuthkit.autopsy.geolocation.datamodel.Waypoint; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; /** @@ -87,6 +90,7 @@ final public class MapPanel extends javax.swing.JPanel { private boolean zoomChanging; private KdTree waypointTree; private Set waypointSet; + private List> tracks = new ArrayList<>(); private Popup currentPopup; private final PopupFactory popupFactory; @@ -207,6 +211,10 @@ final public class MapPanel extends javax.swing.JPanel { mapViewer.setCenterPosition(new GeoPosition(0, 0)); + initializePainter(); + } + + void initializePainter() { // Basic painters for the way points. WaypointPainter waypointPainter = new WaypointPainter() { @Override @@ -219,8 +227,13 @@ final public class MapPanel extends javax.swing.JPanel { } }; waypointPainter.setRenderer(new MapWaypointRenderer()); - - mapViewer.setOverlayPainter(waypointPainter); + + ArrayList> painters = new ArrayList<>(); + painters.add(new MapTrackRenderer(tracks)); + painters.add(waypointPainter); + + CompoundPainter compoundPainter = new CompoundPainter<>(painters); + mapViewer.setOverlayPainter(compoundPainter); } /** @@ -308,6 +321,14 @@ final public class MapPanel extends javax.swing.JPanel { } mapViewer.repaint(); } + + /** + * Stores the given List of tracks from which to draw paths later + * @param tracks + */ + void setTracks(List> tracks) { + this.tracks = tracks; + } /** * Set the current zoom level. @@ -739,7 +760,7 @@ final public class MapPanel extends javax.swing.JPanel { /** * Creates a waypoint image with the specified color * - * @param color the color of the new waypoint image + * @param color the color of the new image * @return the new waypoint image */ private BufferedImage createWaypointImage(Color color) { @@ -749,6 +770,7 @@ final public class MapPanel extends javax.swing.JPanel { BufferedImage ret = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); Graphics2D g = ret.createGraphics(); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g.drawImage(whiteWaypointImage, 0, 0, null); g.setComposite(AlphaComposite.SrcIn); g.setColor(color); @@ -760,31 +782,87 @@ final public class MapPanel extends javax.swing.JPanel { } @Override - public void paintWaypoint(Graphics2D gd, JXMapViewer jxmv, MapWaypoint waypoint) { + public void paintWaypoint(Graphics2D g, JXMapViewer map, MapWaypoint waypoint) { Color color = getColor(waypoint, currentlySelectedWaypoint); BufferedImage image; int artifactType = waypoint.getArtifactTypeID(); - - if (artifactType == ARTIFACT_TYPE.TSK_GPS_TRACKPOINT.getTypeID() || - artifactType == ARTIFACT_TYPE.TSK_GPS_TRACK.getTypeID()) { - image = dotImageCache.computeIfAbsent(color, k -> { - return createTrackDotImage(color); - }); - } else { - // Store computed images in cache for later use - image = waypointImageCache.computeIfAbsent(color, k -> { - return createWaypointImage(color); - }); - } - - Point2D point = jxmv.getTileFactory().geoToPixel(waypoint.getPosition(), jxmv.getZoom()); - + Point2D point = map.getTileFactory().geoToPixel(waypoint.getPosition(), map.getZoom()); int x = (int) point.getX(); int y = (int) point.getY(); - gd = (Graphics2D) gd.create(); - gd.drawImage(image, x - image.getWidth() / 2, y - image.getHeight(), null); - gd.dispose(); + if (artifactType == ARTIFACT_TYPE.TSK_GPS_TRACKPOINT.getTypeID() || + artifactType == ARTIFACT_TYPE.TSK_GPS_TRACK.getTypeID() || + artifactType == ARTIFACT_TYPE.TSK_GPS_ROUTE.getTypeID()) { + image = dotImageCache.computeIfAbsent(color, k -> { + return createTrackDotImage(color); + }); + // Center the dot on the GPS coordinate + y -= image.getHeight() / 2; + } else { + image = waypointImageCache.computeIfAbsent(color, k -> { + return createWaypointImage(color); + }); + // Align the bottom of the pin with the GPS coordinate + y -= image.getHeight(); + } + // Center image horizontally on image + x -= image.getWidth() / 2; + + g = (Graphics2D) g.create(); + g.drawImage(image, x, y, null); + g.dispose(); + } + } + + /** + * Renderer for map track routes + */ + private class MapTrackRenderer implements Painter { + private final List> tracks; + + MapTrackRenderer(List> tracks) { + this.tracks = tracks; + } + + private void drawRoute(Set track, Graphics2D g, JXMapViewer map) { + int lastX = 0; + int lastY = 0; + + boolean first = true; + + for (MapWaypoint wp : track) { + Point2D p = map.getTileFactory().geoToPixel(wp.getPosition(), map.getZoom()); + int thisX = (int) p.getX(); + int thisY = (int) p.getY(); + + if (first) { + first = false; + } else { + g.drawLine(lastX, lastY, thisX, thisY); + } + + lastX = thisX; + lastY = thisY; + } + } + + @Override + public void paint(Graphics2D g, JXMapViewer map, int w, int h) { + g = (Graphics2D) g.create(); + + Rectangle bounds = map.getViewportBounds(); + g.translate(-bounds.x, -bounds.y); + + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + g.setColor(Color.BLACK); + g.setStroke(new BasicStroke(2)); + + for (Set track : tracks) { + drawRoute(track, g, map); + } + + g.dispose(); } } } diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/MapWaypoint.java b/Core/src/org/sleuthkit/autopsy/geolocation/MapWaypoint.java index 1bf9e1be0c..f7673338b1 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/MapWaypoint.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/MapWaypoint.java @@ -84,7 +84,7 @@ final class MapWaypoint extends KdTree.XYZPoint implements org.jxmapviewer.viewe private final GeoPosition position; /** - * Returns a list of of MapWaypoint objects for the given list of + * Returns a list of MapWaypoint objects for the given list of * datamodel.Waypoint objects. * * @param dmWaypoints diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/WaypointBuilder.java b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/WaypointBuilder.java index 1f8f25452e..39bf0f8b2f 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/WaypointBuilder.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/WaypointBuilder.java @@ -98,7 +98,7 @@ public final class WaypointBuilder { * * @param wwaypoints This of waypoints. */ - void process(List wwaypoints); + void process(List waypoints); } /** @@ -479,6 +479,7 @@ public final class WaypointBuilder { @Override public void process(ResultSet rs) { List waypoints = new ArrayList<>(); + //List> tracks = new ArrayList<>(); try { while (rs.next()) { int artifact_type_id = rs.getInt("artifact_type_id"); //NON-NLS @@ -486,7 +487,12 @@ public final class WaypointBuilder { ARTIFACT_TYPE type = ARTIFACT_TYPE.fromID(artifact_type_id); if (artifactTypes.contains(type)) { - waypoints.addAll(getWaypointForArtifact(skCase.getBlackboardArtifact(artifact_id), type)); + List curWaypoints = getWaypointForArtifact(skCase.getBlackboardArtifact(artifact_id), type); + /*if (type == ARTIFACT_TYPE.TSK_GPS_TRACKPOINT || + type == ARTIFACT_TYPE.TSK_GPS_TRACK) { + //tracks.add(curWaypoints); + }*/ + waypoints.addAll(curWaypoints); } } From cb66620dba9972b62b409cb0a6af6aa81e3d808a Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 16 Apr 2020 13:04:38 -0400 Subject: [PATCH 13/20] updates to remove datasource mentions --- .../GPX_Module/GPX_Parser_Module.py | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/InternalPythonModules/GPX_Module/GPX_Parser_Module.py b/InternalPythonModules/GPX_Module/GPX_Parser_Module.py index 35b18847d1..d77b9e6278 100644 --- a/InternalPythonModules/GPX_Module/GPX_Parser_Module.py +++ b/InternalPythonModules/GPX_Module/GPX_Parser_Module.py @@ -44,7 +44,6 @@ from org.sleuthkit.datamodel.blackboardutils.attributes.GeoTrackPoints import Tr from org.sleuthkit.autopsy.datamodel import ContentUtils from org.sleuthkit.autopsy.ingest import IngestModule from org.sleuthkit.autopsy.ingest.IngestModule import IngestModuleException -from org.sleuthkit.autopsy.ingest import DataSourceIngestModule from org.sleuthkit.autopsy.ingest import FileIngestModule from org.sleuthkit.autopsy.ingest import IngestModuleFactoryAdapter from org.sleuthkit.autopsy.ingest import IngestMessage @@ -65,9 +64,7 @@ import uuid # Factory that defines the name and details of the module and allows Autopsy # to create instances of the modules that will do the analysis. - - -class GPXParserDataSourceIngestModuleFactory(IngestModuleFactoryAdapter): +class GPXParserFileIngestModuleFactory(IngestModuleFactoryAdapter): moduleName = "GPX Parser" @@ -84,14 +81,14 @@ class GPXParserDataSourceIngestModuleFactory(IngestModuleFactoryAdapter): return True def createFileIngestModule(self, ingestOptions): - return GPXParserDataSourceIngestModule() + return GPXParserFileIngestModule() -# Data Source-level ingest module. One gets created per data source. -class GPXParserDataSourceIngestModule(FileIngestModule): +# File level ingest module. +class GPXParserFileIngestModule(FileIngestModule): logger = Logger.getLogger( - GPXParserDataSourceIngestModuleFactory.moduleName) + GPXParserFileIngestModuleFactory.moduleName) writeDebugMsgs = False def log(self, level, msg): @@ -103,7 +100,7 @@ class GPXParserDataSourceIngestModule(FileIngestModule): self.fileCount = 0 # Get the module name, it will be needed for adding attributes - self.moduleName = GPXParserDataSourceIngestModuleFactory.moduleName + self.moduleName = GPXParserFileIngestModuleFactory.moduleName # Get the case database and its blackboard. self.skCase = Case.getCurrentCase().getSleuthkitCase() @@ -247,11 +244,9 @@ class GPXParserDataSourceIngestModule(FileIngestModule): self.fileCount += 1 return IngestModule.ProcessResult.OK - # Where any shutdown code is run and resources are freed. def shutDown(self): - # As a final part of this example, we'll send a message to the ingest inbox with the number of files found (in this thread) message = IngestMessage.createMessage( - IngestMessage.MessageType.DATA, GPXParserDataSourceIngestModuleFactory.moduleName, + IngestMessage.MessageType.DATA, GPXParserFileIngestModuleFactory.moduleName, str(self.fileCount) + " files found") ingestServices = IngestServices.getInstance().postMessage(message) From 846881136cc0a9c7e4aaf20534b3ae7f0c2b375b Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 16 Apr 2020 13:05:10 -0400 Subject: [PATCH 14/20] formatting --- InternalPythonModules/GPX_Module/GPX_Parser_Module.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/InternalPythonModules/GPX_Module/GPX_Parser_Module.py b/InternalPythonModules/GPX_Module/GPX_Parser_Module.py index d77b9e6278..0e4face2bc 100644 --- a/InternalPythonModules/GPX_Module/GPX_Parser_Module.py +++ b/InternalPythonModules/GPX_Module/GPX_Parser_Module.py @@ -64,6 +64,8 @@ import uuid # Factory that defines the name and details of the module and allows Autopsy # to create instances of the modules that will do the analysis. + + class GPXParserFileIngestModuleFactory(IngestModuleFactoryAdapter): moduleName = "GPX Parser" @@ -131,7 +133,8 @@ class GPXParserFileIngestModule(FileIngestModule): fileName = os.path.join(self.dirName, uuid.uuid4().hex + ".gpx") # Create a GeoArtifactsHelper for this file. - geoArtifactHelper = GeoArtifactsHelper(self.skCase, self.moduleName, None, file) + geoArtifactHelper = GeoArtifactsHelper( + self.skCase, self.moduleName, None, file) if self.writeDebugMsgs: self.log(Level.INFO, "Processing " + file.getUniquePath() + @@ -244,7 +247,6 @@ class GPXParserFileIngestModule(FileIngestModule): self.fileCount += 1 return IngestModule.ProcessResult.OK - def shutDown(self): message = IngestMessage.createMessage( IngestMessage.MessageType.DATA, GPXParserFileIngestModuleFactory.moduleName, From 7def5251e09a8bad3bd31276fbed8e5cb400f6eb Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 16 Apr 2020 14:20:31 -0400 Subject: [PATCH 15/20] fixes based on review --- Core/src/org/sleuthkit/autopsy/casemodule/Case.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 917ddf6210..dbfabb7a3e 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -61,7 +61,6 @@ import javax.annotation.concurrent.GuardedBy; import javax.annotation.concurrent.ThreadSafe; import javax.swing.JOptionPane; import javax.swing.SwingUtilities; -import org.openide.util.Exceptions; import org.openide.util.Lookup; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; @@ -160,7 +159,7 @@ public class Case { private static final AutopsyEventPublisher eventPublisher = new AutopsyEventPublisher(); private static final Object caseActionSerializationLock = new Object(); private static Future backgroundOpenFileSystemsFuture = null; - private static final ExecutorService startIngestJobsExecutor + private static final ExecutorService backgroundOpenFileSystemsExecutor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("Case-open-file-systems-%d").build()); private static volatile Frame mainFrame; private static volatile Case currentCase; @@ -2007,8 +2006,8 @@ public class Case { /** * Starts a background task that reads a sector from each file system of - * each image of a case to do an eager open of the filesystems in case. If - * this method is called before another background file system read has + * each image of a case to do an eager open of the filesystems in the case. + * If this method is called before another background file system read has * finished the earlier one will be cancelled. * * @throws CaseActionCancelledException Exception thrown if task is @@ -2018,13 +2017,13 @@ public class Case { "# {0} - case", "Case.openFileSystems.retrievingImages=Retrieving images for case: {0}...", "# {0} - image", "Case.openFileSystems.openingImage=Opening all filesystems for image: {0}..." }) - private void openFileSystemsInBackground() throws CaseActionCancelledException { + private void openFileSystemsInBackground() { if (backgroundOpenFileSystemsFuture != null && !backgroundOpenFileSystemsFuture.isDone()) { backgroundOpenFileSystemsFuture.cancel(true); } BackgroundOpenFileSystemsTask backgroundTask = new BackgroundOpenFileSystemsTask(this.caseDb, new LoggingProgressIndicator()); - backgroundOpenFileSystemsFuture = startIngestJobsExecutor.submit(backgroundTask); + backgroundOpenFileSystemsFuture = backgroundOpenFileSystemsExecutor.submit(backgroundTask); } /** From 5fb341f51451bd85cc331662113306e6ee5e27cf Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 16 Apr 2020 14:49:55 -0400 Subject: [PATCH 16/20] rename of executor service --- Core/src/org/sleuthkit/autopsy/casemodule/Case.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index dbfabb7a3e..5a703a4ea9 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -159,8 +159,8 @@ public class Case { private static final AutopsyEventPublisher eventPublisher = new AutopsyEventPublisher(); private static final Object caseActionSerializationLock = new Object(); private static Future backgroundOpenFileSystemsFuture = null; - private static final ExecutorService backgroundOpenFileSystemsExecutor - = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("Case-open-file-systems-%d").build()); + private static final ExecutorService openFileSystemsExecutor + = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("case-open-file-systems-%d").build()); private static volatile Frame mainFrame; private static volatile Case currentCase; private final CaseMetadata metadata; @@ -2023,7 +2023,7 @@ public class Case { } BackgroundOpenFileSystemsTask backgroundTask = new BackgroundOpenFileSystemsTask(this.caseDb, new LoggingProgressIndicator()); - backgroundOpenFileSystemsFuture = backgroundOpenFileSystemsExecutor.submit(backgroundTask); + backgroundOpenFileSystemsFuture = openFileSystemsExecutor.submit(backgroundTask); } /** From 4aeb5b9d6a2eea17b5675a74b418dac942834733 Mon Sep 17 00:00:00 2001 From: apriestman Date: Thu, 16 Apr 2020 16:31:47 -0400 Subject: [PATCH 17/20] Central repo should only subscribe to events it processes --- .../eventlisteners/CaseEventListener.java | 28 +++++++++++++++++++ .../eventlisteners/Installer.java | 4 +-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/CaseEventListener.java b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/CaseEventListener.java index 8f648a9945..d8bd1c43c3 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/CaseEventListener.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/CaseEventListener.java @@ -21,7 +21,9 @@ package org.sleuthkit.autopsy.centralrepository.eventlisteners; import com.google.common.util.concurrent.ThreadFactoryBuilder; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.util.EnumSet; import java.util.List; +import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.logging.Level; @@ -64,6 +66,15 @@ final class CaseEventListener implements PropertyChangeListener { private static final Logger LOGGER = Logger.getLogger(CaseEventListener.class.getName()); private final ExecutorService jobProcessingExecutor; private static final String CASE_EVENT_THREAD_NAME = "Case-Event-Listener-%d"; + + private static final Set CASE_EVENTS_OF_INTEREST = EnumSet.of( + Case.Events.CONTENT_TAG_ADDED, Case.Events.CONTENT_TAG_DELETED, + Case.Events.BLACKBOARD_ARTIFACT_TAG_DELETED, Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED, + Case.Events.CONTENT_TAG_ADDED, Case.Events.CONTENT_TAG_DELETED, + Case.Events.DATA_SOURCE_ADDED, + Case.Events.TAG_DEFINITION_CHANGED, + Case.Events.CURRENT_CASE, + Case.Events.DATA_SOURCE_NAME_CHANGED); CaseEventListener() { jobProcessingExecutor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat(CASE_EVENT_THREAD_NAME).build()); @@ -82,6 +93,9 @@ final class CaseEventListener implements PropertyChangeListener { LOGGER.log(Level.SEVERE, "Failed to get instance of db manager.", ex); return; } + + // If any changes are made to which event types are handled the change + // must also be made to CASE_EVENTS_OF_INTEREST. switch (Case.Events.valueOf(evt.getPropertyName())) { case CONTENT_TAG_ADDED: case CONTENT_TAG_DELETED: { @@ -113,6 +127,20 @@ final class CaseEventListener implements PropertyChangeListener { break; } } + + /* + * Add all of our Case Event Listeners to the case. + */ + void installListeners() { + Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, this); + } + + /* + * Remove all of our Case Event Listeners from the case. + */ + void uninstallListeners() { + Case.removeEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, this); + } private final class ContentTagTask implements Runnable { diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/Installer.java b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/Installer.java index da58e8936b..d4f0253cd2 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/Installer.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/Installer.java @@ -101,7 +101,7 @@ public class Installer extends ModuleInstall { * central repository. */ private void addApplicationEventListeners() { - Case.addPropertyChangeListener(caseEventListener); + caseEventListener.installListeners(); ingestEventListener.installListeners(); } @@ -226,7 +226,7 @@ public class Installer extends ModuleInstall { * * THIS CODE IS NEVER EXECUTED. */ - Case.removePropertyChangeListener(caseEventListener); + caseEventListener.uninstallListeners(); caseEventListener.shutdown(); ingestEventListener.shutdown(); ingestEventListener.uninstallListeners(); From 9e14a5264590db5d3954999cc93b72b52db307f1 Mon Sep 17 00:00:00 2001 From: Ethan Roseman Date: Thu, 16 Apr 2020 17:31:13 -0400 Subject: [PATCH 18/20] 6160 Full track highlighting --- .../autopsy/geolocation/MapPanel.java | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java b/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java index 3fe66cbd66..a573483b86 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java @@ -103,6 +103,7 @@ final public class MapPanel extends javax.swing.JPanel { private BufferedImage transparentWaypointImage; private MapWaypoint currentlySelectedWaypoint; + private Set currentlySelectedTrack; /** * Creates new form MapPanel @@ -348,6 +349,7 @@ final public class MapPanel extends javax.swing.JPanel { void clearWaypoints() { waypointTree = null; currentlySelectedWaypoint = null; + currentlySelectedTrack = null; if (currentPopup != null) { currentPopup.hide(); } @@ -685,9 +687,17 @@ final public class MapPanel extends javax.swing.JPanel { if (!evt.isPopupTrigger() && SwingUtilities.isLeftMouseButton(evt)) { List waypoints = findClosestWaypoint(evt.getPoint()); if (waypoints.size() > 0) { - currentlySelectedWaypoint = waypoints.get(0); + MapWaypoint selection = waypoints.get(0); + currentlySelectedWaypoint = selection; + for (Set track : tracks) { + if (track.contains(selection)) { + currentlySelectedTrack = track; + break; + } + } } else { currentlySelectedWaypoint = null; + currentlySelectedTrack = null; } showDetailsPopup(); } @@ -721,13 +731,13 @@ final public class MapPanel extends javax.swing.JPanel { /** * * @param waypoint the waypoint for which to get the color - * @param currentlySelectedWaypoint the waypoint that is currently * selected * @return the color that this waypoint should be rendered */ - private Color getColor(MapWaypoint waypoint, MapWaypoint currentlySelectedWaypoint) { + private Color getColor(MapWaypoint waypoint) { Color baseColor = waypoint.getColor(); - if (waypoint.equals(currentlySelectedWaypoint)) { + if (waypoint.equals(currentlySelectedWaypoint) || + (currentlySelectedTrack != null && currentlySelectedTrack.contains(waypoint))) { // Highlight this waypoint since it is selected return Color.YELLOW; } else { @@ -783,7 +793,7 @@ final public class MapPanel extends javax.swing.JPanel { @Override public void paintWaypoint(Graphics2D g, JXMapViewer map, MapWaypoint waypoint) { - Color color = getColor(waypoint, currentlySelectedWaypoint); + Color color = getColor(waypoint); BufferedImage image; int artifactType = waypoint.getArtifactTypeID(); Point2D point = map.getTileFactory().geoToPixel(waypoint.getPosition(), map.getZoom()); From 94deee76d9ff8ab2a3fd2aae7b894afcc41cdcd6 Mon Sep 17 00:00:00 2001 From: Ethan Roseman Date: Thu, 16 Apr 2020 17:33:21 -0400 Subject: [PATCH 19/20] 6160 Restore WaypointBuilder --- .../autopsy/geolocation/datamodel/WaypointBuilder.java | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/WaypointBuilder.java b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/WaypointBuilder.java index 39bf0f8b2f..11ff0107dd 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/WaypointBuilder.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/WaypointBuilder.java @@ -479,7 +479,6 @@ public final class WaypointBuilder { @Override public void process(ResultSet rs) { List waypoints = new ArrayList<>(); - //List> tracks = new ArrayList<>(); try { while (rs.next()) { int artifact_type_id = rs.getInt("artifact_type_id"); //NON-NLS @@ -487,12 +486,7 @@ public final class WaypointBuilder { ARTIFACT_TYPE type = ARTIFACT_TYPE.fromID(artifact_type_id); if (artifactTypes.contains(type)) { - List curWaypoints = getWaypointForArtifact(skCase.getBlackboardArtifact(artifact_id), type); - /*if (type == ARTIFACT_TYPE.TSK_GPS_TRACKPOINT || - type == ARTIFACT_TYPE.TSK_GPS_TRACK) { - //tracks.add(curWaypoints); - }*/ - waypoints.addAll(curWaypoints); + waypoints.addAll(getWaypointForArtifact(skCase.getBlackboardArtifact(artifact_id), type)); } } From 6c10cd89e372fe6975fc01a24ab1916c575547cd Mon Sep 17 00:00:00 2001 From: Ethan Roseman Date: Fri, 17 Apr 2020 12:11:26 -0400 Subject: [PATCH 20/20] 6160 codacy --- .../geolocation/GeolocationTopComponent.java | 1 - .../autopsy/geolocation/MapPanel.java | 72 +++++++++---------- 2 files changed, 36 insertions(+), 37 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.java b/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.java index 7973218de9..ae387c7847 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.java @@ -50,7 +50,6 @@ import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.geolocation.GeoFilterPanel.GeoFilter; import org.sleuthkit.autopsy.geolocation.datamodel.GeoLocationDataException; -import org.sleuthkit.autopsy.geolocation.datamodel.Track; import org.sleuthkit.autopsy.ingest.IngestManager; import static org.sleuthkit.autopsy.ingest.IngestManager.IngestModuleEvent.DATA_ADDED; import org.sleuthkit.autopsy.ingest.ModuleDataEvent; diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java b/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java index a573483b86..44de21ff81 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java @@ -75,7 +75,6 @@ import javax.imageio.ImageIO; import javax.swing.SwingUtilities; import org.jxmapviewer.painter.CompoundPainter; import org.jxmapviewer.painter.Painter; -import org.sleuthkit.autopsy.geolocation.datamodel.Waypoint; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; /** @@ -214,7 +213,7 @@ final public class MapPanel extends javax.swing.JPanel { initializePainter(); } - + void initializePainter() { // Basic painters for the way points. WaypointPainter waypointPainter = new WaypointPainter() { @@ -228,11 +227,11 @@ final public class MapPanel extends javax.swing.JPanel { } }; waypointPainter.setRenderer(new MapWaypointRenderer()); - + ArrayList> painters = new ArrayList<>(); painters.add(new MapTrackRenderer(tracks)); painters.add(waypointPainter); - + CompoundPainter compoundPainter = new CompoundPainter<>(painters); mapViewer.setOverlayPainter(compoundPainter); } @@ -322,10 +321,11 @@ final public class MapPanel extends javax.swing.JPanel { } mapViewer.repaint(); } - + /** * Stores the given List of tracks from which to draw paths later - * @param tracks + * + * @param tracks */ void setTracks(List> tracks) { this.tracks = tracks; @@ -730,14 +730,13 @@ final public class MapPanel extends javax.swing.JPanel { /** * - * @param waypoint the waypoint for which to get the color - * selected + * @param waypoint the waypoint for which to get the color selected * @return the color that this waypoint should be rendered */ private Color getColor(MapWaypoint waypoint) { Color baseColor = waypoint.getColor(); - if (waypoint.equals(currentlySelectedWaypoint) || - (currentlySelectedTrack != null && currentlySelectedTrack.contains(waypoint))) { + if (waypoint.equals(currentlySelectedWaypoint) + || (currentlySelectedTrack != null && currentlySelectedTrack.contains(waypoint))) { // Highlight this waypoint since it is selected return Color.YELLOW; } else { @@ -800,9 +799,9 @@ final public class MapPanel extends javax.swing.JPanel { int x = (int) point.getX(); int y = (int) point.getY(); - if (artifactType == ARTIFACT_TYPE.TSK_GPS_TRACKPOINT.getTypeID() || - artifactType == ARTIFACT_TYPE.TSK_GPS_TRACK.getTypeID() || - artifactType == ARTIFACT_TYPE.TSK_GPS_ROUTE.getTypeID()) { + if (artifactType == ARTIFACT_TYPE.TSK_GPS_TRACKPOINT.getTypeID() + || artifactType == ARTIFACT_TYPE.TSK_GPS_TRACK.getTypeID() + || artifactType == ARTIFACT_TYPE.TSK_GPS_ROUTE.getTypeID()) { image = dotImageCache.computeIfAbsent(color, k -> { return createTrackDotImage(color); }); @@ -818,61 +817,62 @@ final public class MapPanel extends javax.swing.JPanel { // Center image horizontally on image x -= image.getWidth() / 2; - g = (Graphics2D) g.create(); - g.drawImage(image, x, y, null); - g.dispose(); + Graphics2D g2d = (Graphics2D) g.create(); + g2d.drawImage(image, x, y, null); + g2d.dispose(); } } - + /** * Renderer for map track routes */ private class MapTrackRenderer implements Painter { + private final List> tracks; - + MapTrackRenderer(List> tracks) { this.tracks = tracks; } - + private void drawRoute(Set track, Graphics2D g, JXMapViewer map) { int lastX = 0; int lastY = 0; - + boolean first = true; - + for (MapWaypoint wp : track) { Point2D p = map.getTileFactory().geoToPixel(wp.getPosition(), map.getZoom()); int thisX = (int) p.getX(); int thisY = (int) p.getY(); - + if (first) { first = false; } else { g.drawLine(lastX, lastY, thisX, thisY); } - + lastX = thisX; lastY = thisY; } } - + @Override public void paint(Graphics2D g, JXMapViewer map, int w, int h) { - g = (Graphics2D) g.create(); - + Graphics2D g2d = (Graphics2D) g.create(); + Rectangle bounds = map.getViewportBounds(); - g.translate(-bounds.x, -bounds.y); - - g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - - g.setColor(Color.BLACK); - g.setStroke(new BasicStroke(2)); - + g2d.translate(-bounds.x, -bounds.y); + + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + g2d.setColor(Color.BLACK); + g2d.setStroke(new BasicStroke(2)); + for (Set track : tracks) { - drawRoute(track, g, map); + drawRoute(track, g2d, map); } - - g.dispose(); + + g2d.dispose(); } } }