Merge remote-tracking branch 'upstream/develop' into 6154-System-Resource-Usage-Database-Program-Run-and-Network-Activity-Artifacts

This commit is contained in:
Mark McKinnon 2020-03-23 12:25:41 -04:00
commit d8fb5a64db
37 changed files with 1032 additions and 550 deletions

View File

@ -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.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.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.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.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.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 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.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.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.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.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-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 file.reference.gax-grpc-1.44.0.jar=release/modules/ext/gax-grpc-1.44.0.jar

View File

@ -615,10 +615,6 @@
<runtime-relative-path>ext/commons-validator-1.6.jar</runtime-relative-path> <runtime-relative-path>ext/commons-validator-1.6.jar</runtime-relative-path>
<binary-origin>release/modules/ext/commons-validator-1.6.jar</binary-origin> <binary-origin>release/modules/ext/commons-validator-1.6.jar</binary-origin>
</class-path-extension> </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> <class-path-extension>
<runtime-relative-path>ext/jbig2-imageio-3.0.2.jar</runtime-relative-path> <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> <binary-origin>release\modules\ext\jbig2-imageio-3.0.2.jar</binary-origin>

View File

@ -51,7 +51,7 @@ public class CentralRepoDbUpgrader13To14 implements CentralRepoDbUpgrader {
if (type.getId() >= CorrelationAttributeInstance.ADDITIONAL_TYPES_BASE_ID) { if (type.getId() >= CorrelationAttributeInstance.ADDITIONAL_TYPES_BASE_ID) {
// these are new Correlation types - new tables need to be created // 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.getAddCaseIdIndexTemplate(), instance_type_dbname, instance_type_dbname));
statement.execute(String.format(RdbmsCentralRepoFactory.getAddDataSourceIdIndexTemplate(), 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)); 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 // add new correlation type
CentralRepoDbUtil.insertCorrelationType(connection, type); CentralRepoDbUtil.insertCorrelationType(connection, type);
} else { } 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
// Alter the existing X_Instance tables to add account_id column
String sqlStr = String.format(getAlterArtifactInstancesAddAccountIdTemplate(selectedPlatform), instance_type_dbname); String sqlStr = String.format(getAlterArtifactInstancesAddAccountIdTemplate(selectedPlatform), instance_type_dbname);
statement.execute(sqlStr); statement.execute(sqlStr);

View File

@ -325,4 +325,17 @@ public class CentralRepoDbUtil {
closeStatement(preparedStatement); 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;
}
} }

View File

@ -63,9 +63,21 @@ public class CentralRepoPostgresSettingsUtil {
private 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 { try {
handler.operation(); setter.set(value);
} }
catch (CentralRepoException | NumberFormatException e) { catch (CentralRepoException | NumberFormatException e) {
LOGGER.log(Level.WARNING, "There was an error in converting central repo postgres settings", 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 { private interface ValueSetter {
void operation() throws CentralRepoException, NumberFormatException; void set(String value) throws CentralRepoException, NumberFormatException;
} }
/** /**
@ -96,15 +108,12 @@ public class CentralRepoPostgresSettingsUtil {
return settings; return settings;
} }
logException(() -> settings.setHost(muConn.getHost())); setValOrLog((v) -> settings.setHost(v), muConn.getHost());
logException(() -> settings.setDbName(PostgresConnectionSettings.DEFAULT_DBNAME)); setValOrLog((v) -> settings.setUserName(v), muConn.getUserName());
logException(() -> settings.setUserName(muConn.getUserName())); setValOrLog((v) -> settings.setPassword(v), muConn.getPassword());
logException(() -> settings.setPort(Integer.parseInt(muConn.getPort())));
logException(() -> settings.setBulkThreshold(RdbmsCentralRepo.DEFAULT_BULK_THRESHHOLD));
logException(() -> settings.setPassword(muConn.getPassword()));
setValOrLog((v) -> settings.setPort(Integer.parseInt(v)), muConn.getPort());
return settings; return settings;
} }
@ -120,24 +129,27 @@ public class CentralRepoPostgresSettingsUtil {
Map<String, String> keyVals = ModuleSettings.getConfigSettings(MODULE_KEY); Map<String, String> keyVals = ModuleSettings.getConfigSettings(MODULE_KEY);
logException(() -> settings.setHost(keyVals.get(HOST_KEY))); setValOrLog((v) -> settings.setHost(v), keyVals.get(HOST_KEY));
logException(() -> settings.setDbName(keyVals.get(DBNAME_KEY))); setValOrLog((v) -> settings.setDbName(v), keyVals.get(DBNAME_KEY));
logException(() -> settings.setUserName(keyVals.get(USER_KEY))); setValOrLog((v) -> settings.setUserName(v), keyVals.get(USER_KEY));
logException(() -> settings.setPort(Integer.parseInt(keyVals.get(PORT_KEY)))); setValOrLog((v) -> settings.setPort(Integer.parseInt(v)), keyVals.get(PORT_KEY));
logException(() -> settings.setBulkThreshold(Integer.parseInt(keyVals.get((BULK_THRESHOLD_KEY))))); setValOrLog((v) -> settings.setBulkThreshold(Integer.parseInt(v)), keyVals.get((BULK_THRESHOLD_KEY)));
String passwordHex = keyVals.get(PASSWORD_KEY); String passwordHex = keyVals.get(PASSWORD_KEY);
String password; if (passwordHex != null) {
try { String password;
password = TextConverter.convertHexTextToText(passwordHex); try {
} catch (TextConverterException ex) { password = TextConverter.convertHexTextToText(passwordHex);
LOGGER.log(Level.WARNING, "Failed to convert password from hex text to text.", ex); } catch (TextConverterException ex) {
password = null; 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; return settings;
} }

View File

@ -294,8 +294,9 @@ public class CorrelationAttributeInstance implements Serializable {
// Create Correlation Types for Accounts. // Create Correlation Types for Accounts.
int correlationTypeId = ADDITIONAL_TYPES_BASE_ID; int correlationTypeId = ADDITIONAL_TYPES_BASE_ID;
for (Account.Type type : Account.Type.PREDEFINED_ACCOUNT_TYPES) { 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. // 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 defaultCorrelationTypes.add(new CorrelationAttributeInstance.Type(correlationTypeId, type.getDisplayName(), type.getTypeName().toLowerCase() + "_acct", true, true)); //NON-NLS
correlationTypeId++; correlationTypeId++;
} }

View File

@ -27,6 +27,7 @@ import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount.CentralRepoAccountType; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount.CentralRepoAccountType;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Account;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
@ -184,25 +185,29 @@ public class CorrelationAttributeUtil {
// Get the account type from the artifact // Get the account type from the artifact
BlackboardAttribute accountTypeAttribute = acctArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE)); BlackboardAttribute accountTypeAttribute = acctArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE));
String accountTypeStr = accountTypeAttribute.getValueString(); 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. // Get the corresponding CentralRepoAccountType from the database.
CentralRepoAccountType crAccountType = CentralRepository.getInstance().getAccountTypeByName(accountTypeStr); CentralRepoAccountType crAccountType = CentralRepository.getInstance().getAccountTypeByName(accountTypeStr);
int corrTypeId = crAccountType.getCorrelationTypeId(); int corrTypeId = crAccountType.getCorrelationTypeId();
CorrelationAttributeInstance.Type corrType = CentralRepository.getInstance().getCorrelationTypeById(corrTypeId); CorrelationAttributeInstance.Type corrType = CentralRepository.getInstance().getCorrelationTypeById(corrTypeId);
// Get the account identifier // Get the account identifier
BlackboardAttribute accountIdAttribute = acctArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ID)); BlackboardAttribute accountIdAttribute = acctArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ID));
String accountIdStr = accountIdAttribute.getValueString(); String accountIdStr = accountIdAttribute.getValueString();
// add/get the account and get its accountId. // add/get the account and get its accountId.
CentralRepoAccount crAccount = CentralRepository.getInstance().getOrCreateAccount(crAccountType, accountIdStr); CentralRepoAccount crAccount = CentralRepository.getInstance().getOrCreateAccount(crAccountType, accountIdStr);
CorrelationAttributeInstance corrAttr = makeCorrAttr(acctArtifact, corrType, accountIdStr); CorrelationAttributeInstance corrAttr = makeCorrAttr(acctArtifact, corrType, accountIdStr);
if (corrAttr != null) { if (corrAttr != null) {
// set the account_id in correlation attribute // set the account_id in correlation attribute
corrAttr.setAccountId(crAccount.getAccountId()); corrAttr.setAccountId(crAccount.getAccountId());
corrAttrInstances.add(corrAttr); corrAttrInstances.add(corrAttr);
}
} }
} }

View File

@ -1002,18 +1002,26 @@ abstract class RdbmsCentralRepo implements CentralRepository {
public void addArtifactInstance(CorrelationAttributeInstance eamArtifact) throws CentralRepoException { public void addArtifactInstance(CorrelationAttributeInstance eamArtifact) throws CentralRepoException {
checkAddArtifactInstanceNulls(eamArtifact); checkAddArtifactInstanceNulls(eamArtifact);
// @@@ We should cache the case and data source IDs in memory // @@@ We should cache the case and data source IDs in memory
String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(eamArtifact.getCorrelationType()); String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(eamArtifact.getCorrelationType());
String sql boolean artifactHasAnAccount = CentralRepoDbUtil.correlationAttribHasAnAccount(eamArtifact.getCorrelationType());
= "INSERT INTO "
String sql;
// _instance table for accounts have an additional account_id column
if (artifactHasAnAccount) {
sql = "INSERT INTO "
+ tableName + tableName
+ "(case_id, data_source_id, value, file_path, known_status, comment, file_obj_id, account_id) " + "(case_id, data_source_id, value, file_path, known_status, comment, file_obj_id, account_id) "
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?) " + "VALUES (?, ?, ?, ?, ?, ?, ?, ?) "
+ getConflictClause(); + 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(); try (Connection conn = connect();
PreparedStatement preparedStatement = conn.prepareStatement(sql);) { PreparedStatement preparedStatement = conn.prepareStatement(sql);) {
@ -1032,10 +1040,13 @@ abstract class RdbmsCentralRepo implements CentralRepository {
} }
preparedStatement.setLong(7, eamArtifact.getFileObjectId()); preparedStatement.setLong(7, eamArtifact.getFileObjectId());
if (eamArtifact.getAccountId() >= 0) { // set in the accountId only for artifacts that represent accounts
preparedStatement.setLong(8, eamArtifact.getAccountId()); if (artifactHasAnAccount) {
} else { if (eamArtifact.getAccountId() >= 0) {
preparedStatement.setNull(8, Types.INTEGER); preparedStatement.setLong(8, eamArtifact.getAccountId());
} else {
preparedStatement.setNull(8, Types.INTEGER);
}
} }
preparedStatement.executeUpdate(); preparedStatement.executeUpdate();

View File

@ -83,6 +83,7 @@ public class RdbmsCentralRepoFactory {
public boolean initializeDatabaseSchema() { public boolean initializeDatabaseSchema() {
String createArtifactInstancesTableTemplate = getCreateArtifactInstancesTableTemplate(selectedPlatform); String createArtifactInstancesTableTemplate = getCreateArtifactInstancesTableTemplate(selectedPlatform);
String createAccountInstancesTableTemplate = getCreateAccountInstancesTableTemplate(selectedPlatform);
String instancesCaseIdIdx = getAddCaseIdIndexTemplate(); String instancesCaseIdIdx = getAddCaseIdIndexTemplate();
String instancesDatasourceIdIdx = getAddDataSourceIdIndexTemplate(); String instancesDatasourceIdIdx = getAddDataSourceIdIndexTemplate();
@ -147,7 +148,13 @@ public class RdbmsCentralRepoFactory {
reference_type_dbname = CentralRepoDbUtil.correlationTypeToReferenceTableName(type); reference_type_dbname = CentralRepoDbUtil.correlationTypeToReferenceTableName(type);
instance_type_dbname = CentralRepoDbUtil.correlationTypeToInstanceTableName(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(instancesCaseIdIdx, instance_type_dbname, instance_type_dbname));
stmt.execute(String.format(instancesDatasourceIdIdx, 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)); stmt.execute(String.format(instancesValueIdx, instance_type_dbname, instance_type_dbname));
@ -193,7 +200,7 @@ public class RdbmsCentralRepoFactory {
result = CentralRepoDbUtil.insertDefaultCorrelationTypes(conn) result = CentralRepoDbUtil.insertDefaultCorrelationTypes(conn)
&& CentralRepoDbUtil.insertDefaultOrganization(conn) && && CentralRepoDbUtil.insertDefaultOrganization(conn) &&
insertDefaultAccountsTablesContent(conn); RdbmsCentralRepoFactory.insertDefaultAccountsTablesContent(conn, selectedPlatform );
// @TODO: uncomment when ready to create/populate persona tables // @TODO: uncomment when ready to create/populate persona tables
// && insertDefaultPersonaTablesContent(conn); // && 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 * central repository. %s will exist in the template where the name of the
* new table will be added. * new table will be added.
* *
@ -365,6 +372,31 @@ public class RdbmsCentralRepoFactory {
*/ */
static String getCreateArtifactInstancesTableTemplate(CentralRepoPlatforms selectedPlatform) { static String getCreateArtifactInstancesTableTemplate(CentralRepoPlatforms selectedPlatform) {
// Each "%s" will be replaced with the relevant TYPE_instances table name. // 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 (" return "CREATE TABLE IF NOT EXISTS %s ("
+ getNumericPrimaryKeyClause("id", selectedPlatform) + getNumericPrimaryKeyClause("id", selectedPlatform)
+ "case_id integer NOT NULL," + "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 (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)"; + "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 * Get the statement String for creating a new data_sources table in a
* Sqlite central repository. * Sqlite central repository.
@ -494,6 +526,7 @@ public class RdbmsCentralRepoFactory {
* on the selected CR platform/RDMBS. * on the selected CR platform/RDMBS.
* *
* @param pkName name of primary key. * @param pkName name of primary key.
* @param selectedPlatform The selected platform.
* *
* @return SQL clause to be used in a Create table statement * @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. * Inserts the default content in persona related tables.
* *
* @param conn Database connection to use. * @param conn Database connection to use.
* @param selectedPlatform The selected platform.
* *
* @return True if success, false otherwise. * @return True if success, false otherwise.
*/ */
@ -838,11 +845,13 @@ public class RdbmsCentralRepoFactory {
// Populate the account_types table // Populate the account_types table
for (Account.Type type : Account.Type.PREDEFINED_ACCOUNT_TYPES) { for (Account.Type type : Account.Type.PREDEFINED_ACCOUNT_TYPES) {
int correlationTypeId = getCorrelationTypeIdForAccountType(conn, type); if (type != Account.Type.DEVICE) {
if (correlationTypeId > 0) { int correlationTypeId = getCorrelationTypeIdForAccountType(conn, type);
String sqlString = String.format("INSERT INTO account_types (type_name, display_name, correlation_type_id) VALUES ('%s', '%s', %d)" + getOnConflictDoNothingClause(selectedPlatform), if (correlationTypeId > 0) {
type.getTypeName(), type.getDisplayName(), correlationTypeId); String sqlString = String.format("INSERT INTO account_types (type_name, display_name, correlation_type_id) VALUES ('%s', '%s', %d)" + getOnConflictDoNothingClause(selectedPlatform),
stmt.execute(sqlString); type.getTypeName(), type.getDisplayName(), correlationTypeId);
stmt.execute(sqlString);
}
} }
} }

View File

@ -24,6 +24,7 @@ import java.awt.Cursor;
import java.awt.HeadlessException; import java.awt.HeadlessException;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
@ -31,14 +32,15 @@ import java.util.logging.Level;
import javax.swing.JDialog; import javax.swing.JDialog;
import javax.swing.JFileChooser; import javax.swing.JFileChooser;
import javax.swing.JFrame; import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList; import javax.swing.JList;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import javax.swing.JTextField; import javax.swing.JTextField;
import javax.swing.ListCellRenderer;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener; import javax.swing.event.DocumentListener;
import javax.swing.filechooser.FileFilter; import javax.swing.filechooser.FileFilter;
import javax.swing.plaf.basic.BasicComboBoxRenderer;
import org.netbeans.spi.options.OptionsPanelController; import org.netbeans.spi.options.OptionsPanelController;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages; 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. * 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; 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) { int index, boolean isSelected, boolean cellHasFocus) {
CentralRepoDbChoice item = (CentralRepoDbChoice) value;
// disable cell if it is the db connection from multi user settings // disable cell if it is the db connection from multi user settings
// and that option is not enabled in multi user settings // and that option is not enabled in multi user settings
setText(item.getTitle()); setText(value.getTitle());
setEnabled(isDbChoiceSelectable(item)); setEnabled(isDbChoiceSelectable(value));
return this; return this;
} }
} }
@ -135,7 +137,7 @@ public class EamDbSettingsDialog extends JDialog {
valid(); valid();
display(); display();
} }
private void setupDbChoice(CentralRepoDbChoice initialMenuItem) { private void setupDbChoice(CentralRepoDbChoice initialMenuItem) {
// setup initially selected item // setup initially selected item
@ -144,10 +146,8 @@ public class EamDbSettingsDialog extends JDialog {
manager.getSelectedDbChoice() : manager.getSelectedDbChoice() :
CentralRepoDbChoice.DB_CHOICES[0] : CentralRepoDbChoice.DB_CHOICES[0] :
initialMenuItem; initialMenuItem;
// set the renderer so item is unselectable if inappropriate
cbDatabaseType.setRenderer(DB_CHOICE_RENDERER); cbDatabaseType.setRenderer(DB_CHOICE_RENDERER);
changeDbSelection(toSelect); changeDbSelection(toSelect);
} }

View File

@ -731,9 +731,9 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
enableButtonSubComponents(cbUseCentralRepo.isSelected()); enableButtonSubComponents(cbUseCentralRepo.isSelected());
} else { } else {
load(); load();
enableDatabaseConfigureButton(cbUseCentralRepo.isSelected() && !caseIsOpen);
} }
enableDatabaseConfigureButton(cbUseCentralRepo.isSelected() && !caseIsOpen);
} }
/** /**

View File

@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.communications.relationships; package org.sleuthkit.autopsy.communications.relationships;
import java.util.logging.Level; import java.util.logging.Level;
import org.apache.commons.lang.StringUtils;
import org.openide.nodes.Sheet; import org.openide.nodes.Sheet;
import org.sleuthkit.autopsy.communications.Utils; import org.sleuthkit.autopsy.communications.Utils;
import static org.sleuthkit.autopsy.communications.relationships.RelationshipsNodeUtilities.getAttributeDisplayString; import static org.sleuthkit.autopsy.communications.relationships.RelationshipsNodeUtilities.getAttributeDisplayString;
@ -67,14 +68,6 @@ final class CallLogNode extends BlackboardArtifactNode {
return sheet; 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; long duration = -1;
try{ try{
duration = getCallDuration(artifact); duration = getCallDuration(artifact);
@ -84,7 +77,7 @@ final class CallLogNode extends BlackboardArtifactNode {
sheetSet.put(createNode(TSK_DATETIME_START, artifact)); sheetSet.put(createNode(TSK_DATETIME_START, artifact));
sheetSet.put(createNode(TSK_DIRECTION, 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) { if(duration != -1) {
sheetSet.put(new NodeProperty<>("duration", "Duration", "", Long.toString(duration))); sheetSet.put(new NodeProperty<>("duration", "Duration", "", Long.toString(duration)));
} }
@ -107,6 +100,59 @@ final class CallLogNode extends BlackboardArtifactNode {
return endAttribute.getValueLong() - startAttribute.getValueLong(); 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 * Circumvent DataResultFilterNode's slightly odd delegation to
* BlackboardArtifactNode.getSourceName(). * BlackboardArtifactNode.getSourceName().

View File

@ -41,6 +41,7 @@ MediaFileViewer.AccessibleContext.accessibleDescription=
MediaFileViewer.title=Media MediaFileViewer.title=Media
MediaFileViewer.toolTip=Displays supported multimedia files (images, videos, audio) MediaFileViewer.toolTip=Displays supported multimedia files (images, videos, audio)
MediaPlayerPanel.noSupport=File not supported. 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.timeFormat=%02d:%02d:%02d
MediaPlayerPanel.unknownTime=Unknown MediaPlayerPanel.unknownTime=Unknown
MediaViewImagePanel.createTagOption=Create MediaViewImagePanel.createTagOption=Create
@ -168,7 +169,7 @@ MediaPlayerPanel.playBackSpeedLabel.text=Speed:
SQLiteViewer.readTable.errorText=Error getting rows for table: {0} SQLiteViewer.readTable.errorText=Error getting rows for table: {0}
# {0} - tableName # {0} - tableName
SQLiteViewer.selectTable.errorText=Error getting row count for table: {0} 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.originalText=Original Text
TranslatablePanel.comboBoxOption.translatedText=Translated Text TranslatablePanel.comboBoxOption.translatedText=Translated Text
# {0} - exception message
TranslatablePanel.onSetContentError.text=There was an error displaying the text: {0}

View File

@ -194,6 +194,6 @@ class MediaFileViewer extends javax.swing.JPanel implements FileTypeViewer {
@Override @Override
public boolean isSupported(AbstractFile file){ public boolean isSupported(AbstractFile file){
return true; return mediaPlayerPanel.isSupported(file) || imagePanel.isSupported(file);
} }
} }

View File

@ -63,7 +63,7 @@
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" max="-2" attributes="0"> <Group type="103" groupAlignment="0" max="-2" attributes="0">
<Component id="progressLabel" max="32767" 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> </Group>
<EmptySpace min="-2" pref="10" max="-2" attributes="0"/> <EmptySpace min="-2" pref="10" max="-2" attributes="0"/>
</Group> </Group>
@ -129,12 +129,6 @@
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <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, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MediaPlayerPanel.playButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </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"> <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[49, 29]"/> <Dimension value="[49, 29]"/>
</Property> </Property>

View File

@ -74,7 +74,9 @@ import org.freedesktop.gstreamer.Format;
import org.freedesktop.gstreamer.GstException; import org.freedesktop.gstreamer.GstException;
import org.freedesktop.gstreamer.event.SeekFlags; import org.freedesktop.gstreamer.event.SeekFlags;
import org.freedesktop.gstreamer.event.SeekType; 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 * 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. //and the TrackListener on the slider itself.
private final Semaphore sliderLock; private final Semaphore sliderLock;
private static volatile boolean IS_GST_ENABLED = true;
/** /**
* Creates a new MediaPlayerPanel * Creates a new MediaPlayerPanel
*/ */
@ -222,18 +226,10 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
//True for fairness. In other words, //True for fairness. In other words,
//acquire() calls are processed in order of invocation. //acquire() calls are processed in order of invocation.
sliderLock = new Semaphore(1, true); 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() { private void customizeComponents() {
progressSlider.setEnabled(false); // disable slider; enable after user plays vid enableComponents(false);
progressSlider.setMinimum(0); progressSlider.setMinimum(0);
progressSlider.setMaximum(PROGRESS_SLIDER_SIZE); progressSlider.setMaximum(PROGRESS_SLIDER_SIZE);
progressSlider.setValue(0); progressSlider.setValue(0);
@ -390,13 +386,16 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
} }
timer.stop(); timer.stop();
if (gstPlayBin != null) { if (gstPlayBin != null) {
gstPlayBin.stop(); Gst.getExecutor().submit(() -> {
gstPlayBin.getBus().disconnect(endOfStreamListener); gstPlayBin.stop();
gstPlayBin.getBus().disconnect(stateChangeListener); gstPlayBin.getBus().disconnect(endOfStreamListener);
gstPlayBin.getBus().disconnect(errorListener); gstPlayBin.getBus().disconnect(stateChangeListener);
gstPlayBin.dispose(); gstPlayBin.getBus().disconnect(errorListener);
fxAppSink.clear(); gstPlayBin.getBus().dispose();
gstPlayBin = null; gstPlayBin.dispose();
fxAppSink.clear();
gstPlayBin = null;
});
} }
videoPanel.removeAll(); videoPanel.removeAll();
resetComponents(); resetComponents();
@ -425,6 +424,10 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
@Override @Override
public boolean isSupported(AbstractFile file) { public boolean isSupported(AbstractFile file) {
if (!IS_GST_ENABLED) {
return false;
}
String extension = file.getNameExtension(); String extension = file.getNameExtension();
/** /**
* Although it seems too restrictive, requiring both a supported * 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. * 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 @Override
protected void done() { protected void done() {
try { try {
@ -551,44 +559,49 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
return; return;
} }
// Initialize Gstreamer. It is safe to call this for every file. GstStatus loadStatus = GstLoader.tryLoad();
// It was moved here from the constructor because having it happen if (loadStatus == GstStatus.FAILURE) {
// earlier resulted in conflicts on Linux. See JIRA-5888. MessageNotifyUtil.Message.error(Bundle.MediaPlayerPanel_playbackDisabled());
if (!PlatformUtil.isWindowsOS()) {
Gst.init(); // This will disable the panel for future use.
IS_GST_ENABLED = false;
return;
} }
//Video is ready for playback. Create new components Gst.getExecutor().submit(() -> {
gstPlayBin = new PlayBin("VideoPlayer", tempFile.toURI()); //Video is ready for playback. Create new components
//Configure event handling gstPlayBin = new PlayBin("VideoPlayer", tempFile.toURI());
if (gstPlayBin != null) { //Configure event handling
Bus playBinBus = gstPlayBin.getBus(); Bus playBinBus = gstPlayBin.getBus();
playBinBus.connect(endOfStreamListener); playBinBus.connect(endOfStreamListener);
playBinBus.connect(stateChangeListener); playBinBus.connect(stateChangeListener);
playBinBus.connect(errorListener); playBinBus.connect(errorListener);
}
if (this.isCancelled()) { if (this.isCancelled()) {
return; return;
} }
JFXPanel fxPanel = new JFXPanel(); JFXPanel fxPanel = new JFXPanel();
videoPanel.removeAll(); videoPanel.removeAll();
videoPanel.setLayout(new BoxLayout(videoPanel, BoxLayout.Y_AXIS)); videoPanel.setLayout(new BoxLayout(videoPanel, BoxLayout.Y_AXIS));
videoPanel.add(fxPanel); videoPanel.add(fxPanel);
fxAppSink = new JavaFxAppSink("JavaFxAppSink", fxPanel); fxAppSink = new JavaFxAppSink("JavaFxAppSink", fxPanel);
if (gstPlayBin != null) { if (gstPlayBin != null) {
gstPlayBin.setVideoSink(fxAppSink); gstPlayBin.setVideoSink(fxAppSink);
} }
if (this.isCancelled()) { if (this.isCancelled()) {
return; return;
} }
if (gstPlayBin != null) { if (gstPlayBin != null) {
gstPlayBin.setVolume((audioSlider.getValue() * 2.0) / 100.0); gstPlayBin.setVolume((audioSlider.getValue() * 2.0) / 100.0);
gstPlayBin.pause(); gstPlayBin.pause();
} }
timer.start();
enableComponents(true); timer.start();
SwingUtilities.invokeLater(() -> {
enableComponents(true);
});
});
} catch (CancellationException ex) { } catch (CancellationException ex) {
logger.log(Level.INFO, "Media buffering was canceled."); //NON-NLS logger.log(Level.INFO, "Media buffering was canceled."); //NON-NLS
} catch (InterruptedException ex) { } catch (InterruptedException ex) {
@ -607,23 +620,29 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
if (!progressSlider.getValueIsAdjusting() && gstPlayBin != null) { if (!progressSlider.getValueIsAdjusting() && gstPlayBin != null) {
sliderLock.acquireUninterruptibly(); Gst.getExecutor().submit(() -> {
long position = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS); try {
long duration = gstPlayBin.queryDuration(TimeUnit.NANOSECONDS); sliderLock.acquireUninterruptibly();
/** long position = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
* Duration may not be known until there is video data in the long duration = gstPlayBin.queryDuration(TimeUnit.NANOSECONDS);
* pipeline. We start this updater when data-flow has just been /**
* initiated so buffering may still be in progress. * Duration may not be known until there is video data
*/ * in the pipeline. We start this updater when data-flow
if (duration >= 0 && position >= 0) { * has just been initiated so buffering may still be in
double relativePosition = (double) position / duration; * progress.
progressSlider.setValue((int) (relativePosition * PROGRESS_SLIDER_SIZE)); */
} if (duration >= 0 && position >= 0) {
double relativePosition = (double) position / duration;
progressSlider.setValue((int) (relativePosition * PROGRESS_SLIDER_SIZE));
}
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(() -> {
updateTimeLabel(position, duration); 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 * thumb at the given width and height. It also paints the track blue as
* the thumb progresses. * the thumb progresses.
* *
* @param slider JSlider component * @param slider JSlider component
* @param thumbDimension * @param thumbDimension
*/ */
public CircularJSliderUI(JSlider slider, Dimension thumbDimension) { public CircularJSliderUI(JSlider slider, Dimension thumbDimension) {
@ -991,96 +1010,104 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
private void rewindButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_rewindButtonActionPerformed private void rewindButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_rewindButtonActionPerformed
if (gstPlayBin != null) { Gst.getExecutor().submit(() -> {
long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS); if (gstPlayBin != null) {
//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();
long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS); 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, gstPlayBin.seek(playBackRate,
Format.TIME, Format.TIME,
//FLUSH - flushes the pipeline //FLUSH - flushes the pipeline
//ACCURATE - video will seek exactly to the position requested //ACCURATE - video will seek exactly to the position requested
EnumSet.of(SeekFlags.FLUSH, SeekFlags.ACCURATE), EnumSet.of(SeekFlags.FLUSH, SeekFlags.ACCURATE),
//Set the start position to newTime //Set the start position to newTime
SeekType.SET, currentTime, SeekType.SET, newTime,
//Do nothing for the end position //Do nothing for the end position
SeekType.NONE, -1); 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 private void fastForwardButtonActionPerformed(java.awt.event.ActionEvent evt) {
if (gstPlayBin != null) { Gst.getExecutor().submit(() -> {
double playBackRate = getPlayBackRate(); if (gstPlayBin != null) {
long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS); long duration = gstPlayBin.queryDuration(TimeUnit.NANOSECONDS);
gstPlayBin.seek(playBackRate, long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
Format.TIME, //Skip 30 seconds.
//FLUSH - flushes the pipeline long fastForwardDelta = TimeUnit.NANOSECONDS.convert(SKIP_IN_SECONDS, TimeUnit.SECONDS);
//ACCURATE - video will seek exactly to the position requested //Don't allow skipping within 2 seconds of video ending. Skipping right to
EnumSet.of(SeekFlags.FLUSH, SeekFlags.ACCURATE), //the end causes undefined behavior for some gstreamer plugins.
//Set the position to the currentTime, we are only adjusting the long twoSecondsInNano = TimeUnit.NANOSECONDS.convert(2, TimeUnit.SECONDS);
//playback rate. if ((duration - currentTime) <= twoSecondsInNano) {
SeekType.SET, currentTime, return;
SeekType.NONE, 0); }
}
}//GEN-LAST:event_playBackSpeedComboBoxActionPerformed 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 // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel VolumeIcon; private javax.swing.JLabel VolumeIcon;

View 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() {
}
}

View File

@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.datamodel;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.text.MessageFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.List; import java.util.List;
@ -28,7 +29,6 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import org.openide.nodes.Sheet; import org.openide.nodes.Sheet;
@ -66,6 +66,7 @@ import org.sleuthkit.datamodel.Tag;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.texttranslation.utils.FileNameTranslationUtil;
/** /**
* An abstract node that encapsulates AbstractFile data * 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); IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, weakPcl);
} }
} }
try { try {
//See JIRA-5971 //See JIRA-5971
//Attempt to cache file path during construction of this UI component. //Attempt to cache file path during construction of this UI component.
this.content.getUniquePath(); this.content.getUniquePath();
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Failed attempt to cache the " logger.log(Level.SEVERE, String.format("Failed attempt to cache the "
+ "unique path of the abstract file instance. Name: %s (objID=%d)", + "unique path of the abstract file instance. Name: %s (objID=%d)",
this.content.getName(), this.content.getId()), ex); this.content.getName(), this.content.getId()), ex);
} }
if (TextTranslationService.getInstance().hasProvider() && UserPreferences.displayTranslatedFileNames()) { 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 * Translates the name of the file this node represents. An empty string
* name is in english or if there is now translation service available. * will be returned if the translation fails for any reason.
*
* @return The translated file name or the empty string.
*/ */
String getTranslatedFileName() { String getTranslatedFileName() {
//If already in complete English, don't translate. try {
if (content.getName().matches("^\\p{ASCII}+$")) { 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 ""; 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 "";
} }
/** /**

View File

@ -76,6 +76,9 @@ import org.sleuthkit.datamodel.Tag;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; 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 * 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 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 * 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 List<NodeProperty<? extends Object>> customProperties;
private final PropertyChangeListener appEventListener = new PropertyChangeListener() { private final PropertyChangeListener listener = new PropertyChangeListener() {
@Override @Override
public void propertyChange(PropertyChangeEvent evt) { public void propertyChange(PropertyChangeEvent evt) {
String eventType = evt.getPropertyName(); String eventType = evt.getPropertyName();
@ -148,25 +152,19 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
updateSheet(); updateSheet();
} }
} else if (eventType.equals(Case.Events.CONTENT_TAG_ADDED.toString())) { } else if (eventType.equals(Case.Events.CONTENT_TAG_ADDED.toString())) {
if (srcContent != null) { ContentTagAddedEvent event = (ContentTagAddedEvent) evt;
ContentTagAddedEvent event = (ContentTagAddedEvent) evt; if (event.getAddedTag().getContent().equals(srcContent)) {
if (event.getAddedTag().getContent().equals(srcContent)) { updateSheet();
updateSheet();
}
} }
} else if (eventType.equals(Case.Events.CONTENT_TAG_DELETED.toString())) { } else if (eventType.equals(Case.Events.CONTENT_TAG_DELETED.toString())) {
if (srcContent != null) { ContentTagDeletedEvent event = (ContentTagDeletedEvent) evt;
ContentTagDeletedEvent event = (ContentTagDeletedEvent) evt; if (event.getDeletedTagInfo().getContentID() == srcContent.getId()) {
if (event.getDeletedTagInfo().getContentID() == srcContent.getId()) { updateSheet();
updateSheet();
}
} }
} else if (eventType.equals(Case.Events.CR_COMMENT_CHANGED.toString())) { } else if (eventType.equals(Case.Events.CR_COMMENT_CHANGED.toString())) {
if (srcContent != null) { CommentChangedEvent event = (CommentChangedEvent) evt;
CommentChangedEvent event = (CommentChangedEvent) evt; if (event.getContentID() == srcContent.getId()) {
if (event.getContentID() == srcContent.getId()) { updateSheet();
updateSheet();
}
} }
} else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) { } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
if (evt.getNewValue() == null) { if (evt.getNewValue() == null) {
@ -179,14 +177,41 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
} else if (eventType.equals(NodeSpecificEvents.SCO_AVAILABLE.toString()) && !UserPreferences.getHideSCOColumns()) { } else if (eventType.equals(NodeSpecificEvents.SCO_AVAILABLE.toString()) && !UserPreferences.getHideSCOColumns()) {
SCOData scoData = (SCOData) evt.getNewValue(); SCOData scoData = (SCOData) evt.getNewValue();
if (scoData.getScoreAndDescription() != null) { 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) { 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) { 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 * listener held by event publishers prevents garbage collection of this
* node. * node.
*/ */
private final PropertyChangeListener weakAppEventListener = WeakListeners.propertyChange(appEventListener, null); private final PropertyChangeListener weakListener = WeakListeners.propertyChange(listener, null);
/** /**
* Constructs a BlackboardArtifactNode, an AbstractNode implementation that * Constructs a BlackboardArtifactNode, an AbstractNode implementation that
@ -207,7 +232,6 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
* @param artifact The artifact to represent. * @param artifact The artifact to represent.
* @param iconPath The path to the icon for the artifact type. * @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) { public BlackboardArtifactNode(BlackboardArtifact artifact, String iconPath) {
super(artifact, createLookup(artifact)); super(artifact, createLookup(artifact));
this.artifact = artifact; this.artifact = artifact;
@ -218,20 +242,25 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
/* /*
* Calling this getter causes the unique path of the source * Calling this getter causes the unique path of the source
* content to be cached in the Content object. This is * content to be cached in the Content object. This is
* advantageous if this node is constructed in a background * advantageous as long as this node is constructed in a
* thread instead of a UI thread. * background thread instead of a UI thread.
*/ */
srcContent.getUniquePath(); srcContent.getUniquePath();
} catch (TskCoreException ex) { } 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; break;
} }
} }
if (srcContent == null) {
throw new IllegalArgumentException(MessageFormat.format("Artifact missing source content (artifact objID={0})", artifact));
}
setName(Long.toString(artifact.getArtifactID())); setName(Long.toString(artifact.getArtifactID()));
setDisplayName(Bundle.BlackboardArtifactNode_displayName_artifact(artifact.getDisplayName())); String displayName = srcContent.getName();
setDisplayName(displayName);
setShortDescription(displayName);
setIconBaseWithExtension(iconPath); 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. * Unregisters this node's application event listener.
*/ */
private void unregisterListener() { 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) { } catch (TskCoreException ex) {
logger.log(Level.SEVERE, MessageFormat.format("Error getting linked file of artifact (artifact objID={0})", artifact.getId()), ex); //NON-NLS 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 * 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. * 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) { if (null != file) {
actionsList.add(ViewFileInTimelineAction.createViewSourceFileAction(file)); actionsList.add(ViewFileInTimelineAction.createViewSourceFileAction(file));
} }
@ -348,14 +379,14 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
* @return The source content name. * @return The source content name.
*/ */
public String getSourceName() { public String getSourceName() {
String name = ""; return srcContent.getName();
if (srcContent != null) {
name = srcContent.getName();
}
return name;
} }
@NbBundle.Messages({ @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.displayName=Result Type",
"BlackboardArtifactNode.createSheet.artifactType.name=Result Type", "BlackboardArtifactNode.createSheet.artifactType.name=Result Type",
"BlackboardArtifactNode.createSheet.artifactDetails.displayName=Result Details", "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 * 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"), sheetSet.put(new NodeProperty<>(
NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.srcFile.displayName"), Bundle.BlackboardArtifactNode_createSheet_srcFile_name(),
Bundle.BlackboardArtifactNode_createSheet_srcFile_displayName(),
NO_DESCR, 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()) { if (!UserPreferences.getHideSCOColumns()) {
/* /*
@ -396,12 +449,24 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
* will fire a PropertyChangeEvent when the computation is completed * will fire a PropertyChangeEvent when the computation is completed
* and this node's PropertyChangeListener will update the sheet. * 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<>(
sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_comment_name(), Bundle.BlackboardArtifactNode_createSheet_comment_displayName(), VALUE_LOADING, "")); 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()) { 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)); BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT));
if (attribute != null) { if (attribute != null) {
BlackboardArtifact associatedArtifact = Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboardArtifact(attribute.getValueLong()); 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"), NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.artifactType.displayName"),
NO_DESCR, NO_DESCR,
associatedArtifact.getDisplayName())); 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"), NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.artifactDetails.displayName"),
NO_DESCR, NO_DESCR,
associatedArtifact.getShortDescription())); associatedArtifact.getShortDescription()));
@ -459,15 +526,17 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
if (artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_EXT_MISMATCH_DETECTED.getTypeID()) { if (artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_EXT_MISMATCH_DETECTED.getTypeID()) {
String ext = ""; //NON-NLS String ext = ""; //NON-NLS
String actualMimeType = ""; //NON-NLS String actualMimeType = ""; //NON-NLS
if (srcContent != null && srcContent instanceof AbstractFile) { if (srcContent instanceof AbstractFile) {
AbstractFile file = (AbstractFile) srcContent; AbstractFile file = (AbstractFile) srcContent;
ext = file.getNameExtension(); ext = file.getNameExtension();
actualMimeType = file.getMIMEType(); actualMimeType = file.getMIMEType();
if (actualMimeType == null) { if (actualMimeType == null) {
actualMimeType = ""; //NON-NLS 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"), NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.ext.displayName"),
NO_DESCR, NO_DESCR,
ext)); ext));
@ -484,12 +553,11 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
*/ */
if (Arrays.asList(SHOW_UNIQUE_PATH).contains(artifactTypeId)) { if (Arrays.asList(SHOW_UNIQUE_PATH).contains(artifactTypeId)) {
String sourcePath = ""; //NON-NLS String sourcePath = ""; //NON-NLS
if (srcContent != null) { try {
try { sourcePath = srcContent.getUniquePath();
sourcePath = srcContent.getUniquePath(); } catch (TskCoreException ex) {
} catch (TskCoreException ex) { logger.log(Level.SEVERE, MessageFormat.format("Error getting unique path of source content (artifact objID={0})", artifact.getId()), ex); //NON-NLS
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) { if (sourcePath.isEmpty() == false) {
@ -506,45 +574,50 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
* sheet. Otherwise, add the data source to the sheet. * sheet. Otherwise, add the data source to the sheet.
*/ */
if (Arrays.asList(SHOW_FILE_METADATA).contains(artifactTypeId)) { if (Arrays.asList(SHOW_FILE_METADATA).contains(artifactTypeId)) {
AbstractFile file = srcContent != null && srcContent instanceof AbstractFile ? (AbstractFile) srcContent : null; AbstractFile file = srcContent instanceof AbstractFile ? (AbstractFile) srcContent : null;
sheetSet.put(new NodeProperty<>(NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileModifiedTime.name"), sheetSet.put(new NodeProperty<>(
NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileModifiedTime.name"),
NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileModifiedTime.displayName"), NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileModifiedTime.displayName"),
"", "",
file == null ? "" : ContentUtils.getStringTime(file.getMtime(), file))); 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"), NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileChangedTime.displayName"),
"", "",
file == null ? "" : ContentUtils.getStringTime(file.getCtime(), file))); 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"), NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileAccessedTime.displayName"),
"", "",
file == null ? "" : ContentUtils.getStringTime(file.getAtime(), file))); 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"), NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileCreatedTime.displayName"),
"", "",
file == null ? "" : ContentUtils.getStringTime(file.getCrtime(), file))); 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"), NbBundle.getMessage(BlackboardArtifactNode.class, "ContentTagNode.createSheet.fileSize.displayName"),
"", "",
file == null ? "" : file.getSize())); 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(), Bundle.BlackboardArtifactNode_createSheet_artifactMD5_displayName(),
"", "",
file == null ? "" : StringUtils.defaultString(file.getMd5Hash()))); file == null ? "" : StringUtils.defaultString(file.getMd5Hash())));
} }
} else { } else {
String dataSourceStr = ""; String dataSourceStr = "";
if (srcContent != null) { try {
try { Content dataSource = srcContent.getDataSource();
Content dataSource = srcContent.getDataSource(); if (dataSource != null) {
if (dataSource != null) { dataSourceStr = dataSource.getName();
dataSourceStr = dataSource.getName(); } else {
} else { dataSourceStr = getRootAncestorName();
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
} }
} 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) { if (dataSourceStr.isEmpty() == false) {
@ -563,24 +636,27 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
if (artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF.getTypeID()) { if (artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF.getTypeID()) {
long size = 0; long size = 0;
String path = ""; //NON-NLS String path = ""; //NON-NLS
if (srcContent != null && srcContent instanceof AbstractFile) { if (srcContent instanceof AbstractFile) {
AbstractFile af = (AbstractFile) srcContent; AbstractFile af = (AbstractFile) srcContent;
size = af.getSize(); size = af.getSize();
try { try {
path = af.getUniquePath(); path = af.getUniquePath();
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
path = af.getParentPath(); 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"), NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.fileSize.displayName"),
NO_DESCR, NO_DESCR,
size)); size));
sheetSet.put(new NodeProperty<>( sheetSet
NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.path.name"), .put(new NodeProperty<>(
NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.path.displayName"), NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.path.name"),
NO_DESCR, NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.path.displayName"),
path)); NO_DESCR,
path));
} }
return sheet; return sheet;
@ -597,9 +673,7 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
List<Tag> tags = new ArrayList<>(); List<Tag> tags = new ArrayList<>();
try { try {
tags.addAll(Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsByArtifact(artifact)); 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) { } 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); 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 @Override
protected final CorrelationAttributeInstance getCorrelationAttributeInstance() { protected final CorrelationAttributeInstance getCorrelationAttributeInstance() {
CorrelationAttributeInstance correlationAttribute = null; CorrelationAttributeInstance correlationAttribute = null;
if (srcContent != null && CentralRepository.isEnabled() && srcContent instanceof AbstractFile) { if (CentralRepository.isEnabled() && srcContent instanceof AbstractFile) {
correlationAttribute = CorrelationAttributeUtil.getCorrAttrForFile((AbstractFile) srcContent); correlationAttribute = CorrelationAttributeUtil.getCorrAttrForFile((AbstractFile) srcContent);
} }
return correlationAttribute; return correlationAttribute;
@ -695,7 +769,7 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
*/ */
Score score = Score.NO_SCORE; Score score = Score.NO_SCORE;
String description = Bundle.BlackboardArtifactNode_createSheet_noScore_description(); String description = Bundle.BlackboardArtifactNode_createSheet_noScore_description();
if (srcContent != null && srcContent instanceof AbstractFile) { if (srcContent instanceof AbstractFile) {
if (((AbstractFile) srcContent).getKnown() == TskData.FileKnown.BAD) { if (((AbstractFile) srcContent).getKnown() == TskData.FileKnown.BAD) {
score = Score.NOTABLE_SCORE; score = Score.NOTABLE_SCORE;
description = Bundle.BlackboardArtifactNode_createSheet_notableFile_description(); description = Bundle.BlackboardArtifactNode_createSheet_notableFile_description();
@ -725,7 +799,7 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
/* /*
* Is the artifact's source content notable? * Is the artifact's source content notable?
*/ */
if (score == Score.NO_SCORE && srcContent != null) { if (score == Score.NO_SCORE) {
try { try {
if (!srcContent.getArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT).isEmpty()) { if (!srcContent.getArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT).isEmpty()) {
score = Score.INTERESTING_SCORE; score = Score.INTERESTING_SCORE;
@ -814,7 +888,7 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
parentName = parent.getName(); parentName = parent.getName();
} }
} catch (TskCoreException ex) { } 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 "";
} }
return parentName; return parentName;
@ -1036,8 +1110,8 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
* @param attribute The correlation attribute instance to use for the * @param attribute The correlation attribute instance to use for the
* central repository lookup. * central repository lookup.
* *
* @deprecated Do not use. The other occurrences property is now computed in a * @deprecated Do not use. The other occurrences property is now computed in
* background thread and added to the property sheet via property change * a background thread and added to the property sheet via property change
* event. * event.
*/ */
@NbBundle.Messages({"BlackboardArtifactNode.createSheet.comment.name=C", @NbBundle.Messages({"BlackboardArtifactNode.createSheet.comment.name=C",

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2013-2018 Basis Technology Corp. * Copyright 2013-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * 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.List;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.Action; import javax.swing.Action;
import org.openide.nodes.Children;
import org.openide.nodes.Sheet; import org.openide.nodes.Sheet;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.openide.util.lookup.Lookups; import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.coreutils.Logger; 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.ViewArtifactInTimelineAction;
import org.sleuthkit.autopsy.timeline.actions.ViewFileInTimelineAction; import org.sleuthkit.autopsy.timeline.actions.ViewFileInTimelineAction;
import org.sleuthkit.datamodel.AbstractFile; 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 * tag name nodes have tag type child nodes; tag type nodes are the parents of
* either content or blackboard artifact tag nodes. * 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 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 static final String ICON_PATH = "org/sleuthkit/autopsy/images/green-tag-icon-16.png"; //NON-NLS
private final BlackboardArtifactTag tag; private final BlackboardArtifactTag tag;
public BlackboardArtifactTagNode(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.setName(tag.getContent().getName());
super.setDisplayName(tag.getContent().getName()); super.setDisplayName(tag.getContent().getName());
this.setIconBaseWithExtension(ICON_PATH); this.setIconBaseWithExtension(ICON_PATH);
@ -75,6 +73,7 @@ public class BlackboardArtifactTagNode extends DisplayableItemNode {
NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.srcFile.text"), NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.srcFile.text"),
"", "",
tag.getContent().getName())); tag.getContent().getName()));
addOriginalNameProp(properties);
String contentPath; String contentPath;
try { try {
contentPath = tag.getContent().getUniquePath(); 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 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"); contentPath = NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.unavail.text");
} }
properties.put(new NodeProperty<>( properties.put(new NodeProperty<>(
NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.srcFilePath.text"), NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.srcFilePath.text"),
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); return visitor.visit(this);
} }
@Override
public boolean isLeafTypeNode() {
return true;
}
@Override @Override
public String getItemType() { public String getItemType() {
return getClass().getName(); return getClass().getName();

View File

@ -11,8 +11,6 @@ ArtifactTypeNode.createSheet.childCnt.name=Child Count
ArtifactTypeNode.createSheet.childCnt.displayName=Child Count ArtifactTypeNode.createSheet.childCnt.displayName=Child Count
ArtifactTypeNode.createSheet.childCnt.desc=no description ArtifactTypeNode.createSheet.childCnt.desc=no description
BlackboardArtifactNode.noDesc.text=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.name=Extension
BlackboardArtifactNode.createSheet.ext.displayName=Extension BlackboardArtifactNode.createSheet.ext.displayName=Extension
BlackboardArtifactNode.createSheet.mimeType.name=MIME Type BlackboardArtifactNode.createSheet.mimeType.name=MIME Type

View File

@ -74,10 +74,12 @@ BlackboardArtifactNode.createSheet.path.displayName=Path
BlackboardArtifactNode.createSheet.path.name=Path BlackboardArtifactNode.createSheet.path.name=Path
BlackboardArtifactNode.createSheet.score.displayName=S BlackboardArtifactNode.createSheet.score.displayName=S
BlackboardArtifactNode.createSheet.score.name=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.taggedItem.description=Result or associated file has been tagged.
BlackboardArtifactNode.createSheet.tags.displayName=Tags BlackboardArtifactNode.createSheet.tags.displayName=Tags
# {0} - artifactDisplayName
BlackboardArtifactNode.displayName.artifact={0} Artifact
BlackboardArtifactTagNode.createSheet.userName.text=User Name BlackboardArtifactTagNode.createSheet.userName.text=User Name
BlackboardArtifactTagNode.viewSourceArtifact.text=View Source Result BlackboardArtifactTagNode.viewSourceArtifact.text=View Source Result
Category.five=CAT-5: Non-pertinent 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 Category.zero=CAT-0: Uncategorized
ContentTagNode.createSheet.artifactMD5.displayName=MD5 Hash ContentTagNode.createSheet.artifactMD5.displayName=MD5 Hash
ContentTagNode.createSheet.artifactMD5.name=MD5 Hash ContentTagNode.createSheet.artifactMD5.name=MD5 Hash
ContentTagNode.createSheet.origFileName=Original Name
ContentTagNode.createSheet.userName.text=User Name ContentTagNode.createSheet.userName.text=User Name
DeletedContent.allDelFilter.text=All DeletedContent.allDelFilter.text=All
DeletedContent.createSheet.filterType.desc=no description 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.displayName=Child Count
ArtifactTypeNode.createSheet.childCnt.desc=no description ArtifactTypeNode.createSheet.childCnt.desc=no description
BlackboardArtifactNode.noDesc.text=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.name=Extension
BlackboardArtifactNode.createSheet.ext.displayName=Extension BlackboardArtifactNode.createSheet.ext.displayName=Extension
BlackboardArtifactNode.createSheet.mimeType.name=MIME Type BlackboardArtifactNode.createSheet.mimeType.name=MIME Type
@ -345,6 +346,8 @@ TagNameNode.bbArtTagTypeNodeKey.text=Result Tags
TagNameNode.bookmark.text=Bookmark TagNameNode.bookmark.text=Bookmark
TagNameNode.createSheet.name.name=Name TagNameNode.createSheet.name.name=Name
TagNameNode.createSheet.name.displayName=Name TagNameNode.createSheet.name.displayName=Name
TagNode.propertySheet.origName=Original Name
TagNode.propertySheet.origNameDisplayName=Original Name
TagsNode.displayName.text=Tags TagsNode.displayName.text=Tags
TagsNode.createSheet.name.name=Name TagsNode.createSheet.name.name=Name
TagsNode.createSheet.name.displayName=Name TagsNode.createSheet.name.displayName=Name

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2013-2016 Basis Technology Corp. * Copyright 2013-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -24,7 +24,6 @@ import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.Action; import javax.swing.Action;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.openide.nodes.Children;
import org.openide.nodes.Sheet; import org.openide.nodes.Sheet;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages; 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 * 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 * 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 * content and artifact tags, grouped first by tag type, then by tag name.
* name.
*/ */
class ContentTagNode extends DisplayableItemNode { class ContentTagNode extends TagNode {
private static final Logger LOGGER = Logger.getLogger(ContentTagNode.class.getName()); 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 static final String ICON_PATH = "org/sleuthkit/autopsy/images/blue-tag-icon-16.png"; //NON-NLS
private final ContentTag tag; private final ContentTag tag;
public ContentTagNode(ContentTag tag) { ContentTagNode(ContentTag tag) {
super(Children.LEAF, Lookups.fixed(tag, tag.getContent())); super(Lookups.fixed(tag, tag.getContent()), tag.getContent());
super.setName(tag.getContent().getName()); super.setName(tag.getContent().getName());
super.setDisplayName(tag.getContent().getName()); super.setDisplayName(tag.getContent().getName());
this.setIconBaseWithExtension(ICON_PATH); this.setIconBaseWithExtension(ICON_PATH);
@ -58,6 +55,7 @@ class ContentTagNode extends DisplayableItemNode {
} }
@Messages({ @Messages({
"ContentTagNode.createSheet.origFileName=Original Name",
"ContentTagNode.createSheet.artifactMD5.displayName=MD5 Hash", "ContentTagNode.createSheet.artifactMD5.displayName=MD5 Hash",
"ContentTagNode.createSheet.artifactMD5.name=MD5 Hash", "ContentTagNode.createSheet.artifactMD5.name=MD5 Hash",
"ContentTagNode.createSheet.userName.text=User Name"}) "ContentTagNode.createSheet.userName.text=User Name"})
@ -79,15 +77,19 @@ class ContentTagNode extends DisplayableItemNode {
properties = Sheet.createPropertiesSet(); properties = Sheet.createPropertiesSet();
propertySheet.put(properties); 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"), NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.file.displayName"),
"", "",
content.getName())); 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"), NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.filePath.displayName"),
"", "",
contentPath)); 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"), NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.comment.displayName"),
"", "",
tag.getComment())); tag.getComment()));
@ -95,23 +97,28 @@ class ContentTagNode extends DisplayableItemNode {
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileModifiedTime.displayName"), NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileModifiedTime.displayName"),
"", "",
file != null ? ContentUtils.getStringTime(file.getMtime(), file) : "")); 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"), NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileChangedTime.displayName"),
"", "",
file != null ? ContentUtils.getStringTime(file.getCtime(), file) : "")); 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"), NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileAccessedTime.displayName"),
"", "",
file != null ? ContentUtils.getStringTime(file.getAtime(), file) : "")); 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"), NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileCreatedTime.displayName"),
"", "",
file != null ? ContentUtils.getStringTime(file.getCrtime(), file) : "")); 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"), NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileSize.displayName"),
"", "",
content.getSize())); 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(), Bundle.ContentTagNode_createSheet_artifactMD5_displayName(),
"", "",
file != null ? StringUtils.defaultString(file.getMd5Hash()) : "")); file != null ? StringUtils.defaultString(file.getMd5Hash()) : ""));
@ -128,8 +135,7 @@ class ContentTagNode extends DisplayableItemNode {
List<Action> actions = new ArrayList<>(); List<Action> actions = new ArrayList<>();
actions.addAll(Arrays.asList(super.getActions(context))); actions.addAll(Arrays.asList(super.getActions(context)));
AbstractFile file = getLookup().lookup(AbstractFile.class AbstractFile file = getLookup().lookup(AbstractFile.class);
);
if (file != null) { if (file != null) {
actions.add(ViewFileInTimelineAction.createViewFileAction(file)); actions.add(ViewFileInTimelineAction.createViewFileAction(file));
} }
@ -144,13 +150,9 @@ class ContentTagNode extends DisplayableItemNode {
return visitor.visit(this); return visitor.visit(this);
} }
@Override
public boolean isLeafTypeNode() {
return true;
}
@Override @Override
public String getItemType() { public String getItemType() {
return getClass().getName(); return getClass().getName();
} }
} }

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2017 Basis Technology Corp. * Copyright 2012-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * 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.AbstractNode;
import org.openide.nodes.Children; import org.openide.nodes.Children;
import org.openide.nodes.Sheet;
import org.openide.util.Lookup; import org.openide.util.Lookup;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
@ -142,4 +143,26 @@ public abstract class DisplayableItemNode extends AbstractNode {
return selectedChildNodeInfo; 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);
}
} }

View 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));
}
}
}
}

View File

@ -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 * @return The Future of the task, may be used for task cancellation by
* calling Future.cancel(true). * calling Future.cancel(true).
*/ */
public static Future<?> submitTask(AbstractNodePropertySheetTask<?> task) { private static Future<?> submitTask(AbstractNodePropertySheetTask<?> task) {
return executor.submit(task); return executor.submit(task);
} }
@ -104,12 +104,22 @@ public abstract class AbstractNodePropertySheetTask<T extends AbstractNode> impl
* *
* @param node The AbstractNode. * @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; 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 @Override
final public void run() { public final void run() {
try { try {
T node = this.weakNodeRef.get(); T node = this.weakNodeRef.get();
PropertyChangeListener listener = this.weakListenerRef.get(); PropertyChangeListener listener = this.weakListenerRef.get();

View 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);
}
}

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2019 Basis Technology Corp. * Copyright 2012-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -187,27 +187,6 @@ public class DataResultFilterNode extends FilterNode {
return propertySets; 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 * Adds information about which child node of this node, if any, should be
* selected. Can be null. * selected. Can be null.

View File

@ -23,7 +23,6 @@ import com.google.common.cache.CacheBuilder;
import com.google.common.io.Files; import com.google.common.io.Files;
import java.awt.Image; import java.awt.Image;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.IOException; import java.io.IOException;
import java.io.Reader; import java.io.Reader;
import java.nio.file.Paths; import java.nio.file.Paths;
@ -95,6 +94,7 @@ class FileSearch {
.build(); .build();
private static final int PREVIEW_SIZE = 256; private static final int PREVIEW_SIZE = 256;
private static volatile TextSummarizer summarizerToUse = null; 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. * 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 + "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 * Get the video thumbnails for a file which exists in a
* VideoThumbnailsWrapper and update the VideoThumbnailsWrapper to include * VideoThumbnailsWrapper and update the VideoThumbnailsWrapper to include
@ -476,7 +490,6 @@ class FileSearch {
cacheDirectory = null; cacheDirectory = null;
logger.log(Level.WARNING, "Unable to get cache directory, video thumbnails will not be saved", ex); 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()) { if (cacheDirectory == null || file.getMd5Hash() == null || !Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, file.getMd5Hash()).toFile().exists()) {
java.io.File tempFile; java.io.File tempFile;
try { try {
@ -488,7 +501,7 @@ class FileSearch {
0, 0,
0, 0,
0}; 0};
thumbnailWrapper.setThumbnails(createDefaultThumbnailList(), framePositions); thumbnailWrapper.setThumbnails(createDefaultThumbnailList(VIDEO_DEFAULT_IMAGE), framePositions);
return; return;
} }
if (tempFile.exists() == false || tempFile.length() < file.getSize()) { if (tempFile.exists() == false || tempFile.length() < file.getSize()) {
@ -502,7 +515,7 @@ class FileSearch {
0, 0,
0, 0,
0}; 0};
thumbnailWrapper.setThumbnails(createDefaultThumbnailList(), framePositions); thumbnailWrapper.setThumbnails(createDefaultThumbnailList(VIDEO_DEFAULT_IMAGE), framePositions);
return; return;
} }
ContentUtils.writeToFile(file, tempFile, progress, null, true); ContentUtils.writeToFile(file, tempFile, progress, null, true);
@ -523,7 +536,7 @@ class FileSearch {
0, 0,
0, 0,
0}; 0};
thumbnailWrapper.setThumbnails(createDefaultThumbnailList(), framePositions); thumbnailWrapper.setThumbnails(createDefaultThumbnailList(VIDEO_DEFAULT_IMAGE), framePositions);
return; return;
} }
double fps = videoFile.get(5); // gets frame per second double fps = videoFile.get(5); // gets frame per second
@ -535,7 +548,7 @@ class FileSearch {
0, 0,
0, 0,
0}; 0};
thumbnailWrapper.setThumbnails(createDefaultThumbnailList(), framePositions); thumbnailWrapper.setThumbnails(createDefaultThumbnailList(VIDEO_DEFAULT_IMAGE), framePositions);
return; return;
} }
if (Thread.interrupted()) { if (Thread.interrupted()) {
@ -544,7 +557,7 @@ class FileSearch {
0, 0,
0, 0,
0}; 0};
thumbnailWrapper.setThumbnails(createDefaultThumbnailList(), framePositions); thumbnailWrapper.setThumbnails(createDefaultThumbnailList(VIDEO_DEFAULT_IMAGE), framePositions);
return; 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 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. // 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) { if (cacheDirectory != null) {
try { 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) Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, file.getMd5Hash(), i + "-" + framePositions[i] + "." + THUMBNAIL_FORMAT).toFile()); //NON-NLS)
} catch (IOException ex) { } catch (IOException ex) {
logger.log(Level.WARNING, "Unable to save default video thumbnail for " + file.getMd5Hash() + " at frame position " + framePositions[i], 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)) { if (!videoFile.read(imageMatrix)) {
logger.log(Level.WARNING, "Error reading frame at " + framePositions[i] + "ms from {0}", file.getParentPath() + "/" + file.getName()); //NON-NLS 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. // 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) { if (cacheDirectory != null) {
try { 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) Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, file.getMd5Hash(), i + "-" + framePositions[i] + "." + THUMBNAIL_FORMAT).toFile()); //NON-NLS)
} catch (IOException ex) { } catch (IOException ex) {
logger.log(Level.WARNING, "Unable to save default video thumbnail for " + file.getMd5Hash() + " at frame position " + framePositions[i], 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 the image is empty, return since no buffered image can be created.
if (imageMatrix.empty()) { if (imageMatrix.empty()) {
videoThumbnails.add(ImageUtils.getDefaultThumbnail()); videoThumbnails.add(VIDEO_DEFAULT_IMAGE);
if (cacheDirectory != null) { if (cacheDirectory != null) {
try { 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) Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, file.getMd5Hash(), i + "-" + framePositions[i] + "." + THUMBNAIL_FORMAT).toFile()); //NON-NLS)
} catch (IOException ex) { } catch (IOException ex) {
logger.log(Level.WARNING, "Unable to save default video thumbnail for " + file.getMd5Hash() + " at frame position " + framePositions[i], 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} videoFile.release(); // close the file}
} }
} else { } else {
loadSavedThumbnails(cacheDirectory, thumbnailWrapper); loadSavedThumbnails(cacheDirectory, thumbnailWrapper, VIDEO_DEFAULT_IMAGE);
} }
} }
@ -674,7 +687,7 @@ class FileSearch {
* information about the file and the thumbnails * information about the file and the thumbnails
* associated with it. * 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]; int[] framePositions = new int[4];
List<Image> videoThumbnails = new ArrayList<>(); List<Image> videoThumbnails = new ArrayList<>();
int thumbnailNumber = 0; int thumbnailNumber = 0;
@ -683,7 +696,7 @@ class FileSearch {
try { try {
videoThumbnails.add(ImageIO.read(Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, md5, fileName).toFile())); videoThumbnails.add(ImageIO.read(Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, md5, fileName).toFile()));
} catch (IOException ex) { } catch (IOException ex) {
videoThumbnails.add(ImageUtils.getDefaultThumbnail()); videoThumbnails.add(failedVideoThumbImage);
logger.log(Level.WARNING, "Unable to read saved video thumbnail " + fileName + " for " + md5, ex); logger.log(Level.WARNING, "Unable to read saved video thumbnail " + fileName + " for " + md5, ex);
} }
int framePos = Integer.valueOf(FilenameUtils.getBaseName(fileName).substring(2)); int framePos = Integer.valueOf(FilenameUtils.getBaseName(fileName).substring(2));
@ -699,12 +712,12 @@ class FileSearch {
* *
* @return List containing the default thumbnail. * @return List containing the default thumbnail.
*/ */
private static List<Image> createDefaultThumbnailList() { private static List<Image> createDefaultThumbnailList(BufferedImage failedVideoThumbImage) {
List<Image> videoThumbnails = new ArrayList<>(); List<Image> videoThumbnails = new ArrayList<>();
videoThumbnails.add(ImageUtils.getDefaultThumbnail()); videoThumbnails.add(failedVideoThumbImage);
videoThumbnails.add(ImageUtils.getDefaultThumbnail()); videoThumbnails.add(failedVideoThumbImage);
videoThumbnails.add(ImageUtils.getDefaultThumbnail()); videoThumbnails.add(failedVideoThumbImage);
videoThumbnails.add(ImageUtils.getDefaultThumbnail()); videoThumbnails.add(failedVideoThumbImage);
return videoThumbnails; return videoThumbnails;
} }

View File

@ -273,7 +273,6 @@ final class FileSearchData {
= new ImmutableSet.Builder<String>() = new ImmutableSet.Builder<String>()
.add("text/html", //NON-NLS .add("text/html", //NON-NLS
"text/csv", //NON-NLS "text/csv", //NON-NLS
"text/x-log", //NON-NLS
"application/rtf", //NON-NLS "application/rtf", //NON-NLS
"application/pdf", //NON-NLS "application/pdf", //NON-NLS
"application/xhtml+xml", //NON-NLS "application/xhtml+xml", //NON-NLS

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -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() {
}
}

View File

@ -14,8 +14,7 @@
<!-- for viewers --> <!-- for viewers -->
<dependency conf="autopsy_core->*" org="org.freedesktop.gstreamer" name="gst1-java-core" rev="1.0.0"/> <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="jna-platform" rev="5.5.0"/>
<dependency conf="autopsy_core->*" org="net.java.dev.jna" name="platform" rev="3.4.0"/>
<!-- for file search --> <!-- for file search -->
<dependency conf="autopsy_core->*" org="com.github.lgooddatepicker" name="LGoodDatePicker" rev="10.3.1"/> <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"/> <dependency conf="autopsy_core->default" org="com.googlecode.plist" name="dd-plist" rev="1.20"/>
<exclude org="*" ext="*" type="javadoc"/> <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> </dependencies>
</ivy-module> </ivy-module>

View File

@ -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.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.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.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.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-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 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-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.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-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.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.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 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.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.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.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-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-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 file.reference.poi-ooxml-4.0.1.jar=release\\modules\\ext\\poi-ooxml-4.0.1.jar

View File

@ -806,10 +806,6 @@
<runtime-relative-path>ext/sigar-1.6.4.jar</runtime-relative-path> <runtime-relative-path>ext/sigar-1.6.4.jar</runtime-relative-path>
<binary-origin>release/modules/ext/sigar-1.6.4.jar</binary-origin> <binary-origin>release/modules/ext/sigar-1.6.4.jar</binary-origin>
</class-path-extension> </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> <class-path-extension>
<runtime-relative-path>ext/gson-2.8.5.jar</runtime-relative-path> <runtime-relative-path>ext/gson-2.8.5.jar</runtime-relative-path>
<binary-origin>release/modules/ext/gson-2.8.5.jar</binary-origin> <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> <runtime-relative-path>ext/commons-csv-1.4.jar</runtime-relative-path>
<binary-origin>release/modules/ext/commons-csv-1.4.jar</binary-origin> <binary-origin>release/modules/ext/commons-csv-1.4.jar</binary-origin>
</class-path-extension> </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> <class-path-extension>
<runtime-relative-path>ext/imageio-sgi-3.2.jar</runtime-relative-path> <runtime-relative-path>ext/imageio-sgi-3.2.jar</runtime-relative-path>
<binary-origin>release/modules/ext/imageio-sgi-3.2.jar</binary-origin> <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> <runtime-relative-path>ext/imageio-bmp-3.2.jar</runtime-relative-path>
<binary-origin>release/modules/ext/imageio-bmp-3.2.jar</binary-origin> <binary-origin>release/modules/ext/imageio-bmp-3.2.jar</binary-origin>
</class-path-extension> </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> <class-path-extension>
<runtime-relative-path>ext/commons-lang-2.6.jar</runtime-relative-path> <runtime-relative-path>ext/commons-lang-2.6.jar</runtime-relative-path>
<binary-origin>release/modules/ext/commons-lang-2.6.jar</binary-origin> <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> <runtime-relative-path>ext/dom4j-1.6.1.jar</runtime-relative-path>
<binary-origin>release/modules/ext/dom4j-1.6.1.jar</binary-origin> <binary-origin>release/modules/ext/dom4j-1.6.1.jar</binary-origin>
</class-path-extension> </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> <class-path-extension>
<runtime-relative-path>ext/imageio-metadata-3.2.jar</runtime-relative-path> <runtime-relative-path>ext/imageio-metadata-3.2.jar</runtime-relative-path>
<binary-origin>release/modules/ext/imageio-metadata-3.2.jar</binary-origin> <binary-origin>release/modules/ext/imageio-metadata-3.2.jar</binary-origin>

View File

@ -95,7 +95,7 @@ class BrowserLocationAnalyzer(general.AndroidComponentAnalyzer):
longitude = Double.valueOf(resultSet.getString("longitude")) longitude = Double.valueOf(resultSet.getString("longitude"))
attributes = ArrayList() 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_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_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_DATETIME, general.MODULE_NAME, timestamp))

View File

@ -41,6 +41,7 @@ from org.sleuthkit.datamodel import TskCoreException
import traceback import traceback
import general import general
import struct
""" """
Parses cache files that Android maintains for Wifi and cell towers. Adds GPS points to blackboard. 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): def __findGeoLocationsInFile(self, file, abstractFile):
tempBytes = bytearray([0] * 2) # will temporarily hold bytes to be converted into the correct data types
try: try:
inputStream = FileInputStream(file) # 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')
inputStream.read(tempBytes) # version (version, entries) = struct.unpack('>hh', cacheFile.read(4))
i = 0
tempBytes = bytearray([0] * 2) while i < entries:
inputStream.read(tempBytes) # number of location entries key = cacheFile.read(struct.unpack('>h', cacheFile.read(2))[0])
(accuracy, confidence, latitude, longitude, readtime) = struct.unpack('>iiddQ', cacheFile.read(32))
iterations = BigInteger(tempBytes).intValue() timestamp = readtime/1000
i = i + 1
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
attributes = ArrayList() 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, AndroidAnalyzer.MODULE_NAME, latitude)) attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE, general.MODULE_NAME, latitude))
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE, AndroidAnalyzer.MODULE_NAME, longitude)) attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE, general.MODULE_NAME, longitude))
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, AndroidModuleFactorymodule.Name, timestamp)) attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, general.MODULE_NAME, timestamp))
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, AndroidAnalyzer.MODULE_NAME, attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, general.MODULE_NAME,
file.getName() + "Location History")) abstractFile.getName() + " Location History"))
artifact.addAttributes(attributes) artifact.addAttributes(attributes)
#Not storing these for now. #Not storing these for now.
@ -136,15 +101,13 @@ class CacheLocationAnalyzer(general.AndroidComponentAnalyzer):
try: try:
# index the artifact for keyword search # index the artifact for keyword search
blackboard = Case.getCurrentCase().getSleuthkitCase().getBlackboard() blackboard = Case.getCurrentCase().getSleuthkitCase().getBlackboard()
blackboard.postArtifact(artifact, MODULE_NAME) blackboard.postArtifact(artifact, general.MODULE_NAME)
except Blackboard.BlackboardException as ex: except Blackboard.BlackboardException as ex:
self._logger.log(Level.SEVERE, "Unable to index blackboard artifact " + str(artifact.getArtifactID()), ex) self._logger.log(Level.SEVERE, "Unable to index blackboard artifact " + str(artifact.getArtifactID()), ex)
self._logger.log(Level.SEVERE, traceback.format_exc()) self._logger.log(Level.SEVERE, traceback.format_exc())
MessageNotifyUtil.Notify.error("Failed to index GPS trackpoint artifact for keyword search.", artifact.getDisplayName()) 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: except Exception as ex:
self._logger.log(Level.SEVERE, "Error parsing Cached GPS locations to blackboard", ex) self._logger.log(Level.SEVERE, "Error parsing Cached GPS locations to blackboard", ex)
self._logger.log(Level.SEVERE, traceback.format_exc()) self._logger.log(Level.SEVERE, traceback.format_exc())