mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-17 18:17:43 +00:00
Merge remote-tracking branch 'upstream/develop' into 6154-System-Resource-Usage-Database-Program-Run-and-Network-Activity-Artifacts
This commit is contained in:
commit
d8fb5a64db
@ -45,7 +45,6 @@ file.reference.jericho-html-3.3.jar=release/modules/ext/jericho-html-3.3.jar
|
||||
file.reference.jgraphx-v3.8.0.jar=release/modules/ext/jgraphx-v3.8.0.jar
|
||||
file.reference.jhighlight-1.0.3.jar=release\\modules\\ext\\jhighlight-1.0.3.jar
|
||||
file.reference.jmatio-1.5.jar=release\\modules\\ext\\jmatio-1.5.jar
|
||||
file.reference.jna-5.1.0.jar=release\\modules\\ext\\jna-5.1.0.jar
|
||||
file.reference.json-simple-1.1.1.jar=release\\modules\\ext\\json-simple-1.1.1.jar
|
||||
file.reference.jsoup-1.11.3.jar=release\\modules\\ext\\jsoup-1.11.3.jar
|
||||
file.reference.jul-to-slf4j-1.7.25.jar=release\\modules\\ext\\jul-to-slf4j-1.7.25.jar
|
||||
@ -97,7 +96,6 @@ file.reference.xz-1.8.jar=release\\modules\\ext\\xz-1.8.jar
|
||||
file.reference.zookeeper-3.4.6.jar=release/modules/ext/zookeeper-3.4.6.jar
|
||||
file.reference.SparseBitSet-1.1.jar=release/modules/ext/SparseBitSet-1.1.jar
|
||||
file.reference.commons-validator-1.6.jar=release/modules/ext/commons-validator-1.6.jar
|
||||
file.reference.jna-3.4.0.jar=release/modules/ext/jna-3.4.0.jar
|
||||
file.reference.api-common-1.7.0.jar=release/modules/ext/api-common-1.7.0.jar
|
||||
file.reference.gax-1.44.0.jar=release/modules/ext/gax-1.44.0.jar
|
||||
file.reference.gax-grpc-1.44.0.jar=release/modules/ext/gax-grpc-1.44.0.jar
|
||||
|
@ -615,10 +615,6 @@
|
||||
<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>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/jna-5.1.0.jar</runtime-relative-path>
|
||||
<binary-origin>release\modules\ext\jna-5.1.0.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/jbig2-imageio-3.0.2.jar</runtime-relative-path>
|
||||
<binary-origin>release\modules\ext\jbig2-imageio-3.0.2.jar</binary-origin>
|
||||
|
@ -51,7 +51,7 @@ public class CentralRepoDbUpgrader13To14 implements CentralRepoDbUpgrader {
|
||||
if (type.getId() >= CorrelationAttributeInstance.ADDITIONAL_TYPES_BASE_ID) {
|
||||
|
||||
// these are new Correlation types - new tables need to be created
|
||||
statement.execute(String.format(RdbmsCentralRepoFactory.getCreateArtifactInstancesTableTemplate(selectedPlatform), instance_type_dbname, instance_type_dbname));
|
||||
statement.execute(String.format(RdbmsCentralRepoFactory.getCreateAccountInstancesTableTemplate(selectedPlatform), instance_type_dbname, instance_type_dbname));
|
||||
statement.execute(String.format(RdbmsCentralRepoFactory.getAddCaseIdIndexTemplate(), instance_type_dbname, instance_type_dbname));
|
||||
statement.execute(String.format(RdbmsCentralRepoFactory.getAddDataSourceIdIndexTemplate(), instance_type_dbname, instance_type_dbname));
|
||||
statement.execute(String.format(RdbmsCentralRepoFactory.getAddValueIndexTemplate(), instance_type_dbname, instance_type_dbname));
|
||||
@ -61,9 +61,8 @@ public class CentralRepoDbUpgrader13To14 implements CentralRepoDbUpgrader {
|
||||
// add new correlation type
|
||||
CentralRepoDbUtil.insertCorrelationType(connection, type);
|
||||
|
||||
} else {
|
||||
|
||||
// Alter the existing X_Instance tables to add account_id column
|
||||
} else if (type.getId() == CorrelationAttributeInstance.EMAIL_TYPE_ID || type.getId() == CorrelationAttributeInstance.PHONE_TYPE_ID) {
|
||||
// Alter the existing _instance tables for Phone and Email attributes to add account_id column
|
||||
String sqlStr = String.format(getAlterArtifactInstancesAddAccountIdTemplate(selectedPlatform), instance_type_dbname);
|
||||
statement.execute(sqlStr);
|
||||
|
||||
|
@ -325,4 +325,17 @@ public class CentralRepoDbUtil {
|
||||
closeStatement(preparedStatement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given correlation attribute type has an account behind it.
|
||||
*
|
||||
* @param type Correlation type to check.
|
||||
*
|
||||
* @return True If the specified correlation type has an account.
|
||||
*/
|
||||
static boolean correlationAttribHasAnAccount(CorrelationAttributeInstance.Type type) {
|
||||
return (type.getId() >= CorrelationAttributeInstance.ADDITIONAL_TYPES_BASE_ID)
|
||||
|| type.getId() == CorrelationAttributeInstance.PHONE_TYPE_ID
|
||||
|| type.getId() == CorrelationAttributeInstance.EMAIL_TYPE_ID;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -63,9 +63,21 @@ public class CentralRepoPostgresSettingsUtil {
|
||||
|
||||
private CentralRepoPostgresSettingsUtil() {}
|
||||
|
||||
private void logException(TryHandler handler) {
|
||||
/**
|
||||
* Uses setter object to set a value as specified by 'value'. In the event that 'value'
|
||||
* is null, the setter will not be called. Exceptions that are raised from the setter will
|
||||
* be logged.
|
||||
*
|
||||
* @param setter The setter to call.
|
||||
* @param value The value to use with the setter.
|
||||
*/
|
||||
private void setValOrLog(ValueSetter setter, String value) {
|
||||
// ignore null values as they indicate a setting that is not set yet
|
||||
if (value == null || value.isEmpty())
|
||||
return;
|
||||
|
||||
try {
|
||||
handler.operation();
|
||||
setter.set(value);
|
||||
}
|
||||
catch (CentralRepoException | NumberFormatException e) {
|
||||
LOGGER.log(Level.WARNING, "There was an error in converting central repo postgres settings", e);
|
||||
@ -73,10 +85,10 @@ public class CentralRepoPostgresSettingsUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* This interface represents an action that potentially throws an exception.
|
||||
* This interface represents a setter that potentially throws an exception.
|
||||
*/
|
||||
private interface TryHandler {
|
||||
void operation() throws CentralRepoException, NumberFormatException;
|
||||
private interface ValueSetter {
|
||||
void set(String value) throws CentralRepoException, NumberFormatException;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -96,15 +108,12 @@ public class CentralRepoPostgresSettingsUtil {
|
||||
return settings;
|
||||
}
|
||||
|
||||
logException(() -> settings.setHost(muConn.getHost()));
|
||||
logException(() -> settings.setDbName(PostgresConnectionSettings.DEFAULT_DBNAME));
|
||||
logException(() -> settings.setUserName(muConn.getUserName()));
|
||||
|
||||
logException(() -> settings.setPort(Integer.parseInt(muConn.getPort())));
|
||||
logException(() -> settings.setBulkThreshold(RdbmsCentralRepo.DEFAULT_BULK_THRESHHOLD));
|
||||
|
||||
logException(() -> settings.setPassword(muConn.getPassword()));
|
||||
setValOrLog((v) -> settings.setHost(v), muConn.getHost());
|
||||
setValOrLog((v) -> settings.setUserName(v), muConn.getUserName());
|
||||
setValOrLog((v) -> settings.setPassword(v), muConn.getPassword());
|
||||
|
||||
setValOrLog((v) -> settings.setPort(Integer.parseInt(v)), muConn.getPort());
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
@ -120,24 +129,27 @@ public class CentralRepoPostgresSettingsUtil {
|
||||
Map<String, String> keyVals = ModuleSettings.getConfigSettings(MODULE_KEY);
|
||||
|
||||
|
||||
logException(() -> settings.setHost(keyVals.get(HOST_KEY)));
|
||||
logException(() -> settings.setDbName(keyVals.get(DBNAME_KEY)));
|
||||
logException(() -> settings.setUserName(keyVals.get(USER_KEY)));
|
||||
setValOrLog((v) -> settings.setHost(v), keyVals.get(HOST_KEY));
|
||||
setValOrLog((v) -> settings.setDbName(v), keyVals.get(DBNAME_KEY));
|
||||
setValOrLog((v) -> settings.setUserName(v), keyVals.get(USER_KEY));
|
||||
|
||||
logException(() -> settings.setPort(Integer.parseInt(keyVals.get(PORT_KEY))));
|
||||
logException(() -> settings.setBulkThreshold(Integer.parseInt(keyVals.get((BULK_THRESHOLD_KEY)))));
|
||||
setValOrLog((v) -> settings.setPort(Integer.parseInt(v)), keyVals.get(PORT_KEY));
|
||||
setValOrLog((v) -> settings.setBulkThreshold(Integer.parseInt(v)), keyVals.get((BULK_THRESHOLD_KEY)));
|
||||
|
||||
String passwordHex = keyVals.get(PASSWORD_KEY);
|
||||
String password;
|
||||
try {
|
||||
password = TextConverter.convertHexTextToText(passwordHex);
|
||||
} catch (TextConverterException ex) {
|
||||
LOGGER.log(Level.WARNING, "Failed to convert password from hex text to text.", ex);
|
||||
password = null;
|
||||
if (passwordHex != null) {
|
||||
String password;
|
||||
try {
|
||||
password = TextConverter.convertHexTextToText(passwordHex);
|
||||
} catch (TextConverterException ex) {
|
||||
LOGGER.log(Level.WARNING, "Failed to convert password from hex text to text.", ex);
|
||||
password = null;
|
||||
}
|
||||
|
||||
final String finalPassword = password;
|
||||
setValOrLog((v) -> settings.setPassword(v), finalPassword);
|
||||
}
|
||||
|
||||
final String finalPassword = password;
|
||||
logException(() -> settings.setPassword(finalPassword));
|
||||
return settings;
|
||||
}
|
||||
|
||||
|
@ -294,8 +294,9 @@ public class CorrelationAttributeInstance implements Serializable {
|
||||
// Create Correlation Types for Accounts.
|
||||
int correlationTypeId = ADDITIONAL_TYPES_BASE_ID;
|
||||
for (Account.Type type : Account.Type.PREDEFINED_ACCOUNT_TYPES) {
|
||||
// Skip Device account type - we dont want to correlate on those.
|
||||
// Skip Phone and Email accounts as there are already Correlation types defined for those.
|
||||
if (type != Account.Type.EMAIL && type != Account.Type.PHONE) {
|
||||
if (type != Account.Type.DEVICE && type != Account.Type.EMAIL && type != Account.Type.PHONE) {
|
||||
defaultCorrelationTypes.add(new CorrelationAttributeInstance.Type(correlationTypeId, type.getDisplayName(), type.getTypeName().toLowerCase() + "_acct", true, true)); //NON-NLS
|
||||
correlationTypeId++;
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount.CentralRepoAccountType;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.Account;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
@ -184,25 +185,29 @@ public class CorrelationAttributeUtil {
|
||||
// Get the account type from the artifact
|
||||
BlackboardAttribute accountTypeAttribute = acctArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE));
|
||||
String accountTypeStr = accountTypeAttribute.getValueString();
|
||||
|
||||
// do not create any correlation attribute instance for a Device account
|
||||
if (Account.Type.DEVICE.getTypeName().equalsIgnoreCase(accountTypeStr) == false) {
|
||||
|
||||
// Get the corresponding CentralRepoAccountType from the database.
|
||||
CentralRepoAccountType crAccountType = CentralRepository.getInstance().getAccountTypeByName(accountTypeStr);
|
||||
// Get the corresponding CentralRepoAccountType from the database.
|
||||
CentralRepoAccountType crAccountType = CentralRepository.getInstance().getAccountTypeByName(accountTypeStr);
|
||||
|
||||
int corrTypeId = crAccountType.getCorrelationTypeId();
|
||||
CorrelationAttributeInstance.Type corrType = CentralRepository.getInstance().getCorrelationTypeById(corrTypeId);
|
||||
|
||||
// Get the account identifier
|
||||
BlackboardAttribute accountIdAttribute = acctArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ID));
|
||||
String accountIdStr = accountIdAttribute.getValueString();
|
||||
|
||||
// add/get the account and get its accountId.
|
||||
CentralRepoAccount crAccount = CentralRepository.getInstance().getOrCreateAccount(crAccountType, accountIdStr);
|
||||
|
||||
CorrelationAttributeInstance corrAttr = makeCorrAttr(acctArtifact, corrType, accountIdStr);
|
||||
if (corrAttr != null) {
|
||||
// set the account_id in correlation attribute
|
||||
corrAttr.setAccountId(crAccount.getAccountId());
|
||||
corrAttrInstances.add(corrAttr);
|
||||
int corrTypeId = crAccountType.getCorrelationTypeId();
|
||||
CorrelationAttributeInstance.Type corrType = CentralRepository.getInstance().getCorrelationTypeById(corrTypeId);
|
||||
|
||||
// Get the account identifier
|
||||
BlackboardAttribute accountIdAttribute = acctArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ID));
|
||||
String accountIdStr = accountIdAttribute.getValueString();
|
||||
|
||||
// add/get the account and get its accountId.
|
||||
CentralRepoAccount crAccount = CentralRepository.getInstance().getOrCreateAccount(crAccountType, accountIdStr);
|
||||
|
||||
CorrelationAttributeInstance corrAttr = makeCorrAttr(acctArtifact, corrType, accountIdStr);
|
||||
if (corrAttr != null) {
|
||||
// set the account_id in correlation attribute
|
||||
corrAttr.setAccountId(crAccount.getAccountId());
|
||||
corrAttrInstances.add(corrAttr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1002,18 +1002,26 @@ abstract class RdbmsCentralRepo implements CentralRepository {
|
||||
public void addArtifactInstance(CorrelationAttributeInstance eamArtifact) throws CentralRepoException {
|
||||
checkAddArtifactInstanceNulls(eamArtifact);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// @@@ We should cache the case and data source IDs in memory
|
||||
String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(eamArtifact.getCorrelationType());
|
||||
String sql
|
||||
= "INSERT INTO "
|
||||
boolean artifactHasAnAccount = CentralRepoDbUtil.correlationAttribHasAnAccount(eamArtifact.getCorrelationType());
|
||||
|
||||
String sql;
|
||||
// _instance table for accounts have an additional account_id column
|
||||
if (artifactHasAnAccount) {
|
||||
sql = "INSERT INTO "
|
||||
+ tableName
|
||||
+ "(case_id, data_source_id, value, file_path, known_status, comment, file_obj_id, account_id) "
|
||||
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?) "
|
||||
+ getConflictClause();
|
||||
}
|
||||
else {
|
||||
sql = "INSERT INTO "
|
||||
+ tableName
|
||||
+ "(case_id, data_source_id, value, file_path, known_status, comment, file_obj_id) "
|
||||
+ "VALUES (?, ?, ?, ?, ?, ?, ?) "
|
||||
+ getConflictClause();
|
||||
}
|
||||
|
||||
try (Connection conn = connect();
|
||||
PreparedStatement preparedStatement = conn.prepareStatement(sql);) {
|
||||
@ -1032,10 +1040,13 @@ abstract class RdbmsCentralRepo implements CentralRepository {
|
||||
}
|
||||
preparedStatement.setLong(7, eamArtifact.getFileObjectId());
|
||||
|
||||
if (eamArtifact.getAccountId() >= 0) {
|
||||
preparedStatement.setLong(8, eamArtifact.getAccountId());
|
||||
} else {
|
||||
preparedStatement.setNull(8, Types.INTEGER);
|
||||
// set in the accountId only for artifacts that represent accounts
|
||||
if (artifactHasAnAccount) {
|
||||
if (eamArtifact.getAccountId() >= 0) {
|
||||
preparedStatement.setLong(8, eamArtifact.getAccountId());
|
||||
} else {
|
||||
preparedStatement.setNull(8, Types.INTEGER);
|
||||
}
|
||||
}
|
||||
|
||||
preparedStatement.executeUpdate();
|
||||
|
@ -83,6 +83,7 @@ public class RdbmsCentralRepoFactory {
|
||||
public boolean initializeDatabaseSchema() {
|
||||
|
||||
String createArtifactInstancesTableTemplate = getCreateArtifactInstancesTableTemplate(selectedPlatform);
|
||||
String createAccountInstancesTableTemplate = getCreateAccountInstancesTableTemplate(selectedPlatform);
|
||||
|
||||
String instancesCaseIdIdx = getAddCaseIdIndexTemplate();
|
||||
String instancesDatasourceIdIdx = getAddDataSourceIdIndexTemplate();
|
||||
@ -147,7 +148,13 @@ public class RdbmsCentralRepoFactory {
|
||||
reference_type_dbname = CentralRepoDbUtil.correlationTypeToReferenceTableName(type);
|
||||
instance_type_dbname = CentralRepoDbUtil.correlationTypeToInstanceTableName(type);
|
||||
|
||||
stmt.execute(String.format(createArtifactInstancesTableTemplate, instance_type_dbname, instance_type_dbname));
|
||||
// use the correct create table template, based on whether the attribute type represents an account or not.
|
||||
String createTableTemplate = (CentralRepoDbUtil.correlationAttribHasAnAccount(type))
|
||||
? createAccountInstancesTableTemplate
|
||||
: createArtifactInstancesTableTemplate;
|
||||
|
||||
stmt.execute(String.format(createTableTemplate, instance_type_dbname, instance_type_dbname));
|
||||
|
||||
stmt.execute(String.format(instancesCaseIdIdx, instance_type_dbname, instance_type_dbname));
|
||||
stmt.execute(String.format(instancesDatasourceIdIdx, instance_type_dbname, instance_type_dbname));
|
||||
stmt.execute(String.format(instancesValueIdx, instance_type_dbname, instance_type_dbname));
|
||||
@ -193,7 +200,7 @@ public class RdbmsCentralRepoFactory {
|
||||
|
||||
result = CentralRepoDbUtil.insertDefaultCorrelationTypes(conn)
|
||||
&& CentralRepoDbUtil.insertDefaultOrganization(conn) &&
|
||||
insertDefaultAccountsTablesContent(conn);
|
||||
RdbmsCentralRepoFactory.insertDefaultAccountsTablesContent(conn, selectedPlatform );
|
||||
// @TODO: uncomment when ready to create/populate persona tables
|
||||
// && insertDefaultPersonaTablesContent(conn);
|
||||
|
||||
@ -357,7 +364,7 @@ public class RdbmsCentralRepoFactory {
|
||||
+ ")";
|
||||
}
|
||||
/**
|
||||
* Get the template String for creating a new _instances table in a Sqlite
|
||||
* Get the template String for creating a new _instances table for non account artifacts in
|
||||
* central repository. %s will exist in the template where the name of the
|
||||
* new table will be added.
|
||||
*
|
||||
@ -365,6 +372,31 @@ public class RdbmsCentralRepoFactory {
|
||||
*/
|
||||
static String getCreateArtifactInstancesTableTemplate(CentralRepoPlatforms selectedPlatform) {
|
||||
// Each "%s" will be replaced with the relevant TYPE_instances table name.
|
||||
|
||||
return "CREATE TABLE IF NOT EXISTS %s ("
|
||||
+ getNumericPrimaryKeyClause("id", selectedPlatform)
|
||||
+ "case_id integer NOT NULL,"
|
||||
+ "data_source_id integer NOT NULL,"
|
||||
+ "value text NOT NULL,"
|
||||
+ "file_path text NOT NULL,"
|
||||
+ "known_status integer NOT NULL,"
|
||||
+ "comment text,"
|
||||
+ "file_obj_id " + getBigIntType(selectedPlatform) + " ,"
|
||||
+ "CONSTRAINT %s_multi_unique UNIQUE(data_source_id, value, file_path)" + getOnConflictIgnoreClause(selectedPlatform) + ","
|
||||
+ "foreign key (case_id) references cases(id) ON UPDATE SET NULL ON DELETE SET NULL,"
|
||||
+ "foreign key (data_source_id) references data_sources(id) ON UPDATE SET NULL ON DELETE SET NULL)";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the template String for creating a new _instances table for Accounts in
|
||||
* central repository. %s will exist in the template where the name of the
|
||||
* new table will be added.
|
||||
*
|
||||
* @return a String which is a template for creating a _instances table
|
||||
*/
|
||||
static String getCreateAccountInstancesTableTemplate(CentralRepoPlatforms selectedPlatform) {
|
||||
// Each "%s" will be replaced with the relevant TYPE_instances table name.
|
||||
|
||||
return "CREATE TABLE IF NOT EXISTS %s ("
|
||||
+ getNumericPrimaryKeyClause("id", selectedPlatform)
|
||||
+ "case_id integer NOT NULL,"
|
||||
@ -380,7 +412,7 @@ public class RdbmsCentralRepoFactory {
|
||||
+ "foreign key (case_id) references cases(id) ON UPDATE SET NULL ON DELETE SET NULL,"
|
||||
+ "foreign key (data_source_id) references data_sources(id) ON UPDATE SET NULL ON DELETE SET NULL)";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the statement String for creating a new data_sources table in a
|
||||
* Sqlite central repository.
|
||||
@ -494,6 +526,7 @@ public class RdbmsCentralRepoFactory {
|
||||
* on the selected CR platform/RDMBS.
|
||||
*
|
||||
* @param pkName name of primary key.
|
||||
* @param selectedPlatform The selected platform.
|
||||
*
|
||||
* @return SQL clause to be used in a Create table statement
|
||||
*/
|
||||
@ -766,37 +799,11 @@ public class RdbmsCentralRepoFactory {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inserts the default content in accounts related tables.
|
||||
*
|
||||
* @param conn Database connection to use.
|
||||
*
|
||||
* @return True if success, false otherwise.
|
||||
*/
|
||||
private boolean insertDefaultAccountsTablesContent(Connection conn) {
|
||||
|
||||
try (Statement stmt = conn.createStatement()) {
|
||||
// Populate the account_types table
|
||||
for (Account.Type type : Account.Type.PREDEFINED_ACCOUNT_TYPES) {
|
||||
int correlationTypeId = getCorrelationTypeIdForAccountType(conn, type);
|
||||
if (correlationTypeId > 0) {
|
||||
String sqlString = String.format("INSERT INTO account_types (type_name, display_name, correlation_type_id) VALUES ('%s', '%s', %d)" + getOnConflictDoNothingClause(selectedPlatform),
|
||||
type.getTypeName(), type.getDisplayName(), correlationTypeId);
|
||||
stmt.execute(sqlString);
|
||||
}
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
LOGGER.log(Level.SEVERE, String.format("Failed to populate default data in Accounts tables."), ex);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the default content in persona related tables.
|
||||
*
|
||||
* @param conn Database connection to use.
|
||||
* @param selectedPlatform The selected platform.
|
||||
*
|
||||
* @return True if success, false otherwise.
|
||||
*/
|
||||
@ -838,11 +845,13 @@ public class RdbmsCentralRepoFactory {
|
||||
|
||||
// Populate the account_types table
|
||||
for (Account.Type type : Account.Type.PREDEFINED_ACCOUNT_TYPES) {
|
||||
int correlationTypeId = getCorrelationTypeIdForAccountType(conn, type);
|
||||
if (correlationTypeId > 0) {
|
||||
String sqlString = String.format("INSERT INTO account_types (type_name, display_name, correlation_type_id) VALUES ('%s', '%s', %d)" + getOnConflictDoNothingClause(selectedPlatform),
|
||||
type.getTypeName(), type.getDisplayName(), correlationTypeId);
|
||||
stmt.execute(sqlString);
|
||||
if (type != Account.Type.DEVICE) {
|
||||
int correlationTypeId = getCorrelationTypeIdForAccountType(conn, type);
|
||||
if (correlationTypeId > 0) {
|
||||
String sqlString = String.format("INSERT INTO account_types (type_name, display_name, correlation_type_id) VALUES ('%s', '%s', %d)" + getOnConflictDoNothingClause(selectedPlatform),
|
||||
type.getTypeName(), type.getDisplayName(), correlationTypeId);
|
||||
stmt.execute(sqlString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,7 @@ import java.awt.Cursor;
|
||||
import java.awt.HeadlessException;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
@ -31,14 +32,15 @@ import java.util.logging.Level;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JFileChooser;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.ListCellRenderer;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import javax.swing.filechooser.FileFilter;
|
||||
import javax.swing.plaf.basic.BasicComboBoxRenderer;
|
||||
import org.netbeans.spi.options.OptionsPanelController;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
@ -65,18 +67,18 @@ public class EamDbSettingsDialog extends JDialog {
|
||||
/**
|
||||
* This class handles displaying and rendering drop down menu for database choices in central repo.
|
||||
*/
|
||||
private class DbChoiceRenderer extends BasicComboBoxRenderer {
|
||||
private class DbChoiceRenderer extends JLabel implements ListCellRenderer<CentralRepoDbChoice>, Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public Component getListCellRendererComponent(JList list, Object value,
|
||||
@Override
|
||||
public Component getListCellRendererComponent(
|
||||
JList<? extends CentralRepoDbChoice> list, CentralRepoDbChoice value,
|
||||
int index, boolean isSelected, boolean cellHasFocus) {
|
||||
|
||||
CentralRepoDbChoice item = (CentralRepoDbChoice) value;
|
||||
|
||||
// disable cell if it is the db connection from multi user settings
|
||||
// and that option is not enabled in multi user settings
|
||||
setText(item.getTitle());
|
||||
setEnabled(isDbChoiceSelectable(item));
|
||||
setText(value.getTitle());
|
||||
setEnabled(isDbChoiceSelectable(value));
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@ -135,7 +137,7 @@ public class EamDbSettingsDialog extends JDialog {
|
||||
valid();
|
||||
display();
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void setupDbChoice(CentralRepoDbChoice initialMenuItem) {
|
||||
// setup initially selected item
|
||||
@ -144,10 +146,8 @@ public class EamDbSettingsDialog extends JDialog {
|
||||
manager.getSelectedDbChoice() :
|
||||
CentralRepoDbChoice.DB_CHOICES[0] :
|
||||
initialMenuItem;
|
||||
|
||||
// set the renderer so item is unselectable if inappropriate
|
||||
|
||||
cbDatabaseType.setRenderer(DB_CHOICE_RENDERER);
|
||||
|
||||
changeDbSelection(toSelect);
|
||||
}
|
||||
|
||||
|
@ -731,9 +731,9 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
|
||||
enableButtonSubComponents(cbUseCentralRepo.isSelected());
|
||||
} else {
|
||||
load();
|
||||
enableDatabaseConfigureButton(cbUseCentralRepo.isSelected() && !caseIsOpen);
|
||||
}
|
||||
|
||||
enableDatabaseConfigureButton(cbUseCentralRepo.isSelected() && !caseIsOpen);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -19,6 +19,7 @@
|
||||
package org.sleuthkit.autopsy.communications.relationships;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.openide.nodes.Sheet;
|
||||
import org.sleuthkit.autopsy.communications.Utils;
|
||||
import static org.sleuthkit.autopsy.communications.relationships.RelationshipsNodeUtilities.getAttributeDisplayString;
|
||||
@ -67,14 +68,6 @@ final class CallLogNode extends BlackboardArtifactNode {
|
||||
return sheet;
|
||||
}
|
||||
|
||||
String phoneNumber = getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_FROM);
|
||||
if(phoneNumber == null || phoneNumber.isEmpty()) {
|
||||
phoneNumber = getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_TO);
|
||||
}
|
||||
if(phoneNumber == null || phoneNumber.isEmpty()) {
|
||||
phoneNumber = getAttributeDisplayString(artifact, TSK_PHONE_NUMBER);
|
||||
}
|
||||
|
||||
long duration = -1;
|
||||
try{
|
||||
duration = getCallDuration(artifact);
|
||||
@ -84,7 +77,7 @@ final class CallLogNode extends BlackboardArtifactNode {
|
||||
|
||||
sheetSet.put(createNode(TSK_DATETIME_START, artifact));
|
||||
sheetSet.put(createNode(TSK_DIRECTION, artifact));
|
||||
sheetSet.put(new NodeProperty<>(TSK_PHONE_NUMBER.getLabel(), TSK_PHONE_NUMBER.getDisplayName(), "", phoneNumber));
|
||||
sheetSet.put(new NodeProperty<>(TSK_PHONE_NUMBER.getLabel(), TSK_PHONE_NUMBER.getDisplayName(), "", getPhoneNumber(artifact)));
|
||||
if(duration != -1) {
|
||||
sheetSet.put(new NodeProperty<>("duration", "Duration", "", Long.toString(duration)));
|
||||
}
|
||||
@ -107,6 +100,59 @@ final class CallLogNode extends BlackboardArtifactNode {
|
||||
return endAttribute.getValueLong() - startAttribute.getValueLong();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the phone number to display in the To/From column. The number is
|
||||
* picked from one of the 3 possible phone number attributes, based on the
|
||||
* direction of the call.
|
||||
*
|
||||
* @param artifact Call log artifact.
|
||||
*
|
||||
* @return Phone number to display.
|
||||
*/
|
||||
private String getPhoneNumber(BlackboardArtifact artifact) {
|
||||
String direction = getAttributeDisplayString(artifact, TSK_DIRECTION);
|
||||
|
||||
String phoneNumberToReturn;
|
||||
String fromPhoneNumber = getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_FROM);
|
||||
String toPhoneNumber = getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_TO);
|
||||
String phoneNumber = getAttributeDisplayString(artifact, TSK_PHONE_NUMBER);
|
||||
switch (direction.toLowerCase()) {
|
||||
case "incoming": // NON-NLS
|
||||
phoneNumberToReturn = getFirstNonBlank(fromPhoneNumber, phoneNumber, toPhoneNumber);
|
||||
break;
|
||||
case "outgoing": // NON-NLS
|
||||
phoneNumberToReturn = getFirstNonBlank(toPhoneNumber, phoneNumber, fromPhoneNumber);
|
||||
break;
|
||||
default:
|
||||
phoneNumberToReturn = getFirstNonBlank(toPhoneNumber, fromPhoneNumber, phoneNumber );
|
||||
break;
|
||||
}
|
||||
|
||||
return phoneNumberToReturn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the given string arguments in order and returns the first non blank string.
|
||||
* Returns a blank string if all the input strings are blank.
|
||||
*
|
||||
* @param string1 First string to check
|
||||
* @param string2 Second string to check
|
||||
* @param string3 Third string to check
|
||||
*
|
||||
* @retunr first non blank string if there is one, blank string otherwise.
|
||||
*
|
||||
*/
|
||||
private String getFirstNonBlank(String string1, String string2, String string3 ) {
|
||||
|
||||
if (!StringUtils.isBlank(string1)) {
|
||||
return string1;
|
||||
} else if (!StringUtils.isBlank(string2)) {
|
||||
return string2;
|
||||
} else if (!StringUtils.isBlank(string3)) {
|
||||
return string3;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
/**
|
||||
* Circumvent DataResultFilterNode's slightly odd delegation to
|
||||
* BlackboardArtifactNode.getSourceName().
|
||||
|
@ -41,6 +41,7 @@ MediaFileViewer.AccessibleContext.accessibleDescription=
|
||||
MediaFileViewer.title=Media
|
||||
MediaFileViewer.toolTip=Displays supported multimedia files (images, videos, audio)
|
||||
MediaPlayerPanel.noSupport=File not supported.
|
||||
MediaPlayerPanel.playbackDisabled=A problem was encountered with the video and audio playback service. Video and audio playback will be disabled for the remainder of the session.
|
||||
MediaPlayerPanel.timeFormat=%02d:%02d:%02d
|
||||
MediaPlayerPanel.unknownTime=Unknown
|
||||
MediaViewImagePanel.createTagOption=Create
|
||||
@ -168,7 +169,7 @@ MediaPlayerPanel.playBackSpeedLabel.text=Speed:
|
||||
SQLiteViewer.readTable.errorText=Error getting rows for table: {0}
|
||||
# {0} - tableName
|
||||
SQLiteViewer.selectTable.errorText=Error getting row count for table: {0}
|
||||
TextTranslatableComponent.setPanelContent.onSetContentError=Unable to display text at this time.
|
||||
TextTranslatableComponent.setTranslated.onTranslateError=Unable to translate text at this time.
|
||||
TranslatablePanel.comboBoxOption.originalText=Original Text
|
||||
TranslatablePanel.comboBoxOption.translatedText=Translated Text
|
||||
# {0} - exception message
|
||||
TranslatablePanel.onSetContentError.text=There was an error displaying the text: {0}
|
||||
|
@ -194,6 +194,6 @@ class MediaFileViewer extends javax.swing.JPanel implements FileTypeViewer {
|
||||
|
||||
@Override
|
||||
public boolean isSupported(AbstractFile file){
|
||||
return true;
|
||||
return mediaPlayerPanel.isSupported(file) || imagePanel.isSupported(file);
|
||||
}
|
||||
}
|
||||
|
@ -63,7 +63,7 @@
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" max="-2" attributes="0">
|
||||
<Component id="progressLabel" max="32767" attributes="0"/>
|
||||
<Component id="playBackPanel" pref="0" max="32767" attributes="0"/>
|
||||
<Component id="playBackPanel" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace min="-2" pref="10" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
@ -129,12 +129,6 @@
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MediaPlayerPanel.playButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[53, 29]"/>
|
||||
</Property>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[53, 29]"/>
|
||||
</Property>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[49, 29]"/>
|
||||
</Property>
|
||||
|
@ -74,7 +74,9 @@ import org.freedesktop.gstreamer.Format;
|
||||
import org.freedesktop.gstreamer.GstException;
|
||||
import org.freedesktop.gstreamer.event.SeekFlags;
|
||||
import org.freedesktop.gstreamer.event.SeekType;
|
||||
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
|
||||
import org.sleuthkit.autopsy.contentviewers.utils.GstLoader;
|
||||
import org.sleuthkit.autopsy.contentviewers.utils.GstLoader.GstStatus;
|
||||
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
||||
|
||||
/**
|
||||
* This is a video player that is part of the Media View layered pane. It uses
|
||||
@ -213,6 +215,8 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
|
||||
//and the TrackListener on the slider itself.
|
||||
private final Semaphore sliderLock;
|
||||
|
||||
private static volatile boolean IS_GST_ENABLED = true;
|
||||
|
||||
/**
|
||||
* Creates a new MediaPlayerPanel
|
||||
*/
|
||||
@ -222,18 +226,10 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
|
||||
//True for fairness. In other words,
|
||||
//acquire() calls are processed in order of invocation.
|
||||
sliderLock = new Semaphore(1, true);
|
||||
|
||||
/**
|
||||
* See JIRA-5888 for details. Initializing gstreamer here is more stable
|
||||
* on Windows.
|
||||
*/
|
||||
if (PlatformUtil.isWindowsOS()) {
|
||||
Gst.init();
|
||||
}
|
||||
}
|
||||
|
||||
private void customizeComponents() {
|
||||
progressSlider.setEnabled(false); // disable slider; enable after user plays vid
|
||||
enableComponents(false);
|
||||
progressSlider.setMinimum(0);
|
||||
progressSlider.setMaximum(PROGRESS_SLIDER_SIZE);
|
||||
progressSlider.setValue(0);
|
||||
@ -390,13 +386,16 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
|
||||
}
|
||||
timer.stop();
|
||||
if (gstPlayBin != null) {
|
||||
gstPlayBin.stop();
|
||||
gstPlayBin.getBus().disconnect(endOfStreamListener);
|
||||
gstPlayBin.getBus().disconnect(stateChangeListener);
|
||||
gstPlayBin.getBus().disconnect(errorListener);
|
||||
gstPlayBin.dispose();
|
||||
fxAppSink.clear();
|
||||
gstPlayBin = null;
|
||||
Gst.getExecutor().submit(() -> {
|
||||
gstPlayBin.stop();
|
||||
gstPlayBin.getBus().disconnect(endOfStreamListener);
|
||||
gstPlayBin.getBus().disconnect(stateChangeListener);
|
||||
gstPlayBin.getBus().disconnect(errorListener);
|
||||
gstPlayBin.getBus().dispose();
|
||||
gstPlayBin.dispose();
|
||||
fxAppSink.clear();
|
||||
gstPlayBin = null;
|
||||
});
|
||||
}
|
||||
videoPanel.removeAll();
|
||||
resetComponents();
|
||||
@ -425,6 +424,10 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
|
||||
|
||||
@Override
|
||||
public boolean isSupported(AbstractFile file) {
|
||||
if (!IS_GST_ENABLED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String extension = file.getNameExtension();
|
||||
/**
|
||||
* Although it seems too restrictive, requiring both a supported
|
||||
@ -542,6 +545,11 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
|
||||
/*
|
||||
* Initialize the playback components if the extraction was successful.
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"MediaPlayerPanel.playbackDisabled=A problem was encountered with"
|
||||
+ " the video and audio playback service. Video and audio "
|
||||
+ "playback will be disabled for the remainder of the session."
|
||||
})
|
||||
@Override
|
||||
protected void done() {
|
||||
try {
|
||||
@ -551,44 +559,49 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize Gstreamer. It is safe to call this for every file.
|
||||
// It was moved here from the constructor because having it happen
|
||||
// earlier resulted in conflicts on Linux. See JIRA-5888.
|
||||
if (!PlatformUtil.isWindowsOS()) {
|
||||
Gst.init();
|
||||
GstStatus loadStatus = GstLoader.tryLoad();
|
||||
if (loadStatus == GstStatus.FAILURE) {
|
||||
MessageNotifyUtil.Message.error(Bundle.MediaPlayerPanel_playbackDisabled());
|
||||
|
||||
// This will disable the panel for future use.
|
||||
IS_GST_ENABLED = false;
|
||||
return;
|
||||
}
|
||||
|
||||
//Video is ready for playback. Create new components
|
||||
gstPlayBin = new PlayBin("VideoPlayer", tempFile.toURI());
|
||||
//Configure event handling
|
||||
if (gstPlayBin != null) {
|
||||
Gst.getExecutor().submit(() -> {
|
||||
//Video is ready for playback. Create new components
|
||||
gstPlayBin = new PlayBin("VideoPlayer", tempFile.toURI());
|
||||
//Configure event handling
|
||||
Bus playBinBus = gstPlayBin.getBus();
|
||||
playBinBus.connect(endOfStreamListener);
|
||||
playBinBus.connect(stateChangeListener);
|
||||
playBinBus.connect(errorListener);
|
||||
}
|
||||
|
||||
if (this.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
if (this.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
JFXPanel fxPanel = new JFXPanel();
|
||||
videoPanel.removeAll();
|
||||
videoPanel.setLayout(new BoxLayout(videoPanel, BoxLayout.Y_AXIS));
|
||||
videoPanel.add(fxPanel);
|
||||
fxAppSink = new JavaFxAppSink("JavaFxAppSink", fxPanel);
|
||||
if (gstPlayBin != null) {
|
||||
gstPlayBin.setVideoSink(fxAppSink);
|
||||
}
|
||||
if (this.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
if (gstPlayBin != null) {
|
||||
gstPlayBin.setVolume((audioSlider.getValue() * 2.0) / 100.0);
|
||||
gstPlayBin.pause();
|
||||
}
|
||||
timer.start();
|
||||
enableComponents(true);
|
||||
JFXPanel fxPanel = new JFXPanel();
|
||||
videoPanel.removeAll();
|
||||
videoPanel.setLayout(new BoxLayout(videoPanel, BoxLayout.Y_AXIS));
|
||||
videoPanel.add(fxPanel);
|
||||
fxAppSink = new JavaFxAppSink("JavaFxAppSink", fxPanel);
|
||||
if (gstPlayBin != null) {
|
||||
gstPlayBin.setVideoSink(fxAppSink);
|
||||
}
|
||||
if (this.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
if (gstPlayBin != null) {
|
||||
gstPlayBin.setVolume((audioSlider.getValue() * 2.0) / 100.0);
|
||||
gstPlayBin.pause();
|
||||
}
|
||||
|
||||
timer.start();
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
enableComponents(true);
|
||||
});
|
||||
});
|
||||
} catch (CancellationException ex) {
|
||||
logger.log(Level.INFO, "Media buffering was canceled."); //NON-NLS
|
||||
} catch (InterruptedException ex) {
|
||||
@ -607,23 +620,29 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
if (!progressSlider.getValueIsAdjusting() && gstPlayBin != null) {
|
||||
sliderLock.acquireUninterruptibly();
|
||||
long position = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
|
||||
long duration = gstPlayBin.queryDuration(TimeUnit.NANOSECONDS);
|
||||
/**
|
||||
* Duration may not be known until there is video data in the
|
||||
* pipeline. We start this updater when data-flow has just been
|
||||
* initiated so buffering may still be in progress.
|
||||
*/
|
||||
if (duration >= 0 && position >= 0) {
|
||||
double relativePosition = (double) position / duration;
|
||||
progressSlider.setValue((int) (relativePosition * PROGRESS_SLIDER_SIZE));
|
||||
}
|
||||
Gst.getExecutor().submit(() -> {
|
||||
try {
|
||||
sliderLock.acquireUninterruptibly();
|
||||
long position = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
|
||||
long duration = gstPlayBin.queryDuration(TimeUnit.NANOSECONDS);
|
||||
/**
|
||||
* Duration may not be known until there is video data
|
||||
* in the pipeline. We start this updater when data-flow
|
||||
* has just been initiated so buffering may still be in
|
||||
* progress.
|
||||
*/
|
||||
if (duration >= 0 && position >= 0) {
|
||||
double relativePosition = (double) position / duration;
|
||||
progressSlider.setValue((int) (relativePosition * PROGRESS_SLIDER_SIZE));
|
||||
}
|
||||
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
updateTimeLabel(position, duration);
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
updateTimeLabel(position, duration);
|
||||
});
|
||||
} finally {
|
||||
sliderLock.release();
|
||||
}
|
||||
});
|
||||
sliderLock.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -643,7 +662,7 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
|
||||
* thumb at the given width and height. It also paints the track blue as
|
||||
* the thumb progresses.
|
||||
*
|
||||
* @param slider JSlider component
|
||||
* @param slider JSlider component
|
||||
* @param thumbDimension
|
||||
*/
|
||||
public CircularJSliderUI(JSlider slider, Dimension thumbDimension) {
|
||||
@ -991,96 +1010,104 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
private void rewindButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_rewindButtonActionPerformed
|
||||
if (gstPlayBin != null) {
|
||||
long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
|
||||
//Skip 30 seconds.
|
||||
long rewindDelta = TimeUnit.NANOSECONDS.convert(SKIP_IN_SECONDS, TimeUnit.SECONDS);
|
||||
//Ensure new video position is within bounds
|
||||
long newTime = Math.max(currentTime - rewindDelta, 0);
|
||||
double playBackRate = getPlayBackRate();
|
||||
gstPlayBin.seek(playBackRate,
|
||||
Format.TIME,
|
||||
//FLUSH - flushes the pipeline
|
||||
//ACCURATE - video will seek exactly to the position requested
|
||||
EnumSet.of(SeekFlags.FLUSH, SeekFlags.ACCURATE),
|
||||
//Set the start position to newTime
|
||||
SeekType.SET, newTime,
|
||||
//Do nothing for the end position
|
||||
SeekType.NONE, -1);
|
||||
}
|
||||
}//GEN-LAST:event_rewindButtonActionPerformed
|
||||
|
||||
private void fastForwardButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_fastForwardButtonActionPerformed
|
||||
if (gstPlayBin != null) {
|
||||
long duration = gstPlayBin.queryDuration(TimeUnit.NANOSECONDS);
|
||||
long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
|
||||
//Skip 30 seconds.
|
||||
long fastForwardDelta = TimeUnit.NANOSECONDS.convert(SKIP_IN_SECONDS, TimeUnit.SECONDS);
|
||||
//Don't allow skipping within 2 seconds of video ending. Skipping right to
|
||||
//the end causes undefined behavior for some gstreamer plugins.
|
||||
long twoSecondsInNano = TimeUnit.NANOSECONDS.convert(2, TimeUnit.SECONDS);
|
||||
if ((duration - currentTime) <= twoSecondsInNano) {
|
||||
return;
|
||||
}
|
||||
|
||||
long newTime;
|
||||
if (currentTime + fastForwardDelta >= duration) {
|
||||
//If there are less than 30 seconds left, only fast forward to the midpoint.
|
||||
newTime = currentTime + (duration - currentTime) / 2;
|
||||
} else {
|
||||
newTime = currentTime + fastForwardDelta;
|
||||
}
|
||||
|
||||
double playBackRate = getPlayBackRate();
|
||||
gstPlayBin.seek(playBackRate,
|
||||
Format.TIME,
|
||||
//FLUSH - flushes the pipeline
|
||||
//ACCURATE - video will seek exactly to the position requested
|
||||
EnumSet.of(SeekFlags.FLUSH, SeekFlags.ACCURATE),
|
||||
//Set the start position to newTime
|
||||
SeekType.SET, newTime,
|
||||
//Do nothing for the end position
|
||||
SeekType.NONE, -1);
|
||||
}
|
||||
}//GEN-LAST:event_fastForwardButtonActionPerformed
|
||||
|
||||
private void playButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_playButtonActionPerformed
|
||||
if (gstPlayBin != null) {
|
||||
if (gstPlayBin.isPlaying()) {
|
||||
gstPlayBin.pause();
|
||||
} else {
|
||||
double playBackRate = getPlayBackRate();
|
||||
Gst.getExecutor().submit(() -> {
|
||||
if (gstPlayBin != null) {
|
||||
long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
|
||||
//Set playback rate before play.
|
||||
//Skip 30 seconds.
|
||||
long rewindDelta = TimeUnit.NANOSECONDS.convert(SKIP_IN_SECONDS, TimeUnit.SECONDS);
|
||||
//Ensure new video position is within bounds
|
||||
long newTime = Math.max(currentTime - rewindDelta, 0);
|
||||
double playBackRate = getPlayBackRate();
|
||||
gstPlayBin.seek(playBackRate,
|
||||
Format.TIME,
|
||||
//FLUSH - flushes the pipeline
|
||||
//ACCURATE - video will seek exactly to the position requested
|
||||
EnumSet.of(SeekFlags.FLUSH, SeekFlags.ACCURATE),
|
||||
//Set the start position to newTime
|
||||
SeekType.SET, currentTime,
|
||||
SeekType.SET, newTime,
|
||||
//Do nothing for the end position
|
||||
SeekType.NONE, -1);
|
||||
gstPlayBin.play();
|
||||
}
|
||||
}
|
||||
}//GEN-LAST:event_playButtonActionPerformed
|
||||
});
|
||||
}//GEN-LAST:event_rewindButtonActionPerformed
|
||||
|
||||
private void playBackSpeedComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_playBackSpeedComboBoxActionPerformed
|
||||
if (gstPlayBin != null) {
|
||||
double playBackRate = getPlayBackRate();
|
||||
long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
|
||||
gstPlayBin.seek(playBackRate,
|
||||
Format.TIME,
|
||||
//FLUSH - flushes the pipeline
|
||||
//ACCURATE - video will seek exactly to the position requested
|
||||
EnumSet.of(SeekFlags.FLUSH, SeekFlags.ACCURATE),
|
||||
//Set the position to the currentTime, we are only adjusting the
|
||||
//playback rate.
|
||||
SeekType.SET, currentTime,
|
||||
SeekType.NONE, 0);
|
||||
}
|
||||
}//GEN-LAST:event_playBackSpeedComboBoxActionPerformed
|
||||
private void fastForwardButtonActionPerformed(java.awt.event.ActionEvent evt) {
|
||||
Gst.getExecutor().submit(() -> {
|
||||
if (gstPlayBin != null) {
|
||||
long duration = gstPlayBin.queryDuration(TimeUnit.NANOSECONDS);
|
||||
long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
|
||||
//Skip 30 seconds.
|
||||
long fastForwardDelta = TimeUnit.NANOSECONDS.convert(SKIP_IN_SECONDS, TimeUnit.SECONDS);
|
||||
//Don't allow skipping within 2 seconds of video ending. Skipping right to
|
||||
//the end causes undefined behavior for some gstreamer plugins.
|
||||
long twoSecondsInNano = TimeUnit.NANOSECONDS.convert(2, TimeUnit.SECONDS);
|
||||
if ((duration - currentTime) <= twoSecondsInNano) {
|
||||
return;
|
||||
}
|
||||
|
||||
long newTime;
|
||||
if (currentTime + fastForwardDelta >= duration) {
|
||||
//If there are less than 30 seconds left, only fast forward to the midpoint.
|
||||
newTime = currentTime + (duration - currentTime) / 2;
|
||||
} else {
|
||||
newTime = currentTime + fastForwardDelta;
|
||||
}
|
||||
|
||||
double playBackRate = getPlayBackRate();
|
||||
gstPlayBin.seek(playBackRate,
|
||||
Format.TIME,
|
||||
//FLUSH - flushes the pipeline
|
||||
//ACCURATE - video will seek exactly to the position requested
|
||||
EnumSet.of(SeekFlags.FLUSH, SeekFlags.ACCURATE),
|
||||
//Set the start position to newTime
|
||||
SeekType.SET, newTime,
|
||||
//Do nothing for the end position
|
||||
SeekType.NONE, -1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void playButtonActionPerformed(java.awt.event.ActionEvent evt) {
|
||||
Gst.getExecutor().submit(() -> {
|
||||
if (gstPlayBin != null) {
|
||||
if (gstPlayBin.isPlaying()) {
|
||||
gstPlayBin.pause();
|
||||
} else {
|
||||
double playBackRate = getPlayBackRate();
|
||||
long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
|
||||
//Set playback rate before play.
|
||||
gstPlayBin.seek(playBackRate,
|
||||
Format.TIME,
|
||||
//FLUSH - flushes the pipeline
|
||||
//ACCURATE - video will seek exactly to the position requested
|
||||
EnumSet.of(SeekFlags.FLUSH, SeekFlags.ACCURATE),
|
||||
//Set the start position to newTime
|
||||
SeekType.SET, currentTime,
|
||||
//Do nothing for the end position
|
||||
SeekType.NONE, -1);
|
||||
gstPlayBin.play();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void playBackSpeedComboBoxActionPerformed(java.awt.event.ActionEvent evt) {
|
||||
Gst.getExecutor().submit(() -> {
|
||||
if (gstPlayBin != null) {
|
||||
double playBackRate = getPlayBackRate();
|
||||
long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
|
||||
gstPlayBin.seek(playBackRate,
|
||||
Format.TIME,
|
||||
//FLUSH - flushes the pipeline
|
||||
//ACCURATE - video will seek exactly to the position requested
|
||||
EnumSet.of(SeekFlags.FLUSH, SeekFlags.ACCURATE),
|
||||
//Set the position to the currentTime, we are only adjusting the
|
||||
//playback rate.
|
||||
SeekType.SET, currentTime,
|
||||
SeekType.NONE, 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JLabel VolumeIcon;
|
||||
|
74
Core/src/org/sleuthkit/autopsy/contentviewers/utils/GstLoader.java
Executable file
74
Core/src/org/sleuthkit/autopsy/contentviewers/utils/GstLoader.java
Executable file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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.contentviewers.utils;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import org.freedesktop.gstreamer.Gst;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
|
||||
/**
|
||||
* A utility class that loads the gstreamer bindings.
|
||||
*/
|
||||
public final class GstLoader {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(GstLoader.class.getName());
|
||||
private static GstStatus status;
|
||||
|
||||
/**
|
||||
* Attempts to load the gstreamer bindings. Only one attempt will be
|
||||
* performed per Autopsy process. Clients should not attempt to interact
|
||||
* with the gstreamer bindings unless the load was successful.
|
||||
*
|
||||
* @return Status - SUCCESS or FAILURE
|
||||
*/
|
||||
public synchronized static GstStatus tryLoad() {
|
||||
// Null is our 'unknown' status. Prior to the first call, the status
|
||||
// is unknown.
|
||||
if (status != null) {
|
||||
return status;
|
||||
}
|
||||
|
||||
try {
|
||||
// Setting the following property causes the GST
|
||||
// Java bindings to call dispose() on the GST
|
||||
// service thread instead of running it in the GST
|
||||
// Native Object Reaper thread.
|
||||
System.setProperty("glib.reapOnEDT", "true");
|
||||
Gst.init();
|
||||
status = GstStatus.SUCCESS;
|
||||
} catch (Throwable ex) {
|
||||
status = GstStatus.FAILURE;
|
||||
logger.log(Level.WARNING, "Failed to load gsteamer bindings", ex);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* The various init statuses that tryLoad can return.
|
||||
*/
|
||||
public enum GstStatus {
|
||||
SUCCESS, FAILURE
|
||||
}
|
||||
|
||||
private GstLoader() {
|
||||
|
||||
}
|
||||
}
|
@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.datamodel;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
@ -28,7 +29,6 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.openide.nodes.Sheet;
|
||||
@ -66,6 +66,7 @@ import org.sleuthkit.datamodel.Tag;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
|
||||
import org.sleuthkit.autopsy.texttranslation.utils.FileNameTranslationUtil;
|
||||
|
||||
/**
|
||||
* An abstract node that encapsulates AbstractFile data
|
||||
@ -94,15 +95,15 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
|
||||
IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, weakPcl);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
//See JIRA-5971
|
||||
//Attempt to cache file path during construction of this UI component.
|
||||
this.content.getUniquePath();
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Failed attempt to cache the "
|
||||
+ "unique path of the abstract file instance. Name: %s (objID=%d)",
|
||||
this.content.getName(), this.content.getId()), ex);
|
||||
+ "unique path of the abstract file instance. Name: %s (objID=%d)",
|
||||
this.content.getName(), this.content.getId()), ex);
|
||||
}
|
||||
|
||||
if (TextTranslationService.getInstance().hasProvider() && UserPreferences.displayTranslatedFileNames()) {
|
||||
@ -490,39 +491,18 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates this nodes content name. Doesn't attempt translation if the
|
||||
* name is in english or if there is now translation service available.
|
||||
* Translates the name of the file this node represents. An empty string
|
||||
* will be returned if the translation fails for any reason.
|
||||
*
|
||||
* @return The translated file name or the empty string.
|
||||
*/
|
||||
String getTranslatedFileName() {
|
||||
//If already in complete English, don't translate.
|
||||
if (content.getName().matches("^\\p{ASCII}+$")) {
|
||||
try {
|
||||
return FileNameTranslationUtil.translate(content.getName());
|
||||
} catch (NoServiceProviderException | TranslationException ex) {
|
||||
logger.log(Level.WARNING, MessageFormat.format("Error translating file name (objID={0}))", content.getId()), ex);
|
||||
return "";
|
||||
}
|
||||
TextTranslationService tts = TextTranslationService.getInstance();
|
||||
if (tts.hasProvider()) {
|
||||
//Seperate out the base and ext from the contents file name.
|
||||
String base = FilenameUtils.getBaseName(content.getName());
|
||||
try {
|
||||
String translation = tts.translate(base);
|
||||
String ext = FilenameUtils.getExtension(content.getName());
|
||||
|
||||
//If we have no extension, then we shouldn't add the .
|
||||
String extensionDelimiter = (ext.isEmpty()) ? "" : ".";
|
||||
|
||||
//Talk directly to this nodes pcl, fire an update when the translation
|
||||
//is complete.
|
||||
if (!translation.isEmpty()) {
|
||||
return translation + extensionDelimiter + ext;
|
||||
}
|
||||
} catch (NoServiceProviderException noServiceEx) {
|
||||
logger.log(Level.WARNING, "Translate unsuccessful because no TextTranslator "
|
||||
+ "implementation was provided.", noServiceEx.getMessage());
|
||||
} catch (TranslationException noTranslationEx) {
|
||||
logger.log(Level.WARNING, "Could not successfully translate file name "
|
||||
+ content.getName(), noTranslationEx.getMessage());
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -76,6 +76,9 @@ import org.sleuthkit.datamodel.Tag;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
|
||||
import static org.sleuthkit.autopsy.datamodel.AbstractContentNode.NO_DESCR;
|
||||
import org.sleuthkit.autopsy.texttranslation.TextTranslationService;
|
||||
import org.sleuthkit.autopsy.datamodel.utils.FileNameTransTask;
|
||||
|
||||
/**
|
||||
* A BlackboardArtifactNode is an AbstractNode implementation that can be used
|
||||
@ -124,7 +127,8 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
};
|
||||
|
||||
private final BlackboardArtifact artifact;
|
||||
private Content srcContent; // May be null.
|
||||
private Content srcContent;
|
||||
private volatile String translatedSourceName;
|
||||
|
||||
/*
|
||||
* A method has been provided to allow the injection of properties into this
|
||||
@ -133,7 +137,7 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
*/
|
||||
private List<NodeProperty<? extends Object>> customProperties;
|
||||
|
||||
private final PropertyChangeListener appEventListener = new PropertyChangeListener() {
|
||||
private final PropertyChangeListener listener = new PropertyChangeListener() {
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
String eventType = evt.getPropertyName();
|
||||
@ -148,25 +152,19 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
updateSheet();
|
||||
}
|
||||
} else if (eventType.equals(Case.Events.CONTENT_TAG_ADDED.toString())) {
|
||||
if (srcContent != null) {
|
||||
ContentTagAddedEvent event = (ContentTagAddedEvent) evt;
|
||||
if (event.getAddedTag().getContent().equals(srcContent)) {
|
||||
updateSheet();
|
||||
}
|
||||
ContentTagAddedEvent event = (ContentTagAddedEvent) evt;
|
||||
if (event.getAddedTag().getContent().equals(srcContent)) {
|
||||
updateSheet();
|
||||
}
|
||||
} else if (eventType.equals(Case.Events.CONTENT_TAG_DELETED.toString())) {
|
||||
if (srcContent != null) {
|
||||
ContentTagDeletedEvent event = (ContentTagDeletedEvent) evt;
|
||||
if (event.getDeletedTagInfo().getContentID() == srcContent.getId()) {
|
||||
updateSheet();
|
||||
}
|
||||
ContentTagDeletedEvent event = (ContentTagDeletedEvent) evt;
|
||||
if (event.getDeletedTagInfo().getContentID() == srcContent.getId()) {
|
||||
updateSheet();
|
||||
}
|
||||
} else if (eventType.equals(Case.Events.CR_COMMENT_CHANGED.toString())) {
|
||||
if (srcContent != null) {
|
||||
CommentChangedEvent event = (CommentChangedEvent) evt;
|
||||
if (event.getContentID() == srcContent.getId()) {
|
||||
updateSheet();
|
||||
}
|
||||
CommentChangedEvent event = (CommentChangedEvent) evt;
|
||||
if (event.getContentID() == srcContent.getId()) {
|
||||
updateSheet();
|
||||
}
|
||||
} else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
|
||||
if (evt.getNewValue() == null) {
|
||||
@ -179,14 +177,41 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
} else if (eventType.equals(NodeSpecificEvents.SCO_AVAILABLE.toString()) && !UserPreferences.getHideSCOColumns()) {
|
||||
SCOData scoData = (SCOData) evt.getNewValue();
|
||||
if (scoData.getScoreAndDescription() != null) {
|
||||
updateSheet(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_score_name(), Bundle.BlackboardArtifactNode_createSheet_score_displayName(), scoData.getScoreAndDescription().getRight(), scoData.getScoreAndDescription().getLeft()));
|
||||
updateSheet(new NodeProperty<>(
|
||||
Bundle.BlackboardArtifactNode_createSheet_score_name(),
|
||||
Bundle.BlackboardArtifactNode_createSheet_score_displayName(),
|
||||
scoData.getScoreAndDescription().getRight(),
|
||||
scoData.getScoreAndDescription().getLeft()));
|
||||
}
|
||||
if (scoData.getComment() != null) {
|
||||
updateSheet(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_comment_name(), Bundle.BlackboardArtifactNode_createSheet_comment_displayName(), NO_DESCR, scoData.getComment()));
|
||||
updateSheet(new NodeProperty<>(
|
||||
Bundle.BlackboardArtifactNode_createSheet_comment_name(),
|
||||
Bundle.BlackboardArtifactNode_createSheet_comment_displayName(),
|
||||
NO_DESCR, scoData.getComment()));
|
||||
}
|
||||
if (scoData.getCountAndDescription() != null) {
|
||||
updateSheet(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_count_name(), Bundle.BlackboardArtifactNode_createSheet_count_displayName(), scoData.getCountAndDescription().getRight(), scoData.getCountAndDescription().getLeft()));
|
||||
updateSheet(new NodeProperty<>(
|
||||
Bundle.BlackboardArtifactNode_createSheet_count_name(),
|
||||
Bundle.BlackboardArtifactNode_createSheet_count_displayName(),
|
||||
scoData.getCountAndDescription().getRight(),
|
||||
scoData.getCountAndDescription().getLeft()));
|
||||
}
|
||||
} else if (eventType.equals(FileNameTransTask.getPropertyName())) {
|
||||
/*
|
||||
* Replace the value of the Source File property with the
|
||||
* translated name via setDisplayName (see note in createSheet),
|
||||
* and put the untranslated name in the Original Name property
|
||||
* and in the tooltip.
|
||||
*/
|
||||
String originalName = evt.getOldValue().toString();
|
||||
translatedSourceName = evt.getNewValue().toString();
|
||||
setDisplayName(translatedSourceName);
|
||||
setShortDescription(originalName);
|
||||
updateSheet(new NodeProperty<>(
|
||||
Bundle.BlackboardArtifactNode_createSheet_srcFile_origName(),
|
||||
Bundle.BlackboardArtifactNode_createSheet_srcFile_origDisplayName(),
|
||||
NO_DESCR,
|
||||
originalName));
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -198,7 +223,7 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
* listener held by event publishers prevents garbage collection of this
|
||||
* node.
|
||||
*/
|
||||
private final PropertyChangeListener weakAppEventListener = WeakListeners.propertyChange(appEventListener, null);
|
||||
private final PropertyChangeListener weakListener = WeakListeners.propertyChange(listener, null);
|
||||
|
||||
/**
|
||||
* Constructs a BlackboardArtifactNode, an AbstractNode implementation that
|
||||
@ -207,7 +232,6 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
* @param artifact The artifact to represent.
|
||||
* @param iconPath The path to the icon for the artifact type.
|
||||
*/
|
||||
@NbBundle.Messages({"# {0} - artifactDisplayName", "BlackboardArtifactNode.displayName.artifact={0} Artifact"})
|
||||
public BlackboardArtifactNode(BlackboardArtifact artifact, String iconPath) {
|
||||
super(artifact, createLookup(artifact));
|
||||
this.artifact = artifact;
|
||||
@ -218,20 +242,25 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
/*
|
||||
* Calling this getter causes the unique path of the source
|
||||
* content to be cached in the Content object. This is
|
||||
* advantageous if this node is constructed in a background
|
||||
* thread instead of a UI thread.
|
||||
* advantageous as long as this node is constructed in a
|
||||
* background thread instead of a UI thread.
|
||||
*/
|
||||
srcContent.getUniquePath();
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, MessageFormat.format("Error getting the unique path of the source content (artifact objID={0})", artifact.getId()), ex);
|
||||
logger.log(Level.WARNING, MessageFormat.format("Error getting the unique path of the source content (artifact objID={0})", artifact.getId()), ex);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (srcContent == null) {
|
||||
throw new IllegalArgumentException(MessageFormat.format("Artifact missing source content (artifact objID={0})", artifact));
|
||||
}
|
||||
setName(Long.toString(artifact.getArtifactID()));
|
||||
setDisplayName(Bundle.BlackboardArtifactNode_displayName_artifact(artifact.getDisplayName()));
|
||||
String displayName = srcContent.getName();
|
||||
setDisplayName(displayName);
|
||||
setShortDescription(displayName);
|
||||
setIconBaseWithExtension(iconPath);
|
||||
Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, weakAppEventListener);
|
||||
Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, weakListener);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -286,7 +315,7 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
* Unregisters this node's application event listener.
|
||||
*/
|
||||
private void unregisterListener() {
|
||||
Case.removeEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, weakAppEventListener);
|
||||
Case.removeEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, weakListener);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -327,13 +356,15 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, MessageFormat.format("Error getting linked file of artifact (artifact objID={0})", artifact.getId()), ex); //NON-NLS
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* If the source content of the artifact represented by this node is a
|
||||
* file, add an action to view the file in the data source tree.
|
||||
*/
|
||||
AbstractFile file = getLookup().lookup(AbstractFile.class);
|
||||
AbstractFile file = getLookup().lookup(AbstractFile.class
|
||||
);
|
||||
if (null != file) {
|
||||
actionsList.add(ViewFileInTimelineAction.createViewSourceFileAction(file));
|
||||
}
|
||||
@ -348,14 +379,14 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
* @return The source content name.
|
||||
*/
|
||||
public String getSourceName() {
|
||||
String name = "";
|
||||
if (srcContent != null) {
|
||||
name = srcContent.getName();
|
||||
}
|
||||
return name;
|
||||
return srcContent.getName();
|
||||
}
|
||||
|
||||
@NbBundle.Messages({
|
||||
"BlackboardArtifactNode.createSheet.srcFile.name=Source File",
|
||||
"BlackboardArtifactNode.createSheet.srcFile.displayName=Source File",
|
||||
"BlackboardArtifactNode.createSheet.srcFile.origName=Original Name",
|
||||
"BlackboardArtifactNode.createSheet.srcFile.origDisplayName=Original Name",
|
||||
"BlackboardArtifactNode.createSheet.artifactType.displayName=Result Type",
|
||||
"BlackboardArtifactNode.createSheet.artifactType.name=Result Type",
|
||||
"BlackboardArtifactNode.createSheet.artifactDetails.displayName=Result Details",
|
||||
@ -381,12 +412,34 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
|
||||
/*
|
||||
* Add the name of the source content of the artifact represented by
|
||||
* this node to the sheet.
|
||||
* this node to the sheet. The value of this property is the same as the
|
||||
* display name of the node and this a "special" property that displays
|
||||
* the node's icon as well as the display name.
|
||||
*/
|
||||
sheetSet.put(new NodeProperty<>(NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.srcFile.name"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.srcFile.displayName"),
|
||||
sheetSet.put(new NodeProperty<>(
|
||||
Bundle.BlackboardArtifactNode_createSheet_srcFile_name(),
|
||||
Bundle.BlackboardArtifactNode_createSheet_srcFile_displayName(),
|
||||
NO_DESCR,
|
||||
this.getSourceName()));
|
||||
getDisplayName()));
|
||||
|
||||
if (TextTranslationService.getInstance().hasProvider() && UserPreferences.displayTranslatedFileNames()) {
|
||||
/*
|
||||
* If machine translation is configured, add the original name of
|
||||
* the of the source content of the artifact represented by this
|
||||
* node to the sheet.
|
||||
*/
|
||||
sheetSet.put(new NodeProperty<>(
|
||||
Bundle.BlackboardArtifactNode_createSheet_srcFile_origName(),
|
||||
Bundle.BlackboardArtifactNode_createSheet_srcFile_origDisplayName(),
|
||||
NO_DESCR,
|
||||
translatedSourceName != null ? srcContent.getName() : ""));
|
||||
if (translatedSourceName == null) {
|
||||
/*
|
||||
* NOTE: The task makes its own weak reference to the listener.
|
||||
*/
|
||||
new FileNameTransTask(srcContent.getName(), this, listener).submit();
|
||||
}
|
||||
}
|
||||
|
||||
if (!UserPreferences.getHideSCOColumns()) {
|
||||
/*
|
||||
@ -396,12 +449,24 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
* will fire a PropertyChangeEvent when the computation is completed
|
||||
* and this node's PropertyChangeListener will update the sheet.
|
||||
*/
|
||||
sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_score_name(), Bundle.BlackboardArtifactNode_createSheet_score_displayName(), VALUE_LOADING, ""));
|
||||
sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_comment_name(), Bundle.BlackboardArtifactNode_createSheet_comment_displayName(), VALUE_LOADING, ""));
|
||||
sheetSet.put(new NodeProperty<>(
|
||||
Bundle.BlackboardArtifactNode_createSheet_score_name(),
|
||||
Bundle.BlackboardArtifactNode_createSheet_score_displayName(),
|
||||
VALUE_LOADING,
|
||||
""));
|
||||
sheetSet.put(new NodeProperty<>(
|
||||
Bundle.BlackboardArtifactNode_createSheet_comment_name(),
|
||||
Bundle.BlackboardArtifactNode_createSheet_comment_displayName(),
|
||||
VALUE_LOADING,
|
||||
""));
|
||||
if (CentralRepository.isEnabled()) {
|
||||
sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_count_name(), Bundle.BlackboardArtifactNode_createSheet_count_displayName(), VALUE_LOADING, ""));
|
||||
sheetSet.put(new NodeProperty<>(
|
||||
Bundle.BlackboardArtifactNode_createSheet_count_name(),
|
||||
Bundle.BlackboardArtifactNode_createSheet_count_displayName(),
|
||||
VALUE_LOADING,
|
||||
""));
|
||||
}
|
||||
backgroundTasksPool.submit(new GetSCOTask(new WeakReference<>(this), weakAppEventListener));
|
||||
backgroundTasksPool.submit(new GetSCOTask(new WeakReference<>(this), weakListener));
|
||||
}
|
||||
|
||||
/*
|
||||
@ -414,11 +479,13 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT));
|
||||
if (attribute != null) {
|
||||
BlackboardArtifact associatedArtifact = Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboardArtifact(attribute.getValueLong());
|
||||
sheetSet.put(new NodeProperty<>(NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.artifactType.name"),
|
||||
sheetSet.put(new NodeProperty<>(
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.artifactType.name"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.artifactType.displayName"),
|
||||
NO_DESCR,
|
||||
associatedArtifact.getDisplayName()));
|
||||
sheetSet.put(new NodeProperty<>(NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.artifactDetails.name"),
|
||||
sheetSet.put(new NodeProperty<>(
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.artifactDetails.name"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.artifactDetails.displayName"),
|
||||
NO_DESCR,
|
||||
associatedArtifact.getShortDescription()));
|
||||
@ -459,15 +526,17 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
if (artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_EXT_MISMATCH_DETECTED.getTypeID()) {
|
||||
String ext = ""; //NON-NLS
|
||||
String actualMimeType = ""; //NON-NLS
|
||||
if (srcContent != null && srcContent instanceof AbstractFile) {
|
||||
if (srcContent instanceof AbstractFile) {
|
||||
AbstractFile file = (AbstractFile) srcContent;
|
||||
ext = file.getNameExtension();
|
||||
actualMimeType = file.getMIMEType();
|
||||
if (actualMimeType == null) {
|
||||
actualMimeType = ""; //NON-NLS
|
||||
|
||||
}
|
||||
}
|
||||
sheetSet.put(new NodeProperty<>(NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.ext.name"),
|
||||
sheetSet.put(new NodeProperty<>(
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.ext.name"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.ext.displayName"),
|
||||
NO_DESCR,
|
||||
ext));
|
||||
@ -484,12 +553,11 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
*/
|
||||
if (Arrays.asList(SHOW_UNIQUE_PATH).contains(artifactTypeId)) {
|
||||
String sourcePath = ""; //NON-NLS
|
||||
if (srcContent != null) {
|
||||
try {
|
||||
sourcePath = srcContent.getUniquePath();
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, MessageFormat.format("Error getting unique path of source content (artifact objID={0})", artifact.getId()), ex); //NON-NLS
|
||||
}
|
||||
try {
|
||||
sourcePath = srcContent.getUniquePath();
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, MessageFormat.format("Error getting unique path of source content (artifact objID={0})", artifact.getId()), ex); //NON-NLS
|
||||
|
||||
}
|
||||
|
||||
if (sourcePath.isEmpty() == false) {
|
||||
@ -506,45 +574,50 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
* sheet. Otherwise, add the data source to the sheet.
|
||||
*/
|
||||
if (Arrays.asList(SHOW_FILE_METADATA).contains(artifactTypeId)) {
|
||||
AbstractFile file = srcContent != null && srcContent instanceof AbstractFile ? (AbstractFile) srcContent : null;
|
||||
sheetSet.put(new NodeProperty<>(NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileModifiedTime.name"),
|
||||
AbstractFile file = srcContent instanceof AbstractFile ? (AbstractFile) srcContent : null;
|
||||
sheetSet.put(new NodeProperty<>(
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileModifiedTime.name"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileModifiedTime.displayName"),
|
||||
"",
|
||||
file == null ? "" : ContentUtils.getStringTime(file.getMtime(), file)));
|
||||
sheetSet.put(new NodeProperty<>(NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileChangedTime.name"),
|
||||
sheetSet.put(new NodeProperty<>(
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileChangedTime.name"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileChangedTime.displayName"),
|
||||
"",
|
||||
file == null ? "" : ContentUtils.getStringTime(file.getCtime(), file)));
|
||||
sheetSet.put(new NodeProperty<>(NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileAccessedTime.name"),
|
||||
sheetSet.put(new NodeProperty<>(
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileAccessedTime.name"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileAccessedTime.displayName"),
|
||||
"",
|
||||
file == null ? "" : ContentUtils.getStringTime(file.getAtime(), file)));
|
||||
sheetSet.put(new NodeProperty<>(NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileCreatedTime.name"),
|
||||
sheetSet.put(new NodeProperty<>(
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileCreatedTime.name"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileCreatedTime.displayName"),
|
||||
"",
|
||||
file == null ? "" : ContentUtils.getStringTime(file.getCrtime(), file)));
|
||||
sheetSet.put(new NodeProperty<>(NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileSize.name"),
|
||||
sheetSet.put(new NodeProperty<>(
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileSize.name"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileSize.displayName"),
|
||||
"",
|
||||
file == null ? "" : file.getSize()));
|
||||
sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_artifactMD5_name(),
|
||||
sheetSet.put(new NodeProperty<>(
|
||||
Bundle.BlackboardArtifactNode_createSheet_artifactMD5_name(),
|
||||
Bundle.BlackboardArtifactNode_createSheet_artifactMD5_displayName(),
|
||||
"",
|
||||
file == null ? "" : StringUtils.defaultString(file.getMd5Hash())));
|
||||
}
|
||||
} else {
|
||||
String dataSourceStr = "";
|
||||
if (srcContent != null) {
|
||||
try {
|
||||
Content dataSource = srcContent.getDataSource();
|
||||
if (dataSource != null) {
|
||||
dataSourceStr = dataSource.getName();
|
||||
} else {
|
||||
dataSourceStr = getRootAncestorName();
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, MessageFormat.format("Error getting source data source name (artifact objID={0})", artifact.getId()), ex); //NON-NLS
|
||||
try {
|
||||
Content dataSource = srcContent.getDataSource();
|
||||
if (dataSource != null) {
|
||||
dataSourceStr = dataSource.getName();
|
||||
} else {
|
||||
dataSourceStr = getRootAncestorName();
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, MessageFormat.format("Error getting source data source name (artifact objID={0})", artifact.getId()), ex); //NON-NLS
|
||||
|
||||
}
|
||||
|
||||
if (dataSourceStr.isEmpty() == false) {
|
||||
@ -563,24 +636,27 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
if (artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF.getTypeID()) {
|
||||
long size = 0;
|
||||
String path = ""; //NON-NLS
|
||||
if (srcContent != null && srcContent instanceof AbstractFile) {
|
||||
if (srcContent instanceof AbstractFile) {
|
||||
AbstractFile af = (AbstractFile) srcContent;
|
||||
size = af.getSize();
|
||||
try {
|
||||
path = af.getUniquePath();
|
||||
} catch (TskCoreException ex) {
|
||||
path = af.getParentPath();
|
||||
|
||||
}
|
||||
}
|
||||
sheetSet.put(new NodeProperty<>(NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.fileSize.name"),
|
||||
sheetSet.put(new NodeProperty<>(
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.fileSize.name"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.fileSize.displayName"),
|
||||
NO_DESCR,
|
||||
size));
|
||||
sheetSet.put(new NodeProperty<>(
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.path.name"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.path.displayName"),
|
||||
NO_DESCR,
|
||||
path));
|
||||
sheetSet
|
||||
.put(new NodeProperty<>(
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.path.name"),
|
||||
NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.path.displayName"),
|
||||
NO_DESCR,
|
||||
path));
|
||||
}
|
||||
|
||||
return sheet;
|
||||
@ -597,9 +673,7 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
List<Tag> tags = new ArrayList<>();
|
||||
try {
|
||||
tags.addAll(Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsByArtifact(artifact));
|
||||
if (srcContent != null) {
|
||||
tags.addAll(Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByContent(srcContent));
|
||||
}
|
||||
tags.addAll(Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByContent(srcContent));
|
||||
} catch (TskCoreException | NoCurrentCaseException ex) {
|
||||
logger.log(Level.SEVERE, MessageFormat.format("Error getting tags for artifact and its source content (artifact objID={0})", artifact.getId()), ex);
|
||||
}
|
||||
@ -617,7 +691,7 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
@Override
|
||||
protected final CorrelationAttributeInstance getCorrelationAttributeInstance() {
|
||||
CorrelationAttributeInstance correlationAttribute = null;
|
||||
if (srcContent != null && CentralRepository.isEnabled() && srcContent instanceof AbstractFile) {
|
||||
if (CentralRepository.isEnabled() && srcContent instanceof AbstractFile) {
|
||||
correlationAttribute = CorrelationAttributeUtil.getCorrAttrForFile((AbstractFile) srcContent);
|
||||
}
|
||||
return correlationAttribute;
|
||||
@ -695,7 +769,7 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
*/
|
||||
Score score = Score.NO_SCORE;
|
||||
String description = Bundle.BlackboardArtifactNode_createSheet_noScore_description();
|
||||
if (srcContent != null && srcContent instanceof AbstractFile) {
|
||||
if (srcContent instanceof AbstractFile) {
|
||||
if (((AbstractFile) srcContent).getKnown() == TskData.FileKnown.BAD) {
|
||||
score = Score.NOTABLE_SCORE;
|
||||
description = Bundle.BlackboardArtifactNode_createSheet_notableFile_description();
|
||||
@ -725,7 +799,7 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
/*
|
||||
* Is the artifact's source content notable?
|
||||
*/
|
||||
if (score == Score.NO_SCORE && srcContent != null) {
|
||||
if (score == Score.NO_SCORE) {
|
||||
try {
|
||||
if (!srcContent.getArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT).isEmpty()) {
|
||||
score = Score.INTERESTING_SCORE;
|
||||
@ -814,7 +888,7 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
parentName = parent.getName();
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, MessageFormat.format("Error getting root ancestor name for source content (artifact objID={0})", artifact.getId())); //NON-NLS
|
||||
logger.log(Level.SEVERE, MessageFormat.format("Error getting root ancestor name for source content (artifact objID={0})", artifact.getId()), ex); //NON-NLS
|
||||
return "";
|
||||
}
|
||||
return parentName;
|
||||
@ -1036,8 +1110,8 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
* @param attribute The correlation attribute instance to use for the
|
||||
* central repository lookup.
|
||||
*
|
||||
* @deprecated Do not use. The other occurrences property is now computed in a
|
||||
* background thread and added to the property sheet via property change
|
||||
* @deprecated Do not use. The other occurrences property is now computed in
|
||||
* a background thread and added to the property sheet via property change
|
||||
* event.
|
||||
*/
|
||||
@NbBundle.Messages({"BlackboardArtifactNode.createSheet.comment.name=C",
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2013-2018 Basis Technology Corp.
|
||||
* Copyright 2013-2020 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -24,13 +24,11 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.Action;
|
||||
import org.openide.nodes.Children;
|
||||
import org.openide.nodes.Sheet;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.openide.util.lookup.Lookups;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
||||
import org.sleuthkit.autopsy.timeline.actions.ViewArtifactInTimelineAction;
|
||||
import org.sleuthkit.autopsy.timeline.actions.ViewFileInTimelineAction;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
@ -46,14 +44,14 @@ import static org.sleuthkit.autopsy.datamodel.Bundle.*;
|
||||
* tag name nodes have tag type child nodes; tag type nodes are the parents of
|
||||
* either content or blackboard artifact tag nodes.
|
||||
*/
|
||||
public class BlackboardArtifactTagNode extends DisplayableItemNode {
|
||||
public class BlackboardArtifactTagNode extends TagNode {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(BlackboardArtifactTagNode.class.getName());
|
||||
private static final String ICON_PATH = "org/sleuthkit/autopsy/images/green-tag-icon-16.png"; //NON-NLS
|
||||
private final BlackboardArtifactTag tag;
|
||||
|
||||
public BlackboardArtifactTagNode(BlackboardArtifactTag tag) {
|
||||
super(Children.LEAF, Lookups.fixed(tag, tag.getArtifact(), tag.getContent()));
|
||||
super(Lookups.fixed(tag, tag.getArtifact(), tag.getContent()), tag.getContent());
|
||||
super.setName(tag.getContent().getName());
|
||||
super.setDisplayName(tag.getContent().getName());
|
||||
this.setIconBaseWithExtension(ICON_PATH);
|
||||
@ -75,6 +73,7 @@ public class BlackboardArtifactTagNode extends DisplayableItemNode {
|
||||
NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.srcFile.text"),
|
||||
"",
|
||||
tag.getContent().getName()));
|
||||
addOriginalNameProp(properties);
|
||||
String contentPath;
|
||||
try {
|
||||
contentPath = tag.getContent().getUniquePath();
|
||||
@ -82,7 +81,6 @@ public class BlackboardArtifactTagNode extends DisplayableItemNode {
|
||||
Logger.getLogger(ContentTagNode.class.getName()).log(Level.SEVERE, "Failed to get path for content (id = " + tag.getContent().getId() + ")", ex); //NON-NLS
|
||||
contentPath = NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.unavail.text");
|
||||
}
|
||||
|
||||
properties.put(new NodeProperty<>(
|
||||
NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.srcFilePath.text"),
|
||||
NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.srcFilePath.text"),
|
||||
@ -146,11 +144,6 @@ public class BlackboardArtifactTagNode extends DisplayableItemNode {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLeafTypeNode() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getItemType() {
|
||||
return getClass().getName();
|
||||
|
@ -11,8 +11,6 @@ ArtifactTypeNode.createSheet.childCnt.name=Child Count
|
||||
ArtifactTypeNode.createSheet.childCnt.displayName=Child Count
|
||||
ArtifactTypeNode.createSheet.childCnt.desc=no description
|
||||
BlackboardArtifactNode.noDesc.text=no description
|
||||
BlackboardArtifactNode.createSheet.srcFile.name=Source File
|
||||
BlackboardArtifactNode.createSheet.srcFile.displayName=Source File
|
||||
BlackboardArtifactNode.createSheet.ext.name=Extension
|
||||
BlackboardArtifactNode.createSheet.ext.displayName=Extension
|
||||
BlackboardArtifactNode.createSheet.mimeType.name=MIME Type
|
||||
|
@ -74,10 +74,12 @@ BlackboardArtifactNode.createSheet.path.displayName=Path
|
||||
BlackboardArtifactNode.createSheet.path.name=Path
|
||||
BlackboardArtifactNode.createSheet.score.displayName=S
|
||||
BlackboardArtifactNode.createSheet.score.name=S
|
||||
BlackboardArtifactNode.createSheet.srcFile.displayName=Source File
|
||||
BlackboardArtifactNode.createSheet.srcFile.name=Source File
|
||||
BlackboardArtifactNode.createSheet.srcFile.origDisplayName=Original Name
|
||||
BlackboardArtifactNode.createSheet.srcFile.origName=Original Name
|
||||
BlackboardArtifactNode.createSheet.taggedItem.description=Result or associated file has been tagged.
|
||||
BlackboardArtifactNode.createSheet.tags.displayName=Tags
|
||||
# {0} - artifactDisplayName
|
||||
BlackboardArtifactNode.displayName.artifact={0} Artifact
|
||||
BlackboardArtifactTagNode.createSheet.userName.text=User Name
|
||||
BlackboardArtifactTagNode.viewSourceArtifact.text=View Source Result
|
||||
Category.five=CAT-5: Non-pertinent
|
||||
@ -88,6 +90,7 @@ Category.two=CAT-2: Child Exploitation (Non-Illegal/Age Difficult)
|
||||
Category.zero=CAT-0: Uncategorized
|
||||
ContentTagNode.createSheet.artifactMD5.displayName=MD5 Hash
|
||||
ContentTagNode.createSheet.artifactMD5.name=MD5 Hash
|
||||
ContentTagNode.createSheet.origFileName=Original Name
|
||||
ContentTagNode.createSheet.userName.text=User Name
|
||||
DeletedContent.allDelFilter.text=All
|
||||
DeletedContent.createSheet.filterType.desc=no description
|
||||
@ -175,8 +178,6 @@ ArtifactTypeNode.createSheet.childCnt.name=Child Count
|
||||
ArtifactTypeNode.createSheet.childCnt.displayName=Child Count
|
||||
ArtifactTypeNode.createSheet.childCnt.desc=no description
|
||||
BlackboardArtifactNode.noDesc.text=no description
|
||||
BlackboardArtifactNode.createSheet.srcFile.name=Source File
|
||||
BlackboardArtifactNode.createSheet.srcFile.displayName=Source File
|
||||
BlackboardArtifactNode.createSheet.ext.name=Extension
|
||||
BlackboardArtifactNode.createSheet.ext.displayName=Extension
|
||||
BlackboardArtifactNode.createSheet.mimeType.name=MIME Type
|
||||
@ -345,6 +346,8 @@ TagNameNode.bbArtTagTypeNodeKey.text=Result Tags
|
||||
TagNameNode.bookmark.text=Bookmark
|
||||
TagNameNode.createSheet.name.name=Name
|
||||
TagNameNode.createSheet.name.displayName=Name
|
||||
TagNode.propertySheet.origName=Original Name
|
||||
TagNode.propertySheet.origNameDisplayName=Original Name
|
||||
TagsNode.displayName.text=Tags
|
||||
TagsNode.createSheet.name.name=Name
|
||||
TagsNode.createSheet.name.displayName=Name
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2013-2016 Basis Technology Corp.
|
||||
* Copyright 2013-2020 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -24,7 +24,6 @@ import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.Action;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.openide.nodes.Children;
|
||||
import org.openide.nodes.Sheet;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
@ -39,18 +38,16 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
/**
|
||||
* Instances of this class wrap ContentTag objects. In the Autopsy presentation
|
||||
* of the SleuthKit data model, they are leaf nodes of a tree consisting of
|
||||
* content and blackboard artifact tags, grouped first by tag type, then by tag
|
||||
* name.
|
||||
* content and artifact tags, grouped first by tag type, then by tag name.
|
||||
*/
|
||||
class ContentTagNode extends DisplayableItemNode {
|
||||
class ContentTagNode extends TagNode {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(ContentTagNode.class.getName());
|
||||
|
||||
private static final String ICON_PATH = "org/sleuthkit/autopsy/images/blue-tag-icon-16.png"; //NON-NLS
|
||||
private final ContentTag tag;
|
||||
|
||||
public ContentTagNode(ContentTag tag) {
|
||||
super(Children.LEAF, Lookups.fixed(tag, tag.getContent()));
|
||||
ContentTagNode(ContentTag tag) {
|
||||
super(Lookups.fixed(tag, tag.getContent()), tag.getContent());
|
||||
super.setName(tag.getContent().getName());
|
||||
super.setDisplayName(tag.getContent().getName());
|
||||
this.setIconBaseWithExtension(ICON_PATH);
|
||||
@ -58,6 +55,7 @@ class ContentTagNode extends DisplayableItemNode {
|
||||
}
|
||||
|
||||
@Messages({
|
||||
"ContentTagNode.createSheet.origFileName=Original Name",
|
||||
"ContentTagNode.createSheet.artifactMD5.displayName=MD5 Hash",
|
||||
"ContentTagNode.createSheet.artifactMD5.name=MD5 Hash",
|
||||
"ContentTagNode.createSheet.userName.text=User Name"})
|
||||
@ -79,15 +77,19 @@ class ContentTagNode extends DisplayableItemNode {
|
||||
properties = Sheet.createPropertiesSet();
|
||||
propertySheet.put(properties);
|
||||
}
|
||||
properties.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.file.name"),
|
||||
properties.put(new NodeProperty<>(
|
||||
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.file.name"),
|
||||
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.file.displayName"),
|
||||
"",
|
||||
content.getName()));
|
||||
properties.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.filePath.name"),
|
||||
addOriginalNameProp(properties);
|
||||
properties.put(new NodeProperty<>(
|
||||
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.filePath.name"),
|
||||
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.filePath.displayName"),
|
||||
"",
|
||||
contentPath));
|
||||
properties.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.comment.name"),
|
||||
properties.put(new NodeProperty<>(
|
||||
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.comment.name"),
|
||||
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.comment.displayName"),
|
||||
"",
|
||||
tag.getComment()));
|
||||
@ -95,23 +97,28 @@ class ContentTagNode extends DisplayableItemNode {
|
||||
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileModifiedTime.displayName"),
|
||||
"",
|
||||
file != null ? ContentUtils.getStringTime(file.getMtime(), file) : ""));
|
||||
properties.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileChangedTime.name"),
|
||||
properties.put(new NodeProperty<>(
|
||||
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileChangedTime.name"),
|
||||
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileChangedTime.displayName"),
|
||||
"",
|
||||
file != null ? ContentUtils.getStringTime(file.getCtime(), file) : ""));
|
||||
properties.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileAccessedTime.name"),
|
||||
properties.put(new NodeProperty<>(
|
||||
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileAccessedTime.name"),
|
||||
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileAccessedTime.displayName"),
|
||||
"",
|
||||
file != null ? ContentUtils.getStringTime(file.getAtime(), file) : ""));
|
||||
properties.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileCreatedTime.name"),
|
||||
properties.put(new NodeProperty<>(
|
||||
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileCreatedTime.name"),
|
||||
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileCreatedTime.displayName"),
|
||||
"",
|
||||
file != null ? ContentUtils.getStringTime(file.getCrtime(), file) : ""));
|
||||
properties.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileSize.name"),
|
||||
properties.put(new NodeProperty<>(
|
||||
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileSize.name"),
|
||||
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileSize.displayName"),
|
||||
"",
|
||||
content.getSize()));
|
||||
properties.put(new NodeProperty<>(Bundle.ContentTagNode_createSheet_artifactMD5_name(),
|
||||
properties.put(new NodeProperty<>(
|
||||
Bundle.ContentTagNode_createSheet_artifactMD5_name(),
|
||||
Bundle.ContentTagNode_createSheet_artifactMD5_displayName(),
|
||||
"",
|
||||
file != null ? StringUtils.defaultString(file.getMd5Hash()) : ""));
|
||||
@ -128,8 +135,7 @@ class ContentTagNode extends DisplayableItemNode {
|
||||
List<Action> actions = new ArrayList<>();
|
||||
actions.addAll(Arrays.asList(super.getActions(context)));
|
||||
|
||||
AbstractFile file = getLookup().lookup(AbstractFile.class
|
||||
);
|
||||
AbstractFile file = getLookup().lookup(AbstractFile.class);
|
||||
if (file != null) {
|
||||
actions.add(ViewFileInTimelineAction.createViewFileAction(file));
|
||||
}
|
||||
@ -144,13 +150,9 @@ class ContentTagNode extends DisplayableItemNode {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLeafTypeNode() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getItemType() {
|
||||
return getClass().getName();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-2017 Basis Technology Corp.
|
||||
* Copyright 2012-2020 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.datamodel;
|
||||
|
||||
import org.openide.nodes.AbstractNode;
|
||||
import org.openide.nodes.Children;
|
||||
import org.openide.nodes.Sheet;
|
||||
import org.openide.util.Lookup;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
@ -142,4 +143,26 @@ public abstract class DisplayableItemNode extends AbstractNode {
|
||||
return selectedChildNodeInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the node property sheet by replacing existing properties with new
|
||||
* properties with the same property name.
|
||||
*
|
||||
* @param newProps The replacement property objects.
|
||||
*/
|
||||
protected synchronized final void updatePropertySheet(NodeProperty<?>... newProps) {
|
||||
Sheet currentSheet = this.getSheet();
|
||||
Sheet.Set currentPropsSet = currentSheet.get(Sheet.PROPERTIES);
|
||||
Property<?>[] currentProps = currentPropsSet.getProperties();
|
||||
for (NodeProperty<?> newProp : newProps) {
|
||||
for (int i = 0; i < currentProps.length; i++) {
|
||||
if (currentProps[i].getName().equals(newProp.getName())) {
|
||||
currentProps[i] = newProp;
|
||||
}
|
||||
}
|
||||
}
|
||||
currentPropsSet.put(currentProps);
|
||||
currentSheet.put(currentPropsSet);
|
||||
this.setSheet(currentSheet);
|
||||
}
|
||||
|
||||
}
|
||||
|
128
Core/src/org/sleuthkit/autopsy/datamodel/TagNode.java
Executable file
128
Core/src/org/sleuthkit/autopsy/datamodel/TagNode.java
Executable file
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2020-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.datamodel;
|
||||
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import org.openide.nodes.Children;
|
||||
import org.openide.nodes.Sheet;
|
||||
import org.openide.util.Lookup;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.core.UserPreferences;
|
||||
import org.sleuthkit.autopsy.datamodel.utils.FileNameTransTask;
|
||||
import org.sleuthkit.autopsy.texttranslation.TextTranslationService;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
|
||||
/**
|
||||
* An abstract superclass for a node that represents a tag, uses the name of a
|
||||
* given Content object as its display name, and has a property sheet with an
|
||||
* original name property when machine translation is enabled.
|
||||
*
|
||||
* The translation of the Content name is done in a background thread. The
|
||||
* translated name is made the display name of the node and the untranslated
|
||||
* name is put into both the original name property and into the node's tooltip.
|
||||
*
|
||||
* TODO (Jira-6174): Consider modifying this class to be able to use it more broadly
|
||||
* within the Autopsy data model (i.e., AbstractNode suclasses). It's not really
|
||||
* specific to a tag node.
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"TagNode.propertySheet.origName=Original Name",
|
||||
"TagNode.propertySheet.origNameDisplayName=Original Name"
|
||||
})
|
||||
abstract class TagNode extends DisplayableItemNode {
|
||||
|
||||
private final static String ORIG_NAME_PROP_NAME = Bundle.TagNode_propertySheet_origName();
|
||||
private final static String ORIG_NAME_PROP_DISPLAY_NAME = Bundle.TagNode_propertySheet_origNameDisplayName();
|
||||
|
||||
private final String originalName;
|
||||
private volatile String translatedName;
|
||||
|
||||
/**
|
||||
* An abstract superclass for a node that represents a tag, uses the name of
|
||||
* a given Content object as its display name, and has a property sheet with
|
||||
* an untranslated file name property when machine translation is enabled.
|
||||
*
|
||||
* @param lookup The Lookup of the node.
|
||||
* @param content The Content to use for the node display name.
|
||||
*/
|
||||
TagNode(Lookup lookup, Content content) {
|
||||
super(Children.LEAF, lookup);
|
||||
originalName = content.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLeafTypeNode() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
abstract public String getItemType();
|
||||
|
||||
@Override
|
||||
abstract public <T> T accept(DisplayableItemNodeVisitor<T> visitor);
|
||||
|
||||
/**
|
||||
* Adds an original name property to the node's property sheet and submits
|
||||
* an original name translation task.
|
||||
*
|
||||
* The translation of the original name is done in a background thread. The
|
||||
* translated name is made the display name of the node and the untranslated
|
||||
* name is put into both the original name property and into the node's
|
||||
* tooltip.
|
||||
*
|
||||
* @param properties The node's property sheet.
|
||||
*/
|
||||
protected void addOriginalNameProp(Sheet.Set properties) {
|
||||
if (TextTranslationService.getInstance().hasProvider() && UserPreferences.displayTranslatedFileNames()) {
|
||||
properties.put(new NodeProperty<>(
|
||||
ORIG_NAME_PROP_NAME,
|
||||
ORIG_NAME_PROP_DISPLAY_NAME,
|
||||
"",
|
||||
translatedName != null ? originalName : ""));
|
||||
if (translatedName == null) {
|
||||
new FileNameTransTask(originalName, this, new NameTranslationListener()).submit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A listener for PropertyChangeEvents from a background task used to
|
||||
* translate the original display name associated with the node.
|
||||
*/
|
||||
private class NameTranslationListener implements PropertyChangeListener {
|
||||
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
String eventType = evt.getPropertyName();
|
||||
if (eventType.equals(FileNameTransTask.getPropertyName())) {
|
||||
translatedName = evt.getNewValue().toString();
|
||||
String originalName = evt.getOldValue().toString();
|
||||
setDisplayName(translatedName);
|
||||
setShortDescription(originalName);
|
||||
updatePropertySheet(new NodeProperty<>(
|
||||
ORIG_NAME_PROP_NAME,
|
||||
ORIG_NAME_PROP_DISPLAY_NAME,
|
||||
"",
|
||||
originalName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -64,7 +64,7 @@ public abstract class AbstractNodePropertySheetTask<T extends AbstractNode> impl
|
||||
* @return The Future of the task, may be used for task cancellation by
|
||||
* calling Future.cancel(true).
|
||||
*/
|
||||
public static Future<?> submitTask(AbstractNodePropertySheetTask<?> task) {
|
||||
private static Future<?> submitTask(AbstractNodePropertySheetTask<?> task) {
|
||||
return executor.submit(task);
|
||||
}
|
||||
|
||||
@ -104,12 +104,22 @@ public abstract class AbstractNodePropertySheetTask<T extends AbstractNode> impl
|
||||
*
|
||||
* @param node The AbstractNode.
|
||||
*
|
||||
* @return The result of the computation as a PropertyChangeEvent.
|
||||
* @return The result of the computation as a PropertyChangeEvent, may be
|
||||
* null.
|
||||
*/
|
||||
protected abstract PropertyChangeEvent computePropertyValue(T node) throws Exception;
|
||||
|
||||
/**
|
||||
* Submits this task to the ExecutorService for the thread pool.
|
||||
*
|
||||
* @return The task's Future from the ExecutorService.
|
||||
*/
|
||||
public final Future<?> submit() {
|
||||
return submitTask(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
final public void run() {
|
||||
public final void run() {
|
||||
try {
|
||||
T node = this.weakNodeRef.get();
|
||||
PropertyChangeListener listener = this.weakListenerRef.get();
|
||||
|
61
Core/src/org/sleuthkit/autopsy/datamodel/utils/FileNameTransTask.java
Executable file
61
Core/src/org/sleuthkit/autopsy/datamodel/utils/FileNameTransTask.java
Executable file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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.datamodel.utils;
|
||||
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import org.openide.nodes.AbstractNode;
|
||||
import org.sleuthkit.autopsy.texttranslation.utils.FileNameTranslationUtil;
|
||||
|
||||
/**
|
||||
* An AbstractNodePropertySheetTask that translates a file name for an
|
||||
* AbstractNode's property sheet.
|
||||
*/
|
||||
public class FileNameTransTask extends AbstractNodePropertySheetTask<AbstractNode> {
|
||||
|
||||
private final static String EVENT_SOURCE = FileNameTransTask.class.getName();
|
||||
private final static String PROPERTY_NAME = EVENT_SOURCE + ".TranslatedFileName";
|
||||
private final String fileName;
|
||||
|
||||
public static String getPropertyName() {
|
||||
return PROPERTY_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an AbstractNodePropertySheetTask that translates a file name
|
||||
* for an AbstractNode's property sheet. When the translation is complete, a
|
||||
* PropertyChangeEvent will be fired to the node's PropertyChangeListener.
|
||||
* Call getPropertyName() to identify the property.
|
||||
*
|
||||
* @param node The node.
|
||||
* @param listener The node's PropertyChangeListener.
|
||||
* @param fileName THe file name.
|
||||
*/
|
||||
public FileNameTransTask(String fileName, AbstractNode node, PropertyChangeListener listener) {
|
||||
super(node, listener);
|
||||
this.fileName = fileName;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PropertyChangeEvent computePropertyValue(AbstractNode node) throws Exception {
|
||||
String translatedFileName = FileNameTranslationUtil.translate(fileName);
|
||||
return translatedFileName.isEmpty() ? null : new PropertyChangeEvent(EVENT_SOURCE, PROPERTY_NAME, fileName, translatedFileName);
|
||||
}
|
||||
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-2019 Basis Technology Corp.
|
||||
* Copyright 2012-2020 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -187,27 +187,6 @@ public class DataResultFilterNode extends FilterNode {
|
||||
return propertySets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the display name for the wrapped node.
|
||||
*
|
||||
* OutlineView used in the DataResult table uses getDisplayName() to
|
||||
* populate the first column, which is Source File.
|
||||
*
|
||||
* Hence this override to return the 'correct' displayName for the wrapped
|
||||
* node.
|
||||
*
|
||||
* @return The display name for the node.
|
||||
*/
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
final Node orig = getOriginal();
|
||||
String name = orig.getDisplayName();
|
||||
if ((orig instanceof BlackboardArtifactNode)) {
|
||||
name = ((BlackboardArtifactNode) orig).getSourceName();
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds information about which child node of this node, if any, should be
|
||||
* selected. Can be null.
|
||||
|
@ -23,7 +23,6 @@ import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.io.Files;
|
||||
import java.awt.Image;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.RenderedImage;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.nio.file.Paths;
|
||||
@ -95,6 +94,7 @@ class FileSearch {
|
||||
.build();
|
||||
private static final int PREVIEW_SIZE = 256;
|
||||
private static volatile TextSummarizer summarizerToUse = null;
|
||||
private static final BufferedImage VIDEO_DEFAULT_IMAGE = getDefaultVideoThumbnail();
|
||||
|
||||
/**
|
||||
* Run the file search and returns the SearchResults object for debugging.
|
||||
@ -456,6 +456,20 @@ class FileSearch {
|
||||
+ "AND blackboard_artifacts.obj_id IN (" + objIdList + ") "; // NON-NLS
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default image to display when a thumbnail is not available.
|
||||
*
|
||||
* @return The default video thumbnail.
|
||||
*/
|
||||
private static BufferedImage getDefaultVideoThumbnail() {
|
||||
try {
|
||||
return ImageIO.read(ImageUtils.class.getResourceAsStream("/org/sleuthkit/autopsy/images/failedToCreateVideoThumb.png"));//NON-NLS
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.SEVERE, "Failed to load 'failed to create video' placeholder.", ex); //NON-NLS
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the video thumbnails for a file which exists in a
|
||||
* VideoThumbnailsWrapper and update the VideoThumbnailsWrapper to include
|
||||
@ -476,7 +490,6 @@ class FileSearch {
|
||||
cacheDirectory = null;
|
||||
logger.log(Level.WARNING, "Unable to get cache directory, video thumbnails will not be saved", ex);
|
||||
}
|
||||
|
||||
if (cacheDirectory == null || file.getMd5Hash() == null || !Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, file.getMd5Hash()).toFile().exists()) {
|
||||
java.io.File tempFile;
|
||||
try {
|
||||
@ -488,7 +501,7 @@ class FileSearch {
|
||||
0,
|
||||
0,
|
||||
0};
|
||||
thumbnailWrapper.setThumbnails(createDefaultThumbnailList(), framePositions);
|
||||
thumbnailWrapper.setThumbnails(createDefaultThumbnailList(VIDEO_DEFAULT_IMAGE), framePositions);
|
||||
return;
|
||||
}
|
||||
if (tempFile.exists() == false || tempFile.length() < file.getSize()) {
|
||||
@ -502,7 +515,7 @@ class FileSearch {
|
||||
0,
|
||||
0,
|
||||
0};
|
||||
thumbnailWrapper.setThumbnails(createDefaultThumbnailList(), framePositions);
|
||||
thumbnailWrapper.setThumbnails(createDefaultThumbnailList(VIDEO_DEFAULT_IMAGE), framePositions);
|
||||
return;
|
||||
}
|
||||
ContentUtils.writeToFile(file, tempFile, progress, null, true);
|
||||
@ -523,7 +536,7 @@ class FileSearch {
|
||||
0,
|
||||
0,
|
||||
0};
|
||||
thumbnailWrapper.setThumbnails(createDefaultThumbnailList(), framePositions);
|
||||
thumbnailWrapper.setThumbnails(createDefaultThumbnailList(VIDEO_DEFAULT_IMAGE), framePositions);
|
||||
return;
|
||||
}
|
||||
double fps = videoFile.get(5); // gets frame per second
|
||||
@ -535,7 +548,7 @@ class FileSearch {
|
||||
0,
|
||||
0,
|
||||
0};
|
||||
thumbnailWrapper.setThumbnails(createDefaultThumbnailList(), framePositions);
|
||||
thumbnailWrapper.setThumbnails(createDefaultThumbnailList(VIDEO_DEFAULT_IMAGE), framePositions);
|
||||
return;
|
||||
}
|
||||
if (Thread.interrupted()) {
|
||||
@ -544,7 +557,7 @@ class FileSearch {
|
||||
0,
|
||||
0,
|
||||
0};
|
||||
thumbnailWrapper.setThumbnails(createDefaultThumbnailList(), framePositions);
|
||||
thumbnailWrapper.setThumbnails(createDefaultThumbnailList(VIDEO_DEFAULT_IMAGE), framePositions);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -573,10 +586,10 @@ class FileSearch {
|
||||
logger.log(Level.WARNING, "Error seeking to " + framePositions[i] + "ms in {0}", file.getParentPath() + "/" + file.getName()); //NON-NLS
|
||||
// If we can't set the time, continue to the next frame position and try again.
|
||||
|
||||
videoThumbnails.add(ImageUtils.getDefaultThumbnail());
|
||||
videoThumbnails.add(VIDEO_DEFAULT_IMAGE);
|
||||
if (cacheDirectory != null) {
|
||||
try {
|
||||
ImageIO.write((RenderedImage) ImageUtils.getDefaultThumbnail(), THUMBNAIL_FORMAT,
|
||||
ImageIO.write(VIDEO_DEFAULT_IMAGE, THUMBNAIL_FORMAT,
|
||||
Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, file.getMd5Hash(), i + "-" + framePositions[i] + "." + THUMBNAIL_FORMAT).toFile()); //NON-NLS)
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.WARNING, "Unable to save default video thumbnail for " + file.getMd5Hash() + " at frame position " + framePositions[i], ex);
|
||||
@ -588,10 +601,10 @@ class FileSearch {
|
||||
if (!videoFile.read(imageMatrix)) {
|
||||
logger.log(Level.WARNING, "Error reading frame at " + framePositions[i] + "ms from {0}", file.getParentPath() + "/" + file.getName()); //NON-NLS
|
||||
// If the image is bad for some reason, continue to the next frame position and try again.
|
||||
videoThumbnails.add(ImageUtils.getDefaultThumbnail());
|
||||
videoThumbnails.add(VIDEO_DEFAULT_IMAGE);
|
||||
if (cacheDirectory != null) {
|
||||
try {
|
||||
ImageIO.write((RenderedImage) ImageUtils.getDefaultThumbnail(), THUMBNAIL_FORMAT,
|
||||
ImageIO.write(VIDEO_DEFAULT_IMAGE, THUMBNAIL_FORMAT,
|
||||
Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, file.getMd5Hash(), i + "-" + framePositions[i] + "." + THUMBNAIL_FORMAT).toFile()); //NON-NLS)
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.WARNING, "Unable to save default video thumbnail for " + file.getMd5Hash() + " at frame position " + framePositions[i], ex);
|
||||
@ -602,10 +615,10 @@ class FileSearch {
|
||||
}
|
||||
// If the image is empty, return since no buffered image can be created.
|
||||
if (imageMatrix.empty()) {
|
||||
videoThumbnails.add(ImageUtils.getDefaultThumbnail());
|
||||
videoThumbnails.add(VIDEO_DEFAULT_IMAGE);
|
||||
if (cacheDirectory != null) {
|
||||
try {
|
||||
ImageIO.write((RenderedImage) ImageUtils.getDefaultThumbnail(), THUMBNAIL_FORMAT,
|
||||
ImageIO.write(VIDEO_DEFAULT_IMAGE, THUMBNAIL_FORMAT,
|
||||
Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, file.getMd5Hash(), i + "-" + framePositions[i] + "." + THUMBNAIL_FORMAT).toFile()); //NON-NLS)
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.WARNING, "Unable to save default video thumbnail for " + file.getMd5Hash() + " at frame position " + framePositions[i], ex);
|
||||
@ -660,7 +673,7 @@ class FileSearch {
|
||||
videoFile.release(); // close the file}
|
||||
}
|
||||
} else {
|
||||
loadSavedThumbnails(cacheDirectory, thumbnailWrapper);
|
||||
loadSavedThumbnails(cacheDirectory, thumbnailWrapper, VIDEO_DEFAULT_IMAGE);
|
||||
}
|
||||
}
|
||||
|
||||
@ -674,7 +687,7 @@ class FileSearch {
|
||||
* information about the file and the thumbnails
|
||||
* associated with it.
|
||||
*/
|
||||
private static void loadSavedThumbnails(String cacheDirectory, VideoThumbnailsWrapper thumbnailWrapper) {
|
||||
private static void loadSavedThumbnails(String cacheDirectory, VideoThumbnailsWrapper thumbnailWrapper, BufferedImage failedVideoThumbImage) {
|
||||
int[] framePositions = new int[4];
|
||||
List<Image> videoThumbnails = new ArrayList<>();
|
||||
int thumbnailNumber = 0;
|
||||
@ -683,7 +696,7 @@ class FileSearch {
|
||||
try {
|
||||
videoThumbnails.add(ImageIO.read(Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, md5, fileName).toFile()));
|
||||
} catch (IOException ex) {
|
||||
videoThumbnails.add(ImageUtils.getDefaultThumbnail());
|
||||
videoThumbnails.add(failedVideoThumbImage);
|
||||
logger.log(Level.WARNING, "Unable to read saved video thumbnail " + fileName + " for " + md5, ex);
|
||||
}
|
||||
int framePos = Integer.valueOf(FilenameUtils.getBaseName(fileName).substring(2));
|
||||
@ -699,12 +712,12 @@ class FileSearch {
|
||||
*
|
||||
* @return List containing the default thumbnail.
|
||||
*/
|
||||
private static List<Image> createDefaultThumbnailList() {
|
||||
private static List<Image> createDefaultThumbnailList(BufferedImage failedVideoThumbImage) {
|
||||
List<Image> videoThumbnails = new ArrayList<>();
|
||||
videoThumbnails.add(ImageUtils.getDefaultThumbnail());
|
||||
videoThumbnails.add(ImageUtils.getDefaultThumbnail());
|
||||
videoThumbnails.add(ImageUtils.getDefaultThumbnail());
|
||||
videoThumbnails.add(ImageUtils.getDefaultThumbnail());
|
||||
videoThumbnails.add(failedVideoThumbImage);
|
||||
videoThumbnails.add(failedVideoThumbImage);
|
||||
videoThumbnails.add(failedVideoThumbImage);
|
||||
videoThumbnails.add(failedVideoThumbImage);
|
||||
return videoThumbnails;
|
||||
}
|
||||
|
||||
|
@ -273,7 +273,6 @@ final class FileSearchData {
|
||||
= new ImmutableSet.Builder<String>()
|
||||
.add("text/html", //NON-NLS
|
||||
"text/csv", //NON-NLS
|
||||
"text/x-log", //NON-NLS
|
||||
"application/rtf", //NON-NLS
|
||||
"application/pdf", //NON-NLS
|
||||
"application/xhtml+xml", //NON-NLS
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 2.4 KiB |
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2020-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.texttranslation.utils;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.sleuthkit.autopsy.texttranslation.NoServiceProviderException;
|
||||
import org.sleuthkit.autopsy.texttranslation.TextTranslationService;
|
||||
import org.sleuthkit.autopsy.texttranslation.TranslationException;
|
||||
|
||||
/**
|
||||
* A utility to translate file names.
|
||||
*/
|
||||
public final class FileNameTranslationUtil {
|
||||
|
||||
/**
|
||||
* Translates a file name using the configured machine translation service.
|
||||
*
|
||||
* @param fileName The file name.
|
||||
*
|
||||
* @return The translation of the file name.
|
||||
*
|
||||
* @throws NoServiceProviderException If machine translation is not
|
||||
* configured.
|
||||
* @throws TranslationException If there is an error doing the
|
||||
* translation.
|
||||
*/
|
||||
public static String translate(String fileName) throws NoServiceProviderException, TranslationException {
|
||||
/*
|
||||
* Don't attempt translation if the characters of the file name are all
|
||||
* ASCII chars.
|
||||
*
|
||||
* TODO (Jira-6175): This filter prevents translation of many
|
||||
* non-English file names composed entirely of Latin chars.
|
||||
*/
|
||||
if (fileName.matches("^\\p{ASCII}+$")) {
|
||||
return "";
|
||||
}
|
||||
|
||||
TextTranslationService translator = TextTranslationService.getInstance();
|
||||
String baseName = FilenameUtils.getBaseName(fileName);
|
||||
String translation = translator.translate(baseName);
|
||||
if (!translation.isEmpty()) {
|
||||
String extension = FilenameUtils.getExtension(fileName);
|
||||
if (!extension.isEmpty()) {
|
||||
String extensionDelimiter = (extension.isEmpty()) ? "" : ".";
|
||||
translation += extensionDelimiter + extension;
|
||||
}
|
||||
}
|
||||
return translation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent instantiation of this utility class
|
||||
*/
|
||||
private FileNameTranslationUtil() {
|
||||
}
|
||||
|
||||
}
|
@ -14,8 +14,7 @@
|
||||
|
||||
<!-- for viewers -->
|
||||
<dependency conf="autopsy_core->*" org="org.freedesktop.gstreamer" name="gst1-java-core" rev="1.0.0"/>
|
||||
<dependency conf="autopsy_core->*" org="net.java.dev.jna" name="jna" rev="3.4.0"/>
|
||||
<dependency conf="autopsy_core->*" org="net.java.dev.jna" name="platform" rev="3.4.0"/>
|
||||
<dependency conf="autopsy_core->*" org="net.java.dev.jna" name="jna-platform" rev="5.5.0"/>
|
||||
|
||||
<!-- for file search -->
|
||||
<dependency conf="autopsy_core->*" org="com.github.lgooddatepicker" name="LGoodDatePicker" rev="10.3.1"/>
|
||||
@ -73,8 +72,5 @@
|
||||
<dependency conf="autopsy_core->default" org="com.googlecode.plist" name="dd-plist" rev="1.20"/>
|
||||
|
||||
<exclude org="*" ext="*" type="javadoc"/>
|
||||
<!-- conflict resolutions for multiple JAR versions -->
|
||||
<conflict org="net.java.dev.jna" module="jna" rev="3.4.0"/>
|
||||
<conflict org="net.java.dev.jna" module="platform" rev="3.4.0"/>
|
||||
</dependencies>
|
||||
</ivy-module>
|
||||
|
@ -21,7 +21,6 @@ file.reference.dom4j-1.6.1.jar=release/modules/ext/dom4j-1.6.1.jar
|
||||
file.reference.geronimo-jms_1.1_spec-1.0.jar=release/modules/ext/geronimo-jms_1.1_spec-1.0.jar
|
||||
file.reference.gson-2.8.5.jar=release/modules/ext/gson-2.8.5.jar
|
||||
file.reference.gst1-java-core-1.0.0.jar=release\\modules\\ext\\gst1-java-core-1.0.0.jar
|
||||
file.reference.jna-3.4.0.jar=release/modules/ext/jna-3.4.0.jar
|
||||
file.reference.guava-19.0.jar=release/modules/ext/guava-19.0.jar
|
||||
file.reference.imageio-bmp-3.2.jar=release/modules/ext/imageio-bmp-3.2.jar
|
||||
file.reference.imageio-core-3.2.jar=release/modules/ext/imageio-core-3.2.jar
|
||||
@ -44,6 +43,8 @@ file.reference.jfxtras-common-8.0-r4.jar=release/modules/ext/jfxtras-common-8.0-
|
||||
file.reference.jfxtras-controls-8.0-r4.jar=release/modules/ext/jfxtras-controls-8.0-r4.jar
|
||||
file.reference.jfxtras-fxml-8.0-r4.jar=release/modules/ext/jfxtras-fxml-8.0-r4.jar
|
||||
file.reference.jna-3.4.0.jar=release/modules/ext/jna-3.4.0.jar
|
||||
file.reference.jna-5.5.0.jar=release\\modules\\ext\\jna-5.5.0.jar
|
||||
file.reference.jna-platform-5.5.0.jar=release\\modules\\ext\\jna-platform-5.5.0.jar
|
||||
file.reference.joda-time-2.4.jar=release/modules/ext/joda-time-2.4.jar
|
||||
file.reference.jsr305-1.3.9.jar=release/modules/ext/jsr305-1.3.9.jar
|
||||
file.reference.LGoodDatePicker-10.3.1.jar=release/modules/ext/LGoodDatePicker-10.3.1.jar
|
||||
@ -52,7 +53,6 @@ file.reference.logkit-1.0.1.jar=release/modules/ext/logkit-1.0.1.jar
|
||||
file.reference.mail-1.4.3.jar=release/modules/ext/mail-1.4.3.jar
|
||||
file.reference.opencv-248.jar=release/modules/ext/opencv-248.jar
|
||||
file.reference.openjfx-dialogs-1.0.2.jar=release/modules/ext/openjfx-dialogs-1.0.3.jar
|
||||
file.reference.platform-3.4.0.jar=release/modules/ext/platform-3.4.0.jar
|
||||
file.reference.poi-4.0.1.jar=release\\modules\\ext\\poi-4.0.1.jar
|
||||
file.reference.poi-excelant-4.0.1.jar=release\\modules\\ext\\poi-excelant-4.0.1.jar
|
||||
file.reference.poi-ooxml-4.0.1.jar=release\\modules\\ext\\poi-ooxml-4.0.1.jar
|
||||
|
@ -806,10 +806,6 @@
|
||||
<runtime-relative-path>ext/sigar-1.6.4.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/sigar-1.6.4.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/jna-3.4.0.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/jna-3.4.0.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/gson-2.8.5.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/gson-2.8.5.jar</binary-origin>
|
||||
@ -902,6 +898,10 @@
|
||||
<runtime-relative-path>ext/commons-csv-1.4.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/commons-csv-1.4.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/jna-5.5.0.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/jna-5.5.0.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/imageio-sgi-3.2.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/imageio-sgi-3.2.jar</binary-origin>
|
||||
@ -946,10 +946,6 @@
|
||||
<runtime-relative-path>ext/imageio-bmp-3.2.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/imageio-bmp-3.2.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/platform-3.4.0.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/platform-3.4.0.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/commons-lang-2.6.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/commons-lang-2.6.jar</binary-origin>
|
||||
@ -1018,6 +1014,10 @@
|
||||
<runtime-relative-path>ext/dom4j-1.6.1.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/dom4j-1.6.1.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/jna-platform-5.5.0.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/jna-platform-5.5.0.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/imageio-metadata-3.2.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/imageio-metadata-3.2.jar</binary-origin>
|
||||
|
@ -95,7 +95,7 @@ class BrowserLocationAnalyzer(general.AndroidComponentAnalyzer):
|
||||
longitude = Double.valueOf(resultSet.getString("longitude"))
|
||||
|
||||
attributes = ArrayList()
|
||||
artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACKPOINT)
|
||||
artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_BOOKMARK)
|
||||
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE, general.MODULE_NAME, latitude))
|
||||
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE, general.MODULE_NAME, longitude))
|
||||
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, general.MODULE_NAME, timestamp))
|
||||
|
@ -41,6 +41,7 @@ from org.sleuthkit.datamodel import TskCoreException
|
||||
|
||||
import traceback
|
||||
import general
|
||||
import struct
|
||||
|
||||
"""
|
||||
Parses cache files that Android maintains for Wifi and cell towers. Adds GPS points to blackboard.
|
||||
@ -74,60 +75,24 @@ class CacheLocationAnalyzer(general.AndroidComponentAnalyzer):
|
||||
|
||||
def __findGeoLocationsInFile(self, file, abstractFile):
|
||||
|
||||
tempBytes = bytearray([0] * 2) # will temporarily hold bytes to be converted into the correct data types
|
||||
|
||||
try:
|
||||
inputStream = FileInputStream(file)
|
||||
|
||||
inputStream.read(tempBytes) # version
|
||||
|
||||
tempBytes = bytearray([0] * 2)
|
||||
inputStream.read(tempBytes) # number of location entries
|
||||
|
||||
iterations = BigInteger(tempBytes).intValue()
|
||||
|
||||
for i in range(iterations): # loop through every entry
|
||||
tempBytes = bytearray([0] * 2)
|
||||
inputStream.read(tempBytes)
|
||||
|
||||
tempBytes = bytearray([0])
|
||||
inputStream.read(tempBytes)
|
||||
|
||||
while BigInteger(tempBytes).intValue() != 0: # pass through non important values until the start of accuracy(around 7-10 bytes)
|
||||
if 0 > inputStream.read(tempBytes):
|
||||
break # we've passed the end of the file, so stop
|
||||
|
||||
tempBytes = bytearray([0] * 3)
|
||||
inputStream.read(tempBytes)
|
||||
if BigInteger(tempBytes).intValue() <= 0: # This refers to a location that could not be calculated
|
||||
tempBytes = bytearray([0] * 28) # read rest of the row's bytes
|
||||
inputStream.read(tempBytes)
|
||||
continue
|
||||
accuracy = "" + BigInteger(tempBytes).intValue()
|
||||
|
||||
tempBytes = bytearray([0] * 4)
|
||||
inputStream.read(tempBytes)
|
||||
confidence = "" + BigInteger(tempBytes).intValue()
|
||||
|
||||
tempBytes = bytearray([0] * 8)
|
||||
inputStream.read(tempBytes)
|
||||
latitude = CacheLocationAnalyzer.toDouble(bytes)
|
||||
|
||||
tempBytes = bytearray([0] * 8)
|
||||
inputStream.read(tempBytes)
|
||||
longitude = CacheLocationAnalyzer.toDouble(bytes)
|
||||
|
||||
tempBytes = bytearray([0] * 8)
|
||||
inputStream.read(tempBytes)
|
||||
timestamp = BigInteger(tempBytes).longValue() / 1000
|
||||
# code to parse the cache.wifi and cache.cell taken from https://forensics.spreitzenbarth.de/2011/10/28/decoding-cache-cell-and-cache-wifi-files/
|
||||
cacheFile = open(str(file), 'rb')
|
||||
(version, entries) = struct.unpack('>hh', cacheFile.read(4))
|
||||
i = 0
|
||||
while i < entries:
|
||||
key = cacheFile.read(struct.unpack('>h', cacheFile.read(2))[0])
|
||||
(accuracy, confidence, latitude, longitude, readtime) = struct.unpack('>iiddQ', cacheFile.read(32))
|
||||
timestamp = readtime/1000
|
||||
i = i + 1
|
||||
|
||||
attributes = ArrayList()
|
||||
artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACKPOINT)
|
||||
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE, AndroidAnalyzer.MODULE_NAME, latitude))
|
||||
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE, AndroidAnalyzer.MODULE_NAME, longitude))
|
||||
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, AndroidModuleFactorymodule.Name, timestamp))
|
||||
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, AndroidAnalyzer.MODULE_NAME,
|
||||
file.getName() + "Location History"))
|
||||
artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_BOOKMARK)
|
||||
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE, general.MODULE_NAME, latitude))
|
||||
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE, general.MODULE_NAME, longitude))
|
||||
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, general.MODULE_NAME, timestamp))
|
||||
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, general.MODULE_NAME,
|
||||
abstractFile.getName() + " Location History"))
|
||||
|
||||
artifact.addAttributes(attributes)
|
||||
#Not storing these for now.
|
||||
@ -136,15 +101,13 @@ class CacheLocationAnalyzer(general.AndroidComponentAnalyzer):
|
||||
try:
|
||||
# index the artifact for keyword search
|
||||
blackboard = Case.getCurrentCase().getSleuthkitCase().getBlackboard()
|
||||
blackboard.postArtifact(artifact, MODULE_NAME)
|
||||
blackboard.postArtifact(artifact, general.MODULE_NAME)
|
||||
except Blackboard.BlackboardException as ex:
|
||||
self._logger.log(Level.SEVERE, "Unable to index blackboard artifact " + str(artifact.getArtifactID()), ex)
|
||||
self._logger.log(Level.SEVERE, traceback.format_exc())
|
||||
MessageNotifyUtil.Notify.error("Failed to index GPS trackpoint artifact for keyword search.", artifact.getDisplayName())
|
||||
cacheFile.close()
|
||||
|
||||
except SQLException as ex:
|
||||
# Unable to execute Cached GPS locations SQL query against database.
|
||||
pass
|
||||
except Exception as ex:
|
||||
self._logger.log(Level.SEVERE, "Error parsing Cached GPS locations to blackboard", ex)
|
||||
self._logger.log(Level.SEVERE, traceback.format_exc())
|
||||
|
Loading…
x
Reference in New Issue
Block a user