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