Autopsy 4.22.1
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 */
19package org.sleuthkit.autopsy.geolocation;
20
21import java.awt.BorderLayout;
22import java.awt.Component;
23import java.awt.event.ActionEvent;
24import java.awt.event.ActionListener;
25import java.beans.PropertyChangeEvent;
26import java.beans.PropertyChangeListener;
27import java.io.File;
28import java.io.IOException;
29import java.text.DateFormat;
30import java.text.SimpleDateFormat;
31import java.util.Date;
32import java.util.EnumSet;
33import java.util.LinkedHashSet;
34import java.util.List;
35import java.util.Locale;
36import java.util.Set;
37import java.util.logging.Level;
38import javax.swing.JOptionPane;
39import javax.swing.SwingUtilities;
40import javax.swing.SwingWorker;
41import org.openide.filesystems.FileUtil;
42import org.openide.util.NbBundle.Messages;
43import org.openide.windows.RetainLocation;
44import org.openide.windows.TopComponent;
45import org.openide.windows.WindowManager;
46import org.sleuthkit.autopsy.casemodule.Case;
47import static org.sleuthkit.autopsy.casemodule.Case.Events.CURRENT_CASE;
48import org.sleuthkit.autopsy.coreutils.Logger;
49import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
50import org.sleuthkit.autopsy.coreutils.ThreadConfined;
51import org.sleuthkit.autopsy.geolocation.datamodel.GeoLocationDataException;
52import org.sleuthkit.autopsy.ingest.IngestManager;
53import static org.sleuthkit.autopsy.ingest.IngestManager.IngestModuleEvent.DATA_ADDED;
54import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
55import org.sleuthkit.autopsy.report.ReportProgressPanel;
56import org.sleuthkit.autopsy.report.modules.kml.KMLReport;
57import 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")
67public 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")
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) {
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) {
154 }
155 });
156
157 geoFilterPanel.addPropertyChangeListener(GeoFilterPanel.INITPROPERTY, new PropertyChangeListener() {
158 @Override
159 public void propertyChange(PropertyChangeEvent evt) {
160 if (geoFilterPanel.hasDataSources()) {
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
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
217 if (filter == null) {
218 throw new GeoLocationUIException("Filter provided cannot be null.");
219 }
220
221 if (this.isOpened()) {
222 geoFilterPanel.setupFilter(filter);
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}
static void removeEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition Case.java:757
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition Case.java:712
synchronized static Logger getLogger(String name)
Definition Logger.java:124
void handleFilteredWaypointSet(Set< MapWaypoint > mapWaypoints, List< Set< MapWaypoint > > tracks, List< Set< MapWaypoint > > areas, boolean wasEntirelySuccessful)
static final Set< IngestManager.IngestModuleEvent > INGEST_MODULE_EVENTS_OF_INTEREST
static synchronized IngestManager getInstance()
void removeIngestModuleEventListener(final PropertyChangeListener listener)
void addIngestModuleEventListener(final PropertyChangeListener listener)
final void setLabels(String reportName, String reportPath)
void generateReport(String baseReportDir, ReportProgressPanel progressPanel, List< Waypoint > waypointList)
static synchronized KMLReport getDefault()

Copyright © 2012-2024 Sleuth Kit Labs. Generated on:
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.