mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-09 06:39:33 +00:00
Merge branch 'develop' into 5465-CASE-UCO-reference-local-files
This commit is contained in:
commit
c0959ce934
@ -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>
|
||||
|
||||
|
@ -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>
|
||||
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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() {
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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=
|
||||
|
@ -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)
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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>
|
||||
|
@ -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();
|
||||
|
@ -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) {
|
||||
|
||||
|
141
Core/src/org/sleuthkit/autopsy/geolocation/datamodel/GeoPath.java
Executable file
141
Core/src/org/sleuthkit/autopsy/geolocation/datamodel/GeoPath.java
Executable 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 : "";
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
243
Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Track.java
Executable file
243
Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Track.java
Executable 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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),
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
BIN
Core/src/org/sleuthkit/autopsy/images/minus-grey.png
Executable file
BIN
Core/src/org/sleuthkit/autopsy/images/minus-grey.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 128 B |
BIN
Core/src/org/sleuthkit/autopsy/images/plus-grey.png
Executable file
BIN
Core/src/org/sleuthkit/autopsy/images/plus-grey.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 197 B |
6
Core/src/org/sleuthkit/autopsy/modules/drones/Bundle.properties-MERGED
Executable file
6
Core/src/org/sleuthkit/autopsy/modules/drones/Bundle.properties-MERGED
Executable 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}
|
149
Core/src/org/sleuthkit/autopsy/modules/drones/DATDumper.java
Executable file
149
Core/src/org/sleuthkit/autopsy/modules/drones/DATDumper.java
Executable 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
|
||||
}
|
||||
}
|
||||
}
|
322
Core/src/org/sleuthkit/autopsy/modules/drones/DATExtractor.java
Executable file
322
Core/src/org/sleuthkit/autopsy/modules/drones/DATExtractor.java
Executable 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
120
Core/src/org/sleuthkit/autopsy/modules/drones/DroneExtractor.java
Executable file
120
Core/src/org/sleuthkit/autopsy/modules/drones/DroneExtractor.java
Executable 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();
|
||||
}
|
||||
|
||||
}
|
48
Core/src/org/sleuthkit/autopsy/modules/drones/DroneIngestException.java
Executable file
48
Core/src/org/sleuthkit/autopsy/modules/drones/DroneIngestException.java
Executable 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);
|
||||
}
|
||||
}
|
94
Core/src/org/sleuthkit/autopsy/modules/drones/DroneIngestModule.java
Executable file
94
Core/src/org/sleuthkit/autopsy/modules/drones/DroneIngestModule.java
Executable 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;
|
||||
}
|
||||
|
||||
}
|
72
Core/src/org/sleuthkit/autopsy/modules/drones/DroneIngestModuleFactory.java
Executable file
72
Core/src/org/sleuthkit/autopsy/modules/drones/DroneIngestModuleFactory.java
Executable 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();
|
||||
}
|
||||
|
||||
}
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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
BIN
thirdparty/DatCon/3.6.9/DatCon.jar
vendored
Executable file
Binary file not shown.
@ -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>
|
||||
|
Loading…
x
Reference in New Issue
Block a user