Autopsy  4.20.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
KMLReport.java
Go to the documentation of this file.
1 /*
2  *
3  * Autopsy Forensic Browser
4  *
5  * Copyright 2014-2020 Basis Technology Corp.
6  * contact: carrier <at> sleuthkit <dot> org
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  */
20 package org.sleuthkit.autopsy.report.modules.kml;
21 
23 import javax.swing.JPanel;
24 import org.openide.util.NbBundle;
28 import java.io.File;
29 import java.io.FileOutputStream;
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.io.OutputStream;
33 import java.nio.file.Path;
34 import java.nio.file.Paths;
35 import java.text.SimpleDateFormat;
36 import java.util.List;
37 import java.util.logging.Level;
38 import java.util.stream.Collectors;
39 import org.jdom2.Document;
40 import org.jdom2.Element;
41 import org.jdom2.Namespace;
42 import org.jdom2.output.Format;
43 import org.jdom2.output.XMLOutputter;
44 import org.jdom2.CDATA;
45 import org.openide.filesystems.FileUtil;
46 import org.openide.util.NbBundle.Messages;
58 import org.sleuthkit.datamodel.AbstractFile;
59 import org.sleuthkit.datamodel.Content;
60 import org.sleuthkit.datamodel.ReadContentInputStream;
61 import org.sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException;
62 import org.sleuthkit.datamodel.SleuthkitCase;
63 import org.sleuthkit.datamodel.TskCoreException;
64 
68 public final class KMLReport implements GeneralReportModule {
69 
70  private static final Logger logger = Logger.getLogger(KMLReport.class.getName());
71  private static final String KML_STYLE_FILE = "style.kml";
72  private static final String REPORT_KML = "ReportKML.kml";
73  private static final String STYLESHEETS_PATH = "/org/sleuthkit/autopsy/report/stylesheets/";
74  private static KMLReport instance = null;
75  private Case currentCase;
76  private SleuthkitCase skCase;
77  private final SimpleDateFormat kmlDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");
78  private Namespace ns;
79  private final static String HTML_PROP_FORMAT = "<b>%s: </b>%s<br>";
80 
81  private Element gpsExifMetadataFolder;
82  private Element gpsBookmarksFolder;
83  private Element gpsLastKnownLocationFolder;
84  private Element gpsRouteFolder;
85  private Element gpsSearchesFolder;
86  private Element gpsTrackpointsFolder;
87  private Element gpsTracksFolder;
88  private Element gpsAreasFolder;
89 
91 
92  private List<Waypoint> waypointList = null;
93 
94  private enum FeatureColor {
95  RED("style.kml#redFeature"),
96  GREEN("style.kml#greenFeature"),
97  BLUE("style.kml#blueFeature"),
98  PURPLE("style.kml#purpleFeature"),
99  WHITE("style.kml#whiteFeature"),
100  YELLOW("style.kml#yellowFeature");
101  private final String color;
102 
103  FeatureColor(String color) {
104  this.color = color;
105  }
106 
107  String getColor() {
108  return this.color;
109  }
110  }
111 
112  // Hidden constructor for the report
113  private KMLReport() {
114  }
115 
116  // Get the default implementation of this report
117  public static synchronized KMLReport getDefault() {
118  if (instance == null) {
119  instance = new KMLReport();
120  }
121  return instance;
122  }
123 
131  @Messages({
132  "KMLReport.failedToCompleteReport=Failed to complete report.",
133  "KMLReport.partialFailure=There was an error creating the report. Some items were not exported.",
134  "KMLReport.unableToExtractPhotos=Could not extract photo information.",
135  "KMLReport.exifPhotoError=Could not extract photos with EXIF metadata.",
136  "KMLReport.bookmarkError=Could not extract Bookmark information.",
137  "KMLReport.gpsBookmarkError=Could not get GPS Bookmarks from database.",
138  "KMLReport.locationError=Could not extract Last Known Location information.",
139  "KMLReport.locationDatabaseError=Could not get GPS Last Known Location from database.",
140  "KMLReport.gpsRouteError=Could not extract GPS Route information.",
141  "KMLReport.gpsRouteDatabaseError=Could not get GPS Routes from database.",
142  "KMLReport.gpsSearchDatabaseError=Could not get GPS Searches from database.",
143  "KMLReport.trackpointError=Could not extract Trackpoint information.",
144  "KMLReport.trackpointDatabaseError=Could not get GPS Trackpoints from database.",
145  "KMLReport.stylesheetError=Error placing KML stylesheet. The .KML file will not function properly.",
146  "KMLReport.kmlFileWriteError=Could not write the KML file.",
147  "# {0} - filePath",
148  "KMLReport.errorGeneratingReport=Error adding {0} to case as a report.",
149  "KMLReport.unableToOpenCase=Exception while getting open case.",
150  "Waypoint_Bookmark_Display_String=GPS Bookmark",
151  "Waypoint_Last_Known_Display_String=GPS Last Known Location",
152  "Waypoint_EXIF_Display_String=EXIF Metadata With Location",
153  "Waypoint_Route_Point_Display_String=GPS Individual Route Point",
154  "Waypoint_Search_Display_String=GPS Search",
155  "Waypoint_Trackpoint_Display_String=GPS Trackpoint",
156  "Waypoint_Track_Display_String=GPS Track",
157  "Route_Details_Header=GPS Route",
158  "ReportBodyFile.ingestWarning.text=Ingest Warning message",
159  "Waypoint_Track_Point_Display_String=GPS Individual Track Point",
160  "Waypoint_Area_Point_Display_String=GPS Area Outline Point",
161  })
162 
163  public void generateReport(String baseReportDir, ReportProgressPanel progressPanel, List<Waypoint> waypointList) {
164  this.waypointList = waypointList;
165  GeneralReportSettings reportSettings = new GeneralReportSettings();
166  reportSettings.setReportDirectoryPath(baseReportDir);
167  generateReport(reportSettings, progressPanel);
168  }
169 
170  @Override
171  public boolean supportsDataSourceSelection() {
172  return true;
173  }
174 
175  @Override
176  public void generateReport(GeneralReportSettings settings, ReportProgressPanel progressPanel) {
177  try {
178  currentCase = Case.getCurrentCaseThrows();
179  } catch (NoCurrentCaseException ex) {
180  logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS
181  return;
182  }
183 
184  if(settings.getSelectedDataSources() == null) {
185  // Process all data sources if the list is null.
186  try {
187  List<Long> selectedDataSources = currentCase.getDataSources()
188  .stream()
189  .map(Content::getId)
190  .collect(Collectors.toList());
191  settings.setSelectedDataSources(selectedDataSources);
192  } catch (TskCoreException ex) {
193  logger.log(Level.SEVERE, "Could not get the datasources from the case", ex);
194  return;
195  }
196  }
197 
198  String baseReportDir = settings.getReportDirectoryPath();
199  this.settings = settings;
200  // Start the progress bar and setup the report
201  progressPanel.setIndeterminate(true);
202  progressPanel.start();
203  progressPanel.updateStatusLabel(NbBundle.getMessage(this.getClass(), "ReportKML.progress.querying"));
204  String kmlFileFullPath = baseReportDir + REPORT_KML; //NON-NLS
205  String errorMessage = "";
206 
207  skCase = currentCase.getSleuthkitCase();
208 
209  progressPanel.updateStatusLabel(NbBundle.getMessage(this.getClass(), "ReportKML.progress.loading"));
210 
211  Document kmlDocument = setupReportDocument();
212 
214 
215  try {
216  makeRoutes(skCase);
217  boolean entirelySuccessful = makeTracks(skCase);
218  if (!entirelySuccessful) {
220  errorMessage = Bundle.KMLReport_partialFailure();
221  }
222  entirelySuccessful = makeAreas(skCase);
223  if (!entirelySuccessful) {
225  errorMessage = Bundle.KMLReport_partialFailure();
226  }
227 
228  addLocationsToReport(skCase, baseReportDir);
229  } catch (GeoLocationDataException | IOException | TskCoreException ex) {
230  errorMessage = Bundle.KMLReport_failedToCompleteReport();
231  logger.log(Level.SEVERE, errorMessage, ex); //NON-NLS
233  }
234 
235  // Copy the style sheet
236  try {
237  InputStream input = getClass().getResourceAsStream(STYLESHEETS_PATH + KML_STYLE_FILE); // Preserve slash direction
238  OutputStream output = new FileOutputStream(baseReportDir + KML_STYLE_FILE); // Preserve slash direction
239  FileUtil.copy(input, output);
240  } catch (IOException ex) {
241  errorMessage = Bundle.KMLReport_stylesheetError();
242  logger.log(Level.SEVERE, errorMessage, ex); //NON-NLS
244  }
245 
246  try (FileOutputStream writer = new FileOutputStream(kmlFileFullPath)) {
247  XMLOutputter outputter = new XMLOutputter(Format.getPrettyFormat());
248  outputter.output(kmlDocument, writer);
249  String prependedStatus = "";
250  if (result == ReportProgressPanel.ReportStatus.ERROR) {
251  prependedStatus = "Incomplete ";
252  }
253  Case.getCurrentCaseThrows().addReport(kmlFileFullPath,
254  NbBundle.getMessage(this.getClass(), "ReportKML.genReport.srcModuleName.text"),
255  prependedStatus + NbBundle.getMessage(this.getClass(), "ReportKML.genReport.reportName"));
256  } catch (IOException ex) {
257  errorMessage = Bundle.KMLReport_kmlFileWriteError();
258  logger.log(Level.SEVERE, errorMessage, ex); //NON-NLS
259  progressPanel.complete(ReportProgressPanel.ReportStatus.ERROR, errorMessage);
260  } catch (TskCoreException ex) {
261  errorMessage = Bundle.KMLReport_errorGeneratingReport(kmlFileFullPath);
262  logger.log(Level.SEVERE, errorMessage, ex);
264  } catch (NoCurrentCaseException ex) {
265  errorMessage = Bundle.KMLReport_unableToOpenCase();
266  logger.log(Level.SEVERE, errorMessage, ex);
268  }
269 
270  progressPanel.complete(result, errorMessage);
271  }
272 
278  private Document setupReportDocument() {
279  ns = Namespace.getNamespace("", "http://www.opengis.net/kml/2.2"); //NON-NLS
280 
281  Element kml = new Element("kml", ns); //NON-NLS
282  kml.addNamespaceDeclaration(Namespace.getNamespace("gx", "http://www.google.com/kml/ext/2.2")); //NON-NLS
283  kml.addNamespaceDeclaration(Namespace.getNamespace("kml", "http://www.opengis.net/kml/2.2")); //NON-NLS
284  kml.addNamespaceDeclaration(Namespace.getNamespace("atom", "http://www.w3.org/2005/Atom")); //NON-NLS
285  Document kmlDocument = new Document(kml);
286 
287  Element document = new Element("Document", ns); //NON-NLS
288  kml.addContent(document);
289 
290  Element name = new Element("name", ns); //NON-NLS
291  ReportBranding rb = new ReportBranding();
292  name.setText(rb.getReportTitle() + " KML"); //NON-NLS
293  document.addContent(name);
294 
295  // Check if ingest has finished
297  Element ingestwarning = new Element("snippet", ns); //NON-NLS
298  ingestwarning.addContent(NbBundle.getMessage(this.getClass(), "ReportBodyFile.ingestWarning.text")); //NON-NLS
299  document.addContent(ingestwarning);
300  }
301 
302  // Create folder structure
303  gpsExifMetadataFolder = new Element("Folder", ns); //NON-NLS
304  CDATA cdataExifMetadataFolder = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/camera-icon-16.png"); //NON-NLS
305  Element hrefExifMetadata = new Element("href", ns).addContent(cdataExifMetadataFolder); //NON-NLS
306  gpsExifMetadataFolder.addContent(new Element("Icon", ns).addContent(hrefExifMetadata)); //NON-NLS
307 
308  gpsBookmarksFolder = new Element("Folder", ns); //NON-NLS
309  CDATA cdataBookmarks = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gpsfav.png"); //NON-NLS
310  Element hrefBookmarks = new Element("href", ns).addContent(cdataBookmarks); //NON-NLS
311  gpsBookmarksFolder.addContent(new Element("Icon", ns).addContent(hrefBookmarks)); //NON-NLS
312 
313  gpsLastKnownLocationFolder = new Element("Folder", ns); //NON-NLS
314  CDATA cdataLastKnownLocation = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-lastlocation.png"); //NON-NLS
315  Element hrefLastKnownLocation = new Element("href", ns).addContent(cdataLastKnownLocation); //NON-NLS
316  gpsLastKnownLocationFolder.addContent(new Element("Icon", ns).addContent(hrefLastKnownLocation)); //NON-NLS
317 
318  gpsRouteFolder = new Element("Folder", ns); //NON-NLS
319  CDATA cdataRoute = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-trackpoint.png"); //NON-NLS
320  Element hrefRoute = new Element("href", ns).addContent(cdataRoute); //NON-NLS
321  gpsRouteFolder.addContent(new Element("Icon", ns).addContent(hrefRoute)); //NON-NLS
322 
323  gpsSearchesFolder = new Element("Folder", ns); //NON-NLS
324  CDATA cdataSearches = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-search.png"); //NON-NLS
325  Element hrefSearches = new Element("href", ns).addContent(cdataSearches); //NON-NLS
326  gpsSearchesFolder.addContent(new Element("Icon", ns).addContent(hrefSearches)); //NON-NLS
327 
328  gpsTrackpointsFolder = new Element("Folder", ns); //NON-NLS
329  CDATA cdataTrackpoints = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-trackpoint.png"); //NON-NLS
330  Element hrefTrackpoints = new Element("href", ns).addContent(cdataTrackpoints); //NON-NLS
331  gpsTrackpointsFolder.addContent(new Element("Icon", ns).addContent(hrefTrackpoints)); //NON-NLS
332 
333  gpsTracksFolder = new Element("Folder", ns); //NON-NLS
334  CDATA cdataTrack = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-trackpoint.png"); //NON-NLS
335  Element hrefTrack = new Element("href", ns).addContent(cdataTrack); //NON-NLS
336  gpsTracksFolder.addContent(new Element("Icon", ns).addContent(hrefTrack)); //NON-NLS
337 
338  gpsAreasFolder = new Element("Folder", ns); //NON-NLS
339  CDATA cdataArea = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-area.png"); //NON-NLS
340  Element hrefArea = new Element("href", ns).addContent(cdataArea); //NON-NLS
341  gpsAreasFolder.addContent(new Element("Icon", ns).addContent(hrefArea)); //NON-NLS
342 
343  gpsExifMetadataFolder.addContent(new Element("name", ns).addContent("EXIF Metadata")); //NON-NLS
344  gpsBookmarksFolder.addContent(new Element("name", ns).addContent("GPS Bookmarks")); //NON-NLS
345  gpsLastKnownLocationFolder.addContent(new Element("name", ns).addContent("GPS Last Known Location")); //NON-NLS
346  gpsRouteFolder.addContent(new Element("name", ns).addContent("GPS Routes")); //NON-NLS
347  gpsSearchesFolder.addContent(new Element("name", ns).addContent("GPS Searches")); //NON-NLS
348  gpsTrackpointsFolder.addContent(new Element("name", ns).addContent("GPS Trackpoints")); //NON-NLS
349  gpsTracksFolder.addContent(new Element("name", ns).addContent("GPS Tracks")); //NON-NLS
350  gpsAreasFolder.addContent(new Element("name", ns).addContent("GPS Areas")); //NON-NLS
351 
352  document.addContent(gpsExifMetadataFolder);
353  document.addContent(gpsBookmarksFolder);
354  document.addContent(gpsLastKnownLocationFolder);
355  document.addContent(gpsRouteFolder);
356  document.addContent(gpsSearchesFolder);
357  document.addContent(gpsTrackpointsFolder);
358  document.addContent(gpsTracksFolder);
359  document.addContent(gpsAreasFolder);
360 
361  return kmlDocument;
362  }
363 
373  void addExifMetadataContent(List<Waypoint> points, String baseReportDirectory) throws IOException, TskCoreException {
374  for (Waypoint point : points) {
375  if(shouldFilterFromReport(point.getArtifact())) {
376  continue;
377  }
378 
379  Element mapPoint = makePoint(point);
380  if (mapPoint == null) {
381  return;
382  }
383 
384  AbstractFile abstractFile = point.getImage();
385  String details = getFormattedDetails(point, Bundle.Waypoint_EXIF_Display_String());
386 
387  Path path;
388  copyFileUsingStream(abstractFile, Paths.get(baseReportDirectory, abstractFile.getName()).toFile());
389  try {
390  path = Paths.get(removeLeadingImgAndVol(abstractFile.getUniquePath()));
391  } catch (TskCoreException ex) {
392  path = Paths.get(abstractFile.getParentPath(), abstractFile.getName());
393  }
394  if (path == null) {
395  path = Paths.get(abstractFile.getName());
396  }
397 
398  gpsExifMetadataFolder.addContent(makePlacemarkWithPicture(abstractFile.getName(), FeatureColor.RED, details, point.getTimestamp(), mapPoint, path, formattedCoordinates(point.getLatitude(), point.getLongitude())));
399  }
400  }
401 
411  void addLocationsToReport(SleuthkitCase skCase, String baseReportDir) throws GeoLocationDataException, IOException, TskCoreException {
412  if (waypointList == null) {
413  addExifMetadataContent(WaypointBuilder.getEXIFWaypoints(skCase), baseReportDir);
414  addWaypoints(WaypointBuilder.getBookmarkWaypoints(skCase), gpsBookmarksFolder, FeatureColor.BLUE, Bundle.Waypoint_Bookmark_Display_String());
415  addWaypoints(WaypointBuilder.getLastKnownWaypoints(skCase), gpsLastKnownLocationFolder, FeatureColor.PURPLE, Bundle.Waypoint_Last_Known_Display_String());
416  addWaypoints(WaypointBuilder.getSearchWaypoints(skCase), gpsSearchesFolder, FeatureColor.WHITE, Bundle.Waypoint_Search_Display_String());
417  addWaypoints(WaypointBuilder.getTrackpointWaypoints(skCase), gpsTrackpointsFolder, FeatureColor.WHITE, Bundle.Waypoint_Trackpoint_Display_String());
418  } else {
419  addExifMetadataContent(WaypointBuilder.getEXIFWaypoints(waypointList), baseReportDir);
420  addWaypoints(WaypointBuilder.getBookmarkWaypoints(waypointList), gpsBookmarksFolder, FeatureColor.BLUE, Bundle.Waypoint_Bookmark_Display_String());
421  addWaypoints(WaypointBuilder.getLastKnownWaypoints(waypointList), gpsLastKnownLocationFolder, FeatureColor.PURPLE, Bundle.Waypoint_Last_Known_Display_String());
422  addWaypoints(WaypointBuilder.getSearchWaypoints(waypointList), gpsSearchesFolder, FeatureColor.WHITE, Bundle.Waypoint_Search_Display_String());
423  addWaypoints(WaypointBuilder.getTrackpointWaypoints(waypointList), gpsTrackpointsFolder, FeatureColor.WHITE, Bundle.Waypoint_Trackpoint_Display_String());
424  }
425  }
426 
435  void addWaypoints(List<Waypoint> points, Element folder, FeatureColor waypointColor, String headerLabel) throws TskCoreException {
436  for (Waypoint point : points) {
437  if(shouldFilterFromReport(point.getArtifact())) {
438  continue;
439  }
440  addContent(folder, point.getLabel(), waypointColor, getFormattedDetails(point, headerLabel), point.getTimestamp(), makePoint(point), point.getLatitude(), point.getLongitude());
441  }
442  }
443 
456  void addContent(Element folder, String waypointLabel, FeatureColor waypointColor, String formattedDetails, Long timestamp, Element point, Double latitude, Double longitude) {
457  if (folder != null && point != null) {
458  String formattedCords = formattedCoordinates(latitude, longitude);
459  folder.addContent(makePlacemark(waypointLabel, waypointColor, formattedDetails, timestamp, point, formattedCords));
460  }
461  }
462 
470  void makeRoutes(SleuthkitCase skCase) throws GeoLocationDataException, TskCoreException {
471  List<Route> routes = null;
472 
473  if (waypointList == null) {
474  routes = Route.getRoutes(skCase);
475  } else {
476  routes = WaypointBuilder.getRoutes(waypointList);
477  }
478 
479  for (Route route : routes) {
480  if(shouldFilterFromReport(route.getArtifact())) {
481  continue;
482  }
483  addRouteToReport(route);
484  }
485  }
486 
492  private void addRouteToReport(Route route) {
493  List<Waypoint> routePoints = route.getRoute();
494  Waypoint start = null;
495  Waypoint end = null;
496  // This is hardcoded knowledge that there is only two points
497  // a start and end. In the long run it would be nice to
498  // support the idea of a route with multiple points. The Route
499  // class supports that idea. Would be nice to figure out how to support
500  // for report.
501  if (routePoints != null && routePoints.size() > 1) {
502  start = routePoints.get(0);
503  end = routePoints.get(1);
504  }
505 
506  if (start == null || end == null) {
507  return;
508  }
509 
510  Element reportRoute = makeLineString(start.getLatitude(), start.getLongitude(), end.getLatitude(), end.getLongitude());
511  Element startingPoint = makePoint(start.getLatitude(), start.getLongitude(), start.getAltitude());
512  Element endingPoint = makePoint(end.getLatitude(), end.getLongitude(), end.getAltitude());
513 
514  String formattedEnd = formattedCoordinates(end.getLatitude(), end.getLongitude());
515  String formattedStart = formattedCoordinates(start.getLatitude(), start.getLongitude());
516 
517  String formattedCoordinates = String.format("%s to %s", formattedStart, formattedEnd);
518 
519  if (reportRoute != null) {
520  gpsRouteFolder.addContent(makePlacemark(route.getLabel(), FeatureColor.GREEN, getFormattedDetails(route), route.getTimestamp(), reportRoute, formattedCoordinates)); //NON-NLS
521  }
522 
523  if (startingPoint != null) {
524  gpsRouteFolder.addContent(makePlacemark(start.getLabel(),
525  FeatureColor.GREEN, getFormattedDetails(start, Bundle.Waypoint_Route_Point_Display_String()),
526  start.getTimestamp(), startingPoint, formattedStart)); //NON-NLS
527  }
528 
529  if (endingPoint != null) {
530  gpsRouteFolder.addContent(makePlacemark(end.getLabel(),
532  getFormattedDetails(end, Bundle.Waypoint_Route_Point_Display_String()),
533  end.getTimestamp(), endingPoint, formattedEnd)); //NON-NLS
534  }
535  }
536 
545  boolean makeTracks(SleuthkitCase skCase) throws GeoLocationDataException, TskCoreException {
546  List<Track> tracks = null;
547  boolean successful = true;
548 
549  if (waypointList == null) {
550  GeoLocationParseResult<Track> result = Track.getTracks(skCase, null);
551  tracks = result.getItems();
552  successful = result.isSuccessfullyParsed();
553  } else {
554  tracks = WaypointBuilder.getTracks(waypointList);
555  }
556 
557  for (Track track : tracks) {
558  if(shouldFilterFromReport(track.getArtifact())) {
559  continue;
560  }
561  addTrackToReport(track);
562  }
563 
564  return successful;
565  }
566 
572  private void addTrackToReport(Track track) {
573  List<Waypoint> trackPoints = track.getPath();
574 
575  // Adding a folder with the track name so that all of the
576  // tracks waypoints with be grouped together.
577  Element trackFolder = new Element("Folder", ns); //NON-NLS
578  trackFolder.addContent(new Element("name", ns).addContent(track.getLabel())); //NON-NLS
579  gpsTracksFolder.addContent(trackFolder);
580 
581  for (Waypoint point : trackPoints) {
582  Element element = makePoint(point.getLatitude(), point.getLongitude(), point.getAltitude());
583  trackFolder.addContent(makePlacemark("",
584  FeatureColor.GREEN, getFormattedDetails(point, Bundle.Waypoint_Track_Point_Display_String()),
585  point.getTimestamp(), element, formattedCoordinates(point.getLatitude(), point.getLongitude()))); //NON-NLS
586  }
587  }
588 
597  boolean makeAreas(SleuthkitCase skCase) throws GeoLocationDataException, TskCoreException {
598  List<Area> areas;
599  boolean successful = true;
600 
601  if (waypointList == null) {
602  GeoLocationParseResult<Area> result = Area.getAreas(skCase, null);
603  areas = result.getItems();
604  successful = result.isSuccessfullyParsed();
605  } else {
606  areas = WaypointBuilder.getAreas(waypointList);
607  }
608 
609  for (Area area : areas) {
610  if(shouldFilterFromReport(area.getArtifact())) {
611  continue;
612  }
613  addAreaToReport(area);
614  }
615 
616  return successful;
617  }
618 
624  private void addAreaToReport(Area area) {
625  List<Waypoint> areaPoints = area.getPath();
626 
627  if (areaPoints.isEmpty()) {
628  return;
629  }
630 
631  // Adding a folder with the area name so that all of the
632  // area border points will be grouped together.
633  Element areaFolder = new Element("Folder", ns); //NON-NLS
634  areaFolder.addContent(new Element("name", ns).addContent(area.getLabel())); //NON-NLS
635  gpsAreasFolder.addContent(areaFolder);
636 
637  // Create a polygon using the waypoints
638  Element element = makePolygon(areaPoints);
639  Waypoint firstWp = areaPoints.get(0);
640  areaFolder.addContent(makePlacemark("",
641  FeatureColor.GREEN, getFormattedDetails(firstWp, Bundle.Waypoint_Area_Point_Display_String()),
642  firstWp.getTimestamp(), element, formattedCoordinates(firstWp.getLatitude(), firstWp.getLongitude()))); //NON-NLS
643  }
644 
652  private String getTimeStamp(long timeStamp) {
653  return kmlDateFormat.format(new java.util.Date(timeStamp * 1000));
654  }
655 
663  private Element makePoint(Waypoint point) {
664  return makePoint(point.getLatitude(), point.getLongitude(), point.getAltitude());
665  }
666 
678  private Element makePoint(Double latitude, Double longitude, Double altitude) {
679  if (latitude == null || longitude == null) {
680  return null;
681  }
682 
683  Element point = new Element("Point", ns); //NON-NLS
684 
685  // KML uses lon, lat. Deliberately reversed.1
686  Element coordinates = new Element("coordinates", ns).addContent(longitude + "," + latitude + "," + (altitude != null ? altitude : 0.0)); //NON-NLS
687 
688  if (altitude != null && altitude != 0) {
689  /*
690  * Though we are including a non-zero altitude, clamp it to the
691  * ground because inaccuracies from the GPS data can cause the
692  * terrain to occlude points when zoomed in otherwise. Show the
693  * altitude, but keep the point clamped to the ground. We may change
694  * this later for flying GPS sensors.
695  */
696  Element altitudeMode = new Element("altitudeMode", ns).addContent("clampToGround"); //NON-NLS
697  point.addContent(altitudeMode);
698  }
699  point.addContent(coordinates);
700 
701  return point;
702  }
703 
720  private Element makeLineString(Double startLatitude, Double startLongitude, Double stopLatitude, Double stopLongitude) {
721  if (startLatitude == null || startLongitude == null || stopLatitude == null || stopLongitude == null) {
722  return null;
723  }
724 
725  Element lineString = new Element("LineString", ns); //NON-NLS
726  lineString.addContent(new Element("extrude", ns).addContent("1")); //NON-NLS
727  lineString.addContent(new Element("tessellate", ns).addContent("1")); //NON-NLS
728  lineString.addContent(new Element("altitudeMode", ns).addContent("clampToGround")); //NON-NLS
729  // KML uses lon, lat. Deliberately reversed.
730  lineString.addContent(new Element("coordinates", ns).addContent(
731  startLongitude + "," + startLatitude + ",0.0,"
732  + stopLongitude + "," + stopLatitude + ",0.0")); //NON-NLS
733  return lineString;
734  }
735 
743  private Element makePolygon(List<Waypoint> waypoints) {
744 
745  Element polygon = new Element("Polygon", ns); //NON-NLS
746 
747  Element altitudeMode = new Element("altitudeMode", ns).addContent("clampToGround"); //NON-NLS
748  polygon.addContent(altitudeMode);
749 
750  // KML uses lon, lat. Deliberately reversed.
751  Element coordinates = new Element("coordinates", ns);
752  for (Waypoint wp : waypoints) {
753  coordinates.addContent(wp.getLongitude() + "," + wp.getLatitude() + ",0 "); //NON-NLS
754  }
755  // Add the first one again
756  coordinates.addContent(waypoints.get(0).getLongitude() + "," + waypoints.get(0).getLatitude() + ",0 "); //NON-NLS
757 
758  Element linearRing = new Element("LinearRing", ns).addContent(coordinates);
759  Element outerBoundary = new Element("outerBoundaryIs", ns).addContent(linearRing);
760  polygon.addContent(outerBoundary);
761 
762  return polygon;
763  }
764 
779  private Element makePlacemark(String name, FeatureColor color, String description, Long timestamp, Element feature, String coordinates) {
780  Element placemark = new Element("Placemark", ns); //NON-NLS
781  if (name != null && !name.isEmpty()) {
782  placemark.addContent(new Element("name", ns).addContent(name)); //NON-NLS
783  } else if (timestamp != null) {
784  placemark.addContent(new Element("name", ns).addContent(getTimeStamp(timestamp))); //NON-NLS
785  } else {
786  placemark.addContent(new Element("name", ns).addContent("")); //NON-NLS
787  }
788  placemark.addContent(new Element("styleUrl", ns).addContent(color.getColor())); //NON-NLS
789  placemark.addContent(new Element("description", ns).addContent(description)); //NON-NLS
790  if (timestamp != null) {
791  Element time = new Element("TimeStamp", ns); //NON-NLS
792  time.addContent(new Element("when", ns).addContent(getTimeStamp(timestamp))); //NON-NLS
793  placemark.addContent(time);
794  }
795  placemark.addContent(feature);
796  if (coordinates != null && !coordinates.isEmpty()) {
797  placemark.addContent(new Element("snippet", ns).addContent(coordinates)); //NON-NLS
798  }
799  return placemark;
800  }
801 
817  private Element makePlacemarkWithPicture(String name, FeatureColor color, String description, Long timestamp, Element feature, Path path, String coordinates) {
818  Element placemark = new Element("Placemark", ns); //NON-NLS
819  Element desc = new Element("description", ns); //NON-NLS
820  if (name != null && !name.isEmpty()) {
821  placemark.addContent(new Element("name", ns).addContent(name)); //NON-NLS
822  String image = "<img src='" + name + "' width='400'/>"; //NON-NLS
823  desc.addContent(image);
824  }
825  placemark.addContent(new Element("styleUrl", ns).addContent(color.getColor())); //NON-NLS
826  if (path != null) {
827  String pathAsString = path.toString();
828  if (pathAsString != null && !pathAsString.isEmpty()) {
829  desc.addContent(description + "<b>Source Path:</b> " + pathAsString);
830  }
831  }
832  placemark.addContent(desc);
833 
834  if (timestamp != null) {
835  Element time = new Element("TimeStamp", ns); //NON-NLS
836  time.addContent(new Element("when", ns).addContent(getTimeStamp(timestamp))); //NON-NLS
837  placemark.addContent(time);
838  }
839  placemark.addContent(feature);
840  if (coordinates != null && !coordinates.isEmpty()) {
841  placemark.addContent(new Element("snippet", ns).addContent(coordinates)); //NON-NLS
842  }
843  return placemark;
844  }
845 
856  private void copyFileUsingStream(AbstractFile inputFile, File outputFile) throws ReadContentInputStreamException, IOException {
857  byte[] buffer = new byte[65536];
858  int length;
859  outputFile.createNewFile();
860  try (InputStream is = new ReadContentInputStream(inputFile);
861  OutputStream os = new FileOutputStream(outputFile)) {
862  while ((length = is.read(buffer)) != -1) {
863  os.write(buffer, 0, length);
864  }
865  }
866  }
867 
868  @Override
869  public String getName() {
870  String name = NbBundle.getMessage(this.getClass(), "ReportKML.getName.text");
871  return name;
872  }
873 
874  @Override
875  public String getRelativeFilePath() {
876  return "ReportKML.kml"; //NON-NLS
877  }
878 
879  @Override
880  public String getDescription() {
881  String desc = NbBundle.getMessage(this.getClass(), "ReportKML.getDesc.text");
882  return desc;
883  }
884 
885  @Override
886  public JPanel getConfigurationPanel() {
887  return null; // No configuration panel
888  }
889 
900  private static String removeLeadingImgAndVol(String uniquePath) {
901  // split the path into parts
902  String[] pathSegments = uniquePath.replaceFirst("^/*", "").split("/"); //NON-NLS
903 
904  // Replace image/volume name if they exist in specific entries
905  if (pathSegments.length > 0) {
906  pathSegments[0] = pathSegments[0].replaceFirst("^img_", ""); //NON-NLS
907  }
908  if (pathSegments.length > 1) {
909  pathSegments[1] = pathSegments[1].replaceFirst("^vol_", ""); //NON-NLS
910  }
911 
912  // Assemble the path
913  StringBuilder strbuf = new StringBuilder();
914  for (String segment : pathSegments) {
915  if (!segment.isEmpty()) {
916  strbuf.append("/").append(segment);
917  }
918  }
919  return strbuf.toString();
920  }
921 
930  private String getFormattedDetails(Waypoint point, String header) {
931  StringBuilder result = new StringBuilder(); //NON-NLS
932  result.append(String.format("<h3>%s</h3>", header))
933  .append(formatAttribute("Name", point.getLabel()));
934 
935  Long timestamp = point.getTimestamp();
936  if (timestamp != null) {
937  result.append(formatAttribute("Timestamp", getTimeStamp(timestamp)));
938  }
939 
940  result.append(formatAttribute("Latitude", point.getLatitude().toString()))
941  .append(formatAttribute("Longitude", point.getLongitude().toString()));
942 
943  if (point.getAltitude() != null) {
944  result.append(formatAttribute("Altitude", point.getAltitude().toString()));
945  }
946 
947  List<Waypoint.Property> list = point.getOtherProperties();
948  for (Waypoint.Property prop : list) {
949  String value = prop.getValue();
950  if (value != null && !value.isEmpty()) {
951  result.append(formatAttribute(prop.getDisplayName(), value));
952  }
953  }
954 
955  return result.toString();
956  }
957 
966  private String formatAttribute(String title, String value) {
967  return String.format(HTML_PROP_FORMAT, title, value);
968  }
969 
977  private String getFormattedDetails(Route route) {
978  List<Waypoint> points = route.getRoute();
979  StringBuilder result = new StringBuilder(); //NON-NLS
980 
981  result.append(String.format("<h3>%s</h3>", Bundle.Route_Details_Header()))
982  .append(formatAttribute("Name", route.getLabel()));
983 
984  Long timestamp = route.getTimestamp();
985  if (timestamp != null) {
986  result.append(formatAttribute("Timestamp", getTimeStamp(timestamp)));
987  }
988 
989  if (points.size() > 1) {
990  Waypoint start = points.get(0);
991  Waypoint end = points.get(1);
992 
993  result.append(formatAttribute("Start Latitude", start.getLatitude().toString()))
994  .append(formatAttribute("Start Longitude", start.getLongitude().toString()));
995 
996  Double altitude = start.getAltitude();
997  if (altitude != null) {
998  result.append(formatAttribute("Start Altitude", altitude.toString()));
999  }
1000 
1001  result.append(formatAttribute("End Latitude", end.getLatitude().toString()))
1002  .append(formatAttribute("End Longitude", end.getLongitude().toString()));
1003 
1004  altitude = end.getAltitude();
1005  if (altitude != null) {
1006  result.append(formatAttribute("End Altitude", altitude.toString()));
1007  }
1008  }
1009 
1010  List<Waypoint.Property> list = route.getOtherProperties();
1011  for (Waypoint.Property prop : list) {
1012  String value = prop.getValue();
1013  if (value != null && !value.isEmpty()) {
1014  result.append(formatAttribute(prop.getDisplayName(), value));
1015  }
1016  }
1017 
1018  return result.toString();
1019  }
1020 
1029  private String formattedCoordinates(Double latitude, Double longitude) {
1030  if (latitude == null || longitude == null) {
1031  return "";
1032  }
1033 
1034  return String.format("%.2f, %.2f", latitude, longitude);
1035  }
1036 
1040  private boolean shouldFilterFromReport(Content content) throws TskCoreException {
1041  if(this.settings.getSelectedDataSources() == null) {
1042  return false;
1043  }
1044  long dataSourceId = content.getDataSource().getId();
1045  return !this.settings.getSelectedDataSources().contains(dataSourceId);
1046  }
1047 }
List< Content > getDataSources()
Definition: Case.java:1696
void copyFileUsingStream(AbstractFile inputFile, File outputFile)
Definition: KMLReport.java:856
Element makeLineString(Double startLatitude, Double startLongitude, Double stopLatitude, Double stopLongitude)
Definition: KMLReport.java:720
static synchronized IngestManager getInstance()
void generateReport(GeneralReportSettings settings, ReportProgressPanel progressPanel)
Definition: KMLReport.java:176
static GeoLocationParseResult< Track > getTracks(SleuthkitCase skCase, List<?extends Content > sourceList)
Definition: GeoPath.java:80
Element makePlacemarkWithPicture(String name, FeatureColor color, String description, Long timestamp, Element feature, Path path, String coordinates)
Definition: KMLReport.java:817
Element makePoint(Double latitude, Double longitude, Double altitude)
Definition: KMLReport.java:678
void addReport(String localPath, String srcModuleName, String reportName)
Definition: Case.java:1919
Element makePolygon(List< Waypoint > waypoints)
Definition: KMLReport.java:743
static String removeLeadingImgAndVol(String uniquePath)
Definition: KMLReport.java:900
static synchronized KMLReport getDefault()
Definition: KMLReport.java:117
List< Waypoint.Property > getOtherProperties()
Definition: Route.java:85
void generateReport(String baseReportDir, ReportProgressPanel progressPanel, List< Waypoint > waypointList)
Definition: KMLReport.java:163
String formatAttribute(String title, String value)
Definition: KMLReport.java:966
Element makePlacemark(String name, FeatureColor color, String description, Long timestamp, Element feature, String coordinates)
Definition: KMLReport.java:779
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
static GeoLocationParseResult< Area > getAreas(SleuthkitCase skCase, List<?extends Content > sourceList)
Definition: GeoPath.java:115
void setSelectedDataSources(List< Long > selectedDataSources)
String getFormattedDetails(Waypoint point, String header)
Definition: KMLReport.java:930
String formattedCoordinates(Double latitude, Double longitude)

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.