20 package org.sleuthkit.autopsy.report;
 
   22 import javax.swing.JPanel;
 
   24 import org.openide.util.NbBundle;
 
   31 import java.io.FileOutputStream;
 
   32 import java.io.IOException;
 
   33 import java.io.InputStream;
 
   34 import java.io.OutputStream;
 
   35 import java.nio.file.Path;
 
   36 import java.nio.file.Paths;
 
   37 import java.text.SimpleDateFormat;
 
   38 import java.util.logging.Level;
 
   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;
 
   47 import org.
sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException;
 
   52 class ReportKML 
implements GeneralReportModule {
 
   54     private static final Logger logger = Logger.getLogger(ReportKML.class.getName());
 
   55     private static final String KML_STYLE_FILE = 
"style.kml";
 
   56     private static final String REPORT_KML = 
"ReportKML.kml";
 
   57     private static final String STYLESHEETS_PATH = 
"/org/sleuthkit/autopsy/report/stylesheets/";
 
   58     private static ReportKML instance = null;
 
   59     private Case currentCase;
 
   60     private SleuthkitCase skCase;
 
   61     private final SimpleDateFormat kmlDateFormat = 
new SimpleDateFormat(
"yyyy-MM-dd'T'HH:mm:ssX");
 
   63     private final String SEP = 
"<br>";
 
   66         RED(
"style.kml#redFeature"),
 
   67         GREEN(
"style.kml#greenFeature"),
 
   68         BLUE(
"style.kml#blueFeature"),
 
   70         WHITE(
"style.kml#whiteFeature"),
 
   88     public static synchronized ReportKML getDefault() {
 
   89         if (instance == null) {
 
   90             instance = 
new ReportKML();
 
  102     public void generateReport(String baseReportDir, ReportProgressPanel progressPanel) {
 
  104             currentCase = Case.getCurrentCaseThrows();
 
  105         } 
catch (NoCurrentCaseException ex) {
 
  106             logger.log(Level.SEVERE, 
"Exception while getting open case.", ex); 
 
  110         progressPanel.setIndeterminate(
true);
 
  111         progressPanel.start();
 
  112         progressPanel.updateStatusLabel(NbBundle.getMessage(
this.getClass(), 
"ReportKML.progress.querying"));
 
  113         String kmlFileFullPath = baseReportDir + REPORT_KML; 
 
  115         skCase = currentCase.getSleuthkitCase();
 
  117         progressPanel.updateStatusLabel(NbBundle.getMessage(
this.getClass(), 
"ReportKML.progress.loading"));
 
  119         ns = Namespace.getNamespace(
"", 
"http://www.opengis.net/kml/2.2"); 
 
  121         Element kml = 
new Element(
"kml", ns); 
 
  122         kml.addNamespaceDeclaration(Namespace.getNamespace(
"gx", 
"http://www.google.com/kml/ext/2.2")); 
 
  123         kml.addNamespaceDeclaration(Namespace.getNamespace(
"kml", 
"http://www.opengis.net/kml/2.2")); 
 
  124         kml.addNamespaceDeclaration(Namespace.getNamespace(
"atom", 
"http://www.w3.org/2005/Atom")); 
 
  125         Document kmlDocument = 
new Document(kml);
 
  127         Element document = 
new Element(
"Document", ns); 
 
  128         kml.addContent(document);
 
  130         Element name = 
new Element(
"name", ns); 
 
  131         ReportBranding rb = 
new ReportBranding();
 
  132         name.setText(rb.getReportTitle() + 
" KML"); 
 
  133         document.addContent(name);
 
  136         if (IngestManager.getInstance().isIngestRunning()) {
 
  137             Element ingestwarning = 
new Element(
"snippet", ns); 
 
  138             ingestwarning.addContent(NbBundle.getMessage(
this.getClass(), 
"ReportBodyFile.ingestWarning.text")); 
 
  139             document.addContent(ingestwarning);
 
  143         Element gpsExifMetadataFolder = 
new Element(
"Folder", ns); 
 
  144         CDATA cdataExifMetadataFolder = 
new CDATA(
"https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/camera-icon-16.png"); 
 
  145         Element hrefExifMetadata = 
new Element(
"href", ns).addContent(cdataExifMetadataFolder); 
 
  146         gpsExifMetadataFolder.addContent(
new Element(
"Icon", ns).addContent(hrefExifMetadata)); 
 
  148         Element gpsBookmarksFolder = 
new Element(
"Folder", ns); 
 
  149         CDATA cdataBookmarks = 
new CDATA(
"https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gpsfav.png"); 
 
  150         Element hrefBookmarks = 
new Element(
"href", ns).addContent(cdataBookmarks); 
 
  151         gpsBookmarksFolder.addContent(
new Element(
"Icon", ns).addContent(hrefBookmarks)); 
 
  153         Element gpsLastKnownLocationFolder = 
new Element(
"Folder", ns); 
 
  154         CDATA cdataLastKnownLocation = 
new CDATA(
"https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-lastlocation.png"); 
 
  155         Element hrefLastKnownLocation = 
new Element(
"href", ns).addContent(cdataLastKnownLocation); 
 
  156         gpsLastKnownLocationFolder.addContent(
new Element(
"Icon", ns).addContent(hrefLastKnownLocation)); 
 
  158         Element gpsRouteFolder = 
new Element(
"Folder", ns); 
 
  159         CDATA cdataRoute = 
new CDATA(
"https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-trackpoint.png"); 
 
  160         Element hrefRoute = 
new Element(
"href", ns).addContent(cdataRoute); 
 
  161         gpsRouteFolder.addContent(
new Element(
"Icon", ns).addContent(hrefRoute)); 
 
  163         Element gpsSearchesFolder = 
new Element(
"Folder", ns); 
 
  164         CDATA cdataSearches = 
new CDATA(
"https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-search.png"); 
 
  165         Element hrefSearches = 
new Element(
"href", ns).addContent(cdataSearches); 
 
  166         gpsSearchesFolder.addContent(
new Element(
"Icon", ns).addContent(hrefSearches)); 
 
  168         Element gpsTrackpointsFolder = 
new Element(
"Folder", ns); 
 
  169         CDATA cdataTrackpoints = 
new CDATA(
"https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-trackpoint.png"); 
 
  170         Element hrefTrackpoints = 
new Element(
"href", ns).addContent(cdataTrackpoints); 
 
  171         gpsTrackpointsFolder.addContent(
new Element(
"Icon", ns).addContent(hrefTrackpoints)); 
 
  173         gpsExifMetadataFolder.addContent(
new Element(
"name", ns).addContent(
"EXIF Metadata")); 
 
  174         gpsBookmarksFolder.addContent(
new Element(
"name", ns).addContent(
"GPS Bookmarks")); 
 
  175         gpsLastKnownLocationFolder.addContent(
new Element(
"name", ns).addContent(
"GPS Last Known Location")); 
 
  176         gpsRouteFolder.addContent(
new Element(
"name", ns).addContent(
"GPS Routes")); 
 
  177         gpsSearchesFolder.addContent(
new Element(
"name", ns).addContent(
"GPS Searches")); 
 
  178         gpsTrackpointsFolder.addContent(
new Element(
"name", ns).addContent(
"GPS Trackpoints")); 
 
  180         document.addContent(gpsExifMetadataFolder);
 
  181         document.addContent(gpsBookmarksFolder);
 
  182         document.addContent(gpsLastKnownLocationFolder);
 
  183         document.addContent(gpsRouteFolder);
 
  184         document.addContent(gpsSearchesFolder);
 
  185         document.addContent(gpsTrackpointsFolder);
 
  187         ReportProgressPanel.ReportStatus result = ReportProgressPanel.ReportStatus.COMPLETE;
 
  205             for (BlackboardArtifact artifact : skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF)) {
 
  206                 String fileName = 
"";
 
  209                     Long timestamp = getLong(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED);
 
  210                     String desc = getDescriptionFromArtifact(artifact, 
"EXIF Metadata With Locations"); 
 
  211                     Double lat = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE);
 
  212                     Double lon = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE);
 
  213                     Element point = makePoint(lat, lon, getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE));
 
  215                     if (lat != null && lat != 0.0 && lon != null && lon != 0.0) {
 
  216                         AbstractFile abstractFile = artifact.getSleuthkitCase().getAbstractFileById(artifact.getObjectID());
 
  217                         fileName = abstractFile.getName();
 
  218                         fileId = abstractFile.getId();
 
  220                         copyFileUsingStream(abstractFile, Paths.get(baseReportDir, abstractFile.getName()).toFile());
 
  222                             path = Paths.get(removeLeadingImgAndVol(abstractFile.getUniquePath()));
 
  223                         } 
catch (TskCoreException ex) {
 
  224                             path = Paths.get(abstractFile.getParentPath(), abstractFile.getName());
 
  226                         String formattedCoordinates = String.format(
"%.2f, %.2f", lat, lon);
 
  228                             path = Paths.get(abstractFile.getName());
 
  230                         gpsExifMetadataFolder.addContent(makePlacemarkWithPicture(abstractFile.getName(), FeatureColor.RED, desc, timestamp, point, path, formattedCoordinates));
 
  232                 } 
catch (ReadContentInputStreamException ex) {
 
  233                     logger.log(Level.WARNING, String.format(
"Error reading file '%s' (id=%d).", fileName, fileId), ex);
 
  234                 } 
catch (Exception ex) {
 
  235                     logger.log(Level.SEVERE, 
"Could not extract photo information.", ex); 
 
  236                     result = ReportProgressPanel.ReportStatus.ERROR;
 
  239         } 
catch (TskCoreException ex) {
 
  240             logger.log(Level.SEVERE, 
"Could not extract photos with EXIF metadata.", ex); 
 
  241             result = ReportProgressPanel.ReportStatus.ERROR;
 
  245             for (BlackboardArtifact artifact : skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_BOOKMARK)) {
 
  247                     Long timestamp = getLong(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME);
 
  248                     String desc = getDescriptionFromArtifact(artifact, 
"GPS Bookmark"); 
 
  249                     Double lat = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE);
 
  250                     Double lon = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE);
 
  251                     Element point = makePoint(lat, lon, getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE));
 
  252                     String bookmarkName = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME);
 
  253                     String formattedCoordinates = String.format(
"%.2f, %.2f", lat, lon);
 
  254                     gpsBookmarksFolder.addContent(makePlacemark(bookmarkName, FeatureColor.BLUE, desc, timestamp, point, formattedCoordinates));
 
  255                 } 
catch (Exception ex) {
 
  256                     logger.log(Level.SEVERE, 
"Could not extract Bookmark information.", ex); 
 
  257                     result = ReportProgressPanel.ReportStatus.ERROR;
 
  260         } 
catch (TskCoreException ex) {
 
  261             logger.log(Level.SEVERE, 
"Could not get GPS Bookmarks from database.", ex); 
 
  262             result = ReportProgressPanel.ReportStatus.ERROR;
 
  266             for (BlackboardArtifact artifact : skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_LAST_KNOWN_LOCATION)) {
 
  268                     Long timestamp = getLong(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME);
 
  269                     String desc = getDescriptionFromArtifact(artifact, 
"GPS Last Known Location"); 
 
  270                     Double lat = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE);
 
  271                     Double lon = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE);
 
  272                     Double alt = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE);
 
  273                     Element point = makePoint(lat, lon, alt);
 
  274                     String formattedCoordinates = String.format(
"%.2f, %.2f", lat, lon);
 
  275                     gpsLastKnownLocationFolder.addContent(makePlacemark(
"Last Known Location", FeatureColor.PURPLE, desc, timestamp, point, formattedCoordinates)); 
 
  276                 } 
catch (Exception ex) {
 
  277                     logger.log(Level.SEVERE, 
"Could not extract Last Known Location information.", ex); 
 
  278                     result = ReportProgressPanel.ReportStatus.ERROR;
 
  281         } 
catch (TskCoreException ex) {
 
  282             logger.log(Level.SEVERE, 
"Could not get GPS Last Known Location from database.", ex); 
 
  283             result = ReportProgressPanel.ReportStatus.ERROR;
 
  287             for (BlackboardArtifact artifact : skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_ROUTE)) {
 
  289                     Long timestamp = getLong(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME);
 
  290                     String desc = getDescriptionFromArtifact(artifact, 
"GPS Route");
 
  291                     Double latitudeStart = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_START);
 
  292                     Double longitudeStart = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_START);
 
  293                     Double latitudeEnd = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_END);
 
  294                     Double longitudeEnd = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_END);
 
  295                     Double altitude = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE);
 
  297                     Element route = makeLineString(latitudeStart, longitudeStart, altitude, latitudeEnd, longitudeEnd, altitude);
 
  298                     Element startingPoint = makePoint(latitudeStart, longitudeStart, altitude);
 
  299                     Element endingPoint = makePoint(latitudeEnd, longitudeEnd, altitude);
 
  301                     String formattedCoordinates = String.format(
"%.2f, %.2f to %.2f, %.2f", latitudeStart, longitudeStart, latitudeEnd, longitudeEnd);
 
  302                     gpsRouteFolder.addContent(makePlacemark(
"As-the-crow-flies Route", FeatureColor.GREEN, desc, timestamp, route, formattedCoordinates)); 
 
  304                     formattedCoordinates = String.format(
"%.2f, %.2f", latitudeStart, longitudeStart);
 
  305                     gpsRouteFolder.addContent(makePlacemark(
"Start", FeatureColor.GREEN, desc, timestamp, startingPoint, formattedCoordinates)); 
 
  307                     formattedCoordinates = String.format(
"%.2f, %.2f", latitudeEnd, longitudeEnd);
 
  308                     gpsRouteFolder.addContent(makePlacemark(
"End", FeatureColor.GREEN, desc, timestamp, endingPoint, formattedCoordinates)); 
 
  309                 } 
catch (Exception ex) {
 
  310                     logger.log(Level.SEVERE, 
"Could not extract GPS Route information.", ex); 
 
  311                     result = ReportProgressPanel.ReportStatus.ERROR;
 
  314         } 
catch (TskCoreException ex) {
 
  315             logger.log(Level.SEVERE, 
"Could not get GPS Routes from database.", ex); 
 
  316             result = ReportProgressPanel.ReportStatus.ERROR;
 
  320             for (BlackboardArtifact artifact : skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_SEARCH)) {
 
  321                 Long timestamp = getLong(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME);
 
  322                 String desc = getDescriptionFromArtifact(artifact, 
"GPS Search"); 
 
  323                 Double lat = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE);
 
  324                 Double lon = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE);
 
  325                 Double alt = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE);
 
  326                 Element point = makePoint(lat, lon, alt);
 
  327                 String formattedCoordinates = String.format(
"%.2f, %.2f", lat, lon);
 
  328                 String searchName = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME);
 
  329                 if (searchName == null || searchName.isEmpty()) {
 
  330                     searchName = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_LOCATION);
 
  332                 if (searchName == null || searchName.isEmpty()) {
 
  333                     searchName = 
"GPS Search";
 
  335                 gpsSearchesFolder.addContent(makePlacemark(searchName, FeatureColor.WHITE, desc, timestamp, point, formattedCoordinates)); 
 
  337         } 
catch (TskCoreException ex) {
 
  338             logger.log(Level.SEVERE, 
"Could not get GPS Searches from database.", ex); 
 
  339             result = ReportProgressPanel.ReportStatus.ERROR;
 
  343             for (BlackboardArtifact artifact : skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACKPOINT)) {
 
  345                     Long timestamp = getLong(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME);
 
  346                     String desc = getDescriptionFromArtifact(artifact, 
"GPS Trackpoint"); 
 
  347                     Double lat = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE);
 
  348                     Double lon = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE);
 
  349                     Double alt = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE);
 
  350                     Element point = makePoint(lat, lon, alt);
 
  351                     String formattedCoordinates = String.format(
"%.2f, %.2f, %.2f", lat, lon, alt);
 
  352                     String trackName = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME);
 
  353                     if (trackName == null || trackName.isEmpty()) {
 
  354                         trackName = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME);
 
  356                     if (trackName == null || trackName.isEmpty()) {
 
  357                         trackName = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_FLAG);
 
  359                     if (trackName == null || trackName.isEmpty()) {
 
  360                         trackName = 
"GPS Trackpoint";
 
  362                     gpsTrackpointsFolder.addContent(makePlacemark(trackName, FeatureColor.YELLOW, desc, timestamp, point, formattedCoordinates));
 
  363                 } 
catch (Exception ex) {
 
  364                     logger.log(Level.SEVERE, 
"Could not extract Trackpoint information.", ex); 
 
  365                     result = ReportProgressPanel.ReportStatus.ERROR;
 
  368         } 
catch (TskCoreException ex) {
 
  369             logger.log(Level.SEVERE, 
"Could not get GPS Trackpoints from database.", ex); 
 
  370             result = ReportProgressPanel.ReportStatus.ERROR;
 
  375             InputStream input = getClass().getResourceAsStream(STYLESHEETS_PATH + KML_STYLE_FILE); 
 
  376             OutputStream output = 
new FileOutputStream(baseReportDir + KML_STYLE_FILE); 
 
  377             FileUtil.copy(input, output);
 
  378         } 
catch (IOException ex) {
 
  379             logger.log(Level.SEVERE, 
"Error placing KML stylesheet. The .KML file will not function properly.", ex); 
 
  380             result = ReportProgressPanel.ReportStatus.ERROR;
 
  383         try (FileOutputStream writer = 
new FileOutputStream(kmlFileFullPath)) {
 
  384             XMLOutputter outputter = 
new XMLOutputter(Format.getPrettyFormat());
 
  385             outputter.output(kmlDocument, writer);
 
  386             String prependedStatus = 
"";
 
  387             if (result == ReportProgressPanel.ReportStatus.ERROR) {
 
  388                 prependedStatus = 
"Incomplete ";
 
  390             Case.getCurrentCaseThrows().addReport(kmlFileFullPath,
 
  391                     NbBundle.getMessage(
this.getClass(), 
"ReportKML.genReport.srcModuleName.text"),
 
  392                     prependedStatus + NbBundle.getMessage(
this.getClass(), 
"ReportKML.genReport.reportName"));
 
  393         } 
catch (IOException ex) {
 
  394             logger.log(Level.SEVERE, 
"Could not write the KML file.", ex); 
 
  395             progressPanel.complete(ReportProgressPanel.ReportStatus.ERROR);
 
  396         } 
catch (TskCoreException ex) {
 
  397             String errorMessage = String.format(
"Error adding %s to case as a report", kmlFileFullPath); 
 
  398             logger.log(Level.SEVERE, errorMessage, ex);
 
  399             result = ReportProgressPanel.ReportStatus.ERROR;
 
  400         } 
catch (NoCurrentCaseException ex) {
 
  401             logger.log(Level.SEVERE, 
"Exception while getting open case.", ex);
 
  402             result = ReportProgressPanel.ReportStatus.ERROR;
 
  405         progressPanel.complete(result);
 
  416     private Double getDouble(BlackboardArtifact artifact, BlackboardAttribute.ATTRIBUTE_TYPE type) {
 
  417         Double returnValue = null;
 
  419             BlackboardAttribute bba = artifact.getAttribute(
new BlackboardAttribute.Type(type));
 
  421                 Double value = bba.getValueDouble();
 
  424         } 
catch (TskCoreException ex) {
 
  425             logger.log(Level.SEVERE, 
"Error getting Double value: " + type.toString(), ex); 
 
  438     private Long getLong(BlackboardArtifact artifact, BlackboardAttribute.ATTRIBUTE_TYPE type) {
 
  439         Long returnValue = null;
 
  441             BlackboardAttribute bba = artifact.getAttribute(
new BlackboardAttribute.Type(type));
 
  443                 Long value = bba.getValueLong();
 
  446         } 
catch (TskCoreException ex) {
 
  447             logger.log(Level.SEVERE, 
"Error getting Long value: " + type.toString(), ex); 
 
  460     private Integer getInteger(BlackboardArtifact artifact, BlackboardAttribute.ATTRIBUTE_TYPE type) {
 
  461         Integer returnValue = null;
 
  463             BlackboardAttribute bba = artifact.getAttribute(
new BlackboardAttribute.Type(type));
 
  465                 Integer value = bba.getValueInt();
 
  468         } 
catch (TskCoreException ex) {
 
  469             logger.log(Level.SEVERE, 
"Error getting Integer value: " + type.toString(), ex); 
 
  482     private String getString(BlackboardArtifact artifact, BlackboardAttribute.ATTRIBUTE_TYPE type) {
 
  483         String returnValue = null;
 
  485             BlackboardAttribute bba = artifact.getAttribute(
new BlackboardAttribute.Type(type));
 
  487                 String value = bba.getValueString();
 
  488                 if (value != null && !value.isEmpty()) {
 
  492         } 
catch (TskCoreException ex) {
 
  493             logger.log(Level.SEVERE, 
"Error getting String value: " + type.toString(), ex); 
 
  515     private String getDescriptionFromArtifact(BlackboardArtifact artifact, String featureType) {
 
  516         StringBuilder result = 
new StringBuilder(
"<h3>" + featureType + 
"</h3>"); 
 
  518         String name = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME);
 
  519         if (name != null && !name.isEmpty()) {
 
  520             result.append(
"<b>Name:</b> ").append(name).append(SEP); 
 
  523         String location = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_LOCATION);
 
  524         if (location != null && !location.isEmpty()) {
 
  525             result.append(
"<b>Location:</b> ").append(location).append(SEP); 
 
  528         Long timestamp = getLong(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME);
 
  529         if (timestamp != null) {
 
  530             result.append(
"<b>Timestamp:</b> ").append(getTimeStamp(timestamp)).append(SEP); 
 
  531             result.append(
"<b>Unix timestamp:</b> ").append(timestamp).append(SEP); 
 
  534         Long startingTimestamp = getLong(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START);
 
  535         if (startingTimestamp != null) {
 
  536             result.append(
"<b>Starting Timestamp:</b> ").append(getTimeStamp(startingTimestamp)).append(SEP); 
 
  537             result.append(
"<b>Starting Unix timestamp:</b> ").append(startingTimestamp).append(SEP); 
 
  540         Long endingTimestamp = getLong(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_END);
 
  541         if (endingTimestamp != null) {
 
  542             result.append(
"<b>Ending Timestamp:</b> ").append(getTimeStamp(endingTimestamp)).append(SEP); 
 
  543             result.append(
"<b>Ending Unix timestamp:</b> ").append(endingTimestamp).append(SEP); 
 
  546         Long createdTimestamp = getLong(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED);
 
  547         if (createdTimestamp != null) {
 
  548             result.append(
"<b>Created Timestamp:</b> ").append(getTimeStamp(createdTimestamp)).append(SEP); 
 
  549             result.append(
"<b>Created Unix timestamp:</b> ").append(createdTimestamp).append(SEP); 
 
  552         Double latitude = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE);
 
  553         if (latitude != null) {
 
  554             result.append(
"<b>Latitude:</b> ").append(latitude).append(SEP); 
 
  557         Double longitude = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE);
 
  558         if (longitude != null) {
 
  559             result.append(
"<b>Longitude:</b> ").append(longitude).append(SEP); 
 
  562         Double latitudeStart = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_START);
 
  563         if (latitudeStart != null) {
 
  564             result.append(
"<b>Latitude Start:</b> ").append(latitudeStart).append(SEP); 
 
  567         Double longitudeStart = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_START);
 
  568         if (longitudeStart != null) {
 
  569             result.append(
"<b>Longitude Start:</b> ").append(longitudeStart).append(SEP); 
 
  572         Double latitudeEnd = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_END);
 
  573         if (latitudeEnd != null) {
 
  574             result.append(
"<b>Latitude End:</b> ").append(latitudeEnd).append(SEP); 
 
  577         Double longitudeEnd = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_END);
 
  578         if (longitudeEnd != null) {
 
  579             result.append(
"<b>Longitude End:</b> ").append(longitudeEnd).append(SEP); 
 
  582         Double velocity = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_VELOCITY);
 
  583         if (velocity != null) {
 
  584             result.append(
"<b>Velocity:</b> ").append(velocity).append(SEP); 
 
  587         Double altitude = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE);
 
  588         if (altitude != null) {
 
  589             result.append(
"<b>Altitude:</b> ").append(altitude).append(SEP); 
 
  592         Double bearing = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_BEARING);
 
  593         if (bearing != null) {
 
  594             result.append(
"<b>Bearing:</b> ").append(bearing).append(SEP); 
 
  597         Integer hPrecision = getInteger(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_HPRECISION);
 
  598         if (hPrecision != null) {
 
  599             result.append(
"<b>Horizontal Precision Figure of Merit:</b> ").append(hPrecision).append(SEP); 
 
  602         Integer vPrecision = getInteger(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_VPRECISION);
 
  603         if (vPrecision != null) {
 
  604             result.append(
"<b>Vertical Precision Figure of Merit:</b> ").append(vPrecision).append(SEP); 
 
  607         String mapDatum = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_MAPDATUM);
 
  608         if (mapDatum != null && !mapDatum.isEmpty()) {
 
  609             result.append(
"<b>Map Datum:</b> ").append(mapDatum).append(SEP); 
 
  612         String programName = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME);
 
  613         if (programName != null && !programName.isEmpty()) {
 
  614             result.append(
"<b>Reported by:</b> ").append(programName).append(SEP); 
 
  617         String flag = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_FLAG);
 
  618         if (flag != null && !flag.isEmpty()) {
 
  619             result.append(
"<b>Flag:</b> ").append(flag).append(SEP); 
 
  622         String pathSource = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_SOURCE);
 
  623         if (pathSource != null && !pathSource.isEmpty()) {
 
  624             result.append(
"<b>Source:</b> ").append(pathSource).append(SEP); 
 
  627         String deviceMake = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_MAKE);
 
  628         if (deviceMake != null && !deviceMake.isEmpty()) {
 
  629             result.append(
"<b>Device Make:</b> ").append(deviceMake).append(SEP); 
 
  632         String deviceModel = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_MODEL);
 
  633         if (deviceModel != null && !deviceModel.isEmpty()) {
 
  634             result.append(
"<b>Device Model:</b> ").append(deviceModel).append(SEP); 
 
  637         return result.toString();
 
  640     private String getTimeStamp(
long timeStamp) {
 
  641         return kmlDateFormat.format(
new java.util.Date(timeStamp * 1000));
 
  657     private Element makePoint(Double latitude, Double longitude, Double altitude) {
 
  658         if (latitude == null) {
 
  661         if (longitude == null) {
 
  664         if (altitude == null) {
 
  667         Element point = 
new Element(
"Point", ns); 
 
  670         Element coordinates = 
new Element(
"coordinates", ns).addContent(longitude + 
"," + latitude + 
"," + altitude); 
 
  680             Element altitudeMode = 
new Element(
"altitudeMode", ns).addContent(
"clampToGround"); 
 
  681             point.addContent(altitudeMode);
 
  683         point.addContent(coordinates);
 
  705     private Element makeLineString(Double startLatitude, Double startLongitude, Double startAltitude, Double stopLatitude, Double stopLongitude, Double stopAltitude) {
 
  706         if (startLatitude == null) {
 
  709         if (startLongitude == null) {
 
  710             startLongitude = 0.0;
 
  712         if (startAltitude == null) {
 
  715         if (stopLatitude == null) {
 
  718         if (stopLongitude == null) {
 
  721         if (stopAltitude == null) {
 
  725         Element lineString = 
new Element(
"LineString", ns); 
 
  726         lineString.addContent(
new Element(
"extrude", ns).addContent(
"1")); 
 
  727         lineString.addContent(
new Element(
"tessellate", ns).addContent(
"1")); 
 
  728         lineString.addContent(
new Element(
"altitudeMode", ns).addContent(
"clampToGround")); 
 
  730         lineString.addContent(
new Element(
"coordinates", ns).addContent(
 
  731                 startLongitude + 
"," + startLatitude + 
",0.0," 
  732                 + stopLongitude + 
"," + stopLatitude + 
",0.0")); 
 
  750     private Element makePlacemark(String name, FeatureColor color, String description, Long timestamp, Element feature, String coordinates) {
 
  751         Element placemark = 
new Element(
"Placemark", ns); 
 
  752         if (name != null && !name.isEmpty()) {
 
  753             placemark.addContent(
new Element(
"name", ns).addContent(name)); 
 
  754         } 
else if (timestamp != null) {
 
  755             placemark.addContent(
new Element(
"name", ns).addContent(getTimeStamp(timestamp))); 
 
  757             placemark.addContent(
new Element(
"name", ns).addContent(
"")); 
 
  759         placemark.addContent(
new Element(
"styleUrl", ns).addContent(color.getColor())); 
 
  760         placemark.addContent(
new Element(
"description", ns).addContent(description)); 
 
  761         if (timestamp != null) {
 
  762             Element time = 
new Element(
"TimeStamp", ns); 
 
  763             time.addContent(
new Element(
"when", ns).addContent(getTimeStamp(timestamp))); 
 
  764             placemark.addContent(time);
 
  766         placemark.addContent(feature);
 
  767         if (coordinates != null && !coordinates.isEmpty()) {
 
  768             placemark.addContent(
new Element(
"snippet", ns).addContent(coordinates)); 
 
  788     private Element makePlacemarkWithPicture(String name, FeatureColor color, String description, Long timestamp, Element feature, Path path, String coordinates) {
 
  789         Element placemark = 
new Element(
"Placemark", ns); 
 
  790         Element desc = 
new Element(
"description", ns); 
 
  791         if (name != null && !name.isEmpty()) {
 
  792             placemark.addContent(
new Element(
"name", ns).addContent(name)); 
 
  793             String image = 
"<img src='" + name + 
"' width='400'/>"; 
 
  794             desc.addContent(image);
 
  796         placemark.addContent(
new Element(
"styleUrl", ns).addContent(color.getColor())); 
 
  798             String pathAsString = path.toString();
 
  799             if (pathAsString != null && !pathAsString.isEmpty()) {
 
  800                 desc.addContent(description + 
"<b>Source Path:</b> " + pathAsString);
 
  803         placemark.addContent(desc);
 
  805         if (timestamp != null) {
 
  806             Element time = 
new Element(
"TimeStamp", ns); 
 
  807             time.addContent(
new Element(
"when", ns).addContent(getTimeStamp(timestamp))); 
 
  808             placemark.addContent(time);
 
  810         placemark.addContent(feature);
 
  811         if (coordinates != null && !coordinates.isEmpty()) {
 
  812             placemark.addContent(
new Element(
"snippet", ns).addContent(coordinates)); 
 
  827     private void copyFileUsingStream(AbstractFile inputFile, File outputFile) 
throws ReadContentInputStreamException, IOException {
 
  828         byte[] buffer = 
new byte[65536];
 
  830         outputFile.createNewFile();
 
  831         try (InputStream is = 
new ReadContentInputStream(inputFile);
 
  832                 OutputStream os = 
new FileOutputStream(outputFile)) {
 
  833             while ((length = is.read(buffer)) != -1) {
 
  834                 os.write(buffer, 0, length);
 
  840     public String getName() {
 
  841         String name = NbBundle.getMessage(this.getClass(), 
"ReportKML.getName.text");
 
  846     public String getRelativeFilePath() {
 
  847         return "ReportKML.kml"; 
 
  851     public String getDescription() {
 
  852         String desc = NbBundle.getMessage(this.getClass(), 
"ReportKML.getDesc.text");
 
  857     public JPanel getConfigurationPanel() {
 
  871     private static String removeLeadingImgAndVol(String uniquePath) {
 
  873         String[] pathSegments = uniquePath.replaceFirst(
"^/*", 
"").split(
"/"); 
 
  876         if (pathSegments.length > 0) {
 
  877             pathSegments[0] = pathSegments[0].replaceFirst(
"^img_", 
""); 
 
  879         if (pathSegments.length > 1) {
 
  880             pathSegments[1] = pathSegments[1].replaceFirst(
"^vol_", 
""); 
 
  884         StringBuilder strbuf = 
new StringBuilder();
 
  885         for (String segment : pathSegments) {
 
  886             if (!segment.isEmpty()) {
 
  887                 strbuf.append(
"/").append(segment);
 
  890         return strbuf.toString();
 
FeatureColor(String color)