Merge branch 'develop' into 5465-CASE-UCO-reference-local-files

This commit is contained in:
U-BASIS\dsmyda 2020-01-23 11:04:32 -05:00
commit c0959ce934
39 changed files with 1891 additions and 395 deletions

View File

@ -15,7 +15,7 @@
<target name="-download-ivy" unless="ivy.available">
<mkdir dir="${ivy.jar.dir}"/>
<get src="http://repo2.maven.org/maven2/org/apache/ivy/ivy/${ivy.install.version}/ivy-${ivy.install.version}.jar"
<get src="https://repo1.maven.org/maven2/org/apache/ivy/ivy/${ivy.install.version}/ivy-${ivy.install.version}.jar"
dest="${ivy.jar.file}" usetimestamp="true"/>
</target>

View File

@ -74,6 +74,7 @@
<copy file="${thirdparty.dir}/stix/StixLib.jar" todir="${ext.dir}" />
<copy file="${thirdparty.dir}/jdom/jdom-2.0.5.jar" todir="${ext.dir}" />
<copy file="${thirdparty.dir}/jdom/jdom-2.0.5-contrib.jar" todir="${ext.dir}" />
<copy file="${thirdparty.dir}/DatCon/3.6.9/DatCon.jar" todir="${ext.dir}" />
</target>

View File

@ -2,7 +2,7 @@
<settings defaultResolver="main"/>
<resolvers>
<chain name="main">
<ibiblio name="central" m2compatible="true"/>
<ibiblio name="central" root="https://repo1.maven.org/maven2" m2compatible="true"/>
<ibiblio name="maven.restlet.org" root="http://maven.restlet.com" m2compatible="true" />
<ibiblio name="jitpack.io" root="https://jitpack.io" m2compatible="true" />
</chain>

View File

@ -120,6 +120,7 @@ file.reference.okhttp-2.7.5-javadoc.jar=release/modules/ext/okhttp-2.7.5-javadoc
file.reference.okhttp-2.7.5-sources.jar=release/modules/ext/okhttp-2.7.5-sources.jar
file.reference.okhttp-2.7.5.jar=release/modules/ext/okhttp-2.7.5.jar
file.reference.okio-1.6.0.jar=release/modules/ext/okio-1.6.0.jar
file.reference.datcon.jar=release/modules/ext/DatCon.jar
javac.source=1.8
javac.compilerargs=-Xlint -Xlint:-serial
license.file=../LICENSE-2.0.txt

View File

@ -803,6 +803,10 @@
<class-path-extension>
<runtime-relative-path>ext/jutf7-1.0.0.jar</runtime-relative-path>
<binary-origin>release/modules/ext/jutf7-1.0.0.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/DatCon.jar</runtime-relative-path>
<binary-origin>release/modules/ext/DatCon.jar</binary-origin>
</class-path-extension>
</data>
</configuration>

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011 Basis Technology Corp.
* Copyright 2012-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -23,6 +23,7 @@ import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
@ -30,47 +31,57 @@ import java.util.Set;
import java.util.logging.Level;
/**
* This class contains the framework to read, add, update, and remove from the
* property files located at %USERDIR%/Config/x.properties
* Provides utility methods for creating, updating, and deleting Java properties
* files with paths such as %USERDIR%/Config/[module name].properties, where
* "module name" is intended to be a module name and the properties file is
* intended to be a settings file for the module.
*
* Very coarse-grained thread safety is provided by these utilities if all
* modules confine themselves to their use when manipulating their settings
* files, with the consequence of serializing all such operations across the
* entire application.
*
* TODO (JIRA-5964): The error handling in this class is not consistent with
* Autopsy error handling policy.
*/
public class ModuleSettings {
// The directory where the properties file is located
private final static String moduleDirPath = PlatformUtil.getUserConfigDirectory();
private final static Logger logger = Logger.getLogger(ModuleSettings.class.getName());
private final static String MODULE_DIR_PATH = PlatformUtil.getUserConfigDirectory();
private final static String SETTINGS_FILE_EXT = ".properties";
/*
* These SHOULD NOT be public and DO NOT belong in this file. They are being
* retained only for the sake of backwards compatibility.
*/
public static final String DEFAULT_CONTEXT = "GeneralContext"; //NON-NLS
public static final String MAIN_SETTINGS = "Case"; //NON-NLS
public static final String CURRENT_CASE_TYPE = "Current_Case_Type"; //NON-NLS
/**
* the constructor
*/
private ModuleSettings() {
}
/**
* Makes a new config file of the specified name. Do not include the
* extension.
* Makes a new settings file for a module.
*
* @param moduleName - The name of the config file to make
* @param moduleName The module name.
*
* @return True if successfully created, false if already exists or an error
* is thrown.
* @return True if the settings file was created, false if the file already
* existed or could not be created.
*/
public static boolean makeConfigFile(String moduleName) {
public static synchronized boolean makeConfigFile(String moduleName) {
if (!configExists(moduleName)) {
File propPath = new File(moduleDirPath + File.separator + moduleName + ".properties");
File propPath = new File(getSettingsFilePath(moduleName));
File parent = new File(propPath.getParent());
if (!parent.exists()) {
parent.mkdirs();
}
Properties props = new Properties();
try {
propPath.createNewFile();
FileOutputStream fos = new FileOutputStream(propPath);
props.store(fos, "");
fos.close();
} catch (IOException e) {
Logger.getLogger(ModuleSettings.class.getName()).log(Level.WARNING, "Was not able to create a new properties file.", e); //NON-NLS
try (FileOutputStream fos = new FileOutputStream(propPath)) {
props.store(fos, "Created module settings file");
}
} catch (IOException ex) {
logger.log(Level.SEVERE, String.format("Failed to create module settings file at %s)", propPath), ex); //NON-NLS
return false;
}
return true;
@ -79,213 +90,217 @@ public class ModuleSettings {
}
/**
* Determines if a given properties file exists or not.
* Indicates whether or not a settings file exists for a given module.
*
* @param moduleName - The name of the config file to evaluate
* @param moduleName The module name.
*
* @return true if the config exists, false otherwise.
* @return True or false.
*/
public static boolean configExists(String moduleName) {
File f = new File(moduleDirPath + File.separator + moduleName + ".properties");
return f.exists();
public static synchronized boolean configExists(String moduleName) {
return new File(getSettingsFilePath(moduleName)).exists();
}
public static boolean settingExists(String moduleName, String settingName) {
/**
* Determines whether or not a given setting exists in the settings file for
* a module.
*
* @param moduleName The module name.
* @param settingName The name of the setting (property).
*
* @return True if the setting file exists, can be read, and contains the
* specified setting (property), false otherwise.
*/
public static synchronized boolean settingExists(String moduleName, String settingName) {
if (!configExists(moduleName)) {
return false;
}
try {
Properties props = fetchProperties(moduleName);
return (props.getProperty(settingName) != null);
} catch (IOException e) {
} catch (IOException ex) {
logger.log(Level.SEVERE, String.format("Failed to get %s setting from module settings file at %s)", settingName, getSettingsFilePath(moduleName)), ex); //NON-NLS
return false;
}
}
/**
* Returns the path of the given properties file.
* Constructs a settings file path for a given module.
*
* @param moduleName - The name of the config file to evaluate
* @param moduleName The module name.
*
* @return The path of the given config file. Returns null if the config
* file doesn't exist.
* @return The settings file path as a string.
*/
private static String getPropertyPath(String moduleName) {
if (configExists(moduleName)) {
return moduleDirPath + File.separator + moduleName + ".properties"; //NON-NLS
}
return null;
private static String getSettingsFilePath(String moduleName) {
return Paths.get(MODULE_DIR_PATH, moduleName + SETTINGS_FILE_EXT).toString();
}
/**
* Returns the given properties file's setting as specific by settingName.
* Gets the value of a setting (property) from a module settings file.
*
* @param moduleName - The name of the config file to read from.
* @param settingName - The setting name to retrieve.
* NOTE: If the settings file does not already exist, it is created.
*
* @return - the value associated with the setting.
* @param moduleName The module name.
* @param settingName The setting name.
*
* @throws IOException
* @return The value of the setting or null if the file cannot be read or
* the setting is not found.
*/
public static String getConfigSetting(String moduleName, String settingName) {
public static synchronized String getConfigSetting(String moduleName, String settingName) {
if (!configExists(moduleName)) {
makeConfigFile(moduleName);
Logger.getLogger(ModuleSettings.class.getName()).log(Level.INFO, "File did not exist. Created file [" + moduleName + ".properties]"); //NON-NLS NON-NLS
}
try {
Properties props = fetchProperties(moduleName);
return props.getProperty(settingName);
} catch (IOException e) {
Logger.getLogger(ModuleSettings.class.getName()).log(Level.WARNING, "Could not read config file [" + moduleName + "]", e); //NON-NLS
} catch (IOException ex) {
logger.log(Level.SEVERE, String.format("Failed to get %s setting from module settings file at %s)", settingName, getSettingsFilePath(moduleName)), ex); //NON-NLS
return null;
}
}
/**
* Returns the given properties file's map of settings.
* Gets the settings (properties) from a module settings file.
*
* @param moduleName - the name of the config file to read from.
* NOTE: If the settings file does not already exist, it is created.
*
* @return - the map of all key:value pairs representing the settings of the
* config.
* @param moduleName The module name.
*
* @throws IOException
* @return A mapping of setting names to setting values from the settings
* file, may be empty.
*/
public static Map< String, String> getConfigSettings(String moduleName) {
public static synchronized Map<String, String> getConfigSettings(String moduleName) {
if (!configExists(moduleName)) {
makeConfigFile(moduleName);
Logger.getLogger(ModuleSettings.class.getName()).log(Level.INFO, "File did not exist. Created file [" + moduleName + ".properties]"); //NON-NLS NON-NLS
}
try {
Properties props = fetchProperties(moduleName);
Set<String> keys = props.stringPropertyNames();
Map<String, String> map = new HashMap<String, String>();
Map<String, String> map = new HashMap<>();
for (String s : keys) {
map.put(s, props.getProperty(s));
}
return map;
} catch (IOException e) {
Logger.getLogger(ModuleSettings.class.getName()).log(Level.WARNING, "Could not read config file [" + moduleName + "]", e); //NON-NLS
} catch (IOException ex) {
logger.log(Level.SEVERE, String.format("Failed to get settings from module settings file at %s)", getSettingsFilePath(moduleName)), ex); //NON-NLS
return null;
}
}
/**
* Sets the given properties file to the given setting map.
* Adds a mapping of setting name to setting values to a module settings
* file.
*
* @param moduleName - The name of the module to be written to.
* @param settings - The mapping of all key:value pairs of settings to add
* to the config.
* NOTE: If the settings file does not already exist, it is created.
*
* @param moduleName The module name.
* @param settings The module settings.
*/
public static synchronized void setConfigSettings(String moduleName, Map<String, String> settings) {
if (!configExists(moduleName)) {
makeConfigFile(moduleName);
Logger.getLogger(ModuleSettings.class.getName()).log(Level.INFO, "File did not exist. Created file [" + moduleName + ".properties]"); //NON-NLS NON-NLS
}
try {
Properties props = fetchProperties(moduleName);
for (Map.Entry<String, String> kvp : settings.entrySet()) {
props.setProperty(kvp.getKey(), kvp.getValue());
}
File path = new File(getPropertyPath(moduleName));
FileOutputStream fos = new FileOutputStream(path);
props.store(fos, "Changed config settings(batch)"); //NON-NLS
fos.close();
} catch (IOException e) {
Logger.getLogger(ModuleSettings.class.getName()).log(Level.WARNING, "Property file exists for [" + moduleName + "] at [" + getPropertyPath(moduleName) + "] but could not be loaded.", e); //NON-NLS NON-NLS NON-NLS
File path = new File(getSettingsFilePath(moduleName));
try (FileOutputStream fos = new FileOutputStream(path)) {
props.store(fos, "Set settings (batch)"); //NON-NLS
}
} catch (IOException ex) {
logger.log(Level.SEVERE, String.format("Error writing to module settings file at %s)", getSettingsFilePath(moduleName)), ex); //NON-NLS
}
}
/**
* Sets the given properties file to the given settings.
* Sets the value of a setting (property) in a module settings file.
*
* @param moduleName - The name of the module to be written to.
* @param settingName - The name of the setting to be modified.
* @param settingVal - the value to set the setting to.
* NOTE: If the settings file does not already exist, it is created.
*
* @param moduleName The module name.
* @param settingName The setting name.
* @param settingVal The setting value.
*/
public static synchronized void setConfigSetting(String moduleName, String settingName, String settingVal) {
if (!configExists(moduleName)) {
makeConfigFile(moduleName);
Logger.getLogger(ModuleSettings.class.getName()).log(Level.INFO, "File did not exist. Created file [" + moduleName + ".properties]"); //NON-NLS NON-NLS
}
try {
Properties props = fetchProperties(moduleName);
props.setProperty(settingName, settingVal);
File path = new File(getPropertyPath(moduleName));
FileOutputStream fos = new FileOutputStream(path);
props.store(fos, "Changed config settings(single)"); //NON-NLS
fos.close();
} catch (IOException e) {
Logger.getLogger(ModuleSettings.class.getName()).log(Level.WARNING, "Property file exists for [" + moduleName + "] at [" + getPropertyPath(moduleName) + "] but could not be loaded.", e); //NON-NLS NON-NLS NON-NLS
}
}
/**
* Removes the given key from the given properties file.
*
* @param moduleName - The name of the properties file to be modified.
* @param key - the name of the key to remove.
*/
public static synchronized void removeProperty(String moduleName, String key) {
try {
if (getConfigSetting(moduleName, key) != null) {
Properties props = fetchProperties(moduleName);
props.remove(key);
File path = new File(getPropertyPath(moduleName));
FileOutputStream fos = new FileOutputStream(path);
props.store(fos, "Removed " + key); //NON-NLS
fos.close();
File path = new File(getSettingsFilePath(moduleName));
try (FileOutputStream fos = new FileOutputStream(path)) {
props.store(fos, "Set " + settingName); //NON-NLS
}
} catch (IOException e) {
Logger.getLogger(ModuleSettings.class.getName()).log(Level.WARNING, "Could not remove property from file, file not found", e); //NON-NLS
} catch (IOException ex) {
logger.log(Level.SEVERE, String.format("Error writing %s setting to module settings file at %s)", settingName, getSettingsFilePath(moduleName)), ex); //NON-NLS
}
}
/**
* Returns the properties file as specified by moduleName.
* Removes a setting (property) in a module settings file.
*
* @param moduleName
*
* @return Properties file as specified by moduleName.
*
* @throws IOException
* @param moduleName The module name.
* @param settingName The setting name.
*/
private static Properties fetchProperties(String moduleName) throws IOException {
InputStream inputStream = new FileInputStream(getPropertyPath(moduleName));
Properties props = new Properties();
props.load(inputStream);
inputStream.close();
public static synchronized void removeProperty(String moduleName, String settingName) {
try {
if (getConfigSetting(moduleName, settingName) != null) {
Properties props = fetchProperties(moduleName);
props.remove(settingName);
File path = new File(getSettingsFilePath(moduleName));
try (FileOutputStream fos = new FileOutputStream(path)) {
props.store(fos, "Removed " + settingName); //NON-NLS
}
}
} catch (IOException ex) {
logger.log(Level.SEVERE, String.format("Error removing %s setting from module settings file at %s)", settingName, getSettingsFilePath(moduleName)), ex); //NON-NLS
}
}
/**
* Gets the contents of a module settings file as a Properties object.
*
* @param moduleName The module name.
*
* @return The Properties object.
*
* @throws IOException If there is a problem reading the settings file.
*/
private static synchronized Properties fetchProperties(String moduleName) throws IOException {
Properties props;
try (InputStream inputStream = new FileInputStream(getSettingsFilePath(moduleName))) {
props = new Properties();
props.load(inputStream);
}
return props;
}
/**
* Gets the property file as specified.
* Gets a File object for a module settings (properties) file.
*
* @param moduleName
* @param moduleName The module name.
*
* @return A new file handle, returns null if the file does not exist.
* @return The File object or null if the file does not exist.
*/
public static File getPropertyFile(String moduleName) {
String path = getPropertyPath(moduleName);
if (path == null) {
return null;
} else {
return new File(getPropertyPath(moduleName));
public static synchronized File getPropertyFile(String moduleName) {
File configFile = null;
if (configExists(moduleName)) {
configFile = new File(getSettingsFilePath(moduleName));
}
return configFile;
}
/**
* Private constructor to prevent instantiation of this utility class.
*/
private ModuleSettings() {
}
}

View File

@ -286,8 +286,14 @@ public abstract class BaseChildFactory<T extends Content> extends ChildFactory.D
* If pageSize is set split keys into pages, otherwise create a
* single page containing all keys.
*/
if (keys.isEmpty()) {
pages.clear();
if (keys.isEmpty() && !pages.isEmpty()) {
/**
* If we previously had keys (i.e. pages is not empty) and now
* we don't have keys, reset pages to an empty list.
* Cannot use a call to List.clear() here because the call to
* Lists.partition() below returns an unmodifiable list.
*/
pages = new ArrayList<>();
} else {
pages = Lists.partition(keys, pageSize > 0 ? pageSize : keys.size());
}

View File

@ -312,7 +312,11 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
}
if (displayName.isEmpty() && artifact != null) {
displayName = artifact.getName();
try {
displayName = Case.getCurrentCaseThrows().getSleuthkitCase().getAbstractFileById(this.artifact.getObjectID()).getName();
} catch (TskCoreException | NoCurrentCaseException ex) {
displayName = artifact.getName();
}
}
this.setDisplayName(displayName);
@ -837,7 +841,9 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|| attributeTypeID == ATTRIBUTE_TYPE.TSK_TAGGED_ARTIFACT.getTypeID()
|| attributeTypeID == ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT.getTypeID()
|| attributeTypeID == ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()
|| attributeTypeID == ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE.getTypeID()) {
|| attributeTypeID == ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE.getTypeID()
|| attribute.getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.JSON) {
continue;
} else if (artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()) {
addEmailMsgProperty(map, attribute);
} else if (attribute.getAttributeType().getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME) {

View File

@ -35,4 +35,5 @@ GeolocationSettingsPanel.zipFileBrowseBnt.text=Browse
GeolocationSettingsPanel.mbtileTestBtn.text=Test
GeolocationTopComponent.reportButton.text=KML Report
GeolocationTopComponent.coordLabel.text=
MapPanel.zoomInBtn.text=
MapPanel.zoomOutBtn.text=

View File

@ -11,11 +11,11 @@ GeolocationSettings_mbtile_test_success_message=The supplied file is a valid mbt
GeolocationSettings_mbtile_test_success_title=Success
GeolocationSettings_path_not_valid_message=The supplied file path is empty.\nPlease supply a valid file path.
GeolocationSettings_path_not_valid_title=File Not Valid
GeolocationSettingsPanel_malformed_url_message=The supplies OSM tile server address is invalid.\nPlease supply a well formed url prefixed with http://
GeolocationSettingsPanel_malformed_url_message=The supplied OSM tile server address is invalid.\nPlease supply a well formed url prefixed with http://
GeolocationSettingsPanel_malformed_url_message_tile=Malformed URL
GeolocationSettingsPanel_osm_server_test_fail_message=OSM tile server test failed.\nUnable to connect to server.
GeolocationSettingsPanel_osm_server_test_fail_message_title=Error
GeolocationSettingsPanel_osm_server_test_success_message=The provide OSM tile server address is valid.
GeolocationSettingsPanel_osm_server_test_success_message=The provided OSM tile server address is valid.
GeolocationSettingsPanel_osm_server_test_success_message_title=Success
GeolocationTC_connection_failure_message=Failed to connect to map title source.\nPlease review map source in Options dialog.
GeolocationTC_connection_failure_message_title=Connection Failure
@ -24,11 +24,11 @@ GeolocationTC_KML_report_title=KML Report
GeolocationTC_report_progress_title=KML Report Progress
GeoTopComponent_filer_data_invalid_msg=Unable to run waypoint filter.\nPlease select one or more data sources.
GeoTopComponent_filer_data_invalid_Title=Filter Failure
GeoTopComponent_filter_exception_msg=Exception occured during waypoint filtering.
GeoTopComponent_filter_exception_msg=Exception occurred during waypoint filtering.
GeoTopComponent_filter_exception_Title=Filter Failure
GeoTopComponent_no_waypoints_returned_mgs=Applied filter failed to find waypoints that matched criteria.\nRevise filter options and try again.
GeoTopComponent_no_waypoints_returned_Title=No Waypoints Found
GLTopComponent_initilzation_error=An error occurred during waypoint initilization. Geolocation data maybe incomplete.
GLTopComponent_initilzation_error=An error occurred during waypoint initilization. Geolocation data maybe incomplete.
GLTopComponent_name=Geolocation
HidingPane_default_title=Filters
MapPanel_connection_failure_message=Failed to connect to new geolocation map tile source.
@ -70,5 +70,6 @@ GeolocationSettingsPanel.zipFileBrowseBnt.text=Browse
GeolocationSettingsPanel.mbtileTestBtn.text=Test
GeolocationTopComponent.reportButton.text=KML Report
GeolocationTopComponent.coordLabel.text=
MapPanel.zoomInBtn.text=
MapPanel.zoomOutBtn.text=
WaypointExtractAction_label=Extract Files(s)

View File

@ -344,11 +344,11 @@ final class GeolocationSettingsPanel extends javax.swing.JPanel implements Optio
}//GEN-LAST:event_zipFileRBntActionPerformed
@Messages({
"GeolocationSettingsPanel_malformed_url_message=The supplies OSM tile server address is invalid.\nPlease supply a well formed url prefixed with http://",
"GeolocationSettingsPanel_malformed_url_message=The supplied OSM tile server address is invalid.\nPlease supply a well formed url prefixed with http://",
"GeolocationSettingsPanel_malformed_url_message_tile=Malformed URL",
"GeolocationSettingsPanel_osm_server_test_fail_message=OSM tile server test failed.\nUnable to connect to server.",
"GeolocationSettingsPanel_osm_server_test_fail_message_title=Error",
"GeolocationSettingsPanel_osm_server_test_success_message=The provide OSM tile server address is valid.",
"GeolocationSettingsPanel_osm_server_test_success_message=The provided OSM tile server address is valid.",
"GeolocationSettingsPanel_osm_server_test_success_message_title=Success",})
private void serverTestBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_serverTestBtnActionPerformed
String address = osmServerAddressField.getText();

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 Basis Technology Corp.
* Copyright 2019-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.geolocation;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
@ -30,6 +31,7 @@ import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.EnumSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
@ -49,6 +51,7 @@ import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.geolocation.GeoFilterPanel.GeoFilter;
import org.sleuthkit.autopsy.geolocation.datamodel.GeoLocationDataException;
import org.sleuthkit.autopsy.geolocation.datamodel.Track;
import org.sleuthkit.autopsy.geolocation.datamodel.Waypoint;
import org.sleuthkit.autopsy.geolocation.datamodel.WaypointBuilder;
import org.sleuthkit.autopsy.geolocation.datamodel.WaypointBuilder.WaypointFilterQueryCallBack;
@ -85,12 +88,12 @@ public final class GeolocationTopComponent extends TopComponent {
// This is the hardcoded report name from KMLReport.java
private static final String REPORT_KML = "ReportKML.kml";
private boolean mapInitalized = false;
@Messages({
"GLTopComponent_name=Geolocation",
"GLTopComponent_initilzation_error=An error occurred during waypoint initilization. Geolocation data maybe incomplete."
"GLTopComponent_initilzation_error=An error occurred during waypoint initilization. Geolocation data maybe incomplete."
})
/**
@ -113,13 +116,14 @@ public final class GeolocationTopComponent extends TopComponent {
|| eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_LAST_KNOWN_LOCATION.getTypeID()
|| eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_ROUTE.getTypeID()
|| eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF.getTypeID()
|| eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_BOOKMARK.getTypeID())) {
|| eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_BOOKMARK.getTypeID()
|| eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACK.getTypeID())) {
showRefreshPanel(true);
}
}
};
this.caseEventListener = pce -> {
mapPanel.clearWaypoints();
if (pce.getNewValue() != null) {
@ -148,7 +152,7 @@ public final class GeolocationTopComponent extends TopComponent {
filterPane.setPanel(geoFilterPanel);
geoFilterPanel.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
public void actionPerformed(ActionEvent e) {
updateWaypoints();
}
});
@ -186,7 +190,7 @@ public final class GeolocationTopComponent extends TopComponent {
public void componentOpened() {
super.componentOpened();
WindowManager.getDefault().setTopComponentFloating(this, true);
}
@Messages({
@ -199,7 +203,7 @@ public final class GeolocationTopComponent extends TopComponent {
mapPanel.clearWaypoints();
geoFilterPanel.clearDataSourceList();
geoFilterPanel.updateDataSourceList();
// Let's make sure we only do this on the first open
if (!mapInitalized) {
try {
@ -217,7 +221,7 @@ public final class GeolocationTopComponent extends TopComponent {
return; // Doen't set the waypoints.
}
}
mapPanel.setWaypoints(new ArrayList<>());
mapPanel.setWaypoints(new LinkedHashSet<>());
updateWaypoints();
}
@ -227,12 +231,27 @@ public final class GeolocationTopComponent extends TopComponent {
* @param show Whether to show or hide the panel.
*/
private void showRefreshPanel(boolean show) {
if (show) {
mapPanel.add(refreshPanel, BorderLayout.NORTH);
} else {
mapPanel.remove(refreshPanel);
}
mapPanel.revalidate();
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
boolean isShowing = false;
Component[] comps = mapPanel.getComponents();
for(Component comp: comps) {
if(comp.equals(refreshPanel)) {
isShowing = true;
break;
}
}
if (show && !isShowing) {
mapPanel.add(refreshPanel, BorderLayout.NORTH);
mapPanel.revalidate();
} else if(!show && isShowing){
mapPanel.remove(refreshPanel);
mapPanel.revalidate();
}
}
});
}
/**
@ -242,7 +261,7 @@ public final class GeolocationTopComponent extends TopComponent {
@Messages({
"GeoTopComponent_no_waypoints_returned_mgs=Applied filter failed to find waypoints that matched criteria.\nRevise filter options and try again.",
"GeoTopComponent_no_waypoints_returned_Title=No Waypoints Found",
"GeoTopComponent_filter_exception_msg=Exception occured during waypoint filtering.",
"GeoTopComponent_filter_exception_msg=Exception occurred during waypoint filtering.",
"GeoTopComponent_filter_exception_Title=Filter Failure",
"GeoTopComponent_filer_data_invalid_msg=Unable to run waypoint filter.\nPlease select one or more data sources.",
"GeoTopComponent_filer_data_invalid_Title=Filter Failure"
@ -378,7 +397,7 @@ public final class GeolocationTopComponent extends TopComponent {
String reportBaseDir = createReportDirectory();
progressPanel.setLabels(REPORT_KML, reportBaseDir);
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
@Override
protected Void doInBackground() throws Exception {
@ -406,7 +425,7 @@ public final class GeolocationTopComponent extends TopComponent {
/**
* A runnable class for getting waypoints based on the current filters.
*/
private class WaypointRunner implements Runnable {
private class WaypointRunner implements Runnable, WaypointFilterQueryCallBack {
private final GeoFilter filters;
@ -428,7 +447,7 @@ public final class GeolocationTopComponent extends TopComponent {
filters.showAllWaypoints(),
filters.getMostRecentNumDays(),
filters.showWaypointsWithoutTimeStamp(),
new WaypointCallBack());
this);
} catch (GeoLocationDataException ex) {
logger.log(Level.SEVERE, "Failed to filter waypoints.", ex);
@ -439,30 +458,32 @@ public final class GeolocationTopComponent extends TopComponent {
Bundle.GeoTopComponent_filter_exception_Title(),
Bundle.GeoTopComponent_filter_exception_msg(),
JOptionPane.ERROR_MESSAGE);
setWaypointLoading(false);
}
});
}
}
}
/**
* Callback for getting waypoints.
*/
private class WaypointCallBack implements WaypointFilterQueryCallBack {
@Override
public void process(final List<Waypoint> waypoints) {
// Make sure that the waypoints are added to the map panel in
// the correct thread.
public void process(List<Waypoint> waypoints) {
List<Track> tracks = null;
try {
tracks = Track.getTracks(Case.getCurrentCase().getSleuthkitCase(), filters.getDataSources());
} catch (GeoLocationDataException ex) {
logger.log(Level.WARNING, "Exception thrown while retrieving list of Tracks", ex);
}
List<Waypoint> completeList = createWaypointList(waypoints, tracks);
final Set<MapWaypoint> pointSet = MapWaypoint.getWaypoints(completeList);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
// If the list is empty, tell the user and do not change
// the visible waypoints.
if (waypoints == null || waypoints.isEmpty()) {
if (completeList == null || completeList.isEmpty()) {
mapPanel.clearWaypoints();
JOptionPane.showMessageDialog(GeolocationTopComponent.this,
Bundle.GeoTopComponent_no_waypoints_returned_Title(),
@ -473,11 +494,173 @@ public final class GeolocationTopComponent extends TopComponent {
return;
}
mapPanel.clearWaypoints();
mapPanel.setWaypoints(MapWaypoint.getWaypoints(waypoints));
mapPanel.setWaypoints(pointSet);
setWaypointLoading(false);
geoFilterPanel.setEnabled(true);
}
});
}
/**
* Returns a complete list of waypoints including the tracks. Takes into
* account the current filters and includes waypoints as approprate.
*
* @param waypoints List of waypoints
* @param tracks List of tracks
*
* @return A list of waypoints including the tracks based on the current
* filters.
*/
private List<Waypoint> createWaypointList(List<Waypoint> waypoints, List<Track> tracks) {
final List<Waypoint> completeList = new ArrayList<>();
if (tracks != null) {
Long timeRangeEnd;
Long timeRangeStart;
if (!filters.showAllWaypoints()) {
// Figure out what the most recent time is given the filtered
// waypoints and the tracks.
timeRangeEnd = getMostRecent(waypoints, tracks);
timeRangeStart = timeRangeEnd - (86400 * filters.getMostRecentNumDays());
completeList.addAll(getWaypointsInRange(timeRangeStart, timeRangeEnd, waypoints));
completeList.addAll(getTracksInRange(timeRangeStart, timeRangeEnd, tracks));
} else {
completeList.addAll(waypoints);
for (Track track : tracks) {
completeList.addAll(track.getPath());
}
}
} else {
completeList.addAll(waypoints);
}
return completeList;
}
/**
* Return a list of waypoints that fall into the given time range.
*
* @param timeRangeStart start timestamp of range (seconds from java
* epoch)
* @param timeRangeEnd start timestamp of range (seconds from java
* epoch)
* @param waypoints List of waypoints to filter.
*
* @return A list of waypoints that fall into the time range.
*/
private List<Waypoint> getWaypointsInRange(Long timeRangeStart, Long timeRangeEnd, List<Waypoint> waypoints) {
List<Waypoint> completeList = new ArrayList<>();
// Add all of the waypoints that fix into the time range.
if (waypoints != null) {
for (Waypoint point : waypoints) {
Long time = point.getTimestamp();
if ((time == null && filters.showWaypointsWithoutTimeStamp())
|| (time != null && (time >= timeRangeStart && time <= timeRangeEnd))) {
completeList.add(point);
}
}
}
return completeList;
}
/**
* Return a list of waypoints from the given tracks that fall into for
* tracks that fall into the given time range. The track start time will
* used for determining if the whole track falls into the range.
*
* @param timeRangeStart start timestamp of range (seconds from java
* epoch)
* @param timeRangeEnd start timestamp of range (seconds from java
* epoch)
* @param tracks Track list.
*
* @return A list of waypoints that that belong to tracks that fall into
* the time range.
*/
private List<Waypoint> getTracksInRange(Long timeRangeStart, Long timeRangeEnd, List<Track> tracks) {
List<Waypoint> completeList = new ArrayList<>();
if (tracks != null) {
for (Track track : tracks) {
Long trackTime = track.getStartTime();
if ((trackTime == null && filters.showWaypointsWithoutTimeStamp())
|| (trackTime != null && (trackTime >= timeRangeStart && trackTime <= timeRangeEnd))) {
completeList.addAll(track.getPath());
}
}
}
return completeList;
}
/**
* Find the latest time stamp in the given list of waypoints.
*
* @param points List of Waypoints, required.
*
* @return The latest time stamp (seconds from java epoch)
*/
private Long findMostRecentTimestamp(List<Waypoint> points) {
Long mostRecent = null;
for (Waypoint point : points) {
if (mostRecent == null) {
mostRecent = point.getTimestamp();
} else {
mostRecent = Math.max(mostRecent, point.getTimestamp());
}
}
return mostRecent;
}
/**
* Find the latest time stamp in the given list of tracks.
*
* @param tracks List of Waypoints, required.
*
* @return The latest time stamp (seconds from java epoch)
*/
private Long findMostRecentTracks(List<Track> tracks) {
Long mostRecent = null;
for (Track track : tracks) {
if (mostRecent == null) {
mostRecent = track.getStartTime();
} else {
mostRecent = Math.max(mostRecent, track.getStartTime());
}
}
return mostRecent;
}
/**
* Returns the "most recent" timestamp amount the list of waypoints and
* track points.
*
* @param points List of Waypoints
* @param tracks List of Tracks
*
* @return Latest time stamp (seconds from java epoch)
*/
private Long getMostRecent(List<Waypoint> points, List<Track> tracks) {
Long waypointMostRecent = findMostRecentTimestamp(points);
Long trackMostRecent = findMostRecentTracks(tracks);
if (waypointMostRecent != null && trackMostRecent != null) {
return Math.max(waypointMostRecent, trackMostRecent);
} else if (waypointMostRecent == null && trackMostRecent != null) {
return trackMostRecent;
} else if (waypointMostRecent != null && trackMostRecent == null) {
return waypointMostRecent;
}
return null;
}
}
}

View File

@ -14,7 +14,7 @@
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,-84,0,0,0,58"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,-84,0,0,0,-47"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
@ -46,26 +46,7 @@
</Constraint>
</Constraints>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
<Component id="zoomSlider" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<EmptySpace max="32767" attributes="0"/>
<Component id="zoomSlider" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<SubComponents>
<Component class="javax.swing.JSlider" name="zoomSlider">
<Properties>
@ -86,6 +67,69 @@
<Events>
<EventHandler event="stateChanged" listener="javax.swing.event.ChangeListener" parameters="javax.swing.event.ChangeEvent" handler="zoomSliderStateChanged"/>
</Events>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="1" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JButton" name="zoomInBtn">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/images/plus-grey.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/geolocation/Bundle.properties" key="MapPanel.zoomInBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="null"/>
</Property>
<Property name="borderPainted" type="boolean" value="false"/>
<Property name="focusPainted" type="boolean" value="false"/>
<Property name="requestFocusEnabled" type="boolean" value="false"/>
<Property name="rolloverEnabled" type="boolean" value="false"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="zoomInBtnActionPerformed"/>
</Events>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JButton" name="zoomOutBtn">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/images/minus-grey.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/geolocation/Bundle.properties" key="MapPanel.zoomOutBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="null"/>
</Property>
<Property name="borderPainted" type="boolean" value="false"/>
<Property name="focusPainted" type="boolean" value="false"/>
<Property name="requestFocusEnabled" type="boolean" value="false"/>
<Property name="rolloverEnabled" type="boolean" value="false"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="zoomOutBtnActionPerformed"/>
</Events>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="2" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
</SubComponents>
</Container>

View File

@ -82,6 +82,7 @@ final public class MapPanel extends javax.swing.JPanel {
private static final long serialVersionUID = 1L;
private boolean zoomChanging;
private KdTree<MapWaypoint> waypointTree;
private Set<MapWaypoint> waypointSet;
private Popup currentPopup;
private final PopupFactory popupFactory;
@ -195,26 +196,14 @@ final public class MapPanel extends javax.swing.JPanel {
mapViewer.setCenterPosition(new GeoPosition(0, 0));
// Basic painters for the way points.
WaypointPainter<Waypoint> waypointPainter = new WaypointPainter<Waypoint>() {
WaypointPainter<MapWaypoint> waypointPainter = new WaypointPainter<MapWaypoint>() {
@Override
public Set<Waypoint> getWaypoints() {
//To assure that the currentlySelectedWaypoint is visible it needs
// to be painted last. LinkedHashSet has a predicable ordering.
Set<Waypoint> set = new LinkedHashSet<>();
if (waypointTree != null) {
Iterator<MapWaypoint> iterator = waypointTree.iterator();
while (iterator.hasNext()) {
MapWaypoint point = iterator.next();
set.add(point);
}
// Add the currentlySelectedWaypoint to the end so that
// it will be painted last.
if (currentlySelectedWaypoint != null) {
set.remove(currentlySelectedWaypoint);
set.add(currentlySelectedWaypoint);
}
public Set<MapWaypoint> getWaypoints() {
if (currentlySelectedWaypoint != null) {
waypointSet.remove(currentlySelectedWaypoint);
waypointSet.add(currentlySelectedWaypoint);
}
return set;
return waypointSet;
}
};
@ -222,7 +211,6 @@ final public class MapPanel extends javax.swing.JPanel {
waypointPainter.setRenderer(new MapWaypointRenderer());
} catch (IOException ex) {
logger.log(Level.WARNING, "Failed to load waypoint image resource, using DefaultWaypointRenderer", ex);
waypointPainter.setRenderer(new DefaultWaypointRenderer());
}
mapViewer.setOverlayPainter(waypointPainter);
@ -305,13 +293,12 @@ final public class MapPanel extends javax.swing.JPanel {
*
* @param waypoints List of waypoints
*/
void setWaypoints(List<MapWaypoint> waypoints) {
void setWaypoints(Set<MapWaypoint> waypoints) {
waypointTree = new KdTree<>();
this.waypointSet = waypoints;
for (MapWaypoint waypoint : waypoints) {
waypointTree.add(waypoint);
}
mapViewer.repaint();
}
@ -548,6 +535,8 @@ final public class MapPanel extends javax.swing.JPanel {
mapViewer = new org.jxmapviewer.JXMapViewer();
zoomPanel = new javax.swing.JPanel();
zoomSlider = new javax.swing.JSlider();
javax.swing.JButton zoomInBtn = new javax.swing.JButton();
javax.swing.JButton zoomOutBtn = new javax.swing.JButton();
setFocusable(false);
setLayout(new java.awt.BorderLayout());
@ -573,6 +562,7 @@ final public class MapPanel extends javax.swing.JPanel {
zoomPanel.setFocusable(false);
zoomPanel.setOpaque(false);
zoomPanel.setRequestFocusEnabled(false);
zoomPanel.setLayout(new java.awt.GridBagLayout());
zoomSlider.setMaximum(15);
zoomSlider.setMinimum(10);
@ -588,23 +578,44 @@ final public class MapPanel extends javax.swing.JPanel {
zoomSliderStateChanged(evt);
}
});
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 1;
zoomPanel.add(zoomSlider, gridBagConstraints);
javax.swing.GroupLayout zoomPanelLayout = new javax.swing.GroupLayout(zoomPanel);
zoomPanel.setLayout(zoomPanelLayout);
zoomPanelLayout.setHorizontalGroup(
zoomPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(zoomPanelLayout.createSequentialGroup()
.addGap(0, 0, 0)
.addComponent(zoomSlider, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
zoomPanelLayout.setVerticalGroup(
zoomPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, zoomPanelLayout.createSequentialGroup()
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(zoomSlider, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 0, 0))
);
zoomInBtn.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/plus-grey.png"))); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(zoomInBtn, org.openide.util.NbBundle.getMessage(MapPanel.class, "MapPanel.zoomInBtn.text")); // NOI18N
zoomInBtn.setBorder(null);
zoomInBtn.setBorderPainted(false);
zoomInBtn.setFocusPainted(false);
zoomInBtn.setRequestFocusEnabled(false);
zoomInBtn.setRolloverEnabled(false);
zoomInBtn.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
zoomInBtnActionPerformed(evt);
}
});
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
zoomPanel.add(zoomInBtn, gridBagConstraints);
zoomOutBtn.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/minus-grey.png"))); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(zoomOutBtn, org.openide.util.NbBundle.getMessage(MapPanel.class, "MapPanel.zoomOutBtn.text")); // NOI18N
zoomOutBtn.setBorder(null);
zoomOutBtn.setBorderPainted(false);
zoomOutBtn.setFocusPainted(false);
zoomOutBtn.setRequestFocusEnabled(false);
zoomOutBtn.setRolloverEnabled(false);
zoomOutBtn.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
zoomOutBtnActionPerformed(evt);
}
});
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 2;
zoomPanel.add(zoomOutBtn, gridBagConstraints);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.anchor = java.awt.GridBagConstraints.SOUTHWEST;
@ -652,6 +663,16 @@ final public class MapPanel extends javax.swing.JPanel {
}
}//GEN-LAST:event_mapViewerMouseClicked
private void zoomInBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_zoomInBtnActionPerformed
int currentValue = mapViewer.getZoom();
setZoom(currentValue-1);
}//GEN-LAST:event_zoomInBtnActionPerformed
private void zoomOutBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_zoomOutBtnActionPerformed
int currentValue = mapViewer.getZoom();
setZoom(currentValue+1);
}//GEN-LAST:event_zoomOutBtnActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private org.jxmapviewer.JXMapViewer mapViewer;
@ -662,7 +683,7 @@ final public class MapPanel extends javax.swing.JPanel {
/**
* Renderer for the map waypoints.
*/
private class MapWaypointRenderer implements WaypointRenderer<Waypoint> {
private class MapWaypointRenderer implements WaypointRenderer<MapWaypoint> {
private final BufferedImage defaultWaypointImage;
private final BufferedImage selectedWaypointImage;
@ -677,7 +698,7 @@ final public class MapPanel extends javax.swing.JPanel {
}
@Override
public void paintWaypoint(Graphics2D gd, JXMapViewer jxmv, Waypoint waypoint) {
public void paintWaypoint(Graphics2D gd, JXMapViewer jxmv, MapWaypoint waypoint) {
Point2D point = jxmv.getTileFactory().geoToPixel(waypoint.getPosition(), jxmv.getZoom());
int x = (int)point.getX();

View File

@ -23,8 +23,10 @@ import java.awt.image.BufferedImage;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.logging.Level;
import javax.swing.AbstractAction;
import javax.swing.Action;
@ -45,11 +47,7 @@ import org.sleuthkit.autopsy.directorytree.ExternalViewerAction;
import org.sleuthkit.autopsy.directorytree.ExternalViewerShortcutAction;
import org.sleuthkit.autopsy.directorytree.ExtractAction;
import org.sleuthkit.autopsy.directorytree.actionhelpers.ExtractActionHelper;
import org.sleuthkit.autopsy.geolocation.datamodel.GeoLocationDataException;
import org.sleuthkit.autopsy.geolocation.datamodel.Route;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.autopsy.geolocation.datamodel.Waypoint;
import org.sleuthkit.autopsy.geolocation.datamodel.WaypointBuilder;
import org.sleuthkit.autopsy.timeline.actions.ViewArtifactInTimelineAction;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
@ -70,32 +68,6 @@ final class MapWaypoint extends KdTree.XYZPoint implements org.jxmapviewer.viewe
private final Waypoint dataModelWaypoint;
private final GeoPosition position;
/**
* Returns a list of waypoints for the currently open case.
*
* @param skCase Current case
*
* @return list of waypoints, list will be empty if no waypoints were found
*
* @throws GeoLocationDataException
*/
static List<MapWaypoint> getWaypoints(SleuthkitCase skCase) throws GeoLocationDataException {
List<Waypoint> points = WaypointBuilder.getAllWaypoints(skCase);
List<Route> routes = Route.getRoutes(skCase);
for (Route route : routes) {
points.addAll(route.getRoute());
}
List<MapWaypoint> mapPoints = new ArrayList<>();
for (Waypoint point : points) {
mapPoints.add(new MapWaypoint(point));
}
return mapPoints;
}
/**
* Returns a list of of MapWaypoint objects for the given list of
* datamodel.Waypoint objects.
@ -105,8 +77,8 @@ final class MapWaypoint extends KdTree.XYZPoint implements org.jxmapviewer.viewe
* @return List of MapWaypoint objects. List will be empty if dmWaypoints
* was empty or null.
*/
static List<MapWaypoint> getWaypoints(List<Waypoint> dmWaypoints) {
List<MapWaypoint> mapPoints = new ArrayList<>();
static Set<MapWaypoint> getWaypoints(List<Waypoint> dmWaypoints) {
Set<MapWaypoint> mapPoints = new LinkedHashSet<>();
if (dmWaypoints != null) {

View File

@ -0,0 +1,141 @@
/*
*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.geolocation.datamodel;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Class representing a series of waypoints that form a path.
*/
public class GeoPath {
private final List<Waypoint> waypointList;
private final String pathName;
private final BlackboardArtifact artifact;
/**
* Gets the list of Routes from the TSK_GPS_ROUTE artifacts.
*
* @param skCase Currently open SleuthkitCase
*
* @return List of Route objects, empty list will be returned if no Routes
* were found
*
* @throws GeoLocationDataException
*/
static public List<Route> getRoutes(SleuthkitCase skCase) throws GeoLocationDataException {
List<BlackboardArtifact> artifacts = null;
try {
artifacts = skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_ROUTE);
} catch (TskCoreException ex) {
throw new GeoLocationDataException("Unable to get artifacts for type: TSK_GPS_BOOKMARK", ex);
}
List<Route> routes = new ArrayList<>();
for (BlackboardArtifact artifact : artifacts) {
Route route = new Route(artifact);
routes.add(route);
}
return routes;
}
/**
* Gets the list of Routes from the TSK_GPS_TRACK artifacts.
*
* @param skCase Currently open SleuthkitCase
* @param sourceList List of source to return tracks from, maybe null to
* return tracks from all sources
*
* @return List of Route objects, empty list will be returned if no Routes
* were found
*
* @throws GeoLocationDataException
*/
static public List<Track> getTracks(SleuthkitCase skCase, List<? extends Content> sourceList) throws GeoLocationDataException {
List<BlackboardArtifact> artifacts = null;
List<Track> tracks = new ArrayList<>();
try {
artifacts = skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACK);
for (BlackboardArtifact artifact : artifacts) {
if (sourceList == null || sourceList.contains(artifact.getDataSource())) {
Track route = new Track(artifact);
tracks.add(route);
}
}
} catch (TskCoreException ex) {
throw new GeoLocationDataException("Unable to get artifacts for type: TSK_GPS_BOOKMARK", ex);
}
return tracks;
}
/**
* Path constructor.
*
* @param artifact BlackboardARtifact that this path represents, required
* @param pathName Name for this path, maybe null or empty string.
*/
GeoPath(BlackboardArtifact artifact, String pathName) {
this.waypointList = new ArrayList<>();
this.pathName = pathName;
this.artifact = artifact;
}
/**
* Adds a Waypoint to the path.
*
* @param point
*/
final void addToPath(Waypoint point) {
waypointList.add(point);
}
/**
* Get the list of way points for this route;
*
* @return List an unmodifiableList of ArtifactWaypoints for this route
*/
final public List<Waypoint> getPath() {
return Collections.unmodifiableList(waypointList);
}
/**
* Returns the BlackboardARtifact that this path represents.
*
* @return
*/
final BlackboardArtifact getArtifact() {
return artifact;
}
/**
* Returns the label\display name for this path.
*
* @return GeoPath label, empty string
*/
public String getLabel() {
return pathName != null ? pathName : "";
}
}

View File

@ -19,15 +19,12 @@
*/
package org.sleuthkit.autopsy.geolocation.datamodel;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
/**
* A Route represents a TSK_GPS_ROUTE artifact which has a start and end point
@ -35,56 +32,35 @@ import org.sleuthkit.datamodel.TskCoreException;
* more that two points.
*
*/
public final class Route {
private final List<Waypoint> points;
public class Route extends GeoPath{
private final Long timestamp;
// This list is not expected to change after construction so the
// constructor will take care of creating an unmodifiable List
private final List<Waypoint.Property> immutablePropertiesList;
/**
* Gets the list of Routes from the TSK_GPS_ROUTE artifacts.
*
* @param skCase Currently open SleuthkitCase
*
* @return List of Route objects, empty list will be returned if no Routes
* were found
*
* @throws GeoLocationDataException
*/
static public List<Route> getRoutes(SleuthkitCase skCase) throws GeoLocationDataException {
List<BlackboardArtifact> artifacts = null;
try {
artifacts = skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_ROUTE);
} catch (TskCoreException ex) {
throw new GeoLocationDataException("Unable to get artifacts for type: TSK_GPS_BOOKMARK", ex);
}
List<Route> routes = new ArrayList<>();
for (BlackboardArtifact artifact : artifacts) {
Route route = new Route(artifact);
routes.add(route);
}
return routes;
}
private final List<Waypoint.Property> propertiesList;
/**
* Construct a route for the given artifact.
*
* @param artifact TSK_GPS_ROUTE artifact object
*/
@Messages({
// This is the original static hardcoded label from the
// original kml-report code
"Route_Label=As-the-crow-flies Route"
})
Route(BlackboardArtifact artifact) throws GeoLocationDataException {
points = new ArrayList<>();
Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> attributeMap = Waypoint.getAttributesFromArtifactAsMap(artifact);
points.add(getRouteStartPoint(artifact, attributeMap));
points.add(getRouteEndPoint(artifact, attributeMap));
super(artifact, Bundle.Route_Label());
Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> attributeMap = Waypoint.getAttributesFromArtifactAsMap(artifact);
addToPath(getRouteStartPoint(artifact, attributeMap));
addToPath(getRouteEndPoint(artifact, attributeMap));
BlackboardAttribute attribute = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME);
timestamp = attribute != null ? attribute.getValueLong() : null;
immutablePropertiesList = Collections.unmodifiableList(Waypoint.createGeolocationProperties(attributeMap));
propertiesList = Waypoint.createGeolocationProperties(attributeMap);
}
/**
@ -93,7 +69,7 @@ public final class Route {
* @return List an unmodifiableList of ArtifactWaypoints for this route
*/
public List<Waypoint> getRoute() {
return Collections.unmodifiableList(points);
return getPath();
}
/**
@ -103,21 +79,9 @@ public final class Route {
* @return Map of key, value pairs.
*/
public List<Waypoint.Property> getOtherProperties() {
return immutablePropertiesList;
return Collections.unmodifiableList(propertiesList);
}
/**
* Get the route label.
*/
@Messages({
// This is the original static hardcoded label from the
// original kml-report code
"Route_Label=As-the-crow-flies Route"
})
public String getLabel() {
return Bundle.Route_Label();
}
public Long getTimestamp() {
return timestamp;
}
@ -171,7 +135,7 @@ public final class Route {
@Messages({
"Route_End_Label=End"
})
Waypoint getRouteEndPoint(BlackboardArtifact artifact, Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> attributeMap) throws GeoLocationDataException {
private Waypoint getRouteEndPoint(BlackboardArtifact artifact, Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> attributeMap) throws GeoLocationDataException {
BlackboardAttribute latitude = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_END);
BlackboardAttribute longitude = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_END);
BlackboardAttribute altitude = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE);

View File

@ -0,0 +1,243 @@
/*
*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.geolocation.datamodel;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.blackboardutils.attributes.GeoTrackPoints;
import org.sleuthkit.datamodel.blackboardutils.attributes.GeoWaypoint.GeoTrackPoint;
/**
* A GPS track with which wraps the TSK_GPS_TRACK artifact.
*/
public final class Track extends GeoPath{
private final Long startTimestamp;
private final Long endTimeStamp;
/**
* Construct a new Track for the given artifact.
*
* @param artifact
*
* @throws GeoLocationDataException
*/
public Track(BlackboardArtifact artifact) throws GeoLocationDataException {
this(artifact, Waypoint.getAttributesFromArtifactAsMap(artifact));
}
/**
* Construct a Track for the given artifact and attributeMap.
*
* @param artifact TSK_GPD_TRACK artifact
* @param attributeMap Map of the artifact attributes
*
* @throws GeoLocationDataException
*/
private Track(BlackboardArtifact artifact, Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> attributeMap) throws GeoLocationDataException {
super(artifact, getTrackName(attributeMap));
List<GeoTrackPoint> points = getPointsList(attributeMap);
buildPath(points);
startTimestamp = findStartTime(points);
endTimeStamp = findEndTime(points);
}
/**
* Returns the start time of this track.
*
* @return Earliest time, or null if none was available.
* (seconds from java epoch)
*/
public Long getStartTime() {
return startTimestamp;
}
/**
* Returns the end time of this track.
*
* @return Earliest timestamp, or null if none was available.
* (seconds from java epoch)
*/
public Long getEndTime() {
return endTimeStamp;
}
/**
* Return the name of the track from the attributeMap.
* Track name is stored in the attribute TSK_NAME
*
* @param attributeMap
* @return Track name or empty string if none was available.
*/
private static String getTrackName(Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> attributeMap) {
BlackboardAttribute attribute = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME);
return attribute != null ? attribute.getValueString() : "";
}
/**
* Create the list of TrackWaypoints from the GeoTrackPoint list.
*
* @param points List of GeoTrackPoints
*
* @throws GeoLocationDataException
*/
private void buildPath(List<GeoTrackPoint> points) throws GeoLocationDataException {
for (GeoTrackPoint point : points) {
addToPath(new TrackWaypoint(point));
}
}
/**
* Returns the list of GeoTrackPoints from the attributeMap. Creates the
* GeoTrackPoint list from the TSK_GEO_TRACKPOINTS attribute.
*
* @param attributeMap Map of artifact attributes.
*
* @return GeoTrackPoint list empty list if the attribute was not found.
*/
private List<GeoTrackPoint> getPointsList(Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> attributeMap) {
BlackboardAttribute attribute = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_TRACKPOINTS);
if (attribute != null) {
String value = attribute.getValueString();
return GeoTrackPoints.deserializePoints(value);
}
return new ArrayList<>();
}
/**
* Return the start time for the track. Assumes the points are in time
* order.
*
* @param points List of GeoTrackPoints.
*
* @return First non-null time stamp or null, if one was not found.
*/
private Long findStartTime(List<GeoTrackPoint> points) {
if (points != null) {
for (GeoTrackPoint point : points) {
if (point.getTimeStamp() != null) {
return point.getTimeStamp();
}
}
}
return null;
}
/**
* Return the ends time for the track. Assumes the points are in time
* order.
*
* @param points List of GeoTrackPoints.
*
* @return First non-null time stamp or null, if one was not found.
*/
private Long findEndTime(List<GeoTrackPoint> points) {
if (points != null) {
for (int index = points.size() - 1; index >= 0; index--) {
GeoTrackPoint point = points.get(index);
if (point.getTimeStamp() != null) {
return point.getTimeStamp();
}
}
}
return null;
}
/**
* A Waypoint subclass for the points of a track.
*/
final class TrackWaypoint extends Waypoint {
private final List<Waypoint.Property> propertyList;
/**
* Construct a TrackWaypoint.
*
* @param point GeoTrackPoint
*
* @throws GeoLocationDataException
*/
TrackWaypoint(GeoTrackPoint point) throws GeoLocationDataException {
super(null, "",
point.getTimeStamp(),
point.getLatitude(),
point.getLongitude(),
point.getAltitude(),
null,
null,
Track.this);
propertyList = createPropertyList(point);
}
/**
* Overloaded to return a property list that is generated from
* the GeoTrackPoint instead of an artifact.
*
* @return unmodifiable list of Waypoint.Property
*/
@Override
public List<Waypoint.Property> getOtherProperties() {
return Collections.unmodifiableList(propertyList);
}
/**
* Create a propertyList specific to GeoTrackPoints.
*
* @param point GeoTrackPoint to get values from.
*
* @return A list of Waypoint.properies.
*/
private List<Waypoint.Property> createPropertyList(GeoTrackPoint point) {
List<Waypoint.Property> list = new ArrayList<>();
Long timestamp = point.getTimeStamp();
if (timestamp != null) {
list.add(new Property(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME.getDisplayName(), timestamp.toString()));
}
Double value = point.getVelocity();
if (value != null) {
list.add(new Property(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_VELOCITY.getDisplayName(), value.toString()));
}
value = point.getDistanceTraveled();
if (value != null) {
list.add(new Property(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_DISTANCE_TRAVELED.getDisplayName(), value.toString()));
}
value = point.getDistanceFromHP();
if (value != null) {
list.add(new Property(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_DISTANCE_FROM_HOME_POINT.getDisplayName(), value.toString()));
}
return list;
}
}
}

View File

@ -35,7 +35,7 @@ final class TrackpointWaypoint extends Waypoint {
TrackpointWaypoint(BlackboardArtifact artifact) throws GeoLocationDataException {
this(artifact, getAttributesFromArtifactAsMap(artifact));
}
private TrackpointWaypoint(BlackboardArtifact artifact, Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> attributeMap) throws GeoLocationDataException {
super(artifact,
getLabelFromArtifact(attributeMap),

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 Basis Technology Corp.
* Copyright 2019-2020 Basis Technology Corp.
* contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -43,17 +43,15 @@ public class Waypoint {
final private String label;
final private AbstractFile image;
final private BlackboardArtifact artifact;
final private Route route;
final private GeoPath path;
// This list is not expected to change after construction. The
// constructor will take care of making an unmodifiable List
final private List<Waypoint.Property> immutablePropertiesList;
final private List<Waypoint.Property> propertiesList;
/**
* This is a list of attributes that are already being handled by the by
* getter functions.
*/
static final private BlackboardAttribute.ATTRIBUTE_TYPE[] ALREADY_HANDLED_ATTRIBUTES = {
static final BlackboardAttribute.ATTRIBUTE_TYPE[] ALREADY_HANDLED_ATTRIBUTES = {
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME,
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE,
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE,
@ -80,7 +78,7 @@ public class Waypoint {
* @throws GeoLocationDataException Exception will be thrown if artifact did
* not have a valid longitude and latitude.
*/
Waypoint(BlackboardArtifact artifact, String label, Long timestamp, Double latitude, Double longitude, Double altitude, AbstractFile image, Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> attributeMap, Route route) throws GeoLocationDataException {
Waypoint(BlackboardArtifact artifact, String label, Long timestamp, Double latitude, Double longitude, Double altitude, AbstractFile image, Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> attributeMap, GeoPath path) throws GeoLocationDataException {
if (longitude == null || latitude == null) {
throw new GeoLocationDataException("Invalid waypoint, null value passed for longitude or latitude");
}
@ -92,9 +90,9 @@ public class Waypoint {
this.longitude = longitude;
this.latitude = latitude;
this.altitude = altitude;
this.route = null;
this.path = path;
immutablePropertiesList = Collections.unmodifiableList(createGeolocationProperties(attributeMap));
propertiesList = createGeolocationProperties(attributeMap);
}
/**
@ -171,7 +169,7 @@ public class Waypoint {
* @return A List of waypoint properties
*/
public List<Waypoint.Property> getOtherProperties() {
return immutablePropertiesList;
return Collections.unmodifiableList(propertiesList);
}
/**
@ -180,25 +178,8 @@ public class Waypoint {
* @return The waypoint route or null if the waypoint is not apart of a
* route.
*/
public Route getRoute() {
return route;
}
/**
* Gets the label for this waypoint.
*
* @param attributeMap Attributes for waypoint
*
* @return Returns a label for the waypoint, or empty string if no label was
* found.
*/
private static String getLabelFromArtifact(Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> attributeMap) {
BlackboardAttribute attribute = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME);
if (attribute != null) {
return attribute.getDisplayString();
}
return "";
public GeoPath getPath() {
return path;
}
/**
@ -238,29 +219,32 @@ public class Waypoint {
*
* @throws GeoLocationDataException
*/
static public List<Waypoint.Property> createGeolocationProperties(Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> attributeMap) throws GeoLocationDataException {
static List<Waypoint.Property> createGeolocationProperties(Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> attributeMap) throws GeoLocationDataException {
List<Waypoint.Property> list = new ArrayList<>();
if(attributeMap != null) {
Set<BlackboardAttribute.ATTRIBUTE_TYPE> keys = new HashSet<>(attributeMap.keySet());
Set<BlackboardAttribute.ATTRIBUTE_TYPE> keys = new HashSet<>(attributeMap.keySet());
for (BlackboardAttribute.ATTRIBUTE_TYPE type : ALREADY_HANDLED_ATTRIBUTES) {
keys.remove(type);
}
for (BlackboardAttribute.ATTRIBUTE_TYPE type : ALREADY_HANDLED_ATTRIBUTES) {
keys.remove(type);
}
for (BlackboardAttribute.ATTRIBUTE_TYPE type : keys) {
String key = type.getDisplayName();
String value = attributeMap.get(type).getDisplayString();
for (BlackboardAttribute.ATTRIBUTE_TYPE type : keys) {
String key = type.getDisplayName();
String value = attributeMap.get(type).getDisplayString();
list.add(new Waypoint.Property(key, value));
list.add(new Waypoint.Property(key, value));
}
}
return list;
}
/**
* Simple property class for waypoint properties that a purely
* informational.
*/
public static final class Property {
public final static class Property {
private final String displayName;
private final String value;
@ -272,7 +256,7 @@ public class Waypoint {
* or empty string.
* @param value String value for property. Can be null.
*/
private Property(String displayName, String value) {
Property(String displayName, String value) {
this.displayName = displayName;
this.value = value;
}

View File

@ -132,15 +132,40 @@ public final class WaypointBuilder {
public static List<Route> getRoutes(List<Waypoint> waypoints) {
List<Route> routeList = new ArrayList<>();
for (Waypoint point : waypoints) {
Route route = point.getRoute();
if (route != null && !routeList.contains(route)) {
routeList.add(route);
GeoPath path = point.getPath();
if (path instanceof Route) {
Route route = (Route) path;
if (!routeList.contains(route)) {
routeList.add(route);
}
}
}
return routeList;
}
/**
* Returns a list of tracks from the given list of waypoints.
*
* @param waypoints A list of waypoints
*
* @return A list of track or an empty list if none were found.
*/
public static List<Track> getTracks(List<Waypoint> waypoints) {
List<Track> trackList = new ArrayList<>();
for (Waypoint point : waypoints) {
GeoPath path = point.getPath();
if (path instanceof Track) {
Track route = (Track) path;
if (!trackList.contains(route)) {
trackList.add(route);
}
}
}
return trackList;
}
/**
* Gets a list of Waypoints for TSK_GPS_TRACKPOINT artifacts.
*
@ -470,18 +495,16 @@ public final class WaypointBuilder {
* @return SQL SELECT statement
*/
static private String buildQueryForWaypointsWOTimeStamps(List<DataSource> dataSources) {
// SELECT_WO_TIMESTAMP
// SELECT DISTINCT artifact_id, artifact_type_id
// FROM blackboard_attributes
// WHERE artifact_id NOT IN (%s)
// AND artifact_id IN (%s)
// GEO_ARTIFACT_QUERY_ID_ONLY
// SELECT artifact_id
// FROM blackboard_attributes
// WHERE attribute_type_id IN (%d, %d)
return String.format(SELECT_WO_TIMESTAMP,
String.format(GEO_ARTIFACT_QUERY_ID_ONLY,
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID(),
@ -523,12 +546,12 @@ public final class WaypointBuilder {
// IN ( %s )
//
mostRecentQuery = String.format("AND value_int64 > (%s)", //NON-NLS
String.format(MOST_RECENT_TIME,
cntDaysFromRecent,
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID(),
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED.getTypeID(),
getWaypointListQuery(dataSources)
));
String.format(MOST_RECENT_TIME,
cntDaysFromRecent,
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID(),
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED.getTypeID(),
getWaypointListQuery(dataSources)
));
}
// GEO_ARTIFACT_QUERY
@ -622,8 +645,12 @@ public final class WaypointBuilder {
case TSK_GPS_LAST_KNOWN_LOCATION:
waypoints.add(new LastKnownWaypoint(artifact));
break;
case TSK_GPS_TRACK:
Track track = new Track(artifact);
waypoints.addAll(track.getPath());
break;
default:
waypoints.add(new CustomArtifactWaypoint(artifact));
waypoints.add(new CustomArtifactWaypoint(artifact));
}
return waypoints;

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 B

View File

@ -0,0 +1,6 @@
DATExtractor_process_message=Processing DJI DAT file: %s
DATFileExtractor_Extractor_Name=DAT File Extractor
DroneIngestModule_Description=Description
DroneIngestModule_Name=Drone
# {0} - AbstractFileName
DroneIngestModule_process_start=Started {0}

View File

@ -0,0 +1,149 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.modules.drones;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import src.Files.ConvertDat;
import src.Files.CsvWriter;
import src.Files.DatFile;
import src.Files.DJIAssistantFile;
import src.Files.Exception.FileEnd;
import src.Files.Exception.NotDatFile;
/**
* Dump DJI DAT files to csv file using the DatCon.jar.
*
*/
final class DATDumper {
/**
* Construct a DATDumper.
*/
DATDumper() {
// This constructor is intentionally empty. Nothing special is needed here.
}
/**
* Dump the DJI DAT file to a csv file.
*
* @param datFilePath Path to input DAT file
* @param outputFilePath Output file path
* @param overWriteExisting True to overwrite an existing csv file with
* outputFilePath
*
* @throws DroneIngestException
*/
void dumpDATFile(String datFilePath, String outputFilePath, boolean overWriteExisting) throws DroneIngestException {
// Validate the input and output file paths.
validateOutputFile(outputFilePath, overWriteExisting);
if (!isDATFile(datFilePath)) {
throw new DroneIngestException(String.format("Not a DAT file! DAT = %s", datFilePath)); //NON-NLS
}
DatFile datFile = null;
try (CsvWriter writer = new CsvWriter(outputFilePath)) {
// Creates a version specific DatFile object
datFile = DatFile.createDatFile(datFilePath);
datFile.reset();
// preAnalyze does an inital pass of the DAT file to gather some
// information about the file.
datFile.preAnalyze();
// Creates a version specific ConvertDat object
ConvertDat convertDat = datFile.createConVertDat();
// The lower the sample rate the smaller the output csv file will be
// however the date will be less precise. For our purposes we are going
// a sample rate of 1.
convertDat.sampleRate = 1;
// Setting the tickRangeLower and upper values reduces some of the
// noise invalid data in the output file.
if (datFile.gpsLockTick != -1) {
convertDat.tickRangeLower = datFile.gpsLockTick;
}
if (datFile.lastMotorStopTick != -1) {
convertDat.tickRangeUpper = datFile.lastMotorStopTick;
}
convertDat.setCsvWriter(writer);
convertDat.createRecordParsers();
datFile.reset();
// Analyze does the work of parsing the data, everything prior was
// setup
convertDat.analyze(true);
} catch (IOException | NotDatFile | FileEnd ex) {
throw new DroneIngestException(String.format("Failed to dump DAT file to csv. DAT = %s, CSV = %s", datFilePath, outputFilePath), ex); //NON-NLS
} finally {
if (datFile != null) {
datFile.close();
}
}
}
/**
* Validate that if the given csv file exists that the overWriteExsiting
* param is true. Throws an exception if the file exists and
* overWriteExisting is false.
*
* @param outputFileName Absolute path for the output csv file
* @param overWriteExisting True to over write an existing file.
*
* @throws DroneIngestException Throws exception if overWriteExisting is
* true and outputFileName exists
*/
private void validateOutputFile(String outputFileName, boolean overWriteExisting) throws DroneIngestException {
File csvFile = new File(outputFileName);
if (csvFile.exists()) {
if (overWriteExisting) {
csvFile.delete();
} else {
throw new DroneIngestException(String.format("Unable to dump DAT file. overWriteExsiting is false and DAT output csv file exists: %s", outputFileName)); //NON-NLS
}
}
}
/**
* Validate that the DAT file exists and it is in a known format that can be
* dumped.
*
* @param datFilePath Absolute path to DAT file
*
* @throws DroneIngestException
*/
public boolean isDATFile(String datFilePath) throws DroneIngestException {
File datFile = new File(datFilePath);
if (!datFile.exists()) {
throw new DroneIngestException(String.format("Unable to dump DAT file DAT file does not exist: %s", datFilePath)); //NON-NLS
}
try {
return DatFile.isDatFile(datFilePath) || DJIAssistantFile.isDJIDat(datFile);
} catch (FileNotFoundException ex) {
throw new DroneIngestException(String.format("Unable to dump DAT file. File not found %s", datFilePath), ex); //NON-NLS
}
}
}

View File

@ -0,0 +1,322 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.modules.drones;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.nio.file.Paths;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.services.FileManager;
import org.sleuthkit.autopsy.coreutils.FileUtil;
import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress;
import org.sleuthkit.autopsy.ingest.IngestJobContext;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.blackboardutils.attributes.GeoWaypoint.GeoTrackPoint;
import org.sleuthkit.datamodel.blackboardutils.GeoArtifactsHelper;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.Blackboard.BlackboardException;
/**
* Extract drone position data from DJI Phantom drones.
*
* Module uses DatCon.jar to dump FLYXXX.DAT file to a CSV file which is stored
* in the case temp directory. Artifacts are created by parsing the csv file.
*
*/
final class DATExtractor extends DroneExtractor {
private static final Logger logger = Logger.getLogger(DATExtractor.class.getName());
private static final String HEADER_LONG = "IMU_ATTI(0):Longitude"; //NON-NLS
private static final String HEADER_LAT = "IMU_ATTI(0):Latitude"; //NON-NLS
private static final String HEADER_VELOCITY = "IMU_ATTI(0):velComposite"; //NON-NLS
private static final String HEADER_DATETILE = "GPS:dateTimeStamp"; //NON-NLS
private static final String HEADER_ALTITUDE = "GPS(0):heightMSL"; //NON-NLS
private static final String HEADER_DISTANCE_FROM_HP = "IMU_ATTI(0):distanceHP"; //NON-NLS
private static final String HEADER_DISTANCE_TRAVELED = "IMU_ATTI(0):distanceTravelled"; //NON-NLS
/**
* Construct a DATExtractor.
*
* @throws DroneIngestException
*/
DATExtractor() throws DroneIngestException {
super();
}
@Messages({
"DATExtractor_process_message=Processing DJI DAT file: %s"
})
@Override
void process(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar) throws DroneIngestException {
List<AbstractFile> datFiles = findDATFiles(dataSource);
DATDumper dumper = new DATDumper();
try {
for (AbstractFile DATFile : datFiles) {
if (context.dataSourceIngestIsCancelled()) {
break;
}
progressBar.progress(String.format(Bundle.DATExtractor_process_message(), DATFile.getName()));
// Copy the DAT file into the case temp folder
File tempDATFile = getTemporaryFile(context, DATFile);
// Create a path for the csv file
String csvFilePath = getCSVPathForDAT(DATFile);
try {
if (!dumper.isDATFile(tempDATFile.getAbsolutePath())) {
logger.log(Level.WARNING, String.format("%s is not a valid DAT file", DATFile.getName())); //NON-NLS
continue;
}
// Dump the DAT file to a csv file
dumper.dumpDATFile(tempDATFile.getAbsolutePath(), csvFilePath, true);
if (context.dataSourceIngestIsCancelled()) {
break;
}
// Process the csv file
List<GeoTrackPoint> trackPoints = processCSVFile(context, DATFile, csvFilePath);
if (trackPoints != null && !trackPoints.isEmpty()) {
(new GeoArtifactsHelper(getSleuthkitCase(), getName(), DATFile)).addTrack(DATFile.getName(), trackPoints);
} else {
logger.log(Level.INFO, String.format("No trackpoints with valid longitude or latitude found in %s", DATFile.getName())); //NON-NLS
}
} catch (TskCoreException | BlackboardException ex) {
logger.log(Level.WARNING, String.format("Exception thrown while processing DAT file %s", DATFile.getName()), ex); //NON-NLS
} finally {
tempDATFile.delete();
(new File(csvFilePath)).delete();
}
}
} finally {
FileUtil.deleteDir(getExtractorTempPath().toFile());
}
}
@NbBundle.Messages({
"DATFileExtractor_Extractor_Name=DAT File Extractor"
})
@Override
String getName() {
return Bundle.DATFileExtractor_Extractor_Name();
}
/**
* Find any files that have the file name FLYXXX.DAT where the X are digit
* characters.
*
* @param dataSource Data source to search
*
* @return List of found files or empty list if none where found
*
* @throws DroneIngestException
*/
private List<AbstractFile> findDATFiles(Content dataSource) throws DroneIngestException {
List<AbstractFile> fileList = new ArrayList<>();
FileManager fileManager = getCurrentCase().getServices().getFileManager();
// findFiles use the SQL wildcard # in the file name
try {
fileList = fileManager.findFiles(dataSource, "FLY___.DAT"); //NON-NLS
} catch (TskCoreException ex) {
throw new DroneIngestException("Unable to find drone DAT files.", ex); //NON-NLS
}
return fileList;
}
/**
* Return an absolute path for the given DAT file.
*
* @param file DAT file
*
* @return Absolute csv file path
*/
private String getCSVPathForDAT(AbstractFile file) {
String tempFileName = file.getName() + file.getId() + ".csv"; //NON-NLS
return Paths.get(getExtractorTempPath().toString(), tempFileName).toString();
}
/**
* Process the csv dump of the drone DAT file.
*
* Create artifacts for all rows that have a valid longitude and latitude.
*
* @param context current case job context
* @param DATFile Original DAT file
* @param csvFilePath Path of csv file to process
*
* @throws DroneIngestException
*/
private List<GeoTrackPoint> processCSVFile(IngestJobContext context, AbstractFile DATFile, String csvFilePath) throws DroneIngestException {
List<GeoTrackPoint> trackPoints = new ArrayList<>();
try (BufferedReader reader = new BufferedReader(new FileReader(new File(csvFilePath)))) {
// First read in the header line and process
String line = reader.readLine();
Map<String, Integer> headerMap = makeHeaderMap(line.split(",")); //NON-NLS
while ((line = reader.readLine()) != null) {
if (context.dataSourceIngestIsCancelled()) {
return null;
}
String[] values = line.split(","); //NON-NLS
GeoTrackPoint point = createTrackPoint(headerMap, values);
if (point != null) {
trackPoints.add(point);
}
}
} catch (IOException ex) {
throw new DroneIngestException(String.format("Failed to read DAT csvFile %s created for AbstractFile: %s", csvFilePath, DATFile.getId()), ex); //NON-NLS
}
return trackPoints;
}
/**
* Create a lookup to quickly match the column header with its array index
*
* @param headers
*
* @return Map of column names with the column index
*/
private Map<String, Integer> makeHeaderMap(String[] headers) {
Map<String, Integer> map = new HashMap<>();
for (int index = 0; index < headers.length; index++) {
map.put(headers[index], index);
}
return map;
}
/**
* Returns a list of BlackboardAttributes generated from the String array.
*
* If longitude and latitude are not valid, assume we the row is not
* interesting and return a null collection.
*
* @param columnLookup column header lookup map
* @param values Row data
*
* @return Collection of BlackboardAttributes for row or null collection if
* longitude or latitude was not valid
*
* @throws DroneIngestException
*/
private GeoTrackPoint createTrackPoint(Map<String, Integer> columnLookup, String[] values) throws DroneIngestException {
Double latitude = getDoubleValue(columnLookup.get(HEADER_LAT), values);
Double longitude = getDoubleValue(columnLookup.get(HEADER_LONG), values);
if (longitude == null || latitude == null) {
// Assume the row is not valid\has junk
return null;
}
return new GeoTrackPoint(latitude,
longitude,
getDoubleValue(columnLookup.get(HEADER_ALTITUDE), values),
getDoubleValue(columnLookup.get(HEADER_VELOCITY), values),
getDoubleValue(columnLookup.get(HEADER_DISTANCE_FROM_HP), values),
getDoubleValue(columnLookup.get(HEADER_DISTANCE_TRAVELED), values),
getDateTimeValue(columnLookup, values));
}
/**
* Returns the waypoint timestamp in java/unix epoch seconds.
*
* The format of the date time string is 2016-09-26T18:26:19Z.
*
* @param headerMap
* @param values
*
* @return Epoch seconds or null if no dateTime value was found
*/
private Long getDateTimeValue(Map<String, Integer> headerMap, String[] values) {
Integer index = headerMap.get(HEADER_DATETILE);
if (index == null || index == -1 || index > values.length) {
return null;
}
String value = values[index];
if (value == null || value.isEmpty()) {
return null;
}
try {
ZonedDateTime zdt = ZonedDateTime.parse(value, DateTimeFormatter.ISO_DATE_TIME);
return zdt.toLocalDateTime().toEpochSecond(ZoneOffset.UTC);
} catch (DateTimeParseException ex) {
return null;
}
}
/**
* Returns the string value at the given index parsed as a double.
*
* @param index Index to string array
* @param values Array of string values
*
* @return Double value or null if the index is out of bounds of the string
* array or the string value at index was not a double.
*/
private Double getDoubleValue(Integer index, String[] values) {
if (index == null || index == -1 || index > values.length) {
return null;
}
String value = values[index];
if (value == null || value.isEmpty()) {
return null;
}
try {
return Double.parseDouble(value);
} catch (NumberFormatException ex) {
return null;
}
}
}

View File

@ -0,0 +1,120 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.modules.drones;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress;
import org.sleuthkit.autopsy.ingest.IngestJobContext;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.SleuthkitCase;
/**
* Abstract base class for all Drone file extractors.
*/
abstract class DroneExtractor {
static private final String TEMP_FOLDER_NAME = "DroneExtractor"; //NON-NLS
private final Case currentCase;
/**
* Common constructor. Subclasses should call super in their constructor.
*
* @throws DroneIngestException
*/
protected DroneExtractor() throws DroneIngestException {
try {
currentCase = Case.getCurrentCaseThrows();
} catch (NoCurrentCaseException ex) {
throw new DroneIngestException("Unable to create drone extractor, no open case.", ex); //NON-NLS
}
}
abstract void process(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar) throws DroneIngestException;
abstract String getName();
/**
* Return the current case object.
*
* @return Current case
*/
final protected Case getCurrentCase() {
return currentCase;
}
/**
* Return the current SleuthkitCase.
*
* @return Current sleuthkit case
*/
final protected SleuthkitCase getSleuthkitCase() {
return currentCase.getSleuthkitCase();
}
/**
* Build the temp path and create the directory if it does not currently
* exist.
*
* @param currentCase Currently open case
* @param extractorName Name of extractor
*
* @return Path of the temp directory for this module
*/
protected Path getExtractorTempPath() {
Path path = Paths.get(currentCase.getTempDirectory(), TEMP_FOLDER_NAME, this.getClass().getCanonicalName());
File dir = path.toFile();
if (!dir.exists()) {
dir.mkdirs();
}
return path;
}
/**
* Create a copy of file in the case temp directory.
*
* @param context Current ingest context
* @param file File to be copied
*
* @return File copy.
*
* @throws DroneIngestException
*/
protected File getTemporaryFile(IngestJobContext context, AbstractFile file) throws DroneIngestException {
String tempFileName = file.getName() + file.getId() + file.getNameExtension();
Path tempFilePath = Paths.get(getExtractorTempPath().toString(), tempFileName);
try {
ContentUtils.writeToFile(file, tempFilePath.toFile(), context::dataSourceIngestIsCancelled);
} catch (IOException ex) {
throw new DroneIngestException(String.format("Unable to create temp file %s for abstract file %s objectID: %d", tempFilePath.toString(), file.getName(), file.getId()), ex); //NON-NLS
}
return tempFilePath.toFile();
}
}

View File

@ -0,0 +1,48 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.modules.drones;
import org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException;
/**
* IngestModuleException wrapper class.
*/
public class DroneIngestException extends IngestModuleException {
private static final long serialVersionUID = 1L;
/**
* Create exception containing the error message
*
* @param msg the message
*/
public DroneIngestException(String msg) {
super(msg);
}
/**
* Create exception containing the error message and cause exception
*
* @param msg the message
* @param ex cause exception
*/
public DroneIngestException(String msg, Exception ex) {
super(msg, ex);
}
}

View File

@ -0,0 +1,94 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.modules.drones;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.ingest.DataSourceIngestModule;
import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress;
import org.sleuthkit.autopsy.ingest.IngestJobContext;
import org.sleuthkit.autopsy.ingest.IngestMessage;
import org.sleuthkit.autopsy.ingest.IngestServices;
import org.sleuthkit.datamodel.Content;
/**
* Drone file ingest module.
*
*/
public final class DroneIngestModule implements DataSourceIngestModule {
private static final Logger logger = Logger.getLogger(DroneIngestModule.class.getName());
final private List<DroneExtractor> extractors;
private IngestJobContext context;
/**
* Construct a new drone ingest module.
*/
DroneIngestModule() {
extractors = new ArrayList<>();
}
@Override
public void startUp(IngestJobContext context) throws IngestModuleException {
this.context = context;
extractors.add(new DATExtractor());
}
@Messages({
"# {0} - AbstractFileName",
"DroneIngestModule_process_start=Started {0}"
})
@Override
public ProcessResult process(Content dataSource, DataSourceIngestModuleProgress progressBar) {
ProcessResult processResult = ProcessResult.OK;
IngestServices services = IngestServices.getInstance();
services.postMessage(IngestMessage.createMessage(
IngestMessage.MessageType.INFO,
DroneIngestModuleFactory.getModuleName(),
Bundle.DroneIngestModule_process_start(dataSource.getName())));
progressBar.switchToIndeterminate();
for (DroneExtractor extractor : extractors) {
if (context.dataSourceIngestIsCancelled()) {
logger.log(Level.INFO, "Drone ingest has been canceled, quitting before {0}", extractor.getName()); //NON-NLS
break;
}
progressBar.progress(extractor.getName());
try {
extractor.process(dataSource, context, progressBar);
} catch (DroneIngestException ex) {
logger.log(Level.SEVERE, String.format("Exception thrown from drone extractor %s", extractor.getName()), ex);
}
}
return processResult;
}
}

View File

@ -0,0 +1,72 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.modules.drones;
import org.openide.util.NbBundle.Messages;
import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.coreutils.Version;
import org.sleuthkit.autopsy.ingest.DataSourceIngestModule;
import org.sleuthkit.autopsy.ingest.IngestModuleFactory;
import org.sleuthkit.autopsy.ingest.IngestModuleFactoryAdapter;
import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings;
/**
* Drone file ingest module factory.
*/
@ServiceProvider(service = IngestModuleFactory.class)
public class DroneIngestModuleFactory extends IngestModuleFactoryAdapter {
@Messages({
"DroneIngestModule_Name=Drone",
"DroneIngestModule_Description=Description"
})
/**
* Helper function for returning the name of this module.
*/
static String getModuleName() {
return Bundle.DroneIngestModule_Name();
}
@Override
public String getModuleDisplayName() {
return getModuleName();
}
@Override
public String getModuleDescription() {
return Bundle.DroneIngestModule_Description();
}
@Override
public String getModuleVersionNumber() {
return Version.getVersion();
}
@Override
public boolean isDataSourceIngestModuleFactory() {
return true;
}
@Override
public DataSourceIngestModule createDataSourceIngestModule(IngestModuleIngestJobSettings ingestJobOptions) {
return new DroneIngestModule();
}
}

View File

@ -14,6 +14,7 @@ KMLReport.trackpointDatabaseError=Could not get GPS Trackpoints from database.
KMLReport.trackpointError=Could not extract Trackpoint information.
KMLReport.unableToExtractPhotos=Could not extract photo information.
KMLReport.unableToOpenCase=Exception while getting open case.
ReportBodyFile.ingestWarning.text=Ingest Warning message
ReportKML.progress.querying=Querying files...
ReportKML.progress.loading=Loading files...
ReportKML.getName.text=Google Earth KML
@ -29,4 +30,6 @@ Waypoint_EXIF_Display_String=EXIF Metadata With Location
Waypoint_Last_Known_Display_String=GPS Last Known Location
Waypoint_Route_Point_Display_String=GPS Individual Route Point
Waypoint_Search_Display_String=GPS Search
Waypoint_Track_Display_String=GPS Track
Waypoint_Track_Point_Display_String=GPS Individual Track Point
Waypoint_Trackpoint_Display_String=GPS Trackpoint

View File

@ -2,7 +2,7 @@
*
* Autopsy Forensic Browser
*
* Copyright 2014-2018 Basis Technology Corp.
* Copyright 2014-2020 Basis Technology Corp.
* contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -33,7 +33,6 @@ import java.io.OutputStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import org.jdom2.Document;
@ -48,6 +47,7 @@ import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.geolocation.datamodel.GeoLocationDataException;
import org.sleuthkit.autopsy.geolocation.datamodel.Waypoint;
import org.sleuthkit.autopsy.geolocation.datamodel.Route;
import org.sleuthkit.autopsy.geolocation.datamodel.Track;
import org.sleuthkit.autopsy.geolocation.datamodel.WaypointBuilder;
import org.sleuthkit.autopsy.report.ReportBranding;
import org.sleuthkit.autopsy.report.ReportProgressPanel;
@ -79,6 +79,7 @@ public final class KMLReport implements GeneralReportModule {
private Element gpsRouteFolder;
private Element gpsSearchesFolder;
private Element gpsTrackpointsFolder;
private Element gpsTracksFolder;
private List<Waypoint> waypointList = null;
@ -142,7 +143,10 @@ public final class KMLReport implements GeneralReportModule {
"Waypoint_Route_Point_Display_String=GPS Individual Route Point",
"Waypoint_Search_Display_String=GPS Search",
"Waypoint_Trackpoint_Display_String=GPS Trackpoint",
"Route_Details_Header=GPS Route"
"Waypoint_Track_Display_String=GPS Track",
"Route_Details_Header=GPS Route",
"ReportBodyFile.ingestWarning.text=Ingest Warning message",
"Waypoint_Track_Point_Display_String=GPS Individual Track Point"
})
public void generateReport(String baseReportDir, ReportProgressPanel progressPanel, List<Waypoint> waypointList) {
@ -175,6 +179,7 @@ public final class KMLReport implements GeneralReportModule {
try {
makeRoutes(skCase);
makeTracks(skCase);
addLocationsToReport(skCase, baseReportDir);
} catch (GeoLocationDataException | IOException ex) {
errorMessage = "Failed to complete report.";
@ -280,12 +285,18 @@ public final class KMLReport implements GeneralReportModule {
Element hrefTrackpoints = new Element("href", ns).addContent(cdataTrackpoints); //NON-NLS
gpsTrackpointsFolder.addContent(new Element("Icon", ns).addContent(hrefTrackpoints)); //NON-NLS
gpsTracksFolder = new Element("Folder", ns); //NON-NLS
CDATA cdataTrack = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-trackpoint.png"); //NON-NLS
Element hrefTrack = new Element("href", ns).addContent(cdataTrack); //NON-NLS
gpsTracksFolder.addContent(new Element("Icon", ns).addContent(hrefTrack)); //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
gpsTracksFolder.addContent(new Element("name", ns).addContent("GPS Tracks")); //NON-NLS
document.addContent(gpsExifMetadataFolder);
document.addContent(gpsBookmarksFolder);
@ -293,6 +304,7 @@ public final class KMLReport implements GeneralReportModule {
document.addContent(gpsRouteFolder);
document.addContent(gpsSearchesFolder);
document.addContent(gpsTrackpointsFolder);
document.addContent(gpsTracksFolder);
return kmlDocument;
}
@ -402,7 +414,7 @@ public final class KMLReport implements GeneralReportModule {
if (waypointList == null) {
routes = Route.getRoutes(skCase);
} else {
routes = new ArrayList<>();
routes = WaypointBuilder.getRoutes(waypointList);
}
for (Route route : routes) {
@ -410,7 +422,12 @@ public final class KMLReport implements GeneralReportModule {
}
}
void addRouteToReport(Route route) {
/**
* Add the given route to the KML report.
*
* @param route
*/
private void addRouteToReport(Route route) {
List<Waypoint> routePoints = route.getRoute();
Waypoint start = null;
Waypoint end = null;
@ -455,6 +472,49 @@ public final class KMLReport implements GeneralReportModule {
}
}
/**
* Add the track to the track folder in the document.
*
* @param skCase Currently open case.
*
* @throws TskCoreException
*/
void makeTracks(SleuthkitCase skCase) throws GeoLocationDataException {
List<Track> tracks = null;
if (waypointList == null) {
tracks = Track.getTracks(skCase, null);
} else {
tracks = WaypointBuilder.getTracks(waypointList);
}
for (Track track : tracks) {
addTrackToReport(track);
}
}
/**
* Add a track to the KML report.
*
* @param track
*/
private void addTrackToReport(Track track) {
List<Waypoint> trackPoints = track.getPath();
// Adding a folder with the track name so that all of the
// tracks waypoints with be grouped together.
Element trackFolder = new Element("Folder", ns); //NON-NLS
trackFolder.addContent(new Element("name", ns).addContent(track.getLabel())); //NON-NLS
gpsTracksFolder.addContent(trackFolder);
for (Waypoint point : trackPoints) {
Element element = makePoint(point.getLatitude(), point.getLongitude(), point.getAltitude());
trackFolder.addContent(makePlacemark("",
FeatureColor.GREEN, getFormattedDetails(point, Bundle.Waypoint_Track_Point_Display_String()),
point.getTimestamp(), element, formattedCoordinates(point.getLatitude(), point.getLongitude()))); //NON-NLS
}
}
/**
* Format a point time stamp (in seconds) to the report format.
*
@ -739,6 +799,14 @@ public final class KMLReport implements GeneralReportModule {
return result.toString();
}
/**
* Returns a HTML formatted string with the given title and value.
*
* @param title
* @param value
*
* @return HTML formatted string
*/
private String formatAttribute(String title, String value) {
return String.format(HTML_PROP_FORMAT, title, value);
}

View File

@ -2,7 +2,7 @@
<settings defaultResolver="main"/>
<resolvers>
<chain name="main">
<ibiblio name="central" m2compatible="true"/>
<ibiblio name="central" root="https://repo1.maven.org/maven2" m2compatible="true"/>
<ibiblio name="ibiblio" m2compatible="true"/>
<ibiblio name="xerial" m2compatible="true" root="http://www.xerial.org/maven/repository/snapshot" />
</chain>

View File

@ -2,7 +2,7 @@
<settings defaultResolver="main"/>
<resolvers>
<chain name="main">
<ibiblio name="central" m2compatible="true"/>
<ibiblio name="central" root="https://repo1.maven.org/maven2" m2compatible="true"/>
<ibiblio name="ibiblio" m2compatible="true"/>
<ibiblio name="xerial" m2compatible="true" root="http://www.xerial.org/maven/repository/snapshot" />
</chain>

View File

@ -2,7 +2,7 @@
<settings defaultResolver="main"/>
<resolvers>
<chain name="main">
<ibiblio name="central" m2compatible="true"/>
<ibiblio name="central" root="https://repo1.maven.org/maven2" m2compatible="true"/>
<ibiblio name="maven.restlet.org" root="http://maven.restlet.com" m2compatible="true" />
</chain>
</resolvers>

View File

@ -2,7 +2,7 @@
<settings defaultResolver="main"/>
<resolvers>
<chain name="main">
<ibiblio name="central" m2compatible="true"/>
<ibiblio name="central" root="https://repo1.maven.org/maven2" m2compatible="true"/>
<ibiblio name="maven.restlet.org" root="http://maven.restlet.com" m2compatible="true" />
</chain>
</resolvers>

View File

@ -14,8 +14,8 @@ One can add and remove MIME types in the "Tools", "Options", "File Extension Mis
\image html extension-mismatch-detected-configuration.PNG
<br>
If you'd like to contribute your changes back to the community, then you'll need to upload your updated %APPDATA%\autopsy\dev\config\mismatch_config.xml file by either:
- Make a fork of the Github Autopsy repository, copy the new file into the src\org\sleuthkit\autopsy\fileextmismatch folder and submit a pull request
If you'd like to contribute your changes back to the community, then you'll need to upload your updated %APPDATA%\\autopsy\\dev\\config\\mismatch_config.xml file by either:
- Make a fork of the Github Autopsy repository, copy the new file into the src\\org\\sleuthkit\\autopsy\\fileextmismatch folder and submit a pull request
- Attach the entire mismatch_config.xml file to a github issue.
Using the Module

BIN
thirdparty/DatCon/3.6.9/DatCon.jar vendored Executable file

Binary file not shown.

View File

@ -2,7 +2,7 @@
<settings defaultResolver="main"/>
<resolvers>
<chain name="main">
<ibiblio name="central" m2compatible="true"/>
<ibiblio name="central" root="https://repo1.maven.org/maven2" m2compatible="true"/>
<ibiblio name="maven.restlet.org" root="http://maven.restlet.com" m2compatible="true" />
</chain>
</resolvers>