Autopsy  4.20.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
GeolocationTopComponent.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2019-2020 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.geolocation;
20 
21 import java.awt.BorderLayout;
22 import java.awt.Component;
23 import java.awt.event.ActionEvent;
24 import java.awt.event.ActionListener;
25 import java.beans.PropertyChangeEvent;
26 import java.beans.PropertyChangeListener;
27 import java.io.File;
28 import java.io.IOException;
29 import java.text.DateFormat;
30 import java.text.SimpleDateFormat;
31 import java.util.Date;
32 import java.util.EnumSet;
33 import java.util.LinkedHashSet;
34 import java.util.List;
35 import java.util.Locale;
36 import java.util.Set;
37 import java.util.logging.Level;
38 import javax.swing.JOptionPane;
39 import javax.swing.SwingUtilities;
40 import javax.swing.SwingWorker;
41 import org.openide.filesystems.FileUtil;
42 import org.openide.util.NbBundle.Messages;
43 import org.openide.windows.RetainLocation;
44 import org.openide.windows.TopComponent;
45 import org.openide.windows.WindowManager;
57 import org.sleuthkit.datamodel.BlackboardArtifact;
58 
63 @TopComponent.Description(preferredID = "GeolocationTopComponent", persistenceType = TopComponent.PERSISTENCE_NEVER)
64 @TopComponent.Registration(mode = "geolocation", openAtStartup = false)
65 @RetainLocation("geolocation")
66 @SuppressWarnings("PMD.SingularField")
67 public final class GeolocationTopComponent extends TopComponent {
68 
69  private static final long serialVersionUID = 1L;
70 
71  private static final Logger logger = Logger.getLogger(GeolocationTopComponent.class.getName());
72 
73  private static final Set<IngestManager.IngestModuleEvent> INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(DATA_ADDED);
74 
75  private final PropertyChangeListener ingestListener;
76  private final PropertyChangeListener caseEventListener;
77  private final GeoFilterPanel geoFilterPanel;
78 
79  final RefreshPanel refreshPanel = new RefreshPanel();
80 
81  private static final String REPORT_PATH_FMT_STR = "%s" + File.separator + "%s %s %s" + File.separator;
82 
83  // This is the hardcoded report name from KMLReport.java
84  private static final String REPORT_KML = "ReportKML.kml";
85 
86  private boolean mapInitalized = false;
87 
88  @Messages({
89  "GLTopComponent_name=Geolocation",
90  "GLTopComponent_initilzation_error=An error occurred during waypoint initilization. Geolocation data maybe incomplete.",
91  "GLTopComponent_No_dataSource_message=There are no data sources with Geolocation artifacts found.",
92  "GLTopComponent_No_dataSource_Title=No Geolocation artifacts found"
93  })
94 
99  @SuppressWarnings("deprecation")
101  initComponents();
102 
103  setName(Bundle.GLTopComponent_name());
104 
105  this.ingestListener = pce -> {
106  String eventType = pce.getPropertyName();
107  if (eventType.equals(DATA_ADDED.toString())) {
108  // Indicate that a refresh may be needed for GPS data.
109  ModuleDataEvent eventData = (ModuleDataEvent) pce.getOldValue();
110  if (null != eventData
111  && (eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACKPOINT.getTypeID()
112  || eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_SEARCH.getTypeID()
113  || eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_LAST_KNOWN_LOCATION.getTypeID()
114  || eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_ROUTE.getTypeID()
115  || eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF.getTypeID()
116  || eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_BOOKMARK.getTypeID()
117  || eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACK.getTypeID()
118  || eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_AREA.getTypeID())) {
119 
120  showRefreshPanel(true);
121  }
122  }
123  };
124 
125  this.caseEventListener = pce -> {
126  mapPanel.clearWaypoints();
127  if (pce.getNewValue() != null) {
128  updateWaypoints();
129  }
130  };
131 
132  refreshPanel.addCloseActionListener(new ActionListener() {
133  @Override
134  public void actionPerformed(ActionEvent e) {
135  showRefreshPanel(false);
136  }
137  });
138 
139  refreshPanel.addRefreshActionListner(new ActionListener() {
140  @Override
141  public void actionPerformed(ActionEvent e) {
142  geoFilterPanel.updateDataSourceList();
143  mapPanel.clearWaypoints();
144  showRefreshPanel(false);
145  }
146  });
147 
148  geoFilterPanel = new GeoFilterPanel();
149  filterPane.setPanel(geoFilterPanel);
150  geoFilterPanel.addActionListener(new ActionListener() {
151  @Override
152  public void actionPerformed(ActionEvent e) {
153  updateWaypoints();
154  }
155  });
156 
157  geoFilterPanel.addPropertyChangeListener(GeoFilterPanel.INITPROPERTY, new PropertyChangeListener() {
158  @Override
159  public void propertyChange(PropertyChangeEvent evt) {
160  if (geoFilterPanel.hasDataSources()) {
161  updateWaypoints();
162  } else {
163  geoFilterPanel.setEnabled(false);
164  setWaypointLoading(false);
165  JOptionPane.showMessageDialog(GeolocationTopComponent.this,
166  Bundle.GLTopComponent_No_dataSource_message(),
167  Bundle.GLTopComponent_No_dataSource_Title(),
168  JOptionPane.ERROR_MESSAGE);
169  }
170  }
171 
172  });
173 
174  mapPanel.addPropertyChangeListener(MapPanel.CURRENT_MOUSE_GEOPOSITION, new PropertyChangeListener() {
175  @Override
176  public void propertyChange(PropertyChangeEvent evt) {
177  String label = "";
178  Object newValue = evt.getNewValue();
179  if (newValue != null) {
180  label = newValue.toString();
181  }
182 
183  coordLabel.setText(label);
184  }
185 
186  });
187  }
188 
189  @Override
190  public void addNotify() {
191  super.addNotify();
192  IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, ingestListener);
193  Case.addEventTypeSubscriber(EnumSet.of(CURRENT_CASE), caseEventListener);
194  }
195 
196  @Override
197  public void removeNotify() {
198  super.removeNotify();
200  Case.removeEventTypeSubscriber(EnumSet.of(CURRENT_CASE), caseEventListener);
201  }
202 
203  @Override
204  public void componentOpened() {
205  super.componentOpened();
206  WindowManager.getDefault().setTopComponentFloating(this, true);
207 
208  }
209 
216  public void setFilterState(GeoFilter filter) throws GeoLocationUIException {
217  if (filter == null) {
218  throw new GeoLocationUIException("Filter provided cannot be null.");
219  }
220 
221  if (this.isOpened()) {
222  geoFilterPanel.setupFilter(filter);
223  updateWaypoints();
224  } else {
225  geoFilterPanel.setInitialFilterState(filter);
226  }
227  }
228 
229  @Messages({
230  "GeolocationTC_connection_failure_message=Failed to connect to map title source.\nPlease review map source in Options dialog.",
231  "GeolocationTC_connection_failure_message_title=Connection Failure"
232  })
233  @Override
234  public void open() {
235  super.open();
236 
237  // Let's make sure we only do this on the first open
238  if (!mapInitalized) {
239  try {
240  mapPanel.initMap();
241  mapInitalized = true;
242  } catch (GeoLocationDataException ex) {
243  JOptionPane.showMessageDialog(this,
244  Bundle.GeolocationTC_connection_failure_message(),
245  Bundle.GeolocationTC_connection_failure_message_title(),
246  JOptionPane.ERROR_MESSAGE);
248  Bundle.GeolocationTC_connection_failure_message_title(),
249  Bundle.GeolocationTC_connection_failure_message());
250  logger.log(Level.SEVERE, ex.getMessage(), ex);
251  return; // Doen't set the waypoints.
252  }
253  }
254 
255  mapPanel.clearWaypoints();
256  geoFilterPanel.clearDataSourceList();
257  geoFilterPanel.updateDataSourceList();
258  mapPanel.setWaypoints(new LinkedHashSet<>());
259 
260  }
261 
267  private void showRefreshPanel(boolean show) {
268  SwingUtilities.invokeLater(new Runnable() {
269  @Override
270  public void run() {
271  boolean isShowing = false;
272  Component[] comps = mapPanel.getComponents();
273  for (Component comp : comps) {
274  if (comp.equals(refreshPanel)) {
275  isShowing = true;
276  break;
277  }
278  }
279  if (show && !isShowing) {
280  mapPanel.add(refreshPanel, BorderLayout.NORTH);
281  mapPanel.revalidate();
282  } else if (!show && isShowing) {
283  mapPanel.remove(refreshPanel);
284  mapPanel.revalidate();
285  }
286  }
287  });
288 
289  }
290 
295  @Messages({
296  "GeoTopComponent_no_waypoints_returned_mgs=Applied filter failed to find waypoints that matched criteria.\nRevise filter options and try again.",
297  "GeoTopComponent_no_waypoints_returned_Title=No Waypoints Found",
298  "GeoTopComponent_filter_exception_msg=Exception occurred during waypoint filtering.",
299  "GeoTopComponent_filter_exception_Title=Filter Failure",
300  "GeoTopComponent_filer_data_invalid_msg=Unable to run waypoint filter.\nPlease select one or more data sources.",
301  "GeoTopComponent_filer_data_invalid_Title=Filter Failure"
302  })
303  private void updateWaypoints() {
304  GeoFilter filters;
305 
306  // Show a warning message if the user has not selected a data source
307  try {
308  filters = geoFilterPanel.getFilterState();
309  } catch (GeoLocationUIException ex) {
310  JOptionPane.showMessageDialog(this,
311  ex.getMessage(),
312  Bundle.GeoTopComponent_filer_data_invalid_Title(),
313  JOptionPane.INFORMATION_MESSAGE);
314  return;
315  }
316 
317  setWaypointLoading(true);
318  geoFilterPanel.setEnabled(false);
319 
320  Thread thread = new Thread(new Runnable() {
321  @Override
322  public void run() {
323  try {
324  (new WaypointFetcher(filters)).getWaypoints();
325  } catch (GeoLocationDataException ex) {
326  logger.log(Level.SEVERE, "Failed to filter waypoints.", ex);
327  SwingUtilities.invokeLater(new Runnable() {
328  @Override
329  public void run() {
330  JOptionPane.showMessageDialog(GeolocationTopComponent.this,
331  Bundle.GeoTopComponent_filter_exception_Title(),
332  Bundle.GeoTopComponent_filter_exception_msg(),
333  JOptionPane.ERROR_MESSAGE);
334 
335  setWaypointLoading(false);
336  }
337  });
338 
339  }
340  }
341 
342  });
343  thread.start();
344  }
345 
352  void addWaypointsToMap(Set<MapWaypoint> waypointList, List<Set<MapWaypoint>> tracks, List<Set<MapWaypoint>> areas) {
353  SwingUtilities.invokeLater(new Runnable() {
354  @Override
355  public void run() {
356  // If the list is empty, tell the user
357  if (waypointList == null || waypointList.isEmpty()) {
358  mapPanel.clearWaypoints();
359  JOptionPane.showMessageDialog(GeolocationTopComponent.this,
360  Bundle.GeoTopComponent_no_waypoints_returned_Title(),
361  Bundle.GeoTopComponent_no_waypoints_returned_mgs(),
362  JOptionPane.INFORMATION_MESSAGE);
363  setWaypointLoading(false);
364  geoFilterPanel.setEnabled(true);
365  return;
366  }
367  mapPanel.clearWaypoints();
368  mapPanel.setWaypoints(waypointList);
369  mapPanel.setTracks(tracks);
370  mapPanel.setAreas(areas);
371  mapPanel.initializePainter();
372  setWaypointLoading(false);
373  geoFilterPanel.setEnabled(true);
374  }
375  });
376  }
377 
383  void setWaypointLoading(boolean loading) {
384  progressBar.setEnabled(true);
385  progressBar.setVisible(loading);
386  progressBar.setString("Loading Waypoints");
387  }
388 
399  private static String createReportDirectory() throws IOException {
400  Case currentCase = Case.getCurrentCase();
401 
402  // Create the root reports directory path of the form: <CASE DIRECTORY>/Reports/<Case fileName> <Timestamp>/
403  DateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy-HH-mm-ss", Locale.US);
404  Date date = new Date();
405  String dateNoTime = dateFormat.format(date);
406  String reportPath = String.format(REPORT_PATH_FMT_STR, currentCase.getReportDirectory(), currentCase.getDisplayName(), "Google Earth KML", dateNoTime);
407  // Create the root reports directory.
408  try {
409  FileUtil.createFolder(new File(reportPath));
410  } catch (IOException ex) {
411  throw new IOException("Failed to make report folder, unable to generate reports.", ex);
412  }
413  return reportPath;
414  }
415 
421  @SuppressWarnings("unchecked")
422  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
423  private void initComponents() {
424  java.awt.GridBagConstraints gridBagConstraints;
425 
426  filterPane = new org.sleuthkit.autopsy.geolocation.HidingPane();
427  statusBar = new javax.swing.JPanel();
428  reportButton = new javax.swing.JButton();
429  progressBar = new javax.swing.JProgressBar();
430  coordLabel = new javax.swing.JLabel();
431  mapPanel = new org.sleuthkit.autopsy.geolocation.MapPanel();
432 
433  setLayout(new java.awt.BorderLayout());
434  add(filterPane, java.awt.BorderLayout.WEST);
435 
436  statusBar.setLayout(new java.awt.GridBagLayout());
437 
438  org.openide.awt.Mnemonics.setLocalizedText(reportButton, org.openide.util.NbBundle.getMessage(GeolocationTopComponent.class, "GeolocationTopComponent.reportButton.text")); // NOI18N
439  reportButton.addActionListener(new java.awt.event.ActionListener() {
440  public void actionPerformed(java.awt.event.ActionEvent evt) {
441  reportButtonActionPerformed(evt);
442  }
443  });
444  gridBagConstraints = new java.awt.GridBagConstraints();
445  gridBagConstraints.gridx = 2;
446  gridBagConstraints.gridy = 0;
447  gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
448  gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5);
449  statusBar.add(reportButton, gridBagConstraints);
450 
451  progressBar.setIndeterminate(true);
452  gridBagConstraints = new java.awt.GridBagConstraints();
453  gridBagConstraints.gridx = 1;
454  gridBagConstraints.gridy = 0;
455  gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
456  statusBar.add(progressBar, gridBagConstraints);
457 
458  org.openide.awt.Mnemonics.setLocalizedText(coordLabel, org.openide.util.NbBundle.getMessage(GeolocationTopComponent.class, "GeolocationTopComponent.coordLabel.text")); // NOI18N
459  gridBagConstraints = new java.awt.GridBagConstraints();
460  gridBagConstraints.gridx = 0;
461  gridBagConstraints.gridy = 0;
462  gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
463  gridBagConstraints.weightx = 1.0;
464  gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 0);
465  statusBar.add(coordLabel, gridBagConstraints);
466 
467  add(statusBar, java.awt.BorderLayout.SOUTH);
468  add(mapPanel, java.awt.BorderLayout.CENTER);
469  }// </editor-fold>//GEN-END:initComponents
470 
471  @Messages({
472  "GeolocationTC_empty_waypoint_message=Unable to generate KML report due to a lack of waypoints.\nPlease make sure there are waypoints visible before generating the KML report",
473  "GeolocationTC_KML_report_title=KML Report",
474  "GeolocationTC_report_progress_title=KML Report Progress"
475  })
476  private void reportButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_reportButtonActionPerformed
477  List<MapWaypoint> visiblePoints = mapPanel.getVisibleWaypoints();
478  if (visiblePoints.isEmpty()) {
479  JOptionPane.showConfirmDialog(this, Bundle.GeolocationTC_empty_waypoint_message(), Bundle.GeolocationTC_KML_report_title(), JOptionPane.OK_OPTION, JOptionPane.INFORMATION_MESSAGE);
480  return;
481  }
482 
483  try {
484  ReportProgressPanel progressPanel = new ReportProgressPanel();
485  String reportBaseDir = createReportDirectory();
486 
487  progressPanel.setLabels(REPORT_KML, reportBaseDir);
488 
489  SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
490  @Override
491  protected Void doInBackground() throws Exception {
492  KMLReport.getDefault().generateReport(reportBaseDir, progressPanel, MapWaypoint.getDataModelWaypoints(visiblePoints));
493  return null;
494  }
495  };
496  worker.execute();
497  JOptionPane.showConfirmDialog(this, progressPanel, Bundle.GeolocationTC_report_progress_title(), JOptionPane.CLOSED_OPTION, JOptionPane.PLAIN_MESSAGE);
498  } catch (IOException ex) {
499  logger.log(Level.WARNING, "Unable to create KML report", ex);
500  }
501  }//GEN-LAST:event_reportButtonActionPerformed
502 
503 
504  // Variables declaration - do not modify//GEN-BEGIN:variables
505  private javax.swing.JLabel coordLabel;
508  private javax.swing.JProgressBar progressBar;
509  private javax.swing.JButton reportButton;
510  private javax.swing.JPanel statusBar;
511  // End of variables declaration//GEN-END:variables
512 
517  @Messages({
518  "GeolocationTopComponent.WaypointFetcher.onErrorTitle=Error gathering GPS Track Data",
519  "GeolocationTopComponent.WaypointFetcher.onErrorDescription=There was an error gathering some GPS Track Data. Some results have been excluded."
520  })
521  final private class WaypointFetcher extends AbstractWaypointFetcher {
522 
523  WaypointFetcher(GeoFilter filters) {
524  super(filters);
525  }
526 
527  @Override
528  protected void handleFilteredWaypointSet(Set<MapWaypoint> mapWaypoints, List<Set<MapWaypoint>> tracks,
529  List<Set<MapWaypoint>> areas, boolean wasEntirelySuccessful) {
530  addWaypointsToMap(mapWaypoints, tracks, areas);
531 
532  // if there is an error, present to the user.
533  if (!wasEntirelySuccessful) {
534  JOptionPane.showMessageDialog(GeolocationTopComponent.this,
535  Bundle.GeolocationTopComponent_WaypointFetcher_onErrorDescription(),
536  Bundle.GeolocationTopComponent_WaypointFetcher_onErrorTitle(),
537  JOptionPane.ERROR_MESSAGE);
538  }
539  }
540  }
541 }
final void setLabels(String reportName, String reportPath)
BlackboardArtifact.Type getBlackboardArtifactType()
void removeIngestModuleEventListener(final PropertyChangeListener listener)
static synchronized IngestManager getInstance()
static synchronized KMLReport getDefault()
Definition: KMLReport.java:117
void generateReport(String baseReportDir, ReportProgressPanel progressPanel, List< Waypoint > waypointList)
Definition: KMLReport.java:163
static void error(String title, String message)
void addIngestModuleEventListener(final PropertyChangeListener listener)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:704
org.sleuthkit.autopsy.geolocation.HidingPane filterPane
void handleFilteredWaypointSet(Set< MapWaypoint > mapWaypoints, List< Set< MapWaypoint >> tracks, List< Set< MapWaypoint >> areas, boolean wasEntirelySuccessful)
static void removeEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:749

Copyright © 2012-2022 Basis Technology. Generated on: Tue Aug 1 2023
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.