Autopsy 4.22.1
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 */
20package org.sleuthkit.autopsy.report.modules.kml;
21
22import org.sleuthkit.autopsy.report.GeneralReportModule;
23import javax.swing.JPanel;
24import org.openide.util.NbBundle;
25import org.sleuthkit.autopsy.casemodule.Case;
26import org.sleuthkit.autopsy.coreutils.Logger;
27import org.sleuthkit.autopsy.ingest.IngestManager;
28import java.io.File;
29import java.io.FileOutputStream;
30import java.io.IOException;
31import java.io.InputStream;
32import java.io.OutputStream;
33import java.nio.file.Path;
34import java.nio.file.Paths;
35import java.text.SimpleDateFormat;
36import java.util.List;
37import java.util.logging.Level;
38import java.util.stream.Collectors;
39import org.jdom2.Document;
40import org.jdom2.Element;
41import org.jdom2.Namespace;
42import org.jdom2.output.Format;
43import org.jdom2.output.XMLOutputter;
44import org.jdom2.CDATA;
45import org.openide.filesystems.FileUtil;
46import org.openide.util.NbBundle.Messages;
47import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
48import org.sleuthkit.autopsy.geolocation.datamodel.GeoLocationDataException;
49import org.sleuthkit.autopsy.geolocation.datamodel.GeoLocationParseResult;
50import org.sleuthkit.autopsy.geolocation.datamodel.Waypoint;
51import org.sleuthkit.autopsy.geolocation.datamodel.Route;
52import org.sleuthkit.autopsy.geolocation.datamodel.Track;
53import org.sleuthkit.autopsy.geolocation.datamodel.Area;
54import org.sleuthkit.autopsy.geolocation.datamodel.WaypointBuilder;
55import org.sleuthkit.autopsy.report.GeneralReportSettings;
56import org.sleuthkit.autopsy.report.ReportBranding;
57import org.sleuthkit.autopsy.report.ReportProgressPanel;
58import org.sleuthkit.datamodel.AbstractFile;
59import org.sleuthkit.datamodel.Content;
60import org.sleuthkit.datamodel.ReadContentInputStream;
61import org.sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException;
62import org.sleuthkit.datamodel.SleuthkitCase;
63import org.sleuthkit.datamodel.TskCoreException;
64
68public 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;
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;
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
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
172 return true;
173 }
174
175 @Override
177 try {
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
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) {
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) {
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}
void addReport(String localPath, String srcModuleName, String reportName)
Definition Case.java:1929
synchronized static Logger getLogger(String name)
Definition Logger.java:124
static GeoLocationParseResult< Track > getTracks(SleuthkitCase skCase, List<? extends Content > sourceList)
Definition GeoPath.java:80
static GeoLocationParseResult< Area > getAreas(SleuthkitCase skCase, List<? extends Content > sourceList)
Definition GeoPath.java:115
List< Waypoint.Property > getOtherProperties()
Definition Route.java:85
static synchronized IngestManager getInstance()
void generateReport(String baseReportDir, ReportProgressPanel progressPanel, List< Waypoint > waypointList)
Element makePolygon(List< Waypoint > waypoints)
void generateReport(GeneralReportSettings settings, ReportProgressPanel progressPanel)
Element makePlacemark(String name, FeatureColor color, String description, Long timestamp, Element feature, String coordinates)
Element makePlacemarkWithPicture(String name, FeatureColor color, String description, Long timestamp, Element feature, Path path, String coordinates)
Element makeLineString(Double startLatitude, Double startLongitude, Double stopLatitude, Double stopLongitude)
String formattedCoordinates(Double latitude, Double longitude)
String getFormattedDetails(Waypoint point, String header)
static String removeLeadingImgAndVol(String uniquePath)
String formatAttribute(String title, String value)
static synchronized KMLReport getDefault()
Element makePoint(Double latitude, Double longitude, Double altitude)
void copyFileUsingStream(AbstractFile inputFile, File outputFile)

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