Autopsy  4.16.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;
57 import org.sleuthkit.datamodel.AbstractFile;
58 import org.sleuthkit.datamodel.Content;
59 import org.sleuthkit.datamodel.ReadContentInputStream;
60 import org.sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException;
61 import org.sleuthkit.datamodel.SleuthkitCase;
62 import org.sleuthkit.datamodel.TskCoreException;
63 
67 public final class KMLReport implements GeneralReportModule {
68 
69  private static final Logger logger = Logger.getLogger(KMLReport.class.getName());
70  private static final String KML_STYLE_FILE = "style.kml";
71  private static final String REPORT_KML = "ReportKML.kml";
72  private static final String STYLESHEETS_PATH = "/org/sleuthkit/autopsy/report/stylesheets/";
73  private static KMLReport instance = null;
74  private Case currentCase;
75  private SleuthkitCase skCase;
76  private final SimpleDateFormat kmlDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");
77  private Namespace ns;
78  private final static String HTML_PROP_FORMAT = "<b>%s: </b>%s<br>";
79 
80  private Element gpsExifMetadataFolder;
81  private Element gpsBookmarksFolder;
82  private Element gpsLastKnownLocationFolder;
83  private Element gpsRouteFolder;
84  private Element gpsSearchesFolder;
85  private Element gpsTrackpointsFolder;
86  private Element gpsTracksFolder;
87 
89 
90  private List<Waypoint> waypointList = null;
91 
92  private enum FeatureColor {
93  RED("style.kml#redFeature"),
94  GREEN("style.kml#greenFeature"),
95  BLUE("style.kml#blueFeature"),
96  PURPLE("style.kml#purpleFeature"),
97  WHITE("style.kml#whiteFeature"),
98  YELLOW("style.kml#yellowFeature");
99  private final String color;
100 
101  FeatureColor(String color) {
102  this.color = color;
103  }
104 
105  String getColor() {
106  return this.color;
107  }
108  }
109 
110  // Hidden constructor for the report
111  private KMLReport() {
112  }
113 
114  // Get the default implementation of this report
115  public static synchronized KMLReport getDefault() {
116  if (instance == null) {
117  instance = new KMLReport();
118  }
119  return instance;
120  }
121 
129  @Messages({
130  "KMLReport.failedToCompleteReport=Failed to complete report.",
131  "KMLReport.partialFailure=There was an error creating the report. Some items were not exported.",
132  "KMLReport.unableToExtractPhotos=Could not extract photo information.",
133  "KMLReport.exifPhotoError=Could not extract photos with EXIF metadata.",
134  "KMLReport.bookmarkError=Could not extract Bookmark information.",
135  "KMLReport.gpsBookmarkError=Could not get GPS Bookmarks from database.",
136  "KMLReport.locationError=Could not extract Last Known Location information.",
137  "KMLReport.locationDatabaseError=Could not get GPS Last Known Location from database.",
138  "KMLReport.gpsRouteError=Could not extract GPS Route information.",
139  "KMLReport.gpsRouteDatabaseError=Could not get GPS Routes from database.",
140  "KMLReport.gpsSearchDatabaseError=Could not get GPS Searches from database.",
141  "KMLReport.trackpointError=Could not extract Trackpoint information.",
142  "KMLReport.trackpointDatabaseError=Could not get GPS Trackpoints from database.",
143  "KMLReport.stylesheetError=Error placing KML stylesheet. The .KML file will not function properly.",
144  "KMLReport.kmlFileWriteError=Could not write the KML file.",
145  "# {0} - filePath",
146  "KMLReport.errorGeneratingReport=Error adding {0} to case as a report.",
147  "KMLReport.unableToOpenCase=Exception while getting open case.",
148  "Waypoint_Bookmark_Display_String=GPS Bookmark",
149  "Waypoint_Last_Known_Display_String=GPS Last Known Location",
150  "Waypoint_EXIF_Display_String=EXIF Metadata With Location",
151  "Waypoint_Route_Point_Display_String=GPS Individual Route Point",
152  "Waypoint_Search_Display_String=GPS Search",
153  "Waypoint_Trackpoint_Display_String=GPS Trackpoint",
154  "Waypoint_Track_Display_String=GPS Track",
155  "Route_Details_Header=GPS Route",
156  "ReportBodyFile.ingestWarning.text=Ingest Warning message",
157  "Waypoint_Track_Point_Display_String=GPS Individual Track Point"
158  })
159 
160  public void generateReport(String baseReportDir, ReportProgressPanel progressPanel, List<Waypoint> waypointList) {
161  this.waypointList = waypointList;
162  GeneralReportSettings reportSettings = new GeneralReportSettings();
163  reportSettings.setReportDirectoryPath(baseReportDir);
164  generateReport(reportSettings, progressPanel);
165  }
166 
167  @Override
168  public boolean supportsDataSourceSelection() {
169  return true;
170  }
171 
172  @Override
173  public void generateReport(GeneralReportSettings settings, ReportProgressPanel progressPanel) {
174  try {
175  currentCase = Case.getCurrentCaseThrows();
176  } catch (NoCurrentCaseException ex) {
177  logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS
178  return;
179  }
180 
181  if(settings.getSelectedDataSources() == null) {
182  // Process all data sources if the list is null.
183  try {
184  List<Long> selectedDataSources = currentCase.getDataSources()
185  .stream()
186  .map(Content::getId)
187  .collect(Collectors.toList());
188  settings.setSelectedDataSources(selectedDataSources);
189  } catch (TskCoreException ex) {
190  logger.log(Level.SEVERE, "Could not get the datasources from the case", ex);
191  return;
192  }
193  }
194 
195  String baseReportDir = settings.getReportDirectoryPath();
196  this.settings = settings;
197  // Start the progress bar and setup the report
198  progressPanel.setIndeterminate(true);
199  progressPanel.start();
200  progressPanel.updateStatusLabel(NbBundle.getMessage(this.getClass(), "ReportKML.progress.querying"));
201  String kmlFileFullPath = baseReportDir + REPORT_KML; //NON-NLS
202  String errorMessage = "";
203 
204  skCase = currentCase.getSleuthkitCase();
205 
206  progressPanel.updateStatusLabel(NbBundle.getMessage(this.getClass(), "ReportKML.progress.loading"));
207 
208  Document kmlDocument = setupReportDocument();
209 
211 
212  try {
213  makeRoutes(skCase);
214  boolean entirelySuccessful = makeTracks(skCase);
215  if (!entirelySuccessful) {
217  errorMessage = Bundle.KMLReport_partialFailure();
218  }
219 
220  addLocationsToReport(skCase, baseReportDir);
221  } catch (GeoLocationDataException | IOException | TskCoreException ex) {
222  errorMessage = Bundle.KMLReport_failedToCompleteReport();
223  logger.log(Level.SEVERE, errorMessage, ex); //NON-NLS
225  }
226 
227  // Copy the style sheet
228  try {
229  InputStream input = getClass().getResourceAsStream(STYLESHEETS_PATH + KML_STYLE_FILE); // Preserve slash direction
230  OutputStream output = new FileOutputStream(baseReportDir + KML_STYLE_FILE); // Preserve slash direction
231  FileUtil.copy(input, output);
232  } catch (IOException ex) {
233  errorMessage = Bundle.KMLReport_stylesheetError();
234  logger.log(Level.SEVERE, errorMessage, ex); //NON-NLS
236  }
237 
238  try (FileOutputStream writer = new FileOutputStream(kmlFileFullPath)) {
239  XMLOutputter outputter = new XMLOutputter(Format.getPrettyFormat());
240  outputter.output(kmlDocument, writer);
241  String prependedStatus = "";
242  if (result == ReportProgressPanel.ReportStatus.ERROR) {
243  prependedStatus = "Incomplete ";
244  }
245  Case.getCurrentCaseThrows().addReport(kmlFileFullPath,
246  NbBundle.getMessage(this.getClass(), "ReportKML.genReport.srcModuleName.text"),
247  prependedStatus + NbBundle.getMessage(this.getClass(), "ReportKML.genReport.reportName"));
248  } catch (IOException ex) {
249  errorMessage = Bundle.KMLReport_kmlFileWriteError();
250  logger.log(Level.SEVERE, errorMessage, ex); //NON-NLS
251  progressPanel.complete(ReportProgressPanel.ReportStatus.ERROR, errorMessage);
252  } catch (TskCoreException ex) {
253  errorMessage = Bundle.KMLReport_errorGeneratingReport(kmlFileFullPath);
254  logger.log(Level.SEVERE, errorMessage, ex);
256  } catch (NoCurrentCaseException ex) {
257  errorMessage = Bundle.KMLReport_unableToOpenCase();
258  logger.log(Level.SEVERE, errorMessage, ex);
260  }
261 
262  progressPanel.complete(result, errorMessage);
263  }
264 
270  private Document setupReportDocument() {
271  ns = Namespace.getNamespace("", "http://www.opengis.net/kml/2.2"); //NON-NLS
272 
273  Element kml = new Element("kml", ns); //NON-NLS
274  kml.addNamespaceDeclaration(Namespace.getNamespace("gx", "http://www.google.com/kml/ext/2.2")); //NON-NLS
275  kml.addNamespaceDeclaration(Namespace.getNamespace("kml", "http://www.opengis.net/kml/2.2")); //NON-NLS
276  kml.addNamespaceDeclaration(Namespace.getNamespace("atom", "http://www.w3.org/2005/Atom")); //NON-NLS
277  Document kmlDocument = new Document(kml);
278 
279  Element document = new Element("Document", ns); //NON-NLS
280  kml.addContent(document);
281 
282  Element name = new Element("name", ns); //NON-NLS
283  ReportBranding rb = new ReportBranding();
284  name.setText(rb.getReportTitle() + " KML"); //NON-NLS
285  document.addContent(name);
286 
287  // Check if ingest has finished
289  Element ingestwarning = new Element("snippet", ns); //NON-NLS
290  ingestwarning.addContent(NbBundle.getMessage(this.getClass(), "ReportBodyFile.ingestWarning.text")); //NON-NLS
291  document.addContent(ingestwarning);
292  }
293 
294  // Create folder structure
295  gpsExifMetadataFolder = new Element("Folder", ns); //NON-NLS
296  CDATA cdataExifMetadataFolder = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/camera-icon-16.png"); //NON-NLS
297  Element hrefExifMetadata = new Element("href", ns).addContent(cdataExifMetadataFolder); //NON-NLS
298  gpsExifMetadataFolder.addContent(new Element("Icon", ns).addContent(hrefExifMetadata)); //NON-NLS
299 
300  gpsBookmarksFolder = new Element("Folder", ns); //NON-NLS
301  CDATA cdataBookmarks = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gpsfav.png"); //NON-NLS
302  Element hrefBookmarks = new Element("href", ns).addContent(cdataBookmarks); //NON-NLS
303  gpsBookmarksFolder.addContent(new Element("Icon", ns).addContent(hrefBookmarks)); //NON-NLS
304 
305  gpsLastKnownLocationFolder = new Element("Folder", ns); //NON-NLS
306  CDATA cdataLastKnownLocation = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-lastlocation.png"); //NON-NLS
307  Element hrefLastKnownLocation = new Element("href", ns).addContent(cdataLastKnownLocation); //NON-NLS
308  gpsLastKnownLocationFolder.addContent(new Element("Icon", ns).addContent(hrefLastKnownLocation)); //NON-NLS
309 
310  gpsRouteFolder = new Element("Folder", ns); //NON-NLS
311  CDATA cdataRoute = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-trackpoint.png"); //NON-NLS
312  Element hrefRoute = new Element("href", ns).addContent(cdataRoute); //NON-NLS
313  gpsRouteFolder.addContent(new Element("Icon", ns).addContent(hrefRoute)); //NON-NLS
314 
315  gpsSearchesFolder = new Element("Folder", ns); //NON-NLS
316  CDATA cdataSearches = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-search.png"); //NON-NLS
317  Element hrefSearches = new Element("href", ns).addContent(cdataSearches); //NON-NLS
318  gpsSearchesFolder.addContent(new Element("Icon", ns).addContent(hrefSearches)); //NON-NLS
319 
320  gpsTrackpointsFolder = new Element("Folder", ns); //NON-NLS
321  CDATA cdataTrackpoints = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-trackpoint.png"); //NON-NLS
322  Element hrefTrackpoints = new Element("href", ns).addContent(cdataTrackpoints); //NON-NLS
323  gpsTrackpointsFolder.addContent(new Element("Icon", ns).addContent(hrefTrackpoints)); //NON-NLS
324 
325  gpsTracksFolder = new Element("Folder", ns); //NON-NLS
326  CDATA cdataTrack = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-trackpoint.png"); //NON-NLS
327  Element hrefTrack = new Element("href", ns).addContent(cdataTrack); //NON-NLS
328  gpsTracksFolder.addContent(new Element("Icon", ns).addContent(hrefTrack)); //NON-NLS
329 
330  gpsExifMetadataFolder.addContent(new Element("name", ns).addContent("EXIF Metadata")); //NON-NLS
331  gpsBookmarksFolder.addContent(new Element("name", ns).addContent("GPS Bookmarks")); //NON-NLS
332  gpsLastKnownLocationFolder.addContent(new Element("name", ns).addContent("GPS Last Known Location")); //NON-NLS
333  gpsRouteFolder.addContent(new Element("name", ns).addContent("GPS Routes")); //NON-NLS
334  gpsSearchesFolder.addContent(new Element("name", ns).addContent("GPS Searches")); //NON-NLS
335  gpsTrackpointsFolder.addContent(new Element("name", ns).addContent("GPS Trackpoints")); //NON-NLS
336  gpsTracksFolder.addContent(new Element("name", ns).addContent("GPS Tracks")); //NON-NLS
337 
338  document.addContent(gpsExifMetadataFolder);
339  document.addContent(gpsBookmarksFolder);
340  document.addContent(gpsLastKnownLocationFolder);
341  document.addContent(gpsRouteFolder);
342  document.addContent(gpsSearchesFolder);
343  document.addContent(gpsTrackpointsFolder);
344  document.addContent(gpsTracksFolder);
345 
346  return kmlDocument;
347  }
348 
358  void addExifMetadataContent(List<Waypoint> points, String baseReportDirectory) throws IOException, TskCoreException {
359  for (Waypoint point : points) {
360  if(shouldFilterFromReport(point.getArtifact())) {
361  continue;
362  }
363 
364  Element mapPoint = makePoint(point);
365  if (mapPoint == null) {
366  return;
367  }
368 
369  AbstractFile abstractFile = point.getImage();
370  String details = getFormattedDetails(point, Bundle.Waypoint_EXIF_Display_String());
371 
372  Path path;
373  copyFileUsingStream(abstractFile, Paths.get(baseReportDirectory, abstractFile.getName()).toFile());
374  try {
375  path = Paths.get(removeLeadingImgAndVol(abstractFile.getUniquePath()));
376  } catch (TskCoreException ex) {
377  path = Paths.get(abstractFile.getParentPath(), abstractFile.getName());
378  }
379  if (path == null) {
380  path = Paths.get(abstractFile.getName());
381  }
382 
383  gpsExifMetadataFolder.addContent(makePlacemarkWithPicture(abstractFile.getName(), FeatureColor.RED, details, point.getTimestamp(), mapPoint, path, formattedCoordinates(point.getLatitude(), point.getLongitude())));
384  }
385  }
386 
396  void addLocationsToReport(SleuthkitCase skCase, String baseReportDir) throws GeoLocationDataException, IOException, TskCoreException {
397  if (waypointList == null) {
398  addExifMetadataContent(WaypointBuilder.getEXIFWaypoints(skCase), baseReportDir);
399  addWaypoints(WaypointBuilder.getBookmarkWaypoints(skCase), gpsBookmarksFolder, FeatureColor.BLUE, Bundle.Waypoint_Bookmark_Display_String());
400  addWaypoints(WaypointBuilder.getLastKnownWaypoints(skCase), gpsLastKnownLocationFolder, FeatureColor.PURPLE, Bundle.Waypoint_Last_Known_Display_String());
401  addWaypoints(WaypointBuilder.getSearchWaypoints(skCase), gpsSearchesFolder, FeatureColor.WHITE, Bundle.Waypoint_Search_Display_String());
402  addWaypoints(WaypointBuilder.getTrackpointWaypoints(skCase), gpsTrackpointsFolder, FeatureColor.WHITE, Bundle.Waypoint_Trackpoint_Display_String());
403  } else {
404  addExifMetadataContent(WaypointBuilder.getEXIFWaypoints(waypointList), baseReportDir);
405  addWaypoints(WaypointBuilder.getBookmarkWaypoints(waypointList), gpsBookmarksFolder, FeatureColor.BLUE, Bundle.Waypoint_Bookmark_Display_String());
406  addWaypoints(WaypointBuilder.getLastKnownWaypoints(waypointList), gpsLastKnownLocationFolder, FeatureColor.PURPLE, Bundle.Waypoint_Last_Known_Display_String());
407  addWaypoints(WaypointBuilder.getSearchWaypoints(waypointList), gpsSearchesFolder, FeatureColor.WHITE, Bundle.Waypoint_Search_Display_String());
408  addWaypoints(WaypointBuilder.getTrackpointWaypoints(waypointList), gpsTrackpointsFolder, FeatureColor.WHITE, Bundle.Waypoint_Trackpoint_Display_String());
409  }
410  }
411 
420  void addWaypoints(List<Waypoint> points, Element folder, FeatureColor waypointColor, String headerLabel) throws TskCoreException {
421  for (Waypoint point : points) {
422  if(shouldFilterFromReport(point.getArtifact())) {
423  continue;
424  }
425  addContent(folder, point.getLabel(), waypointColor, getFormattedDetails(point, headerLabel), point.getTimestamp(), makePoint(point), point.getLatitude(), point.getLongitude());
426  }
427  }
428 
441  void addContent(Element folder, String waypointLabel, FeatureColor waypointColor, String formattedDetails, Long timestamp, Element point, Double latitude, Double longitude) {
442  if (folder != null && point != null) {
443  String formattedCords = formattedCoordinates(latitude, longitude);
444  folder.addContent(makePlacemark(waypointLabel, waypointColor, formattedDetails, timestamp, point, formattedCords));
445  }
446  }
447 
455  void makeRoutes(SleuthkitCase skCase) throws GeoLocationDataException, TskCoreException {
456  List<Route> routes = null;
457 
458  if (waypointList == null) {
459  routes = Route.getRoutes(skCase);
460  } else {
461  routes = WaypointBuilder.getRoutes(waypointList);
462  }
463 
464  for (Route route : routes) {
465  if(shouldFilterFromReport(route.getArtifact())) {
466  continue;
467  }
468  addRouteToReport(route);
469  }
470  }
471 
477  private void addRouteToReport(Route route) {
478  List<Waypoint> routePoints = route.getRoute();
479  Waypoint start = null;
480  Waypoint end = null;
481  // This is hardcoded knowledge that there is only two points
482  // a start and end. In the long run it would be nice to
483  // support the idea of a route with multiple points. The Route
484  // class supports that idea. Would be nice to figure out how to support
485  // for report.
486  if (routePoints != null && routePoints.size() > 1) {
487  start = routePoints.get(0);
488  end = routePoints.get(1);
489  }
490 
491  if (start == null || end == null) {
492  return;
493  }
494 
495  Element reportRoute = makeLineString(start.getLatitude(), start.getLongitude(), end.getLatitude(), end.getLongitude());
496  Element startingPoint = makePoint(start.getLatitude(), start.getLongitude(), start.getAltitude());
497  Element endingPoint = makePoint(end.getLatitude(), end.getLongitude(), end.getAltitude());
498 
499  String formattedEnd = formattedCoordinates(end.getLatitude(), end.getLongitude());
500  String formattedStart = formattedCoordinates(start.getLatitude(), start.getLongitude());
501 
502  String formattedCoordinates = String.format("%s to %s", formattedStart, formattedEnd);
503 
504  if (reportRoute != null) {
505  gpsRouteFolder.addContent(makePlacemark(route.getLabel(), FeatureColor.GREEN, getFormattedDetails(route), route.getTimestamp(), reportRoute, formattedCoordinates)); //NON-NLS
506  }
507 
508  if (startingPoint != null) {
509  gpsRouteFolder.addContent(makePlacemark(start.getLabel(),
510  FeatureColor.GREEN, getFormattedDetails(start, Bundle.Waypoint_Route_Point_Display_String()),
511  start.getTimestamp(), startingPoint, formattedStart)); //NON-NLS
512  }
513 
514  if (endingPoint != null) {
515  gpsRouteFolder.addContent(makePlacemark(end.getLabel(),
517  getFormattedDetails(end, Bundle.Waypoint_Route_Point_Display_String()),
518  end.getTimestamp(), endingPoint, formattedEnd)); //NON-NLS
519  }
520  }
521 
530  boolean makeTracks(SleuthkitCase skCase) throws GeoLocationDataException, TskCoreException {
531  List<Track> tracks = null;
532  boolean successful = true;
533 
534  if (waypointList == null) {
535  GeoLocationParseResult<Track> result = Track.getTracks(skCase, null);
536  tracks = result.getItems();
537  successful = result.isSuccessfullyParsed();
538  } else {
539  tracks = WaypointBuilder.getTracks(waypointList);
540  }
541 
542  for (Track track : tracks) {
543  if(shouldFilterFromReport(track.getArtifact())) {
544  continue;
545  }
546  addTrackToReport(track);
547  }
548 
549  return successful;
550  }
551 
557  private void addTrackToReport(Track track) {
558  List<Waypoint> trackPoints = track.getPath();
559 
560  // Adding a folder with the track name so that all of the
561  // tracks waypoints with be grouped together.
562  Element trackFolder = new Element("Folder", ns); //NON-NLS
563  trackFolder.addContent(new Element("name", ns).addContent(track.getLabel())); //NON-NLS
564  gpsTracksFolder.addContent(trackFolder);
565 
566  for (Waypoint point : trackPoints) {
567  Element element = makePoint(point.getLatitude(), point.getLongitude(), point.getAltitude());
568  trackFolder.addContent(makePlacemark("",
569  FeatureColor.GREEN, getFormattedDetails(point, Bundle.Waypoint_Track_Point_Display_String()),
570  point.getTimestamp(), element, formattedCoordinates(point.getLatitude(), point.getLongitude()))); //NON-NLS
571  }
572  }
573 
581  private String getTimeStamp(long timeStamp) {
582  return kmlDateFormat.format(new java.util.Date(timeStamp * 1000));
583  }
584 
592  private Element makePoint(Waypoint point) {
593  return makePoint(point.getLatitude(), point.getLongitude(), point.getAltitude());
594  }
595 
607  private Element makePoint(Double latitude, Double longitude, Double altitude) {
608  if (latitude == null || longitude == null) {
609  return null;
610  }
611 
612  Element point = new Element("Point", ns); //NON-NLS
613 
614  // KML uses lon, lat. Deliberately reversed.1
615  Element coordinates = new Element("coordinates", ns).addContent(longitude + "," + latitude + "," + (altitude != null ? altitude : 0.0)); //NON-NLS
616 
617  if (altitude != null && altitude != 0) {
618  /*
619  * Though we are including a non-zero altitude, clamp it to the
620  * ground because inaccuracies from the GPS data can cause the
621  * terrain to occlude points when zoomed in otherwise. Show the
622  * altitude, but keep the point clamped to the ground. We may change
623  * this later for flying GPS sensors.
624  */
625  Element altitudeMode = new Element("altitudeMode", ns).addContent("clampToGround"); //NON-NLS
626  point.addContent(altitudeMode);
627  }
628  point.addContent(coordinates);
629 
630  return point;
631  }
632 
649  private Element makeLineString(Double startLatitude, Double startLongitude, Double stopLatitude, Double stopLongitude) {
650  if (startLatitude == null || startLongitude == null || stopLatitude == null || stopLongitude == null) {
651  return null;
652  }
653 
654  Element lineString = new Element("LineString", ns); //NON-NLS
655  lineString.addContent(new Element("extrude", ns).addContent("1")); //NON-NLS
656  lineString.addContent(new Element("tessellate", ns).addContent("1")); //NON-NLS
657  lineString.addContent(new Element("altitudeMode", ns).addContent("clampToGround")); //NON-NLS
658  // KML uses lon, lat. Deliberately reversed.
659  lineString.addContent(new Element("coordinates", ns).addContent(
660  startLongitude + "," + startLatitude + ",0.0,"
661  + stopLongitude + "," + stopLatitude + ",0.0")); //NON-NLS
662  return lineString;
663  }
664 
679  private Element makePlacemark(String name, FeatureColor color, String description, Long timestamp, Element feature, String coordinates) {
680  Element placemark = new Element("Placemark", ns); //NON-NLS
681  if (name != null && !name.isEmpty()) {
682  placemark.addContent(new Element("name", ns).addContent(name)); //NON-NLS
683  } else if (timestamp != null) {
684  placemark.addContent(new Element("name", ns).addContent(getTimeStamp(timestamp))); //NON-NLS
685  } else {
686  placemark.addContent(new Element("name", ns).addContent("")); //NON-NLS
687  }
688  placemark.addContent(new Element("styleUrl", ns).addContent(color.getColor())); //NON-NLS
689  placemark.addContent(new Element("description", ns).addContent(description)); //NON-NLS
690  if (timestamp != null) {
691  Element time = new Element("TimeStamp", ns); //NON-NLS
692  time.addContent(new Element("when", ns).addContent(getTimeStamp(timestamp))); //NON-NLS
693  placemark.addContent(time);
694  }
695  placemark.addContent(feature);
696  if (coordinates != null && !coordinates.isEmpty()) {
697  placemark.addContent(new Element("snippet", ns).addContent(coordinates)); //NON-NLS
698  }
699  return placemark;
700  }
701 
717  private Element makePlacemarkWithPicture(String name, FeatureColor color, String description, Long timestamp, Element feature, Path path, String coordinates) {
718  Element placemark = new Element("Placemark", ns); //NON-NLS
719  Element desc = new Element("description", ns); //NON-NLS
720  if (name != null && !name.isEmpty()) {
721  placemark.addContent(new Element("name", ns).addContent(name)); //NON-NLS
722  String image = "<img src='" + name + "' width='400'/>"; //NON-NLS
723  desc.addContent(image);
724  }
725  placemark.addContent(new Element("styleUrl", ns).addContent(color.getColor())); //NON-NLS
726  if (path != null) {
727  String pathAsString = path.toString();
728  if (pathAsString != null && !pathAsString.isEmpty()) {
729  desc.addContent(description + "<b>Source Path:</b> " + pathAsString);
730  }
731  }
732  placemark.addContent(desc);
733 
734  if (timestamp != null) {
735  Element time = new Element("TimeStamp", ns); //NON-NLS
736  time.addContent(new Element("when", ns).addContent(getTimeStamp(timestamp))); //NON-NLS
737  placemark.addContent(time);
738  }
739  placemark.addContent(feature);
740  if (coordinates != null && !coordinates.isEmpty()) {
741  placemark.addContent(new Element("snippet", ns).addContent(coordinates)); //NON-NLS
742  }
743  return placemark;
744  }
745 
756  private void copyFileUsingStream(AbstractFile inputFile, File outputFile) throws ReadContentInputStreamException, IOException {
757  byte[] buffer = new byte[65536];
758  int length;
759  outputFile.createNewFile();
760  try (InputStream is = new ReadContentInputStream(inputFile);
761  OutputStream os = new FileOutputStream(outputFile)) {
762  while ((length = is.read(buffer)) != -1) {
763  os.write(buffer, 0, length);
764  }
765  }
766  }
767 
768  @Override
769  public String getName() {
770  String name = NbBundle.getMessage(this.getClass(), "ReportKML.getName.text");
771  return name;
772  }
773 
774  @Override
775  public String getRelativeFilePath() {
776  return "ReportKML.kml"; //NON-NLS
777  }
778 
779  @Override
780  public String getDescription() {
781  String desc = NbBundle.getMessage(this.getClass(), "ReportKML.getDesc.text");
782  return desc;
783  }
784 
785  @Override
786  public JPanel getConfigurationPanel() {
787  return null; // No configuration panel
788  }
789 
800  private static String removeLeadingImgAndVol(String uniquePath) {
801  // split the path into parts
802  String[] pathSegments = uniquePath.replaceFirst("^/*", "").split("/"); //NON-NLS
803 
804  // Replace image/volume name if they exist in specific entries
805  if (pathSegments.length > 0) {
806  pathSegments[0] = pathSegments[0].replaceFirst("^img_", ""); //NON-NLS
807  }
808  if (pathSegments.length > 1) {
809  pathSegments[1] = pathSegments[1].replaceFirst("^vol_", ""); //NON-NLS
810  }
811 
812  // Assemble the path
813  StringBuilder strbuf = new StringBuilder();
814  for (String segment : pathSegments) {
815  if (!segment.isEmpty()) {
816  strbuf.append("/").append(segment);
817  }
818  }
819  return strbuf.toString();
820  }
821 
830  private String getFormattedDetails(Waypoint point, String header) {
831  StringBuilder result = new StringBuilder(); //NON-NLS
832  result.append(String.format("<h3>%s</h3>", header))
833  .append(formatAttribute("Name", point.getLabel()));
834 
835  Long timestamp = point.getTimestamp();
836  if (timestamp != null) {
837  result.append(formatAttribute("Timestamp", getTimeStamp(timestamp)));
838  }
839 
840  result.append(formatAttribute("Latitude", point.getLatitude().toString()))
841  .append(formatAttribute("Longitude", point.getLongitude().toString()));
842 
843  if (point.getAltitude() != null) {
844  result.append(formatAttribute("Altitude", point.getAltitude().toString()));
845  }
846 
847  List<Waypoint.Property> list = point.getOtherProperties();
848  for (Waypoint.Property prop : list) {
849  String value = prop.getValue();
850  if (value != null && !value.isEmpty()) {
851  result.append(formatAttribute(prop.getDisplayName(), value));
852  }
853  }
854 
855  return result.toString();
856  }
857 
866  private String formatAttribute(String title, String value) {
867  return String.format(HTML_PROP_FORMAT, title, value);
868  }
869 
877  private String getFormattedDetails(Route route) {
878  List<Waypoint> points = route.getRoute();
879  StringBuilder result = new StringBuilder(); //NON-NLS
880 
881  result.append(String.format("<h3>%s</h3>", Bundle.Route_Details_Header()))
882  .append(formatAttribute("Name", route.getLabel()));
883 
884  Long timestamp = route.getTimestamp();
885  if (timestamp != null) {
886  result.append(formatAttribute("Timestamp", getTimeStamp(timestamp)));
887  }
888 
889  if (points.size() > 1) {
890  Waypoint start = points.get(0);
891  Waypoint end = points.get(1);
892 
893  result.append(formatAttribute("Start Latitude", start.getLatitude().toString()))
894  .append(formatAttribute("Start Longitude", start.getLongitude().toString()));
895 
896  Double altitude = start.getAltitude();
897  if (altitude != null) {
898  result.append(formatAttribute("Start Altitude", altitude.toString()));
899  }
900 
901  result.append(formatAttribute("End Latitude", end.getLatitude().toString()))
902  .append(formatAttribute("End Longitude", end.getLongitude().toString()));
903 
904  altitude = end.getAltitude();
905  if (altitude != null) {
906  result.append(formatAttribute("End Altitude", altitude.toString()));
907  }
908  }
909 
910  List<Waypoint.Property> list = route.getOtherProperties();
911  for (Waypoint.Property prop : list) {
912  String value = prop.getValue();
913  if (value != null && !value.isEmpty()) {
914  result.append(formatAttribute(prop.getDisplayName(), value));
915  }
916  }
917 
918  return result.toString();
919  }
920 
929  private String formattedCoordinates(Double latitude, Double longitude) {
930  if (latitude == null || longitude == null) {
931  return "";
932  }
933 
934  return String.format("%.2f, %.2f", latitude, longitude);
935  }
936 
940  private boolean shouldFilterFromReport(Content content) throws TskCoreException {
941  if(this.settings.getSelectedDataSources() == null) {
942  return false;
943  }
944  long dataSourceId = content.getDataSource().getId();
945  return !this.settings.getSelectedDataSources().contains(dataSourceId);
946  }
947 }
List< Content > getDataSources()
Definition: Case.java:1428
void copyFileUsingStream(AbstractFile inputFile, File outputFile)
Definition: KMLReport.java:756
Element makeLineString(Double startLatitude, Double startLongitude, Double stopLatitude, Double stopLongitude)
Definition: KMLReport.java:649
static synchronized IngestManager getInstance()
void generateReport(GeneralReportSettings settings, ReportProgressPanel progressPanel)
Definition: KMLReport.java:173
static GeoLocationParseResult< Track > getTracks(SleuthkitCase skCase, List<?extends Content > sourceList)
Definition: GeoPath.java:76
Element makePlacemarkWithPicture(String name, FeatureColor color, String description, Long timestamp, Element feature, Path path, String coordinates)
Definition: KMLReport.java:717
Element makePoint(Double latitude, Double longitude, Double altitude)
Definition: KMLReport.java:607
void addReport(String localPath, String srcModuleName, String reportName)
Definition: Case.java:1646
static String removeLeadingImgAndVol(String uniquePath)
Definition: KMLReport.java:800
static synchronized KMLReport getDefault()
Definition: KMLReport.java:115
List< Waypoint.Property > getOtherProperties()
Definition: Route.java:85
void generateReport(String baseReportDir, ReportProgressPanel progressPanel, List< Waypoint > waypointList)
Definition: KMLReport.java:160
String formatAttribute(String title, String value)
Definition: KMLReport.java:866
Element makePlacemark(String name, FeatureColor color, String description, Long timestamp, Element feature, String coordinates)
Definition: KMLReport.java:679
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
void setSelectedDataSources(List< Long > selectedDataSources)
String getFormattedDetails(Waypoint point, String header)
Definition: KMLReport.java:830
String formattedCoordinates(Double latitude, Double longitude)
Definition: KMLReport.java:929

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