mirror of
https://github.com/elisspace/autopsy.git
synced 2026-09-30 06:19:52 +00:00
Merge branch 'develop' of github.com:sleuthkit/autopsy into 4430_limit_results
This commit is contained in:
@@ -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));
|
||||
}
|
||||
|
||||
+50
-27
@@ -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
|
||||
*/
|
||||
|
||||
+68
@@ -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.
|
||||
*
|
||||
|
||||
+4
-1
@@ -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);
|
||||
|
||||
+2
-5
@@ -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
|
||||
|
||||
+2
-2
@@ -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>
|
||||
|
||||
+20
-28
@@ -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=\
|
||||
+141
@@ -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;
|
||||
}
|
||||
}
|
||||
+85
@@ -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)};
|
||||
}
|
||||
}
|
||||
+69
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
+49
@@ -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;
|
||||
}
|
||||
}
|
||||
+159
@@ -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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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="<String>"/>
|
||||
</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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
+381
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user