Merge branch 'develop' of https://github.com/sleuthkit/autopsy into 2665_FixFormatting

This commit is contained in:
U-BASIS\dsmyda 2018-09-20 10:47:21 -04:00
commit 2a589a77e8
20 changed files with 411 additions and 203 deletions

View File

@ -98,16 +98,22 @@ 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);
switch (groupByDataSourceValue) { if (groupByDataSourceValue != null) {
case VALUE_TRUE: switch (groupByDataSourceValue) {
groupItemsInTreeByDataSource = true; case VALUE_TRUE:
break; groupItemsInTreeByDataSource = true;
case VALUE_FALSE: break;
groupItemsInTreeByDataSource = false; case VALUE_FALSE:
break; groupItemsInTreeByDataSource = false;
default: break;
groupItemsInTreeByDataSource = null; default:
break; logger.log(Level.WARNING, String.format("Unexpected value '%s' for key '%s'. Using 'null' instead.",
groupByDataSourceValue, KEY_GROUP_BY_DATA_SOURCE));
groupItemsInTreeByDataSource = null;
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);

View File

@ -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;
@ -88,7 +107,7 @@ abstract class AbstractSqlEamDb implements EamDb {
/** /**
* Add a new name/value pair in the db_info table. * Add a new name/value pair in the db_info table.
* *
* @param name Key to set * @param name Key to set
* @param value Value to set * @param value Value to set
* *
* @throws EamDbException * @throws EamDbException
@ -149,10 +168,21 @@ 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.
* *
* @param name Name to find * @param name Name to find
* @param value Value to assign to name. * @param value Value to assign to name.
* *
* @throws EamDbException * @throws EamDbException
@ -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);
} }
@ -505,18 +624,43 @@ abstract class AbstractSqlEamDb implements EamDb {
/** /**
* Retrieves Data Source details based on data source device ID * Retrieves Data Source details based on data source device ID
* *
* @param correlationCase the current CorrelationCase used for ensuring * @param correlationCase the current CorrelationCase used for ensuring
* uniqueness of DataSource * uniqueness of DataSource
* @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 {
@ -548,8 +695,8 @@ abstract class AbstractSqlEamDb implements EamDb {
* Retrieves Data Source details based on data source ID * Retrieves Data Source details based on data source ID
* *
* @param correlationCase the current CorrelationCase used for ensuring * @param correlationCase the current CorrelationCase used for ensuring
* uniqueness of DataSource * uniqueness of DataSource
* @param dataSourceId the data source ID number * @param dataSourceId the data source ID number
* *
* @return The data source * @return The data source
*/ */
@ -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 {
@ -715,7 +884,7 @@ abstract class AbstractSqlEamDb implements EamDb {
public List<CorrelationAttributeInstance> getArtifactInstancesByTypeValue(CorrelationAttributeInstance.Type aType, String value) throws EamDbException, CorrelationAttributeNormalizationException { public List<CorrelationAttributeInstance> getArtifactInstancesByTypeValue(CorrelationAttributeInstance.Type aType, String value) throws EamDbException, CorrelationAttributeNormalizationException {
String normalizedValue = CorrelationAttributeNormalizer.normalize(aType, value); String normalizedValue = CorrelationAttributeNormalizer.normalize(aType, value);
Connection conn = connect(); Connection conn = connect();
List<CorrelationAttributeInstance> artifactInstances = new ArrayList<>(); List<CorrelationAttributeInstance> artifactInstances = new ArrayList<>();
@ -764,7 +933,7 @@ abstract class AbstractSqlEamDb implements EamDb {
* Retrieves eamArtifact instances from the database that are associated * Retrieves eamArtifact instances from the database that are associated
* with the aType and filePath * with the aType and filePath
* *
* @param aType EamArtifact.Type to search for * @param aType EamArtifact.Type to search for
* @param filePath File path to search for * @param filePath File path to search for
* *
* @return List of 0 or more EamArtifactInstances * @return List of 0 or more EamArtifactInstances
@ -835,7 +1004,7 @@ abstract class AbstractSqlEamDb implements EamDb {
* @param value The correlation value * @param value The correlation value
* *
* @return Number of artifact instances having ArtifactType and * @return Number of artifact instances having ArtifactType and
* ArtifactValue. * ArtifactValue.
*/ */
@Override @Override
public Long getCountArtifactInstancesByTypeValue(CorrelationAttributeInstance.Type aType, String value) throws EamDbException, CorrelationAttributeNormalizationException { public Long getCountArtifactInstancesByTypeValue(CorrelationAttributeInstance.Type aType, String value) throws EamDbException, CorrelationAttributeNormalizationException {
@ -957,11 +1126,11 @@ abstract class AbstractSqlEamDb implements EamDb {
* associated with the caseDisplayName and dataSource of the given * associated with the caseDisplayName and dataSource of the given
* eamArtifact instance. * eamArtifact instance.
* *
* @param caseUUID Case ID to search for * @param caseUUID Case ID to search for
* @param dataSourceID Data source ID to search for * @param dataSourceID Data source ID to search for
* *
* @return Number of artifact instances having caseDisplayName and * @return Number of artifact instances having caseDisplayName and
* dataSource * dataSource
*/ */
@Override @Override
public Long getCountArtifactInstancesByCaseDataSource(String caseUUID, String dataSourceID) throws EamDbException { public Long getCountArtifactInstancesByCaseDataSource(String caseUUID, String dataSourceID) throws EamDbException {
@ -1224,7 +1393,7 @@ abstract class AbstractSqlEamDb implements EamDb {
* associated CorrelationAttribute object. * associated CorrelationAttribute object.
* *
* @param eamArtifact The correlation attribute whose database instance will * @param eamArtifact The correlation attribute whose database instance will
* be updated. * be updated.
* *
* @throws EamDbException * @throws EamDbException
*/ */
@ -1274,11 +1443,11 @@ abstract class AbstractSqlEamDb implements EamDb {
* Find a correlation attribute in the Central Repository database given the * Find a correlation attribute in the Central Repository database given the
* instance type, case, data source, value, and file path. * instance type, case, data source, value, and file path.
* *
* @param type The type of instance. * @param type The type of instance.
* @param correlationCase The case tied to the instance. * @param correlationCase The case tied to the instance.
* @param correlationDataSource The data source tied to the instance. * @param correlationDataSource The data source tied to the instance.
* @param value The value tied to the instance. * @param value The value tied to the instance.
* @param filePath The file path tied to the instance. * @param filePath The file path tied to the instance.
* *
* @return The correlation attribute if it exists; otherwise null. * @return The correlation attribute if it exists; otherwise null.
* *
@ -1287,7 +1456,7 @@ abstract class AbstractSqlEamDb implements EamDb {
@Override @Override
public CorrelationAttributeInstance getCorrelationAttributeInstance(CorrelationAttributeInstance.Type type, CorrelationCase correlationCase, public CorrelationAttributeInstance getCorrelationAttributeInstance(CorrelationAttributeInstance.Type type, CorrelationCase correlationCase,
CorrelationDataSource correlationDataSource, String value, String filePath) throws EamDbException, CorrelationAttributeNormalizationException { CorrelationDataSource correlationDataSource, String value, String filePath) throws EamDbException, CorrelationAttributeNormalizationException {
if (correlationCase == null) { if (correlationCase == null) {
throw new EamDbException("Correlation case is null"); throw new EamDbException("Correlation case is null");
} }
@ -1306,7 +1475,7 @@ abstract class AbstractSqlEamDb implements EamDb {
try { try {
String normalizedValue = CorrelationAttributeNormalizer.normalize(type, value); String normalizedValue = CorrelationAttributeNormalizer.normalize(type, value);
String tableName = EamDbUtil.correlationTypeToInstanceTableName(type); String tableName = EamDbUtil.correlationTypeToInstanceTableName(type);
String sql String sql
= "SELECT id, known_status, comment FROM " = "SELECT id, known_status, comment FROM "
@ -1349,7 +1518,7 @@ abstract class AbstractSqlEamDb implements EamDb {
* *
* @param eamArtifact Artifact containing exactly one (1) ArtifactInstance. * @param eamArtifact Artifact containing exactly one (1) ArtifactInstance.
* @param knownStatus The status to change the artifact to. Should never be * @param knownStatus The status to change the artifact to. Should never be
* KNOWN * KNOWN
*/ */
@Override @Override
public void setAttributeInstanceKnownStatus(CorrelationAttributeInstance eamArtifact, TskData.FileKnown knownStatus) throws EamDbException { public void setAttributeInstanceKnownStatus(CorrelationAttributeInstance eamArtifact, TskData.FileKnown knownStatus) throws EamDbException {
@ -1548,7 +1717,7 @@ abstract class AbstractSqlEamDb implements EamDb {
artifactInstances.add(artifactInstance); artifactInstances.add(artifactInstance);
} catch (CorrelationAttributeNormalizationException ex) { } catch (CorrelationAttributeNormalizationException ex) {
logger.log(Level.INFO, "Unable to get artifact instance from resultset.", ex); logger.log(Level.INFO, "Unable to get artifact instance from resultset.", ex);
} }
} }
} catch (SQLException ex) { } catch (SQLException ex) {
throw new EamDbException("Error getting notable artifact instances.", ex); // NON-NLS throw new EamDbException("Error getting notable artifact instances.", ex); // NON-NLS
@ -1571,7 +1740,7 @@ abstract class AbstractSqlEamDb implements EamDb {
*/ */
@Override @Override
public Long getCountArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType, String value) throws EamDbException, CorrelationAttributeNormalizationException { public Long getCountArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType, String value) throws EamDbException, CorrelationAttributeNormalizationException {
String normalizedValue = CorrelationAttributeNormalizer.normalize(aType, value); String normalizedValue = CorrelationAttributeNormalizer.normalize(aType, value);
Connection conn = connect(); Connection conn = connect();
@ -1612,13 +1781,13 @@ abstract class AbstractSqlEamDb implements EamDb {
* @param value Value to search for * @param value Value to search for
* *
* @return List of cases containing this artifact with instances marked as * @return List of cases containing this artifact with instances marked as
* bad * bad
* *
* @throws EamDbException * @throws EamDbException
*/ */
@Override @Override
public List<String> getListCasesHavingArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType, String value) throws EamDbException, CorrelationAttributeNormalizationException { public List<String> getListCasesHavingArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType, String value) throws EamDbException, CorrelationAttributeNormalizationException {
String normalizedValue = CorrelationAttributeNormalizer.normalize(aType, value); String normalizedValue = CorrelationAttributeNormalizer.normalize(aType, value);
Connection conn = connect(); Connection conn = connect();
@ -1778,7 +1947,7 @@ abstract class AbstractSqlEamDb implements EamDb {
public boolean isValueInReferenceSet(String value, int referenceSetID, int correlationTypeID) throws EamDbException, CorrelationAttributeNormalizationException { public boolean isValueInReferenceSet(String value, int referenceSetID, int correlationTypeID) throws EamDbException, CorrelationAttributeNormalizationException {
String normalizeValued = CorrelationAttributeNormalizer.normalize(this.getCorrelationTypeById(correlationTypeID), value); String normalizeValued = CorrelationAttributeNormalizer.normalize(this.getCorrelationTypeById(correlationTypeID), value);
Connection conn = connect(); Connection conn = connect();
Long matchingInstances = 0L; Long matchingInstances = 0L;
@ -1816,10 +1985,10 @@ abstract class AbstractSqlEamDb implements EamDb {
*/ */
@Override @Override
public boolean isArtifactKnownBadByReference(CorrelationAttributeInstance.Type aType, String value) throws EamDbException, CorrelationAttributeNormalizationException { public boolean isArtifactKnownBadByReference(CorrelationAttributeInstance.Type aType, String value) throws EamDbException, CorrelationAttributeNormalizationException {
//this should be done here so that we can be certain that aType and value are valid before we proceed //this should be done here so that we can be certain that aType and value are valid before we proceed
String normalizeValued = CorrelationAttributeNormalizer.normalize(aType, value); String normalizeValued = CorrelationAttributeNormalizer.normalize(aType, value);
// TEMP: Only support file correlation type // TEMP: Only support file correlation type
if (aType.getId() != CorrelationAttributeInstance.FILES_TYPE_ID) { if (aType.getId() != CorrelationAttributeInstance.FILES_TYPE_ID) {
return false; return false;
@ -1832,7 +2001,7 @@ abstract class AbstractSqlEamDb implements EamDb {
ResultSet resultSet = null; ResultSet resultSet = null;
String sql = "SELECT count(*) FROM %s WHERE value=? AND known_status=?"; String sql = "SELECT count(*) FROM %s WHERE value=? AND known_status=?";
try { try {
preparedStatement = conn.prepareStatement(String.format(sql, EamDbUtil.correlationTypeToReferenceTableName(aType))); preparedStatement = conn.prepareStatement(String.format(sql, EamDbUtil.correlationTypeToReferenceTableName(aType)));
preparedStatement.setString(1, normalizeValued); preparedStatement.setString(1, normalizeValued);
preparedStatement.setByte(2, TskData.FileKnown.BAD.getFileKnownValue()); preparedStatement.setByte(2, TskData.FileKnown.BAD.getFileKnownValue());
@ -1853,7 +2022,7 @@ abstract class AbstractSqlEamDb implements EamDb {
/** /**
* Process the Artifact instance in the EamDb * Process the Artifact instance in the 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
* *
* @throws EamDbException * @throws EamDbException
@ -1892,9 +2061,10 @@ abstract class AbstractSqlEamDb implements EamDb {
/** /**
* Process the Artifact instance in the EamDb give a where clause * Process the Artifact instance in the EamDb give a where clause
* *
* @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
@ -2076,7 +2246,7 @@ abstract class AbstractSqlEamDb implements EamDb {
* Update an existing organization. * Update an existing organization.
* *
* @param updatedOrganization the values the Organization with the same ID * @param updatedOrganization the values the Organization with the same ID
* will be updated to in the database. * will be updated to in the database.
* *
* @throws EamDbException * @throws EamDbException
*/ */
@ -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
*/ */
@ -2407,7 +2578,7 @@ abstract class AbstractSqlEamDb implements EamDb {
/** /**
* Get all reference entries having a given correlation type and value * Get all reference entries having a given correlation type and value
* *
* @param aType Type to use for matching * @param aType Type to use for matching
* @param aValue Value to use for matching * @param aValue Value to use for matching
* *
* @return List of all global file instances with a type and value * @return List of all global file instances with a type and value
@ -2440,7 +2611,7 @@ abstract class AbstractSqlEamDb implements EamDb {
EamDbUtil.closeResultSet(resultSet); EamDbUtil.closeResultSet(resultSet);
EamDbUtil.closeConnection(conn); EamDbUtil.closeConnection(conn);
} }
return globalFileInstances; return globalFileInstances;
} }
@ -2607,7 +2778,7 @@ abstract class AbstractSqlEamDb implements EamDb {
* artifacts. * artifacts.
* *
* @return List of enabled EamArtifact.Type's. If none are defined in the * @return List of enabled EamArtifact.Type's. If none are defined in the
* database, the default list will be returned. * database, the default list will be returned.
* *
* @throws EamDbException * @throws EamDbException
*/ */
@ -2642,7 +2813,7 @@ abstract class AbstractSqlEamDb implements EamDb {
* correlate artifacts. * correlate artifacts.
* *
* @return List of supported EamArtifact.Type's. If none are defined in the * @return List of supported EamArtifact.Type's. If none are defined in the
* database, the default list will be returned. * database, the default list will be returned.
* *
* @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;
@ -2746,7 +2937,7 @@ abstract class AbstractSqlEamDb implements EamDb {
* Convert a ResultSet to a EamCase object * Convert a ResultSet to a EamCase object
* *
* @param resultSet A resultSet with a set of values to create a EamCase * @param resultSet A resultSet with a set of values to create a EamCase
* object. * object.
* *
* @return fully populated EamCase object, or null * @return fully populated EamCase object, or null
* *
@ -2816,7 +3007,7 @@ abstract class AbstractSqlEamDb implements EamDb {
* Convert a ResultSet to a EamArtifactInstance object * Convert a ResultSet to a EamArtifactInstance object
* *
* @param resultSet A resultSet with a set of values to create a * @param resultSet A resultSet with a set of values to create a
* EamArtifactInstance object. * EamArtifactInstance object.
* *
* @return fully populated EamArtifactInstance, or null * @return fully populated EamArtifactInstance, or null
* *

View File

@ -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

View File

@ -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

View File

@ -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

View File

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

View File

@ -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(); try {
String currentDataSource = currentAttributeInstance.getCorrelationDataSource().getName(); String currentFullPath = currentAttributeInstance.getFilePath();
currentCase = Case.getCurrentCaseThrows();
if (this.dataSourceNameToIdMap.containsKey(currentDataSource)) { // Only attempt to make the abstract file if the attribute is from the current case
Long dataSourceObjectId = this.dataSourceNameToIdMap.get(currentDataSource); if (currentCase.getName().equals(currentAttributeInstance.getCorrelationCase().getCaseUUID())) {
try {
currentCase = Case.getCurrentCaseThrows();
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("\\", "/");
// 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, dataSourceObjectId); 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);
} }
} else {
} 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; return null;
} }
} else { } 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; 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;
}
} }

View File

@ -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 {

View File

@ -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;
} }

View File

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

View File

@ -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);
} }
/** /**

View File

@ -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);

View File

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

View File

@ -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));

View File

@ -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();

View File

@ -229,45 +229,47 @@ 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)) {
this.controller.reset(); if (this.controller != null) {
} this.controller.reset();
this.controller = controller;
Platform.runLater(new Runnable() {
@Override
public void run() {
//initialize jfx ui
fullUIStack = new StackPane(); //this is passed into controller
myScene = new Scene(fullUIStack);
jfxPanel.setScene(myScene);
groupPane = new GroupPane(controller);
centralStack = new StackPane(groupPane); //this is passed into controller
fullUIStack.getChildren().add(borderPane);
splitPane = new SplitPane();
borderPane.setCenter(splitPane);
Toolbar toolbar = new Toolbar(controller);
borderPane.setTop(toolbar);
borderPane.setBottom(new StatusBar(controller));
metaDataTable = new MetaDataPane(controller);
groupTree = new GroupTree(controller);
hashHitList = new HashHitGroupList(controller);
TabPane tabPane = new TabPane(groupTree, hashHitList);
tabPane.setPrefWidth(TabPane.USE_COMPUTED_SIZE);
tabPane.setMinWidth(TabPane.USE_PREF_SIZE);
VBox.setVgrow(tabPane, Priority.ALWAYS);
leftPane = new VBox(tabPane, new SummaryTablePane(controller));
SplitPane.setResizableWithParent(leftPane, Boolean.FALSE);
SplitPane.setResizableWithParent(groupPane, Boolean.TRUE);
SplitPane.setResizableWithParent(metaDataTable, Boolean.FALSE);
splitPane.getItems().addAll(leftPane, centralStack, metaDataTable);
splitPane.setDividerPositions(0.1, 1.0);
controller.regroupDisabledProperty().addListener((Observable observable) -> checkForGroups());
controller.getGroupManager().getAnalyzedGroups().addListener((Observable observable) -> Platform.runLater(() -> checkForGroups()));
Platform.runLater(() -> checkForGroups());
} }
}); this.controller = controller;
Platform.runLater(new Runnable() {
@Override
public void run() {
//initialize jfx ui
fullUIStack = new StackPane(); //this is passed into controller
myScene = new Scene(fullUIStack);
jfxPanel.setScene(myScene);
groupPane = new GroupPane(controller);
centralStack = new StackPane(groupPane); //this is passed into controller
fullUIStack.getChildren().add(borderPane);
splitPane = new SplitPane();
borderPane.setCenter(splitPane);
Toolbar toolbar = new Toolbar(controller);
borderPane.setTop(toolbar);
borderPane.setBottom(new StatusBar(controller));
metaDataTable = new MetaDataPane(controller);
groupTree = new GroupTree(controller);
hashHitList = new HashHitGroupList(controller);
TabPane tabPane = new TabPane(groupTree, hashHitList);
tabPane.setPrefWidth(TabPane.USE_COMPUTED_SIZE);
tabPane.setMinWidth(TabPane.USE_PREF_SIZE);
VBox.setVgrow(tabPane, Priority.ALWAYS);
leftPane = new VBox(tabPane, new SummaryTablePane(controller));
SplitPane.setResizableWithParent(leftPane, Boolean.FALSE);
SplitPane.setResizableWithParent(groupPane, Boolean.TRUE);
SplitPane.setResizableWithParent(metaDataTable, Boolean.FALSE);
splitPane.getItems().addAll(leftPane, centralStack, metaDataTable);
splitPane.setDividerPositions(0.1, 1.0);
controller.regroupDisabledProperty().addListener((Observable observable) -> checkForGroups());
controller.getGroupManager().getAnalyzedGroups().addListener((Observable observable) -> Platform.runLater(() -> checkForGroups()));
Platform.runLater(() -> checkForGroups());
}
});
}
} }
/** /**

View File

@ -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 (analyzedGroups.contains(group)) { if (groupKey.getAttribute() != DrawableAttribute.CATEGORY) {
analyzedGroups.remove(group); if (analyzedGroups.contains(group)) {
sortAnalyzedGroups(); analyzedGroups.remove(group);
sortAnalyzedGroups();
}
if (unSeenGroups.contains(group)) {
unSeenGroups.remove(group);
sortUnseenGroups();
}
} }
if (unSeenGroups.contains(group)) {
unSeenGroups.remove(group);
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);
}
} }
} }

View File

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

View File

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

View File

@ -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;
} }
/** /**