mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-12 16:06:15 +00:00
Merge branch 'develop' of https://github.com/sleuthkit/autopsy into 2665_FixFormatting
This commit is contained in:
commit
2a589a77e8
@ -98,6 +98,7 @@ public final class CasePreferences {
|
|||||||
Properties props = new Properties();
|
Properties props = new Properties();
|
||||||
props.load(inputStream);
|
props.load(inputStream);
|
||||||
String groupByDataSourceValue = props.getProperty(KEY_GROUP_BY_DATA_SOURCE);
|
String groupByDataSourceValue = props.getProperty(KEY_GROUP_BY_DATA_SOURCE);
|
||||||
|
if (groupByDataSourceValue != null) {
|
||||||
switch (groupByDataSourceValue) {
|
switch (groupByDataSourceValue) {
|
||||||
case VALUE_TRUE:
|
case VALUE_TRUE:
|
||||||
groupItemsInTreeByDataSource = true;
|
groupItemsInTreeByDataSource = true;
|
||||||
@ -106,9 +107,14 @@ public final class CasePreferences {
|
|||||||
groupItemsInTreeByDataSource = false;
|
groupItemsInTreeByDataSource = false;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
logger.log(Level.WARNING, String.format("Unexpected value '%s' for key '%s'. Using 'null' instead.",
|
||||||
|
groupByDataSourceValue, KEY_GROUP_BY_DATA_SOURCE));
|
||||||
groupItemsInTreeByDataSource = null;
|
groupItemsInTreeByDataSource = null;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
groupItemsInTreeByDataSource = null;
|
||||||
|
}
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
logger.log(Level.SEVERE, "Error reading settings file", ex);
|
logger.log(Level.SEVERE, "Error reading settings file", ex);
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.centralrepository.datamodel;
|
package org.sleuthkit.autopsy.centralrepository.datamodel;
|
||||||
|
|
||||||
|
import com.google.common.cache.Cache;
|
||||||
|
import com.google.common.cache.CacheBuilder;
|
||||||
|
import com.google.common.cache.CacheLoader;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -34,6 +37,8 @@ import java.time.LocalDate;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
import static org.sleuthkit.autopsy.centralrepository.datamodel.EamDbUtil.updateSchemaVersion;
|
import static org.sleuthkit.autopsy.centralrepository.datamodel.EamDbUtil.updateSchemaVersion;
|
||||||
@ -57,7 +62,21 @@ abstract class AbstractSqlEamDb implements EamDb {
|
|||||||
private int bulkArtifactsCount;
|
private int bulkArtifactsCount;
|
||||||
protected int bulkArtifactsThreshold;
|
protected int bulkArtifactsThreshold;
|
||||||
private final Map<String, Collection<CorrelationAttributeInstance>> bulkArtifacts;
|
private final Map<String, Collection<CorrelationAttributeInstance>> bulkArtifacts;
|
||||||
|
private static final int CASE_CACHE_TIMEOUT = 5;
|
||||||
|
private static final int DATA_SOURCE_CACHE_TIMEOUT = 5;
|
||||||
|
private static final Cache<Integer, CorrelationAttributeInstance.Type> typeCache = CacheBuilder.newBuilder().build();
|
||||||
|
private static final Cache<String, CorrelationCase> caseCacheByUUID = CacheBuilder.newBuilder()
|
||||||
|
.expireAfterWrite(CASE_CACHE_TIMEOUT, TimeUnit.MINUTES).
|
||||||
|
build();
|
||||||
|
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()
|
||||||
|
.expireAfterWrite(DATA_SOURCE_CACHE_TIMEOUT, TimeUnit.MINUTES).
|
||||||
|
build();
|
||||||
|
private static final Cache<String, CorrelationDataSource> dataSourceCacheById = CacheBuilder.newBuilder()
|
||||||
|
.expireAfterWrite(DATA_SOURCE_CACHE_TIMEOUT, TimeUnit.MINUTES).
|
||||||
|
build();
|
||||||
// Maximum length for the value column in the instance tables
|
// Maximum length for the value column in the instance tables
|
||||||
static final int MAX_VALUE_LENGTH = 256;
|
static final int MAX_VALUE_LENGTH = 256;
|
||||||
|
|
||||||
@ -149,6 +168,17 @@ abstract class AbstractSqlEamDb implements EamDb {
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the contents of the caches associated with EamDb results.
|
||||||
|
*/
|
||||||
|
protected final void clearCaches() {
|
||||||
|
typeCache.invalidateAll();
|
||||||
|
caseCacheByUUID.invalidateAll();
|
||||||
|
caseCacheById.invalidateAll();
|
||||||
|
dataSourceCacheByDeviceId.invalidateAll();
|
||||||
|
dataSourceCacheById.invalidateAll();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the value for a name in the name/value db_info table.
|
* Update the value for a name in the name/value db_info table.
|
||||||
*
|
*
|
||||||
@ -201,9 +231,9 @@ abstract class AbstractSqlEamDb implements EamDb {
|
|||||||
+ "examiner_name, examiner_email, examiner_phone, notes) "
|
+ "examiner_name, examiner_email, examiner_phone, notes) "
|
||||||
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) "
|
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) "
|
||||||
+ getConflictClause();
|
+ getConflictClause();
|
||||||
|
ResultSet resultSet = null;
|
||||||
try {
|
try {
|
||||||
preparedStatement = conn.prepareStatement(sql);
|
preparedStatement = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
|
||||||
|
|
||||||
preparedStatement.setString(1, eamCase.getCaseUUID());
|
preparedStatement.setString(1, eamCase.getCaseUUID());
|
||||||
if (null == eamCase.getOrg()) {
|
if (null == eamCase.getOrg()) {
|
||||||
@ -240,9 +270,21 @@ abstract class AbstractSqlEamDb implements EamDb {
|
|||||||
}
|
}
|
||||||
|
|
||||||
preparedStatement.executeUpdate();
|
preparedStatement.executeUpdate();
|
||||||
|
//update the case in the caches
|
||||||
|
resultSet = preparedStatement.getGeneratedKeys();
|
||||||
|
if (!resultSet.next()) {
|
||||||
|
throw new EamDbException(String.format("Failed to INSERT case %s in central repo", eamCase.getCaseUUID()));
|
||||||
|
}
|
||||||
|
int caseID = resultSet.getInt(1); //last_insert_rowid()
|
||||||
|
CorrelationCase correlationCase = new CorrelationCase(caseID, eamCase.getCaseUUID(), eamCase.getOrg(),
|
||||||
|
eamCase.getDisplayName(), eamCase.getCreationDate(), eamCase.getCaseNumber(), eamCase.getExaminerName(),
|
||||||
|
eamCase.getExaminerEmail(), eamCase.getExaminerPhone(), eamCase.getNotes());
|
||||||
|
caseCacheByUUID.put(eamCase.getCaseUUID(), correlationCase);
|
||||||
|
caseCacheById.put(caseID, correlationCase);
|
||||||
} catch (SQLException ex) {
|
} catch (SQLException ex) {
|
||||||
throw new EamDbException("Error inserting new case.", ex); // NON-NLS
|
throw new EamDbException("Error inserting new case.", ex); // NON-NLS
|
||||||
} finally {
|
} finally {
|
||||||
|
EamDbUtil.closeResultSet(resultSet);
|
||||||
EamDbUtil.closeStatement(preparedStatement);
|
EamDbUtil.closeStatement(preparedStatement);
|
||||||
EamDbUtil.closeConnection(conn);
|
EamDbUtil.closeConnection(conn);
|
||||||
}
|
}
|
||||||
@ -339,6 +381,9 @@ abstract class AbstractSqlEamDb implements EamDb {
|
|||||||
preparedStatement.setString(9, eamCase.getCaseUUID());
|
preparedStatement.setString(9, eamCase.getCaseUUID());
|
||||||
|
|
||||||
preparedStatement.executeUpdate();
|
preparedStatement.executeUpdate();
|
||||||
|
//update the case in the cache
|
||||||
|
caseCacheById.put(eamCase.getID(), eamCase);
|
||||||
|
caseCacheByUUID.put(eamCase.getCaseUUID(), eamCase);
|
||||||
} catch (SQLException ex) {
|
} catch (SQLException ex) {
|
||||||
throw new EamDbException("Error updating case.", ex); // NON-NLS
|
throw new EamDbException("Error updating case.", ex); // NON-NLS
|
||||||
} finally {
|
} finally {
|
||||||
@ -347,6 +392,25 @@ abstract class AbstractSqlEamDb implements EamDb {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves Case details based on Case UUID from the central repo
|
||||||
|
*
|
||||||
|
* @param caseUUID unique identifier for a case
|
||||||
|
*
|
||||||
|
* @return The retrieved case
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public CorrelationCase getCaseByUUID(String caseUUID) throws EamDbException {
|
||||||
|
try {
|
||||||
|
return caseCacheByUUID.get(caseUUID, () -> getCaseByUUIDFromCr(caseUUID));
|
||||||
|
} catch (CacheLoader.InvalidCacheLoadException ignored) {
|
||||||
|
//lambda valueloader returned a null value and cache can not store null values this is normal if the case does not exist in the central repo yet
|
||||||
|
return null;
|
||||||
|
} catch (ExecutionException ex) {
|
||||||
|
throw new EamDbException("Error getting autopsy case from Central repo", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves Case details based on Case UUID
|
* Retrieves Case details based on Case UUID
|
||||||
*
|
*
|
||||||
@ -354,10 +418,7 @@ abstract class AbstractSqlEamDb implements EamDb {
|
|||||||
*
|
*
|
||||||
* @return The retrieved case
|
* @return The retrieved case
|
||||||
*/
|
*/
|
||||||
@Override
|
private CorrelationCase getCaseByUUIDFromCr(String caseUUID) throws EamDbException {
|
||||||
public CorrelationCase getCaseByUUID(String caseUUID) throws EamDbException {
|
|
||||||
// @@@ We should have a cache here...
|
|
||||||
|
|
||||||
Connection conn = connect();
|
Connection conn = connect();
|
||||||
|
|
||||||
CorrelationCase eamCaseResult = null;
|
CorrelationCase eamCaseResult = null;
|
||||||
@ -377,6 +438,10 @@ abstract class AbstractSqlEamDb implements EamDb {
|
|||||||
if (resultSet.next()) {
|
if (resultSet.next()) {
|
||||||
eamCaseResult = getEamCaseFromResultSet(resultSet);
|
eamCaseResult = getEamCaseFromResultSet(resultSet);
|
||||||
}
|
}
|
||||||
|
if (eamCaseResult != null) {
|
||||||
|
//Update the version in the other cache
|
||||||
|
caseCacheById.put(eamCaseResult.getID(), eamCaseResult);
|
||||||
|
}
|
||||||
} catch (SQLException ex) {
|
} catch (SQLException ex) {
|
||||||
throw new EamDbException("Error getting case details.", ex); // NON-NLS
|
throw new EamDbException("Error getting case details.", ex); // NON-NLS
|
||||||
} finally {
|
} finally {
|
||||||
@ -397,8 +462,24 @@ abstract class AbstractSqlEamDb implements EamDb {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public CorrelationCase getCaseById(int caseId) throws EamDbException {
|
public CorrelationCase getCaseById(int caseId) throws EamDbException {
|
||||||
// @@@ We should have a cache here...
|
try {
|
||||||
|
return caseCacheById.get(caseId, () -> getCaseByIdFromCr(caseId));
|
||||||
|
} catch (CacheLoader.InvalidCacheLoadException ignored) {
|
||||||
|
//lambda valueloader returned a null value and cache can not store null values this is normal if the case does not exist in the central repo yet
|
||||||
|
return null;
|
||||||
|
} catch (ExecutionException ex) {
|
||||||
|
throw new EamDbException("Error getting autopsy case from Central repo", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves Case details based on Case ID
|
||||||
|
*
|
||||||
|
* @param caseID unique identifier for a case
|
||||||
|
*
|
||||||
|
* @return The retrieved case
|
||||||
|
*/
|
||||||
|
private CorrelationCase getCaseByIdFromCr(int caseId) throws EamDbException {
|
||||||
Connection conn = connect();
|
Connection conn = connect();
|
||||||
|
|
||||||
CorrelationCase eamCaseResult = null;
|
CorrelationCase eamCaseResult = null;
|
||||||
@ -410,7 +491,6 @@ abstract class AbstractSqlEamDb implements EamDb {
|
|||||||
+ "FROM cases "
|
+ "FROM cases "
|
||||||
+ "LEFT JOIN organizations ON cases.org_id=organizations.id "
|
+ "LEFT JOIN organizations ON cases.org_id=organizations.id "
|
||||||
+ "WHERE cases.id=?";
|
+ "WHERE cases.id=?";
|
||||||
|
|
||||||
try {
|
try {
|
||||||
preparedStatement = conn.prepareStatement(sql);
|
preparedStatement = conn.prepareStatement(sql);
|
||||||
preparedStatement.setInt(1, caseId);
|
preparedStatement.setInt(1, caseId);
|
||||||
@ -418,6 +498,10 @@ abstract class AbstractSqlEamDb implements EamDb {
|
|||||||
if (resultSet.next()) {
|
if (resultSet.next()) {
|
||||||
eamCaseResult = getEamCaseFromResultSet(resultSet);
|
eamCaseResult = getEamCaseFromResultSet(resultSet);
|
||||||
}
|
}
|
||||||
|
if (eamCaseResult != null) {
|
||||||
|
//Update the version in the other cache
|
||||||
|
caseCacheByUUID.put(eamCaseResult.getCaseUUID(), eamCaseResult);
|
||||||
|
}
|
||||||
} catch (SQLException ex) {
|
} catch (SQLException ex) {
|
||||||
throw new EamDbException("Error getting case details.", ex); // NON-NLS
|
throw new EamDbException("Error getting case details.", ex); // NON-NLS
|
||||||
} finally {
|
} finally {
|
||||||
@ -466,6 +550,32 @@ abstract class AbstractSqlEamDb implements EamDb {
|
|||||||
return cases;
|
return cases;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a key to the DataSourceCacheByDeviceId
|
||||||
|
*
|
||||||
|
* @param caseId - the id of the CorrelationCase in the Central
|
||||||
|
* Repository
|
||||||
|
* @param dataSourceDeviceId - the device Id of the data source
|
||||||
|
*
|
||||||
|
* @return a String to be used as a key for the dataSourceCacheByDeviceId
|
||||||
|
*/
|
||||||
|
private static String getDataSourceByDeviceIdCacheKey(int caseId, String dataSourceDeviceId) {
|
||||||
|
return "Case" + caseId + "DeviceId" + dataSourceDeviceId; //NON-NLS
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a key to the DataSourceCacheById
|
||||||
|
*
|
||||||
|
* @param caseId - the id of the CorrelationCase in the Central
|
||||||
|
* Repository
|
||||||
|
* @param dataSourceId - the id of the datasource in the central repository
|
||||||
|
*
|
||||||
|
* @return a String to be used as a key for the dataSourceCacheById
|
||||||
|
*/
|
||||||
|
private static String getDataSourceByIdCacheKey(int caseId, int dataSourceId) {
|
||||||
|
return "Case" + caseId + "Id" + dataSourceId; //NON-NLS
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates new Data Source in the database
|
* Creates new Data Source in the database
|
||||||
*
|
*
|
||||||
@ -485,18 +595,27 @@ abstract class AbstractSqlEamDb implements EamDb {
|
|||||||
|
|
||||||
String sql = "INSERT INTO data_sources(device_id, case_id, name) VALUES (?, ?, ?) "
|
String sql = "INSERT INTO data_sources(device_id, case_id, name) VALUES (?, ?, ?) "
|
||||||
+ getConflictClause();
|
+ getConflictClause();
|
||||||
|
ResultSet resultSet = null;
|
||||||
try {
|
try {
|
||||||
preparedStatement = conn.prepareStatement(sql);
|
preparedStatement = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
|
||||||
|
|
||||||
preparedStatement.setString(1, eamDataSource.getDeviceID());
|
preparedStatement.setString(1, eamDataSource.getDeviceID());
|
||||||
preparedStatement.setInt(2, eamDataSource.getCaseID());
|
preparedStatement.setInt(2, eamDataSource.getCaseID());
|
||||||
preparedStatement.setString(3, eamDataSource.getName());
|
preparedStatement.setString(3, eamDataSource.getName());
|
||||||
|
|
||||||
preparedStatement.executeUpdate();
|
preparedStatement.executeUpdate();
|
||||||
|
resultSet = preparedStatement.getGeneratedKeys();
|
||||||
|
if (!resultSet.next()) {
|
||||||
|
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);
|
||||||
|
dataSourceCacheById.put(getDataSourceByIdCacheKey(dataSource.getCaseID(), dataSource.getID()), dataSource);
|
||||||
} catch (SQLException ex) {
|
} catch (SQLException ex) {
|
||||||
throw new EamDbException("Error inserting new data source.", ex); // NON-NLS
|
throw new EamDbException("Error inserting new data source.", ex); // NON-NLS
|
||||||
} finally {
|
} finally {
|
||||||
|
EamDbUtil.closeResultSet(resultSet);
|
||||||
EamDbUtil.closeStatement(preparedStatement);
|
EamDbUtil.closeStatement(preparedStatement);
|
||||||
EamDbUtil.closeConnection(conn);
|
EamDbUtil.closeConnection(conn);
|
||||||
}
|
}
|
||||||
@ -510,13 +629,38 @@ abstract class AbstractSqlEamDb implements EamDb {
|
|||||||
* @param dataSourceDeviceId the data source device ID number
|
* @param dataSourceDeviceId the data source device ID number
|
||||||
*
|
*
|
||||||
* @return The data source
|
* @return The data source
|
||||||
|
*
|
||||||
|
* @throws EamDbException
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public CorrelationDataSource getDataSource(CorrelationCase correlationCase, String dataSourceDeviceId) throws EamDbException {
|
public CorrelationDataSource getDataSource(CorrelationCase correlationCase, String dataSourceDeviceId) throws EamDbException {
|
||||||
|
|
||||||
if (correlationCase == null) {
|
if (correlationCase == null) {
|
||||||
throw new EamDbException("Correlation case is null");
|
throw new EamDbException("Correlation case is null");
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
return dataSourceCacheByDeviceId.get(getDataSourceByDeviceIdCacheKey(correlationCase.getID(), dataSourceDeviceId), () -> getDataSourceFromCr(correlationCase, dataSourceDeviceId));
|
||||||
|
} 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;
|
||||||
|
} catch (ExecutionException ex) {
|
||||||
|
throw new EamDbException("Error getting data source from central repository", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the Data Source details based on data source device ID from the
|
||||||
|
* central repository.
|
||||||
|
*
|
||||||
|
* @param correlationCase the current CorrelationCase used for ensuring
|
||||||
|
* uniqueness of DataSource
|
||||||
|
* @param dataSourceDeviceId the data source device ID number
|
||||||
|
*
|
||||||
|
* @return The data source
|
||||||
|
*
|
||||||
|
* @throws EamDbException
|
||||||
|
*/
|
||||||
|
private CorrelationDataSource getDataSourceFromCr(CorrelationCase correlationCase, String dataSourceDeviceId) throws EamDbException {
|
||||||
Connection conn = connect();
|
Connection conn = connect();
|
||||||
|
|
||||||
CorrelationDataSource eamDataSourceResult = null;
|
CorrelationDataSource eamDataSourceResult = null;
|
||||||
@ -533,6 +677,9 @@ abstract class AbstractSqlEamDb implements EamDb {
|
|||||||
if (resultSet.next()) {
|
if (resultSet.next()) {
|
||||||
eamDataSourceResult = getEamDataSourceFromResultSet(resultSet);
|
eamDataSourceResult = getEamDataSourceFromResultSet(resultSet);
|
||||||
}
|
}
|
||||||
|
if (eamDataSourceResult != null) {
|
||||||
|
dataSourceCacheById.put(getDataSourceByIdCacheKey(correlationCase.getID(), eamDataSourceResult.getID()), eamDataSourceResult);
|
||||||
|
}
|
||||||
} catch (SQLException ex) {
|
} catch (SQLException ex) {
|
||||||
throw new EamDbException("Error getting data source.", ex); // NON-NLS
|
throw new EamDbException("Error getting data source.", ex); // NON-NLS
|
||||||
} finally {
|
} finally {
|
||||||
@ -558,7 +705,26 @@ abstract class AbstractSqlEamDb implements EamDb {
|
|||||||
if (correlationCase == null) {
|
if (correlationCase == null) {
|
||||||
throw new EamDbException("Correlation case is null");
|
throw new EamDbException("Correlation case is null");
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
return dataSourceCacheById.get(getDataSourceByIdCacheKey(correlationCase.getID(), dataSourceId), () -> getDataSourceByIdFromCr(correlationCase, dataSourceId));
|
||||||
|
} 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;
|
||||||
|
} catch (ExecutionException ex) {
|
||||||
|
throw new EamDbException("Error getting data source from central repository", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves Data Source details based on data source ID
|
||||||
|
*
|
||||||
|
* @param correlationCase the current CorrelationCase used for ensuring
|
||||||
|
* uniqueness of DataSource
|
||||||
|
* @param dataSourceId the data source ID number
|
||||||
|
*
|
||||||
|
* @return The data source
|
||||||
|
*/
|
||||||
|
private CorrelationDataSource getDataSourceByIdFromCr(CorrelationCase correlationCase, int dataSourceId) throws EamDbException {
|
||||||
Connection conn = connect();
|
Connection conn = connect();
|
||||||
|
|
||||||
CorrelationDataSource eamDataSourceResult = null;
|
CorrelationDataSource eamDataSourceResult = null;
|
||||||
@ -575,6 +741,9 @@ abstract class AbstractSqlEamDb implements EamDb {
|
|||||||
if (resultSet.next()) {
|
if (resultSet.next()) {
|
||||||
eamDataSourceResult = getEamDataSourceFromResultSet(resultSet);
|
eamDataSourceResult = getEamDataSourceFromResultSet(resultSet);
|
||||||
}
|
}
|
||||||
|
if (eamDataSourceResult != null) {
|
||||||
|
dataSourceCacheByDeviceId.put(getDataSourceByDeviceIdCacheKey(correlationCase.getID(), eamDataSourceResult.getDeviceID()), eamDataSourceResult);
|
||||||
|
}
|
||||||
} catch (SQLException ex) {
|
} catch (SQLException ex) {
|
||||||
throw new EamDbException("Error getting data source.", ex); // NON-NLS
|
throw new EamDbException("Error getting data source.", ex); // NON-NLS
|
||||||
} finally {
|
} finally {
|
||||||
@ -1895,6 +2064,7 @@ abstract class AbstractSqlEamDb implements EamDb {
|
|||||||
* @param type EamArtifact.Type to search for
|
* @param type EamArtifact.Type to search for
|
||||||
* @param instanceTableCallback callback to process the instance
|
* @param instanceTableCallback callback to process the instance
|
||||||
* @param whereClause query string to execute
|
* @param whereClause query string to execute
|
||||||
|
*
|
||||||
* @throws EamDbException
|
* @throws EamDbException
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@ -2279,7 +2449,8 @@ abstract class AbstractSqlEamDb implements EamDb {
|
|||||||
* Add a new reference instance
|
* Add a new reference instance
|
||||||
*
|
*
|
||||||
* @param eamGlobalFileInstance The reference instance to add
|
* @param eamGlobalFileInstance The reference instance to add
|
||||||
* @param correlationType Correlation Type that this Reference Instance is
|
* @param correlationType Correlation Type that this Reference
|
||||||
|
* Instance is
|
||||||
*
|
*
|
||||||
* @throws EamDbException
|
* @throws EamDbException
|
||||||
*/
|
*/
|
||||||
@ -2694,7 +2865,7 @@ abstract class AbstractSqlEamDb implements EamDb {
|
|||||||
preparedStatement.setInt(4, aType.isEnabled() ? 1 : 0);
|
preparedStatement.setInt(4, aType.isEnabled() ? 1 : 0);
|
||||||
preparedStatement.setInt(5, aType.getId());
|
preparedStatement.setInt(5, aType.getId());
|
||||||
preparedStatement.executeUpdate();
|
preparedStatement.executeUpdate();
|
||||||
|
typeCache.put(aType.getId(), aType);
|
||||||
} catch (SQLException ex) {
|
} catch (SQLException ex) {
|
||||||
throw new EamDbException("Error updating correlation type.", ex); // NON-NLS
|
throw new EamDbException("Error updating correlation type.", ex); // NON-NLS
|
||||||
} finally {
|
} finally {
|
||||||
@ -2715,6 +2886,26 @@ abstract class AbstractSqlEamDb implements EamDb {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public CorrelationAttributeInstance.Type getCorrelationTypeById(int typeId) throws EamDbException {
|
public CorrelationAttributeInstance.Type getCorrelationTypeById(int typeId) throws EamDbException {
|
||||||
|
try {
|
||||||
|
return typeCache.get(CorrelationAttributeInstance.FILES_TYPE_ID, () -> getCorrelationTypeByIdFromCr(typeId));
|
||||||
|
} catch (CacheLoader.InvalidCacheLoadException ignored) {
|
||||||
|
//lambda valueloader returned a null value and cache can not store null values this is normal if the correlation type does not exist in the central repo yet
|
||||||
|
return null;
|
||||||
|
} catch (ExecutionException ex) {
|
||||||
|
throw new EamDbException("Error getting correlation type", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the EamArtifact.Type that has the given Type.Id from the central repo
|
||||||
|
*
|
||||||
|
* @param typeId Type.Id of Correlation Type to get
|
||||||
|
*
|
||||||
|
* @return EamArtifact.Type or null if it doesn't exist.
|
||||||
|
*
|
||||||
|
* @throws EamDbException
|
||||||
|
*/
|
||||||
|
private CorrelationAttributeInstance.Type getCorrelationTypeByIdFromCr(int typeId) throws EamDbException {
|
||||||
Connection conn = connect();
|
Connection conn = connect();
|
||||||
|
|
||||||
CorrelationAttributeInstance.Type aType;
|
CorrelationAttributeInstance.Type aType;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Central Repository
|
* Central Repository
|
||||||
*
|
*
|
||||||
* Copyright 2015-2017 Basis Technology Corp.
|
* Copyright 2015-2018 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -78,6 +78,7 @@ final class PostgresEamDb extends AbstractSqlEamDb {
|
|||||||
connectionPool.close();
|
connectionPool.close();
|
||||||
connectionPool = null; // force it to be re-created on next connect()
|
connectionPool = null; // force it to be re-created on next connect()
|
||||||
}
|
}
|
||||||
|
clearCaches();
|
||||||
}
|
}
|
||||||
} catch (SQLException ex) {
|
} catch (SQLException ex) {
|
||||||
throw new EamDbException("Failed to close existing database connections.", ex); // NON-NLS
|
throw new EamDbException("Failed to close existing database connections.", ex); // NON-NLS
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Central Repository
|
* Central Repository
|
||||||
*
|
*
|
||||||
* Copyright 2015-2017 Basis Technology Corp.
|
* Copyright 2015-2018 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -85,6 +85,7 @@ final class SqliteEamDb extends AbstractSqlEamDb {
|
|||||||
connectionPool.close();
|
connectionPool.close();
|
||||||
connectionPool = null; // force it to be re-created on next connect()
|
connectionPool = null; // force it to be re-created on next connect()
|
||||||
}
|
}
|
||||||
|
clearCaches();
|
||||||
}
|
}
|
||||||
} catch (SQLException ex) {
|
} catch (SQLException ex) {
|
||||||
throw new EamDbException("Failed to close existing database connections.", ex); // NON-NLS
|
throw new EamDbException("Failed to close existing database connections.", ex); // NON-NLS
|
||||||
|
@ -39,22 +39,16 @@ import org.sleuthkit.datamodel.TskCoreException;
|
|||||||
*/
|
*/
|
||||||
public abstract class AbstractCommonAttributeSearcher {
|
public abstract class AbstractCommonAttributeSearcher {
|
||||||
|
|
||||||
private final Map<Long, String> dataSourceIdToNameMap;
|
|
||||||
private boolean filterByMedia;
|
private boolean filterByMedia;
|
||||||
private boolean filterByDoc;
|
private boolean filterByDoc;
|
||||||
final int frequencyPercentageThreshold;
|
final int frequencyPercentageThreshold;
|
||||||
|
|
||||||
AbstractCommonAttributeSearcher(Map<Long, String> dataSourceIdMap, boolean filterByMedia, boolean filterByDoc, int percentageThreshold) {
|
AbstractCommonAttributeSearcher(boolean filterByMedia, boolean filterByDoc, int percentageThreshold) {
|
||||||
this.filterByDoc = filterByDoc;
|
this.filterByDoc = filterByDoc;
|
||||||
this.filterByMedia = filterByMedia;
|
this.filterByMedia = filterByMedia;
|
||||||
this.dataSourceIdToNameMap = dataSourceIdMap;
|
|
||||||
this.frequencyPercentageThreshold = percentageThreshold;
|
this.frequencyPercentageThreshold = percentageThreshold;
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<Long, String> getDataSourceIdToNameMap() {
|
|
||||||
return Collections.unmodifiableMap(this.dataSourceIdToNameMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implement this to search for files with common attributes. Creates an
|
* Implement this to search for files with common attributes. Creates an
|
||||||
* object (CommonAttributeSearchResults) which contains all of the
|
* object (CommonAttributeSearchResults) which contains all of the
|
||||||
|
@ -43,13 +43,13 @@ public class AllInterCaseCommonAttributeSearcher extends InterCaseCommonAttribut
|
|||||||
*
|
*
|
||||||
* @throws EamDbException
|
* @throws EamDbException
|
||||||
*/
|
*/
|
||||||
public AllInterCaseCommonAttributeSearcher(Map<Long, String> dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType, Type corAttrType, int percentageThreshold) throws EamDbException {
|
public AllInterCaseCommonAttributeSearcher(boolean filterByMediaMimeType, boolean filterByDocMimeType, Type corAttrType, int percentageThreshold) throws EamDbException {
|
||||||
super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType, corAttrType, percentageThreshold);
|
super(filterByMediaMimeType, filterByDocMimeType, corAttrType, percentageThreshold);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CommonAttributeSearchResults findMatches() throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException {
|
public CommonAttributeSearchResults findMatches() throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException {
|
||||||
InterCaseSearchResultsProcessor eamDbAttrInst = new InterCaseSearchResultsProcessor(this.getDataSourceIdToNameMap(), corAttrType);
|
InterCaseSearchResultsProcessor eamDbAttrInst = new InterCaseSearchResultsProcessor(corAttrType);
|
||||||
Map<Integer, CommonAttributeValueList> interCaseCommonFiles = eamDbAttrInst.findInterCaseCommonAttributeValues(Case.getCurrentCase());
|
Map<Integer, CommonAttributeValueList> interCaseCommonFiles = eamDbAttrInst.findInterCaseCommonAttributeValues(Case.getCurrentCase());
|
||||||
return new CommonAttributeSearchResults(interCaseCommonFiles, this.frequencyPercentageThreshold, this.corAttrType);
|
return new CommonAttributeSearchResults(interCaseCommonFiles, this.frequencyPercentageThreshold, this.corAttrType);
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||||
@ -31,6 +32,7 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeIns
|
|||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.autopsy.datamodel.DisplayableItemNode;
|
import org.sleuthkit.autopsy.datamodel.DisplayableItemNode;
|
||||||
import org.sleuthkit.datamodel.AbstractFile;
|
import org.sleuthkit.datamodel.AbstractFile;
|
||||||
|
import org.sleuthkit.datamodel.DataSource;
|
||||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
|
||||||
@ -45,12 +47,10 @@ final public class CentralRepoCommonAttributeInstance extends AbstractCommonAttr
|
|||||||
private final Integer crFileId;
|
private final Integer crFileId;
|
||||||
private CorrelationAttributeInstance currentAttribute;
|
private CorrelationAttributeInstance currentAttribute;
|
||||||
private final CorrelationAttributeInstance.Type correlationType;
|
private final CorrelationAttributeInstance.Type correlationType;
|
||||||
private final Map<String, Long> dataSourceNameToIdMap;
|
|
||||||
|
|
||||||
CentralRepoCommonAttributeInstance(Integer attrInstId, Map<Long, String> dataSourceIdToNameMap, CorrelationAttributeInstance.Type correlationType) {
|
CentralRepoCommonAttributeInstance(Integer attrInstId, CorrelationAttributeInstance.Type correlationType) {
|
||||||
super();
|
super();
|
||||||
this.crFileId = attrInstId;
|
this.crFileId = attrInstId;
|
||||||
this.dataSourceNameToIdMap = invertMap(dataSourceIdToNameMap);
|
|
||||||
this.correlationType = correlationType;
|
this.correlationType = correlationType;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,22 +71,35 @@ final public class CentralRepoCommonAttributeInstance extends AbstractCommonAttr
|
|||||||
|
|
||||||
final CorrelationAttributeInstance currentAttributeInstance = this.currentAttribute;
|
final CorrelationAttributeInstance currentAttributeInstance = this.currentAttribute;
|
||||||
|
|
||||||
String currentFullPath = currentAttributeInstance.getFilePath();
|
|
||||||
String currentDataSource = currentAttributeInstance.getCorrelationDataSource().getName();
|
|
||||||
|
|
||||||
if (this.dataSourceNameToIdMap.containsKey(currentDataSource)) {
|
|
||||||
Long dataSourceObjectId = this.dataSourceNameToIdMap.get(currentDataSource);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
String currentFullPath = currentAttributeInstance.getFilePath();
|
||||||
currentCase = Case.getCurrentCaseThrows();
|
currentCase = Case.getCurrentCaseThrows();
|
||||||
|
|
||||||
|
// Only attempt to make the abstract file if the attribute is from the current case
|
||||||
|
if (currentCase.getName().equals(currentAttributeInstance.getCorrelationCase().getCaseUUID())) {
|
||||||
|
|
||||||
SleuthkitCase tskDb = currentCase.getSleuthkitCase();
|
SleuthkitCase tskDb = currentCase.getSleuthkitCase();
|
||||||
|
|
||||||
|
// Find the correct data source
|
||||||
|
Optional<DataSource> dataSource = tskDb.getDataSources().stream()
|
||||||
|
.filter(p -> p.getDeviceId().equals(currentAttribute.getCorrelationDataSource().getDeviceID()))
|
||||||
|
.findFirst();
|
||||||
|
if (! dataSource.isPresent()) {
|
||||||
|
LOGGER.log(Level.WARNING, String.format("Unable to find data source with device ID %s in the current case", currentAttribute.getCorrelationDataSource().getDeviceID()));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
File fileFromPath = new File(currentFullPath);
|
File fileFromPath = new File(currentFullPath);
|
||||||
String fileName = fileFromPath.getName();
|
String fileName = fileFromPath.getName();
|
||||||
String parentPath = (fileFromPath.getParent() + File.separator).replace("\\", "/");
|
|
||||||
|
|
||||||
final String whereClause = String.format("lower(name) = '%s' AND md5 = '%s' AND lower(parent_path) = '%s' AND data_source_obj_id = %s", fileName, currentAttribute.getCorrelationValue(), parentPath, dataSourceObjectId);
|
// Create the parent path. Make sure not to add a separator if there is already one there.
|
||||||
|
String parentPath = fileFromPath.getParent();
|
||||||
|
if (! parentPath.endsWith(File.separator)) {
|
||||||
|
parentPath = parentPath + File.separator;
|
||||||
|
}
|
||||||
|
parentPath = parentPath.replace("\\", "/");
|
||||||
|
|
||||||
|
final String whereClause = String.format("lower(name) = '%s' AND md5 = '%s' AND lower(parent_path) = '%s' AND data_source_obj_id = %s", fileName, currentAttribute.getCorrelationValue(), parentPath, dataSource.get().getId());
|
||||||
List<AbstractFile> potentialAbstractFiles = tskDb.findAllFilesWhere(whereClause);
|
List<AbstractFile> potentialAbstractFiles = tskDb.findAllFilesWhere(whereClause);
|
||||||
|
|
||||||
if (potentialAbstractFiles.isEmpty()) {
|
if (potentialAbstractFiles.isEmpty()) {
|
||||||
@ -97,14 +110,14 @@ final public class CentralRepoCommonAttributeInstance extends AbstractCommonAttr
|
|||||||
} else {
|
} else {
|
||||||
return potentialAbstractFiles.get(0);
|
return potentialAbstractFiles.get(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (TskCoreException | NoCurrentCaseException ex) {
|
|
||||||
LOGGER.log(Level.SEVERE, String.format("Unable to find AbstractFile for record with filePath: %s. Node not created.", new Object[]{currentFullPath}), ex);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
} catch (TskCoreException | NoCurrentCaseException ex) {
|
||||||
|
LOGGER.log(Level.SEVERE, String.format("Unable to find AbstractFile for record with filePath: %s. Node not created.", new Object[]{currentAttributeInstance.getFilePath()}), ex);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -131,12 +144,4 @@ final public class CentralRepoCommonAttributeInstance extends AbstractCommonAttr
|
|||||||
|
|
||||||
return attrInstNodeList.toArray(new DisplayableItemNode[attrInstNodeList.size()]);
|
return attrInstNodeList.toArray(new DisplayableItemNode[attrInstNodeList.size()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, Long> invertMap(Map<Long, String> dataSourceIdToNameMap) {
|
|
||||||
HashMap<String, Long> invertedMap = new HashMap<>();
|
|
||||||
for (Map.Entry<Long, String> entry : dataSourceIdToNameMap.entrySet()) {
|
|
||||||
invertedMap.put(entry.getValue(), entry.getKey());
|
|
||||||
}
|
|
||||||
return invertedMap;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -231,10 +231,10 @@ final class CommonAttributePanel extends javax.swing.JDialog implements Observer
|
|||||||
corType = CorrelationAttributeInstance.getDefaultCorrelationTypes().get(0);
|
corType = CorrelationAttributeInstance.getDefaultCorrelationTypes().get(0);
|
||||||
}
|
}
|
||||||
if (caseId == InterCasePanel.NO_CASE_SELECTED) {
|
if (caseId == InterCasePanel.NO_CASE_SELECTED) {
|
||||||
builder = new AllInterCaseCommonAttributeSearcher(intraCasePanel.getDataSourceMap(), filterByMedia, filterByDocuments, corType, percentageThreshold);
|
builder = new AllInterCaseCommonAttributeSearcher(filterByMedia, filterByDocuments, corType, percentageThreshold);
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
builder = new SingleInterCaseCommonAttributeSearcher(caseId, intraCasePanel.getDataSourceMap(), filterByMedia, filterByDocuments, corType, percentageThreshold);
|
builder = new SingleInterCaseCommonAttributeSearcher(caseId, filterByMedia, filterByDocuments, corType, percentageThreshold);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -48,8 +48,8 @@ abstract class InterCaseCommonAttributeSearcher extends AbstractCommonAttributeS
|
|||||||
*
|
*
|
||||||
* @throws EamDbException
|
* @throws EamDbException
|
||||||
*/
|
*/
|
||||||
InterCaseCommonAttributeSearcher(Map<Long, String> dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType, Type corAttrType, int percentageThreshold) throws EamDbException {
|
InterCaseCommonAttributeSearcher(boolean filterByMediaMimeType, boolean filterByDocMimeType, Type corAttrType, int percentageThreshold) throws EamDbException {
|
||||||
super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType, percentageThreshold);
|
super(filterByMediaMimeType, filterByDocMimeType, percentageThreshold);
|
||||||
dbManager = EamDb.getInstance();
|
dbManager = EamDb.getInstance();
|
||||||
this.corAttrType = corAttrType;
|
this.corAttrType = corAttrType;
|
||||||
}
|
}
|
||||||
|
@ -44,8 +44,6 @@ import org.sleuthkit.datamodel.HashUtility;
|
|||||||
*/
|
*/
|
||||||
final class InterCaseSearchResultsProcessor {
|
final class InterCaseSearchResultsProcessor {
|
||||||
|
|
||||||
private Map<Long, String> dataSources;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The CorrelationAttributeInstance.Type this Processor will query on
|
* The CorrelationAttributeInstance.Type this Processor will query on
|
||||||
*/
|
*/
|
||||||
@ -70,9 +68,8 @@ final class InterCaseSearchResultsProcessor {
|
|||||||
* @param dataSources the cases to filter and correlate on
|
* @param dataSources the cases to filter and correlate on
|
||||||
* @param theType the type of CR data to search
|
* @param theType the type of CR data to search
|
||||||
*/
|
*/
|
||||||
InterCaseSearchResultsProcessor(Map<Long, String> dataSources, CorrelationAttributeInstance.Type theType) {
|
InterCaseSearchResultsProcessor(CorrelationAttributeInstance.Type theType) {
|
||||||
this.correlationType = theType;
|
this.correlationType = theType;
|
||||||
this.dataSources = dataSources;
|
|
||||||
interCaseWhereClause = getInterCaseWhereClause();
|
interCaseWhereClause = getInterCaseWhereClause();
|
||||||
singleInterCaseWhereClause = getSingleInterCaseWhereClause();
|
singleInterCaseWhereClause = getSingleInterCaseWhereClause();
|
||||||
}
|
}
|
||||||
@ -101,18 +98,6 @@ final class InterCaseSearchResultsProcessor {
|
|||||||
return sqlString.toString();
|
return sqlString.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Used in the CentralRepoCommonAttributeInstance to find common attribute
|
|
||||||
* instances and generate nodes at the UI level.
|
|
||||||
*
|
|
||||||
* @param theType the type of CR data to search
|
|
||||||
*/
|
|
||||||
InterCaseSearchResultsProcessor(CorrelationAttributeInstance.Type theType) {
|
|
||||||
this.correlationType = theType;
|
|
||||||
interCaseWhereClause = getInterCaseWhereClause();
|
|
||||||
singleInterCaseWhereClause = getSingleInterCaseWhereClause();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds a single CorrelationAttribute given an id.
|
* Finds a single CorrelationAttribute given an id.
|
||||||
*
|
*
|
||||||
@ -257,7 +242,7 @@ final class InterCaseSearchResultsProcessor {
|
|||||||
// we don't *have* all the information for the rows in the CR,
|
// we don't *have* all the information for the rows in the CR,
|
||||||
// so we need to consult the present case via the SleuthkitCase object
|
// so we need to consult the present case via the SleuthkitCase object
|
||||||
// Later, when the FileInstanceNode is built. Therefore, build node generators for now.
|
// Later, when the FileInstanceNode is built. Therefore, build node generators for now.
|
||||||
AbstractCommonAttributeInstance searchResult = new CentralRepoCommonAttributeInstance(resultId, InterCaseSearchResultsProcessor.this.dataSources, correlationType);
|
AbstractCommonAttributeInstance searchResult = new CentralRepoCommonAttributeInstance(resultId, correlationType);
|
||||||
commonAttributeValue.addInstance(searchResult);
|
commonAttributeValue.addInstance(searchResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.commonfilesearch;
|
|||||||
|
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -46,6 +47,8 @@ public abstract class IntraCaseCommonAttributeSearcher extends AbstractCommonAtt
|
|||||||
|
|
||||||
private static final String FILTER_BY_MIME_TYPES_WHERE_CLAUSE = " and mime_type in (%s)"; //NON-NLS // where %s is csv list of mime_types to filter on
|
private static final String FILTER_BY_MIME_TYPES_WHERE_CLAUSE = " and mime_type in (%s)"; //NON-NLS // where %s is csv list of mime_types to filter on
|
||||||
|
|
||||||
|
private final Map<Long, String> dataSourceIdToNameMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subclass this to implement different algorithms for getting common files.
|
* Subclass this to implement different algorithms for getting common files.
|
||||||
*
|
*
|
||||||
@ -56,7 +59,13 @@ public abstract class IntraCaseCommonAttributeSearcher extends AbstractCommonAtt
|
|||||||
* broadly categorized as document types
|
* broadly categorized as document types
|
||||||
*/
|
*/
|
||||||
IntraCaseCommonAttributeSearcher(Map<Long, String> dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType, int percentageThreshold) {
|
IntraCaseCommonAttributeSearcher(Map<Long, String> dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType, int percentageThreshold) {
|
||||||
super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType, percentageThreshold);
|
super(filterByMediaMimeType, filterByDocMimeType, percentageThreshold);
|
||||||
|
this.dataSourceIdToNameMap = dataSourceIdMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Map<Long, String> getDataSourceIdToNameMap() {
|
||||||
|
return Collections.unmodifiableMap(this.dataSourceIdToNameMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -46,9 +46,9 @@ public class SingleInterCaseCommonAttributeSearcher extends InterCaseCommonAttri
|
|||||||
*
|
*
|
||||||
* @throws EamDbException
|
* @throws EamDbException
|
||||||
*/
|
*/
|
||||||
public SingleInterCaseCommonAttributeSearcher(int correlationCaseId, Map<Long, String> dataSourceIdMap, boolean filterByMediaMimeType,
|
public SingleInterCaseCommonAttributeSearcher(int correlationCaseId, boolean filterByMediaMimeType,
|
||||||
boolean filterByDocMimeType, Type corAttrType, int percentageThreshold) throws EamDbException {
|
boolean filterByDocMimeType, Type corAttrType, int percentageThreshold) throws EamDbException {
|
||||||
super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType, corAttrType, percentageThreshold);
|
super(filterByMediaMimeType, filterByDocMimeType, corAttrType, percentageThreshold);
|
||||||
|
|
||||||
this.corrleationCaseId = correlationCaseId;
|
this.corrleationCaseId = correlationCaseId;
|
||||||
this.correlationCaseName = "";
|
this.correlationCaseName = "";
|
||||||
@ -77,7 +77,7 @@ public class SingleInterCaseCommonAttributeSearcher extends InterCaseCommonAttri
|
|||||||
}
|
}
|
||||||
|
|
||||||
CommonAttributeSearchResults findFiles(CorrelationCase correlationCase) throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException {
|
CommonAttributeSearchResults findFiles(CorrelationCase correlationCase) throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException {
|
||||||
InterCaseSearchResultsProcessor eamDbAttrInst = new InterCaseSearchResultsProcessor(this.getDataSourceIdToNameMap(), this.corAttrType);
|
InterCaseSearchResultsProcessor eamDbAttrInst = new InterCaseSearchResultsProcessor(this.corAttrType);
|
||||||
Map<Integer, CommonAttributeValueList> interCaseCommonFiles = eamDbAttrInst.findSingleInterCaseCommonAttributeValues(Case.getCurrentCase(), correlationCase);
|
Map<Integer, CommonAttributeValueList> interCaseCommonFiles = eamDbAttrInst.findSingleInterCaseCommonAttributeValues(Case.getCurrentCase(), correlationCase);
|
||||||
|
|
||||||
return new CommonAttributeSearchResults(interCaseCommonFiles, this.frequencyPercentageThreshold, this.corAttrType);
|
return new CommonAttributeSearchResults(interCaseCommonFiles, this.frequencyPercentageThreshold, this.corAttrType);
|
||||||
|
@ -854,6 +854,8 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
|||||||
"DataResultViewerTable.commentRenderer.noComment.toolTip=No comments found"})
|
"DataResultViewerTable.commentRenderer.noComment.toolTip=No comments found"})
|
||||||
@Override
|
@Override
|
||||||
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
|
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
|
||||||
|
Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
|
||||||
|
setBackground(component.getBackground()); //inherit highlighting for selection
|
||||||
setHorizontalAlignment(CENTER);
|
setHorizontalAlignment(CENTER);
|
||||||
Object switchValue = null;
|
Object switchValue = null;
|
||||||
if ((value instanceof NodeProperty)) {
|
if ((value instanceof NodeProperty)) {
|
||||||
@ -908,6 +910,8 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
|
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
|
||||||
|
Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
|
||||||
|
setBackground(component.getBackground()); //inherit highlighting for selection
|
||||||
setHorizontalAlignment(CENTER);
|
setHorizontalAlignment(CENTER);
|
||||||
Object switchValue = null;
|
Object switchValue = null;
|
||||||
if ((value instanceof NodeProperty)) {
|
if ((value instanceof NodeProperty)) {
|
||||||
@ -955,6 +959,8 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
|
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
|
||||||
|
Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
|
||||||
|
setBackground(component.getBackground()); //inherit highlighting for selection
|
||||||
setHorizontalAlignment(LEFT);
|
setHorizontalAlignment(LEFT);
|
||||||
Object countValue = null;
|
Object countValue = null;
|
||||||
if ((value instanceof NodeProperty)) {
|
if ((value instanceof NodeProperty)) {
|
||||||
|
@ -21,7 +21,6 @@ package org.sleuthkit.autopsy.commonfilessearch;
|
|||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.Map;
|
|
||||||
import junit.framework.Assert;
|
import junit.framework.Assert;
|
||||||
import junit.framework.Test;
|
import junit.framework.Test;
|
||||||
import org.netbeans.junit.NbModuleSuite;
|
import org.netbeans.junit.NbModuleSuite;
|
||||||
@ -111,9 +110,8 @@ public class CommonAttributeSearchInterCaseTests extends NbTestCase {
|
|||||||
private void assertResultsAreOfType(CorrelationAttributeInstance.Type type) {
|
private void assertResultsAreOfType(CorrelationAttributeInstance.Type type) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Map<Long, String> dataSources = this.utils.getDataSourceMap();
|
|
||||||
|
|
||||||
AbstractCommonAttributeSearcher builder = new AllInterCaseCommonAttributeSearcher(dataSources, false, false, type, 0);
|
AbstractCommonAttributeSearcher builder = new AllInterCaseCommonAttributeSearcher(false, false, type, 0);
|
||||||
|
|
||||||
CommonAttributeSearchResults metadata = builder.findMatches();
|
CommonAttributeSearchResults metadata = builder.findMatches();
|
||||||
|
|
||||||
@ -146,22 +144,21 @@ public class CommonAttributeSearchInterCaseTests extends NbTestCase {
|
|||||||
*/
|
*/
|
||||||
public void testTwo() {
|
public void testTwo() {
|
||||||
try {
|
try {
|
||||||
Map<Long, String> dataSources = this.utils.getDataSourceMap();
|
|
||||||
|
|
||||||
AbstractCommonAttributeSearcher builder;
|
AbstractCommonAttributeSearcher builder;
|
||||||
CommonAttributeSearchResults metadata;
|
CommonAttributeSearchResults metadata;
|
||||||
|
|
||||||
builder = new AllInterCaseCommonAttributeSearcher(dataSources, false, false, this.utils.USB_ID_TYPE, 100);
|
builder = new AllInterCaseCommonAttributeSearcher(false, false, this.utils.USB_ID_TYPE, 100);
|
||||||
metadata = builder.findMatches();
|
metadata = builder.findMatches();
|
||||||
metadata.size();
|
metadata.size();
|
||||||
//assertTrue("This should yield 13 results.", verifyInstanceCount(metadata, 13));
|
//assertTrue("This should yield 13 results.", verifyInstanceCount(metadata, 13));
|
||||||
|
|
||||||
builder = new AllInterCaseCommonAttributeSearcher(dataSources, false, false, this.utils.USB_ID_TYPE, 20);
|
builder = new AllInterCaseCommonAttributeSearcher(false, false, this.utils.USB_ID_TYPE, 20);
|
||||||
metadata = builder.findMatches();
|
metadata = builder.findMatches();
|
||||||
metadata.size();
|
metadata.size();
|
||||||
//assertTrue("This should yield no results.", verifyInstanceCount(metadata, 0));
|
//assertTrue("This should yield no results.", verifyInstanceCount(metadata, 0));
|
||||||
|
|
||||||
builder = new AllInterCaseCommonAttributeSearcher(dataSources, false, false, this.utils.USB_ID_TYPE, 90);
|
builder = new AllInterCaseCommonAttributeSearcher(false, false, this.utils.USB_ID_TYPE, 90);
|
||||||
metadata = builder.findMatches();
|
metadata = builder.findMatches();
|
||||||
metadata.size();
|
metadata.size();
|
||||||
//assertTrue("This should yield 2 results.", verifyInstanceCount(metadata, 2));
|
//assertTrue("This should yield 2 results.", verifyInstanceCount(metadata, 2));
|
||||||
|
@ -21,7 +21,6 @@ package org.sleuthkit.autopsy.commonfilessearch;
|
|||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.Map;
|
|
||||||
import junit.framework.Test;
|
import junit.framework.Test;
|
||||||
import org.netbeans.junit.NbModuleSuite;
|
import org.netbeans.junit.NbModuleSuite;
|
||||||
import org.netbeans.junit.NbTestCase;
|
import org.netbeans.junit.NbTestCase;
|
||||||
@ -96,10 +95,8 @@ public class IngestedWithHashAndFileTypeInterCaseTests extends NbTestCase {
|
|||||||
*/
|
*/
|
||||||
public void testOne() {
|
public void testOne() {
|
||||||
try {
|
try {
|
||||||
Map<Long, String> dataSources = this.utils.getDataSourceMap();
|
|
||||||
|
|
||||||
//note that the params false and false are presently meaningless because that feature is not supported yet
|
//note that the params false and false are presently meaningless because that feature is not supported yet
|
||||||
AbstractCommonAttributeSearcher builder = new AllInterCaseCommonAttributeSearcher(dataSources, false, false, this.utils.FILE_TYPE, 0);
|
AbstractCommonAttributeSearcher builder = new AllInterCaseCommonAttributeSearcher(false, false, this.utils.FILE_TYPE, 0);
|
||||||
CommonAttributeSearchResults metadata = builder.findMatches();
|
CommonAttributeSearchResults metadata = builder.findMatches();
|
||||||
|
|
||||||
assertTrue("Results should not be empty", metadata.size() != 0);
|
assertTrue("Results should not be empty", metadata.size() != 0);
|
||||||
@ -146,11 +143,10 @@ public class IngestedWithHashAndFileTypeInterCaseTests extends NbTestCase {
|
|||||||
*/
|
*/
|
||||||
public void testTwo() {
|
public void testTwo() {
|
||||||
try {
|
try {
|
||||||
Map<Long, String> dataSources = this.utils.getDataSourceMap();
|
|
||||||
|
|
||||||
int matchesMustAlsoBeFoundInThisCase = this.utils.getCaseMap().get(CASE2);
|
int matchesMustAlsoBeFoundInThisCase = this.utils.getCaseMap().get(CASE2);
|
||||||
CorrelationAttributeInstance.Type fileType = CorrelationAttributeInstance.getDefaultCorrelationTypes().get(0);
|
CorrelationAttributeInstance.Type fileType = CorrelationAttributeInstance.getDefaultCorrelationTypes().get(0);
|
||||||
AbstractCommonAttributeSearcher builder = new SingleInterCaseCommonAttributeSearcher(matchesMustAlsoBeFoundInThisCase, dataSources, false, false, fileType, 0);
|
AbstractCommonAttributeSearcher builder = new SingleInterCaseCommonAttributeSearcher(matchesMustAlsoBeFoundInThisCase, false, false, fileType, 0);
|
||||||
|
|
||||||
CommonAttributeSearchResults metadata = builder.findMatches();
|
CommonAttributeSearchResults metadata = builder.findMatches();
|
||||||
|
|
||||||
@ -199,11 +195,10 @@ public class IngestedWithHashAndFileTypeInterCaseTests extends NbTestCase {
|
|||||||
*/
|
*/
|
||||||
public void testThree(){
|
public void testThree(){
|
||||||
try {
|
try {
|
||||||
Map<Long, String> dataSources = this.utils.getDataSourceMap();
|
|
||||||
|
|
||||||
//note that the params false and false are presently meaningless because that feature is not supported yet
|
//note that the params false and false are presently meaningless because that feature is not supported yet
|
||||||
CorrelationAttributeInstance.Type fileType = CorrelationAttributeInstance.getDefaultCorrelationTypes().get(0);
|
CorrelationAttributeInstance.Type fileType = CorrelationAttributeInstance.getDefaultCorrelationTypes().get(0);
|
||||||
AbstractCommonAttributeSearcher builder = new AllInterCaseCommonAttributeSearcher(dataSources, false, false, fileType, 50);
|
AbstractCommonAttributeSearcher builder = new AllInterCaseCommonAttributeSearcher(false, false, fileType, 50);
|
||||||
|
|
||||||
CommonAttributeSearchResults metadata = builder.findMatches();
|
CommonAttributeSearchResults metadata = builder.findMatches();
|
||||||
|
|
||||||
|
@ -229,7 +229,8 @@ public final class ImageGalleryTopComponent extends TopComponent implements Expl
|
|||||||
}
|
}
|
||||||
|
|
||||||
synchronized private void setController(ImageGalleryController controller) {
|
synchronized private void setController(ImageGalleryController controller) {
|
||||||
if (this.controller != null && notEqual(this.controller, controller)) {
|
if (notEqual(this.controller, controller)) {
|
||||||
|
if (this.controller != null) {
|
||||||
this.controller.reset();
|
this.controller.reset();
|
||||||
}
|
}
|
||||||
this.controller = controller;
|
this.controller = controller;
|
||||||
@ -269,6 +270,7 @@ public final class ImageGalleryTopComponent extends TopComponent implements Expl
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is called from within the constructor to initialize the form.
|
* This method is called from within the constructor to initialize the form.
|
||||||
|
@ -42,6 +42,7 @@ import java.util.TreeSet;
|
|||||||
import java.util.concurrent.CancellationException;
|
import java.util.concurrent.CancellationException;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.function.Consumer;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@ -286,17 +287,19 @@ public class GroupManager {
|
|||||||
group.removeFile(fileID);
|
group.removeFile(fileID);
|
||||||
|
|
||||||
// If we're grouping by category, we don't want to remove empty groups.
|
// If we're grouping by category, we don't want to remove empty groups.
|
||||||
if (groupKey.getAttribute() != DrawableAttribute.CATEGORY
|
if (group.getFileIDs().isEmpty()) {
|
||||||
&& group.getFileIDs().isEmpty()) {
|
markGroupSeen(group, true);
|
||||||
|
if (groupKey.getAttribute() != DrawableAttribute.CATEGORY) {
|
||||||
if (analyzedGroups.contains(group)) {
|
if (analyzedGroups.contains(group)) {
|
||||||
analyzedGroups.remove(group);
|
analyzedGroups.remove(group);
|
||||||
sortAnalyzedGroups();
|
sortAnalyzedGroups();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unSeenGroups.contains(group)) {
|
if (unSeenGroups.contains(group)) {
|
||||||
unSeenGroups.remove(group);
|
unSeenGroups.remove(group);
|
||||||
sortUnseenGroups();
|
sortUnseenGroups();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return group;
|
return group;
|
||||||
}
|
}
|
||||||
@ -447,7 +450,8 @@ public class GroupManager {
|
|||||||
if (!Case.isCaseOpen()) {
|
if (!Case.isCaseOpen()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
setSortBy(sortBy);
|
||||||
|
setSortOrder(sortOrder);
|
||||||
//only re-query the db if the data source or group by attribute changed or it is forced
|
//only re-query the db if the data source or group by attribute changed or it is forced
|
||||||
if (dataSource != getDataSource()
|
if (dataSource != getDataSource()
|
||||||
|| groupBy != getGroupBy()
|
|| groupBy != getGroupBy()
|
||||||
@ -455,13 +459,11 @@ public class GroupManager {
|
|||||||
|
|
||||||
setDataSource(dataSource);
|
setDataSource(dataSource);
|
||||||
setGroupBy(groupBy);
|
setGroupBy(groupBy);
|
||||||
setSortBy(sortBy);
|
|
||||||
setSortOrder(sortOrder);
|
|
||||||
Platform.runLater(regrouper::restart);
|
Platform.runLater(regrouper::restart);
|
||||||
} else {
|
} else {
|
||||||
// resort the list of groups
|
// resort the list of groups
|
||||||
setSortBy(sortBy);
|
|
||||||
setSortOrder(sortOrder);
|
|
||||||
sortAnalyzedGroups();
|
sortAnalyzedGroups();
|
||||||
sortUnseenGroups();
|
sortUnseenGroups();
|
||||||
}
|
}
|
||||||
@ -501,21 +503,26 @@ public class GroupManager {
|
|||||||
//if there is aleady a group that was previously deemed fully analyzed, then add this newly analyzed file to it.
|
//if there is aleady a group that was previously deemed fully analyzed, then add this newly analyzed file to it.
|
||||||
group.addFile(fileID);
|
group.addFile(fileID);
|
||||||
}
|
}
|
||||||
|
markGroupSeen(group, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
synchronized public void handleTagDeleted(ContentTagDeletedEvent evt) {
|
synchronized public void handleTagDeleted(ContentTagDeletedEvent evt) {
|
||||||
GroupKey<?> groupKey = null;
|
GroupKey<?> groupKey = null;
|
||||||
final ContentTagDeletedEvent.DeletedContentTagInfo deletedTagInfo = evt.getDeletedTagInfo();
|
final ContentTagDeletedEvent.DeletedContentTagInfo deletedTagInfo = evt.getDeletedTagInfo();
|
||||||
final TagName tagName = deletedTagInfo.getName();
|
final TagName deletedTagName = deletedTagInfo.getName();
|
||||||
if (getGroupBy() == DrawableAttribute.CATEGORY && CategoryManager.isCategoryTagName(tagName)) {
|
if (getGroupBy() == DrawableAttribute.CATEGORY && CategoryManager.isCategoryTagName(deletedTagName)) {
|
||||||
groupKey = new GroupKey<>(DrawableAttribute.CATEGORY, CategoryManager.categoryFromTagName(tagName), getDataSource());
|
groupKey = new GroupKey<>(DrawableAttribute.CATEGORY, CategoryManager.categoryFromTagName(deletedTagName), null);
|
||||||
} else if (getGroupBy() == DrawableAttribute.TAGS && CategoryManager.isNotCategoryTagName(tagName)) {
|
} else if (getGroupBy() == DrawableAttribute.TAGS && CategoryManager.isNotCategoryTagName(deletedTagName)) {
|
||||||
groupKey = new GroupKey<>(DrawableAttribute.TAGS, tagName, getDataSource());
|
groupKey = new GroupKey<>(DrawableAttribute.TAGS, deletedTagName, null);
|
||||||
}
|
}
|
||||||
if (groupKey != null) {
|
if (groupKey != null) {
|
||||||
final long fileID = deletedTagInfo.getContentID();
|
final long fileID = deletedTagInfo.getContentID();
|
||||||
DrawableGroup g = removeFromGroup(groupKey, fileID);
|
DrawableGroup g = removeFromGroup(groupKey, fileID);
|
||||||
|
|
||||||
|
if (controller.getCategoryManager().getTagName(DhsImageCategory.ZERO).equals(deletedTagName) == false) {
|
||||||
|
addFileToGroup(null, new GroupKey<>(DrawableAttribute.CATEGORY, DhsImageCategory.ZERO, null), fileID);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2016 Basis Technology Corp.
|
* Copyright 2016-18 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -54,7 +54,7 @@ public class SortChooser<X, Y extends Comparator<X>> extends HBox {
|
|||||||
|
|
||||||
private final ReadOnlyObjectWrapper<SortOrder> sortOrder = new ReadOnlyObjectWrapper<>(SortOrder.ASCENDING);
|
private final ReadOnlyObjectWrapper<SortOrder> sortOrder = new ReadOnlyObjectWrapper<>(SortOrder.ASCENDING);
|
||||||
private final SimpleBooleanProperty sortOrderDisabled = new SimpleBooleanProperty(false);
|
private final SimpleBooleanProperty sortOrderDisabled = new SimpleBooleanProperty(false);
|
||||||
private final SimpleObjectProperty<ValueType> valueType = new SimpleObjectProperty<>(ValueType.NUMERIC);
|
private final SimpleObjectProperty<ValueType> valueType = new SimpleObjectProperty<>(ValueType.LEXICOGRAPHIC);
|
||||||
|
|
||||||
public SortChooser(ObservableList<Y> comps) {
|
public SortChooser(ObservableList<Y> comps) {
|
||||||
this.comparators = comps;
|
this.comparators = comps;
|
||||||
@ -73,15 +73,13 @@ public class SortChooser<X, Y extends Comparator<X>> extends HBox {
|
|||||||
descRadio.getStyleClass().remove("radio-button");
|
descRadio.getStyleClass().remove("radio-button");
|
||||||
descRadio.getStyleClass().add("toggle-button");
|
descRadio.getStyleClass().add("toggle-button");
|
||||||
|
|
||||||
valueType.addListener((observable, oldValue, newValue) -> {
|
valueType.addListener(observable -> setValueTypeIcon(valueType.getValue()));
|
||||||
ascRadio.setGraphic(new ImageView(newValue.getAscendingImage()));
|
setValueTypeIcon(valueType.getValue());
|
||||||
descRadio.setGraphic(new ImageView(newValue.getDescendingImage()));
|
|
||||||
});
|
|
||||||
|
|
||||||
ascRadio.disableProperty().bind(sortOrderDisabled);
|
ascRadio.disableProperty().bind(sortOrderDisabled);
|
||||||
descRadio.disableProperty().bind(sortOrderDisabled);
|
descRadio.disableProperty().bind(sortOrderDisabled);
|
||||||
ascRadio.selectedProperty().addListener(selectedToggle -> {
|
ascRadio.selectedProperty().addListener(selectedToggle -> {
|
||||||
sortOrder.set(orderGroup.getSelectedToggle() == ascRadio ? SortOrder.ASCENDING : SortOrder.DESCENDING);
|
sortOrder.set(ascRadio.isSelected() ? SortOrder.ASCENDING : SortOrder.DESCENDING);
|
||||||
});
|
});
|
||||||
|
|
||||||
sortByBox.setItems(comparators);
|
sortByBox.setItems(comparators);
|
||||||
@ -89,6 +87,11 @@ public class SortChooser<X, Y extends Comparator<X>> extends HBox {
|
|||||||
sortByBox.setButtonCell(new ComparatorCell());
|
sortByBox.setButtonCell(new ComparatorCell());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setValueTypeIcon(ValueType newValue) {
|
||||||
|
ascRadio.setGraphic(new ImageView(newValue.getAscendingImage()));
|
||||||
|
descRadio.setGraphic(new ImageView(newValue.getDescendingImage()));
|
||||||
|
}
|
||||||
|
|
||||||
public ValueType getValueType() {
|
public ValueType getValueType() {
|
||||||
return valueType.get();
|
return valueType.get();
|
||||||
}
|
}
|
||||||
|
@ -56,6 +56,10 @@ final class GroupComparators<T extends Comparable<T>> implements Comparator<Draw
|
|||||||
private final Function<DrawableGroup, T> extractor;
|
private final Function<DrawableGroup, T> extractor;
|
||||||
private final Function<T, String> valueFormatter;
|
private final Function<T, String> valueFormatter;
|
||||||
private final boolean orderReveresed;
|
private final boolean orderReveresed;
|
||||||
|
|
||||||
|
boolean isOrderReveresed() {
|
||||||
|
return orderReveresed;
|
||||||
|
}
|
||||||
private final String displayName;
|
private final String displayName;
|
||||||
|
|
||||||
private GroupComparators(String displayName, Function<DrawableGroup, T> extractor, Function<T, String> formatter, boolean defaultOrderReversed) {
|
private GroupComparators(String displayName, Function<DrawableGroup, T> extractor, Function<T, String> formatter, boolean defaultOrderReversed) {
|
||||||
|
@ -123,10 +123,12 @@ abstract class NavPanel<X> extends Tab {
|
|||||||
*/
|
*/
|
||||||
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
||||||
Comparator<DrawableGroup> getComparator() {
|
Comparator<DrawableGroup> getComparator() {
|
||||||
Comparator<DrawableGroup> comparator = sortChooser.getComparator();
|
GroupComparators<?> comparator = sortChooser.getComparator();
|
||||||
return (sortChooser.getSortOrder() == SortOrder.ASCENDING)
|
Comparator<DrawableGroup> comparator2 = (sortChooser.getSortOrder() == SortOrder.ASCENDING)
|
||||||
? comparator
|
? comparator
|
||||||
: comparator.reversed();
|
: comparator.reversed();
|
||||||
|
|
||||||
|
return comparator.isOrderReveresed() ? comparator2.reversed() : comparator2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
x
Reference in New Issue
Block a user