Merge branch 'develop' of github.com:sleuthkit/autopsy into 4430_limit_results

This commit is contained in:
esaunders
2018-11-26 12:19:26 -05:00
35 changed files with 1482 additions and 586 deletions
@@ -71,7 +71,7 @@ abstract class AbstractSqlEamDb implements EamDb {
private static final Cache<Integer, CorrelationCase> caseCacheById = CacheBuilder.newBuilder()
.expireAfterWrite(CASE_CACHE_TIMEOUT, TimeUnit.MINUTES).
build();
private static final Cache<String, CorrelationDataSource> dataSourceCacheByDeviceId = CacheBuilder.newBuilder()
private static final Cache<String, CorrelationDataSource> dataSourceCacheByDsObjectId = CacheBuilder.newBuilder()
.expireAfterWrite(DATA_SOURCE_CACHE_TIMEOUT, TimeUnit.MINUTES).
build();
private static final Cache<String, CorrelationDataSource> dataSourceCacheById = CacheBuilder.newBuilder()
@@ -133,6 +133,24 @@ abstract class AbstractSqlEamDb implements EamDb {
}
@Override
public void addDataSourceObjectId(int rowId, long dataSourceObjectId) throws EamDbException {
Connection conn = connect();
PreparedStatement preparedStatement = null;
String sql = "UPDATE data_sources SET datasource_obj_id=? WHERE id=?";
try {
preparedStatement = conn.prepareStatement(sql);
preparedStatement.setLong(1, dataSourceObjectId);
preparedStatement.setInt(2, rowId);
preparedStatement.executeUpdate();
} catch (SQLException ex) {
throw new EamDbException("Error updating data source object id for data_sources row " + rowId, ex);
} finally {
EamDbUtil.closeStatement(preparedStatement);
EamDbUtil.closeConnection(conn);
}
}
/**
* Get the value for the given name from the name/value db_info table.
*
@@ -175,7 +193,7 @@ abstract class AbstractSqlEamDb implements EamDb {
typeCache.invalidateAll();
caseCacheByUUID.invalidateAll();
caseCacheById.invalidateAll();
dataSourceCacheByDeviceId.invalidateAll();
dataSourceCacheByDsObjectId.invalidateAll();
dataSourceCacheById.invalidateAll();
}
@@ -551,16 +569,17 @@ abstract class AbstractSqlEamDb implements EamDb {
}
/**
* Create a key to the DataSourceCacheByDeviceId
* Create a key to the dataSourceCacheByDsObjectId
*
* @param caseId - the id of the CorrelationCase in the Central
* Repository
* @param dataSourceDeviceId - the device Id of the data source
* @param dataSourceObjectId - the object id if of the data source in the
* case db
*
* @return a String to be used as a key for the dataSourceCacheByDeviceId
* @return a String to be used as a key for the dataSourceCacheByDsObjectId
*/
private static String getDataSourceByDeviceIdCacheKey(int caseId, String dataSourceDeviceId) {
return "Case" + caseId + "DeviceId" + dataSourceDeviceId; //NON-NLS
private static String getDataSourceByDSObjectIdCacheKey(int caseId, Long dataSourceObjectId) {
return "Case" + caseId + "DsObjectId" + dataSourceObjectId; //NON-NLS
}
/**
@@ -582,18 +601,18 @@ abstract class AbstractSqlEamDb implements EamDb {
* @param eamDataSource the data source to add
*/
@Override
public void newDataSource(CorrelationDataSource eamDataSource) throws EamDbException {
public CorrelationDataSource newDataSource(CorrelationDataSource eamDataSource) throws EamDbException {
if (eamDataSource.getCaseID() == -1) {
throw new EamDbException("Case ID is -1");
} else if (eamDataSource.getID() != -1) {
// This data source is already in the central repo
return;
return eamDataSource;
}
Connection conn = connect();
PreparedStatement preparedStatement = null;
String sql = "INSERT INTO data_sources(device_id, case_id, name) VALUES (?, ?, ?) "
String sql = "INSERT INTO data_sources(device_id, case_id, name, datasource_obj_id) VALUES (?, ?, ?, ?) "
+ getConflictClause();
ResultSet resultSet = null;
try {
@@ -602,6 +621,7 @@ abstract class AbstractSqlEamDb implements EamDb {
preparedStatement.setString(1, eamDataSource.getDeviceID());
preparedStatement.setInt(2, eamDataSource.getCaseID());
preparedStatement.setString(3, eamDataSource.getName());
preparedStatement.setLong(4, eamDataSource.getDataSourceObjectID());
preparedStatement.executeUpdate();
resultSet = preparedStatement.getGeneratedKeys();
@@ -609,9 +629,10 @@ abstract class AbstractSqlEamDb implements EamDb {
throw new EamDbException(String.format("Failed to INSERT data source %s in central repo", eamDataSource.getName()));
}
int dataSourceId = resultSet.getInt(1); //last_insert_rowid()
CorrelationDataSource dataSource = new CorrelationDataSource(eamDataSource.getCaseID(), dataSourceId, eamDataSource.getDeviceID(), eamDataSource.getName());
dataSourceCacheByDeviceId.put(getDataSourceByDeviceIdCacheKey(dataSource.getCaseID(), dataSource.getDeviceID()), dataSource);
CorrelationDataSource dataSource = new CorrelationDataSource(eamDataSource.getCaseID(), dataSourceId, eamDataSource.getDeviceID(), eamDataSource.getName(), eamDataSource.getDataSourceObjectID());
dataSourceCacheByDsObjectId.put(getDataSourceByDSObjectIdCacheKey(dataSource.getCaseID(), dataSource.getDataSourceObjectID()), dataSource);
dataSourceCacheById.put(getDataSourceByIdCacheKey(dataSource.getCaseID(), dataSource.getID()), dataSource);
return dataSource;
} catch (SQLException ex) {
throw new EamDbException("Error inserting new data source.", ex); // NON-NLS
} finally {
@@ -622,24 +643,24 @@ abstract class AbstractSqlEamDb implements EamDb {
}
/**
* Retrieves Data Source details based on data source device ID
* Retrieves Data Source details based on data source object ID
*
* @param correlationCase the current CorrelationCase used for ensuring
* uniqueness of DataSource
* @param dataSourceDeviceId the data source device ID number
* @param dataSourceObjectId the object id of the data source
*
* @return The data source
*
* @throws EamDbException
*/
@Override
public CorrelationDataSource getDataSource(CorrelationCase correlationCase, String dataSourceDeviceId) throws EamDbException {
public CorrelationDataSource getDataSource(CorrelationCase correlationCase, Long dataSourceObjectId) throws EamDbException {
if (correlationCase == null) {
throw new EamDbException("Correlation case is null");
}
try {
return dataSourceCacheByDeviceId.get(getDataSourceByDeviceIdCacheKey(correlationCase.getID(), dataSourceDeviceId), () -> getDataSourceFromCr(correlationCase, dataSourceDeviceId));
return dataSourceCacheByDsObjectId.get(getDataSourceByDSObjectIdCacheKey(correlationCase.getID(), dataSourceObjectId), () -> getDataSourceFromCr(correlationCase, dataSourceObjectId));
} catch (CacheLoader.InvalidCacheLoadException ignored) {
//lambda valueloader returned a null value and cache can not store null values this is normal if the dataSource does not exist in the central repo yet
return null;
@@ -654,24 +675,24 @@ abstract class AbstractSqlEamDb implements EamDb {
*
* @param correlationCase the current CorrelationCase used for ensuring
* uniqueness of DataSource
* @param dataSourceDeviceId the data source device ID number
* @param dataSourceDeviceId the object id of the data source
*
* @return The data source
*
* @throws EamDbException
*/
private CorrelationDataSource getDataSourceFromCr(CorrelationCase correlationCase, String dataSourceDeviceId) throws EamDbException {
private CorrelationDataSource getDataSourceFromCr(CorrelationCase correlationCase, Long dataSourceObjectId) throws EamDbException {
Connection conn = connect();
CorrelationDataSource eamDataSourceResult = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
String sql = "SELECT * FROM data_sources WHERE device_id=? AND case_id=?"; // NON-NLS
String sql = "SELECT * FROM data_sources WHERE datasource_obj_id=? AND case_id=?"; // NON-NLS
try {
preparedStatement = conn.prepareStatement(sql);
preparedStatement.setString(1, dataSourceDeviceId);
preparedStatement.setLong(1, dataSourceObjectId);
preparedStatement.setInt(2, correlationCase.getID());
resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
@@ -742,7 +763,7 @@ abstract class AbstractSqlEamDb implements EamDb {
eamDataSourceResult = getEamDataSourceFromResultSet(resultSet);
}
if (eamDataSourceResult != null) {
dataSourceCacheByDeviceId.put(getDataSourceByDeviceIdCacheKey(correlationCase.getID(), eamDataSourceResult.getDeviceID()), eamDataSourceResult);
dataSourceCacheByDsObjectId.put(getDataSourceByDSObjectIdCacheKey(correlationCase.getID(), eamDataSourceResult.getDataSourceObjectID()), eamDataSourceResult);
}
} catch (SQLException ex) {
throw new EamDbException("Error getting data source.", ex); // NON-NLS
@@ -809,28 +830,25 @@ abstract class AbstractSqlEamDb implements EamDb {
= "INSERT INTO "
+ tableName
+ "(case_id, data_source_id, value, file_path, known_status, comment, file_obj_id) "
+ "VALUES ((SELECT id FROM cases WHERE case_uid=? LIMIT 1), "
+ "(SELECT id FROM data_sources WHERE device_id=? AND case_id=? LIMIT 1), ?, ?, ?, ?, ?) "
+ "VALUES (?, ?, ?, ?, ?, ?, ?) "
+ getConflictClause();
try {
preparedStatement = conn.prepareStatement(sql);
if (!eamArtifact.getCorrelationValue().isEmpty()) {
preparedStatement.setString(1, eamArtifact.getCorrelationCase().getCaseUUID());
preparedStatement.setString(2, eamArtifact.getCorrelationDataSource().getDeviceID());
preparedStatement.setInt(3, eamArtifact.getCorrelationDataSource().getCaseID());
preparedStatement.setString(4, eamArtifact.getCorrelationValue());
preparedStatement.setString(5, eamArtifact.getFilePath().toLowerCase());
preparedStatement.setByte(6, eamArtifact.getKnownStatus().getFileKnownValue());
preparedStatement.setInt(1, eamArtifact.getCorrelationCase().getID());
preparedStatement.setInt(2, eamArtifact.getCorrelationDataSource().getID());
preparedStatement.setString(3, eamArtifact.getCorrelationValue());
preparedStatement.setString(4, eamArtifact.getFilePath().toLowerCase());
preparedStatement.setByte(5, eamArtifact.getKnownStatus().getFileKnownValue());
if ("".equals(eamArtifact.getComment())) {
preparedStatement.setNull(7, Types.INTEGER);
preparedStatement.setNull(6, Types.INTEGER);
} else {
preparedStatement.setString(7, eamArtifact.getComment());
preparedStatement.setString(6, eamArtifact.getComment());
}
preparedStatement.setLong(8, eamArtifact.getFileObjectId());
preparedStatement.setLong(7, eamArtifact.getFileObjectId());
preparedStatement.executeUpdate();
}
@@ -904,7 +922,7 @@ abstract class AbstractSqlEamDb implements EamDb {
+ ".value,"
+ tableName
+ ".file_obj_id,"
+ " cases.case_name, cases.case_uid, data_sources.id AS data_source_id, data_sources.name, device_id, file_path, known_status, comment, data_sources.case_id FROM "
+ " cases.case_name, cases.case_uid, data_sources.id AS data_source_id, data_sources.name, device_id, file_path, known_status, comment, data_sources.case_id, data_sources.datasource_obj_id FROM "
+ tableName
+ " LEFT JOIN cases ON "
+ tableName
@@ -969,7 +987,7 @@ abstract class AbstractSqlEamDb implements EamDb {
+ ".value,"
+ tableName
+ ".file_obj_id,"
+ " cases.case_name, cases.case_uid, data_sources.id AS data_source_id, data_sources.name, device_id, file_path, known_status, comment, data_sources.case_id FROM "
+ " cases.case_name, cases.case_uid, data_sources.id AS data_source_id, data_sources.name, device_id, file_path, known_status, comment, data_sources.case_id, data_sources.datasource_obj_id FROM "
+ tableName
+ " LEFT JOIN cases ON "
+ tableName
@@ -1139,7 +1157,7 @@ abstract class AbstractSqlEamDb implements EamDb {
* dataSource
*/
@Override
public Long getCountArtifactInstancesByCaseDataSource(String caseUUID, String dataSourceID) throws EamDbException {
public Long getCountArtifactInstancesByCaseDataSource(CorrelationDataSource correlationDataSource) throws EamDbException {
Connection conn = connect();
Long instanceCount = 0L;
@@ -1147,26 +1165,19 @@ abstract class AbstractSqlEamDb implements EamDb {
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
// Figure out sql variables or subqueries
//Create query to get count of all instances in the database for the specified case specific data source
String sql = "SELECT 0 ";
for (CorrelationAttributeInstance.Type type : artifactTypes) {
String table_name = EamDbUtil.correlationTypeToInstanceTableName(type);
sql
+= "+ (SELECT count(*) FROM "
+ table_name
+ " WHERE data_source_id=(SELECT data_sources.id FROM cases INNER JOIN data_sources ON cases.id = data_sources.case_id WHERE case_uid=? and device_id=?))";
+ " WHERE data_source_id=" + correlationDataSource.getID() + ")";
}
try {
preparedStatement = conn.prepareStatement(sql);
for (int i = 0; i < artifactTypes.size(); ++i) {
preparedStatement.setString(2 * i + 1, caseUUID);
preparedStatement.setString(2 * i + 2, dataSourceID);
}
resultSet = preparedStatement.executeQuery();
resultSet.next();
instanceCount = resultSet.getLong(1);
@@ -1237,7 +1248,7 @@ abstract class AbstractSqlEamDb implements EamDb {
+ tableName
+ " (case_id, data_source_id, value, file_path, known_status, comment, file_obj_id) "
+ "VALUES ((SELECT id FROM cases WHERE case_uid=? LIMIT 1), "
+ "(SELECT id FROM data_sources WHERE device_id=? AND case_id=? LIMIT 1), ?, ?, ?, ?, ?) "
+ "(SELECT id FROM data_sources WHERE datasource_obj_id=? AND case_id=? LIMIT 1), ?, ?, ?, ?, ?) "
+ getConflictClause();
bulkPs = conn.prepareStatement(sql);
@@ -1271,7 +1282,7 @@ abstract class AbstractSqlEamDb implements EamDb {
if (eamArtifact.getCorrelationValue().length() < MAX_VALUE_LENGTH) {
bulkPs.setString(1, eamArtifact.getCorrelationCase().getCaseUUID());
bulkPs.setString(2, eamArtifact.getCorrelationDataSource().getDeviceID());
bulkPs.setLong(2, eamArtifact.getCorrelationDataSource().getDataSourceObjectID());
bulkPs.setInt(3, eamArtifact.getCorrelationDataSource().getCaseID());
bulkPs.setString(4, eamArtifact.getCorrelationValue());
bulkPs.setString(5, eamArtifact.getFilePath());
@@ -1416,25 +1427,23 @@ abstract class AbstractSqlEamDb implements EamDb {
if (eamArtifact.getCorrelationDataSource() == null) {
throw new EamDbException("Correlation data source is null");
}
Connection conn = connect();
PreparedStatement preparedQuery = null;
String tableName = EamDbUtil.correlationTypeToInstanceTableName(eamArtifact.getCorrelationType());
String sqlUpdate
= "UPDATE "
+ tableName
+ " SET comment=? "
+ "WHERE case_id=(SELECT id FROM cases WHERE case_uid=?) "
+ "AND data_source_id=(SELECT id FROM data_sources WHERE device_id=?) "
+ "WHERE case_id=? "
+ "AND data_source_id=? "
+ "AND value=? "
+ "AND file_path=?";
try {
preparedQuery = conn.prepareStatement(sqlUpdate);
preparedQuery.setString(1, eamArtifact.getComment());
preparedQuery.setString(2, eamArtifact.getCorrelationCase().getCaseUUID());
preparedQuery.setString(3, eamArtifact.getCorrelationDataSource().getDeviceID());
preparedQuery.setInt(2, eamArtifact.getCorrelationCase().getID());
preparedQuery.setInt(3, eamArtifact.getCorrelationDataSource().getID());
preparedQuery.setString(4, eamArtifact.getCorrelationValue());
preparedQuery.setString(5, eamArtifact.getFilePath().toLowerCase());
preparedQuery.executeUpdate();
@@ -1616,8 +1625,8 @@ abstract class AbstractSqlEamDb implements EamDb {
String sqlQuery
= "SELECT id FROM "
+ tableName
+ " WHERE case_id=(SELECT id FROM cases WHERE case_uid=?) "
+ "AND data_source_id=(SELECT id FROM data_sources WHERE device_id=?) "
+ " WHERE case_id=? "
+ "AND data_source_id=? "
+ "AND value=? "
+ "AND file_path=?";
@@ -1629,8 +1638,8 @@ abstract class AbstractSqlEamDb implements EamDb {
try {
preparedQuery = conn.prepareStatement(sqlQuery);
preparedQuery.setString(1, eamArtifact.getCorrelationCase().getCaseUUID());
preparedQuery.setString(2, eamArtifact.getCorrelationDataSource().getDeviceID());
preparedQuery.setInt(1, eamArtifact.getCorrelationCase().getID());
preparedQuery.setInt(2, eamArtifact.getCorrelationDataSource().getID());
preparedQuery.setString(3, eamArtifact.getCorrelationValue());
preparedQuery.setString(4, eamArtifact.getFilePath());
resultSet = preparedQuery.executeQuery();
@@ -1660,8 +1669,7 @@ abstract class AbstractSqlEamDb implements EamDb {
if (null == correlationCaseWithId) {
correlationCaseWithId = newCase(eamArtifact.getCorrelationCase());
}
if (null == getDataSource(correlationCaseWithId, eamArtifact.getCorrelationDataSource().getDeviceID())) {
if (null == getDataSource(correlationCaseWithId, eamArtifact.getCorrelationDataSource().getDataSourceObjectID())) {
newDataSource(eamArtifact.getCorrelationDataSource());
}
eamArtifact.setKnownStatus(knownStatus);
@@ -1708,7 +1716,7 @@ abstract class AbstractSqlEamDb implements EamDb {
+ ".value, "
+ tableName
+ ".file_obj_id,"
+ "cases.case_name, cases.case_uid, data_sources.id AS data_source_id, data_sources.name, device_id, file_path, known_status, comment, data_sources.case_id FROM "
+ "cases.case_name, cases.case_uid, data_sources.id AS data_source_id, data_sources.name, device_id, file_path, known_status, comment, data_sources.case_id, data_sources.datasource_obj_id FROM "
+ tableName
+ " LEFT JOIN cases ON "
+ tableName
@@ -1765,7 +1773,7 @@ abstract class AbstractSqlEamDb implements EamDb {
String tableName = EamDbUtil.correlationTypeToInstanceTableName(aType);
String sql
= "SELECT cases.case_name, cases.case_uid, data_sources.name, device_id, file_path, known_status, comment, data_sources.case_id, id, value, file_obj_id FROM "
= "SELECT cases.case_name, cases.case_uid, data_sources.name, device_id, file_path, known_status, comment, data_sources.case_id, id, value, file_obj_id, data_sources.datasource_obj_id FROM "
+ tableName
+ " LEFT JOIN cases ON "
+ tableName
@@ -3047,7 +3055,8 @@ abstract class AbstractSqlEamDb implements EamDb {
resultSet.getInt("case_id"),
resultSet.getInt("id"),
resultSet.getString("device_id"),
resultSet.getString("name")
resultSet.getString("name"),
resultSet.getLong("datasource_obj_id")
);
return eamDataSource;
@@ -3088,7 +3097,7 @@ abstract class AbstractSqlEamDb implements EamDb {
resultSet.getString("value"),
resultSet.getInt("id"),
new CorrelationCase(resultSet.getInt("case_id"), resultSet.getString("case_uid"), resultSet.getString("case_name")),
new CorrelationDataSource(resultSet.getInt("case_id"), resultSet.getInt("data_source_id"), resultSet.getString("device_id"), resultSet.getString("name")),
new CorrelationDataSource(resultSet.getInt("case_id"), resultSet.getInt("data_source_id"), resultSet.getString("device_id"), resultSet.getString("name"), resultSet.getLong("datasource_obj_id")),
resultSet.getString("file_path"),
resultSet.getString("comment"),
TskData.FileKnown.valueOf(resultSet.getByte("known_status")),
@@ -3216,8 +3225,7 @@ abstract class AbstractSqlEamDb implements EamDb {
//Update to 1.2
if (dbSchemaVersion.compareTo(new CaseDbSchemaVersionNumber(1, 2)) < 0) {
EamDbPlatformEnum selectedPlatform = EamDbPlatformEnum.getSelectedPlatform();
final String addObjectIdColumnTemplate = "ALTER TABLE %s ADD COLUMN file_obj_id INTEGER;"; //NON-NLS
final String addIntegerColumnTemplate = "ALTER TABLE %s ADD COLUMN %s INTEGER;"; //NON-NLS
final String addSsidTableTemplate;
final String addCaseIdIndexTemplate;
final String addDataSourceIdIndexTemplate;
@@ -3251,9 +3259,18 @@ abstract class AbstractSqlEamDb implements EamDb {
default:
throw new EamDbException("Currently selected database platform \"" + selectedPlatform.name() + "\" can not be upgraded.");
}
final String dataSourcesTableName = "data_sources";
final String dataSourceObjectIdColumnName = "datasource_obj_id";
if (!doesColumnExist(conn, dataSourcesTableName, dataSourceObjectIdColumnName)) {
statement.execute(String.format(addIntegerColumnTemplate, dataSourcesTableName, dataSourceObjectIdColumnName)); //NON-NLS
}
final String dataSourceObjectIdIndexTemplate = "CREATE INDEX IF NOT EXISTS datasource_object_id ON data_sources (%s)";
statement.execute(String.format(dataSourceObjectIdIndexTemplate, dataSourceObjectIdColumnName));
//update central repository to be able to store new correlation attributes
final String wirelessNetworsDbTableName = "wireless_networks";
final String wirelessNetworksTableInstanceName = wirelessNetworsDbTableName + "_instances";
//add the wireless_networks attribute to the correlation_types table
preparedStatement = conn.prepareStatement(addAttributeSql);
preparedStatement.setInt(1, CorrelationAttributeInstance.SSID_TYPE_ID);
@@ -3270,10 +3287,11 @@ abstract class AbstractSqlEamDb implements EamDb {
statement.execute(String.format(addKnownStatusIndexTemplate, wirelessNetworksTableInstanceName, wirelessNetworksTableInstanceName));
//add file_obj_id column to _instances table which do not already have it
String instance_type_dbname;
final String objectIdColumnName = "file_obj_id";
for (CorrelationAttributeInstance.Type type : CorrelationAttributeInstance.getDefaultCorrelationTypes()) {
instance_type_dbname = EamDbUtil.correlationTypeToInstanceTableName(type);
if (!doesColumnExist(conn, instance_type_dbname, "file_obj_id")) {
statement.execute(String.format(addObjectIdColumnTemplate, instance_type_dbname)); //NON-NLS
if (!doesColumnExist(conn, instance_type_dbname, objectIdColumnName)) {
statement.execute(String.format(addIntegerColumnTemplate, instance_type_dbname, objectIdColumnName)); //NON-NLS
}
statement.execute(String.format(addObjectIdIndexTemplate, instance_type_dbname, instance_type_dbname));
}
@@ -36,38 +36,49 @@ public class CorrelationDataSource implements Serializable {
private final int caseID; //the value in the id column of the case table in the central repo
private final int dataSourceID; //< Id in the central repo
private final Long dataSourceObjectID; //< Id for data source in the caseDB
private final String deviceID; //< Unique to its associated case (not necessarily globally unique)
private final String name;
/**
* @param correlationCase CorrelationCase object data source is associated with. Must have been created by EamDB and have a valid ID.
* @param deviceId User specified case-specific ID
* @param name Display name of data source
* Create a CorrelationDataSource object, the object will not have the data
* source id for the row in the central repository.
*
* @param correlationCase CorrelationCase object data source is
* associated with. Must have been created by
* EamDB and have a valid ID.
* @param deviceId User specified case-specific ID
* @param name Display name of data source
* @param dataSourceObjectId The object ID for the datasource
*/
public CorrelationDataSource(CorrelationCase correlationCase, String deviceId, String name) {
this(correlationCase.getID(), -1, deviceId, name);
}
public CorrelationDataSource(CorrelationCase correlationCase, String deviceId, String name, long dataSourceObjectId) {
this(correlationCase.getID(), -1, deviceId, name, dataSourceObjectId);
}
/**
*
* @param caseId Row ID for Case in DB
* @param dataSourceId Row ID for this data source in DB (or -1)
* @param deviceId User specified ID for device (unique per case)
* @param name User specified name
* Create a CorrelationDataSource object.
*
* @param caseId Row ID for Case in DB
* @param dataSourceId Row ID for this data source in DB (or -1)
* @param deviceId User specified ID for device (unique per case)
* @param name User specified name
* @param dataSourceObjectId The object ID for the datasource
*/
CorrelationDataSource(int caseId,
int dataSourceId,
String deviceId,
String name) {
String name,
Long dataSourceObjectId) {
this.caseID = caseId;
this.dataSourceID = dataSourceId;
this.deviceID = deviceId;
this.name = name;
this.dataSourceObjectID = dataSourceObjectId;
}
/**
* Create a CorrelationDataSource object from a TSK Content object.
* This will add it to the central repository.
* Create a CorrelationDataSource object from a TSK Content object. This
* will add it to the central repository.
*
* @param correlationCase the current CorrelationCase used for ensuring
* uniqueness of DataSource
@@ -85,21 +96,24 @@ public class CorrelationDataSource implements Serializable {
} catch (NoCurrentCaseException ex) {
throw new EamDbException("Autopsy case is closed");
}
String deviceId;
try {
deviceId = curCase.getSleuthkitCase().getDataSource(dataSource.getId()).getDeviceId();
} catch (TskDataException | TskCoreException ex) {
throw new EamDbException("Error getting data source info: " + ex.getMessage());
}
CorrelationDataSource correlationDataSource = null;
if (EamDbUtil.useCentralRepo()) {
correlationDataSource = EamDb.getInstance().getDataSource(correlationCase, deviceId);
boolean useCR = EamDbUtil.useCentralRepo();
if (useCR) {
correlationDataSource = EamDb.getInstance().getDataSource(correlationCase, dataSource.getId());
}
if (correlationDataSource == null) {
correlationDataSource = new CorrelationDataSource(correlationCase, deviceId, dataSource.getName());
if (EamDbUtil.useCentralRepo()) {
EamDb.getInstance().newDataSource(correlationDataSource);
String deviceId;
try {
deviceId = curCase.getSleuthkitCase().getDataSource(dataSource.getId()).getDeviceId();
} catch (TskDataException | TskCoreException ex) {
throw new EamDbException("Error getting data source info: " + ex.getMessage());
}
correlationDataSource = new CorrelationDataSource(correlationCase, deviceId, dataSource.getName(), dataSource.getId());
if (useCR) {
//add the correlation data source to the central repository and fill in the Central repository data source id in the object
correlationDataSource = EamDb.getInstance().newDataSource(correlationDataSource);
}
}
return correlationDataSource;
@@ -144,6 +158,15 @@ public class CorrelationDataSource implements Serializable {
return caseID;
}
/**
* Get the object id for the data source in the case db
*
* @return dataSourceObjectID or NULL if not available
*/
public Long getDataSourceObjectID() {
return dataSourceObjectID;
}
/**
* @return the name
*/
@@ -0,0 +1,68 @@
/*
* Central Repository
*
* Copyright 2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.centralrepository.datamodel;
import org.openide.util.NbBundle;
import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.appservices.AutopsyService;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Class which updates the data sources in the central repository to include the
* object id which ties them to the current case.
*
*/
@ServiceProvider(service = AutopsyService.class)
public class DataSourceUpdateService implements AutopsyService {
@Override
@NbBundle.Messages({"DataSourceUpdateService.serviceName.text=Update Central Repository Data Sources"})
public String getServiceName() {
return Bundle.DataSourceUpdateService_serviceName_text();
}
@Override
public void openCaseResources(CaseContext context) throws AutopsyServiceException {
if (EamDb.isEnabled()) {
try {
EamDb centralRepository = EamDb.getInstance();
CorrelationCase correlationCase = centralRepository.getCase(context.getCase());
//if the case isn't in the central repository yet there won't be data sources in it to update
if (correlationCase != null) {
for (CorrelationDataSource correlationDataSource : centralRepository.getDataSources()) {
//ResultSet.getLong has a value of 0 when the value is null
if (correlationDataSource.getCaseID() == correlationCase.getID() && correlationDataSource.getDataSourceObjectID() == 0) {
for (Content dataSource : context.getCase().getDataSources()) {
if (((DataSource) dataSource).getDeviceId().equals(correlationDataSource.getDeviceID())) {
centralRepository.addDataSourceObjectId(correlationDataSource.getID(), dataSource.getId());
break;
}
}
}
}
}
} catch (EamDbException | TskCoreException ex) {
throw new AutopsyServiceException("Unabe to update datasources in central repository", ex);
}
}
}
}
@@ -328,7 +328,6 @@ public class EamArtifactUtil {
if (null == correlationCase) {
correlationCase = EamDb.getInstance().newCase(Case.getCurrentCaseThrows());
}
return new CorrelationAttributeInstance(
filesType,
af.getMd5Hash(),
@@ -110,6 +110,16 @@ public interface EamDb {
*/
public void newDbInfo(String name, String value) throws EamDbException;
/**
* Set the data source object id for a specific entry in the data_sources
* table
*
* @param rowId - the row id for the data_sources table entry
* @param dataSourceObjectId - the object id for the data source from the
* caseDb
*/
void addDataSourceObjectId(int rowId, long dataSourceObjectId) throws EamDbException;
/**
* Get the value for the given name from the name/value db_info table.
*
@@ -194,8 +204,10 @@ public interface EamDb {
* Creates new Data Source in the database
*
* @param eamDataSource the data source to add
*
* @return - A CorrelationDataSource object with data source's central repository id
*/
void newDataSource(CorrelationDataSource eamDataSource) throws EamDbException;
CorrelationDataSource newDataSource(CorrelationDataSource eamDataSource) throws EamDbException;
/**
* Retrieves Data Source details based on data source device ID
@@ -206,7 +218,7 @@ public interface EamDb {
*
* @return The data source
*/
CorrelationDataSource getDataSource(CorrelationCase correlationCase, String dataSourceDeviceId) throws EamDbException;
CorrelationDataSource getDataSource(CorrelationCase correlationCase, Long caseDbDataSourceId) throws EamDbException;
/**
* Retrieves Data Source details based on data source ID
@@ -309,7 +321,7 @@ public interface EamDb {
* @return Number of artifact instances having caseDisplayName and
* dataSource
*/
Long getCountArtifactInstancesByCaseDataSource(String caseUUID, String dataSourceID) throws EamDbException;
Long getCountArtifactInstancesByCaseDataSource(CorrelationDataSource correlationDataSource) throws EamDbException;
/**
* Adds an eamArtifact to an internal list to be later added to DB. Artifact
@@ -345,9 +357,9 @@ public interface EamDb {
/**
* Find a correlation attribute in the Central Repository database given the
* instance type, case, data source, value, and file path.
*
* Method exists to support instances added using Central Repository version 1,1 and
* older
*
* Method exists to support instances added using Central Repository version
* 1,1 and older
*
* @param type The type of instance.
* @param correlationCase The case tied to the instance.
@@ -356,7 +368,7 @@ public interface EamDb {
* @param filePath The file path tied to the instance.
*
* @return The correlation attribute if it exists; otherwise null.
*
*
* @throws EamDbException
*/
CorrelationAttributeInstance getCorrelationAttributeInstance(CorrelationAttributeInstance.Type type, CorrelationCase correlationCase,
@@ -369,7 +381,8 @@ public interface EamDb {
* @param type The type of instance.
* @param correlationCase The case tied to the instance.
* @param correlationDataSource The data source tied to the instance.
* @param objectID The object id of the file tied to the instance.
* @param objectID The object id of the file tied to the
* instance.
*
* @return The correlation attribute if it exists; otherwise null.
*
@@ -344,11 +344,13 @@ public final class PostgresEamDbSettings {
createDataSourcesTable.append("case_id integer NOT NULL,");
createDataSourcesTable.append("device_id text NOT NULL,");
createDataSourcesTable.append("name text NOT NULL,");
createDataSourcesTable.append("datasource_obj_id integer,");
createDataSourcesTable.append("foreign key (case_id) references cases(id) ON UPDATE SET NULL ON DELETE SET NULL,");
createDataSourcesTable.append("CONSTRAINT datasource_unique UNIQUE (case_id, device_id, name)");
createDataSourcesTable.append(")");
String dataSourceIdx1 = "CREATE INDEX IF NOT EXISTS data_sources_name ON data_sources (name)";
String dataSourceIdx2 = "CREATE INDEX IF NOT EXISTS data_sources_object_id ON data_sources (datasource_obj_id)";
StringBuilder createReferenceSetsTable = new StringBuilder();
createReferenceSetsTable.append("CREATE TABLE IF NOT EXISTS reference_sets (");
@@ -425,7 +427,8 @@ public final class PostgresEamDbSettings {
stmt.execute(createDataSourcesTable.toString());
stmt.execute(dataSourceIdx1);
stmt.execute(dataSourceIdx2);
stmt.execute(createReferenceSetsTable.toString());
stmt.execute(referenceSetsIdx1);
@@ -274,6 +274,16 @@ final class SqliteEamDb extends AbstractSqlEamDb {
}
}
@Override
public void addDataSourceObjectId(int rowId, long dataSourceObjectId) throws EamDbException{
try {
acquireExclusiveLock();
super.addDataSourceObjectId(rowId, dataSourceObjectId);
} finally {
releaseExclusiveLock();
}
}
/**
* Creates new Case in the database
*
@@ -362,10 +372,10 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* @param eamDataSource the data source to add
*/
@Override
public void newDataSource(CorrelationDataSource eamDataSource) throws EamDbException {
public CorrelationDataSource newDataSource(CorrelationDataSource eamDataSource) throws EamDbException {
try {
acquireExclusiveLock();
super.newDataSource(eamDataSource);
return super.newDataSource(eamDataSource);
} finally {
releaseExclusiveLock();
}
@@ -381,10 +391,10 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* @return The data source
*/
@Override
public CorrelationDataSource getDataSource(CorrelationCase correlationCase, String dataSourceDeviceId) throws EamDbException {
public CorrelationDataSource getDataSource(CorrelationCase correlationCase, Long caseDbDataSourceId) throws EamDbException {
try {
acquireSharedLock();
return super.getDataSource(correlationCase, dataSourceDeviceId);
return super.getDataSource(correlationCase, caseDbDataSourceId);
} finally {
releaseSharedLock();
}
@@ -556,10 +566,10 @@ final class SqliteEamDb extends AbstractSqlEamDb {
* dataSource
*/
@Override
public Long getCountArtifactInstancesByCaseDataSource(String caseUUID, String dataSourceID) throws EamDbException {
public Long getCountArtifactInstancesByCaseDataSource(CorrelationDataSource correlationDataSource) throws EamDbException {
try {
acquireSharedLock();
return super.getCountArtifactInstancesByCaseDataSource(caseUUID, dataSourceID);
return super.getCountArtifactInstancesByCaseDataSource(correlationDataSource);
} finally {
releaseSharedLock();
}
@@ -287,11 +287,13 @@ public final class SqliteEamDbSettings {
createDataSourcesTable.append("case_id integer NOT NULL,");
createDataSourcesTable.append("device_id text NOT NULL,");
createDataSourcesTable.append("name text NOT NULL,");
createDataSourcesTable.append("datasource_obj_id integer,");
createDataSourcesTable.append("foreign key (case_id) references cases(id) ON UPDATE SET NULL ON DELETE SET NULL,");
createDataSourcesTable.append("CONSTRAINT datasource_unique UNIQUE (case_id, device_id, name)");
createDataSourcesTable.append(")");
String dataSourceIdx1 = "CREATE INDEX IF NOT EXISTS data_sources_name ON data_sources (name)";
String dataSourceIdx2 = "CREATE INDEX IF NOT EXISTS data_sources_object_id ON data_sources (datasource_obj_id)";
StringBuilder createReferenceSetsTable = new StringBuilder();
createReferenceSetsTable.append("CREATE TABLE IF NOT EXISTS reference_sets (");
@@ -374,6 +376,7 @@ public final class SqliteEamDbSettings {
stmt.execute(createDataSourcesTable.toString());
stmt.execute(dataSourceIdx1);
stmt.execute(dataSourceIdx2);
stmt.execute(createReferenceSetsTable.toString());
stmt.execute(referenceSetsIdx1);
@@ -443,19 +443,16 @@ final class CaseEventListener implements PropertyChangeListener {
Content newDataSource = dataSourceAddedEvent.getDataSource();
try {
String deviceId = openCase.getSleuthkitCase().getDataSource(newDataSource.getId()).getDeviceId();
CorrelationCase correlationCase = dbManager.getCase(openCase);
if (null == correlationCase) {
correlationCase = dbManager.newCase(openCase);
}
if (null == dbManager.getDataSource(correlationCase, deviceId)) {
if (null == dbManager.getDataSource(correlationCase, newDataSource.getId())) {
CorrelationDataSource.fromTSKDataSource(correlationCase, newDataSource);
}
} catch (EamDbException ex) {
LOGGER.log(Level.SEVERE, "Error adding new data source to the central repository", ex); //NON-NLS
} catch (TskCoreException | TskDataException ex) {
LOGGER.log(Level.SEVERE, "Error getting data source from DATA_SOURCE_ADDED event content.", ex); //NON-NLS
}
}
} // DATA_SOURCE_ADDED
}
@@ -192,7 +192,7 @@ final class IngestModule implements FileIngestModule {
logger.log(Level.SEVERE, "Error doing bulk insert of artifacts.", ex); // NON-NLS
}
try {
Long count = dbManager.getCountArtifactInstancesByCaseDataSource(eamCase.getCaseUUID(), eamDataSource.getDeviceID());
Long count = dbManager.getCountArtifactInstancesByCaseDataSource(eamDataSource);
logger.log(Level.INFO, "{0} artifacts in db for case: {1} ds:{2}", new Object[]{count, eamCase.getDisplayName(), eamDataSource.getName()}); // NON-NLS
} catch (EamDbException ex) {
logger.log(Level.SEVERE, "Error counting artifacts.", ex); // NON-NLS
@@ -303,7 +303,7 @@ final class IngestModule implements FileIngestModule {
== 1) {
// ensure we have this data source in the EAM DB
try {
if (null == centralRepoDb.getDataSource(eamCase, eamDataSource.getDeviceID())) {
if (null == centralRepoDb.getDataSource(eamCase, eamDataSource.getDataSourceObjectID())) {
centralRepoDb.newDataSource(eamDataSource);
}
} catch (EamDbException ex) {
@@ -61,7 +61,7 @@ EamDbSettingsDialog.lbFullDbPath.text=
GlobalSettingsPanel.cbUseCentralRepo.text=Use a Central Repository
GlobalSettingsPanel.organizationTextArea.text=Organization information can be tracked in the Central Repository.
GlobalSettingsPanel.manageOrganizationButton.text=Manage Organizations
GlobalSettingsPanel.lbCentralRepository.text=A Central Repository allows you to correlate files and results between cases.
GlobalSettingsPanel.lbCentralRepository.text=A Central Repository allows you to correlate files and results between cases. Central Repository configuration can not be modified while a case is open.
GlobalSettingsPanel.pnCorrelationProperties.border.title=Correlation Properties
GlobalSettingsPanel.organizationPanel.border.title=Organizations
GlobalSettingsPanel.casesPanel.border.title=Case Details
@@ -140,7 +140,7 @@
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Component id="bnDbConfigure" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" max="-2" attributes="0">
@@ -150,7 +150,7 @@
</Group>
<EmptySpace type="unrelated" min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="1" attributes="0">
<Component id="lbDbNameValue" alignment="0" max="32767" attributes="0"/>
<Component id="lbDbNameValue" alignment="0" pref="936" max="32767" attributes="0"/>
<Component id="lbDbPlatformValue" max="32767" attributes="0"/>
<Component id="lbDbLocationValue" alignment="0" max="32767" attributes="0"/>
</Group>
@@ -23,11 +23,13 @@ import java.awt.EventQueue;
import org.sleuthkit.autopsy.coreutils.Logger;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.EnumSet;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import org.netbeans.spi.options.OptionsPanelController;
import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.corecomponents.OptionsPanel;
import org.sleuthkit.autopsy.events.AutopsyEvent;
import org.sleuthkit.autopsy.ingest.IngestManager;
@@ -58,6 +60,10 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
initComponents();
customizeComponents();
addIngestJobEventsListener();
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), (PropertyChangeEvent evt) -> {
//disable when case is open, enable when case is closed
ingestStateUpdated(evt.getNewValue() != null);
});
}
private void customizeComponents() {
@@ -66,7 +72,7 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
private void addIngestJobEventsListener() {
IngestManager.getInstance().addIngestJobEventListener(ingestJobEventListener);
ingestStateUpdated();
ingestStateUpdated(Case.isCaseOpen());
}
@Messages({"GlobalSettingsPanel.updateFailed.title=Update failed",
@@ -173,7 +179,7 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
.addGroup(pnDatabaseConfigurationLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(pnDatabaseConfigurationLayout.createSequentialGroup()
.addComponent(bnDbConfigure)
.addContainerGap())
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addGroup(pnDatabaseConfigurationLayout.createSequentialGroup()
.addGroup(pnDatabaseConfigurationLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
.addComponent(lbDbPlatformTypeLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
@@ -181,7 +187,7 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
.addComponent(lbDbLocationLabel))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGroup(pnDatabaseConfigurationLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(lbDbNameValue, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(lbDbNameValue, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 936, Short.MAX_VALUE)
.addComponent(lbDbPlatformValue, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(lbDbLocationValue, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))))
);
@@ -429,7 +435,7 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
store();
updateDatabase();
load();
this.ingestStateUpdated();
this.ingestStateUpdated(Case.isCaseOpen());
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}//GEN-LAST:event_cbUseCentralRepoActionPerformed
@@ -447,7 +453,7 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
@Messages({"GlobalSettingsPanel.validationerrMsg.mustConfigure=Configure the database to enable this module."})
public void load() {
tbOops.setText("");
enableAllSubComponents(false);
enableButtonSubComponents(false);
EamDbPlatformEnum selectedPlatform = EamDbPlatformEnum.getSelectedPlatform();
cbUseCentralRepo.setSelected(EamDbUtil.useCentralRepo()); // NON-NLS
switch (selectedPlatform) {
@@ -456,20 +462,19 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
lbDbPlatformValue.setText(EamDbPlatformEnum.POSTGRESQL.toString());
lbDbNameValue.setText(dbSettingsPg.getDbName());
lbDbLocationValue.setText(dbSettingsPg.getHost());
enableAllSubComponents(true);
enableButtonSubComponents(cbUseCentralRepo.isSelected());
break;
case SQLITE:
SqliteEamDbSettings dbSettingsSqlite = new SqliteEamDbSettings();
lbDbPlatformValue.setText(EamDbPlatformEnum.SQLITE.toString());
lbDbNameValue.setText(dbSettingsSqlite.getDbName());
lbDbLocationValue.setText(dbSettingsSqlite.getDbDirectory());
enableAllSubComponents(true);
enableButtonSubComponents(cbUseCentralRepo.isSelected());
break;
default:
lbDbPlatformValue.setText(EamDbPlatformEnum.DISABLED.toString());
lbDbNameValue.setText("");
lbDbLocationValue.setText("");
enableDatabaseConfigureButton(cbUseCentralRepo.isSelected());
tbOops.setText(Bundle.GlobalSettingsPanel_validationerrMsg_mustConfigure());
break;
}
@@ -521,7 +526,7 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
ingestStateUpdated();
ingestStateUpdated(Case.isCaseOpen());
}
});
}
@@ -550,38 +555,25 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
}
@Messages({"GlobalSettingsPanel.validationErrMsg.ingestRunning=You cannot change settings while ingest is running."})
private void ingestStateUpdated() {
private void ingestStateUpdated(boolean caseIsOpen) {
if (!SwingUtilities.isEventDispatchThread()) {
SwingUtilities.invokeLater(() -> {
ingestStateUpdated();
ingestStateUpdated(caseIsOpen);
});
return;
}
cbUseCentralRepo.setEnabled(!caseIsOpen);
if (IngestManager.getInstance().isIngestRunning()) {
tbOops.setText(Bundle.GlobalSettingsPanel_validationErrMsg_ingestRunning());
tbOops.setVisible(true);
cbUseCentralRepo.setEnabled(false);
enableAllSubComponents(false);
} else if (!cbUseCentralRepo.isEnabled()) {
cbUseCentralRepo.setEnabled(true);
enableButtonSubComponents(cbUseCentralRepo.isSelected());
} else {
load();
enableDatabaseConfigureButton(cbUseCentralRepo.isSelected() && !caseIsOpen);
}
}
/**
* Wrapper around each of the enableComponentXYZ methods to enable/disable
* them all at the same time.
*
* @param enable
*
* @return True
*/
private boolean enableAllSubComponents(Boolean enable) {
enableDatabaseConfigureButton(cbUseCentralRepo.isSelected() && enable);
enableButtonSubComponents(cbUseCentralRepo.isSelected() && enable);
return true;
}
/**
@@ -24,21 +24,15 @@ import java.awt.Cursor;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.logging.Level;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
@@ -48,11 +42,11 @@ import org.apache.commons.io.FilenameUtils;
import org.openide.util.NbBundle;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.SQLiteTableReaderException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.autopsy.coreutils.SQLiteTableReader;
/**
* A file content viewer for SQLite database files.
@@ -66,8 +60,14 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
private static final Logger logger = Logger.getLogger(FileViewer.class.getName());
private final SQLiteTableView selectedTableView = new SQLiteTableView();
private AbstractFile sqliteDbFile;
private File tmpDbFile;
private Connection connection;
private SQLiteTableReader viewReader;
private Map<String, Object> row = new LinkedHashMap<>();
private List<Map<String, Object>> pageOfTableRows = new ArrayList<>();
private List<String> currentTableHeader = new ArrayList<>();
private String prevTableName;
private int numRows; // num of rows in the selected table
private int currPage = 0; // curr page of rows being displayed
@@ -264,18 +264,18 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
}//GEN-LAST:event_tablesDropdownListActionPerformed
/**
* The action when the Export Csv button is pressed. The file chooser window will pop
* up to choose where the user wants to save the csv file. The default location is case export directory.
* The action when the Export Csv button is pressed. The file chooser window
* will pop up to choose where the user wants to save the csv file. The
* default location is case export directory.
*
* @param evt the action event
*/
@NbBundle.Messages({"SQLiteViewer.csvExport.fileName.empty=Please input a file name for exporting.",
"SQLiteViewer.csvExport.title=Export to csv file",
"SQLiteViewer.csvExport.confirm.msg=Do you want to overwrite the existing file?"})
"SQLiteViewer.csvExport.title=Export to csv file",
"SQLiteViewer.csvExport.confirm.msg=Do you want to overwrite the existing file?"})
private void exportCsvButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_exportCsvButtonActionPerformed
Case openCase = Case.getCurrentCase();
File caseDirectory = new File(openCase.getExportDirectory());
File caseDirectory = new File(openCase.getExportDirectory());
JFileChooser fileChooser = new JFileChooser();
fileChooser.setDragEnabled(false);
fileChooser.setCurrentDirectory(caseDirectory);
@@ -292,14 +292,14 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
File file = fileChooser.getSelectedFile();
if (file.exists() && FilenameUtils.getExtension(file.getName()).equalsIgnoreCase("csv")) {
if (JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(this,
Bundle.SQLiteViewer_csvExport_confirm_msg(),
Bundle.SQLiteViewer_csvExport_title(),
Bundle.SQLiteViewer_csvExport_confirm_msg(),
Bundle.SQLiteViewer_csvExport_title(),
JOptionPane.YES_NO_OPTION)) {
} else {
return;
}
}
}
exportTableToCsv(file);
}
}//GEN-LAST:event_exportCsvButtonActionPerformed
@@ -328,6 +328,7 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
public void setFile(AbstractFile file) {
WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
sqliteDbFile = file;
initReader();
processSQLiteFile();
WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
}
@@ -343,16 +344,15 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
tablesDropdownList.removeAllItems();
numEntriesField.setText("");
// close DB connection to file
if (null != connection) {
try {
connection.close();
connection = null;
} catch (SQLException ex) {
logger.log(Level.SEVERE, "Failed to close DB connection to file.", ex); //NON-NLS
}
try {
viewReader.close();
} catch (SQLiteTableReaderException ex) {
//Could not successfully close the reader, nothing we can do to recover.
}
row = new LinkedHashMap<>();
pageOfTableRows = new ArrayList<>();
currentTableHeader = new ArrayList<>();
viewReader = null;
sqliteDbFile = null;
}
@@ -368,17 +368,10 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
"SQLiteViewer.errorMessage.failedToinitJDBCDriver=The JDBC driver for SQLite could not be loaded.",
"# {0} - exception message", "SQLiteViewer.errorMessage.unexpectedError=An unexpected error occurred:\n{0).",})
private void processSQLiteFile() {
tablesDropdownList.removeAllItems();
try {
String localDiskPath = SqliteUtil.writeAbstractFileToLocalDisk(sqliteDbFile);
SqliteUtil.findAndCopySQLiteMetaFile(sqliteDbFile);
// Load the SQLite JDBC driver, if necessary.
Class.forName("org.sqlite.JDBC"); //NON-NLS
connection = DriverManager.getConnection("jdbc:sqlite:" + localDiskPath); //NON-NLS
tablesDropdownList.removeAllItems();
Collection<String> dbTablesMap = getTables();
Collection<String> dbTablesMap = viewReader.getTableNames();
if (dbTablesMap.isEmpty()) {
tablesDropdownList.addItem(Bundle.SQLiteViewer_comboBox_noTableEntry());
tablesDropdownList.setEnabled(false);
@@ -387,46 +380,20 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
tablesDropdownList.addItem(tableName);
});
}
} catch (ClassNotFoundException ex) {
logger.log(Level.SEVERE, String.format("Failed to initialize JDBC SQLite '%s' (objId=%d)", sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS
MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_errorMessage_failedToinitJDBCDriver());
} catch (SQLException ex) {
logger.log(Level.SEVERE, String.format("Failed to get tables from DB file '%s' (objId=%d)", sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS
} catch (SQLiteTableReaderException ex) {
logger.log(Level.WARNING, String.format("Unable to get table names "
+ "from sqlite file [%s] with id=[%d].", sqliteDbFile.getName(),
sqliteDbFile.getId(), ex.getMessage()));
MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_errorMessage_failedToQueryDatabase());
} catch (IOException | NoCurrentCaseException | TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Failed to create temp copy of DB file '%s' (objId=%d)", sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS
MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_errorMessage_failedToExtractFile());
}
}
/**
* Gets a collection of table names from the SQLite database file.
*
* @return A collection of table names
*/
private Collection<String> getTables() throws SQLException {
Collection<String> tableNames = new LinkedList<>();
try (Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(
"SELECT name FROM sqlite_master "
+ " WHERE type= 'table' ")){
while (resultSet.next()) {
tableNames.add(resultSet.getString("name")); //NON-NLS
}
}
return tableNames;
}
@NbBundle.Messages({"# {0} - tableName",
"SQLiteViewer.selectTable.errorText=Error getting row count for table: {0}"
})
private void selectTable(String tableName) {
try (Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(
"SELECT count (*) as count FROM " + "\"" + tableName + "\"")) { //NON-NLS
numRows = resultSet.getInt("count");
try {
numRows = viewReader.getRowCount(tableName);
numEntriesField.setText(numRows + " entries");
currPage = 1;
@@ -442,25 +409,19 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
} else {
exportCsvButton.setEnabled(false);
nextPageButton.setEnabled(false);
//Execute a dummy SELECT * statement so that the metadata
//contains all column names
Map<String, Object> columnRow;
try (ResultSet metaDataResultSet = statement.executeQuery(
"SELECT * FROM " + "\"" + tableName + "\"")) {
//Column names are not found in the metadata of the result set
//above.
ResultSetMetaData metaData = metaDataResultSet.getMetaData();
columnRow = new LinkedHashMap<>();
for(int i = 1; i < metaData.getColumnCount(); i++){
columnRow.put(metaData.getColumnName(i), "");
}
currentTableHeader = new ArrayList<>();
viewReader.read(tableName);
Map<String, Object> columnRow = new LinkedHashMap<>();
for(int i = 0; i< currentTableHeader.size(); i++){
columnRow.put(currentTableHeader.get(i), "");
}
selectedTableView.setupTable(Collections.singletonList(columnRow));
}
} catch (SQLException ex) {
logger.log(Level.SEVERE, String.format("Failed to load table %s from DB file '%s' (objId=%d)", tableName, sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS
}
} catch (SQLiteTableReaderException ex) {
logger.log(Level.WARNING, String.format("Failed to load table %s " //NON-NLS
+ "from DB file '%s' (objId=%d)", tableName, sqliteDbFile.getName(), //NON-NLS
sqliteDbFile.getId()), ex.getMessage());
MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_selectTable_errorText(tableName));
}
}
@@ -468,110 +429,192 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
@NbBundle.Messages({"# {0} - tableName",
"SQLiteViewer.readTable.errorText=Error getting rows for table: {0}"})
private void readTable(String tableName, int startRow, int numRowsToRead) {
try (
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(
"SELECT * FROM " + "\"" + tableName + "\""
+ " LIMIT " + Integer.toString(numRowsToRead)
+ " OFFSET " + Integer.toString(startRow - 1))) {
List<Map<String, Object>> rows = resultSetToArrayList(resultSet);
if (Objects.nonNull(rows)) {
selectedTableView.setupTable(rows);
} else {
selectedTableView.setupTable(Collections.emptyList());
try {
//If the table name has changed, then clear our table header. SQLiteTableReader
//will also detect the table name has changed and begin reading it as if it
//were a brand new table.
if (!tableName.equals(prevTableName)) {
prevTableName = tableName;
}
} catch (SQLException ex) {
logger.log(Level.SEVERE, String.format("Failed to read table %s from DB file '%s' (objId=%d)", tableName, sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS
currentTableHeader = new ArrayList<>();
viewReader.read(tableName, numRowsToRead, startRow - 1);
selectedTableView.setupTable(pageOfTableRows);
pageOfTableRows = new ArrayList<>();
} catch (SQLiteTableReaderException ex) {
logger.log(Level.WARNING, String.format("Failed to read table %s from DB file '%s' " //NON-NLS
+ "(objId=%d) starting at row [%d] and limit [%d]", //NON-NLS
tableName, sqliteDbFile.getName(), sqliteDbFile.getId(),
startRow - 1, numRowsToRead), ex.getMessage());
MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_readTable_errorText(tableName));
}
}
@NbBundle.Messages("SQLiteViewer.BlobNotShown.message=BLOB Data not shown")
private List<Map<String, Object>> resultSetToArrayList(ResultSet resultSet) throws SQLException {
ResultSetMetaData metaData = resultSet.getMetaData();
int columns = metaData.getColumnCount();
ArrayList<Map<String, Object>> rowlist = new ArrayList<>();
while (resultSet.next()) {
Map<String, Object> row = new LinkedHashMap<>(columns);
for (int i = 1; i <= columns; ++i) {
if (resultSet.getObject(i) == null) {
row.put(metaData.getColumnName(i), "");
} else {
if (metaData.getColumnTypeName(i).compareToIgnoreCase("blob") == 0) {
row.put(metaData.getColumnName(i), Bundle.SQLiteViewer_BlobNotShown_message());
} else {
row.put(metaData.getColumnName(i), resultSet.getObject(i));
}
}
}
rowlist.add(row);
}
return rowlist;
/**
* Creates a new SQLiteTableReader. This class will iterate through the
* table row by row and pass each value to the correct function based on its
* data type. For our use, we want to define an action when encountering
* column names and an action for all other data types.
*/
private void initReader() {
viewReader = new SQLiteTableReader.Builder(sqliteDbFile)
.onColumnNames((columnName) -> {
currentTableHeader.add(columnName);
})
.forAll(getForAllStrategy()).build();
}
/**
* For every database value we encounter on our read of the table do the
* following: 1) Get the string representation of the value 2) Collect the
* values until we have a full database row. 3) If we have the full row,
* write it to the UI.
*
* rowIndex is purely for indicating if we have read the full row.
*
* @return Consumer that will perform the actions above. When the
* SQLiteTableReader is reading, values will be passed to this
* consumer.
*/
private Consumer<Object> getForAllStrategy() {
return new Consumer<Object>() {
private int rowIndex = 0;
@Override
public void accept(Object t) {
rowIndex++;
String objectStr = (t instanceof byte[]) ? "BLOB Data not shown"
: Objects.toString(t, "");
row.put(currentTableHeader.get(rowIndex - 1), objectStr);
//If we have built up a full database row, then add it to our page
//of rows to be displayed in the UI.
if (rowIndex == currentTableHeader.size()) {
pageOfTableRows.add(row);
row = new LinkedHashMap<>();
}
rowIndex %= currentTableHeader.size();
}
};
}
private int totalColumnCount;
@NbBundle.Messages({"SQLiteViewer.exportTableToCsv.write.errText=Failed to export table content to csv file.",
"SQLiteViewer.exportTableToCsv.FileName=File name: ",
"SQLiteViewer.exportTableToCsv.TableName=Table name: "
"SQLiteViewer.exportTableToCsv.FileName=File name: ",
"SQLiteViewer.exportTableToCsv.TableName=Table name: "
})
private void exportTableToCsv(File file) {
File csvFile = new File(file.toString() + ".csv");
String tableName = (String) this.tablesDropdownList.getSelectedItem();
try (
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("SELECT * FROM " + "\"" + tableName + "\"")) {
List<Map<String, Object>> currentTableRows = resultSetToArrayList(resultSet);
if (Objects.isNull(currentTableRows) || currentTableRows.isEmpty()) {
logger.log(Level.INFO, String.format("The table %s is empty. (objId=%d)", tableName, sqliteDbFile.getId())); //NON-NLS
} else {
File csvFile;
String fileName = file.getName();
if (FilenameUtils.getExtension(fileName).equalsIgnoreCase("csv")) {
csvFile = file;
} else {
csvFile = new File(file.toString() + ".csv");
}
try (FileOutputStream out = new FileOutputStream(csvFile, false)) {
out.write((Bundle.SQLiteViewer_exportTableToCsv_FileName() + csvFile.getName() + "\n").getBytes());
out.write((Bundle.SQLiteViewer_exportTableToCsv_TableName() + tableName + "\n").getBytes());
// Set up the column names
Map<String, Object> row = currentTableRows.get(0);
StringBuffer header = new StringBuffer();
for (Map.Entry<String, Object> col : row.entrySet()) {
String colName = col.getKey();
if (header.length() > 0) {
header.append(',').append(colName);
} else {
header.append(colName);
}
}
out.write(header.append('\n').toString().getBytes());
for (Map<String, Object> maps : currentTableRows) {
StringBuffer valueLine = new StringBuffer();
maps.values().forEach((value) -> {
if (valueLine.length() > 0) {
valueLine.append(',').append(value.toString());
} else {
valueLine.append(value.toString());
}
});
out.write(valueLine.append('\n').toString().getBytes());
}
}
try (FileOutputStream out = new FileOutputStream(csvFile, false)) {
try (SQLiteTableReader sqliteStream = new SQLiteTableReader.Builder(sqliteDbFile)
.onColumnNames(getColumnNameCSVStrategy(out))
.forAll(getForAllCSVStrategy(out)).build()) {
totalColumnCount = sqliteStream.getColumnCount(tableName);
sqliteStream.read(tableName);
}
} catch (SQLException ex) {
logger.log(Level.SEVERE, String.format("Failed to read table %s from DB file '%s' (objId=%d)", tableName, sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS
MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_readTable_errorText(tableName));
} catch (IOException ex) {
logger.log(Level.SEVERE, String.format("Failed to export table %s to file '%s'", tableName, file.getName()), ex); //NON-NLS
} catch (IOException | SQLiteTableReaderException | RuntimeException ex) {
logger.log(Level.WARNING, String.format("Failed to export table [%s]"
+ " to CSV in sqlite file '%s' (objId=%d)", tableName, sqliteDbFile.getName(),
sqliteDbFile.getId()), ex.getMessage()); //NON-NLS
MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_exportTableToCsv_write_errText());
}
}
/**
* For every column name we encounter on our read of the table do the
* following: 1) Format the name so that it is comma seperated 2) Write the
* value to the output stream.
*
* columnIndex is purely for keeping track of where the column name is in
* the table so the value can be correctly formatted.
*
* @param out Output stream that this database table is being written to.
*
* @return Consumer that will perform the actions above. When the
* SQLiteTableReader is reading, values will be passed to this
* consumer.
*/
private Consumer<String> getColumnNameCSVStrategy(FileOutputStream out) {
return new Consumer<String>() {
private int columnIndex = 0;
@Override
public void accept(String columnName) {
columnIndex++;
//Format the value to adhere to the format of a CSV file
if (columnIndex == 1) {
columnName = "\"" + columnName + "\"";
} else {
columnName = ",\"" + columnName + "\"";
}
if (columnIndex == totalColumnCount) {
columnName += "\n";
}
try {
out.write(columnName.getBytes());
} catch (IOException ex) {
/*
* If we can no longer write to the output stream, toss a
* runtime exception to get out of iteration. We explicitly
* catch this in exportTableToCsv() above.
*/
throw new RuntimeException(ex);
}
}
};
}
/**
* For every database value we encounter on our read of the table do the
* following: 1) Get the string representation of the value 2) Format it so
* that it adheres to the CSV format. 3) Write it to the output file.
*
* rowIndex is purely for keeping track of positioning of the database value
* in the row, so that it can be properly formatted.
*
* @param out Output file
*
* @return Consumer that will perform the actions above. When the
* SQLiteTableReader is reading, values will be passed to this
* consumer.
*/
private Consumer<Object> getForAllCSVStrategy(FileOutputStream out) {
return new Consumer<Object>() {
private int rowIndex = 0;
@Override
public void accept(Object tableValue) {
rowIndex++;
//Substitute string representation of blob with placeholder text.
//Automatically wrap the value in quotes in case it contains commas.
String objectStr = (tableValue instanceof byte[])
? "BLOB Data not shown" : Objects.toString(tableValue, "");
objectStr = "\"" + objectStr + "\"";
if (rowIndex > 1) {
objectStr = "," + objectStr;
}
if (rowIndex == totalColumnCount) {
objectStr += "\n";
}
try {
out.write(objectStr.getBytes());
} catch (IOException ex) {
/*
* If we can no longer write to the output stream, toss a
* runtime exception to get out of iteration. We explicitly
* catch this in exportTableToCsv() above.
*/
throw new RuntimeException(ex);
}
rowIndex = rowIndex % totalColumnCount;
}
};
}
}
@@ -1,130 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2018-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.contentviewers;
import java.io.File;
import java.io.IOException;
import java.util.List;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.casemodule.services.FileManager;
import org.sleuthkit.autopsy.casemodule.services.Services;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Sqlite utility class. Find and copy metafiles, write sqlite abstract files to
* temp directory, and generate unique temp directory paths.
*/
final class SqliteUtil {
private SqliteUtil() {
}
/**
* Overloaded implementation of
* {@link #findAndCopySQLiteMetaFile(AbstractFile, String) findAndCopySQLiteMetaFile}
* , automatically tries to copy -wal and -shm files without needing to know
* their existence.
*
* @param sqliteFile file which has -wal and -shm meta files
*
* @throws NoCurrentCaseException Case has been closed.
* @throws TskCoreException fileManager cannot find AbstractFile
* files.
* @throws IOException Issue during writing to file.
*/
public static void findAndCopySQLiteMetaFile(AbstractFile sqliteFile)
throws NoCurrentCaseException, TskCoreException, IOException {
findAndCopySQLiteMetaFile(sqliteFile, sqliteFile.getName() + "-wal");
findAndCopySQLiteMetaFile(sqliteFile, sqliteFile.getName() + "-shm");
}
/**
* Searches for a meta file associated with the give SQLite database. If
* found, it copies this file into the temp directory of the current case.
*
* @param sqliteFile file being processed
* @param metaFileName name of meta file to look for
*
* @throws NoCurrentCaseException Case has been closed.
* @throws TskCoreException fileManager cannot find AbstractFile
* files.
* @throws IOException Issue during writing to file.
*/
public static void findAndCopySQLiteMetaFile(AbstractFile sqliteFile,
String metaFileName) throws NoCurrentCaseException, TskCoreException, IOException {
Case openCase = Case.getCurrentCaseThrows();
SleuthkitCase sleuthkitCase = openCase.getSleuthkitCase();
Services services = new Services(sleuthkitCase);
FileManager fileManager = services.getFileManager();
List<AbstractFile> metaFiles = fileManager.findFiles(
sqliteFile.getDataSource(), metaFileName,
sqliteFile.getParent().getName());
if (metaFiles != null) {
for (AbstractFile metaFile : metaFiles) {
writeAbstractFileToLocalDisk(metaFile);
}
}
}
/**
* Copies the file contents into a unique path in the current case temp
* directory.
*
* @param file AbstractFile from the data source
*
* @return The path of the file on disk
*
* @throws IOException Exception writing file contents
* @throws NoCurrentCaseException Current case closed during file copying
*/
public static String writeAbstractFileToLocalDisk(AbstractFile file)
throws IOException, NoCurrentCaseException {
String localDiskPath = getUniqueTempDirectoryPath(file);
File localDatabaseFile = new File(localDiskPath);
if (!localDatabaseFile.exists()) {
ContentUtils.writeToFile(file, localDatabaseFile);
}
return localDiskPath;
}
/**
* Generates a unique local disk path that resides in the temp directory of
* the current case.
*
* @param file The database abstract file
*
* @return Unique local disk path living in the temp directory of the case
*
* @throws org.sleuthkit.autopsy.casemodule.NoCurrentCaseException
*/
public static String getUniqueTempDirectoryPath(AbstractFile file) throws NoCurrentCaseException {
return Case.getCurrentCaseThrows().getTempDirectory()
+ File.separator + file.getId() + file.getName();
}
}
@@ -18,6 +18,8 @@
*/
package org.sleuthkit.autopsy.corecomponents;
import java.beans.PropertyChangeEvent;
import java.util.EnumSet;
import java.util.Objects;
import java.util.TimeZone;
import javax.swing.JPanel;
@@ -46,7 +48,11 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
public ViewPreferencesPanel(boolean immediateUpdates) {
initComponents();
this.immediateUpdates = immediateUpdates;
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), (PropertyChangeEvent evt) -> {
//disable when case is closed, enable when case is open
currentCaseSettingsPanel.setEnabled(evt.getNewValue() != null);
groupByDataSourceCheckbox.setEnabled(evt.getNewValue() != null);
});
this.timeZoneList.setListData(TimeZoneUtils.createTimeZoneList().stream().toArray(String[]::new));
}
@@ -55,13 +55,13 @@ import org.sleuthkit.datamodel.TskCoreException;
*
* or
*
* SQLiteTableReader reader = new SQLiteTableReader.Builder(file) .onInteger(new
* Consumer<Integer>() {
* @Override public void accept(Integer i) {
* System.out.println(i);
* }
* }).build();
*
* SQLiteTableReader reader = new SQLiteTableReader.Builder(file)
* .onInteger(new Consumer<Integer>() {
* @Override public void accept(Integer i) {
* System.out.println(i);
* }
* }).build();
*
* reader.reader(tableName);
*
* Invocation of read(String tableName) reads row by row. When an Integer is
@@ -83,10 +83,9 @@ public class SQLiteTableReader implements AutoCloseable {
private Consumer<Double> onFloatAction;
private Consumer<byte[]> onBlobAction;
private Consumer<Object> forAllAction;
static <T> Consumer<T> doNothing() {
return NOOP -> {
};
return NOOP -> {};
}
/**
@@ -211,7 +210,7 @@ public class SQLiteTableReader implements AutoCloseable {
private final AbstractFile file;
private final Builder builder;
private static final String SELECT_ALL_QUERY = "SELECT * FROM \"%s\"";
private static final Logger logger = Logger.getLogger(SQLiteTableReader.class.getName());
@@ -528,7 +527,6 @@ public class SQLiteTableReader implements AutoCloseable {
@Override
public void close() throws SQLiteTableReaderException {
try {
closeTableResources();
if (Objects.nonNull(conn)) {
conn.close();
}
@@ -304,16 +304,8 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
"AbstractAbstractFileNode.sizeColLbl=Size",
"AbstractAbstractFileNode.flagsDirColLbl=Flags(Dir)",
"AbstractAbstractFileNode.flagsMetaColLbl=Flags(Meta)",
"AbstractAbstractFileNode.modeColLbl=Mode",
"AbstractAbstractFileNode.useridColLbl=UserID",
"AbstractAbstractFileNode.groupidColLbl=GroupID",
"AbstractAbstractFileNode.metaAddrColLbl=Meta Addr.",
"AbstractAbstractFileNode.attrAddrColLbl=Attr. Addr.",
"AbstractAbstractFileNode.typeDirColLbl=Type(Dir)",
"AbstractAbstractFileNode.typeMetaColLbl=Type(Meta)",
"AbstractAbstractFileNode.knownColLbl=Known",
"AbstractAbstractFileNode.md5HashColLbl=MD5 Hash",
"AbstractAbstractFileNode.objectId=Object ID",
"AbstractAbstractFileNode.mimeType=MIME Type",
"AbstractAbstractFileNode.extensionColLbl=Extension"})
public enum AbstractFilePropertyType {
@@ -331,16 +323,8 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
SIZE(AbstractAbstractFileNode_sizeColLbl()),
FLAGS_DIR(AbstractAbstractFileNode_flagsDirColLbl()),
FLAGS_META(AbstractAbstractFileNode_flagsMetaColLbl()),
MODE(AbstractAbstractFileNode_modeColLbl()),
USER_ID(AbstractAbstractFileNode_useridColLbl()),
GROUP_ID(AbstractAbstractFileNode_groupidColLbl()),
META_ADDR(AbstractAbstractFileNode_metaAddrColLbl()),
ATTR_ADDR(AbstractAbstractFileNode_attrAddrColLbl()),
TYPE_DIR(AbstractAbstractFileNode_typeDirColLbl()),
TYPE_META(AbstractAbstractFileNode_typeMetaColLbl()),
KNOWN(AbstractAbstractFileNode_knownColLbl()),
MD5HASH(AbstractAbstractFileNode_md5HashColLbl()),
ObjectID(AbstractAbstractFileNode_objectId()),
MIMETYPE(AbstractAbstractFileNode_mimeType()),
EXTENSION(AbstractAbstractFileNode_extensionColLbl());
@@ -391,16 +375,8 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
properties.add(new NodeProperty<>(SIZE.toString(), SIZE.toString(), NO_DESCR, content.getSize()));
properties.add(new NodeProperty<>(FLAGS_DIR.toString(), FLAGS_DIR.toString(), NO_DESCR, content.getDirFlagAsString()));
properties.add(new NodeProperty<>(FLAGS_META.toString(), FLAGS_META.toString(), NO_DESCR, content.getMetaFlagsAsString()));
properties.add(new NodeProperty<>(MODE.toString(), MODE.toString(), NO_DESCR, content.getModesAsString()));
properties.add(new NodeProperty<>(USER_ID.toString(), USER_ID.toString(), NO_DESCR, content.getUid()));
properties.add(new NodeProperty<>(GROUP_ID.toString(), GROUP_ID.toString(), NO_DESCR, content.getGid()));
properties.add(new NodeProperty<>(META_ADDR.toString(), META_ADDR.toString(), NO_DESCR, content.getMetaAddr()));
properties.add(new NodeProperty<>(ATTR_ADDR.toString(), ATTR_ADDR.toString(), NO_DESCR, content.getAttrType().getValue() + "-" + content.getAttributeId()));
properties.add(new NodeProperty<>(TYPE_DIR.toString(), TYPE_DIR.toString(), NO_DESCR, content.getDirType().getLabel()));
properties.add(new NodeProperty<>(TYPE_META.toString(), TYPE_META.toString(), NO_DESCR, content.getMetaType().toString()));
properties.add(new NodeProperty<>(KNOWN.toString(), KNOWN.toString(), NO_DESCR, content.getKnown().getName()));
properties.add(new NodeProperty<>(MD5HASH.toString(), MD5HASH.toString(), NO_DESCR, StringUtils.defaultString(content.getMd5Hash())));
properties.add(new NodeProperty<>(ObjectID.toString(), ObjectID.toString(), NO_DESCR, content.getId()));
properties.add(new NodeProperty<>(MIMETYPE.toString(), MIMETYPE.toString(), NO_DESCR, StringUtils.defaultString(content.getMIMEType())));
properties.add(new NodeProperty<>(EXTENSION.toString(), EXTENSION.toString(), NO_DESCR, content.getNameExtension()));
@@ -631,17 +607,9 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
map.put(SIZE.toString(), content.getSize());
map.put(FLAGS_DIR.toString(), content.getDirFlagAsString());
map.put(FLAGS_META.toString(), content.getMetaFlagsAsString());
map.put(MODE.toString(), content.getModesAsString());
map.put(USER_ID.toString(), content.getUid());
map.put(GROUP_ID.toString(), content.getGid());
map.put(META_ADDR.toString(), content.getMetaAddr());
map.put(ATTR_ADDR.toString(), content.getAttrType().getValue() + "-" + content.getAttributeId());
map.put(TYPE_DIR.toString(), content.getDirType().getLabel());
map.put(TYPE_META.toString(), content.getMetaType().toString());
map.put(KNOWN.toString(), content.getKnown().getName());
map.put(MD5HASH.toString(), StringUtils.defaultString(content.getMd5Hash()));
map.put(ObjectID.toString(), content.getId());
map.put(MIMETYPE.toString(), StringUtils.defaultString(content.getMIMEType()));
map.put(EXTENSION.toString(), content.getNameExtension());
}
}
}
@@ -6,9 +6,6 @@ AbstractAbstractFileNode.changeTimeColLbl=\u5909\u66f4\u65e5\u6642
AbstractAbstractFileNode.accessTimeColLbl=\u30a2\u30af\u30bb\u30b9\u65e5\u6642
AbstractAbstractFileNode.createdTimeColLbl=\u4f5c\u6210\u65e5\u6642
AbstractAbstractFileNode.sizeColLbl=\u30b5\u30a4\u30ba
AbstractAbstractFileNode.modeColLbl=\u30e2\u30fc\u30c9
AbstractAbstractFileNode.useridColLbl=\u30e6\u30fc\u30b6ID
AbstractAbstractFileNode.groupidColLbl=\u30b0\u30eb\u30fc\u30d7ID
AbstractAbstractFileNode.knownColLbl=\u65e2\u77e5
AbstractAbstractFileNode.md5HashColLbl=MD5\u30cf\u30c3\u30b7\u30e5
AbstractContentChildren.CreateTSKNodeVisitor.exception.noNodeMsg=\u6307\u5b9a\u3055\u308c\u305fSleuthkitItem\u306e\u30ce\u30fc\u30c9\u304c\u5b9a\u7fa9\u3055\u308c\u3066\u3044\u307e\u305b\u3093
@@ -198,10 +195,6 @@ TagsNode.displayName.text=\u30bf\u30b0
TagsNode.createSheet.name.name=\u540d\u524d
AbstractAbstractFileNode.flagsDirColLbl=\u30d5\u30e9\u30b0\uff08\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\uff09
AbstractAbstractFileNode.flagsMetaColLbl=\u30d5\u30e9\u30b0\uff08\u30e1\u30bf\u30c7\u30fc\u30bf\uff09
AbstractAbstractFileNode.metaAddrColLbl=\u30e1\u30bf\u30c7\u30fc\u30bf\u30a2\u30c9\u30ec\u30b9
AbstractAbstractFileNode.attrAddrColLbl=\u5c5e\u6027\u30a2\u30c9\u30ec\u30b9
AbstractAbstractFileNode.typeDirColLbl=\u30bf\u30a4\u30d7\uff08\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\uff09
AbstractAbstractFileNode.typeMetaColLbl=\u30bf\u30a4\u30d7\uff08\u30e1\u30bf\u30c7\u30fc\u30bf\uff09
ArtifactTypeNode.createSheet.childCnt.displayName=\u30c1\u30e3\u30a4\u30eb\u30c9\u6570
TagsNode.createSheet.name.displayName=\u540d\u524d
ViewsNode.name.text=\u30d3\u30e5\u30fc
@@ -238,7 +231,6 @@ KeywordHits.createSheet.numChildren.name=\u30c1\u30e3\u30a4\u30eb\u30c9\u6570
KeywordHits.createSheet.numChildren.displayName=\u30c1\u30e3\u30a4\u30eb\u30c9\u6570
KeywordHits.simpleLiteralSearch.text=\u30b7\u30f3\u30b0\u30eb\u30ea\u30c6\u30e9\u30eb\u691c\u7d22
KeywordHits.singleRegexSearch.text=\u30b7\u30f3\u30b0\u30eb\u6b63\u898f\u8868\u73fe\u691c\u7d22
AbstractAbstractFileNode.objectId=\u30aa\u30d6\u30b8\u30a7\u30af\u30c8ID
ArtifactStringContent.getStr.artifactId.text=\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8ID
OpenReportAction.actionDisplayName=\u30ec\u30dd\u30fc\u30c8\u3092\u958b\u304f
OpenReportAction.actionPerformed.MessageBoxTitle=\u5931\u6557\u30ec\u30dd\u30fc\u30c8\u3092\u958b\u304f
@@ -29,6 +29,7 @@ import org.sleuthkit.autopsy.datamodel.FileSize.FileSizeRootChildren.FileSizeNod
import org.sleuthkit.autopsy.datamodel.FileSize.FileSizeRootNode;
import org.sleuthkit.autopsy.datamodel.FileTypes.FileTypesNode;
import org.sleuthkit.autopsy.datamodel.accounts.Accounts;
import org.sleuthkit.autopsy.othercasessearch.CorrelationAttributeInstanceNode;
/**
* Visitor pattern that goes over all nodes in the directory tree. This includes
@@ -125,6 +126,8 @@ public interface DisplayableItemNodeVisitor<T> {
T visit(CentralRepoCommonAttributeInstanceNode crfin);
T visit(InstanceCountNode icn);
T visit(CorrelationAttributeInstanceNode cain);
/*
* Tags
@@ -214,6 +217,11 @@ public interface DisplayableItemNodeVisitor<T> {
return defaultVisit(icn);
}
@Override
public T visit(CorrelationAttributeInstanceNode cain) {
return defaultVisit(cain);
}
@Override
public T visit(CentralRepoCommonAttributeInstanceNode crfin){
return defaultVisit(crfin);
@@ -0,0 +1,10 @@
OtherCasesSearchDialog.searchButton.AccessibleContext.accessibleDescription=
OtherCasesSearchDialog.searchButton.AccessibleContext.accessibleName=Search
OtherCasesSearchDialog.searchButton.text=Search
OtherCasesSearchDialog.correlationValueTextField.text=
OtherCasesSearchDialog.correlationValueLabel.text=Correlation Property Value:
OtherCasesSearchDialog.descriptionLabel.text=Search data in the Central Repository from other cases.
OtherCasesSearchDialog.errorLabel.text=\
OtherCasesSearchDialog.correlationTypeLabel.text=Correlation Property Type:
OtherCasesSearchDialog.casesLabel.text=\
@@ -0,0 +1,141 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.othercasessearch;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.swing.Action;
import org.openide.nodes.Children;
import org.openide.nodes.Sheet;
import org.openide.util.NbBundle;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationDataSource;
import org.sleuthkit.autopsy.othercasessearch.Bundle;
import org.sleuthkit.autopsy.datamodel.DisplayableItemNode;
import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor;
import org.sleuthkit.autopsy.datamodel.NodeProperty;
/**
* Used by the Other Cases Search feature to encapsulate instances of a given
* search match.
*/
public final class CorrelationAttributeInstanceNode extends DisplayableItemNode {
private final CorrelationAttributeInstance instance;
CorrelationAttributeInstanceNode(CorrelationAttributeInstance content) {
super(Children.LEAF, Lookups.fixed(content));
this.instance = content;
this.setDisplayName(new File(this.instance.getFilePath()).getName());
}
/**
* Get the CorrelationAttributeInstance attached to the node.
*
* @return The CorrelationAttributeInstance object.
*/
public CorrelationAttributeInstance getCorrelationAttributeInstance(){
return this.instance;
}
@Override
public Action[] getActions(boolean context){
List<Action> actionsList = new ArrayList<>();
actionsList.addAll(Arrays.asList(super.getActions(true)));
return actionsList.toArray(new Action[actionsList.size()]);
}
@Override
public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
return visitor.visit(this);
}
@Override
public boolean isLeafTypeNode() {
return true;
}
@Override
public String getItemType() {
return CorrelationAttributeInstanceNode.class.getName();
}
@NbBundle.Messages({
"CorrelationAttributeInstanceNode.columnName.name=Name",
"CorrelationAttributeInstanceNode.columnName.case=Case",
"CorrelationAttributeInstanceNode.columnName.dataSource=Data Source",
"CorrelationAttributeInstanceNode.columnName.known=Known",
"CorrelationAttributeInstanceNode.columnName.path=Path",
"CorrelationAttributeInstanceNode.columnName.comment=Comment",
"CorrelationAttributeInstanceNode.columnName.device=Device"
})
@Override
protected Sheet createSheet(){
Sheet sheet = new Sheet();
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
if(sheetSet == null){
sheetSet = Sheet.createPropertiesSet();
sheet.put(sheetSet);
}
final CorrelationAttributeInstance centralRepoFile = this.getCorrelationAttributeInstance();
final String path = centralRepoFile.getFilePath();
final File file = new File(path);
final String name = file.getName();
final String caseName = centralRepoFile.getCorrelationCase().getDisplayName();
final CorrelationDataSource dataSource = centralRepoFile.getCorrelationDataSource();
final String dataSourceName = dataSource.getName();
final String known = centralRepoFile.getKnownStatus().getName();
final String comment = centralRepoFile.getComment();
final String device = dataSource.getDeviceID();
final String NO_DESCR = "";
sheetSet.put(new NodeProperty<>(
Bundle.CorrelationAttributeInstanceNode_columnName_name(),
Bundle.CorrelationAttributeInstanceNode_columnName_name(), NO_DESCR, name));
sheetSet.put(new NodeProperty<>(
Bundle.CorrelationAttributeInstanceNode_columnName_case(),
Bundle.CorrelationAttributeInstanceNode_columnName_case(), NO_DESCR, caseName));
sheetSet.put(new NodeProperty<>(
Bundle.CorrelationAttributeInstanceNode_columnName_dataSource(),
Bundle.CorrelationAttributeInstanceNode_columnName_dataSource(), NO_DESCR, dataSourceName));
sheetSet.put(new NodeProperty<>(
Bundle.CorrelationAttributeInstanceNode_columnName_known(),
Bundle.CorrelationAttributeInstanceNode_columnName_known(), NO_DESCR, known));
sheetSet.put(new NodeProperty<>(
Bundle.CorrelationAttributeInstanceNode_columnName_path(),
Bundle.CorrelationAttributeInstanceNode_columnName_path(), NO_DESCR, path));
sheetSet.put(new NodeProperty<>(
Bundle.CorrelationAttributeInstanceNode_columnName_comment(),
Bundle.CorrelationAttributeInstanceNode_columnName_comment(), NO_DESCR, comment));
sheetSet.put(new NodeProperty<>(
Bundle.CorrelationAttributeInstanceNode_columnName_device(),
Bundle.CorrelationAttributeInstanceNode_columnName_device(), NO_DESCR, device));
return sheet;
}
}
@@ -0,0 +1,85 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.othercasessearch;
import org.openide.nodes.Children;
import org.openide.nodes.FilterNode;
import org.openide.nodes.Node;
import org.sleuthkit.autopsy.corecomponents.TableFilterNode;
/**
* A <code>Children</code> implementation for a
* <code>CorrelationPropertyFilterNode</code>.
*/
final class OtherCasesFilterChildren extends FilterNode.Children {
/**
* Create a new Children instance.
*
* @param wrappedNode The node to be wrapped.
* @param createChildren If false, return LEAF. Otherwise, return a new
* CorrelationPropertyFilterChildren instance.
*
* @return A Children instance.
*/
static Children createInstance(Node wrappedNode, boolean createChildren) {
if (createChildren) {
return new OtherCasesFilterChildren(wrappedNode);
} else {
return Children.LEAF;
}
}
/**
* Constructs a children (child factory) implementation for a
* <code>CorrelationPropertyFilterNode</code>.
*
* @param wrappedNode The node wrapped by CorrelationPropertyFilterNode.
*/
OtherCasesFilterChildren(Node wrappedNode) {
super(wrappedNode);
}
/**
* Copies a CorrelationPropertyFilterNode, with the children (child factory)
* flag set to false.
*
* @param nodeToCopy The CorrelationPropertyFilterNode to copy.
*
* @return A copy of a CorrelationPropertyFilterNode.
*/
@Override
protected Node copyNode(Node nodeToCopy) {
return new TableFilterNode(nodeToCopy, false);
}
/**
* Creates the child nodes represented by this children (child factory)
* object.
*
* @param key The key, i.e., the node, for which to create the child nodes.
*
* @return A single-element node array.
*/
@Override
protected Node[] createNodes(Node key) {
return new Node[]{this.copyNode(key)};
}
}
@@ -0,0 +1,69 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.othercasessearch;
import java.awt.event.ActionEvent;
import org.openide.awt.ActionID;
import org.openide.awt.ActionReference;
import org.openide.awt.ActionRegistration;
import org.openide.util.HelpCtx;
import org.openide.util.NbBundle;
import org.openide.util.actions.CallableSystemAction;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
import org.sleuthkit.autopsy.othercasessearch.Bundle;
/**
* Action for accessing the Search Other Cases dialog.
*/
@ActionID(category = "Tools", id = "org.sleuthkit.autopsy.othercasessearch.OtherCasesSearchAction")
@ActionRegistration(displayName = "#CTL_OtherCasesSearchAction=Search Other Cases", lazy = false)
@ActionReference(path = "Menu/Tools", position = 104)
@NbBundle.Messages({"CTL_OtherCasesSearchAction=Search Other Cases"})
public class OtherCasesSearchAction extends CallableSystemAction {
@Override
public boolean isEnabled() {
return EamDb.isEnabled() && Case.isCaseOpen();
}
@Override
public void actionPerformed(ActionEvent event) {
performAction();
}
@Override
public void performAction() {
OtherCasesSearchDialog dialog = new OtherCasesSearchDialog();
dialog.display();
}
@NbBundle.Messages({
"OtherCasesSearchAction.getName.text=Search Other Cases"})
@Override
public String getName() {
return Bundle.OtherCasesSearchAction_getName_text();
}
@Override
public HelpCtx getHelpCtx() {
return HelpCtx.DEFAULT_HELP;
}
}
@@ -0,0 +1,49 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.othercasessearch;
import java.util.List;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
/**
* Creates CorrelationAttributeInstanceNodes from a collection of
* CorrelationAttributeInstances.
*/
class OtherCasesSearchChildren extends Children.Keys<CorrelationAttributeInstance> {
/**
* Create an instance of OtherCasesSearchChildren.
*
* @param lazy Lazy load?
* @param fileList List of CorrelationAttributeInstances.
*/
OtherCasesSearchChildren(boolean lazy, List<CorrelationAttributeInstance> instances) {
super(lazy);
this.setKeys(instances);
}
@Override
protected Node[] createNodes(CorrelationAttributeInstance t) {
Node[] node = new Node[1];
node[0] = new CorrelationAttributeInstanceNode(t);
return node;
}
}
@@ -0,0 +1,159 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JDialogFormInfo">
<Properties>
<Property name="defaultCloseOperation" type="int" value="2"/>
<Property name="resizable" type="boolean" value="false"/>
</Properties>
<SyntheticProperties>
<SyntheticProperty name="formSizePolicy" type="int" value="1"/>
<SyntheticProperty name="generateCenter" type="boolean" value="false"/>
</SyntheticProperties>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<Component id="casesLabel" max="32767" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="searchButton" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<Component id="descriptionLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="correlationValueLabel" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="correlationTypeLabel" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="correlationTypeComboBox" pref="289" max="32767" attributes="0"/>
<Component id="correlationValueTextField" max="32767" attributes="0"/>
<Component id="errorLabel" alignment="0" max="32767" attributes="0"/>
</Group>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="descriptionLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="correlationTypeComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="correlationTypeLabel" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="correlationValueLabel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="correlationValueTextField" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="errorLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="11" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="searchButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="casesLabel" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="correlationValueLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/othercasessearch/Bundle.properties" key="OtherCasesSearchDialog.correlationValueLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="correlationValueTextField">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/othercasessearch/Bundle.properties" key="OtherCasesSearchDialog.correlationValueTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="searchButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/othercasessearch/Bundle.properties" key="OtherCasesSearchDialog.searchButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<AccessibilityProperties>
<Property name="AccessibleContext.accessibleName" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/othercasessearch/Bundle.properties" key="OtherCasesSearchDialog.searchButton.AccessibleContext.accessibleName" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="AccessibleContext.accessibleDescription" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/othercasessearch/Bundle.properties" key="OtherCasesSearchDialog.searchButton.AccessibleContext.accessibleDescription" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</AccessibilityProperties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="searchButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JComboBox" name="correlationTypeComboBox">
<Properties>
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
<StringArray count="0"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
</AuxValues>
</Component>
<Component class="javax.swing.JLabel" name="correlationTypeLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/othercasessearch/Bundle.properties" key="OtherCasesSearchDialog.correlationTypeLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="errorLabel">
<Properties>
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="0" green="0" red="ff" type="rgb"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/othercasessearch/Bundle.properties" key="OtherCasesSearchDialog.errorLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="descriptionLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/othercasessearch/Bundle.properties" key="OtherCasesSearchDialog.descriptionLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="casesLabel">
<Properties>
<Property name="horizontalAlignment" type="int" value="0"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/othercasessearch/Bundle.properties" key="OtherCasesSearchDialog.casesLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Form>
@@ -0,0 +1,381 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.othercasessearch;
import java.awt.Color;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.JFrame;
import javax.swing.SwingWorker;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import org.openide.nodes.Node;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle.Messages;
import org.openide.windows.TopComponent;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNormalizationException;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer;
import org.sleuthkit.autopsy.corecomponents.DataResultTopComponent;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
import org.sleuthkit.autopsy.corecomponents.TableFilterNode;
import org.sleuthkit.autopsy.corecomponents.TextPrompt;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.EmptyNode;
@Messages({
"OtherCasesSearchDialog.dialogTitle.text=Search Other Cases",
"OtherCasesSearchDialog.resultsTitle.text=Other Cases",
"OtherCasesSearchDialog.resultsDescription.text=Other Cases Search",
"OtherCasesSearchDialog.emptyNode.text=No results found.",
"OtherCasesSearchDialog.validation.invalidHash=The supplied value is not a valid MD5 hash.",
"# {0} - number of cases",
"OtherCasesSearchDialog.caseLabel.text=The current Central Repository contains {0} case(s)."
})
/**
* The Search Other Cases dialog allows users to search for specific
* types of correlation properties in the Central Repository.
*/
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
final class OtherCasesSearchDialog extends javax.swing.JDialog {
private static final Logger logger = Logger.getLogger(OtherCasesSearchDialog.class.getName());
private static final long serialVersionUID = 1L;
private static final String FILES_CORRELATION_TYPE = "Files";
private final List<CorrelationAttributeInstance.Type> correlationTypes;
private TextPrompt correlationValueTextFieldPrompt;
/**
* Creates a new instance of the Search Other Cases dialog.
*/
OtherCasesSearchDialog() {
super((JFrame) WindowManager.getDefault().getMainWindow(), Bundle.OtherCasesSearchDialog_dialogTitle_text(), true);
this.correlationTypes = new ArrayList<>();
initComponents();
customizeComponents();
}
/**
* Perform the other cases search.
*/
private void search() {
new SwingWorker<List<CorrelationAttributeInstance>, Void>() {
@Override
protected List<CorrelationAttributeInstance> doInBackground() {
List<CorrelationAttributeInstance.Type> correlationTypes;
List<CorrelationAttributeInstance> correlationInstances = new ArrayList<>();
try {
correlationTypes = EamDb.getInstance().getDefinedCorrelationTypes();
for (CorrelationAttributeInstance.Type type : correlationTypes) {
if (type.getDisplayName().equals((String) correlationTypeComboBox.getSelectedItem())) {
correlationInstances = EamDb.getInstance().getArtifactInstancesByTypeValue(type, correlationValueTextField.getText());
break;
}
}
} catch (EamDbException ex) {
logger.log(Level.SEVERE, "Unable to connect to the Central Repository database.", ex);
} catch (CorrelationAttributeNormalizationException ex) {
logger.log(Level.SEVERE, "Unable to retrieve data from the Central Repository.", ex);
}
return correlationInstances;
}
@Override
protected void done() {
try {
super.done();
List<CorrelationAttributeInstance> correlationInstances = this.get();
DataResultViewerTable table = new DataResultViewerTable();
Collection<DataResultViewer> viewers = new ArrayList<>(1);
viewers.add(table);
OtherCasesSearchNode searchNode = new OtherCasesSearchNode(correlationInstances);
TableFilterNode tableFilterNode = new TableFilterNode(searchNode, true, searchNode.getName());
String resultsText = String.format("%s (%s; \"%s\")",
Bundle.OtherCasesSearchDialog_resultsTitle_text(),
(String) correlationTypeComboBox.getSelectedItem(),
correlationValueTextField.getText());
final TopComponent searchResultWin;
if (correlationInstances.isEmpty()) {
Node emptyNode = new TableFilterNode(
new EmptyNode(Bundle.OtherCasesSearchDialog_emptyNode_text()), true);
searchResultWin = DataResultTopComponent.createInstance(
resultsText, Bundle.OtherCasesSearchDialog_resultsDescription_text(), emptyNode, 0);
} else {
searchResultWin = DataResultTopComponent.createInstance(
resultsText, Bundle.OtherCasesSearchDialog_resultsDescription_text(), tableFilterNode, correlationInstances.size(), viewers);
}
searchResultWin.requestActive(); // make it the active top component
} catch (ExecutionException | InterruptedException ex) {
logger.log(Level.SEVERE, "Unable to get CorrelationAttributeInstances.", ex);
}
}
}.execute();
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
correlationValueLabel = new javax.swing.JLabel();
correlationValueTextField = new javax.swing.JTextField();
searchButton = new javax.swing.JButton();
correlationTypeComboBox = new javax.swing.JComboBox<>();
correlationTypeLabel = new javax.swing.JLabel();
errorLabel = new javax.swing.JLabel();
descriptionLabel = new javax.swing.JLabel();
casesLabel = new javax.swing.JLabel();
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
setResizable(false);
org.openide.awt.Mnemonics.setLocalizedText(correlationValueLabel, org.openide.util.NbBundle.getMessage(OtherCasesSearchDialog.class, "OtherCasesSearchDialog.correlationValueLabel.text")); // NOI18N
correlationValueTextField.setText(org.openide.util.NbBundle.getMessage(OtherCasesSearchDialog.class, "OtherCasesSearchDialog.correlationValueTextField.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(searchButton, org.openide.util.NbBundle.getMessage(OtherCasesSearchDialog.class, "OtherCasesSearchDialog.searchButton.text")); // NOI18N
searchButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
searchButtonActionPerformed(evt);
}
});
org.openide.awt.Mnemonics.setLocalizedText(correlationTypeLabel, org.openide.util.NbBundle.getMessage(OtherCasesSearchDialog.class, "OtherCasesSearchDialog.correlationTypeLabel.text")); // NOI18N
errorLabel.setForeground(new java.awt.Color(255, 0, 0));
org.openide.awt.Mnemonics.setLocalizedText(errorLabel, org.openide.util.NbBundle.getMessage(OtherCasesSearchDialog.class, "OtherCasesSearchDialog.errorLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(descriptionLabel, org.openide.util.NbBundle.getMessage(OtherCasesSearchDialog.class, "OtherCasesSearchDialog.descriptionLabel.text")); // NOI18N
casesLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
org.openide.awt.Mnemonics.setLocalizedText(casesLabel, org.openide.util.NbBundle.getMessage(OtherCasesSearchDialog.class, "OtherCasesSearchDialog.casesLabel.text")); // NOI18N
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addComponent(casesLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGap(18, 18, 18)
.addComponent(searchButton))
.addGroup(layout.createSequentialGroup()
.addComponent(descriptionLabel)
.addGap(0, 0, Short.MAX_VALUE))
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(correlationValueLabel)
.addComponent(correlationTypeLabel))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(correlationTypeComboBox, 0, 289, Short.MAX_VALUE)
.addComponent(correlationValueTextField)
.addComponent(errorLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap()
.addComponent(descriptionLabel)
.addGap(18, 18, 18)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(correlationTypeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(correlationTypeLabel))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(correlationValueLabel)
.addComponent(correlationValueTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(errorLabel)
.addGap(11, 11, 11)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(searchButton)
.addComponent(casesLabel))
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
searchButton.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(OtherCasesSearchDialog.class, "OtherCasesSearchDialog.searchButton.AccessibleContext.accessibleName")); // NOI18N
searchButton.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(OtherCasesSearchDialog.class, "OtherCasesSearchDialog.searchButton.AccessibleContext.accessibleDescription")); // NOI18N
pack();
}// </editor-fold>//GEN-END:initComponents
private void searchButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchButtonActionPerformed
if (validateInputs()) {
/*
* Just in case, we'll lock down the type and value components to
* avoid the possibly of a race condition.
*/
correlationTypeComboBox.setEnabled(false);
correlationValueTextField.setEnabled(false);
search();
dispose();
} else {
searchButton.setEnabled(false);
errorLabel.setText(Bundle.OtherCasesSearchDialog_validation_invalidHash());
correlationValueTextField.grabFocus();
}
}//GEN-LAST:event_searchButtonActionPerformed
/**
* Further customize the components beyond the standard initialization.
*/
private void customizeComponents() {
searchButton.setEnabled(false);
/*
* Add correlation types to the combo-box.
*/
try {
EamDb dbManager = EamDb.getInstance();
correlationTypes.clear();
correlationTypes.addAll(dbManager.getDefinedCorrelationTypes());
int numberOfCases = dbManager.getCases().size();
casesLabel.setText(Bundle.OtherCasesSearchDialog_caseLabel_text(numberOfCases));
} catch (EamDbException ex) {
logger.log(Level.SEVERE, "Unable to connect to the Central Repository database.", ex);
}
for (CorrelationAttributeInstance.Type type : correlationTypes) {
// We only support the "Files" type for now.
if (type.getDisplayName().equals(FILES_CORRELATION_TYPE)) {
correlationTypeComboBox.addItem(type.getDisplayName());
}
}
correlationTypeComboBox.setSelectedIndex(0);
correlationTypeComboBox.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
updateSearchButton();
}
});
/*
* Create listener for text input.
*/
correlationValueTextField.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void changedUpdate(DocumentEvent e) {
updateSearchButton();
}
@Override
public void insertUpdate(DocumentEvent e) {
updateSearchButton();
}
@Override
public void removeUpdate(DocumentEvent e) {
updateSearchButton();
}
});
updateCorrelationValueTextFieldPrompt();
}
@Messages({
"OtherCasesSearchDialog.correlationValueTextField.filesExample=Example: \"f0e1d2c3b4a5968778695a4b3c2d1e0f\""
})
/**
* Update the text prompt of the name text field based on the input type
* selection.
*/
private void updateCorrelationValueTextFieldPrompt() {
/**
* Add text prompt to the text field.
*/
String text = Bundle.OtherCasesSearchDialog_correlationValueTextField_filesExample();
correlationValueTextFieldPrompt = new TextPrompt(text, correlationValueTextField);
/**
* Sets the foreground color and transparency of the text prompt.
*/
correlationValueTextFieldPrompt.setForeground(Color.LIGHT_GRAY);
correlationValueTextFieldPrompt.changeAlpha(0.9f); // Mostly opaque
validate();
repaint();
}
/**
* Enable or disable the Search button depending on whether or not text has
* been provided for the correlation property value.
*/
private void updateSearchButton() {
searchButton.setEnabled(correlationValueTextField.getText().isEmpty() == false);
}
/**
* Validate the value input.
*
* @return True if the input is valid for the selected type; otherwise false.
*/
private boolean validateInputs() {
Pattern md5Pattern = Pattern.compile("^[a-fA-F0-9]{32}$"); // NON-NLS
Matcher matcher = md5Pattern.matcher(correlationValueTextField.getText().trim());
if (matcher.find()) {
return true;
}
return false;
}
/**
* Display the Search Other Cases dialog.
*/
public void display() {
this.setLocationRelativeTo(WindowManager.getDefault().getMainWindow());
setVisible(true);
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel casesLabel;
private javax.swing.JComboBox<String> correlationTypeComboBox;
private javax.swing.JLabel correlationTypeLabel;
private javax.swing.JLabel correlationValueLabel;
private javax.swing.JTextField correlationValueTextField;
private javax.swing.JLabel descriptionLabel;
private javax.swing.JLabel errorLabel;
private javax.swing.JButton searchButton;
// End of variables declaration//GEN-END:variables
}
@@ -0,0 +1,47 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.othercasessearch;
import java.util.List;
import org.openide.nodes.AbstractNode;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
/**
* Parent node to OtherCasesSearchChildren.
*/
class OtherCasesSearchNode extends AbstractNode {
/**
* Create an instance of OtherCasesSearchNode.
*
* @param keys The list of CorrelationAttributeInstances.
*/
OtherCasesSearchNode(List<CorrelationAttributeInstance> keys) {
super(new OtherCasesSearchChildren(true, keys));
}
@Messages({
"OtherCasesSearchNode.getName.text=Other Cases Search"
})
@Override
public String getName() {
return Bundle.OtherCasesSearchNode_getName_text();
}
}
@@ -50,8 +50,8 @@ import org.sleuthkit.datamodel.BlackboardArtifact;
@ActionID(category = "Tools", id = "org.sleuthkit.autopsy.report.ReportWizardAction")
@ActionRegistration(displayName = "#CTL_ReportWizardAction", lazy = false)
@ActionReferences(value = {
@ActionReference(path = "Menu/Tools", position = 103),
@ActionReference(path = "Toolbars/Case", position = 103)})
@ActionReference(path = "Menu/Tools", position = 105),
@ActionReference(path = "Toolbars/Case", position = 105)})
public final class ReportWizardAction extends CallableSystemAction implements Presenter.Toolbar, ActionListener {
private final JButton toolbarButton = new JButton();