mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-15 01:07:42 +00:00
Merge branch 'develop' of github.com:sleuthkit/autopsy into sanitizing_lowercase_1982
This commit is contained in:
commit
b65e39ae3b
@ -123,8 +123,8 @@
|
|||||||
tofile="${ext.dir}/sleuthkit-${TSK_VERSION}.jar"/>
|
tofile="${ext.dir}/sleuthkit-${TSK_VERSION}.jar"/>
|
||||||
<copy file="${env.TSK_HOME}/bindings/java/lib/sqlite-jdbc-3.25.2.jar"
|
<copy file="${env.TSK_HOME}/bindings/java/lib/sqlite-jdbc-3.25.2.jar"
|
||||||
tofile="${ext.dir}/sqlite-jdbc-3.25.2.jar"/>
|
tofile="${ext.dir}/sqlite-jdbc-3.25.2.jar"/>
|
||||||
<copy file="${env.TSK_HOME}/bindings/java/lib/postgresql-9.4.1211.jre7.jar"
|
<copy file="${env.TSK_HOME}/bindings/java/lib/postgresql-42.2.18.jar"
|
||||||
tofile="${ext.dir}/postgresql-9.4.1211.jre7.jar"/>
|
tofile="${ext.dir}/postgresql-42.2.18.jar"/>
|
||||||
<copy file="${env.TSK_HOME}/bindings/java/lib/mchange-commons-java-0.2.9.jar"
|
<copy file="${env.TSK_HOME}/bindings/java/lib/mchange-commons-java-0.2.9.jar"
|
||||||
tofile="${ext.dir}/mchange-commons-java-0.2.9.jar"/>
|
tofile="${ext.dir}/mchange-commons-java-0.2.9.jar"/>
|
||||||
<copy file="${env.TSK_HOME}/bindings/java/lib/c3p0-0.9.5.jar"
|
<copy file="${env.TSK_HOME}/bindings/java/lib/c3p0-0.9.5.jar"
|
||||||
|
@ -99,7 +99,7 @@ file.reference.opencensus-api-0.19.2.jar=release\\modules\\ext\\opencensus-api-0
|
|||||||
file.reference.opencensus-contrib-grpc-metrics-0.19.2.jar=release\\modules\\ext\\opencensus-contrib-grpc-metrics-0.19.2.jar
|
file.reference.opencensus-contrib-grpc-metrics-0.19.2.jar=release\\modules\\ext\\opencensus-contrib-grpc-metrics-0.19.2.jar
|
||||||
file.reference.opencensus-contrib-http-util-0.19.2.jar=release\\modules\\ext\\opencensus-contrib-http-util-0.19.2.jar
|
file.reference.opencensus-contrib-http-util-0.19.2.jar=release\\modules\\ext\\opencensus-contrib-http-util-0.19.2.jar
|
||||||
file.reference.opennlp-tools-1.9.1.jar=release\\modules\\ext\\opennlp-tools-1.9.1.jar
|
file.reference.opennlp-tools-1.9.1.jar=release\\modules\\ext\\opennlp-tools-1.9.1.jar
|
||||||
file.reference.postgresql-9.4.1211.jre7.jar=release\\modules\\ext\\postgresql-9.4.1211.jre7.jar
|
file.reference.postgresql-42.2.18.jar=release\\modules\\ext\\postgresql-42.2.18.jar
|
||||||
file.reference.proto-google-cloud-translate-v3beta1-0.53.0.jar=release\\modules\\ext\\proto-google-cloud-translate-v3beta1-0.53.0.jar
|
file.reference.proto-google-cloud-translate-v3beta1-0.53.0.jar=release\\modules\\ext\\proto-google-cloud-translate-v3beta1-0.53.0.jar
|
||||||
file.reference.proto-google-common-protos-1.15.0.jar=release\\modules\\ext\\proto-google-common-protos-1.15.0.jar
|
file.reference.proto-google-common-protos-1.15.0.jar=release\\modules\\ext\\proto-google-common-protos-1.15.0.jar
|
||||||
file.reference.proto-google-iam-v1-0.12.0.jar=release\\modules\\ext\\proto-google-iam-v1-0.12.0.jar
|
file.reference.proto-google-iam-v1-0.12.0.jar=release\\modules\\ext\\proto-google-iam-v1-0.12.0.jar
|
||||||
@ -118,7 +118,7 @@ file.reference.StixLib.jar=release\\modules\\ext\\StixLib.jar
|
|||||||
file.reference.threetenbp-1.3.3.jar=release\\modules\\ext\\threetenbp-1.3.3.jar
|
file.reference.threetenbp-1.3.3.jar=release\\modules\\ext\\threetenbp-1.3.3.jar
|
||||||
file.reference.webp-imageio-sejda-0.1.0.jar=release\\modules\\ext\\webp-imageio-sejda-0.1.0.jar
|
file.reference.webp-imageio-sejda-0.1.0.jar=release\\modules\\ext\\webp-imageio-sejda-0.1.0.jar
|
||||||
file.reference.xmpcore-5.1.3.jar=release\\modules\\ext\\xmpcore-5.1.3.jar
|
file.reference.xmpcore-5.1.3.jar=release\\modules\\ext\\xmpcore-5.1.3.jar
|
||||||
file.reference.YaraJNIWrapper.jar=release\\modules\\ext\\YaraJNIWrapper.jar
|
file.reference.YaraJNIWrapper.jar=release/modules/ext/YaraJNIWrapper.jar
|
||||||
file.reference.zookeeper-3.4.6.jar=release\\modules\\ext\\zookeeper-3.4.6.jar
|
file.reference.zookeeper-3.4.6.jar=release\\modules\\ext\\zookeeper-3.4.6.jar
|
||||||
javac.source=1.8
|
javac.source=1.8
|
||||||
javac.compilerargs=-Xlint -Xlint:-serial
|
javac.compilerargs=-Xlint -Xlint:-serial
|
||||||
|
@ -436,6 +436,10 @@
|
|||||||
<runtime-relative-path>ext/commons-codec-1.11.jar</runtime-relative-path>
|
<runtime-relative-path>ext/commons-codec-1.11.jar</runtime-relative-path>
|
||||||
<binary-origin>release\modules\ext\commons-codec-1.11.jar</binary-origin>
|
<binary-origin>release\modules\ext\commons-codec-1.11.jar</binary-origin>
|
||||||
</class-path-extension>
|
</class-path-extension>
|
||||||
|
<class-path-extension>
|
||||||
|
<runtime-relative-path>ext/postgresql-42.2.18.jar</runtime-relative-path>
|
||||||
|
<binary-origin>release\modules\ext\postgresql-42.2.18.jar</binary-origin>
|
||||||
|
</class-path-extension>
|
||||||
<class-path-extension>
|
<class-path-extension>
|
||||||
<runtime-relative-path>ext/commons-pool2-2.4.2.jar</runtime-relative-path>
|
<runtime-relative-path>ext/commons-pool2-2.4.2.jar</runtime-relative-path>
|
||||||
<binary-origin>release\modules\ext\commons-pool2-2.4.2.jar</binary-origin>
|
<binary-origin>release\modules\ext\commons-pool2-2.4.2.jar</binary-origin>
|
||||||
@ -724,10 +728,6 @@
|
|||||||
<runtime-relative-path>ext/jai_imageio-1.1.jar</runtime-relative-path>
|
<runtime-relative-path>ext/jai_imageio-1.1.jar</runtime-relative-path>
|
||||||
<binary-origin>release\modules\ext\jai_imageio-1.1.jar</binary-origin>
|
<binary-origin>release\modules\ext\jai_imageio-1.1.jar</binary-origin>
|
||||||
</class-path-extension>
|
</class-path-extension>
|
||||||
<class-path-extension>
|
|
||||||
<runtime-relative-path>ext/postgresql-9.4.1211.jre7.jar</runtime-relative-path>
|
|
||||||
<binary-origin>release\modules\ext\postgresql-9.4.1211.jre7.jar</binary-origin>
|
|
||||||
</class-path-extension>
|
|
||||||
<class-path-extension>
|
<class-path-extension>
|
||||||
<runtime-relative-path>ext/junit-3.8.1.jar</runtime-relative-path>
|
<runtime-relative-path>ext/junit-3.8.1.jar</runtime-relative-path>
|
||||||
<binary-origin>release\modules\ext\junit-3.8.1.jar</binary-origin>
|
<binary-origin>release\modules\ext\junit-3.8.1.jar</binary-origin>
|
||||||
|
@ -89,6 +89,7 @@ public final class UserPreferences {
|
|||||||
private static final String GEO_OSM_TILE_ZIP_PATH = "GeolocationOsmZipPath";
|
private static final String GEO_OSM_TILE_ZIP_PATH = "GeolocationOsmZipPath";
|
||||||
private static final String GEO_OSM_SERVER_ADDRESS = "GeolocationOsmServerAddress";
|
private static final String GEO_OSM_SERVER_ADDRESS = "GeolocationOsmServerAddress";
|
||||||
private static final String GEO_MBTILES_FILE_PATH = "GeolcoationMBTilesFilePath";
|
private static final String GEO_MBTILES_FILE_PATH = "GeolcoationMBTilesFilePath";
|
||||||
|
private static final String HEALTH_MONITOR_REPORT_PATH = "HealthMonitorReportPath";
|
||||||
|
|
||||||
// Prevent instantiation.
|
// Prevent instantiation.
|
||||||
private UserPreferences() {
|
private UserPreferences() {
|
||||||
@ -668,4 +669,22 @@ public final class UserPreferences {
|
|||||||
return Paths.get(UserMachinePreferences.getBaseTempDirectory(), getAppName())
|
return Paths.get(UserMachinePreferences.getBaseTempDirectory(), getAppName())
|
||||||
.toAbsolutePath().toString();
|
.toAbsolutePath().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the last used health monitor report path.
|
||||||
|
*
|
||||||
|
* @param reportPath Last used health monitor report path.
|
||||||
|
*/
|
||||||
|
public static void setHealthMonitorReportPath(String reportPath) {
|
||||||
|
preferences.put(HEALTH_MONITOR_REPORT_PATH, reportPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the last used health monitor report path.
|
||||||
|
*
|
||||||
|
* @return Last used health monitor report path. Empty string if no value has been recorded.
|
||||||
|
*/
|
||||||
|
public static String getHealthMonitorReportPath() {
|
||||||
|
return preferences.get(HEALTH_MONITOR_REPORT_PATH, "");
|
||||||
|
}
|
||||||
}
|
}
|
197
Core/src/org/sleuthkit/autopsy/coreutils/DomainTokenizer.java
Normal file
197
Core/src/org/sleuthkit/autopsy/coreutils/DomainTokenizer.java
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
/*
|
||||||
|
* 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.coreutils;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to get the domain from a url/domain provided removing the
|
||||||
|
* subdomain(s).
|
||||||
|
*/
|
||||||
|
class DomainTokenizer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a node in the trie. Children in the hashmap are identified by
|
||||||
|
* token. So for example, for a domain google.co.uk, the top level category
|
||||||
|
* would have an entry for "uk" linking to a domain category with "co".
|
||||||
|
*/
|
||||||
|
private static class DomainCategory extends HashMap<String, DomainCategory> {
|
||||||
|
|
||||||
|
private DomainCategory getOrAddChild(String childKey) {
|
||||||
|
DomainCategory cat = this.get(childKey);
|
||||||
|
if (cat == null) {
|
||||||
|
cat = new DomainCategory();
|
||||||
|
this.put(childKey, cat);
|
||||||
|
}
|
||||||
|
|
||||||
|
return cat;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Character for joining domain segments.
|
||||||
|
private static final String JOINER = ".";
|
||||||
|
// delimiter when used with regex
|
||||||
|
private static final String DELIMITER = "\\" + JOINER;
|
||||||
|
|
||||||
|
private static final String WILDCARD = "*";
|
||||||
|
private static final String EXCEPTION_PREFIX = "!";
|
||||||
|
|
||||||
|
// taken from https://publicsuffix.org/list/public_suffix_list.dat
|
||||||
|
// file containing line seperated suffixes
|
||||||
|
// rules for parsing can be found here: https://publicsuffix.org/list/
|
||||||
|
private static final String DOMAIN_LIST = "public_suffix_list.dat";
|
||||||
|
|
||||||
|
// token for comments
|
||||||
|
private static final String COMMENT_TOKEN = "//";
|
||||||
|
|
||||||
|
// singleton instance of this class.
|
||||||
|
private static DomainTokenizer categorizer = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the singleton instance of this class.
|
||||||
|
*
|
||||||
|
* @return The DomainCategorizer instance.
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
static DomainTokenizer getInstance() throws IOException {
|
||||||
|
if (categorizer == null) {
|
||||||
|
categorizer = load();
|
||||||
|
}
|
||||||
|
|
||||||
|
return categorizer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads a DomainCategorizer instance using the public suffix list.
|
||||||
|
*
|
||||||
|
* @return The DomainCategorizer instance.
|
||||||
|
* @throws IOException If there is an error reading the file.
|
||||||
|
*/
|
||||||
|
private static DomainTokenizer load() throws IOException {
|
||||||
|
try (InputStream is = DomainTokenizer.class.getResourceAsStream(DOMAIN_LIST);
|
||||||
|
InputStreamReader isReader = new InputStreamReader(is, StandardCharsets.UTF_8);
|
||||||
|
BufferedReader reader = new BufferedReader(isReader)) {
|
||||||
|
|
||||||
|
DomainTokenizer categorizer = new DomainTokenizer();
|
||||||
|
while (reader.ready()) {
|
||||||
|
String line = reader.readLine();
|
||||||
|
String trimmed = line.trim();
|
||||||
|
if (!StringUtils.isBlank(trimmed) && !trimmed.startsWith(COMMENT_TOKEN)) {
|
||||||
|
categorizer.addDomainSuffix(trimmed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return categorizer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private DomainTokenizer() {
|
||||||
|
}
|
||||||
|
|
||||||
|
// The top-level trie node.
|
||||||
|
private final DomainCategory trie = new DomainCategory();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a domain suffix and adds components to the trie in reverse order
|
||||||
|
* (i.e. ".co.uk" becomes "uk" -> "co").
|
||||||
|
*
|
||||||
|
* @param domainSuffix The domain suffix.
|
||||||
|
*/
|
||||||
|
private void addDomainSuffix(String domainSuffix) {
|
||||||
|
if (StringUtils.isBlank(domainSuffix)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] tokens = domainSuffix.toLowerCase().trim().split(DELIMITER);
|
||||||
|
|
||||||
|
DomainCategory cat = trie;
|
||||||
|
for (int i = tokens.length - 1; i >= 0; i--) {
|
||||||
|
String token = tokens[i];
|
||||||
|
if (StringUtils.isBlank(token)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
cat = cat.getOrAddChild(tokens[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the domain by attempting to identify the host without the subdomain.
|
||||||
|
* If no domain can be determined, the domain is returned.
|
||||||
|
*
|
||||||
|
* @param domain The domain to query for.
|
||||||
|
* @return If provided argument is blank, null is returned. If no domain
|
||||||
|
* suffixes can be identified, the full host is returned. If a host and
|
||||||
|
* suffixes are identified, the domain (all suffixes with a prefix of the
|
||||||
|
* next token) are returned.
|
||||||
|
*/
|
||||||
|
String getDomain(String domain) {
|
||||||
|
if (StringUtils.isBlank(domain)) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> tokens = Stream.of(domain.toLowerCase().split(DELIMITER))
|
||||||
|
.filter(StringUtils::isNotBlank)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
int idx = tokens.size() - 1;
|
||||||
|
DomainCategory cat = trie;
|
||||||
|
|
||||||
|
for (; idx >= 0; idx--) {
|
||||||
|
// an exception rule must be at the beginning of a suffix, and, in
|
||||||
|
// practice, indicates a domain that would otherwise be a further
|
||||||
|
// suffix with a wildcard rule per: https://publicsuffix.org/list/
|
||||||
|
if (cat.get(EXCEPTION_PREFIX + tokens.get(idx)) != null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
DomainCategory newCat = cat.get(tokens.get(idx));
|
||||||
|
|
||||||
|
// if no matching token can be found, look for wildcard token
|
||||||
|
if (newCat == null) {
|
||||||
|
// if no wildcard token can be found, the portion found
|
||||||
|
// so far is the suffix.
|
||||||
|
newCat = cat.get(WILDCARD);
|
||||||
|
if (newCat == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cat = newCat;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if first suffix cannot be found, return the whole domain
|
||||||
|
if (idx == tokens.size() - 1) {
|
||||||
|
return domain;
|
||||||
|
} else {
|
||||||
|
int minIndex = Math.max(0, idx);
|
||||||
|
List<String> subList = tokens.subList(minIndex, tokens.size());
|
||||||
|
return String.join(JOINER, subList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -18,13 +18,17 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.coreutils;
|
package org.sleuthkit.autopsy.coreutils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
import java.util.StringTokenizer;
|
import java.util.logging.Level;
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
|
||||||
public class NetworkUtils {
|
public class NetworkUtils {
|
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(NetworkUtils.class.getName());
|
||||||
|
|
||||||
private NetworkUtils() {
|
private NetworkUtils() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,7 +36,7 @@ public class NetworkUtils {
|
|||||||
* Set the host name variable. Sometimes the network can be finicky, so the
|
* Set the host name variable. Sometimes the network can be finicky, so the
|
||||||
* answer returned by getHostName() could throw an exception or be null.
|
* answer returned by getHostName() could throw an exception or be null.
|
||||||
* Have it read the environment variable if getHostName() is unsuccessful.
|
* Have it read the environment variable if getHostName() is unsuccessful.
|
||||||
*
|
*
|
||||||
* @return the local host name
|
* @return the local host name
|
||||||
*/
|
*/
|
||||||
public static String getLocalHostName() {
|
public static String getLocalHostName() {
|
||||||
@ -49,16 +53,16 @@ public class NetworkUtils {
|
|||||||
}
|
}
|
||||||
return hostName;
|
return hostName;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempt to manually extract the domain from a URL.
|
* Attempt to manually extract the domain from a URL.
|
||||||
*
|
*
|
||||||
* @param url
|
* @param url
|
||||||
* @return empty string if no domain could be found
|
* @return empty string if no domain could be found
|
||||||
*/
|
*/
|
||||||
private static String getBaseDomain(String url) {
|
private static String getBaseDomain(String url) {
|
||||||
String host = null;
|
String host = null;
|
||||||
|
|
||||||
//strip protocol
|
//strip protocol
|
||||||
String cleanUrl = url.replaceFirst(".*:\\/\\/", "");
|
String cleanUrl = url.replaceFirst(".*:\\/\\/", "");
|
||||||
|
|
||||||
@ -70,42 +74,30 @@ public class NetworkUtils {
|
|||||||
host = cleanUrl;
|
host = cleanUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
//get the domain part from host (last 2)
|
String base = host;
|
||||||
StringTokenizer tok = new StringTokenizer(host, ".");
|
try {
|
||||||
StringBuilder hostB = new StringBuilder();
|
base = DomainTokenizer.getInstance().getDomain(host);
|
||||||
int toks = tok.countTokens();
|
} catch (IOException ex) {
|
||||||
|
logger.log(Level.WARNING, "Unable to load resources for domain categorization.", ex);
|
||||||
for (int count = 0; count < toks; ++count) {
|
|
||||||
String part = tok.nextToken();
|
|
||||||
int diff = toks - count;
|
|
||||||
if (diff < 3) {
|
|
||||||
hostB.append(part);
|
|
||||||
}
|
|
||||||
if (diff == 2) {
|
|
||||||
hostB.append(".");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
String base = hostB.toString();
|
|
||||||
// verify there are no special characters in there
|
// verify there are no special characters in there
|
||||||
if (base.matches(".*[~`!@#$%^&\\*\\(\\)\\+={}\\[\\];:\\?<>,/ ].*")) {
|
if (base.matches(".*[~`!@#$%^&\\*\\(\\)\\+={}\\[\\];:\\?<>,/ ].*")) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
//verify that the base domain actually has a '.', details JIRA-4609
|
//verify that the base domain actually has a '.', details JIRA-4609
|
||||||
if(!base.contains(".")) {
|
if (!base.contains(".")) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
return base;
|
return base;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempt to extract the domain from a URL.
|
* Attempt to extract the domain from a URL. Will start by using the
|
||||||
* Will start by using the built-in URL class, and if that fails will
|
* built-in URL class, and if that fails will try to extract it manually.
|
||||||
* try to extract it manually.
|
*
|
||||||
*
|
|
||||||
* @param urlString The URL to extract the domain from
|
* @param urlString The URL to extract the domain from
|
||||||
* @return empty string if no domain name was found
|
* @return empty string if no domain name was found
|
||||||
*/
|
*/
|
||||||
@ -113,20 +105,22 @@ public class NetworkUtils {
|
|||||||
if (urlString == null) {
|
if (urlString == null) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
String result = "";
|
String urlHost = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
URL url = new URL(urlString);
|
URL url = new URL(urlString);
|
||||||
result = url.getHost();
|
urlHost = url.getHost();
|
||||||
} catch (MalformedURLException ex) {
|
} catch (MalformedURLException ex) {
|
||||||
//do not log if not a valid URL - we will try to extract it ourselves
|
//do not log if not a valid URL - we will try to extract it ourselves
|
||||||
}
|
}
|
||||||
|
|
||||||
//was not a valid URL, try a less picky method
|
// if there is a valid url host, get base domain from that host
|
||||||
if (result == null || result.trim().isEmpty()) {
|
// otherwise use urlString and parse the domain
|
||||||
return getBaseDomain(urlString);
|
String result = (StringUtils.isNotBlank(urlHost))
|
||||||
}
|
? getBaseDomain(urlHost)
|
||||||
|
: getBaseDomain(urlString);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
13494
Core/src/org/sleuthkit/autopsy/coreutils/public_suffix_list.dat
Normal file
13494
Core/src/org/sleuthkit/autopsy/coreutils/public_suffix_list.dat
Normal file
File diff suppressed because it is too large
Load Diff
@ -982,7 +982,21 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
|||||||
}
|
}
|
||||||
map.put(attribute.getAttributeType().getDisplayName(), value);
|
map.put(attribute.getAttributeType().getDisplayName(), value);
|
||||||
} else {
|
} else {
|
||||||
map.put(attribute.getAttributeType().getDisplayName(), attribute.getDisplayString());
|
switch(attribute.getAttributeType().getValueType()) {
|
||||||
|
case INTEGER:
|
||||||
|
map.put(attribute.getAttributeType().getDisplayName(), attribute.getValueInt());
|
||||||
|
break;
|
||||||
|
case DOUBLE:
|
||||||
|
map.put(attribute.getAttributeType().getDisplayName(), attribute.getValueDouble());
|
||||||
|
break;
|
||||||
|
case LONG:
|
||||||
|
map.put(attribute.getAttributeType().getDisplayName(), attribute.getValueLong());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
map.put(attribute.getAttributeType().getDisplayName(), attribute.getDisplayString());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (TskCoreException ex) {
|
} catch (TskCoreException ex) {
|
||||||
|
@ -121,6 +121,8 @@ public final class IconsUtil {
|
|||||||
imageFile = "web-account-type.png"; //NON-NLS
|
imageFile = "web-account-type.png"; //NON-NLS
|
||||||
} else if (typeID == ARTIFACT_TYPE.TSK_WEB_FORM_ADDRESS.getTypeID()) {
|
} else if (typeID == ARTIFACT_TYPE.TSK_WEB_FORM_ADDRESS.getTypeID()) {
|
||||||
imageFile = "web-form-address.png"; //NON-NLS
|
imageFile = "web-form-address.png"; //NON-NLS
|
||||||
|
} else if (typeID == ARTIFACT_TYPE.TSK_GPS_AREA.getTypeID()) {
|
||||||
|
imageFile = "gps-area.png"; //NON-NLS
|
||||||
} else {
|
} else {
|
||||||
imageFile = "artifact-icon.png"; //NON-NLS
|
imageFile = "artifact-icon.png"; //NON-NLS
|
||||||
}
|
}
|
||||||
|
@ -22,11 +22,11 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import javafx.util.Pair;
|
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.autopsy.geolocation.datamodel.GeoLocationDataException;
|
import org.sleuthkit.autopsy.geolocation.datamodel.GeoLocationDataException;
|
||||||
import org.sleuthkit.autopsy.geolocation.datamodel.GeoLocationParseResult;
|
import org.sleuthkit.autopsy.geolocation.datamodel.GeoLocationParseResult;
|
||||||
|
import org.sleuthkit.autopsy.geolocation.datamodel.Area;
|
||||||
import org.sleuthkit.autopsy.geolocation.datamodel.Track;
|
import org.sleuthkit.autopsy.geolocation.datamodel.Track;
|
||||||
import org.sleuthkit.autopsy.geolocation.datamodel.Waypoint;
|
import org.sleuthkit.autopsy.geolocation.datamodel.Waypoint;
|
||||||
import org.sleuthkit.autopsy.geolocation.datamodel.WaypointBuilder;
|
import org.sleuthkit.autopsy.geolocation.datamodel.WaypointBuilder;
|
||||||
@ -81,7 +81,7 @@ abstract class AbstractWaypointFetcher implements WaypointBuilder.WaypointFilter
|
|||||||
* @param wasEntirelySuccessful True if no errors occurred while processing.
|
* @param wasEntirelySuccessful True if no errors occurred while processing.
|
||||||
*/
|
*/
|
||||||
abstract void handleFilteredWaypointSet(Set<MapWaypoint> mapWaypoints, List<Set<MapWaypoint>> tracks,
|
abstract void handleFilteredWaypointSet(Set<MapWaypoint> mapWaypoints, List<Set<MapWaypoint>> tracks,
|
||||||
boolean wasEntirelySuccessful);
|
List<Set<MapWaypoint>> areas, boolean wasEntirelySuccessful);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void process(GeoLocationParseResult<Waypoint> waypointResults) {
|
public void process(GeoLocationParseResult<Waypoint> waypointResults) {
|
||||||
@ -93,36 +93,54 @@ abstract class AbstractWaypointFetcher implements WaypointBuilder.WaypointFilter
|
|||||||
logger.log(Level.WARNING, "Exception thrown while retrieving list of Tracks", ex);
|
logger.log(Level.WARNING, "Exception thrown while retrieving list of Tracks", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Pair<List<Waypoint>, List<List<Waypoint>>> waypointsAndTracks = createWaypointList(
|
GeoLocationParseResult<Area> areaResults = null;
|
||||||
|
if (filters.getArtifactTypes().contains(ARTIFACT_TYPE.TSK_GPS_AREA)) {
|
||||||
|
try {
|
||||||
|
areaResults = Area.getAreas(Case.getCurrentCase().getSleuthkitCase(), filters.getDataSources());
|
||||||
|
} catch (GeoLocationDataException ex) {
|
||||||
|
logger.log(Level.WARNING, "Exception thrown while retrieving list of Areas", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GeoDataSet geoDataSet = createWaypointList(
|
||||||
waypointResults.getItems(),
|
waypointResults.getItems(),
|
||||||
(trackResults == null) ? new ArrayList<Track>() : trackResults.getItems());
|
(trackResults == null) ? new ArrayList<>() : trackResults.getItems(),
|
||||||
|
(areaResults == null) ? new ArrayList<>() : areaResults.getItems());
|
||||||
|
|
||||||
|
|
||||||
final Set<MapWaypoint> pointSet = MapWaypoint.getWaypoints(waypointsAndTracks.getKey());
|
final Set<MapWaypoint> pointSet = MapWaypoint.getWaypoints(geoDataSet.getWaypoints());
|
||||||
final List<Set<MapWaypoint>> trackSets = new ArrayList<>();
|
final List<Set<MapWaypoint>> trackSets = new ArrayList<>();
|
||||||
for (List<Waypoint> t : waypointsAndTracks.getValue()) {
|
for (List<Waypoint> t : geoDataSet.getTracks()) {
|
||||||
trackSets.add(MapWaypoint.getWaypoints(t));
|
trackSets.add(MapWaypoint.getWaypoints(t));
|
||||||
}
|
}
|
||||||
|
final List<Set<MapWaypoint>> areaSets = new ArrayList<>();
|
||||||
|
for (List<Waypoint> t : geoDataSet.getAreas()) {
|
||||||
|
areaSets.add(MapWaypoint.getWaypoints(t));
|
||||||
|
}
|
||||||
|
|
||||||
handleFilteredWaypointSet(
|
handleFilteredWaypointSet(
|
||||||
pointSet, trackSets,
|
pointSet, trackSets, areaSets,
|
||||||
(trackResults == null || trackResults.isSuccessfullyParsed()) && waypointResults.isSuccessfullyParsed());
|
(trackResults == null || trackResults.isSuccessfullyParsed())
|
||||||
|
&& (areaResults == null || areaResults.isSuccessfullyParsed())
|
||||||
|
&& waypointResults.isSuccessfullyParsed());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a complete list of waypoints including the tracks. Takes into
|
* Returns a complete list of waypoints including the tracks and areas. Takes into
|
||||||
* account the current filters and includes waypoints as approprate.
|
* account the current filters and includes waypoints as approprate.
|
||||||
*
|
*
|
||||||
* @param waypoints List of waypoints
|
* @param waypoints List of waypoints
|
||||||
* @param tracks List of tracks
|
* @param tracks List of tracks
|
||||||
|
* @param areas List of areas
|
||||||
*
|
*
|
||||||
* @return A list of waypoints including the tracks based on the current
|
* @return A GeoDataSet object containing a list of waypoints including the tracks and areas based on the current
|
||||||
* filters.
|
filters.
|
||||||
*/
|
*/
|
||||||
private Pair<List<Waypoint>, List<List<Waypoint>>> createWaypointList(List<Waypoint> waypoints, List<Track> tracks) {
|
private GeoDataSet createWaypointList(List<Waypoint> waypoints, List<Track> tracks, List<Area> areas) {
|
||||||
final List<Waypoint> completeList = new ArrayList<>();
|
final List<Waypoint> completeList = new ArrayList<>();
|
||||||
List<List<Waypoint>> filteredTracks = new ArrayList<>();
|
List<List<Waypoint>> filteredTracks = new ArrayList<>();
|
||||||
|
List<List<Waypoint>> filteredAreas = new ArrayList<>();
|
||||||
|
|
||||||
if (tracks != null) {
|
if (tracks != null) {
|
||||||
Long timeRangeEnd;
|
Long timeRangeEnd;
|
||||||
@ -149,7 +167,14 @@ abstract class AbstractWaypointFetcher implements WaypointBuilder.WaypointFilter
|
|||||||
} else {
|
} else {
|
||||||
completeList.addAll(waypoints);
|
completeList.addAll(waypoints);
|
||||||
}
|
}
|
||||||
return new Pair<>(completeList, filteredTracks);
|
|
||||||
|
// Areas don't have timestamps so add all of them
|
||||||
|
for (Area area : areas) {
|
||||||
|
completeList.addAll(area.getPath());
|
||||||
|
filteredAreas.add(area.getPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
return new GeoDataSet(completeList, filteredTracks, filteredAreas);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -270,4 +295,31 @@ abstract class AbstractWaypointFetcher implements WaypointBuilder.WaypointFilter
|
|||||||
|
|
||||||
return -1L;
|
return -1L;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class to collect filtered GPS objects.
|
||||||
|
*/
|
||||||
|
static class GeoDataSet {
|
||||||
|
private final List<Waypoint> waypoints;
|
||||||
|
private final List<List<Waypoint>> tracks;
|
||||||
|
private final List<List<Waypoint>> areas;
|
||||||
|
|
||||||
|
GeoDataSet(List<Waypoint> waypoints, List<List<Waypoint>> tracks, List<List<Waypoint>> areas) {
|
||||||
|
this.waypoints = waypoints;
|
||||||
|
this.tracks = tracks;
|
||||||
|
this.areas = areas;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Waypoint> getWaypoints() {
|
||||||
|
return waypoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<List<Waypoint>> getTracks() {
|
||||||
|
return tracks;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<List<Waypoint>> getAreas() {
|
||||||
|
return areas;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,7 +71,8 @@ class GeoFilterPanel extends javax.swing.JPanel {
|
|||||||
ARTIFACT_TYPE.TSK_GPS_SEARCH,
|
ARTIFACT_TYPE.TSK_GPS_SEARCH,
|
||||||
ARTIFACT_TYPE.TSK_GPS_TRACK,
|
ARTIFACT_TYPE.TSK_GPS_TRACK,
|
||||||
ARTIFACT_TYPE.TSK_GPS_TRACKPOINT,
|
ARTIFACT_TYPE.TSK_GPS_TRACKPOINT,
|
||||||
ARTIFACT_TYPE.TSK_METADATA_EXIF
|
ARTIFACT_TYPE.TSK_METADATA_EXIF,
|
||||||
|
ARTIFACT_TYPE.TSK_GPS_AREA
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -523,6 +524,7 @@ class GeoFilterPanel extends javax.swing.JPanel {
|
|||||||
+ " or attrs.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE.getTypeID()
|
+ " or attrs.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE.getTypeID()
|
||||||
+ " or attrs.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_TRACKPOINTS.getTypeID()
|
+ " or attrs.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_TRACKPOINTS.getTypeID()
|
||||||
+ " or attrs.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_WAYPOINTS.getTypeID()
|
+ " or attrs.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_WAYPOINTS.getTypeID()
|
||||||
|
+ " or attrs.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_AREAPOINTS.getTypeID()
|
||||||
+ " )"
|
+ " )"
|
||||||
+ " ) as innerTable";
|
+ " ) as innerTable";
|
||||||
try (SleuthkitCase.CaseDbQuery queryResult = sleuthkitCase.executeQuery(queryStr);
|
try (SleuthkitCase.CaseDbQuery queryResult = sleuthkitCase.executeQuery(queryStr);
|
||||||
|
@ -115,7 +115,8 @@ public final class GeolocationTopComponent extends TopComponent {
|
|||||||
|| eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_ROUTE.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_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())) {
|
|| eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACK.getTypeID()
|
||||||
|
|| eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_AREA.getTypeID())) {
|
||||||
|
|
||||||
showRefreshPanel(true);
|
showRefreshPanel(true);
|
||||||
}
|
}
|
||||||
@ -330,7 +331,7 @@ public final class GeolocationTopComponent extends TopComponent {
|
|||||||
*
|
*
|
||||||
* @param waypointList
|
* @param waypointList
|
||||||
*/
|
*/
|
||||||
void addWaypointsToMap(Set<MapWaypoint> waypointList, List<Set<MapWaypoint>> tracks) {
|
void addWaypointsToMap(Set<MapWaypoint> waypointList, List<Set<MapWaypoint>> tracks, List<Set<MapWaypoint>> areas) {
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
@ -348,6 +349,7 @@ public final class GeolocationTopComponent extends TopComponent {
|
|||||||
mapPanel.clearWaypoints();
|
mapPanel.clearWaypoints();
|
||||||
mapPanel.setWaypoints(waypointList);
|
mapPanel.setWaypoints(waypointList);
|
||||||
mapPanel.setTracks(tracks);
|
mapPanel.setTracks(tracks);
|
||||||
|
mapPanel.setAreas(areas);
|
||||||
mapPanel.initializePainter();
|
mapPanel.initializePainter();
|
||||||
setWaypointLoading(false);
|
setWaypointLoading(false);
|
||||||
geoFilterPanel.setEnabled(true);
|
geoFilterPanel.setEnabled(true);
|
||||||
@ -505,8 +507,9 @@ public final class GeolocationTopComponent extends TopComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void handleFilteredWaypointSet(Set<MapWaypoint> mapWaypoints, List<Set<MapWaypoint>> tracks, boolean wasEntirelySuccessful) {
|
void handleFilteredWaypointSet(Set<MapWaypoint> mapWaypoints, List<Set<MapWaypoint>> tracks,
|
||||||
addWaypointsToMap(mapWaypoints, tracks);
|
List<Set<MapWaypoint>> areas, boolean wasEntirelySuccessful) {
|
||||||
|
addWaypointsToMap(mapWaypoints, tracks, areas);
|
||||||
|
|
||||||
// if there is an error, present to the user.
|
// if there is an error, present to the user.
|
||||||
if (!wasEntirelySuccessful) {
|
if (!wasEntirelySuccessful) {
|
||||||
|
@ -23,6 +23,7 @@ import java.awt.BasicStroke;
|
|||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.Paint;
|
||||||
import java.awt.Point;
|
import java.awt.Point;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
import java.awt.RenderingHints;
|
import java.awt.RenderingHints;
|
||||||
@ -30,6 +31,7 @@ import java.awt.event.ActionEvent;
|
|||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
import java.awt.event.ComponentAdapter;
|
import java.awt.event.ComponentAdapter;
|
||||||
import java.awt.event.ComponentEvent;
|
import java.awt.event.ComponentEvent;
|
||||||
|
import java.awt.geom.GeneralPath;
|
||||||
import java.awt.geom.Point2D;
|
import java.awt.geom.Point2D;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
@ -91,11 +93,14 @@ final public class MapPanel extends javax.swing.JPanel {
|
|||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
private static final Set<Integer> DOT_WAYPOINT_TYPES = new HashSet<>();
|
private static final Set<Integer> DOT_WAYPOINT_TYPES = new HashSet<>();
|
||||||
private static final int DOT_SIZE = 12;
|
private static final int DOT_SIZE = 12;
|
||||||
|
private static final Set<Integer> VERY_SMALL_DOT_WAYPOINT_TYPES = new HashSet<>();
|
||||||
|
private static final int VERY_SMALL_DOT_SIZE = 6;
|
||||||
|
|
||||||
private boolean zoomChanging;
|
private boolean zoomChanging;
|
||||||
private KdTree<MapWaypoint> waypointTree;
|
private KdTree<MapWaypoint> waypointTree;
|
||||||
private Set<MapWaypoint> waypointSet;
|
private Set<MapWaypoint> waypointSet;
|
||||||
private List<Set<MapWaypoint>> tracks = new ArrayList<>();
|
private List<Set<MapWaypoint>> tracks = new ArrayList<>();
|
||||||
|
private List<Set<MapWaypoint>> areas = new ArrayList<>();
|
||||||
|
|
||||||
private Popup currentPopup;
|
private Popup currentPopup;
|
||||||
private final PopupFactory popupFactory;
|
private final PopupFactory popupFactory;
|
||||||
@ -108,12 +113,13 @@ final public class MapPanel extends javax.swing.JPanel {
|
|||||||
private BufferedImage transparentWaypointImage;
|
private BufferedImage transparentWaypointImage;
|
||||||
|
|
||||||
private MapWaypoint currentlySelectedWaypoint;
|
private MapWaypoint currentlySelectedWaypoint;
|
||||||
private Set<MapWaypoint> currentlySelectedTrack;
|
private Set<MapWaypoint> currentlySelectedSet;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
DOT_WAYPOINT_TYPES.add(ARTIFACT_TYPE.TSK_GPS_TRACKPOINT.getTypeID());
|
DOT_WAYPOINT_TYPES.add(ARTIFACT_TYPE.TSK_GPS_TRACKPOINT.getTypeID());
|
||||||
DOT_WAYPOINT_TYPES.add(ARTIFACT_TYPE.TSK_GPS_TRACK.getTypeID());
|
DOT_WAYPOINT_TYPES.add(ARTIFACT_TYPE.TSK_GPS_TRACK.getTypeID());
|
||||||
DOT_WAYPOINT_TYPES.add(ARTIFACT_TYPE.TSK_GPS_ROUTE.getTypeID());
|
DOT_WAYPOINT_TYPES.add(ARTIFACT_TYPE.TSK_GPS_ROUTE.getTypeID());
|
||||||
|
VERY_SMALL_DOT_WAYPOINT_TYPES.add(ARTIFACT_TYPE.TSK_GPS_AREA.getTypeID());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -241,6 +247,7 @@ final public class MapPanel extends javax.swing.JPanel {
|
|||||||
waypointPainter.setRenderer(new MapWaypointRenderer());
|
waypointPainter.setRenderer(new MapWaypointRenderer());
|
||||||
|
|
||||||
ArrayList<Painter<JXMapViewer>> painters = new ArrayList<>();
|
ArrayList<Painter<JXMapViewer>> painters = new ArrayList<>();
|
||||||
|
painters.add(new MapAreaRenderer(areas));
|
||||||
painters.add(new MapTrackRenderer(tracks));
|
painters.add(new MapTrackRenderer(tracks));
|
||||||
painters.add(waypointPainter);
|
painters.add(waypointPainter);
|
||||||
|
|
||||||
@ -342,6 +349,15 @@ final public class MapPanel extends javax.swing.JPanel {
|
|||||||
void setTracks(List<Set<MapWaypoint>> tracks) {
|
void setTracks(List<Set<MapWaypoint>> tracks) {
|
||||||
this.tracks = tracks;
|
this.tracks = tracks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores the given list of areas from which to draw paths later
|
||||||
|
*
|
||||||
|
* @param areas
|
||||||
|
*/
|
||||||
|
void setAreas(List<Set<MapWaypoint>> areas) {
|
||||||
|
this.areas = areas;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the current zoom level.
|
* Set the current zoom level.
|
||||||
@ -361,7 +377,7 @@ final public class MapPanel extends javax.swing.JPanel {
|
|||||||
void clearWaypoints() {
|
void clearWaypoints() {
|
||||||
waypointTree = null;
|
waypointTree = null;
|
||||||
currentlySelectedWaypoint = null;
|
currentlySelectedWaypoint = null;
|
||||||
currentlySelectedTrack = null;
|
currentlySelectedSet = null;
|
||||||
if (currentPopup != null) {
|
if (currentPopup != null) {
|
||||||
currentPopup.hide();
|
currentPopup.hide();
|
||||||
}
|
}
|
||||||
@ -514,6 +530,13 @@ final public class MapPanel extends javax.swing.JPanel {
|
|||||||
DOT_SIZE,
|
DOT_SIZE,
|
||||||
DOT_SIZE
|
DOT_SIZE
|
||||||
);
|
);
|
||||||
|
} else if (VERY_SMALL_DOT_WAYPOINT_TYPES.contains(nextWaypoint.getArtifactTypeID())) {
|
||||||
|
rect = new Rectangle(
|
||||||
|
pointX - (VERY_SMALL_DOT_SIZE / 2),
|
||||||
|
pointY - (VERY_SMALL_DOT_SIZE / 2),
|
||||||
|
VERY_SMALL_DOT_SIZE,
|
||||||
|
VERY_SMALL_DOT_SIZE
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
rect = new Rectangle(
|
rect = new Rectangle(
|
||||||
pointX - (whiteWaypointImage.getWidth() / 2),
|
pointX - (whiteWaypointImage.getWidth() / 2),
|
||||||
@ -717,16 +740,24 @@ final public class MapPanel extends javax.swing.JPanel {
|
|||||||
if (waypoints.size() > 0) {
|
if (waypoints.size() > 0) {
|
||||||
MapWaypoint selection = waypoints.get(0);
|
MapWaypoint selection = waypoints.get(0);
|
||||||
currentlySelectedWaypoint = selection;
|
currentlySelectedWaypoint = selection;
|
||||||
currentlySelectedTrack = null;
|
currentlySelectedSet = null;
|
||||||
for (Set<MapWaypoint> track : tracks) {
|
for (Set<MapWaypoint> track : tracks) {
|
||||||
if (track.contains(selection)) {
|
if (track.contains(selection)) {
|
||||||
currentlySelectedTrack = track;
|
currentlySelectedSet = track;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (currentlySelectedSet == null) {
|
||||||
|
for (Set<MapWaypoint> area : areas) {
|
||||||
|
if (area.contains(selection)) {
|
||||||
|
currentlySelectedSet = area;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
currentlySelectedWaypoint = null;
|
currentlySelectedWaypoint = null;
|
||||||
currentlySelectedTrack = null;
|
currentlySelectedSet = null;
|
||||||
}
|
}
|
||||||
showDetailsPopup();
|
showDetailsPopup();
|
||||||
}
|
}
|
||||||
@ -755,6 +786,7 @@ final public class MapPanel extends javax.swing.JPanel {
|
|||||||
private class MapWaypointRenderer implements WaypointRenderer<MapWaypoint> {
|
private class MapWaypointRenderer implements WaypointRenderer<MapWaypoint> {
|
||||||
|
|
||||||
private final Map<Color, BufferedImage> dotImageCache = new HashMap<>();
|
private final Map<Color, BufferedImage> dotImageCache = new HashMap<>();
|
||||||
|
private final Map<Color, BufferedImage> verySmallDotImageCache = new HashMap<>();
|
||||||
private final Map<Color, BufferedImage> waypointImageCache = new HashMap<>();
|
private final Map<Color, BufferedImage> waypointImageCache = new HashMap<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -766,7 +798,7 @@ final public class MapPanel extends javax.swing.JPanel {
|
|||||||
private Color getColor(MapWaypoint waypoint) {
|
private Color getColor(MapWaypoint waypoint) {
|
||||||
Color baseColor = waypoint.getColor();
|
Color baseColor = waypoint.getColor();
|
||||||
if (waypoint.equals(currentlySelectedWaypoint)
|
if (waypoint.equals(currentlySelectedWaypoint)
|
||||||
|| (currentlySelectedTrack != null && currentlySelectedTrack.contains(waypoint))) {
|
|| (currentlySelectedSet != null && currentlySelectedSet.contains(waypoint))) {
|
||||||
// Highlight this waypoint since it is selected
|
// Highlight this waypoint since it is selected
|
||||||
return Color.YELLOW;
|
return Color.YELLOW;
|
||||||
} else {
|
} else {
|
||||||
@ -778,11 +810,11 @@ final public class MapPanel extends javax.swing.JPanel {
|
|||||||
* Creates a dot image with the specified color
|
* Creates a dot image with the specified color
|
||||||
*
|
*
|
||||||
* @param color the color of the new image
|
* @param color the color of the new image
|
||||||
|
* @param s the size of the dot
|
||||||
*
|
*
|
||||||
* @return the new dot image
|
* @return the new dot image
|
||||||
*/
|
*/
|
||||||
private BufferedImage createTrackDotImage(Color color) {
|
private BufferedImage createTrackDotImage(Color color, int s) {
|
||||||
int s = DOT_SIZE;
|
|
||||||
|
|
||||||
BufferedImage ret = new BufferedImage(s, s, BufferedImage.TYPE_INT_ARGB);
|
BufferedImage ret = new BufferedImage(s, s, BufferedImage.TYPE_INT_ARGB);
|
||||||
Graphics2D g = ret.createGraphics();
|
Graphics2D g = ret.createGraphics();
|
||||||
@ -831,7 +863,13 @@ final public class MapPanel extends javax.swing.JPanel {
|
|||||||
|
|
||||||
if (DOT_WAYPOINT_TYPES.contains(waypoint.getArtifactTypeID())) {
|
if (DOT_WAYPOINT_TYPES.contains(waypoint.getArtifactTypeID())) {
|
||||||
image = dotImageCache.computeIfAbsent(color, k -> {
|
image = dotImageCache.computeIfAbsent(color, k -> {
|
||||||
return createTrackDotImage(color);
|
return createTrackDotImage(color, DOT_SIZE);
|
||||||
|
});
|
||||||
|
// Center the dot on the GPS coordinate
|
||||||
|
y -= image.getHeight() / 2;
|
||||||
|
} else if (VERY_SMALL_DOT_WAYPOINT_TYPES.contains(waypoint.getArtifactTypeID())) {
|
||||||
|
image = verySmallDotImageCache.computeIfAbsent(color, k -> {
|
||||||
|
return createTrackDotImage(color, VERY_SMALL_DOT_SIZE);
|
||||||
});
|
});
|
||||||
// Center the dot on the GPS coordinate
|
// Center the dot on the GPS coordinate
|
||||||
y -= image.getHeight() / 2;
|
y -= image.getHeight() / 2;
|
||||||
@ -903,4 +941,74 @@ final public class MapPanel extends javax.swing.JPanel {
|
|||||||
g2d.dispose();
|
g2d.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renderer for map areas
|
||||||
|
*/
|
||||||
|
private class MapAreaRenderer implements Painter<JXMapViewer> {
|
||||||
|
|
||||||
|
private final List<Set<MapWaypoint>> areas;
|
||||||
|
|
||||||
|
MapAreaRenderer(List<Set<MapWaypoint>> areas) {
|
||||||
|
this.areas = areas;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shade in the area on the map.
|
||||||
|
*
|
||||||
|
* @param area The waypoints defining the outline of the area.
|
||||||
|
* @param g Graphics2D
|
||||||
|
* @param map JXMapViewer
|
||||||
|
*/
|
||||||
|
private void drawArea(Set<MapWaypoint> area, Graphics2D g, JXMapViewer map) {
|
||||||
|
if (area.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
boolean first = true;
|
||||||
|
|
||||||
|
GeneralPath polygon = new GeneralPath(GeneralPath.WIND_EVEN_ODD, area.size());
|
||||||
|
|
||||||
|
for (MapWaypoint wp : area) {
|
||||||
|
Point2D p = map.getTileFactory().geoToPixel(wp.getPosition(), map.getZoom());
|
||||||
|
int thisX = (int) p.getX();
|
||||||
|
int thisY = (int) p.getY();
|
||||||
|
|
||||||
|
if (first) {
|
||||||
|
polygon.moveTo(thisX, thisY);
|
||||||
|
first = false;
|
||||||
|
} else {
|
||||||
|
polygon.lineTo(thisX, thisY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
polygon.closePath();
|
||||||
|
|
||||||
|
Color areaColor = area.iterator().next().getColor();
|
||||||
|
final double maxColorValue = 255.0;
|
||||||
|
g.setPaint(new Color((float)(areaColor.getRed() / maxColorValue),
|
||||||
|
(float)(areaColor.getGreen() / maxColorValue),
|
||||||
|
(float)(areaColor.getBlue() / maxColorValue),
|
||||||
|
.2f));
|
||||||
|
g.fill(polygon);
|
||||||
|
g.draw(polygon);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void paint(Graphics2D g, JXMapViewer map, int w, int h) {
|
||||||
|
Graphics2D g2d = (Graphics2D) g.create();
|
||||||
|
|
||||||
|
Rectangle bounds = map.getViewportBounds();
|
||||||
|
g2d.translate(-bounds.x, -bounds.y);
|
||||||
|
|
||||||
|
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||||
|
|
||||||
|
g2d.setColor(Color.BLACK);
|
||||||
|
g2d.setStroke(new BasicStroke(2));
|
||||||
|
|
||||||
|
for (Set<MapWaypoint> area : areas) {
|
||||||
|
drawArea(area, g2d, map);
|
||||||
|
}
|
||||||
|
|
||||||
|
g2d.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,6 +79,7 @@ final class MapWaypoint extends KdTree.XYZPoint implements org.jxmapviewer.viewe
|
|||||||
artifactTypesToColors.put(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACK.getTypeID(), Color.ORANGE);
|
artifactTypesToColors.put(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACK.getTypeID(), Color.ORANGE);
|
||||||
artifactTypesToColors.put(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACKPOINT.getTypeID(), Color.ORANGE);
|
artifactTypesToColors.put(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACKPOINT.getTypeID(), Color.ORANGE);
|
||||||
artifactTypesToColors.put(BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF.getTypeID(), Color.MAGENTA);
|
artifactTypesToColors.put(BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF.getTypeID(), Color.MAGENTA);
|
||||||
|
artifactTypesToColors.put(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_AREA.getTypeID(), new Color(0x8a2be2)); // Blue violet
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Waypoint dataModelWaypoint;
|
private final Waypoint dataModelWaypoint;
|
||||||
|
171
Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Area.java
Normal file
171
Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Area.java
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
/*
|
||||||
|
* 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.openide.util.NbBundle.Messages;
|
||||||
|
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||||
|
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||||
|
import org.sleuthkit.datamodel.blackboardutils.attributes.BlackboardJsonAttrUtil;
|
||||||
|
import org.sleuthkit.datamodel.blackboardutils.attributes.BlackboardJsonAttrUtil.InvalidJsonException;
|
||||||
|
import org.sleuthkit.datamodel.blackboardutils.attributes.GeoAreaPoints;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A GPS track with which wraps the TSK_GPS_AREA artifact.
|
||||||
|
*/
|
||||||
|
public final class Area extends GeoPath {
|
||||||
|
/**
|
||||||
|
* Construct a new Area for the given artifact.
|
||||||
|
*
|
||||||
|
* @param artifact
|
||||||
|
*
|
||||||
|
* @throws GeoLocationDataException
|
||||||
|
*/
|
||||||
|
public Area(BlackboardArtifact artifact) throws GeoLocationDataException {
|
||||||
|
this(artifact, Waypoint.getAttributesFromArtifactAsMap(artifact));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct an Area for the given artifact and attributeMap.
|
||||||
|
*
|
||||||
|
* @param artifact TSK_GPD_TRACK artifact
|
||||||
|
* @param attributeMap Map of the artifact attributes
|
||||||
|
*
|
||||||
|
* @throws GeoLocationDataException
|
||||||
|
*/
|
||||||
|
private Area(BlackboardArtifact artifact, Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> attributeMap) throws GeoLocationDataException {
|
||||||
|
super(artifact, getAreaName(attributeMap));
|
||||||
|
|
||||||
|
GeoAreaPoints points = getPointsList(attributeMap);
|
||||||
|
buildPath(points, artifact);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the name of the area from the attributeMap. Track name is stored
|
||||||
|
* in the attribute TSK_NAME
|
||||||
|
*
|
||||||
|
* @param attributeMap
|
||||||
|
*
|
||||||
|
* @return Area name or empty string if none was available.
|
||||||
|
*/
|
||||||
|
private static String getAreaName(Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> attributeMap) {
|
||||||
|
BlackboardAttribute attribute = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME);
|
||||||
|
|
||||||
|
return attribute != null ? attribute.getValueString() : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the list of AreaWaypoints from the GeoTrackPoint list.
|
||||||
|
*
|
||||||
|
* @param points GeoAreaPoints object.
|
||||||
|
* @param artifact The artifact to which these points belong
|
||||||
|
*
|
||||||
|
* @throws GeoLocationDataException
|
||||||
|
*/
|
||||||
|
@Messages({
|
||||||
|
"# {0} - area name",
|
||||||
|
"GEOArea_point_label_header=Area outline point for area: {0}"
|
||||||
|
})
|
||||||
|
private void buildPath(GeoAreaPoints points, BlackboardArtifact artifact) throws GeoLocationDataException {
|
||||||
|
for (GeoAreaPoints.AreaPoint point : points) {
|
||||||
|
addToPath(new AreaWaypoint(artifact, Bundle.GEOArea_point_label_header(getLabel()), point));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of GeoAreaPoints from the attributeMap. Creates the
|
||||||
|
* GeoAreaPoint list from the TSK_GEO_AREAPOINTS attribute.
|
||||||
|
*
|
||||||
|
* @param attributeMap Map of artifact attributes.
|
||||||
|
*
|
||||||
|
* @return GeoTrackPoint list empty list if the attribute was not found.
|
||||||
|
*
|
||||||
|
* @throws GeoLocationDataException
|
||||||
|
*/
|
||||||
|
private GeoAreaPoints getPointsList(Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> attributeMap) throws GeoLocationDataException {
|
||||||
|
BlackboardAttribute attribute = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_AREAPOINTS);
|
||||||
|
if (attribute == null) {
|
||||||
|
throw new GeoLocationDataException("No TSK_GEO_AREAPOINTS attribute present in attribute map to parse.");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return BlackboardJsonAttrUtil.fromAttribute(attribute, GeoAreaPoints.class);
|
||||||
|
} catch (InvalidJsonException ex) {
|
||||||
|
throw new GeoLocationDataException("Unable to parse area points in TSK_GEO_AREAPOINTS attribute", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Waypoint subclass for the points of an area outline.
|
||||||
|
*/
|
||||||
|
final class AreaWaypoint extends Waypoint {
|
||||||
|
|
||||||
|
private final List<Waypoint.Property> propertyList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a AreaWaypoint.
|
||||||
|
*
|
||||||
|
* @param artifact the artifact to which this waypoint belongs
|
||||||
|
*
|
||||||
|
* @param pointLabel the label for the waypoint
|
||||||
|
*
|
||||||
|
* @param point GeoAreaPoint
|
||||||
|
*
|
||||||
|
* @throws GeoLocationDataException
|
||||||
|
*/
|
||||||
|
AreaWaypoint(BlackboardArtifact artifact, String pointLabel, GeoAreaPoints.AreaPoint point) throws GeoLocationDataException {
|
||||||
|
super(artifact, pointLabel,
|
||||||
|
null,
|
||||||
|
point.getLatitude(),
|
||||||
|
point.getLongitude(),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
Area.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 GeoAreaPoints.
|
||||||
|
*
|
||||||
|
* @param point GeoAreaPoint to get values from.
|
||||||
|
*
|
||||||
|
* @return A list of Waypoint.properies.
|
||||||
|
*/
|
||||||
|
private List<Waypoint.Property> createPropertyList(GeoAreaPoints.AreaPoint point) {
|
||||||
|
List<Waypoint.Property> list = new ArrayList<>();
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,5 @@
|
|||||||
|
# {0} - area name
|
||||||
|
GEOArea_point_label_header=Area outline point for area: {0}
|
||||||
# {0} - track name
|
# {0} - track name
|
||||||
GEOTrack_point_label_header=Trackpoint for track: {0}
|
GEOTrack_point_label_header=Trackpoint for track: {0}
|
||||||
LastKnownWaypoint_Label=Last Known Location
|
LastKnownWaypoint_Label=Last Known Location
|
||||||
|
@ -22,6 +22,8 @@ package org.sleuthkit.autopsy.geolocation.datamodel;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||||
import org.sleuthkit.datamodel.Content;
|
import org.sleuthkit.datamodel.Content;
|
||||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||||
@ -31,6 +33,8 @@ import org.sleuthkit.datamodel.TskCoreException;
|
|||||||
* Class representing a series of waypoints that form a path.
|
* Class representing a series of waypoints that form a path.
|
||||||
*/
|
*/
|
||||||
public class GeoPath {
|
public class GeoPath {
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(GeoPath.class.getName());
|
||||||
|
|
||||||
private final List<Waypoint> waypointList;
|
private final List<Waypoint> waypointList;
|
||||||
private final String pathName;
|
private final String pathName;
|
||||||
private final BlackboardArtifact artifact;
|
private final BlackboardArtifact artifact;
|
||||||
@ -62,13 +66,13 @@ public class GeoPath {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the list of Routes from the TSK_GPS_TRACK artifacts.
|
* Gets the list of Tracks from the TSK_GPS_TRACK artifacts.
|
||||||
*
|
*
|
||||||
* @param skCase Currently open SleuthkitCase
|
* @param skCase Currently open SleuthkitCase
|
||||||
* @param sourceList List of source to return tracks from, maybe null to
|
* @param sourceList List of source to return tracks from, maybe null to
|
||||||
* return tracks from all sources
|
* return tracks from all sources
|
||||||
*
|
*
|
||||||
* @return List of Route objects, empty list will be returned if no Routes
|
* @return List of Track objects, empty list will be returned if no tracks
|
||||||
* were found
|
* were found
|
||||||
*
|
*
|
||||||
* @throws GeoLocationDataException
|
* @throws GeoLocationDataException
|
||||||
@ -85,6 +89,7 @@ public class GeoPath {
|
|||||||
tracks.add(new Track(artifact));
|
tracks.add(new Track(artifact));
|
||||||
|
|
||||||
} catch (GeoLocationDataException e) {
|
} catch (GeoLocationDataException e) {
|
||||||
|
LOGGER.log(Level.WARNING, "Error loading track from artifact with ID " + artifact.getArtifactID(), e);
|
||||||
allParsedSuccessfully = false;
|
allParsedSuccessfully = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -94,6 +99,41 @@ public class GeoPath {
|
|||||||
}
|
}
|
||||||
return new GeoLocationParseResult<Track>(tracks, allParsedSuccessfully);
|
return new GeoLocationParseResult<Track>(tracks, allParsedSuccessfully);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the list of Areas from the TSK_GPS_AREA artifacts.
|
||||||
|
*
|
||||||
|
* @param skCase Currently open SleuthkitCase
|
||||||
|
* @param sourceList List of source to return areas from, may be null to
|
||||||
|
* return areas from all sources
|
||||||
|
*
|
||||||
|
* @return List of Area objects, empty list will be returned if no areas
|
||||||
|
* were found
|
||||||
|
*
|
||||||
|
* @throws GeoLocationDataException
|
||||||
|
*/
|
||||||
|
public static GeoLocationParseResult<Area> getAreas(SleuthkitCase skCase, List<? extends Content> sourceList) throws GeoLocationDataException {
|
||||||
|
List<BlackboardArtifact> artifacts;
|
||||||
|
boolean allParsedSuccessfully = true;
|
||||||
|
List<Area> areas = new ArrayList<>();
|
||||||
|
try {
|
||||||
|
artifacts = skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_AREA);
|
||||||
|
for (BlackboardArtifact artifact : artifacts) {
|
||||||
|
if (sourceList == null || sourceList.contains(artifact.getDataSource())) {
|
||||||
|
try {
|
||||||
|
areas.add(new Area(artifact));
|
||||||
|
|
||||||
|
} catch (GeoLocationDataException e) {
|
||||||
|
LOGGER.log(Level.WARNING, "Error loading track from artifact with ID " + artifact.getArtifactID(), e);
|
||||||
|
allParsedSuccessfully = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (TskCoreException ex) {
|
||||||
|
throw new GeoLocationDataException("Unable to get artifacts for type: TSK_GPS_BOOKMARK", ex);
|
||||||
|
}
|
||||||
|
return new GeoLocationParseResult<>(areas, allParsedSuccessfully);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Path constructor.
|
* Path constructor.
|
||||||
|
@ -22,20 +22,17 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.logging.Level;
|
|
||||||
import org.openide.util.NbBundle.Messages;
|
import org.openide.util.NbBundle.Messages;
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||||
import org.sleuthkit.datamodel.blackboardutils.attributes.BlackboardJsonAttrUtil;
|
import org.sleuthkit.datamodel.blackboardutils.attributes.BlackboardJsonAttrUtil;
|
||||||
import org.sleuthkit.datamodel.blackboardutils.attributes.BlackboardJsonAttrUtil.InvalidJsonException;
|
import org.sleuthkit.datamodel.blackboardutils.attributes.BlackboardJsonAttrUtil.InvalidJsonException;
|
||||||
import org.sleuthkit.datamodel.blackboardutils.attributes.GeoTrackPoints;
|
import org.sleuthkit.datamodel.blackboardutils.attributes.GeoTrackPoints;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A GPS track with which wraps the TSK_GPS_TRACK artifact.
|
* A GPS track with which wraps the TSK_GPS_TRACK artifact.
|
||||||
*/
|
*/
|
||||||
public final class Track extends GeoPath {
|
public final class Track extends GeoPath {
|
||||||
private static final Logger LOGGER = Logger.getLogger(Track.class.getName());
|
|
||||||
|
|
||||||
private final Long startTimestamp;
|
private final Long startTimestamp;
|
||||||
private final Long endTimeStamp;
|
private final Long endTimeStamp;
|
||||||
@ -134,14 +131,12 @@ public final class Track extends GeoPath {
|
|||||||
private GeoTrackPoints getPointsList(Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> attributeMap) throws GeoLocationDataException {
|
private GeoTrackPoints getPointsList(Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> attributeMap) throws GeoLocationDataException {
|
||||||
BlackboardAttribute attribute = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_TRACKPOINTS);
|
BlackboardAttribute attribute = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_TRACKPOINTS);
|
||||||
if (attribute == null) {
|
if (attribute == null) {
|
||||||
LOGGER.log(Level.SEVERE, "No TSK_GEO_TRACKPOINTS attribute was present on the artifact.");
|
|
||||||
throw new GeoLocationDataException("No TSK_GEO_TRACKPOINTS attribute present in attribute map to parse.");
|
throw new GeoLocationDataException("No TSK_GEO_TRACKPOINTS attribute present in attribute map to parse.");
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return BlackboardJsonAttrUtil.fromAttribute(attribute, GeoTrackPoints.class);
|
return BlackboardJsonAttrUtil.fromAttribute(attribute, GeoTrackPoints.class);
|
||||||
} catch (InvalidJsonException ex) {
|
} catch (InvalidJsonException ex) {
|
||||||
LOGGER.log(Level.SEVERE, "TSK_GEO_TRACKPOINTS could not be properly parsed from TSK_GEO_TRACKPOINTS attribute.");
|
|
||||||
throw new GeoLocationDataException("Unable to parse track points in TSK_GEO_TRACKPOINTS attribute", ex);
|
throw new GeoLocationDataException("Unable to parse track points in TSK_GEO_TRACKPOINTS attribute", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -178,6 +178,28 @@ public final class WaypointBuilder {
|
|||||||
|
|
||||||
return trackList;
|
return trackList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of areas from the given list of waypoints.
|
||||||
|
*
|
||||||
|
* @param waypoints A list of waypoints
|
||||||
|
*
|
||||||
|
* @return A list of areas or an empty list if none were found.
|
||||||
|
*/
|
||||||
|
public static List<Area> getAreas(List<Waypoint> waypoints) {
|
||||||
|
List<Area> areaList = new ArrayList<>();
|
||||||
|
for (Waypoint point : waypoints) {
|
||||||
|
GeoPath path = point.getParentGeoPath();
|
||||||
|
if (path instanceof Area) {
|
||||||
|
Area area = (Area) path;
|
||||||
|
if (!areaList.contains(area)) {
|
||||||
|
areaList.add(area);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return areaList;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a list of Waypoints for TSK_GPS_TRACKPOINT artifacts.
|
* Gets a list of Waypoints for TSK_GPS_TRACKPOINT artifacts.
|
||||||
|
@ -7,6 +7,10 @@ HealthMonitorDashboard.createTimingControlPanel.skipOutliers=Do not plot outlier
|
|||||||
HealthMonitorDashboard.createTimingPanel.noData=No data to display - monitor is not enabled
|
HealthMonitorDashboard.createTimingPanel.noData=No data to display - monitor is not enabled
|
||||||
HealthMonitorDashboard.createTimingPanel.timingMetricsTitle=Timing Metrics
|
HealthMonitorDashboard.createTimingPanel.timingMetricsTitle=Timing Metrics
|
||||||
HealthMonitorDashboard.createUserControlPanel.maxDays=Max days to display
|
HealthMonitorDashboard.createUserControlPanel.maxDays=Max days to display
|
||||||
|
# {0} - Report file name
|
||||||
|
HealthMonitorDashboard.createUserControlPanel.reportDone=Report saved to: {0}
|
||||||
|
HealthMonitorDashboard.createUserControlPanel.reportError=Error generating report
|
||||||
|
HealthMonitorDashboard.createUserControlPanel.userReportButton=Generate Report
|
||||||
HealthMonitorDashboard.createUserPanel.noData=No data to display - monitor is not enabled
|
HealthMonitorDashboard.createUserPanel.noData=No data to display - monitor is not enabled
|
||||||
HealthMonitorDashboard.createUserPanel.userMetricsTitle=User Metrics
|
HealthMonitorDashboard.createUserPanel.userMetricsTitle=User Metrics
|
||||||
HealthMonitorDashboard.DateRange.oneDay=One day
|
HealthMonitorDashboard.DateRange.oneDay=One day
|
||||||
|
@ -65,7 +65,7 @@ public final class HealthMonitor implements PropertyChangeListener {
|
|||||||
private final static Logger logger = Logger.getLogger(HealthMonitor.class.getName());
|
private final static Logger logger = Logger.getLogger(HealthMonitor.class.getName());
|
||||||
private final static String DATABASE_NAME = "HealthMonitor";
|
private final static String DATABASE_NAME = "HealthMonitor";
|
||||||
private final static long DATABASE_WRITE_INTERVAL = 60; // Minutes
|
private final static long DATABASE_WRITE_INTERVAL = 60; // Minutes
|
||||||
private final static CaseDbSchemaVersionNumber CURRENT_DB_SCHEMA_VERSION = new CaseDbSchemaVersionNumber(1, 1);
|
private final static CaseDbSchemaVersionNumber CURRENT_DB_SCHEMA_VERSION = new CaseDbSchemaVersionNumber(1, 2);
|
||||||
|
|
||||||
private final static AtomicBoolean isEnabled = new AtomicBoolean(false);
|
private final static AtomicBoolean isEnabled = new AtomicBoolean(false);
|
||||||
private static HealthMonitor instance;
|
private static HealthMonitor instance;
|
||||||
@ -77,6 +77,7 @@ public final class HealthMonitor implements PropertyChangeListener {
|
|||||||
private BasicDataSource connectionPool = null;
|
private BasicDataSource connectionPool = null;
|
||||||
private CaseDbConnectionInfo connectionSettingsInUse = null;
|
private CaseDbConnectionInfo connectionSettingsInUse = null;
|
||||||
private String hostName;
|
private String hostName;
|
||||||
|
private final String username;
|
||||||
|
|
||||||
private HealthMonitor() throws HealthMonitorException {
|
private HealthMonitor() throws HealthMonitorException {
|
||||||
|
|
||||||
@ -96,6 +97,9 @@ public final class HealthMonitor implements PropertyChangeListener {
|
|||||||
hostName = UUID.randomUUID().toString();
|
hostName = UUID.randomUUID().toString();
|
||||||
logger.log(Level.SEVERE, "Unable to look up host name - falling back to UUID " + hostName, ex);
|
logger.log(Level.SEVERE, "Unable to look up host name - falling back to UUID " + hostName, ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the user name
|
||||||
|
username = System.getProperty("user.name");
|
||||||
|
|
||||||
// Read from the database to determine if the module is enabled
|
// Read from the database to determine if the module is enabled
|
||||||
updateFromGlobalEnabledStatus();
|
updateFromGlobalEnabledStatus();
|
||||||
@ -197,6 +201,14 @@ public final class HealthMonitor implements PropertyChangeListener {
|
|||||||
+ "case_name text NOT NULL"
|
+ "case_name text NOT NULL"
|
||||||
+ ")");
|
+ ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Upgrade from 1.1 to 1.2
|
||||||
|
// Changes: username added to user_data table
|
||||||
|
if (currentSchema.compareTo(new CaseDbSchemaVersionNumber(1, 2)) < 0) {
|
||||||
|
|
||||||
|
// Add the user_data table
|
||||||
|
statement.execute("ALTER TABLE user_data ADD COLUMN username text");
|
||||||
|
}
|
||||||
|
|
||||||
// Update the schema version
|
// Update the schema version
|
||||||
statement.execute("UPDATE db_info SET value='" + CURRENT_DB_SCHEMA_VERSION.getMajor() + "' WHERE name='SCHEMA_VERSION'");
|
statement.execute("UPDATE db_info SET value='" + CURRENT_DB_SCHEMA_VERSION.getMajor() + "' WHERE name='SCHEMA_VERSION'");
|
||||||
@ -499,7 +511,7 @@ public final class HealthMonitor implements PropertyChangeListener {
|
|||||||
|
|
||||||
// Add metrics to the database
|
// Add metrics to the database
|
||||||
String addTimingInfoSql = "INSERT INTO timing_data (name, host, timestamp, count, average, max, min) VALUES (?, ?, ?, ?, ?, ?, ?)";
|
String addTimingInfoSql = "INSERT INTO timing_data (name, host, timestamp, count, average, max, min) VALUES (?, ?, ?, ?, ?, ?, ?)";
|
||||||
String addUserInfoSql = "INSERT INTO user_data (host, timestamp, event_type, is_examiner, case_name) VALUES (?, ?, ?, ?, ?)";
|
String addUserInfoSql = "INSERT INTO user_data (host, username, timestamp, event_type, is_examiner, case_name) VALUES (?, ?, ?, ?, ?, ?)";
|
||||||
try (PreparedStatement timingStatement = conn.prepareStatement(addTimingInfoSql);
|
try (PreparedStatement timingStatement = conn.prepareStatement(addTimingInfoSql);
|
||||||
PreparedStatement userStatement = conn.prepareStatement(addUserInfoSql)) {
|
PreparedStatement userStatement = conn.prepareStatement(addUserInfoSql)) {
|
||||||
|
|
||||||
@ -519,10 +531,11 @@ public final class HealthMonitor implements PropertyChangeListener {
|
|||||||
|
|
||||||
for (UserData userInfo : userDataCopy) {
|
for (UserData userInfo : userDataCopy) {
|
||||||
userStatement.setString(1, hostName);
|
userStatement.setString(1, hostName);
|
||||||
userStatement.setLong(2, userInfo.getTimestamp());
|
userStatement.setString(2, username);
|
||||||
userStatement.setInt(3, userInfo.getEventType().getEventValue());
|
userStatement.setLong(3, userInfo.getTimestamp());
|
||||||
userStatement.setBoolean(4, userInfo.isExaminerNode());
|
userStatement.setInt(4, userInfo.getEventType().getEventValue());
|
||||||
userStatement.setString(5, userInfo.getCaseName());
|
userStatement.setBoolean(5, userInfo.isExaminerNode());
|
||||||
|
userStatement.setString(6, userInfo.getCaseName());
|
||||||
userStatement.execute();
|
userStatement.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -903,7 +916,8 @@ public final class HealthMonitor implements PropertyChangeListener {
|
|||||||
+ "timestamp bigint NOT NULL,"
|
+ "timestamp bigint NOT NULL,"
|
||||||
+ "event_type int NOT NULL,"
|
+ "event_type int NOT NULL,"
|
||||||
+ "is_examiner BOOLEAN NOT NULL,"
|
+ "is_examiner BOOLEAN NOT NULL,"
|
||||||
+ "case_name text NOT NULL"
|
+ "case_name text NOT NULL,"
|
||||||
|
+ "username text"
|
||||||
+ ")");
|
+ ")");
|
||||||
|
|
||||||
statement.execute("INSERT INTO db_info (name, value) VALUES ('SCHEMA_VERSION', '" + CURRENT_DB_SCHEMA_VERSION.getMajor() + "')");
|
statement.execute("INSERT INTO db_info (name, value) VALUES ('SCHEMA_VERSION', '" + CURRENT_DB_SCHEMA_VERSION.getMajor() + "')");
|
||||||
@ -1340,12 +1354,13 @@ public final class HealthMonitor implements PropertyChangeListener {
|
|||||||
* Class holding user metric data. Can be used for storing new events or
|
* Class holding user metric data. Can be used for storing new events or
|
||||||
* retrieving events out of the database.
|
* retrieving events out of the database.
|
||||||
*/
|
*/
|
||||||
static class UserData {
|
static class UserData implements Comparable<UserData> {
|
||||||
|
|
||||||
private final UserEvent eventType;
|
private final UserEvent eventType;
|
||||||
private long timestamp;
|
private long timestamp;
|
||||||
private final boolean isExaminer;
|
private final boolean isExaminer;
|
||||||
private final String hostname;
|
private final String hostname;
|
||||||
|
private String username;
|
||||||
private String caseName;
|
private String caseName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1359,6 +1374,7 @@ public final class HealthMonitor implements PropertyChangeListener {
|
|||||||
this.timestamp = System.currentTimeMillis();
|
this.timestamp = System.currentTimeMillis();
|
||||||
this.isExaminer = (UserPreferences.SelectedMode.STANDALONE == UserPreferences.getMode());
|
this.isExaminer = (UserPreferences.SelectedMode.STANDALONE == UserPreferences.getMode());
|
||||||
this.hostname = "";
|
this.hostname = "";
|
||||||
|
this.username = "";
|
||||||
|
|
||||||
// If there's a case open, record the name
|
// If there's a case open, record the name
|
||||||
try {
|
try {
|
||||||
@ -1383,6 +1399,10 @@ public final class HealthMonitor implements PropertyChangeListener {
|
|||||||
this.eventType = UserEvent.valueOf(resultSet.getInt("event_type"));
|
this.eventType = UserEvent.valueOf(resultSet.getInt("event_type"));
|
||||||
this.isExaminer = resultSet.getBoolean("is_examiner");
|
this.isExaminer = resultSet.getBoolean("is_examiner");
|
||||||
this.caseName = resultSet.getString("case_name");
|
this.caseName = resultSet.getString("case_name");
|
||||||
|
this.username = resultSet.getString("username");
|
||||||
|
if (this.username == null) {
|
||||||
|
this.username = "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1443,6 +1463,20 @@ public final class HealthMonitor implements PropertyChangeListener {
|
|||||||
String getCaseName() {
|
String getCaseName() {
|
||||||
return caseName;
|
return caseName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the user name for this metric
|
||||||
|
*
|
||||||
|
* @return the user name. Will be the empty string for older data.
|
||||||
|
*/
|
||||||
|
String getUserName() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(UserData otherData) {
|
||||||
|
return Long.compare(getTimestamp(), otherData.getTimestamp());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -24,12 +24,17 @@ import java.awt.Dimension;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
|
import java.io.BufferedWriter;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
import javax.swing.Box;
|
import javax.swing.Box;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
import javax.swing.JDialog;
|
import javax.swing.JDialog;
|
||||||
@ -40,13 +45,19 @@ import javax.swing.JLabel;
|
|||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
import javax.swing.JScrollPane;
|
import javax.swing.JScrollPane;
|
||||||
import javax.swing.BorderFactory;
|
import javax.swing.BorderFactory;
|
||||||
import java.util.Map;
|
|
||||||
import javax.swing.BoxLayout;
|
import javax.swing.BoxLayout;
|
||||||
import java.awt.GridLayout;
|
import java.awt.GridLayout;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
|
import java.text.DateFormat;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import javax.swing.JFileChooser;
|
||||||
import org.openide.util.NbBundle;
|
import org.openide.util.NbBundle;
|
||||||
|
import org.sleuthkit.autopsy.core.UserPreferences;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
||||||
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
|
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
|
||||||
@ -443,7 +454,11 @@ public class HealthMonitorDashboard {
|
|||||||
* Create the panel with controls for the user panel
|
* Create the panel with controls for the user panel
|
||||||
* @return the control panel
|
* @return the control panel
|
||||||
*/
|
*/
|
||||||
@NbBundle.Messages({"HealthMonitorDashboard.createUserControlPanel.maxDays=Max days to display"})
|
@NbBundle.Messages({"HealthMonitorDashboard.createUserControlPanel.maxDays=Max days to display",
|
||||||
|
"HealthMonitorDashboard.createUserControlPanel.userReportButton=Generate Report",
|
||||||
|
"HealthMonitorDashboard.createUserControlPanel.reportError=Error generating report",
|
||||||
|
"# {0} - Report file name",
|
||||||
|
"HealthMonitorDashboard.createUserControlPanel.reportDone=Report saved to: {0}"})
|
||||||
private JPanel createUserControlPanel() {
|
private JPanel createUserControlPanel() {
|
||||||
JPanel userControlPanel = new JPanel();
|
JPanel userControlPanel = new JPanel();
|
||||||
|
|
||||||
@ -473,9 +488,132 @@ public class HealthMonitorDashboard {
|
|||||||
userControlPanel.add(new JLabel(Bundle.HealthMonitorDashboard_createUserControlPanel_maxDays()));
|
userControlPanel.add(new JLabel(Bundle.HealthMonitorDashboard_createUserControlPanel_maxDays()));
|
||||||
userControlPanel.add(userDateComboBox);
|
userControlPanel.add(userDateComboBox);
|
||||||
|
|
||||||
|
// Create a button to create a user report
|
||||||
|
JButton reportButton = new JButton(Bundle.HealthMonitorDashboard_createUserControlPanel_userReportButton());
|
||||||
|
|
||||||
|
// Set up a listener on the report button
|
||||||
|
reportButton.addActionListener(new ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent arg0) {
|
||||||
|
JFileChooser reportFileChooser = new JFileChooser();
|
||||||
|
reportFileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
|
||||||
|
reportFileChooser.setCurrentDirectory(new File(UserPreferences.getHealthMonitorReportPath()));
|
||||||
|
final DateFormat csvTimestampFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
|
||||||
|
String fileName = "UserReport_" + csvTimestampFormat.format(new Date())+ ".csv";
|
||||||
|
reportFileChooser.setSelectedFile(new File(fileName));
|
||||||
|
|
||||||
|
int returnVal = reportFileChooser.showSaveDialog(userControlPanel);
|
||||||
|
if (returnVal == JFileChooser.APPROVE_OPTION) {
|
||||||
|
|
||||||
|
File selectedFile = reportFileChooser.getSelectedFile();
|
||||||
|
UserPreferences.setHealthMonitorReportPath(selectedFile.getParent());
|
||||||
|
try {
|
||||||
|
dialog.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
||||||
|
generateCSVUserReport(selectedFile);
|
||||||
|
MessageNotifyUtil.Message.info(Bundle.HealthMonitorDashboard_createUserControlPanel_reportDone(selectedFile.getAbsoluteFile()));
|
||||||
|
} catch (HealthMonitorException ex) {
|
||||||
|
logger.log(Level.SEVERE, "Error generating report", ex);
|
||||||
|
MessageNotifyUtil.Message.error(Bundle.HealthMonitorDashboard_createUserControlPanel_reportError());
|
||||||
|
} finally {
|
||||||
|
dialog.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
userControlPanel.add(reportButton);
|
||||||
|
|
||||||
return userControlPanel;
|
return userControlPanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a csv report for the last week of user data.
|
||||||
|
*
|
||||||
|
* @param reportFile
|
||||||
|
*
|
||||||
|
* @throws HealthMonitorException
|
||||||
|
*/
|
||||||
|
private void generateCSVUserReport(File reportFile) throws HealthMonitorException {
|
||||||
|
final DateFormat timestampFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
|
||||||
|
|
||||||
|
try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(reportFile), StandardCharsets.UTF_8))) {
|
||||||
|
// Write header
|
||||||
|
writer.write("Case open,Case close,Duration,Host,User,Case name");
|
||||||
|
writer.newLine();
|
||||||
|
|
||||||
|
// Get the list of user data sorted by timestamp
|
||||||
|
List<HealthMonitor.UserData> dataForReport = HealthMonitor.getInstance().getUserMetricsFromDatabase(DateRange.ONE_WEEK.getTimestampRange());
|
||||||
|
Collections.sort(dataForReport);
|
||||||
|
|
||||||
|
// Go through the list of events in order of timestamp. For each case open event, look for the next case closed
|
||||||
|
// event for that host/user/case name.
|
||||||
|
for (int caseOpenIndex = 0; caseOpenIndex < dataForReport.size() - 1; caseOpenIndex++) {
|
||||||
|
if (! dataForReport.get(caseOpenIndex).getEventType().equals(HealthMonitor.UserEvent.CASE_OPEN)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to find the next event logged for this user/host. We do not check that
|
||||||
|
// it is a case closed event.
|
||||||
|
HealthMonitor.UserData caseOpenEvent = dataForReport.get(caseOpenIndex);
|
||||||
|
HealthMonitor.UserData nextEventAfterCaseOpen = null;
|
||||||
|
for (int nextEventIndex = caseOpenIndex + 1; nextEventIndex < dataForReport.size(); nextEventIndex++) {
|
||||||
|
HealthMonitor.UserData nextEvent = dataForReport.get(nextEventIndex);
|
||||||
|
// If the user and host name do not match, ignore this event
|
||||||
|
if ( nextEvent.getHostname().equals(caseOpenEvent.getHostname())
|
||||||
|
&& nextEvent.getUserName().equals(caseOpenEvent.getUserName())) {
|
||||||
|
nextEventAfterCaseOpen = nextEvent;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare the columns
|
||||||
|
String caseOpenTime = timestampFormat.format(caseOpenEvent.getTimestamp());
|
||||||
|
|
||||||
|
// If everything is recorded properly then the next event for a given user after
|
||||||
|
// a case open will be a case close. In this case we record the close time and
|
||||||
|
// how long the case was open. If the next event was not a case close event
|
||||||
|
// or if there is no next event (which could happen if Autopsy crashed or if
|
||||||
|
// there were network issues, or if the user simply still has the case open),
|
||||||
|
// leave the close time and duration blank.
|
||||||
|
String caseCloseTime = "";
|
||||||
|
String duration = "";
|
||||||
|
if (nextEventAfterCaseOpen != null
|
||||||
|
&& nextEventAfterCaseOpen.getEventType().equals(HealthMonitor.UserEvent.CASE_CLOSE)
|
||||||
|
&& nextEventAfterCaseOpen.getCaseName().equals(caseOpenEvent.getCaseName())) {
|
||||||
|
caseCloseTime = timestampFormat.format(nextEventAfterCaseOpen.getTimestamp());
|
||||||
|
duration = getDuration(caseOpenEvent.getTimestamp(), nextEventAfterCaseOpen.getTimestamp());
|
||||||
|
}
|
||||||
|
|
||||||
|
String host = caseOpenEvent.getHostname();
|
||||||
|
String user = caseOpenEvent.getUserName();
|
||||||
|
String caseName = caseOpenEvent.getCaseName();
|
||||||
|
|
||||||
|
String csvEntry = caseOpenTime + "," + caseCloseTime + "," + duration + "," + host + "," + user + ",\"" + caseName + "\"";
|
||||||
|
writer.write(csvEntry);
|
||||||
|
writer.newLine();
|
||||||
|
}
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new HealthMonitorException("Error writing to output file " + reportFile.getAbsolutePath(), ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a string representing the time between
|
||||||
|
* the given timestamps.
|
||||||
|
*
|
||||||
|
* @param start The starting timestamp.
|
||||||
|
* @param end The ending timestamp.
|
||||||
|
*
|
||||||
|
* @return The duration as a string.
|
||||||
|
*/
|
||||||
|
private String getDuration(long start, long end) {
|
||||||
|
long durationInSeconds = (end - start) / 1000;
|
||||||
|
long second = durationInSeconds % 60;
|
||||||
|
long minute = (durationInSeconds / 60) % 60;
|
||||||
|
long hours = durationInSeconds / (60 * 60);
|
||||||
|
|
||||||
|
return String.format("%d:%02d:%02d", hours, minute, second);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the user graphs.
|
* Update the user graphs.
|
||||||
* @throws HealthMonitorException
|
* @throws HealthMonitorException
|
||||||
|
BIN
Core/src/org/sleuthkit/autopsy/images/gps-area.png
Normal file
BIN
Core/src/org/sleuthkit/autopsy/images/gps-area.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 984 B |
@ -1,5 +1,5 @@
|
|||||||
Yara_Module_Description=With the YARA ingest module you use YARA rule files to search files for textual or binary patterns.
|
Yara_Module_Description=The YARA Analyzer uses YARA to search files for textual or binary patterns.
|
||||||
Yara_Module_Name=YARA
|
Yara_Module_Name=YARA Analyzer
|
||||||
YaraIngestModule_no_ruleSets=Unable to run YARA ingest, list of YARA rule sets was empty.
|
YaraIngestModule_no_ruleSets=Unable to run YARA ingest, list of YARA rule sets was empty.
|
||||||
YaraIngestModule_windows_error_msg=The YARA ingest module is only available on 64bit Windows.
|
YaraIngestModule_windows_error_msg=The YARA ingest module is only available on 64bit Windows.
|
||||||
YaraIngestModule_yarac_not_found=Unable to compile YARA rules files. Unable to find executable at.
|
YaraIngestModule_yarac_not_found=Unable to compile YARA rules files. Unable to find executable at.
|
||||||
|
@ -227,9 +227,12 @@ final class YaraIngestHelper {
|
|||||||
|
|
||||||
ProcessBuilder builder = new ProcessBuilder(commandList);
|
ProcessBuilder builder = new ProcessBuilder(commandList);
|
||||||
try {
|
try {
|
||||||
ExecUtil.execute(builder);
|
int result = ExecUtil.execute(builder);
|
||||||
|
if(result != 0) {
|
||||||
|
throw new IngestModuleException(String.format("Failed to compile Yara rules file %s. Compile error %d", file.toString(), result));
|
||||||
|
}
|
||||||
} catch (SecurityException | IOException ex) {
|
} catch (SecurityException | IOException ex) {
|
||||||
throw new IngestModuleException(String.format("Failed to compile Yara rules file", file.toString()), ex);
|
throw new IngestModuleException(String.format("Failed to compile Yara rules file, %s", file.toString()), ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -37,8 +37,8 @@ import org.sleuthkit.autopsy.modules.yara.ui.YaraIngestSettingsPanel;
|
|||||||
public class YaraIngestModuleFactory extends IngestModuleFactoryAdapter {
|
public class YaraIngestModuleFactory extends IngestModuleFactoryAdapter {
|
||||||
|
|
||||||
@Messages({
|
@Messages({
|
||||||
"Yara_Module_Name=YARA",
|
"Yara_Module_Name=YARA Analyzer",
|
||||||
"Yara_Module_Description=With the YARA ingest module you use YARA rule files to search files for textual or binary patterns."
|
"Yara_Module_Description=The YARA Analyzer uses YARA to search files for textual or binary patterns."
|
||||||
})
|
})
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -45,6 +45,10 @@ public class RuleSetManager {
|
|||||||
*/
|
*/
|
||||||
public RuleSet createRuleSet(String name) throws RuleSetException {
|
public RuleSet createRuleSet(String name) throws RuleSetException {
|
||||||
|
|
||||||
|
if(name == null || name.isEmpty()) {
|
||||||
|
throw new RuleSetException("YARA rule set name cannot be null or empty string" );
|
||||||
|
}
|
||||||
|
|
||||||
if (isRuleSetExists(name)) {
|
if (isRuleSetExists(name)) {
|
||||||
throw new RuleSetException(String.format("Yara rule set with name %s already exits.", name));
|
throw new RuleSetException(String.format("Yara rule set with name %s already exits.", name));
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ YaraIngestSettingsPanel.allFilesButton.text=All Files
|
|||||||
YaraIngestSettingsPanel.allFilesButton.toolTipText=
|
YaraIngestSettingsPanel.allFilesButton.toolTipText=
|
||||||
YaraIngestSettingsPanel.executableFilesButton.text=Only Executable Files
|
YaraIngestSettingsPanel.executableFilesButton.text=Only Executable Files
|
||||||
RuleSetDetailsPanel.refreshButton.text=Refresh File List
|
RuleSetDetailsPanel.refreshButton.text=Refresh File List
|
||||||
|
YaraRuleSetOptionPanel_badName2_msg=Rule set is invalid.\nRule set names must be non-empty string and unique.
|
||||||
# {0} - rule set name
|
# {0} - rule set name
|
||||||
YaraRuleSetOptionPanel_badName_msg=Rule set name {0} already exists.\nRule set names must be unique.
|
YaraRuleSetOptionPanel_badName_msg=Rule set name {0} already exists.\nRule set names must be unique.
|
||||||
YaraRuleSetOptionPanel_badName_title=Create Rule Set
|
YaraRuleSetOptionPanel_badName_title=Create Rule Set
|
||||||
|
@ -93,7 +93,8 @@ public class YaraRuleSetOptionPanel extends javax.swing.JPanel {
|
|||||||
"YaraRuleSetOptionPanel_new_rule_set_name_title=Rule Set Name",
|
"YaraRuleSetOptionPanel_new_rule_set_name_title=Rule Set Name",
|
||||||
"# {0} - rule set name",
|
"# {0} - rule set name",
|
||||||
"YaraRuleSetOptionPanel_badName_msg=Rule set name {0} already exists.\nRule set names must be unique.",
|
"YaraRuleSetOptionPanel_badName_msg=Rule set name {0} already exists.\nRule set names must be unique.",
|
||||||
"YaraRuleSetOptionPanel_badName_title=Create Rule Set"
|
"YaraRuleSetOptionPanel_badName_title=Create Rule Set",
|
||||||
|
"YaraRuleSetOptionPanel_badName2_msg=Rule set is invalid.\nRule set names must be non-empty string and unique.",
|
||||||
})
|
})
|
||||||
/**
|
/**
|
||||||
* Handle the new rule set action. Prompt the user for a rule set name,
|
* Handle the new rule set action. Prompt the user for a rule set name,
|
||||||
@ -103,6 +104,15 @@ public class YaraRuleSetOptionPanel extends javax.swing.JPanel {
|
|||||||
String value = JOptionPane.showInputDialog(this,
|
String value = JOptionPane.showInputDialog(this,
|
||||||
Bundle.YaraRuleSetOptionPanel_new_rule_set_name_msg(),
|
Bundle.YaraRuleSetOptionPanel_new_rule_set_name_msg(),
|
||||||
Bundle.YaraRuleSetOptionPanel_new_rule_set_name_title());
|
Bundle.YaraRuleSetOptionPanel_new_rule_set_name_title());
|
||||||
|
|
||||||
|
if(value == null || value.isEmpty()) {
|
||||||
|
JOptionPane.showMessageDialog(this,
|
||||||
|
Bundle.YaraRuleSetOptionPanel_badName2_msg(),
|
||||||
|
Bundle.YaraRuleSetOptionPanel_badName_title(),
|
||||||
|
JOptionPane.ERROR_MESSAGE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ruleSetPanel.addRuleSet(manager.createRuleSet(value));
|
ruleSetPanel.addRuleSet(manager.createRuleSet(value));
|
||||||
} catch (RuleSetException ex) {
|
} catch (RuleSetException ex) {
|
||||||
@ -110,7 +120,7 @@ public class YaraRuleSetOptionPanel extends javax.swing.JPanel {
|
|||||||
Bundle.YaraRuleSetOptionPanel_badName_msg(value),
|
Bundle.YaraRuleSetOptionPanel_badName_msg(value),
|
||||||
Bundle.YaraRuleSetOptionPanel_badName_title(),
|
Bundle.YaraRuleSetOptionPanel_badName_title(),
|
||||||
JOptionPane.ERROR_MESSAGE);
|
JOptionPane.ERROR_MESSAGE);
|
||||||
logger.log(Level.WARNING, "Failed to create new rule set, user provide existing name.", ex);
|
logger.log(Level.WARNING, "Failed to create new rule set, user provided existing name.", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1793,6 +1793,7 @@ class TableReportGenerator {
|
|||||||
|| artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_BOOKMARK.getTypeID()
|
|| artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_BOOKMARK.getTypeID()
|
||||||
|| artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_LAST_KNOWN_LOCATION.getTypeID()
|
|| artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_LAST_KNOWN_LOCATION.getTypeID()
|
||||||
|| artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_SEARCH.getTypeID()
|
|| artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_SEARCH.getTypeID()
|
||||||
|
|| artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_AREA.getTypeID()
|
||||||
|| artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_SERVICE_ACCOUNT.getTypeID()
|
|| artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_SERVICE_ACCOUNT.getTypeID()
|
||||||
|| artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED.getTypeID()
|
|| artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED.getTypeID()
|
||||||
|| artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_SUSPECTED.getTypeID()
|
|| artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_SUSPECTED.getTypeID()
|
||||||
|
@ -384,6 +384,9 @@ public class HTMLReport implements TableReportModule {
|
|||||||
case TSK_WEB_FORM_ADDRESS:
|
case TSK_WEB_FORM_ADDRESS:
|
||||||
in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/images/web-form-address.png"); //NON-NLS
|
in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/images/web-form-address.png"); //NON-NLS
|
||||||
break;
|
break;
|
||||||
|
case TSK_GPS_AREA:
|
||||||
|
in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/images/gps-area.png"); //NON-NLS
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
logger.log(Level.WARNING, "useDataTypeIcon: unhandled artifact type = {0}", dataType); //NON-NLS
|
logger.log(Level.WARNING, "useDataTypeIcon: unhandled artifact type = {0}", dataType); //NON-NLS
|
||||||
in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/star.png"); //NON-NLS
|
in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/star.png"); //NON-NLS
|
||||||
|
@ -27,6 +27,7 @@ ReportKML.genReport.reportName=KML Report
|
|||||||
ReportKML.latLongStartPoint={0};{1};;{2} (Start)\n
|
ReportKML.latLongStartPoint={0};{1};;{2} (Start)\n
|
||||||
ReportKML.latLongEndPoint={0};{1};;{2} (End)\n
|
ReportKML.latLongEndPoint={0};{1};;{2} (End)\n
|
||||||
Route_Details_Header=GPS Route
|
Route_Details_Header=GPS Route
|
||||||
|
Waypoint_Area_Point_Display_String=GPS Area Outline Point
|
||||||
Waypoint_Bookmark_Display_String=GPS Bookmark
|
Waypoint_Bookmark_Display_String=GPS Bookmark
|
||||||
Waypoint_EXIF_Display_String=EXIF Metadata With Location
|
Waypoint_EXIF_Display_String=EXIF Metadata With Location
|
||||||
Waypoint_Last_Known_Display_String=GPS Last Known Location
|
Waypoint_Last_Known_Display_String=GPS Last Known Location
|
||||||
|
@ -50,6 +50,7 @@ import org.sleuthkit.autopsy.geolocation.datamodel.GeoLocationParseResult;
|
|||||||
import org.sleuthkit.autopsy.geolocation.datamodel.Waypoint;
|
import org.sleuthkit.autopsy.geolocation.datamodel.Waypoint;
|
||||||
import org.sleuthkit.autopsy.geolocation.datamodel.Route;
|
import org.sleuthkit.autopsy.geolocation.datamodel.Route;
|
||||||
import org.sleuthkit.autopsy.geolocation.datamodel.Track;
|
import org.sleuthkit.autopsy.geolocation.datamodel.Track;
|
||||||
|
import org.sleuthkit.autopsy.geolocation.datamodel.Area;
|
||||||
import org.sleuthkit.autopsy.geolocation.datamodel.WaypointBuilder;
|
import org.sleuthkit.autopsy.geolocation.datamodel.WaypointBuilder;
|
||||||
import org.sleuthkit.autopsy.report.GeneralReportSettings;
|
import org.sleuthkit.autopsy.report.GeneralReportSettings;
|
||||||
import org.sleuthkit.autopsy.report.ReportBranding;
|
import org.sleuthkit.autopsy.report.ReportBranding;
|
||||||
@ -84,6 +85,7 @@ public final class KMLReport implements GeneralReportModule {
|
|||||||
private Element gpsSearchesFolder;
|
private Element gpsSearchesFolder;
|
||||||
private Element gpsTrackpointsFolder;
|
private Element gpsTrackpointsFolder;
|
||||||
private Element gpsTracksFolder;
|
private Element gpsTracksFolder;
|
||||||
|
private Element gpsAreasFolder;
|
||||||
|
|
||||||
private GeneralReportSettings settings;
|
private GeneralReportSettings settings;
|
||||||
|
|
||||||
@ -154,7 +156,8 @@ public final class KMLReport implements GeneralReportModule {
|
|||||||
"Waypoint_Track_Display_String=GPS Track",
|
"Waypoint_Track_Display_String=GPS Track",
|
||||||
"Route_Details_Header=GPS Route",
|
"Route_Details_Header=GPS Route",
|
||||||
"ReportBodyFile.ingestWarning.text=Ingest Warning message",
|
"ReportBodyFile.ingestWarning.text=Ingest Warning message",
|
||||||
"Waypoint_Track_Point_Display_String=GPS Individual Track Point"
|
"Waypoint_Track_Point_Display_String=GPS Individual Track Point",
|
||||||
|
"Waypoint_Area_Point_Display_String=GPS Area Outline Point",
|
||||||
})
|
})
|
||||||
|
|
||||||
public void generateReport(String baseReportDir, ReportProgressPanel progressPanel, List<Waypoint> waypointList) {
|
public void generateReport(String baseReportDir, ReportProgressPanel progressPanel, List<Waypoint> waypointList) {
|
||||||
@ -216,6 +219,11 @@ public final class KMLReport implements GeneralReportModule {
|
|||||||
result = ReportProgressPanel.ReportStatus.ERROR;
|
result = ReportProgressPanel.ReportStatus.ERROR;
|
||||||
errorMessage = Bundle.KMLReport_partialFailure();
|
errorMessage = Bundle.KMLReport_partialFailure();
|
||||||
}
|
}
|
||||||
|
entirelySuccessful = makeAreas(skCase);
|
||||||
|
if (!entirelySuccessful) {
|
||||||
|
result = ReportProgressPanel.ReportStatus.ERROR;
|
||||||
|
errorMessage = Bundle.KMLReport_partialFailure();
|
||||||
|
}
|
||||||
|
|
||||||
addLocationsToReport(skCase, baseReportDir);
|
addLocationsToReport(skCase, baseReportDir);
|
||||||
} catch (GeoLocationDataException | IOException | TskCoreException ex) {
|
} catch (GeoLocationDataException | IOException | TskCoreException ex) {
|
||||||
@ -326,6 +334,11 @@ public final class KMLReport implements GeneralReportModule {
|
|||||||
CDATA cdataTrack = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-trackpoint.png"); //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
|
Element hrefTrack = new Element("href", ns).addContent(cdataTrack); //NON-NLS
|
||||||
gpsTracksFolder.addContent(new Element("Icon", ns).addContent(hrefTrack)); //NON-NLS
|
gpsTracksFolder.addContent(new Element("Icon", ns).addContent(hrefTrack)); //NON-NLS
|
||||||
|
|
||||||
|
gpsAreasFolder = new Element("Folder", ns); //NON-NLS
|
||||||
|
CDATA cdataArea = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-area.png"); //NON-NLS
|
||||||
|
Element hrefArea = new Element("href", ns).addContent(cdataArea); //NON-NLS
|
||||||
|
gpsAreasFolder.addContent(new Element("Icon", ns).addContent(hrefArea)); //NON-NLS
|
||||||
|
|
||||||
gpsExifMetadataFolder.addContent(new Element("name", ns).addContent("EXIF Metadata")); //NON-NLS
|
gpsExifMetadataFolder.addContent(new Element("name", ns).addContent("EXIF Metadata")); //NON-NLS
|
||||||
gpsBookmarksFolder.addContent(new Element("name", ns).addContent("GPS Bookmarks")); //NON-NLS
|
gpsBookmarksFolder.addContent(new Element("name", ns).addContent("GPS Bookmarks")); //NON-NLS
|
||||||
@ -334,6 +347,7 @@ public final class KMLReport implements GeneralReportModule {
|
|||||||
gpsSearchesFolder.addContent(new Element("name", ns).addContent("GPS Searches")); //NON-NLS
|
gpsSearchesFolder.addContent(new Element("name", ns).addContent("GPS Searches")); //NON-NLS
|
||||||
gpsTrackpointsFolder.addContent(new Element("name", ns).addContent("GPS Trackpoints")); //NON-NLS
|
gpsTrackpointsFolder.addContent(new Element("name", ns).addContent("GPS Trackpoints")); //NON-NLS
|
||||||
gpsTracksFolder.addContent(new Element("name", ns).addContent("GPS Tracks")); //NON-NLS
|
gpsTracksFolder.addContent(new Element("name", ns).addContent("GPS Tracks")); //NON-NLS
|
||||||
|
gpsAreasFolder.addContent(new Element("name", ns).addContent("GPS Areas")); //NON-NLS
|
||||||
|
|
||||||
document.addContent(gpsExifMetadataFolder);
|
document.addContent(gpsExifMetadataFolder);
|
||||||
document.addContent(gpsBookmarksFolder);
|
document.addContent(gpsBookmarksFolder);
|
||||||
@ -342,6 +356,7 @@ public final class KMLReport implements GeneralReportModule {
|
|||||||
document.addContent(gpsSearchesFolder);
|
document.addContent(gpsSearchesFolder);
|
||||||
document.addContent(gpsTrackpointsFolder);
|
document.addContent(gpsTrackpointsFolder);
|
||||||
document.addContent(gpsTracksFolder);
|
document.addContent(gpsTracksFolder);
|
||||||
|
document.addContent(gpsAreasFolder);
|
||||||
|
|
||||||
return kmlDocument;
|
return kmlDocument;
|
||||||
}
|
}
|
||||||
@ -570,6 +585,62 @@ public final class KMLReport implements GeneralReportModule {
|
|||||||
point.getTimestamp(), element, formattedCoordinates(point.getLatitude(), point.getLongitude()))); //NON-NLS
|
point.getTimestamp(), element, formattedCoordinates(point.getLatitude(), point.getLongitude()))); //NON-NLS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the area to the area folder in the document.
|
||||||
|
*
|
||||||
|
* @param skCase Currently open case.
|
||||||
|
* @return The operation was entirely successful.
|
||||||
|
*
|
||||||
|
* @throws TskCoreException
|
||||||
|
*/
|
||||||
|
boolean makeAreas(SleuthkitCase skCase) throws GeoLocationDataException, TskCoreException {
|
||||||
|
List<Area> areas;
|
||||||
|
boolean successful = true;
|
||||||
|
|
||||||
|
if (waypointList == null) {
|
||||||
|
GeoLocationParseResult<Area> result = Area.getAreas(skCase, null);
|
||||||
|
areas = result.getItems();
|
||||||
|
successful = result.isSuccessfullyParsed();
|
||||||
|
} else {
|
||||||
|
areas = WaypointBuilder.getAreas(waypointList);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Area area : areas) {
|
||||||
|
if(shouldFilterFromReport(area.getArtifact())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
addAreaToReport(area);
|
||||||
|
}
|
||||||
|
|
||||||
|
return successful;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a area to the KML report.
|
||||||
|
*
|
||||||
|
* @param area
|
||||||
|
*/
|
||||||
|
private void addAreaToReport(Area area) {
|
||||||
|
List<Waypoint> areaPoints = area.getPath();
|
||||||
|
|
||||||
|
if (areaPoints.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adding a folder with the area name so that all of the
|
||||||
|
// area border points will be grouped together.
|
||||||
|
Element areaFolder = new Element("Folder", ns); //NON-NLS
|
||||||
|
areaFolder.addContent(new Element("name", ns).addContent(area.getLabel())); //NON-NLS
|
||||||
|
gpsAreasFolder.addContent(areaFolder);
|
||||||
|
|
||||||
|
// Create a polygon using the waypoints
|
||||||
|
Element element = makePolygon(areaPoints);
|
||||||
|
Waypoint firstWp = areaPoints.get(0);
|
||||||
|
areaFolder.addContent(makePlacemark("",
|
||||||
|
FeatureColor.GREEN, getFormattedDetails(firstWp, Bundle.Waypoint_Area_Point_Display_String()),
|
||||||
|
firstWp.getTimestamp(), element, formattedCoordinates(firstWp.getLatitude(), firstWp.getLongitude()))); //NON-NLS
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format a point time stamp (in seconds) to the report format.
|
* Format a point time stamp (in seconds) to the report format.
|
||||||
@ -628,7 +699,7 @@ public final class KMLReport implements GeneralReportModule {
|
|||||||
point.addContent(coordinates);
|
point.addContent(coordinates);
|
||||||
|
|
||||||
return point;
|
return point;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a LineString for use in a Placemark. Note in this method, start
|
* Create a LineString for use in a Placemark. Note in this method, start
|
||||||
@ -662,6 +733,35 @@ public final class KMLReport implements GeneralReportModule {
|
|||||||
return lineString;
|
return lineString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a Polygon for use in a Placemark.
|
||||||
|
*
|
||||||
|
* @param waypoints The waypoints making up the outline.
|
||||||
|
*
|
||||||
|
* @return the Polygon as an Element
|
||||||
|
*/
|
||||||
|
private Element makePolygon(List<Waypoint> waypoints) {
|
||||||
|
|
||||||
|
Element polygon = new Element("Polygon", ns); //NON-NLS
|
||||||
|
|
||||||
|
Element altitudeMode = new Element("altitudeMode", ns).addContent("clampToGround"); //NON-NLS
|
||||||
|
polygon.addContent(altitudeMode);
|
||||||
|
|
||||||
|
// KML uses lon, lat. Deliberately reversed.
|
||||||
|
Element coordinates = new Element("coordinates", ns);
|
||||||
|
for (Waypoint wp : waypoints) {
|
||||||
|
coordinates.addContent(wp.getLongitude() + "," + wp.getLatitude() + ",0 "); //NON-NLS
|
||||||
|
}
|
||||||
|
// Add the first one again
|
||||||
|
coordinates.addContent(waypoints.get(0).getLongitude() + "," + waypoints.get(0).getLatitude() + ",0 "); //NON-NLS
|
||||||
|
|
||||||
|
Element linearRing = new Element("LinearRing", ns).addContent(coordinates);
|
||||||
|
Element outerBoundary = new Element("outerBoundaryIs", ns).addContent(linearRing);
|
||||||
|
polygon.addContent(outerBoundary);
|
||||||
|
|
||||||
|
return polygon;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make a Placemark for use in displaying features. Takes a
|
* Make a Placemark for use in displaying features. Takes a
|
||||||
* coordinate-bearing feature (Point, LineString, etc) and places it in the
|
* coordinate-bearing feature (Point, LineString, etc) and places it in the
|
||||||
|
@ -41,6 +41,8 @@ import java.sql.ResultSet;
|
|||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -60,6 +62,7 @@ import org.sleuthkit.autopsy.coreutils.FileTypeUtils.FileTypeCategory;
|
|||||||
import org.sleuthkit.autopsy.report.ReportProgressPanel;
|
import org.sleuthkit.autopsy.report.ReportProgressPanel;
|
||||||
import org.sleuthkit.caseuco.CaseUcoExporter;
|
import org.sleuthkit.caseuco.CaseUcoExporter;
|
||||||
import org.sleuthkit.datamodel.AbstractFile;
|
import org.sleuthkit.datamodel.AbstractFile;
|
||||||
|
import org.sleuthkit.datamodel.Account;
|
||||||
import org.sleuthkit.datamodel.Blackboard.BlackboardException;
|
import org.sleuthkit.datamodel.Blackboard.BlackboardException;
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifactTag;
|
import org.sleuthkit.datamodel.BlackboardArtifactTag;
|
||||||
@ -81,6 +84,9 @@ import org.sleuthkit.datamodel.TskDataException;
|
|||||||
import org.sleuthkit.datamodel.TskData;
|
import org.sleuthkit.datamodel.TskData;
|
||||||
import org.sleuthkit.datamodel.Volume;
|
import org.sleuthkit.datamodel.Volume;
|
||||||
import org.sleuthkit.datamodel.VolumeSystem;
|
import org.sleuthkit.datamodel.VolumeSystem;
|
||||||
|
import org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper;
|
||||||
|
import org.sleuthkit.datamodel.blackboardutils.attributes.BlackboardJsonAttrUtil;
|
||||||
|
import org.sleuthkit.datamodel.blackboardutils.attributes.MessageAttachments;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a portable case from tagged files
|
* Creates a portable case from tagged files
|
||||||
@ -883,6 +889,9 @@ public class PortableCaseReportModule implements ReportModule {
|
|||||||
|
|
||||||
// Copy the artifact
|
// Copy the artifact
|
||||||
BlackboardArtifact newArtifact = copyArtifact(newContentId, tag.getArtifact());
|
BlackboardArtifact newArtifact = copyArtifact(newContentId, tag.getArtifact());
|
||||||
|
|
||||||
|
// Copy any attachments
|
||||||
|
copyAttachments(newArtifact, tag.getArtifact(), portableSkCase.getAbstractFileById(newContentId));
|
||||||
|
|
||||||
// Tag the artfiact
|
// Tag the artfiact
|
||||||
if (!oldTagNameToNewTagName.containsKey(tag.getName())) {
|
if (!oldTagNameToNewTagName.containsKey(tag.getName())) {
|
||||||
@ -932,6 +941,11 @@ public class PortableCaseReportModule implements ReportModule {
|
|||||||
if (oldAttr.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT.getTypeID()) {
|
if (oldAttr.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT.getTypeID()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Attachments will be handled later
|
||||||
|
if (oldAttr.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ATTACHMENTS.getTypeID()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
BlackboardAttribute.Type newAttributeType = getNewAttributeType(oldAttr);
|
BlackboardAttribute.Type newAttributeType = getNewAttributeType(oldAttr);
|
||||||
switch (oldAttr.getValueType()) {
|
switch (oldAttr.getValueType()) {
|
||||||
@ -1142,6 +1156,61 @@ public class PortableCaseReportModule implements ReportModule {
|
|||||||
newIdToContent.put(newContent.getId(), newContent);
|
newIdToContent.put(newContent.getId(), newContent);
|
||||||
return oldIdToNewContent.get(content.getId()).getId();
|
return oldIdToNewContent.get(content.getId()).getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy attachments to the portable case.
|
||||||
|
*
|
||||||
|
* @param newArtifact The new artifact in the portable case. Should be complete apart from the TSK_ATTACHMENTS attribute.
|
||||||
|
* @param oldArtifact The old artifact.
|
||||||
|
* @param newFile The new file in the portable case associated with the artifact.
|
||||||
|
*
|
||||||
|
* @throws TskCoreException
|
||||||
|
*/
|
||||||
|
private void copyAttachments(BlackboardArtifact newArtifact, BlackboardArtifact oldArtifact, AbstractFile newFile) throws TskCoreException {
|
||||||
|
// Get the attachments from TSK_ATTACHMENTS attribute.
|
||||||
|
BlackboardAttribute attachmentsAttr = oldArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ATTACHMENTS));
|
||||||
|
if (attachmentsAttr != null) {
|
||||||
|
try {
|
||||||
|
MessageAttachments msgAttachments = BlackboardJsonAttrUtil.fromAttribute(attachmentsAttr, MessageAttachments.class);
|
||||||
|
|
||||||
|
Collection<MessageAttachments.FileAttachment> oldFileAttachments = msgAttachments.getFileAttachments();
|
||||||
|
List<MessageAttachments.FileAttachment> newFileAttachments = new ArrayList<>();
|
||||||
|
for (MessageAttachments.FileAttachment oldFileAttachment : oldFileAttachments) {
|
||||||
|
long attachedFileObjId = oldFileAttachment.getObjectId();
|
||||||
|
if (attachedFileObjId >= 0) {
|
||||||
|
// Copy the attached file and save to the MessageAttachments object
|
||||||
|
AbstractFile attachedFile = currentCase.getSleuthkitCase().getAbstractFileById(attachedFileObjId);
|
||||||
|
if (attachedFile == null) {
|
||||||
|
throw new TskCoreException("Error loading file with object ID " + attachedFileObjId + " from portable case");
|
||||||
|
}
|
||||||
|
long newFileID = copyContent(attachedFile);
|
||||||
|
newFileAttachments.add(new MessageAttachments.FileAttachment(portableSkCase.getAbstractFileById(newFileID)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the name of the module(s) that created the attachment
|
||||||
|
String newSourceStr = "";
|
||||||
|
List<String> oldSources = attachmentsAttr.getSources();
|
||||||
|
if (! oldSources.isEmpty()) {
|
||||||
|
newSourceStr = String.join(",", oldSources);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the attachment. The account type specified in the constructor will not be used.
|
||||||
|
CommunicationArtifactsHelper communicationArtifactsHelper = new CommunicationArtifactsHelper(currentCase.getSleuthkitCase(),
|
||||||
|
newSourceStr, newFile, Account.Type.EMAIL);
|
||||||
|
communicationArtifactsHelper.addAttachments(newArtifact, new MessageAttachments(newFileAttachments, msgAttachments.getUrlAttachments()));
|
||||||
|
}
|
||||||
|
catch (BlackboardJsonAttrUtil.InvalidJsonException ex) {
|
||||||
|
throw new TskCoreException(String.format("Unable to parse json for MessageAttachments object in artifact: %s", oldArtifact.getName()), ex);
|
||||||
|
}
|
||||||
|
} else { // backward compatibility - email message attachments are derived files, children of the message.
|
||||||
|
for (Content childContent : oldArtifact.getChildren()) {
|
||||||
|
if (childContent instanceof AbstractFile) {
|
||||||
|
copyContent(childContent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the subfolder name for this file based on MIME type
|
* Return the subfolder name for this file based on MIME type
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
</configurations>
|
</configurations>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency conf="experimental->default" org="com.github.lgooddatepicker" name="LGoodDatePicker" rev="10.3.1"/>
|
<dependency conf="experimental->default" org="com.github.lgooddatepicker" name="LGoodDatePicker" rev="10.3.1"/>
|
||||||
<dependency conf="experimental->default" org="org.postgresql" name="postgresql" rev="9.4-1201-jdbc41"/>
|
<dependency conf="experimental->default" org="org.postgresql" name="postgresql" rev="42.2.18"/>
|
||||||
<dependency conf="experimental->default" org="com.mchange" name="c3p0" rev="0.9.5"/>
|
<dependency conf="experimental->default" org="com.mchange" name="c3p0" rev="0.9.5"/>
|
||||||
<dependency conf="experimental->default" org="com.fasterxml.jackson.core" name="jackson-core" rev="2.7.0"/>
|
<dependency conf="experimental->default" org="com.fasterxml.jackson.core" name="jackson-core" rev="2.7.0"/>
|
||||||
<dependency conf="experimental->default" org="org.swinglabs.swingx" name="swingx-all" rev="1.6.4"/>
|
<dependency conf="experimental->default" org="org.swinglabs.swingx" name="swingx-all" rev="1.6.4"/>
|
||||||
|
@ -29,11 +29,14 @@ import java.sql.SQLException;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import org.openide.modules.InstalledFileLocator;
|
import org.openide.modules.InstalledFileLocator;
|
||||||
import org.openide.util.NbBundle.Messages;
|
import org.openide.util.NbBundle.Messages;
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
|
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||||
import org.sleuthkit.autopsy.casemodule.services.FileManager;
|
import org.sleuthkit.autopsy.casemodule.services.FileManager;
|
||||||
import org.sleuthkit.autopsy.coreutils.ExecUtil;
|
import org.sleuthkit.autopsy.coreutils.ExecUtil;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
@ -262,7 +265,7 @@ final class ExtractPrefetch extends Extract {
|
|||||||
|
|
||||||
AbstractFile pfAbstractFile = getAbstractFile(prefetchFileName, PREFETCH_FILE_LOCATION, dataSource);
|
AbstractFile pfAbstractFile = getAbstractFile(prefetchFileName, PREFETCH_FILE_LOCATION, dataSource);
|
||||||
|
|
||||||
List<Long> prefetchExecutionTimes = findNonZeroExecutionTimes(executionTimes);
|
Set<Long> prefetchExecutionTimes = findNonZeroExecutionTimes(executionTimes);
|
||||||
|
|
||||||
if (pfAbstractFile != null) {
|
if (pfAbstractFile != null) {
|
||||||
for (Long executionTime : prefetchExecutionTimes) {
|
for (Long executionTime : prefetchExecutionTimes) {
|
||||||
@ -321,8 +324,8 @@ final class ExtractPrefetch extends Extract {
|
|||||||
*
|
*
|
||||||
* @return List of timestamps that are greater than zero
|
* @return List of timestamps that are greater than zero
|
||||||
*/
|
*/
|
||||||
private List<Long> findNonZeroExecutionTimes(List<Long> executionTimes) {
|
private Set<Long> findNonZeroExecutionTimes(List<Long> executionTimes) {
|
||||||
List<Long> prefetchExecutionTimes = new ArrayList<>();
|
Set<Long> prefetchExecutionTimes = new HashSet<>();
|
||||||
for (Long executionTime : executionTimes) { // only add prefetch file entries that have an actual date associated with them
|
for (Long executionTime : executionTimes) { // only add prefetch file entries that have an actual date associated with them
|
||||||
if (executionTime > 0) {
|
if (executionTime > 0) {
|
||||||
prefetchExecutionTimes.add(executionTime);
|
prefetchExecutionTimes.add(executionTime);
|
||||||
|
@ -331,9 +331,9 @@ final class ExtractSru extends Extract {
|
|||||||
private void createNetUsageArtifacts(String sruDb, AbstractFile sruAbstractFile) {
|
private void createNetUsageArtifacts(String sruDb, AbstractFile sruAbstractFile) {
|
||||||
List<BlackboardArtifact> bba = new ArrayList<>();
|
List<BlackboardArtifact> bba = new ArrayList<>();
|
||||||
|
|
||||||
String sqlStatement = "SELECT STRFTIME('%s', timestamp) ExecutionTime, Application_Name, User_Name, "
|
String sqlStatement = "SELECT STRFTIME('%s', timestamp) ExecutionTime, a.application_name, b.Application_Name formatted_application_name, User_Name, "
|
||||||
+ " bytesSent, BytesRecvd FROM network_Usage , SruDbIdMapTable "
|
+ " bytesSent, BytesRecvd FROM network_Usage a, SruDbIdMapTable, exe_to_app b "
|
||||||
+ " where appId = IdIndex and IdType = 0 order by ExecutionTime;"; //NON-NLS
|
+ " where appId = IdIndex and IdType = 0 and a.application_name = b.source_name order by ExecutionTime;"; //NON-NLS
|
||||||
|
|
||||||
try (SQLiteDBConnect tempdbconnect = new SQLiteDBConnect("org.sqlite.JDBC", "jdbc:sqlite:" + sruDb); //NON-NLS
|
try (SQLiteDBConnect tempdbconnect = new SQLiteDBConnect("org.sqlite.JDBC", "jdbc:sqlite:" + sruDb); //NON-NLS
|
||||||
ResultSet resultSet = tempdbconnect.executeQry(sqlStatement)) {
|
ResultSet resultSet = tempdbconnect.executeQry(sqlStatement)) {
|
||||||
@ -346,6 +346,7 @@ final class ExtractSru extends Extract {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String applicationName = resultSet.getString("Application_Name"); //NON-NLS
|
String applicationName = resultSet.getString("Application_Name"); //NON-NLS
|
||||||
|
String formattedApplicationName = resultSet.getString("formatted_Application_name");
|
||||||
Long executionTime = Long.valueOf(resultSet.getInt("ExecutionTime")); //NON-NLS
|
Long executionTime = Long.valueOf(resultSet.getInt("ExecutionTime")); //NON-NLS
|
||||||
Long bytesSent = Long.valueOf(resultSet.getInt("bytesSent")); //NON-NLS
|
Long bytesSent = Long.valueOf(resultSet.getInt("bytesSent")); //NON-NLS
|
||||||
Long bytesRecvd = Long.valueOf(resultSet.getInt("BytesRecvd")); //NON-NLS
|
Long bytesRecvd = Long.valueOf(resultSet.getInt("BytesRecvd")); //NON-NLS
|
||||||
@ -354,7 +355,7 @@ final class ExtractSru extends Extract {
|
|||||||
Collection<BlackboardAttribute> bbattributes = Arrays.asList(
|
Collection<BlackboardAttribute> bbattributes = Arrays.asList(
|
||||||
new BlackboardAttribute(
|
new BlackboardAttribute(
|
||||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, getName(),
|
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, getName(),
|
||||||
applicationName),//NON-NLS
|
formattedApplicationName),//NON-NLS
|
||||||
new BlackboardAttribute(
|
new BlackboardAttribute(
|
||||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_USER_NAME, getName(),
|
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_USER_NAME, getName(),
|
||||||
userName),
|
userName),
|
||||||
@ -395,9 +396,9 @@ final class ExtractSru extends Extract {
|
|||||||
private void createAppUsageArtifacts(String sruDb, AbstractFile sruAbstractFile) {
|
private void createAppUsageArtifacts(String sruDb, AbstractFile sruAbstractFile) {
|
||||||
List<BlackboardArtifact> bba = new ArrayList<>();
|
List<BlackboardArtifact> bba = new ArrayList<>();
|
||||||
|
|
||||||
String sqlStatement = "SELECT STRFTIME('%s', timestamp) ExecutionTime, Application_Name, User_Name "
|
String sqlStatement = "SELECT STRFTIME('%s', timestamp) ExecutionTime, a.application_name, b.Application_Name formatted_application_name, User_Name "
|
||||||
+ " FROM Application_Resource_Usage, SruDbIdMapTable WHERE "
|
+ " FROM Application_Resource_Usage a, SruDbIdMapTable, exe_to_app b WHERE "
|
||||||
+ " idType = 0 and idIndex = appId order by ExecutionTime;"; //NON-NLS
|
+ " idType = 0 and idIndex = appId and a.application_name = b.source_name order by ExecutionTime;"; //NON-NLS
|
||||||
|
|
||||||
try (SQLiteDBConnect tempdbconnect = new SQLiteDBConnect("org.sqlite.JDBC", "jdbc:sqlite:" + sruDb); //NON-NLS
|
try (SQLiteDBConnect tempdbconnect = new SQLiteDBConnect("org.sqlite.JDBC", "jdbc:sqlite:" + sruDb); //NON-NLS
|
||||||
ResultSet resultSet = tempdbconnect.executeQry(sqlStatement)) {
|
ResultSet resultSet = tempdbconnect.executeQry(sqlStatement)) {
|
||||||
@ -410,13 +411,14 @@ final class ExtractSru extends Extract {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String applicationName = resultSet.getString("Application_Name"); //NON-NLS
|
String applicationName = resultSet.getString("Application_Name"); //NON-NLS
|
||||||
|
String formattedApplicationName = resultSet.getString("formatted_application_name");
|
||||||
Long executionTime = Long.valueOf(resultSet.getInt("ExecutionTime")); //NON-NLS
|
Long executionTime = Long.valueOf(resultSet.getInt("ExecutionTime")); //NON-NLS
|
||||||
String userName = resultSet.getString("User_Name");
|
String userName = resultSet.getString("User_Name");
|
||||||
|
|
||||||
Collection<BlackboardAttribute> bbattributes = Arrays.asList(
|
Collection<BlackboardAttribute> bbattributes = Arrays.asList(
|
||||||
new BlackboardAttribute(
|
new BlackboardAttribute(
|
||||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, getName(),
|
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, getName(),
|
||||||
applicationName),//NON-NLS
|
formattedApplicationName),//NON-NLS
|
||||||
new BlackboardAttribute(
|
new BlackboardAttribute(
|
||||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_USER_NAME, getName(),
|
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_USER_NAME, getName(),
|
||||||
userName),
|
userName),
|
||||||
|
@ -63,7 +63,9 @@ public class RegressionTest extends TestCase {
|
|||||||
clusters(".*").
|
clusters(".*").
|
||||||
enableModules(".*");
|
enableModules(".*");
|
||||||
if (img_path.isFile()) {
|
if (img_path.isFile()) {
|
||||||
conf = conf.addTest("testNewCaseWizardOpen",
|
conf = conf.addTest(
|
||||||
|
"testConfigureCustomCR",
|
||||||
|
"testNewCaseWizardOpen",
|
||||||
"testNewCaseWizard",
|
"testNewCaseWizard",
|
||||||
"testStartAddImageFileDataSource",
|
"testStartAddImageFileDataSource",
|
||||||
"testConfigureIngest1",
|
"testConfigureIngest1",
|
||||||
@ -78,7 +80,9 @@ public class RegressionTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (img_path.isDirectory()) {
|
if (img_path.isDirectory()) {
|
||||||
conf = conf.addTest("testNewCaseWizardOpen",
|
conf = conf.addTest(
|
||||||
|
"testConfigureCustomCR",
|
||||||
|
"testNewCaseWizardOpen",
|
||||||
"testNewCaseWizard",
|
"testNewCaseWizard",
|
||||||
"testStartAddLogicalFilesDataSource",
|
"testStartAddLogicalFilesDataSource",
|
||||||
"testConfigureIngest1",
|
"testConfigureIngest1",
|
||||||
@ -103,7 +107,11 @@ public class RegressionTest extends TestCase {
|
|||||||
public void setUp() {
|
public void setUp() {
|
||||||
logger.info("######## " + AutopsyTestCases.getEscapedPath(System.getProperty("img_path")) + " #######");
|
logger.info("######## " + AutopsyTestCases.getEscapedPath(System.getProperty("img_path")) + " #######");
|
||||||
Timeouts.setDefault("ComponentOperator.WaitComponentTimeout", 1000000);
|
Timeouts.setDefault("ComponentOperator.WaitComponentTimeout", 1000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testConfigureCustomCR() {
|
||||||
|
// Configure a custom CR before proceeding with the test (and creating
|
||||||
|
// a case).
|
||||||
try {
|
try {
|
||||||
if (Boolean.parseBoolean(System.getProperty("isMultiUser"))) {
|
if (Boolean.parseBoolean(System.getProperty("isMultiUser"))) {
|
||||||
// Set up a custom postgres CR using the configuration passed
|
// Set up a custom postgres CR using the configuration passed
|
||||||
|
@ -5,6 +5,7 @@ file.reference.commons-lang3-3.8.1.jar=release/modules/ext/commons-lang3-3.8.1.j
|
|||||||
file.reference.apache-mime4j-core-0.8.0.jar=release/modules/ext/apache-mime4j-core-0.8.0-SNAPSHOT.jar
|
file.reference.apache-mime4j-core-0.8.0.jar=release/modules/ext/apache-mime4j-core-0.8.0-SNAPSHOT.jar
|
||||||
file.reference.apache-mime4j-dom-0.8.0.jar=release/modules/ext/apache-mime4j-dom-0.8.0-SNAPSHOT.jar
|
file.reference.apache-mime4j-dom-0.8.0.jar=release/modules/ext/apache-mime4j-dom-0.8.0-SNAPSHOT.jar
|
||||||
file.reference.apache-mime4j-mbox-iterator-0.8.0.jar=release/modules/ext/apache-mime4j-mbox-iterator-0.8.0-SNAPSHOT.jar
|
file.reference.apache-mime4j-mbox-iterator-0.8.0.jar=release/modules/ext/apache-mime4j-mbox-iterator-0.8.0-SNAPSHOT.jar
|
||||||
|
file.reference.commons-validator-1.6.jar=release/modules/ext/commons-validator-1.6.jar
|
||||||
file.reference.guava-19.0.jar=release/modules/ext/guava-19.0.jar
|
file.reference.guava-19.0.jar=release/modules/ext/guava-19.0.jar
|
||||||
file.reference.java-libpst-1.0-SNAPSHOT.jar=release/modules/ext/java-libpst-1.0-SNAPSHOT.jar
|
file.reference.java-libpst-1.0-SNAPSHOT.jar=release/modules/ext/java-libpst-1.0-SNAPSHOT.jar
|
||||||
file.reference.ez-vcard-0.10.5.jar=release/modules/ext/ez-vcard-0.10.5.jar
|
file.reference.ez-vcard-0.10.5.jar=release/modules/ext/ez-vcard-0.10.5.jar
|
||||||
|
@ -100,6 +100,10 @@
|
|||||||
<runtime-relative-path>ext/vinnie-2.0.2.jar</runtime-relative-path>
|
<runtime-relative-path>ext/vinnie-2.0.2.jar</runtime-relative-path>
|
||||||
<binary-origin>release/modules/ext/vinnie-2.0.2.jar</binary-origin>
|
<binary-origin>release/modules/ext/vinnie-2.0.2.jar</binary-origin>
|
||||||
</class-path-extension>
|
</class-path-extension>
|
||||||
|
<class-path-extension>
|
||||||
|
<runtime-relative-path>ext/commons-validator-1.6.jar</runtime-relative-path>
|
||||||
|
<binary-origin>release/modules/ext/commons-validator-1.6.jar</binary-origin>
|
||||||
|
</class-path-extension>
|
||||||
</data>
|
</data>
|
||||||
</configuration>
|
</configuration>
|
||||||
</project>
|
</project>
|
||||||
|
@ -40,6 +40,7 @@ import org.apache.james.mime4j.mboxiterator.CharBufferWrapper;
|
|||||||
import org.apache.james.mime4j.mboxiterator.MboxIterator;
|
import org.apache.james.mime4j.mboxiterator.MboxIterator;
|
||||||
import org.apache.tika.parser.txt.CharsetDetector;
|
import org.apache.tika.parser.txt.CharsetDetector;
|
||||||
import org.apache.tika.parser.txt.CharsetMatch;
|
import org.apache.tika.parser.txt.CharsetMatch;
|
||||||
|
import org.apache.commons.validator.routines.EmailValidator;
|
||||||
import org.openide.util.NbBundle;
|
import org.openide.util.NbBundle;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -56,7 +57,13 @@ class MboxParser extends MimeJ4MessageParser implements Iterator<EmailMessage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static boolean isValidMimeTypeMbox(byte[] buffer) {
|
static boolean isValidMimeTypeMbox(byte[] buffer) {
|
||||||
return (new String(buffer)).startsWith("From "); //NON-NLS
|
String mboxHeaderLine = new String(buffer);
|
||||||
|
if (mboxHeaderLine.startsWith("From ")) {
|
||||||
|
String[] mboxLineValues = mboxHeaderLine.split(" ");
|
||||||
|
EmailValidator validator = EmailValidator.getInstance(true, true);
|
||||||
|
return validator.isValid(mboxLineValues[1]);
|
||||||
|
}
|
||||||
|
return false; //NON-NLS
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -17,7 +17,7 @@ echo `df -h .`
|
|||||||
|
|
||||||
if [ "${TRAVIS_OS_NAME}" = "osx" ]; then
|
if [ "${TRAVIS_OS_NAME}" = "osx" ]; then
|
||||||
# if os x, just run it
|
# if os x, just run it
|
||||||
ant -q test-no-regression
|
# ant -q test-no-regression
|
||||||
elif [ "${TRAVIS_OS_NAME}" = "linux" ]; then
|
elif [ "${TRAVIS_OS_NAME}" = "linux" ]; then
|
||||||
# if linux use xvfb
|
# if linux use xvfb
|
||||||
xvfb-run ant -q test-no-regression
|
xvfb-run ant -q test-no-regression
|
||||||
|
Loading…
x
Reference in New Issue
Block a user