Autopsy  4.15.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;
58 import org.sleuthkit.datamodel.BlackboardArtifact;
59 
64 @TopComponent.Description(preferredID = "GeolocationTopComponent", persistenceType = TopComponent.PERSISTENCE_NEVER)
65 @TopComponent.Registration(mode = "geolocation", openAtStartup = false)
66 @RetainLocation("geolocation")
67 @SuppressWarnings("PMD.SingularField")
68 public final class GeolocationTopComponent extends TopComponent {
69 
70  private static final long serialVersionUID = 1L;
71 
72  private static final Logger logger = Logger.getLogger(GeolocationTopComponent.class.getName());
73 
74  private static final Set<IngestManager.IngestModuleEvent> INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(DATA_ADDED);
75 
76  private final PropertyChangeListener ingestListener;
77  private final PropertyChangeListener caseEventListener;
78  private final GeoFilterPanel geoFilterPanel;
79 
80  final RefreshPanel refreshPanel = new RefreshPanel();
81 
82  private static final String REPORT_PATH_FMT_STR = "%s" + File.separator + "%s %s %s" + File.separator;
83 
84  // This is the hardcoded report name from KMLReport.java
85  private static final String REPORT_KML = "ReportKML.kml";
86 
87  private boolean mapInitalized = false;
88 
89  @Messages({
90  "GLTopComponent_name=Geolocation",
91  "GLTopComponent_initilzation_error=An error occurred during waypoint initilization. Geolocation data maybe incomplete.",
92  "GLTopComponent_No_dataSource_message=There are no data sources with Geolocation artifacts found.",
93  "GLTopComponent_No_dataSource_Title=No Geolocation artifacts found"
94  })
95 
100  @SuppressWarnings("deprecation")
102  initComponents();
103 
104  setName(Bundle.GLTopComponent_name());
105 
106  this.ingestListener = pce -> {
107  String eventType = pce.getPropertyName();
108  if (eventType.equals(DATA_ADDED.toString())) {
109  // Indicate that a refresh may be needed for GPS data.
110  ModuleDataEvent eventData = (ModuleDataEvent) pce.getOldValue();
111  if (null != eventData
112  && (eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACKPOINT.getTypeID()
113  || eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_SEARCH.getTypeID()
114  || eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_LAST_KNOWN_LOCATION.getTypeID()
115  || eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_ROUTE.getTypeID()
116  || eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF.getTypeID()
117  || eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_BOOKMARK.getTypeID()
118  || eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACK.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 
210  @Messages({
211  "GeolocationTC_connection_failure_message=Failed to connect to map title source.\nPlease review map source in Options dialog.",
212  "GeolocationTC_connection_failure_message_title=Connection Failure"
213  })
214  @Override
215  public void open() {
216  super.open();
217 
218  // Let's make sure we only do this on the first open
219  if (!mapInitalized) {
220  try {
221  mapPanel.initMap();
222  mapInitalized = true;
223  } catch (GeoLocationDataException ex) {
224  JOptionPane.showMessageDialog(this,
225  Bundle.GeolocationTC_connection_failure_message(),
226  Bundle.GeolocationTC_connection_failure_message_title(),
227  JOptionPane.ERROR_MESSAGE);
229  Bundle.GeolocationTC_connection_failure_message_title(),
230  Bundle.GeolocationTC_connection_failure_message());
231  logger.log(Level.SEVERE, ex.getMessage(), ex);
232  return; // Doen't set the waypoints.
233  }
234  }
235 
236  mapPanel.clearWaypoints();
237  geoFilterPanel.clearDataSourceList();
238  geoFilterPanel.updateDataSourceList();
239  mapPanel.setWaypoints(new LinkedHashSet<>());
240 
241  }
242 
248  private void showRefreshPanel(boolean show) {
249  SwingUtilities.invokeLater(new Runnable() {
250  @Override
251  public void run() {
252  boolean isShowing = false;
253  Component[] comps = mapPanel.getComponents();
254  for (Component comp : comps) {
255  if (comp.equals(refreshPanel)) {
256  isShowing = true;
257  break;
258  }
259  }
260  if (show && !isShowing) {
261  mapPanel.add(refreshPanel, BorderLayout.NORTH);
262  mapPanel.revalidate();
263  } else if (!show && isShowing) {
264  mapPanel.remove(refreshPanel);
265  mapPanel.revalidate();
266  }
267  }
268  });
269 
270  }
271 
276  @Messages({
277  "GeoTopComponent_no_waypoints_returned_mgs=Applied filter failed to find waypoints that matched criteria.\nRevise filter options and try again.",
278  "GeoTopComponent_no_waypoints_returned_Title=No Waypoints Found",
279  "GeoTopComponent_filter_exception_msg=Exception occurred during waypoint filtering.",
280  "GeoTopComponent_filter_exception_Title=Filter Failure",
281  "GeoTopComponent_filer_data_invalid_msg=Unable to run waypoint filter.\nPlease select one or more data sources.",
282  "GeoTopComponent_filer_data_invalid_Title=Filter Failure"
283  })
284  private void updateWaypoints() {
285  GeoFilter filters;
286 
287  // Show a warning message if the user has not selected a data source
288  try {
289  filters = geoFilterPanel.getFilterState();
290  } catch (GeoLocationUIException ex) {
291  JOptionPane.showMessageDialog(this,
292  ex.getMessage(),
293  Bundle.GeoTopComponent_filer_data_invalid_Title(),
294  JOptionPane.INFORMATION_MESSAGE);
295  return;
296  }
297 
298  setWaypointLoading(true);
299  geoFilterPanel.setEnabled(false);
300 
301  Thread thread = new Thread(new Runnable() {
302  @Override
303  public void run() {
304  try {
305  (new WaypointFetcher(filters)).getWaypoints();
306  } catch (GeoLocationDataException ex) {
307  logger.log(Level.SEVERE, "Failed to filter waypoints.", ex);
308  SwingUtilities.invokeLater(new Runnable() {
309  @Override
310  public void run() {
311  JOptionPane.showMessageDialog(GeolocationTopComponent.this,
312  Bundle.GeoTopComponent_filter_exception_Title(),
313  Bundle.GeoTopComponent_filter_exception_msg(),
314  JOptionPane.ERROR_MESSAGE);
315 
316  setWaypointLoading(false);
317  }
318  });
319 
320  }
321  }
322 
323  });
324  thread.start();
325  }
326 
333  void addWaypointsToMap(Set<MapWaypoint> waypointList, List<Set<MapWaypoint>> tracks) {
334  SwingUtilities.invokeLater(new Runnable() {
335  @Override
336  public void run() {
337  // If the list is empty, tell the user
338  if (waypointList == null || waypointList.isEmpty()) {
339  mapPanel.clearWaypoints();
340  JOptionPane.showMessageDialog(GeolocationTopComponent.this,
341  Bundle.GeoTopComponent_no_waypoints_returned_Title(),
342  Bundle.GeoTopComponent_no_waypoints_returned_mgs(),
343  JOptionPane.INFORMATION_MESSAGE);
344  setWaypointLoading(false);
345  geoFilterPanel.setEnabled(true);
346  return;
347  }
348  mapPanel.clearWaypoints();
349  mapPanel.setWaypoints(waypointList);
350  mapPanel.setTracks(tracks);
351  mapPanel.initializePainter();
352  setWaypointLoading(false);
353  geoFilterPanel.setEnabled(true);
354  }
355  });
356  }
357 
363  void setWaypointLoading(boolean loading) {
364  progressBar.setEnabled(true);
365  progressBar.setVisible(loading);
366  progressBar.setString("Loading Waypoints");
367  }
368 
379  private static String createReportDirectory() throws IOException {
380  Case currentCase = Case.getCurrentCase();
381 
382  // Create the root reports directory path of the form: <CASE DIRECTORY>/Reports/<Case fileName> <Timestamp>/
383  DateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy-HH-mm-ss", Locale.US);
384  Date date = new Date();
385  String dateNoTime = dateFormat.format(date);
386  String reportPath = String.format(REPORT_PATH_FMT_STR, currentCase.getReportDirectory(), currentCase.getDisplayName(), "Google Earth KML", dateNoTime);
387  // Create the root reports directory.
388  try {
389  FileUtil.createFolder(new File(reportPath));
390  } catch (IOException ex) {
391  throw new IOException("Failed to make report folder, unable to generate reports.", ex);
392  }
393  return reportPath;
394  }
395 
401  @SuppressWarnings("unchecked")
402  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
403  private void initComponents() {
404  java.awt.GridBagConstraints gridBagConstraints;
405 
406  filterPane = new org.sleuthkit.autopsy.geolocation.HidingPane();
407  statusBar = new javax.swing.JPanel();
408  reportButton = new javax.swing.JButton();
409  progressBar = new javax.swing.JProgressBar();
410  coordLabel = new javax.swing.JLabel();
411  mapPanel = new org.sleuthkit.autopsy.geolocation.MapPanel();
412 
413  setLayout(new java.awt.BorderLayout());
414  add(filterPane, java.awt.BorderLayout.WEST);
415 
416  statusBar.setLayout(new java.awt.GridBagLayout());
417 
418  org.openide.awt.Mnemonics.setLocalizedText(reportButton, org.openide.util.NbBundle.getMessage(GeolocationTopComponent.class, "GeolocationTopComponent.reportButton.text")); // NOI18N
419  reportButton.addActionListener(new java.awt.event.ActionListener() {
420  public void actionPerformed(java.awt.event.ActionEvent evt) {
421  reportButtonActionPerformed(evt);
422  }
423  });
424  gridBagConstraints = new java.awt.GridBagConstraints();
425  gridBagConstraints.gridx = 2;
426  gridBagConstraints.gridy = 0;
427  gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
428  gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5);
429  statusBar.add(reportButton, gridBagConstraints);
430 
431  progressBar.setIndeterminate(true);
432  gridBagConstraints = new java.awt.GridBagConstraints();
433  gridBagConstraints.gridx = 1;
434  gridBagConstraints.gridy = 0;
435  gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
436  statusBar.add(progressBar, gridBagConstraints);
437 
438  org.openide.awt.Mnemonics.setLocalizedText(coordLabel, org.openide.util.NbBundle.getMessage(GeolocationTopComponent.class, "GeolocationTopComponent.coordLabel.text")); // NOI18N
439  gridBagConstraints = new java.awt.GridBagConstraints();
440  gridBagConstraints.gridx = 0;
441  gridBagConstraints.gridy = 0;
442  gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
443  gridBagConstraints.weightx = 1.0;
444  gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 0);
445  statusBar.add(coordLabel, gridBagConstraints);
446 
447  add(statusBar, java.awt.BorderLayout.SOUTH);
448  add(mapPanel, java.awt.BorderLayout.CENTER);
449  }// </editor-fold>//GEN-END:initComponents
450 
451  @Messages({
452  "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",
453  "GeolocationTC_KML_report_title=KML Report",
454  "GeolocationTC_report_progress_title=KML Report Progress"
455  })
456  private void reportButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_reportButtonActionPerformed
457  List<MapWaypoint> visiblePoints = mapPanel.getVisibleWaypoints();
458  if (visiblePoints.isEmpty()) {
459  JOptionPane.showConfirmDialog(this, Bundle.GeolocationTC_empty_waypoint_message(), Bundle.GeolocationTC_KML_report_title(), JOptionPane.OK_OPTION, JOptionPane.INFORMATION_MESSAGE);
460  return;
461  }
462 
463  try {
464  ReportProgressPanel progressPanel = new ReportProgressPanel();
465  String reportBaseDir = createReportDirectory();
466 
467  progressPanel.setLabels(REPORT_KML, reportBaseDir);
468 
469  SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
470  @Override
471  protected Void doInBackground() throws Exception {
472  KMLReport.getDefault().generateReport(reportBaseDir, progressPanel, MapWaypoint.getDataModelWaypoints(visiblePoints));
473  return null;
474  }
475  };
476  worker.execute();
477  JOptionPane.showConfirmDialog(this, progressPanel, Bundle.GeolocationTC_report_progress_title(), JOptionPane.CLOSED_OPTION, JOptionPane.PLAIN_MESSAGE);
478  } catch (IOException ex) {
479  logger.log(Level.WARNING, "Unable to create KML report", ex);
480  }
481  }//GEN-LAST:event_reportButtonActionPerformed
482 
483 
484  // Variables declaration - do not modify//GEN-BEGIN:variables
485  private javax.swing.JLabel coordLabel;
488  private javax.swing.JProgressBar progressBar;
489  private javax.swing.JButton reportButton;
490  private javax.swing.JPanel statusBar;
491  // End of variables declaration//GEN-END:variables
492 
497  @Messages({
498  "GeolocationTopComponent.WaypointFetcher.onErrorTitle=Error gathering GPS Track Data",
499  "GeolocationTopComponent.WaypointFetcher.onErrorDescription=There was an error gathering some GPS Track Data. Some results have been excluded."
500  })
501  final private class WaypointFetcher extends AbstractWaypointFetcher {
502 
503  WaypointFetcher(GeoFilter filters) {
504  super(filters);
505  }
506 
507  @Override
508  void handleFilteredWaypointSet(Set<MapWaypoint> mapWaypoints, List<Set<MapWaypoint>> tracks, boolean wasEntirelySuccessful) {
509  addWaypointsToMap(mapWaypoints, tracks);
510 
511  // if there is an error, present to the user.
512  if (!wasEntirelySuccessful) {
513  JOptionPane.showMessageDialog(GeolocationTopComponent.this,
514  Bundle.GeolocationTopComponent_WaypointFetcher_onErrorDescription(),
515  Bundle.GeolocationTopComponent_WaypointFetcher_onErrorTitle(),
516  JOptionPane.ERROR_MESSAGE);
517  }
518  }
519  }
520 }
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:115
void generateReport(String baseReportDir, ReportProgressPanel progressPanel, List< Waypoint > waypointList)
Definition: KMLReport.java:160
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:491
org.sleuthkit.autopsy.geolocation.HidingPane filterPane
static void removeEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:536

Copyright © 2012-2020 Basis Technology. Generated on: Mon Jul 6 2020
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.