diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbImportDatabaseDialog.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbImportDatabaseDialog.java
index 18fd2c6202..337fa49f0b 100644
--- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbImportDatabaseDialog.java
+++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbImportDatabaseDialog.java
@@ -22,6 +22,7 @@ import java.awt.Dimension;
import java.awt.Toolkit;
import java.io.File;
import java.io.IOException;
+import java.nio.file.Paths;
import java.util.logging.Level;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
@@ -31,6 +32,7 @@ import org.apache.commons.io.FilenameUtils;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
+import org.sleuthkit.autopsy.coreutils.PlatformUtil;
import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDb;
import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDb.KnownFilesType;
import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDbManagerException;
@@ -249,9 +251,16 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog {
}// //GEN-END:initComponents
private void openButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_openButtonActionPerformed
+ String lastBaseDirectory = Paths.get(PlatformUtil.getUserConfigDirectory(), "HashDatabases").toString();
if (ModuleSettings.settingExists(ModuleSettings.MAIN_SETTINGS, LAST_FILE_PATH_KEY)) {
- fileChooser.setCurrentDirectory(new File(ModuleSettings.getConfigSetting(ModuleSettings.MAIN_SETTINGS, LAST_FILE_PATH_KEY)));
+ lastBaseDirectory = ModuleSettings.getConfigSetting(ModuleSettings.MAIN_SETTINGS, LAST_FILE_PATH_KEY);
}
+ File hashDbFolder = new File(lastBaseDirectory);
+ // create the folder if it doesn't exist
+ if (!hashDbFolder.exists()) {
+ hashDbFolder.mkdir();
+ }
+ fileChooser.setCurrentDirectory(hashDbFolder);
if (fileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
File databaseFile = fileChooser.getSelectedFile();
try {
diff --git a/Core/src/org/sleuthkit/autopsy/report/Bundle.properties b/Core/src/org/sleuthkit/autopsy/report/Bundle.properties
index ba477498c3..6e920ad631 100644
--- a/Core/src/org/sleuthkit/autopsy/report/Bundle.properties
+++ b/Core/src/org/sleuthkit/autopsy/report/Bundle.properties
@@ -225,7 +225,8 @@ ReportHTML.writeSum.noCaseNum=No case number
ReportBodyFile.generateReport.srcModuleName.text=TSK Body File
ReportExcel.endReport.srcModuleName.text=Excel Report
ReportHTML.writeIndex.srcModuleName.text=HTML Report
-ReportKML.genReport.srcModuleName.text=KML Report
+ReportKML.genReport.srcModuleName.text=Geospatial Data
+ReportKML.genReport.reportName=KML Report
ReportGenerator.artTableColHdr.extension.text=Extension
ReportGenerator.artTableColHdr.mimeType.text=MIME Type
ReportGenerator.artTableColHdr.processorArchitecture.text=Processor Architecture
diff --git a/Core/src/org/sleuthkit/autopsy/report/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/report/Bundle_ja.properties
index 6ff657b68a..a877c52899 100644
--- a/Core/src/org/sleuthkit/autopsy/report/Bundle_ja.properties
+++ b/Core/src/org/sleuthkit/autopsy/report/Bundle_ja.properties
@@ -188,7 +188,7 @@ ReportExcel.endReport.srcModuleName.text=Excel\u30ec\u30dd\u30fc\u30c8
ReportGenerator.artTableColHdr.extension.text=\u62e1\u5f35\u5b50
ReportGenerator.artTableColHdr.mimeType.text=MIME\u30bf\u30a4\u30d7
ReportHTML.writeIndex.srcModuleName.text=HTML\u30ec\u30dd\u30fc\u30c8
-ReportKML.genReport.srcModuleName.text=KML\u30ec\u30dd\u30fc\u30c8
+ReportKML.genReport.reportName=KML\u30ec\u30dd\u30fc\u30c8
ReportGenerator.artTableColHdr.associatedArtifact=\u95a2\u4fc2\u3059\u308b\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8
ReportGenerator.artTableColHdr.count=\u30ab\u30a6\u30f3\u30c8
ReportGenerator.artTableColHdr.devMake=\u6a5f\u5668\u578b\u540d
diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportKML.java b/Core/src/org/sleuthkit/autopsy/report/ReportKML.java
index abdd7dee96..44185bfc94 100644
--- a/Core/src/org/sleuthkit/autopsy/report/ReportKML.java
+++ b/Core/src/org/sleuthkit/autopsy/report/ReportKML.java
@@ -2,7 +2,7 @@
*
* Autopsy Forensic Browser
*
- * Copyright 2014 Basis Technology Corp.
+ * Copyright 2014-2016 Basis Technology Corp.
* contact: carrier sleuthkit org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -27,33 +27,56 @@ import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.*;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.datamodel.BlackboardArtifact;
-import java.io.BufferedReader;
-import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
-import java.io.FileReader;
-import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.text.SimpleDateFormat;
import java.util.logging.Level;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.Namespace;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;
-import org.apache.commons.lang.StringEscapeUtils;
+import org.jdom2.CDATA;
+import org.openide.filesystems.FileUtil;
/**
- * Generates a KML file based on geo coordinates store in blackboard.
+ * Generates a KML file based on geospatial information from the BlackBoard.
*/
class ReportKML implements GeneralReportModule {
private static final Logger logger = Logger.getLogger(ReportKML.class.getName());
+ private static final String KML_STYLE_FILE = "style.kml";
+ private static final String REPORT_KML = "ReportKML.kml";
+ private static final String STYLESHEETS_PATH = "/org/sleuthkit/autopsy/report/stylesheets/";
private static ReportKML instance = null;
private Case currentCase;
private SleuthkitCase skCase;
- private String reportPath;
+ private final SimpleDateFormat kmlDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");
+ private Namespace ns;
+ private final String SEP = "
";
+
+ private enum FeatureColor {
+ RED("style.kml#redFeature"),
+ GREEN("style.kml#greenFeature"),
+ BLUE("style.kml#blueFeature"),
+ PURPLE("style.kml#purpleFeature"),
+ WHITE("style.kml#whiteFeature"),
+ YELLOW("style.kml#yellowFeature");
+ private String color;
+
+ FeatureColor(String color) {
+ this.color = color;
+ }
+
+ String getColor() {
+ return this.color;
+ }
+ }
// Hidden constructor for the report
private ReportKML() {
@@ -80,279 +103,684 @@ class ReportKML implements GeneralReportModule {
progressPanel.setIndeterminate(false);
progressPanel.start();
progressPanel.updateStatusLabel(NbBundle.getMessage(this.getClass(), "ReportKML.progress.querying"));
- reportPath = baseReportDir + "ReportKML.kml"; //NON-NLS
- String reportPath2 = baseReportDir + "ReportKML.txt"; //NON-NLS
+ String kmlFileFullPath = baseReportDir + REPORT_KML; //NON-NLS
currentCase = Case.getCurrentCase();
skCase = currentCase.getSleuthkitCase();
progressPanel.updateStatusLabel(NbBundle.getMessage(this.getClass(), "ReportKML.progress.loading"));
- // Check if ingest has finished
- String ingestwarning = "";
- if (IngestManager.getInstance().isIngestRunning()) {
- ingestwarning = NbBundle.getMessage(this.getClass(), "ReportBodyFile.ingestWarning.text");
- }
+
progressPanel.setMaximumProgress(5);
progressPanel.increment();
- // @@@ BC: I don't get why we do this in two passes.
- // Why not just print the coordinates as we find them and make some utility methods to do the printing?
- // Should pull out time values for all of these points and store in TimeSpan element
+ ns = Namespace.getNamespace("", "http://www.opengis.net/kml/2.2"); //NON-NLS
+
+ Element kml = new Element("kml", ns); //NON-NLS
+ kml.addNamespaceDeclaration(Namespace.getNamespace("gx", "http://www.google.com/kml/ext/2.2")); //NON-NLS
+ kml.addNamespaceDeclaration(Namespace.getNamespace("kml", "http://www.opengis.net/kml/2.2")); //NON-NLS
+ kml.addNamespaceDeclaration(Namespace.getNamespace("atom", "http://www.w3.org/2005/Atom")); //NON-NLS
+ Document kmlDocument = new Document(kml);
+
+ Element document = new Element("Document", ns); //NON-NLS
+ kml.addContent(document);
+
+ Element name = new Element("name", ns); //NON-NLS
+ ReportBranding rb = new ReportBranding();
+ name.setText(rb.getReportTitle() + " KML"); //NON-NLS
+ document.addContent(name);
+
+ // Check if ingest has finished
+ if (IngestManager.getInstance().isIngestRunning()) {
+ Element ingestwarning = new Element("snippet", ns); //NON-NLS
+ ingestwarning.addContent(NbBundle.getMessage(this.getClass(), "ReportBodyFile.ingestWarning.text")); //NON-NLS
+ document.addContent(ingestwarning);
+ }
+
+ // Create folder structure
+ Element gpsExifMetadataFolder = new Element("Folder", ns); //NON-NLS
+ CDATA cdataExifMetadataFolder = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/camera-icon-16.png"); //NON-NLS
+ Element hrefExifMetadata = new Element("href", ns).addContent(cdataExifMetadataFolder); //NON-NLS
+ gpsExifMetadataFolder.addContent(new Element("Icon", ns).addContent(hrefExifMetadata)); //NON-NLS
+
+ Element gpsBookmarksFolder = new Element("Folder", ns); //NON-NLS
+ CDATA cdataBookmarks = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gpsfav.png"); //NON-NLS
+ Element hrefBookmarks = new Element("href", ns).addContent(cdataBookmarks); //NON-NLS
+ gpsBookmarksFolder.addContent(new Element("Icon", ns).addContent(hrefBookmarks)); //NON-NLS
+
+ Element gpsLastKnownLocationFolder = new Element("Folder", ns); //NON-NLS
+ CDATA cdataLastKnownLocation = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-lastlocation.png"); //NON-NLS
+ Element hrefLastKnownLocation = new Element("href", ns).addContent(cdataLastKnownLocation); //NON-NLS
+ gpsLastKnownLocationFolder.addContent(new Element("Icon", ns).addContent(hrefLastKnownLocation)); //NON-NLS
+
+ Element gpsRouteFolder = new Element("Folder", ns); //NON-NLS
+ CDATA cdataRoute = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-trackpoint.png"); //NON-NLS
+ Element hrefRoute = new Element("href", ns).addContent(cdataRoute); //NON-NLS
+ gpsRouteFolder.addContent(new Element("Icon", ns).addContent(hrefRoute)); //NON-NLS
+
+ Element gpsSearchesFolder = new Element("Folder", ns); //NON-NLS
+ CDATA cdataSearches = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-search.png"); //NON-NLS
+ Element hrefSearches = new Element("href", ns).addContent(cdataSearches); //NON-NLS
+ gpsSearchesFolder.addContent(new Element("Icon", ns).addContent(hrefSearches)); //NON-NLS
+
+ Element gpsTrackpointsFolder = new Element("Folder", ns); //NON-NLS
+ CDATA cdataTrackpoints = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-trackpoint.png"); //NON-NLS
+ Element hrefTrackpoints = new Element("href", ns).addContent(cdataTrackpoints); //NON-NLS
+ gpsTrackpointsFolder.addContent(new Element("Icon", ns).addContent(hrefTrackpoints)); //NON-NLS
+
+ gpsExifMetadataFolder.addContent(new Element("name", ns).addContent("EXIF Metadata")); //NON-NLS
+ gpsBookmarksFolder.addContent(new Element("name", ns).addContent("GPS Bookmarks")); //NON-NLS
+ gpsLastKnownLocationFolder.addContent(new Element("name", ns).addContent("GPS Last Known Location")); //NON-NLS
+ gpsRouteFolder.addContent(new Element("name", ns).addContent("GPS Routes")); //NON-NLS
+ gpsSearchesFolder.addContent(new Element("name", ns).addContent("GPS Searches")); //NON-NLS
+ gpsTrackpointsFolder.addContent(new Element("name", ns).addContent("GPS Trackpoints")); //NON-NLS
+
+ document.addContent(gpsExifMetadataFolder);
+ document.addContent(gpsBookmarksFolder);
+ document.addContent(gpsLastKnownLocationFolder);
+ document.addContent(gpsRouteFolder);
+ document.addContent(gpsSearchesFolder);
+ document.addContent(gpsTrackpointsFolder);
+
+ /**
+ * In the following code, nulls are okay, and are handled when we go to
+ * write out the KML feature. Nulls are expected to be returned from any
+ * method where the artifact is not found and is handled in the
+ * individual feature creation methods. This is done because we don't
+ * know beforehand which attributes will be included for which artifact,
+ * as anyone could write a module that adds additional attributes to an
+ * artifact.
+ *
+ */
try {
- try (BufferedWriter out = new BufferedWriter(new FileWriter(reportPath2))) {
-
- double lat = 0; // temp latitude
- double lon = 0; //temp longitude
- AbstractFile aFile;
- String geoPath = ""; // will hold values of images to put in kml
- String imageName = "";
-
- File f;
- for (BlackboardArtifact artifact : skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF)) {
- lat = 0;
- lon = 0;
- geoPath = "";
- String extractedToPath;
- for (BlackboardAttribute attribute : artifact.getAttributes()) {
- if (attribute.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE.getTypeID()) //latitude
- {
-
- lat = attribute.getValueDouble();
- }
- if (attribute.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE.getTypeID()) //longitude
- {
- lon = attribute.getValueDouble();
- }
- }
- if (lon != 0 && lat != 0) {
- aFile = artifact.getSleuthkitCase().getAbstractFileById(artifact.getObjectID());
-
- if (aFile != null) {
- extractedToPath = reportPath + aFile.getName();
- geoPath = extractedToPath;
- f = new File(extractedToPath);
- f.createNewFile();
- copyFileUsingStream(aFile, f);
- imageName = aFile.getName();
- }
- out.write(String.valueOf(lat));
- out.write(";");
- out.write(String.valueOf(lon));
- out.write(";");
- out.write(String.valueOf(geoPath));
- out.write(";");
- out.write(String.valueOf(imageName));
- out.write("\n");
- // lat lon path name
+ for (BlackboardArtifact artifact : skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF)) {
+ Long timestamp = getLong(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED);
+ String desc = getDescriptionFromArtifact(artifact, "EXIF Metadata With Locations"); //NON-NLS
+ Double lat = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE);
+ Double lon = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE);
+ Element point = makePoint(lat, lon, getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE));
+ Path destination = null;
+ if (lat != null && lat != 0.0 && lon != null && lon != 0.0) {
+ AbstractFile abstractFile = artifact.getSleuthkitCase().getAbstractFileById(artifact.getObjectID());
+ if (abstractFile != null) {
+ destination = Paths.get(baseReportDir, abstractFile.getName());
+ copyFileUsingStream(abstractFile, destination.toFile());
}
+ String formattedCoordinates = String.format("%.2f, %.2f", lat, lon);
+ gpsExifMetadataFolder.addContent(makePlacemarkWithPicture(abstractFile.getName(), FeatureColor.RED, desc, timestamp, point, destination, formattedCoordinates));
}
- for (BlackboardArtifact artifact : skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACKPOINT)) {
- lat = 0;
- lon = 0;
- for (BlackboardAttribute attribute : artifact.getAttributes()) {
- if (attribute.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE.getTypeID()) //latitude
- {
- lat = attribute.getValueDouble();
- }
- if (attribute.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE.getTypeID()) //longitude
- {
- lon = attribute.getValueDouble();
- }
- }
- if (lon != 0 && lat != 0) {
- out.write(lat + ";" + lon + "\n");
- }
- }
-
- for (BlackboardArtifact artifact : skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_ROUTE)) {
- lat = 0;
- lon = 0;
- double destlat = 0;
- double destlon = 0;
- String name = "";
- String location = "";
- for (BlackboardAttribute attribute : artifact.getAttributes()) {
- if (attribute.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_START.getTypeID()) //latitude
- {
- lat = attribute.getValueDouble();
- } else if (attribute.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_END.getTypeID()) //longitude
- {
- destlat = attribute.getValueDouble();
- } else if (attribute.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_START.getTypeID()) //longitude
- {
- lon = attribute.getValueDouble();
- } else if (attribute.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_END.getTypeID()) //longitude
- {
- destlon = attribute.getValueDouble();
- } else if (attribute.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME.getTypeID()) //longitude
- {
- name = attribute.getValueString();
- } else if (attribute.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_LOCATION.getTypeID()) //longitude
- {
- location = attribute.getValueString();
- }
- }
-
- // @@@ Should do something more fancy with these in KML and store them as a single point.
- String display = name;
- if (display.isEmpty()) {
- display = location;
- }
-
- if (lon != 0 && lat != 0) {
- out.write(NbBundle.getMessage(this.getClass(), "ReportKML.latLongStartPoint", lat, lon, display));
- }
- if (destlat != 0 && destlon != 0) {
- out.write(NbBundle.getMessage(this.getClass(), "ReportKML.latLongEndPoint", destlat, destlon,
- display));
- }
- }
-
- out.flush();
-
- progressPanel.increment();
- /*
- * Step 1: generate XML stub
- */
- Namespace ns = Namespace.getNamespace("", "http://earth.google.com/kml/2.2"); //NON-NLS
- // kml
- Element kml = new Element("kml", ns); //NON-NLS
- Document kmlDocument = new Document(kml);
-
- // Document
- Element document = new Element("Document", ns); //NON-NLS
- kml.addContent(document);
-
- // name
- Element name = new Element("name", ns); //NON-NLS
- name.setText("Java Generated KML Document"); //NON-NLS
- document.addContent(name);
-
- /*
- * Step 2: add in Style elements
- */
- // Style
- Element style = new Element("Style", ns); //NON-NLS
- style.setAttribute("id", "redIcon"); //NON-NLS
- document.addContent(style);
-
- // IconStyle
- Element iconStyle = new Element("IconStyle", ns); //NON-NLS
- style.addContent(iconStyle);
-
- // color
- Element color = new Element("color", ns); //NON-NLS
- color.setText("990000ff"); //NON-NLS
- iconStyle.addContent(color);
-
- // Icon
- Element icon = new Element("Icon", ns); //NON-NLS
- iconStyle.addContent(icon);
-
- // href
- Element href = new Element("href", ns); //NON-NLS
- href.setText("http://www.cs.mun.ca/~hoeber/teaching/cs4767/notes/02.1-kml/circle.png"); //NON-NLS
- icon.addContent(href);
- progressPanel.increment();
- /*
- * Step 3: read data from source location and add in a Placemark
- * for each data element
- */
-
- File file = new File(reportPath2);
- try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
- String line = reader.readLine();
- while (line != null) {
- String[] lineParts = line.split(";");
- if (lineParts.length > 1) {
- String coordinates = lineParts[1].trim() + "," + lineParts[0].trim(); //lat,lon
- // Placemark
- Element placemark = new Element("Placemark", ns); //NON-NLS
- document.addContent(placemark);
-
- if (lineParts.length == 4) {
- // name
- Element pmName = new Element("name", ns); //NON-NLS
- pmName.setText(lineParts[3].trim());
- placemark.addContent(pmName);
-
- String savedPath = lineParts[2].trim();
- if (savedPath.isEmpty() == false) {
- // Path
- Element pmPath = new Element("Path", ns); //NON-NLS
- pmPath.setText(savedPath);
- placemark.addContent(pmPath);
-
- // description
- Element pmDescription = new Element("description", ns); //NON-NLS
- String xml = "
" + featureType + ""); //NON-NLS
+
+ String name = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME);
+ if (name != null && !name.isEmpty()) {
+ result.append("Name: ").append(name).append(SEP); //NON-NLS
+ }
+
+ String location = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_LOCATION);
+ if (location != null && !location.isEmpty()) {
+ result.append("Location: ").append(location).append(SEP); //NON-NLS
+ }
+
+ Long timestamp = getLong(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME);
+ if (timestamp != null) {
+ result.append("Timestamp: ").append(getTimeStamp(timestamp)).append(SEP); //NON-NLS
+ result.append("Unix timestamp: ").append(timestamp).append(SEP); //NON-NLS
+ }
+
+ Long startingTimestamp = getLong(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START);
+ if (startingTimestamp != null) {
+ result.append("Starting Timestamp: ").append(getTimeStamp(startingTimestamp)).append(SEP); //NON-NLS
+ result.append("Starting Unix timestamp: ").append(startingTimestamp).append(SEP); //NON-NLS
+ }
+
+ Long endingTimestamp = getLong(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_END);
+ if (endingTimestamp != null) {
+ result.append("Ending Timestamp: ").append(getTimeStamp(endingTimestamp)).append(SEP); //NON-NLS
+ result.append("Ending Unix timestamp: ").append(endingTimestamp).append(SEP); //NON-NLS
+ }
+
+ Long createdTimestamp = getLong(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED);
+ if (createdTimestamp != null) {
+ result.append("Created Timestamp: ").append(getTimeStamp(createdTimestamp)).append(SEP); //NON-NLS
+ result.append("Created Unix timestamp: ").append(createdTimestamp).append(SEP); //NON-NLS
+ }
+
+ Double latitude = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE);
+ if (latitude != null) {
+ result.append("Latitude: ").append(latitude).append(SEP); //NON-NLS
+ }
+
+ Double longitude = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE);
+ if (longitude != null) {
+ result.append("Longitude: ").append(longitude).append(SEP); //NON-NLS
+ }
+
+ Double latitudeStart = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_START);
+ if (latitudeStart != null) {
+ result.append("Latitude Start: ").append(latitudeStart).append(SEP); //NON-NLS
+ }
+
+ Double longitudeStart = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_START);
+ if (longitudeStart != null) {
+ result.append("Longitude Start: ").append(longitudeStart).append(SEP); //NON-NLS
+ }
+
+ Double latitudeEnd = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_END);
+ if (latitudeEnd != null) {
+ result.append("Latitude End: ").append(latitudeEnd).append(SEP); //NON-NLS
+ }
+
+ Double longitudeEnd = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_END);
+ if (longitudeEnd != null) {
+ result.append("Longitude End: ").append(longitudeEnd).append(SEP); //NON-NLS
+ }
+
+ Double velocity = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_VELOCITY);
+ if (velocity != null) {
+ result.append("Velocity: ").append(velocity).append(SEP); //NON-NLS
+ }
+
+ Double altitude = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE);
+ if (altitude != null) {
+ result.append("Altitude: ").append(altitude).append(SEP); //NON-NLS
+ }
+
+ Double bearing = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_BEARING);
+ if (bearing != null) {
+ result.append("Bearing: ").append(bearing).append(SEP); //NON-NLS
+ }
+
+ Integer hPrecision = getInteger(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_HPRECISION);
+ if (hPrecision != null) {
+ result.append("Horizontal Precision Figure of Merit: ").append(hPrecision).append(SEP); //NON-NLS
+ }
+
+ Integer vPrecision = getInteger(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_VPRECISION);
+ if (vPrecision != null) {
+ result.append("Vertical Precision Figure of Merit: ").append(vPrecision).append(SEP); //NON-NLS
+ }
+
+ String mapDatum = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_MAPDATUM);
+ if (mapDatum != null && !mapDatum.isEmpty()) {
+ result.append("Map Datum: ").append(mapDatum).append(SEP); //NON-NLS
+ }
+
+ String programName = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME);
+ if (programName != null && !programName.isEmpty()) {
+ result.append("Reported by: ").append(programName).append(SEP); //NON-NLS
+ }
+
+ String flag = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_FLAG);
+ if (flag != null && !flag.isEmpty()) {
+ result.append("Flag: ").append(flag).append(SEP); //NON-NLS
+ }
+
+ String pathSource = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_SOURCE);
+ if (pathSource != null && !pathSource.isEmpty()) {
+ result.append("Source: ").append(pathSource).append(SEP); //NON-NLS
+ }
+
+ String deviceMake = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_MAKE);
+ if (deviceMake != null && !deviceMake.isEmpty()) {
+ result.append("Device Make: ").append(deviceMake).append(SEP); //NON-NLS
+ }
+
+ String deviceModel = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_MODEL);
+ if (deviceModel != null && !deviceModel.isEmpty()) {
+ result.append("Device Model: ").append(deviceModel).append(SEP); //NON-NLS
+ }
+
+ return result.toString();
+ }
+
+ private String getTimeStamp(long timeStamp) {
+ return kmlDateFormat.format(new java.util.Date(timeStamp * 1000));
+ }
+
+ /**
+ * Create a Point for use in a Placemark. Note in this method altitude is
+ * ignored, as Google Earth apparently has trouble using altitudes for
+ * LineStrings, though the parameters are still in the call. Also note that
+ * any null value passed in will be set to 0.0, under the idea that it is
+ * better to show some data with gaps, than to show nothing at all.
+ *
+ * @param latitude point latitude
+ * @param longitude point longitude
+ * @param altitude point altitude. Currently ignored.
+ *
+ * @return the Point as an Element
+ */
+ private Element makePoint(Double latitude, Double longitude, Double altitude) {
+ if (latitude == null) {
+ latitude = 0.0;
+ }
+ if (longitude == null) {
+ longitude = 0.0;
+ }
+ if (altitude == null) {
+ altitude = 0.0;
+ }
+ Element point = new Element("Point", ns); //NON-NLS
+
+ // KML uses lon, lat. Deliberately reversed.
+ Element coordinates = new Element("coordinates", ns).addContent(longitude + "," + latitude + "," + altitude); //NON-NLS
+
+ if (altitude != 0) {
+ /*
+ Though we are including a non-zero altitude, clamp it to the
+ ground because inaccuracies from the GPS data can cause the terrain
+ to occlude points when zoomed in otherwise. Show the altitude, but
+ keep the point clamped to the ground. We may change this later for
+ flying GPS sensors.
+ */
+ Element altitudeMode = new Element("altitudeMode", ns).addContent("clampToGround"); //NON-NLS
+ point.addContent(altitudeMode);
+ }
+ point.addContent(coordinates);
+
+ return point;
+ }
+
+ /**
+ * Create a LineString for use in a Placemark. Note in this method, start
+ * and stop altitudes get ignored, as Google Earth apparently has trouble
+ * using altitudes for LineStrings, though the parameters are still in the
+ * call. Also note that any null value passed in will be set to 0.0, under
+ * the idea that it is better to show some data with gaps, than to show
+ * nothing at all.
+ *
+ * @param startLatitude Starting latitude
+ * @param startLongitude Starting longitude
+ * @param startAltitude Starting altitude. Currently ignored.
+ * @param stopLatitude Ending latitude
+ * @param stopLongitude Ending longitude
+ * @param stopAltitude Ending altitude. Currently ignored.
+ *
+ * @return the Line as an Element
+ */
+ private Element makeLineString(Double startLatitude, Double startLongitude, Double startAltitude, Double stopLatitude, Double stopLongitude, Double stopAltitude) {
+ if (startLatitude == null) {
+ startLatitude = 0.0;
+ }
+ if (startLongitude == null) {
+ startLongitude = 0.0;
+ }
+ if (startAltitude == null) {
+ startAltitude = 0.0;
+ }
+ if (stopLatitude == null) {
+ stopLatitude = 0.0;
+ }
+ if (stopLongitude == null) {
+ stopLongitude = 0.0;
+ }
+ if (stopAltitude == null) {
+ stopAltitude = 0.0;
+ }
+
+ Element lineString = new Element("LineString", ns); //NON-NLS
+ lineString.addContent(new Element("extrude", ns).addContent("1")); //NON-NLS
+ lineString.addContent(new Element("tessellate", ns).addContent("1")); //NON-NLS
+ lineString.addContent(new Element("altitudeMode", ns).addContent("clampToGround")); //NON-NLS
+ // KML uses lon, lat. Deliberately reversed.
+ lineString.addContent(new Element("coordinates", ns).addContent(
+ startLongitude + "," + startLatitude + ",0.0,"
+ + stopLongitude + "," + stopLatitude + ",0.0")); //NON-NLS
+ return lineString;
+ }
+
+ /**
+ * Make a Placemark for use in displaying features. Takes a
+ * coordinate-bearing feature (Point, LineString, etc) and places it in the
+ * Placemark element.
+ *
+ * @param name Placemark name
+ * @param color Placemark color
+ * @param description Description for the info bubble on the map
+ * @param timestamp Placemark timestamp
+ * @param feature The feature to show. Could be Point, LineString, etc.
+ * @param coordinates The coordinates to display in the list view snippet
+ *
+ * @return the entire KML placemark
+ */
+ private Element makePlacemark(String name, FeatureColor color, String description, Long timestamp, Element feature, String coordinates) {
+ Element placemark = new Element("Placemark", ns); //NON-NLS
+ if (name != null && !name.isEmpty()) {
+ placemark.addContent(new Element("name", ns).addContent(name)); //NON-NLS
+ } else if (timestamp != null) {
+ placemark.addContent(new Element("name", ns).addContent(getTimeStamp(timestamp))); //NON-NLS
+ } else {
+ placemark.addContent(new Element("name", ns).addContent("")); //NON-NLS
+ }
+ placemark.addContent(new Element("styleUrl", ns).addContent(color.getColor())); //NON-NLS
+ placemark.addContent(new Element("description", ns).addContent(description)); //NON-NLS
+ if (timestamp != null) {
+ Element time = new Element("TimeStamp", ns); //NON-NLS
+ time.addContent(new Element("when", ns).addContent(getTimeStamp(timestamp))); //NON-NLS
+ placemark.addContent(time);
+ }
+ placemark.addContent(feature);
+ if (coordinates != null && !coordinates.isEmpty()) {
+ placemark.addContent(new Element("snippet", ns).addContent(coordinates)); //NON-NLS
+ }
+ return placemark;
+ }
+
+ /**
+ * Make a Placemark for use in displaying features. Takes a
+ * coordinate-bearing feature (Point, LineString, etc) and places it in the
+ * Placemark element.
+ *
+ * @param name Placemark name
+ * @param color Placemark color
+ * @param description Description for the info bubble on the map
+ * @param timestamp Placemark timestamp
+ * @param feature The feature to show. Could be Point, LineString, etc.
+ * @param path The path to the file on disk
+ * @param coordinates The coordinates to display in the list view snippet
+ *
+ * @return the entire KML Placemark, including a picture.
+ */
+ private Element makePlacemarkWithPicture(String name, FeatureColor color, String description, Long timestamp, Element feature, Path path, String coordinates) {
+ Element placemark = new Element("Placemark", ns); //NON-NLS
+ if (name != null && !name.isEmpty()) {
+ placemark.addContent(new Element("name", ns).addContent(name)); //NON-NLS
+ }
+
+ placemark.addContent(new Element("styleUrl", ns).addContent(color.getColor())); //NON-NLS
+ Element desc = new Element("description", ns); //NON-NLS
+
+ if (path != null) {
+ String pathAsString = path.toString();
+ if (pathAsString != null && !pathAsString.isEmpty()) {
+ String image = "
"; //NON-NLS
+ desc.addContent(image);
+ }
+ }
+
+ desc.addContent(description + "File Path: " + path.toString());
+ placemark.addContent(desc);
+ if (timestamp != null) {
+ Element time = new Element("TimeStamp", ns); //NON-NLS
+ time.addContent(new Element("when", ns).addContent(getTimeStamp(timestamp))); //NON-NLS
+ placemark.addContent(time);
+ }
+ placemark.addContent(feature);
+ if (coordinates != null && !coordinates.isEmpty()) {
+ placemark.addContent(new Element("snippet", ns).addContent(coordinates)); //NON-NLS
+ }
+ return placemark;
+ }
+
+ /**
+ * Extracts the file to the output folder.
+ *
+ * @param inputFile The input AbstractFile to copy
+ * @param outputFile the output file
+ *
+ * @throws IOException
+ */
+ private void copyFileUsingStream(AbstractFile inputFile, File outputFile) throws IOException {
+ byte[] buffer = new byte[65536];
+ int length;
+ outputFile.createNewFile();
+ try (InputStream is = new ReadContentInputStream(inputFile);
+ OutputStream os = new FileOutputStream(outputFile)) {
while ((length = is.read(buffer)) != -1) {
os.write(buffer, 0, length);
}
-
- } finally {
- is.close();
- os.close();
}
}
diff --git a/Core/src/org/sleuthkit/autopsy/report/stylesheets/style.kml b/Core/src/org/sleuthkit/autopsy/report/stylesheets/style.kml
new file mode 100755
index 0000000000..aeb3992df1
--- /dev/null
+++ b/Core/src/org/sleuthkit/autopsy/report/stylesheets/style.kml
@@ -0,0 +1,299 @@
+
+
+
+
+
+ normal
+ #n_YellowPushpin
+
+
+ highlight
+ #h_YellowPushpin
+
+
+
+
+
+
+ normal
+ #n_bluePushpin
+
+
+ highlight
+ #h_bluePushpin
+
+
+
+
+
+
+ normal
+ #n_redPushpin
+
+
+ highlight
+ #h_redPushpin
+
+
+
+
+
+
+ normal
+ #n_greenPushpin
+
+
+ highlight
+ #h_greenPushpin
+
+
+
+
+
+
+ normal
+ #n_purplePushpin
+
+
+ highlight
+ #h_purplePushpin
+
+
+
+
+
+
+ normal
+ #n_whitePushpin
+
+
+ highlight
+ #h_whitePushpin
+
+
+
+
+
+
\ No newline at end of file
diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalEditListPanel.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalEditListPanel.java
index 5b3364451e..c848b2f8ff 100644
--- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalEditListPanel.java
+++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalEditListPanel.java
@@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
- * Copyright 2011-2015 Basis Technology Corp.
+ * Copyright 2011-2016 Basis Technology Corp.
* Contact: carrier sleuthkit org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -45,6 +45,13 @@ import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.corecomponents.OptionsPanel;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.ingest.IngestManager;
+import java.awt.Component;
+import javax.swing.ImageIcon;
+import javax.swing.JLabel;
+import static javax.swing.SwingConstants.CENTER;
+import javax.swing.table.DefaultTableCellRenderer;
+import javax.swing.table.TableCellRenderer;
+import org.openide.util.NbBundle.Messages;
/**
* GlobalEditListPanel widget to manage keywords in lists
@@ -52,7 +59,8 @@ import org.sleuthkit.autopsy.ingest.IngestManager;
class GlobalEditListPanel extends javax.swing.JPanel implements ListSelectionListener, OptionsPanel {
private static final Logger logger = Logger.getLogger(GlobalEditListPanel.class.getName());
- private KeywordTableModel tableModel;
+ private static final long serialVersionUID = 1L;
+ private final KeywordTableModel tableModel;
private KeywordList currentKeywordList;
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
@@ -85,7 +93,8 @@ class GlobalEditListPanel extends javax.swing.JPanel implements ListSelectionLis
column.setPreferredWidth(((int) (width * 0.90)));
} else {
column.setPreferredWidth(((int) (width * 0.10)));
- //column.setCellRenderer(new CheckBoxRenderer());
+ column.setCellRenderer(new CheckBoxRenderer());
+ column.setHeaderRenderer(new HeaderRenderer(keywordTable));
}
}
keywordTable.setCellSelectionEnabled(false);
@@ -686,4 +695,55 @@ class GlobalEditListPanel extends javax.swing.JPanel implements ListSelectionLis
resync();
}
}
+
+ /**
+ * A cell renderer for boolean cells that shows a center-aligned green check
+ * mark if true, nothing if false.
+ */
+ private class CheckBoxRenderer extends DefaultTableCellRenderer {
+
+ private static final long serialVersionUID = 1L;
+ final ImageIcon theCheck = new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/checkmark.png")); // NON-NLS
+
+ CheckBoxRenderer() {
+ setHorizontalAlignment(CENTER);
+ }
+
+ @Override
+ @Messages("IsRegularExpression=Keyword is a regular expression")
+ public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
+
+ if ((value instanceof Boolean)) {
+ if ((Boolean) value) {
+ setIcon(theCheck);
+ setToolTipText(Bundle.IsRegularExpression());
+ } else {
+ setIcon(null);
+ setToolTipText(null);
+ }
+ }
+ return this;
+ }
+ }
+
+ /**
+ * A cell renderer for header cells that center-aligns the header text.
+ */
+ private static class HeaderRenderer implements TableCellRenderer {
+
+ private DefaultTableCellRenderer renderer;
+
+ public HeaderRenderer(JTable table) {
+ renderer = (DefaultTableCellRenderer) table.getTableHeader().getDefaultRenderer();
+ renderer.setHorizontalAlignment(JLabel.CENTER);
+ }
+
+ @Override
+ public Component getTableCellRendererComponent(
+ JTable table, Object value, boolean isSelected,
+ boolean hasFocus, int row, int col) {
+ return renderer.getTableCellRendererComponent(
+ table, value, isSelected, hasFocus, row, col);
+ }
+ }
}
diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/checkmark.png b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/checkmark.png
new file mode 100755
index 0000000000..31d82447c8
Binary files /dev/null and b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/checkmark.png differ