From c51a58698408c277f35dbfa08440d913d127115b Mon Sep 17 00:00:00 2001 From: Ethan Roseman Date: Wed, 25 Mar 2020 17:05:25 -0400 Subject: [PATCH] New panel for type filtering in the geolocation tool --- .../geolocation/AbstractWaypointFetcher.java | 13 +- .../autopsy/geolocation/Bundle.properties | 2 +- .../geolocation/Bundle.properties-MERGED | 4 +- .../autopsy/geolocation/GeoFilterPanel.java | 135 +++++++++++++----- .../geolocation/GeolocationTopComponent.java | 3 +- .../datamodel/WaypointBuilder.java | 22 +-- 6 files changed, 126 insertions(+), 53 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/AbstractWaypointFetcher.java b/Core/src/org/sleuthkit/autopsy/geolocation/AbstractWaypointFetcher.java index a662b410e6..b8e00bcb73 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/AbstractWaypointFetcher.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/AbstractWaypointFetcher.java @@ -28,6 +28,7 @@ import org.sleuthkit.autopsy.geolocation.datamodel.GeoLocationDataException; import org.sleuthkit.autopsy.geolocation.datamodel.Track; import org.sleuthkit.autopsy.geolocation.datamodel.Waypoint; import org.sleuthkit.autopsy.geolocation.datamodel.WaypointBuilder; +import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; /** * The business logic for filtering waypoints. @@ -60,6 +61,7 @@ abstract class AbstractWaypointFetcher implements WaypointBuilder.WaypointFilter Case currentCase = Case.getCurrentCase(); WaypointBuilder.getAllWaypoints(currentCase.getSleuthkitCase(), filters.getDataSources(), + filters.getArtifactTypes(), filters.showAllWaypoints(), filters.getMostRecentNumDays(), filters.showWaypointsWithoutTimeStamp(), @@ -77,12 +79,13 @@ abstract class AbstractWaypointFetcher implements WaypointBuilder.WaypointFilter @Override public void process(List waypoints) { - List tracks = null; - try { - tracks = Track.getTracks(Case.getCurrentCase().getSleuthkitCase(), filters.getDataSources()); - } catch (GeoLocationDataException ex) { - logger.log(Level.WARNING, "Exception thrown while retrieving list of Tracks", ex); + if (filters.getArtifactTypes().contains(ARTIFACT_TYPE.TSK_GPS_TRACK)) { + try { + tracks = Track.getTracks(Case.getCurrentCase().getSleuthkitCase(), filters.getDataSources()); + } catch (GeoLocationDataException ex) { + logger.log(Level.WARNING, "Exception thrown while retrieving list of Tracks", ex); + } } List completeList = createWaypointList(waypoints, tracks); diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/Bundle.properties b/Core/src/org/sleuthkit/autopsy/geolocation/Bundle.properties index 338a91cdb1..df9448a6cb 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/geolocation/Bundle.properties @@ -15,7 +15,7 @@ GeoFilterPanel.daysLabel.text=days of activity CheckBoxListPanel.titleLabel.text=jLabel1 CheckBoxListPanel.checkButton.text=Check All CheckBoxListPanel.uncheckButton.text=Uncheck All -GeoFilterPanel.optionsLabel.text=Waypoints +GeoFilterPanel.optionsLabel.text=Dates OptionsCategory_Name_Geolocation=Geolocation OptionsCategory_Keywords_Geolocation=Geolocation Settings GeolocationSettingsPanel.tilePane.border.title=Map Tile Data Source diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/geolocation/Bundle.properties-MERGED index e5bf351edb..8d5ea262d2 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/geolocation/Bundle.properties-MERGED @@ -1,7 +1,9 @@ CTL_OpenGeolocation=Geolocation CTL_GeolocationTopComponentAction=GeolocationTopComponent CTL_GeolocationTopComponent=Geolocation +GeoFilterPanel_ArtifactType_List_Title=Types GeoFilterPanel_DataSource_List_Title=Data Sources +GeoFilterPanel_empty_artifactType=Type list is empty. GeoFilterPanel_empty_dataSource=Data Source list is empty. GeolocationSettings_mbtile_does_not_exist_message=The file supplied does not exist.\nPlease verify that the file exists and try again. GeolocationSettings_mbtile_does_not_exist_title=File Not Found @@ -52,7 +54,7 @@ GeoFilterPanel.daysLabel.text=days of activity CheckBoxListPanel.titleLabel.text=jLabel1 CheckBoxListPanel.checkButton.text=Check All CheckBoxListPanel.uncheckButton.text=Uncheck All -GeoFilterPanel.optionsLabel.text=Waypoints +GeoFilterPanel.optionsLabel.text=Dates OptionsCategory_Name_Geolocation=Geolocation OptionsCategory_Keywords_Geolocation=Geolocation Settings GeolocationSettingsPanel.tilePane.border.title=Map Tile Data Source diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/GeoFilterPanel.java b/Core/src/org/sleuthkit/autopsy/geolocation/GeoFilterPanel.java index cca9382e51..14094d323e 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/GeoFilterPanel.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/GeoFilterPanel.java @@ -22,7 +22,9 @@ import java.awt.GridBagConstraints; import java.awt.event.ActionListener; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.logging.Level; import javafx.util.Pair; @@ -33,6 +35,7 @@ import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; @@ -49,24 +52,26 @@ class GeoFilterPanel extends javax.swing.JPanel { private static final Logger logger = Logger.getLogger(GeoFilterPanel.class.getName()); private final SpinnerNumberModel numberModel; - private final CheckBoxListPanel checkboxPanel; + private final CheckBoxListPanel dsCheckboxPanel; + private final CheckBoxListPanel atCheckboxPanel; // Make sure to update if @SuppressWarnings("deprecation") - private static final BlackboardArtifact.ARTIFACT_TYPE[] GPS_ARTIFACT_TYPES = { - BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_BOOKMARK, - BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_LAST_KNOWN_LOCATION, - BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_ROUTE, - BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_SEARCH, - BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACK, - BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACKPOINT + private static final ARTIFACT_TYPE[] GPS_ARTIFACT_TYPES = { + ARTIFACT_TYPE.TSK_GPS_BOOKMARK, + ARTIFACT_TYPE.TSK_GPS_LAST_KNOWN_LOCATION, + ARTIFACT_TYPE.TSK_GPS_ROUTE, + ARTIFACT_TYPE.TSK_GPS_SEARCH, + ARTIFACT_TYPE.TSK_GPS_TRACK, + ARTIFACT_TYPE.TSK_GPS_TRACKPOINT }; /** * Creates new GeoFilterPanel */ @Messages({ - "GeoFilterPanel_DataSource_List_Title=Data Sources" + "GeoFilterPanel_DataSource_List_Title=Data Sources", + "GeoFilterPanel_ArtifactType_List_Title=Types" }) GeoFilterPanel() { // numberModel is used in initComponents @@ -76,10 +81,15 @@ class GeoFilterPanel extends javax.swing.JPanel { // The gui builder cannot handle using CheckBoxListPanel due to its // use of generics so we will initalize it here. - checkboxPanel = new CheckBoxListPanel<>(); - checkboxPanel.setPanelTitle(Bundle.GeoFilterPanel_DataSource_List_Title()); - checkboxPanel.setPanelTitleIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/image.png"))); - checkboxPanel.setSetAllSelected(true); + dsCheckboxPanel = new CheckBoxListPanel<>(); + dsCheckboxPanel.setPanelTitle(Bundle.GeoFilterPanel_DataSource_List_Title()); + dsCheckboxPanel.setPanelTitleIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/image.png"))); + dsCheckboxPanel.setSetAllSelected(true); + + atCheckboxPanel = new CheckBoxListPanel<>(); + atCheckboxPanel.setPanelTitle(Bundle.GeoFilterPanel_ArtifactType_List_Title()); + atCheckboxPanel.setPanelTitleIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/extracted_content.png"))); + atCheckboxPanel.setSetAllSelected(true); GridBagConstraints gridBagConstraints = new GridBagConstraints(); gridBagConstraints.gridx = 0; @@ -89,7 +99,17 @@ class GeoFilterPanel extends javax.swing.JPanel { gridBagConstraints.weightx = 1.0; gridBagConstraints.weighty = 1.0; gridBagConstraints.insets = new java.awt.Insets(0, 15, 0, 15); - add(checkboxPanel, gridBagConstraints); + add(dsCheckboxPanel, gridBagConstraints); + + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 4; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.weighty = 1.0; + gridBagConstraints.insets = new java.awt.Insets(0, 15, 0, 15); + add(atCheckboxPanel, gridBagConstraints); } @Override @@ -98,7 +118,8 @@ class GeoFilterPanel extends javax.swing.JPanel { mostRecentButton.setEnabled(enabled); allButton.setEnabled(enabled); showWaypointsWOTSCheckBox.setEnabled(enabled && mostRecentButton.isSelected()); - checkboxPanel.setEnabled(enabled); + dsCheckboxPanel.setEnabled(enabled); + atCheckboxPanel.setEnabled(enabled); daysLabel.setEnabled(enabled); daysSpinner.setEnabled(enabled); } @@ -115,11 +136,12 @@ class GeoFilterPanel extends javax.swing.JPanel { * Clears the data source list. */ void clearDataSourceList() { - checkboxPanel.clearList(); + dsCheckboxPanel.clearList(); + atCheckboxPanel.clearList(); } boolean hasDataSources() { - return !checkboxPanel.isEmpty(); + return !dsCheckboxPanel.isEmpty(); } /** @@ -139,18 +161,24 @@ class GeoFilterPanel extends javax.swing.JPanel { * @throws GeoLocationUIException */ @Messages({ - "GeoFilterPanel_empty_dataSource=Data Source list is empty." + "GeoFilterPanel_empty_dataSource=Data Source list is empty.", + "GeoFilterPanel_empty_artifactType=Type list is empty." }) GeoFilter getFilterState() throws GeoLocationUIException { - List dataSources = checkboxPanel.getSelectedElements(); + List dataSources = dsCheckboxPanel.getSelectedElements(); + List artifactTypes = atCheckboxPanel.getSelectedElements(); if (dataSources.isEmpty()) { throw new GeoLocationUIException(Bundle.GeoFilterPanel_empty_dataSource()); } + if (artifactTypes.isEmpty()) { + throw new GeoLocationUIException(Bundle.GeoFilterPanel_empty_artifactType()); + } return new GeoFilter(allButton.isSelected(), showWaypointsWOTSCheckBox.isSelected(), numberModel.getNumber().intValue(), - dataSources); + dataSources, + artifactTypes); } /** @@ -312,6 +340,7 @@ class GeoFilterPanel extends javax.swing.JPanel { private final boolean showWithoutTimeStamp; private final int mostRecentNumDays; private final List dataSources; + private final List artifactTypes; /** * Construct a Geolocation filter. showAll and mostRecentNumDays are @@ -332,12 +361,17 @@ class GeoFilterPanel extends javax.swing.JPanel { * showAll is true. * @param dataSources A list of dataSources to filter waypoint * for. + * @param artifactTypes A list of artifactTypes to filter waypoint + * for. */ - GeoFilter(boolean showAll, boolean withoutTimeStamp, int mostRecentNumDays, List dataSources) { + GeoFilter(boolean showAll, boolean withoutTimeStamp, + int mostRecentNumDays, List dataSources, + List artifactTypes) { this.showAll = showAll; this.showWithoutTimeStamp = withoutTimeStamp; this.mostRecentNumDays = mostRecentNumDays; this.dataSources = dataSources; + this.artifactTypes = artifactTypes; } /** @@ -380,6 +414,28 @@ class GeoFilterPanel extends javax.swing.JPanel { List getDataSources() { return Collections.unmodifiableList(dataSources); } + + /** + * Returns a list of artifact types to filter the waypoints by, or null + * if all types should be include. + * + * @return A list of artifactTypes or null if all artifactTypes should + * be included. + */ + List getArtifactTypes() { + return Collections.unmodifiableList(artifactTypes); + } + } + + final private class Sources { + final List> dataSources; + final HashMap artifactTypes; + + private Sources(List> dataSources, + HashMap artifactTypes) { + this.dataSources = dataSources; + this.artifactTypes = artifactTypes; + } } /** @@ -388,21 +444,27 @@ class GeoFilterPanel extends javax.swing.JPanel { * doInBackground creates a list of Pair objects that contain the * display name of the data source and the data source object. */ - final private class DataSourceUpdater extends SwingWorker>, Void> { + final private class DataSourceUpdater extends SwingWorker { @Override - protected List> doInBackground() throws Exception { + protected Sources doInBackground() throws Exception { SleuthkitCase sleuthkitCase = Case.getCurrentCase().getSleuthkitCase(); List> validSources = new ArrayList<>(); + HashMap atCountsTotal = new HashMap<>(); + for (DataSource dataSource : sleuthkitCase.getDataSources()) { - if (isGPSDataSource(sleuthkitCase, dataSource)) { + HashMap atCounts = getGPSDataSources(sleuthkitCase, dataSource); + if (!atCounts.isEmpty()) { + for (Map.Entry entry : atCounts.entrySet()) { + atCountsTotal.putIfAbsent(entry.getKey(), 0L); + atCountsTotal.put(entry.getKey(), atCountsTotal.get(entry.getKey()) + entry.getValue()); + } String dsName = sleuthkitCase.getContentById(dataSource.getId()).getName(); Pair pair = new Pair<>(dsName, dataSource); validSources.add(pair); } } - - return validSources; + return new Sources(validSources, atCountsTotal); } /** @@ -415,19 +477,20 @@ class GeoFilterPanel extends javax.swing.JPanel { * * @throws TskCoreException */ - private boolean isGPSDataSource(SleuthkitCase sleuthkitCase, DataSource dataSource) throws TskCoreException { + private HashMap getGPSDataSources(SleuthkitCase sleuthkitCase, DataSource dataSource) throws TskCoreException { + HashMap ret = new HashMap<>(); for (BlackboardArtifact.ARTIFACT_TYPE type : GPS_ARTIFACT_TYPES) { - if (sleuthkitCase.getBlackboardArtifactsTypeCount(type.getTypeID(), dataSource.getId()) > 0) { - return true; + long count = sleuthkitCase.getBlackboardArtifactsTypeCount(type.getTypeID(), dataSource.getId()); + if (count > 0) { + ret.put(type, count); } } - - return false; + return ret; } @Override public void done() { - List> sources = null; + Sources sources = null; try { sources = get(); } catch (InterruptedException | ExecutionException ex) { @@ -440,8 +503,12 @@ class GeoFilterPanel extends javax.swing.JPanel { } if (sources != null) { - for (Pair source : sources) { - checkboxPanel.addElement(source.getKey(), source.getValue()); + for (Pair source : sources.dataSources) { + dsCheckboxPanel.addElement(source.getKey(), source.getValue()); + } + for (Map.Entry entry : sources.artifactTypes.entrySet()) { + String dispName = entry.getKey().getDisplayName() + " (" + entry.getValue() + ")"; + atCheckboxPanel.addElement(dispName, entry.getKey()); } } diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.java b/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.java index 3406e188ef..da8e14a65b 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.java @@ -166,7 +166,6 @@ public final class GeolocationTopComponent extends TopComponent { Bundle.GLTopComponent_No_dataSource_message(), Bundle.GLTopComponent_No_dataSource_Title(), JOptionPane.ERROR_MESSAGE); - } } @@ -290,7 +289,7 @@ public final class GeolocationTopComponent extends TopComponent { filters = geoFilterPanel.getFilterState(); } catch (GeoLocationUIException ex) { JOptionPane.showMessageDialog(this, - Bundle.GeoTopComponent_filer_data_invalid_msg(), + ex.getMessage(), Bundle.GeoTopComponent_filer_data_invalid_Title(), JOptionPane.INFORMATION_MESSAGE); return; diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/WaypointBuilder.java b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/WaypointBuilder.java index 15fd1f764e..ecc1bd9bcf 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/WaypointBuilder.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/WaypointBuilder.java @@ -26,6 +26,7 @@ import java.util.List; import java.util.logging.Level; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.CaseDbAccessManager; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.SleuthkitCase; @@ -189,7 +190,7 @@ public final class WaypointBuilder { public static List getTrackpointWaypoints(SleuthkitCase skCase) throws GeoLocationDataException { List artifacts = null; try { - artifacts = skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACKPOINT); + artifacts = skCase.getBlackboardArtifacts(ARTIFACT_TYPE.TSK_GPS_TRACKPOINT); } catch (TskCoreException ex) { throw new GeoLocationDataException("Unable to get artifacts for type: TSK_GPS_TRACKPOINT", ex);//NON-NLS } @@ -237,7 +238,7 @@ public final class WaypointBuilder { static public List getEXIFWaypoints(SleuthkitCase skCase) throws GeoLocationDataException { List artifacts = null; try { - artifacts = skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF); + artifacts = skCase.getBlackboardArtifacts(ARTIFACT_TYPE.TSK_METADATA_EXIF); } catch (TskCoreException ex) { throw new GeoLocationDataException("Unable to get artifacts for type: TSK_GPS_LAST_KNOWN_LOCATION", ex);//NON-NLS } @@ -289,7 +290,7 @@ public final class WaypointBuilder { public static List getSearchWaypoints(SleuthkitCase skCase) throws GeoLocationDataException { List artifacts = null; try { - artifacts = skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_SEARCH); + artifacts = skCase.getBlackboardArtifacts(ARTIFACT_TYPE.TSK_GPS_SEARCH); } catch (TskCoreException ex) { throw new GeoLocationDataException("Unable to get artifacts for type: TSK_GPS_SEARCH", ex);//NON-NLS } @@ -339,7 +340,7 @@ public final class WaypointBuilder { public static List getLastKnownWaypoints(SleuthkitCase skCase) throws GeoLocationDataException { List artifacts = null; try { - artifacts = skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_LAST_KNOWN_LOCATION); + artifacts = skCase.getBlackboardArtifacts(ARTIFACT_TYPE.TSK_GPS_LAST_KNOWN_LOCATION); } catch (TskCoreException ex) { throw new GeoLocationDataException("Unable to get artifacts for type: TSK_GPS_LAST_KNOWN_LOCATION", ex);//NON-NLS } @@ -390,7 +391,7 @@ public final class WaypointBuilder { public static List getBookmarkWaypoints(SleuthkitCase skCase) throws GeoLocationDataException { List artifacts = null; try { - artifacts = skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_BOOKMARK); + artifacts = skCase.getBlackboardArtifacts(ARTIFACT_TYPE.TSK_GPS_BOOKMARK); } catch (TskCoreException ex) { throw new GeoLocationDataException("Unable to get artifacts for type: TSK_GPS_BOOKMARK", ex);//NON-NLS } @@ -459,7 +460,7 @@ public final class WaypointBuilder { * * @throws GeoLocationDataException */ - static public void getAllWaypoints(SleuthkitCase skCase, List dataSources, boolean showAll, int cntDaysFromRecent, boolean noTimeStamp, WaypointFilterQueryCallBack queryCallBack) throws GeoLocationDataException { + static public void getAllWaypoints(SleuthkitCase skCase, List dataSources, List artifactTypes, boolean showAll, int cntDaysFromRecent, boolean noTimeStamp, WaypointFilterQueryCallBack queryCallBack) throws GeoLocationDataException { String query = buildQuery(dataSources, showAll, cntDaysFromRecent, noTimeStamp); logger.log(Level.INFO, query); @@ -480,9 +481,10 @@ public final class WaypointBuilder { int artifact_type_id = rs.getInt("artifact_type_id"); //NON-NLS long artifact_id = rs.getLong("artifact_id"); //NON-NLS - BlackboardArtifact.ARTIFACT_TYPE type = BlackboardArtifact.ARTIFACT_TYPE.fromID(artifact_type_id); - - waypoints.addAll(getWaypointForArtifact(skCase.getBlackboardArtifact(artifact_id), type)); + ARTIFACT_TYPE type = ARTIFACT_TYPE.fromID(artifact_type_id); + if (artifactTypes.contains(type)) { + waypoints.addAll(getWaypointForArtifact(skCase.getBlackboardArtifact(artifact_id), type)); + } } queryCallBack.process(waypoints); @@ -624,7 +626,7 @@ public final class WaypointBuilder { * * @throws GeoLocationDataException */ - static private List getWaypointForArtifact(BlackboardArtifact artifact, BlackboardArtifact.ARTIFACT_TYPE type) throws GeoLocationDataException { + static private List getWaypointForArtifact(BlackboardArtifact artifact, ARTIFACT_TYPE type) throws GeoLocationDataException { List waypoints = new ArrayList<>(); switch (type) { case TSK_METADATA_EXIF: