diff --git a/Core/build.xml b/Core/build.xml index 2efe03ecea..d246edaa54 100644 --- a/Core/build.xml +++ b/Core/build.xml @@ -97,6 +97,12 @@ + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java index f535d58ccd..991821bebf 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java @@ -387,6 +387,46 @@ abstract class AbstractSqlEamDb implements EamDb { return eamCaseResult; } + /** + * Retrieves Case details based on Case ID + * + * @param caseID unique identifier for a case + * + * @return The retrieved case + */ + @Override + public CorrelationCase getCaseById(int caseId) throws EamDbException { + // @@@ We should have a cache here... + + Connection conn = connect(); + + CorrelationCase eamCaseResult = null; + PreparedStatement preparedStatement = null; + ResultSet resultSet = null; + + String sql = "SELECT cases.id as case_id, case_uid, case_name, creation_date, case_number, examiner_name, " + + "examiner_email, examiner_phone, notes, organizations.id as org_id, org_name, poc_name, poc_email, poc_phone " + + "FROM cases " + + "LEFT JOIN organizations ON cases.org_id=organizations.id " + + "WHERE cases.id=?"; + + try { + preparedStatement = conn.prepareStatement(sql); + preparedStatement.setInt(1, caseId); + resultSet = preparedStatement.executeQuery(); + if (resultSet.next()) { + eamCaseResult = getEamCaseFromResultSet(resultSet); + } + } catch (SQLException ex) { + throw new EamDbException("Error getting case details.", ex); // NON-NLS + } finally { + EamDbUtil.closeStatement(preparedStatement); + EamDbUtil.closeResultSet(resultSet); + EamDbUtil.closeConnection(conn); + } + + return eamCaseResult; + } /** * Retrieves cases that are in DB. @@ -502,6 +542,48 @@ abstract class AbstractSqlEamDb implements EamDb { return eamDataSourceResult; } + + /** + * 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 + */ + @Override + public CorrelationDataSource getDataSourceById(CorrelationCase correlationCase, int dataSourceId) throws EamDbException { + if (correlationCase == null) { + throw new EamDbException("Correlation case is null"); + } + + Connection conn = connect(); + + CorrelationDataSource eamDataSourceResult = null; + PreparedStatement preparedStatement = null; + ResultSet resultSet = null; + + String sql = "SELECT * FROM data_sources WHERE id=? AND case_id=?"; // NON-NLS + + try { + preparedStatement = conn.prepareStatement(sql); + preparedStatement.setInt(1, dataSourceId); + preparedStatement.setInt(2, correlationCase.getID()); + resultSet = preparedStatement.executeQuery(); + if (resultSet.next()) { + eamDataSourceResult = getEamDataSourceFromResultSet(resultSet); + } + } catch (SQLException ex) { + throw new EamDbException("Error getting data source.", ex); // NON-NLS + } finally { + EamDbUtil.closeStatement(preparedStatement); + EamDbUtil.closeResultSet(resultSet); + EamDbUtil.closeConnection(conn); + } + + return eamDataSourceResult; + } /** * Return a list of data sources in the DB @@ -670,7 +752,7 @@ abstract class AbstractSqlEamDb implements EamDb { return artifactInstances; } - + /** * Retrieves eamArtifact instances from the database that are associated * with the aType and filePath @@ -1814,6 +1896,52 @@ abstract class AbstractSqlEamDb implements EamDb { } } + /** + * Process the Artifact instance in the EamDb give a where clause + * + * @param type EamArtifact.Type to search for + * @param instanceTableCallback callback to process the instance + * @param whereClause query string to execute + * @throws EamDbException + */ + @Override + public void processInstanceTableWhere(CorrelationAttribute.Type type, String whereClause, InstanceTableCallback instanceTableCallback) throws EamDbException { + if (type == null) { + throw new EamDbException("Correlation type is null"); + } + + if (instanceTableCallback == null) { + throw new EamDbException("Callback interface is null"); + } + + if(whereClause == null) { + throw new EamDbException("Where clause is null"); + } + + Connection conn = connect(); + PreparedStatement preparedStatement = null; + ResultSet resultSet = null; + String tableName = EamDbUtil.correlationTypeToInstanceTableName(type); + StringBuilder sql = new StringBuilder(300); + sql.append("select * from ") + .append(tableName) + .append(" WHERE ") + .append(whereClause); + + try { + preparedStatement = conn.prepareStatement(sql.toString()); + resultSet = preparedStatement.executeQuery(); + instanceTableCallback.process(resultSet); + } catch (SQLException ex) { + throw new EamDbException("Error getting all artifact instances from instances table", ex); + } finally { + EamDbUtil.closeStatement(preparedStatement); + EamDbUtil.closeResultSet(resultSet); + EamDbUtil.closeConnection(conn); + } + } + + @Override public EamOrganization newOrganization(EamOrganization eamOrg) throws EamDbException { if (eamOrg == null) { diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDb.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDb.java index 7ced4a1d7d..e4fb30583e 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDb.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDb.java @@ -175,13 +175,21 @@ public interface EamDb { */ CorrelationCase getCaseByUUID(String caseUUID) throws EamDbException; + /** + * Retrieves Case details based on Case ID + * + * @param caseID unique identifier for a case + * + * @return The retrieved case + */ + CorrelationCase getCaseById(int caseId) throws EamDbException; /** * Retrieves cases that are in DB. * * @return List of cases */ List getCases() throws EamDbException; - + /** * Creates new Data Source in the database * @@ -200,6 +208,18 @@ public interface EamDb { */ CorrelationDataSource getDataSource(CorrelationCase correlationCase, String dataSourceDeviceId) throws EamDbException; + + /** + * 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 + */ + CorrelationDataSource getDataSourceById(CorrelationCase correlationCase, int dataSourceId) throws EamDbException; + /** * Retrieves data sources that are in DB * @@ -225,7 +245,7 @@ public interface EamDb { * @return List of artifact instances for a given type/value */ List getArtifactInstancesByTypeValue(CorrelationAttribute.Type aType, String value) throws EamDbException; - + /** * Retrieves eamArtifact instances from the database that are associated * with the aType and filePath @@ -685,4 +705,15 @@ public interface EamDb { * @throws EamDbException */ void processInstanceTable(CorrelationAttribute.Type type, InstanceTableCallback instanceTableCallback) throws EamDbException; + + /** + * Process the Artifact instance in the EamDb + * + * @param type EamArtifact.Type to search for + * @param instanceTableCallback callback to process the instance + * @param whereClause query string to execute + * @throws EamDbException + */ + void processInstanceTableWhere(CorrelationAttribute.Type type, String whereClause, InstanceTableCallback instanceTableCallback) throws EamDbException; + } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDb.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDb.java index 4a3ef36530..74d0c36a66 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDb.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDb.java @@ -33,9 +33,9 @@ import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.coordinationservice.CoordinationService; /** - * Sqlite implementation of the Central Repository database. - * All methods in AbstractSqlEamDb that read or write to the database should - * be overriden here and use appropriate locking. + * Sqlite implementation of the Central Repository database. All methods in + * AbstractSqlEamDb that read or write to the database should be overriden here + * and use appropriate locking. */ final class SqliteEamDb extends AbstractSqlEamDb { @@ -46,17 +46,18 @@ final class SqliteEamDb extends AbstractSqlEamDb { private BasicDataSource connectionPool = null; private final SqliteEamDbSettings dbSettings; - + // While the Sqlite database should only be used for single users, it is still // possible for multiple threads to attempt to write to the database simultaneously. private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(true); /** * Get the singleton instance of SqliteEamDb - * + * * @return the singleton instance of SqliteEamDb - * - * @throws EamDbException if one or more default correlation type(s) have an invalid db table name. + * + * @throws EamDbException if one or more default correlation type(s) have an + * invalid db table name. */ public synchronized static SqliteEamDb getInstance() throws EamDbException { if (instance == null) { @@ -67,9 +68,9 @@ final class SqliteEamDb extends AbstractSqlEamDb { } /** - * - * @throws EamDbException if the AbstractSqlEamDb class has one or more default - * correlation type(s) having an invalid db table name. + * + * @throws EamDbException if the AbstractSqlEamDb class has one or more + * default correlation type(s) having an invalid db table name. */ private SqliteEamDb() throws EamDbException { dbSettings = new SqliteEamDbSettings(); @@ -79,7 +80,7 @@ final class SqliteEamDb extends AbstractSqlEamDb { @Override public void shutdownConnections() throws EamDbException { try { - synchronized(this) { + synchronized (this) { if (null != connectionPool) { connectionPool.close(); connectionPool = null; // force it to be re-created on next connect() @@ -89,7 +90,7 @@ final class SqliteEamDb extends AbstractSqlEamDb { throw new EamDbException("Failed to close existing database connections.", ex); // NON-NLS } } - + @Override public void updateSettings() { synchronized (this) { @@ -107,9 +108,9 @@ final class SqliteEamDb extends AbstractSqlEamDb { @Override public void reset() throws EamDbException { - try{ + try { acquireExclusiveLock(); - + Connection conn = connect(); try { @@ -150,11 +151,11 @@ final class SqliteEamDb extends AbstractSqlEamDb { * */ private void setupConnectionPool() throws EamDbException { - + if (dbSettings.dbFileExists() == false) { throw new EamDbException("Central repository database missing"); } - + connectionPool = new BasicDataSource(); connectionPool.setDriverClassName(dbSettings.getDriver()); connectionPool.setUrl(dbSettings.getConnectionURL()); @@ -200,25 +201,24 @@ final class SqliteEamDb extends AbstractSqlEamDb { return ""; } - /** * 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 * * @throws EamDbException */ @Override public void newDbInfo(String name, String value) throws EamDbException { - try{ + try { acquireExclusiveLock(); super.newDbInfo(name, value); } finally { releaseExclusiveLock(); } } - + /** * Get the value for the given name from the name/value db_info table. * @@ -230,47 +230,47 @@ final class SqliteEamDb extends AbstractSqlEamDb { */ @Override public String getDbInfo(String name) throws EamDbException { - try{ + try { acquireSharedLock(); return super.getDbInfo(name); } finally { releaseSharedLock(); - } + } } - + /** * 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. * * @throws EamDbException */ @Override public void updateDbInfo(String name, String value) throws EamDbException { - try{ + try { acquireExclusiveLock(); super.updateDbInfo(name, value); } finally { releaseExclusiveLock(); - } + } } - - /** + + /** * Creates new Case in the database from the given case - * + * * @param autopsyCase The case to add */ @Override public CorrelationCase newCase(Case autopsyCase) throws EamDbException { - try{ + try { acquireExclusiveLock(); return super.newCase(autopsyCase); } finally { releaseExclusiveLock(); - } - } - + } + } + /** * Creates new Case in the database * @@ -280,14 +280,14 @@ final class SqliteEamDb extends AbstractSqlEamDb { */ @Override public CorrelationCase newCase(CorrelationCase eamCase) throws EamDbException { - try{ + try { acquireExclusiveLock(); return super.newCase(eamCase); } finally { releaseExclusiveLock(); - } + } } - + /** * Updates an existing Case in the database * @@ -295,14 +295,14 @@ final class SqliteEamDb extends AbstractSqlEamDb { */ @Override public void updateCase(CorrelationCase eamCase) throws EamDbException { - try{ + try { acquireExclusiveLock(); super.updateCase(eamCase); } finally { releaseExclusiveLock(); - } - } - + } + } + /** * Retrieves Case details based on Case UUID * @@ -312,14 +312,32 @@ final class SqliteEamDb extends AbstractSqlEamDb { */ @Override public CorrelationCase getCaseByUUID(String caseUUID) throws EamDbException { - try{ + try { acquireSharedLock(); return super.getCaseByUUID(caseUUID); } finally { releaseSharedLock(); - } - } - + } + } + + /** + * Retrieves Case details based on Case ID + * + * @param caseID unique identifier for a case + * + * @return The retrieved case + */ + @Override + public CorrelationCase getCaseById(int caseId) throws EamDbException { + try { + acquireSharedLock(); + return super.getCaseById(caseId); + } finally { + releaseSharedLock(); + } + + } + /** * Retrieves cases that are in DB. * @@ -327,14 +345,14 @@ final class SqliteEamDb extends AbstractSqlEamDb { */ @Override public List getCases() throws EamDbException { - try{ + try { acquireSharedLock(); return super.getCases(); } finally { releaseSharedLock(); - } - } - + } + } + /** * Creates new Data Source in the database * @@ -342,32 +360,52 @@ final class SqliteEamDb extends AbstractSqlEamDb { */ @Override public void newDataSource(CorrelationDataSource eamDataSource) throws EamDbException { - try{ + try { acquireExclusiveLock(); super.newDataSource(eamDataSource); } finally { releaseExclusiveLock(); - } - } - + } + } + /** * Retrieves Data Source details based on data source device ID * - * @param correlationCase the current CorrelationCase used for ensuring uniqueness of DataSource + * @param correlationCase the current CorrelationCase used for ensuring + * uniqueness of DataSource * @param dataSourceDeviceId the data source device ID number * * @return The data source */ @Override public CorrelationDataSource getDataSource(CorrelationCase correlationCase, String dataSourceDeviceId) throws EamDbException { - try{ + try { acquireSharedLock(); return super.getDataSource(correlationCase, dataSourceDeviceId); } finally { releaseSharedLock(); - } - } + } + } + /** + * 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 + */ + @Override + public CorrelationDataSource getDataSourceById(CorrelationCase correlationCase, int dataSourceId) throws EamDbException { + try { + acquireSharedLock(); + return super.getDataSourceById(correlationCase, dataSourceId); + } finally { + releaseSharedLock(); + } + } + /** * Return a list of data sources in the DB * @@ -375,14 +413,14 @@ final class SqliteEamDb extends AbstractSqlEamDb { */ @Override public List getDataSources() throws EamDbException { - try{ + try { acquireSharedLock(); return super.getDataSources(); } finally { releaseSharedLock(); - } - } - + } + } + /** * Inserts new Artifact(s) into the database. Should add associated Case and * Data Source first. @@ -391,38 +429,38 @@ final class SqliteEamDb extends AbstractSqlEamDb { */ @Override public void addArtifact(CorrelationAttribute eamArtifact) throws EamDbException { - try{ + try { acquireExclusiveLock(); super.addArtifact(eamArtifact); } finally { releaseExclusiveLock(); - } - } - + } + } + /** * Retrieves eamArtifact instances from the database that are associated * with the eamArtifactType and eamArtifactValue of the given eamArtifact. * - * @param aType The type of the artifact - * @param value The correlation value + * @param aType The type of the artifact + * @param value The correlation value * * @return List of artifact instances for a given type/value */ @Override public List getArtifactInstancesByTypeValue(CorrelationAttribute.Type aType, String value) throws EamDbException { - try{ + try { acquireSharedLock(); return super.getArtifactInstancesByTypeValue(aType, value); } finally { releaseSharedLock(); - } + } } /** * Retrieves eamArtifact instances from the database that are associated * 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 * * @return List of 0 or more EamArtifactInstances @@ -431,14 +469,14 @@ final class SqliteEamDb extends AbstractSqlEamDb { */ @Override public List getArtifactInstancesByPath(CorrelationAttribute.Type aType, String filePath) throws EamDbException { - try{ + try { acquireSharedLock(); return super.getArtifactInstancesByPath(aType, filePath); } finally { releaseSharedLock(); - } - } - + } + } + /** * Retrieves number of artifact instances in the database that are * associated with the ArtifactType and artifactValue of the given artifact. @@ -447,29 +485,29 @@ final class SqliteEamDb extends AbstractSqlEamDb { * @param value The value to search for * * @return Number of artifact instances having ArtifactType and - * ArtifactValue. - * @throws EamDbException + * ArtifactValue. + * @throws EamDbException */ @Override public Long getCountArtifactInstancesByTypeValue(CorrelationAttribute.Type aType, String value) throws EamDbException { - try{ + try { acquireSharedLock(); return super.getCountArtifactInstancesByTypeValue(aType, value); } finally { releaseSharedLock(); - } + } } - + @Override public int getFrequencyPercentage(CorrelationAttribute corAttr) throws EamDbException { - try{ + try { acquireSharedLock(); return super.getFrequencyPercentage(corAttr); } finally { releaseSharedLock(); - } - } - + } + } + /** * Retrieves number of unique caseDisplayName / dataSource tuples in the * database that are associated with the artifactType and artifactValue of @@ -483,130 +521,130 @@ final class SqliteEamDb extends AbstractSqlEamDb { */ @Override public Long getCountUniqueCaseDataSourceTuplesHavingTypeValue(CorrelationAttribute.Type aType, String value) throws EamDbException { - try{ + try { acquireSharedLock(); return super.getCountUniqueCaseDataSourceTuplesHavingTypeValue(aType, value); } finally { releaseSharedLock(); - } - } - + } + } @Override public Long getCountUniqueDataSources() throws EamDbException { - try{ + try { acquireSharedLock(); return super.getCountUniqueDataSources(); } finally { releaseSharedLock(); - } - } - + } + } + /** * Retrieves number of eamArtifact instances in the database that are * associated with the caseDisplayName and dataSource of the given * eamArtifact instance. * - * @param caseUUID Case ID to search for + * @param caseUUID Case ID to search for * @param dataSourceID Data source ID to search for * * @return Number of artifact instances having caseDisplayName and - * dataSource + * dataSource */ @Override public Long getCountArtifactInstancesByCaseDataSource(String caseUUID, String dataSourceID) throws EamDbException { - try{ + try { acquireSharedLock(); return super.getCountArtifactInstancesByCaseDataSource(caseUUID, dataSourceID); } finally { releaseSharedLock(); - } - } - + } + } + /** * Executes a bulk insert of the eamArtifacts added from the * prepareBulkArtifact() method */ @Override public void bulkInsertArtifacts() throws EamDbException { - try{ + try { acquireExclusiveLock(); super.bulkInsertArtifacts(); } finally { releaseExclusiveLock(); - } - } - + } + } + /** * Executes a bulk insert of the cases */ @Override public void bulkInsertCases(List cases) throws EamDbException { - try{ + try { acquireExclusiveLock(); super.bulkInsertCases(cases); } finally { releaseExclusiveLock(); - } - } - + } + } + /** - * Sets an eamArtifact instance to the given knownStatus. - * knownStatus should be BAD if the file has been tagged with a notable tag and - * UNKNOWN otherwise. If eamArtifact - * exists, it is updated. If eamArtifact does not exist it is added with the - * given status. + * Sets an eamArtifact instance to the given knownStatus. knownStatus should + * be BAD if the file has been tagged with a notable tag and UNKNOWN + * otherwise. If eamArtifact exists, it is updated. If eamArtifact does not + * exist it is added with the given status. * * @param eamArtifact Artifact containing exactly one (1) ArtifactInstance. - * @param knownStatus The status to change the artifact to. Should never be KNOWN + * @param knownStatus The status to change the artifact to. Should never be + * KNOWN */ @Override public void setArtifactInstanceKnownStatus(CorrelationAttribute eamArtifact, TskData.FileKnown knownStatus) throws EamDbException { - try{ + try { acquireExclusiveLock(); super.setArtifactInstanceKnownStatus(eamArtifact, knownStatus); } finally { releaseExclusiveLock(); - } - } - + } + } + /** * Gets list of matching eamArtifact instances that have knownStatus = * "Bad". * * @param aType EamArtifact.Type to search for * @param value Value to search for - * + * * @return List with 0 or more matching eamArtifact instances. */ @Override public List getArtifactInstancesKnownBad(CorrelationAttribute.Type aType, String value) throws EamDbException { - try{ + try { acquireSharedLock(); return super.getArtifactInstancesKnownBad(aType, value); } finally { releaseSharedLock(); - } - } - + } + } + /** * * Gets list of matching eamArtifact instances that have knownStatus = * "Bad". + * * @param aType EamArtifact.Type to search for * @return List with 0 or more matching eamArtifact instances. * @throws EamDbException */ @Override public List getArtifactInstancesKnownBad(CorrelationAttribute.Type aType) throws EamDbException { - try{ + try { acquireSharedLock(); return super.getArtifactInstancesKnownBad(aType); } finally { releaseSharedLock(); - } + } } - + /** * Count matching eamArtifacts instances that have knownStatus = "Bad". * @@ -617,14 +655,14 @@ final class SqliteEamDb extends AbstractSqlEamDb { */ @Override public Long getCountArtifactInstancesKnownBad(CorrelationAttribute.Type aType, String value) throws EamDbException { - try{ + try { acquireSharedLock(); return super.getCountArtifactInstancesKnownBad(aType, value); } finally { releaseSharedLock(); - } - } - + } + } + /** * Gets list of distinct case display names, where each case has 1+ Artifact * Instance matching eamArtifact with knownStatus = "Bad". @@ -633,37 +671,39 @@ final class SqliteEamDb extends AbstractSqlEamDb { * @param value Value to search for * * @return List of cases containing this artifact with instances marked as - * bad + * bad * * @throws EamDbException */ @Override public List getListCasesHavingArtifactInstancesKnownBad(CorrelationAttribute.Type aType, String value) throws EamDbException { - try{ + try { acquireSharedLock(); return super.getListCasesHavingArtifactInstancesKnownBad(aType, value); } finally { releaseSharedLock(); - } - } - + } + } + /** * Remove a reference set and all values contained in it. + * * @param referenceSetID - * @throws EamDbException + * @throws EamDbException */ @Override - public void deleteReferenceSet(int referenceSetID) throws EamDbException{ - try{ + public void deleteReferenceSet(int referenceSetID) throws EamDbException { + try { acquireExclusiveLock(); super.deleteReferenceSet(referenceSetID); } finally { releaseExclusiveLock(); - } - } - + } + } + /** * Check if the given hash is in a specific reference set + * * @param value * @param referenceSetID * @param correlationTypeID @@ -671,14 +711,14 @@ final class SqliteEamDb extends AbstractSqlEamDb { */ @Override public boolean isValueInReferenceSet(String value, int referenceSetID, int correlationTypeID) throws EamDbException { - try{ + try { acquireSharedLock(); return super.isValueInReferenceSet(value, referenceSetID, correlationTypeID); } finally { releaseSharedLock(); - } + } } - + /** * Process the Artifact instance in the EamDb * @@ -695,24 +735,44 @@ final class SqliteEamDb extends AbstractSqlEamDb { releaseSharedLock(); } } + /** - * Check whether a reference set with the given name/version is in the central repo. - * Used to check for name collisions when creating reference sets. + * Process the Artifact instance in the EamDb + * + * @param type EamArtifact.Type to search for + * @param instanceTableCallback callback to process the instance + * @throws EamDbException + */ + @Override + public void processInstanceTableWhere(CorrelationAttribute.Type type, String whereClause, InstanceTableCallback instanceTableCallback) throws EamDbException { + try { + acquireSharedLock(); + super.processInstanceTableWhere(type, whereClause, instanceTableCallback); + } finally { + releaseSharedLock(); + } + } + + /** + * Check whether a reference set with the given name/version is in the + * central repo. Used to check for name collisions when creating reference + * sets. + * * @param referenceSetName * @param version * @return true if a matching set is found - * @throws EamDbException + * @throws EamDbException */ @Override public boolean referenceSetExists(String referenceSetName, String version) throws EamDbException { - try{ + try { acquireSharedLock(); return super.referenceSetExists(referenceSetName, version); } finally { releaseSharedLock(); - } + } } - + /** * Is the artifact known as bad according to the reference entries? * @@ -722,34 +782,34 @@ final class SqliteEamDb extends AbstractSqlEamDb { * @return Global known status of the artifact */ @Override - public boolean isArtifactKnownBadByReference(CorrelationAttribute.Type aType, String value) throws EamDbException { - try{ + public boolean isArtifactKnownBadByReference(CorrelationAttribute.Type aType, String value) throws EamDbException { + try { acquireSharedLock(); return super.isArtifactKnownBadByReference(aType, value); } finally { releaseSharedLock(); - } + } } - + /** * Add a new organization * * @return the Organization ID of the newly created organization. - * + * * @param eamOrg The organization to add * * @throws EamDbException */ @Override public EamOrganization newOrganization(EamOrganization eamOrg) throws EamDbException { - try{ + try { acquireExclusiveLock(); return super.newOrganization(eamOrg); } finally { releaseExclusiveLock(); - } - } - + } + } + /** * Get all organizations * @@ -759,14 +819,14 @@ final class SqliteEamDb extends AbstractSqlEamDb { */ @Override public List getOrganizations() throws EamDbException { - try{ + try { acquireSharedLock(); return super.getOrganizations(); } finally { releaseSharedLock(); - } - } - + } + } + /** * Get an organization having the given ID * @@ -778,33 +838,34 @@ final class SqliteEamDb extends AbstractSqlEamDb { */ @Override public EamOrganization getOrganizationByID(int orgID) throws EamDbException { - try{ + try { acquireSharedLock(); return super.getOrganizationByID(orgID); } finally { releaseSharedLock(); - } - } - + } + } + @Override public void updateOrganization(EamOrganization updatedOrganization) throws EamDbException { - try{ + try { acquireExclusiveLock(); super.updateOrganization(updatedOrganization); } finally { releaseExclusiveLock(); - } + } } - + @Override public void deleteOrganization(EamOrganization organizationToDelete) throws EamDbException { - try{ + try { acquireExclusiveLock(); super.deleteOrganization(organizationToDelete); } finally { releaseExclusiveLock(); - } + } } + /** * Add a new Global Set * @@ -816,14 +877,14 @@ final class SqliteEamDb extends AbstractSqlEamDb { */ @Override public int newReferenceSet(EamGlobalSet eamGlobalSet) throws EamDbException { - try{ + try { acquireExclusiveLock(); return super.newReferenceSet(eamGlobalSet); } finally { releaseExclusiveLock(); - } - } - + } + } + /** * Get a reference set by ID * @@ -835,52 +896,51 @@ final class SqliteEamDb extends AbstractSqlEamDb { */ @Override public EamGlobalSet getReferenceSetByID(int referenceSetID) throws EamDbException { - try{ + try { acquireSharedLock(); return super.getReferenceSetByID(referenceSetID); } finally { releaseSharedLock(); - } - } - + } + } + /** * Get all reference sets * * @param correlationType Type of sets to return - * + * * @return List of all reference sets in the central repository * * @throws EamDbException */ @Override public List getAllReferenceSets(CorrelationAttribute.Type correlationType) throws EamDbException { - try{ + try { acquireSharedLock(); return super.getAllReferenceSets(correlationType); } finally { releaseSharedLock(); - } + } } - + /** * Add a new reference instance * * @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 */ @Override public void addReferenceInstance(EamGlobalFileInstance eamGlobalFileInstance, CorrelationAttribute.Type correlationType) throws EamDbException { - try{ + try { acquireExclusiveLock(); super.addReferenceInstance(eamGlobalFileInstance, correlationType); } finally { releaseExclusiveLock(); - } - } - + } + } + /** * Insert the bulk collection of Reference Type Instances * @@ -888,18 +948,18 @@ final class SqliteEamDb extends AbstractSqlEamDb { */ @Override public void bulkInsertReferenceTypeEntries(Set globalInstances, CorrelationAttribute.Type contentType) throws EamDbException { - try{ + try { acquireExclusiveLock(); super.bulkInsertReferenceTypeEntries(globalInstances, contentType); } finally { releaseExclusiveLock(); - } - } - + } + } + /** * 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 * * @return List of all global file instances with a type and value @@ -908,14 +968,14 @@ final class SqliteEamDb extends AbstractSqlEamDb { */ @Override public List getReferenceInstancesByTypeValue(CorrelationAttribute.Type aType, String aValue) throws EamDbException { - try{ + try { acquireSharedLock(); return super.getReferenceInstancesByTypeValue(aType, aValue); } finally { releaseSharedLock(); - } - } - + } + } + /** * Add a new EamArtifact.Type to the db. * @@ -927,71 +987,71 @@ final class SqliteEamDb extends AbstractSqlEamDb { */ @Override public int newCorrelationType(CorrelationAttribute.Type newType) throws EamDbException { - try{ + try { acquireExclusiveLock(); return super.newCorrelationType(newType); } finally { releaseExclusiveLock(); - } - } - + } + } + /** * Get the list of EamArtifact.Type's that will be used to correlate * artifacts. * * @return List of EamArtifact.Type's. If none are defined in the database, - * the default list will be returned. + * the default list will be returned. * * @throws EamDbException */ @Override public List getDefinedCorrelationTypes() throws EamDbException { - try{ + try { acquireSharedLock(); return super.getDefinedCorrelationTypes(); } finally { releaseSharedLock(); - } - } - + } + } + /** * Get the list of enabled EamArtifact.Type's that will be used to correlate * artifacts. * * @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 */ @Override public List getEnabledCorrelationTypes() throws EamDbException { - try{ + try { acquireSharedLock(); return super.getEnabledCorrelationTypes(); } finally { releaseSharedLock(); - } - } - + } + } + /** * Get the list of supported EamArtifact.Type's that can be used to * correlate artifacts. * * @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 */ @Override public List getSupportedCorrelationTypes() throws EamDbException { - try{ + try { acquireSharedLock(); return super.getSupportedCorrelationTypes(); } finally { releaseSharedLock(); - } - } - + } + } + /** * Update a EamArtifact.Type. * @@ -1001,14 +1061,14 @@ final class SqliteEamDb extends AbstractSqlEamDb { */ @Override public void updateCorrelationType(CorrelationAttribute.Type aType) throws EamDbException { - try{ + try { acquireExclusiveLock(); super.updateCorrelationType(aType); } finally { releaseExclusiveLock(); - } - } - + } + } + /** * Get the EamArtifact.Type that has the given Type.Id. * @@ -1020,73 +1080,76 @@ final class SqliteEamDb extends AbstractSqlEamDb { */ @Override public CorrelationAttribute.Type getCorrelationTypeById(int typeId) throws EamDbException { - try{ + try { acquireSharedLock(); return super.getCorrelationTypeById(typeId); } finally { releaseSharedLock(); - } - } - + } + } + /** * Upgrade the schema of the database (if needed) - * @throws EamDbException + * + * @throws EamDbException */ @Override public void upgradeSchema() throws EamDbException, SQLException { - try{ + try { acquireExclusiveLock(); super.upgradeSchema(); } finally { releaseExclusiveLock(); - } + } } - + /** - * Gets an exclusive lock (if applicable). - * Will return the lock if successful, null if unsuccessful because locking - * isn't supported, and throw an exception if we should have been able to get the - * lock but failed (meaning the database is in use). + * Gets an exclusive lock (if applicable). Will return the lock if + * successful, null if unsuccessful because locking isn't supported, and + * throw an exception if we should have been able to get the lock but failed + * (meaning the database is in use). + * * @return the lock, or null if locking is not supported - * @throws EamDbException if the coordination service is running but we fail to get the lock + * @throws EamDbException if the coordination service is running but we fail + * to get the lock */ @Override - public CoordinationService.Lock getExclusiveMultiUserDbLock() throws EamDbException{ + public CoordinationService.Lock getExclusiveMultiUserDbLock() throws EamDbException { // Multiple users are not supported for SQLite return null; } - + /** - * Acquire the lock that provides exclusive access to the case database. - * Call this method in a try block with a call to - * the lock release method in an associated finally block. + * Acquire the lock that provides exclusive access to the case database. + * Call this method in a try block with a call to the lock release method in + * an associated finally block. */ private void acquireExclusiveLock() { rwLock.writeLock().lock(); } /** - * Release the lock that provides exclusive access to the database. - * This method should always be called in the finally - * block of a try block in which the lock was acquired. + * Release the lock that provides exclusive access to the database. This + * method should always be called in the finally block of a try block in + * which the lock was acquired. */ private void releaseExclusiveLock() { rwLock.writeLock().unlock(); } /** - * Acquire the lock that provides shared access to the case database. - * Call this method in a try block with a call to the - * lock release method in an associated finally block. + * Acquire the lock that provides shared access to the case database. Call + * this method in a try block with a call to the lock release method in an + * associated finally block. */ private void acquireSharedLock() { rwLock.readLock().lock(); } /** - * Release the lock that provides shared access to the database. - * This method should always be called in the finally block - * of a try block in which the lock was acquired. + * Release the lock that provides shared access to the database. This method + * should always be called in the finally block of a try block in which the + * lock was acquired. */ private void releaseSharedLock() { rwLock.readLock().unlock(); diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/AbstractCommonAttributeInstance.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AbstractCommonAttributeInstance.java new file mode 100644 index 0000000000..7732c5d393 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AbstractCommonAttributeInstance.java @@ -0,0 +1,163 @@ +/* + * + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.commonfilesearch; + +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; +import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Represents an instance in either the CaseDB or CR that had a common match. + * Different implementations know how to get the instance details from either + * CaseDB or CR. + * + * Defines leaf-type nodes used in the Common Files Search results tree. Leaf + * nodes, may describe common attributes which exist in the current case DB or + * in the Central Repo. When a reference to the AbstractFile is lacking (such as + * in the case that a common attribute is found in the Central Repo) not all + * features of the Content Viewer, and context menu can be supported. Thus, + * multiple types of leaf nodes are required to represent Common Attribute + * Instance nodes. + */ +public abstract class AbstractCommonAttributeInstance { + + private final Long abstractFileObjectId; + private final String caseName; + private final String dataSource; + + /** + * Create a leaf node for attributes found in files in the current case db. + * + * @param abstractFileReference file from which the common attribute was + * found + * @param cachedFiles storage for abstract files which have been used + * already so we can avoid extra roundtrips to the case db + * @param dataSource datasource where this attribute appears + * @param caseName case where this attribute appears + */ + AbstractCommonAttributeInstance(Long abstractFileReference, String dataSource, String caseName) { + this.abstractFileObjectId = abstractFileReference; + this.caseName = caseName; + this.dataSource = dataSource; + } + + /** + * Create a leaf node for attributes found in the central repo and not + * available in the current data case. + * + * @param cachedFiles storage for abstract files which have been used + * already so we can avoid extra roundtrips to the case db + */ + AbstractCommonAttributeInstance() { + this.abstractFileObjectId = -1L; + this.caseName = ""; + this.dataSource = ""; + } + + /** + * Get an AbstractFile for this instance if it can be retrieved from the + * CaseDB. + * + * @return AbstractFile corresponding to this common attribute or null if it + * cannot be found (for example, in the event that this is a central repo + * file) + */ + abstract AbstractFile getAbstractFile(); + + /** + * Create a list of leaf nodes, to be used to display a row in the tree + * table + * + * @return leaf nodes for tree + */ + abstract DisplayableItemNode[] generateNodes(); + + /** + * The name of the case where this common attribute is found. + * + * @return case name + */ + String getCaseName() { + return this.caseName; + } + + /** + * Get string name of the data source where this common attribute appears. + * + * @return data source name + */ + public String getDataSource() { + + /** + * Even though we could get this from the CR record or the AbstractFile, + * we want to avoid getting it from the AbstractFile because it would be + * an extra database roundtrip. + */ + return this.dataSource; + } + + /** + * ObjectId of the AbstractFile that is equivalent to the file from which + * this common attribute instance + * + * @return the abstractFileObjectId + */ + public Long getAbstractFileObjectId() { + return abstractFileObjectId; + } + + /** + * Use this to create an AbstractCommonAttributeInstanceNode of the + * appropriate type. In any case, we'll get something which extends + * DisplayableItemNode which can be used to populate the tree. + * + * If the common attribute in question could be derived from an AbstractFile + * in the present SleuthkitCase, we can use an + * IntraCaseCommonAttributeInstanceNode which enables extended functionality + * in the context menu and in the content viewer. + * + * Otherwise, we will get an InterCaseCommonAttributeInstanceNode which + * supports only baseline functionality. + * + * @param attributeInstance common file attribute instance form the central + * repo + * @param abstractFile an AbstractFile from which the attribute instance was + * found - applies to CaseDbCommonAttributeInstance only + * @param currentCaseName + * @return the appropriate leaf node for the results tree + * @throws TskCoreException + */ + static DisplayableItemNode createNode(CorrelationAttribute attribute, AbstractFile abstractFile, String currentCaseName) throws TskCoreException { + + DisplayableItemNode leafNode; + CorrelationAttributeInstance attributeInstance = attribute.getInstances().get(0); + + if (abstractFile == null) { + leafNode = new CentralRepoCommonAttributeInstanceNode(attributeInstance); + } else { + final String abstractFileDataSourceName = abstractFile.getDataSource().getName(); + leafNode = new CaseDBCommonAttributeInstanceNode(abstractFile, currentCaseName, abstractFileDataSourceName); + } + + return leafNode; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/AbstractCommonAttributeSearcher.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AbstractCommonAttributeSearcher.java new file mode 100644 index 0000000000..97456bda04 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AbstractCommonAttributeSearcher.java @@ -0,0 +1,212 @@ +/* + * + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.commonfilesearch; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Prototype for an object which finds files with common attributes. + * Subclass this and implement findFiles in order + */ +public abstract class AbstractCommonAttributeSearcher { + + private final Map dataSourceIdToNameMap; + private boolean filterByMedia; + private boolean filterByDoc; + + AbstractCommonAttributeSearcher(Map dataSourceIdMap, boolean filterByMedia, boolean filterByDoc){ + this.filterByDoc = filterByDoc; + this.filterByMedia = filterByMedia; + this.dataSourceIdToNameMap = dataSourceIdMap; + } + + Map getDataSourceIdToNameMap(){ + return Collections.unmodifiableMap(this.dataSourceIdToNameMap); + } + + /** + * Implement this to search for files with common attributes. Creates an + * object (CommonAttributeSearchResults) which contains all of the information + * required to display a tree view in the UI. The view will contain 3 layers: + * a top level node, indicating the number matches each of it's children possess, + * a mid level node indicating the matched attribute, + * @return + * @throws TskCoreException + * @throws NoCurrentCaseException + * @throws SQLException + * @throws EamDbException + */ + public abstract CommonAttributeSearchResults findFiles() throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException; + + /** + * Implement this to create a descriptive string for the tab which will display + * this data. + * @return an informative string + */ + @NbBundle.Messages({ + "AbstractCommonFilesMetadataBuilder.buildTabTitle.titleIntraAll=Common Files (All Data Sources, %s)", + "AbstractCommonFilesMetadataBuilder.buildTabTitle.titleIntraSingle=Common Files (Data Source: %s, %s)", + "AbstractCommonFilesMetadataBuilder.buildTabTitle.titleInterAll=Common Files (All Central Repository Cases, %s)", + "AbstractCommonFilesMetadataBuilder.buildTabTitle.titleInterSingle=Common Files (Central Repository Case: %s, %s)", + }) + abstract String buildTabTitle(); + + @NbBundle.Messages({ + "AbstractCommonFilesMetadataBuilder.buildCategorySelectionString.doc=Documents", + "AbstractCommonFilesMetadataBuilder.buildCategorySelectionString.media=Media", + "AbstractCommonFilesMetadataBuilder.buildCategorySelectionString.all=All File Categories" + }) + String buildCategorySelectionString() { + if (!this.isFilterByDoc() && !this.isFilterByMedia()) { + return Bundle.AbstractCommonFilesMetadataBuilder_buildCategorySelectionString_all(); + } else { + List filters = new ArrayList<>(); + if (this.isFilterByDoc()) { + filters.add(Bundle.AbstractCommonFilesMetadataBuilder_buildCategorySelectionString_doc()); + } + if (this.isFilterByMedia()) { + filters.add(Bundle.AbstractCommonFilesMetadataBuilder_buildCategorySelectionString_media()); + } + return String.join(", ", filters); + } + } + + static Map> collateMatchesByNumberOfInstances(Map commonFiles) { + //collate matches by number of matching instances - doing this in sql doesnt seem efficient + Map> instanceCollatedCommonFiles = new TreeMap<>(); + for(CommonAttributeValue md5Metadata : commonFiles.values()){ + Integer size = md5Metadata.getInstanceCount(); + + if(instanceCollatedCommonFiles.containsKey(size)){ + instanceCollatedCommonFiles.get(size).add(md5Metadata); + } else { + ArrayList value = new ArrayList<>(); + value.add(md5Metadata); + instanceCollatedCommonFiles.put(size, value); + } + } + return instanceCollatedCommonFiles; + } + + /* + * The set of the MIME types that will be checked for extension mismatches + * when checkType is ONLY_MEDIA. + * ".jpg", ".jpeg", ".png", ".psd", ".nef", ".tiff", ".bmp", ".tec" + * ".aaf", ".3gp", ".asf", ".avi", ".m1v", ".m2v", //NON-NLS + * ".m4v", ".mp4", ".mov", ".mpeg", ".mpg", ".mpe", ".mp4", ".rm", ".wmv", ".mpv", ".flv", ".swf" + */ + static final Set MEDIA_PICS_VIDEO_MIME_TYPES = Stream.of( + "image/bmp", //NON-NLS + "image/gif", //NON-NLS + "image/jpeg", //NON-NLS + "image/png", //NON-NLS + "image/tiff", //NON-NLS + "image/vnd.adobe.photoshop", //NON-NLS + "image/x-raw-nikon", //NON-NLS + "image/x-ms-bmp", //NON-NLS + "image/x-icon", //NON-NLS + "video/webm", //NON-NLS + "video/3gpp", //NON-NLS + "video/3gpp2", //NON-NLS + "video/ogg", //NON-NLS + "video/mpeg", //NON-NLS + "video/mp4", //NON-NLS + "video/quicktime", //NON-NLS + "video/x-msvideo", //NON-NLS + "video/x-flv", //NON-NLS + "video/x-m4v", //NON-NLS + "video/x-ms-wmv", //NON-NLS + "application/vnd.ms-asf", //NON-NLS + "application/vnd.rn-realmedia", //NON-NLS + "application/x-shockwave-flash" //NON-NLS + ).collect(Collectors.toSet()); + + /* + * The set of the MIME types that will be checked for extension mismatches + * when checkType is ONLY_TEXT_FILES. + * ".doc", ".docx", ".odt", ".xls", ".xlsx", ".ppt", ".pptx" + * ".txt", ".rtf", ".log", ".text", ".xml" + * ".html", ".htm", ".css", ".js", ".php", ".aspx" + * ".pdf" + */ + static final Set TEXT_FILES_MIME_TYPES = Stream.of( + "text/plain", //NON-NLS + "application/rtf", //NON-NLS + "application/pdf", //NON-NLS + "text/css", //NON-NLS + "text/html", //NON-NLS + "text/csv", //NON-NLS + "application/json", //NON-NLS + "application/javascript", //NON-NLS + "application/xml", //NON-NLS + "text/calendar", //NON-NLS + "application/x-msoffice", //NON-NLS + "application/x-ooxml", //NON-NLS + "application/msword", //NON-NLS + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", //NON-NLS + "application/vnd.ms-powerpoint", //NON-NLS + "application/vnd.openxmlformats-officedocument.presentationml.presentation", //NON-NLS + "application/vnd.ms-excel", //NON-NLS + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", //NON-NLS + "application/vnd.oasis.opendocument.presentation", //NON-NLS + "application/vnd.oasis.opendocument.spreadsheet", //NON-NLS + "application/vnd.oasis.opendocument.text" //NON-NLS + ).collect(Collectors.toSet()); + + /** + * @return the filterByMedia + */ + boolean isFilterByMedia() { + return filterByMedia; + } + + /** + * @param filterByMedia the filterByMedia to set + */ + void setFilterByMedia(boolean filterByMedia) { + this.filterByMedia = filterByMedia; + } + + /** + * @return the filterByDoc + */ + boolean isFilterByDoc() { + return filterByDoc; + } + + /** + * @param filterByDoc the filterByDoc to set + */ + void setFilterByDoc(boolean filterByDoc) { + this.filterByDoc = filterByDoc; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/AllInterCaseCommonAttributeSearcher.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AllInterCaseCommonAttributeSearcher.java new file mode 100644 index 0000000000..ca436b7809 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AllInterCaseCommonAttributeSearcher.java @@ -0,0 +1,61 @@ +/* + * + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.commonfilesearch; + +import java.sql.SQLException; +import java.util.List; +import java.util.Map; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Algorithm which finds files anywhere in the Central Repo which also occur in + * present case. + */ +public class AllInterCaseCommonAttributeSearcher extends InterCaseCommonAttributeSearcher { + + /** + * + * @param filterByMediaMimeType match only on files whose mime types can be + * broadly categorized as media types + * @param filterByDocMimeType match only on files whose mime types can be + * broadly categorized as document types + * @throws EamDbException + */ + public AllInterCaseCommonAttributeSearcher(Map dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) throws EamDbException { + super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType); + } + + @Override + public CommonAttributeSearchResults findFiles() throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException { + InterCaseSearchResultsProcessor eamDbAttrInst = new InterCaseSearchResultsProcessor(this.getDataSourceIdToNameMap()); + Map> interCaseCommonFiles = eamDbAttrInst.findInterCaseCommonAttributeValues(Case.getCurrentCase()); + return new CommonAttributeSearchResults(interCaseCommonFiles); + } + + @Override + String buildTabTitle() { + final String buildCategorySelectionString = this.buildCategorySelectionString(); + final String titleTemplate = Bundle.AbstractCommonFilesMetadataBuilder_buildTabTitle_titleInterAll(); + return String.format(titleTemplate, new Object[]{buildCategorySelectionString}); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/AllDataSourcesCommonFilesAlgorithm.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AllIntraCaseCommonAttributeSearcher.java similarity index 79% rename from Core/src/org/sleuthkit/autopsy/commonfilesearch/AllDataSourcesCommonFilesAlgorithm.java rename to Core/src/org/sleuthkit/autopsy/commonfilesearch/AllIntraCaseCommonAttributeSearcher.java index 111cf4aed9..eed61b0fc1 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/AllDataSourcesCommonFilesAlgorithm.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AllIntraCaseCommonAttributeSearcher.java @@ -25,9 +25,9 @@ import org.sleuthkit.datamodel.TskData.FileKnown; /** * Provides logic for selecting common files from all data sources. */ -final public class AllDataSourcesCommonFilesAlgorithm extends CommonFilesMetadataBuilder { +final public class AllIntraCaseCommonAttributeSearcher extends IntraCaseCommonAttributeSearcher { - private static final String WHERE_CLAUSE = "%s md5 in (select md5 from tsk_files where (known != "+ FileKnown.KNOWN.getFileKnownValue() + " OR known IS NULL)%s GROUP BY md5 HAVING COUNT(DISTINCT data_source_obj_id) > 1) order by md5"; //NON-NLS + private static final String WHERE_CLAUSE = "%s md5 in (select md5 from tsk_files where (known != "+ FileKnown.KNOWN.getFileKnownValue() + " OR known IS NULL)%s GROUP BY md5 HAVING COUNT(DISTINCT data_source_obj_id) > 1) order by md5"; //NON-NLS /** * Implements the algorithm for getting common files across all data @@ -37,7 +37,7 @@ final public class AllDataSourcesCommonFilesAlgorithm extends CommonFilesMetadat * @param filterByMediaMimeType match only on files whose mime types can be broadly categorized as media types * @param filterByDocMimeType match only on files whose mime types can be broadly categorized as document types */ - public AllDataSourcesCommonFilesAlgorithm(Map dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) { + public AllIntraCaseCommonAttributeSearcher(Map dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) { super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType); } @@ -49,9 +49,9 @@ final public class AllDataSourcesCommonFilesAlgorithm extends CommonFilesMetadat } @Override - protected String buildTabTitle() { + String buildTabTitle() { final String buildCategorySelectionString = this.buildCategorySelectionString(); - final String titleTemplate = Bundle.CommonFilesMetadataBuilder_buildTabTitle_titleAll(); + final String titleTemplate = Bundle.AbstractCommonFilesMetadataBuilder_buildTabTitle_titleIntraAll(); return String.format(titleTemplate, new Object[]{buildCategorySelectionString}); } } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/Bundle.properties b/Core/src/org/sleuthkit/autopsy/commonfilesearch/Bundle.properties index a2b42155d7..23598648ea 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/Bundle.properties @@ -1,15 +1,25 @@ -CommonFilesPanel.searchButton.text=Search -CommonFilesPanel.withinDataSourceRadioButton.text=At least one instance of a given MD5 must appear in the data source selected below: -CommonFilesPanel.allDataSourcesRadioButton.text=Files can be in any data source -CommonFilesPanel.cancelButton.text=Cancel -CommonFilesPanel.cancelButton.actionCommand=Cancel -CommonFilesPanel.selectedFileCategoriesButton.text=Match on the following file categories: -CommonFilesPanel.selectedFileCategoriesButton.toolTipText=Select from the options below... -CommonFilesPanel.pictureVideoCheckbox.text=Pictures and Videos -CommonFilesPanel.documentsCheckbox.text=Documents CommonFilesPanel.commonFilesSearchLabel.text=Find files in multiple data sources in the current case. -CommonFilesPanel.allFileCategoriesRadioButton.toolTipText=No filtering applied to results... -CommonFilesPanel.allFileCategoriesRadioButton.text=Match on all file types CommonFilesPanel.text=Indicate which data sources to consider while searching for duplicates: -CommonFilesPanel.categoriesLabel.text=Indicate which file types to include in results: -CommonFilesPanel.errorText.text=In order to search, you must select a file category. +CommonFilesPanel.jRadioButton1.text=jRadioButton1 +CommonFilesPanel.jRadioButton2.text=With previous cases in the Central Repository +CommonFilesPanel.intraCaseRadio.label=Correlate within current case only +CommonFilesPanel.interCaseRadio.label=Correlate amongst all known cases (uses Central Repo) +IntraCasePanel.allDataSourcesRadioButton.text=Matches may be from any data source +IntraCasePanel.withinDataSourceRadioButton.text=At least one match must appear in the data source selected below: +IntraCasePanel.selectDataSourceComboBox.actionCommand= +InterCasePanel.specificCentralRepoCaseRadio.text=Matches must be from the following Central Repo case: +InterCasePanel.anyCentralRepoCaseRadio.text=Matches may be from any Central Repo case +CommonAttributePanel.selectedFileCategoriesButton.toolTipText=Select from the options below... +CommonAttributePanel.selectedFileCategoriesButton.text=Only the selected file types: +CommonAttributePanel.allFileCategoriesRadioButton.toolTipText=No filtering applied to results... +CommonAttributePanel.allFileCategoriesRadioButton.text=All file types +CommonAttributePanel.cancelButton.actionCommand=Cancel +CommonAttributePanel.cancelButton.text=Cancel +CommonAttributePanel.searchButton.text=Search +CommonAttributePanel.commonFilesSearchLabel2.text=Scope of Search +CommonAttributePanel.intraCaseRadio.text=Within current case +CommonAttributePanel.commonFilesSearchLabel1.text=Find common files to correlate data soures or cases. +CommonAttributePanel.errorText.text=In order to search, you must select a file category. +CommonAttributePanel.categoriesLabel.text=File Types To Include: +CommonAttributePanel.documentsCheckbox.text=Documents +CommonAttributePanel.pictureVideoCheckbox.text=Pictures and Videos diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CaseDBCommonAttributeInstance.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CaseDBCommonAttributeInstance.java new file mode 100644 index 0000000000..9221a5dc86 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CaseDBCommonAttributeInstance.java @@ -0,0 +1,73 @@ +/* + * + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.commonfilesearch; + +import java.util.Arrays; +import java.util.logging.Level; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Encapsulates data required to instantiate a FileInstanceNode for an instance in the CaseDB + */ +final public class CaseDBCommonAttributeInstance extends AbstractCommonAttributeInstance { + + private static final Logger LOGGER = Logger.getLogger(CaseDBCommonAttributeInstance.class.getName()); + + + /** + * Create meta data required to find an abstract file and build a + * FileInstanceNode. + * + * @param objectId id of abstract file to find + * @param dataSourceName name of datasource where the object is found + */ + CaseDBCommonAttributeInstance(Long abstractFileReference, String dataSource, String caseName) { + super(abstractFileReference, dataSource, caseName); + } + + @Override + public DisplayableItemNode[] generateNodes() { + final CaseDBCommonAttributeInstanceNode intraCaseCommonAttributeInstanceNode = new CaseDBCommonAttributeInstanceNode(this.getAbstractFile(), this.getCaseName(), this.getDataSource()); + return Arrays.asList(intraCaseCommonAttributeInstanceNode).toArray(new DisplayableItemNode[1]); + } + + @Override + AbstractFile getAbstractFile() { + + Case currentCase; + try { + currentCase = Case.getCurrentCaseThrows(); + + SleuthkitCase tskDb = currentCase.getSleuthkitCase(); + + return tskDb.findAllFilesWhere(String.format("obj_id in (%s)", this.getAbstractFileObjectId())).get(0); + + } catch (TskCoreException | NoCurrentCaseException ex) { + LOGGER.log(Level.SEVERE, String.format("Unable to find AbstractFile for record with obj_id: %s. Node not created.", new Object[]{this.getAbstractFileObjectId()}), ex); + return null; + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/FileInstanceNode.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CaseDBCommonAttributeInstanceNode.java similarity index 81% rename from Core/src/org/sleuthkit/autopsy/commonfilesearch/FileInstanceNode.java rename to Core/src/org/sleuthkit/autopsy/commonfilesearch/CaseDBCommonAttributeInstanceNode.java index 899aa5abbc..f105d72498 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/FileInstanceNode.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CaseDBCommonAttributeInstanceNode.java @@ -20,18 +20,18 @@ package org.sleuthkit.autopsy.commonfilesearch; import org.apache.commons.lang3.StringUtils; import org.openide.nodes.Sheet; -import org.openide.util.NbBundle; import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor; import org.sleuthkit.autopsy.datamodel.FileNode; import org.sleuthkit.autopsy.datamodel.NodeProperty; import org.sleuthkit.datamodel.AbstractFile; /** - * Used by the Common Files search feature to encapsulate instances of a given - MD5s matched in the search. These nodes will be children of Md5Nodes. + * Node that wraps CaseDBCommonAttributeInstance to represent a file instance stored + * in the CaseDB. */ -public class FileInstanceNode extends FileNode { +public class CaseDBCommonAttributeInstanceNode extends FileNode { + private final String caseName; private final String dataSource; /** @@ -41,11 +41,10 @@ public class FileInstanceNode extends FileNode { * @param fsContent * @param dataSource */ - public FileInstanceNode(AbstractFile fsContent, String dataSource) { + public CaseDBCommonAttributeInstanceNode(AbstractFile fsContent, String caseName, String dataSource) { super(fsContent); + this.caseName = caseName; this.dataSource = dataSource; - - this.setDisplayName(fsContent.getName()); } @Override @@ -59,11 +58,14 @@ public class FileInstanceNode extends FileNode { return visitor.visit(this); } - String getDataSource() { + public String getCase(){ + return this.caseName; + } + + public String getDataSource() { return this.dataSource; } - @NbBundle.Messages({"FileInstanceNode.createSheet.noDescription= "}) @Override protected Sheet createSheet() { Sheet sheet = new Sheet(); @@ -73,13 +75,14 @@ public class FileInstanceNode extends FileNode { sheet.put(sheetSet); } - final String NO_DESCR = Bundle.FileInstanceNode_createSheet_noDescription(); + final String NO_DESCR = Bundle.CommonFilesSearchResultsViewerTable_noDescText(); sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), NO_DESCR, this.getContent().getName())); sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), NO_DESCR, this.getContent().getParentPath())); sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), NO_DESCR, getHashSetHitsCsvList(this.getContent()))); sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), NO_DESCR, this.getDataSource())); sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_mimeTypeColLbl(), Bundle.CommonFilesSearchResultsViewerTable_mimeTypeColLbl(), NO_DESCR, StringUtils.defaultString(this.getContent().getMIMEType()))); + sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_caseColLbl1(), Bundle.CommonFilesSearchResultsViewerTable_caseColLbl1(), NO_DESCR, caseName)); this.addTagProperty(sheetSet); diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CentralRepoCommonAttributeInstance.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CentralRepoCommonAttributeInstance.java new file mode 100644 index 0000000000..0e70722b6d --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CentralRepoCommonAttributeInstance.java @@ -0,0 +1,137 @@ +/* + * + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.commonfilesearch; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Represents that a row in the CR was found in multiple cases. + * + * Generates a DisplayableItmeNode using a CentralRepositoryFile. + */ +final public class CentralRepoCommonAttributeInstance extends AbstractCommonAttributeInstance { + + private static final Logger LOGGER = Logger.getLogger(CentralRepoCommonAttributeInstance.class.getName()); + private final Integer crFileId; + private CorrelationAttribute currentAttribute; + private final Map dataSourceNameToIdMap; + + CentralRepoCommonAttributeInstance(Integer attrInstId, Map dataSourceIdToNameMap) { + super(); + this.crFileId = attrInstId; + this.dataSourceNameToIdMap = invertMap(dataSourceIdToNameMap); + } + + void setCurrentAttributeInst(CorrelationAttribute attribute) { + this.currentAttribute = attribute; + } + + @Override + AbstractFile getAbstractFile() { + + Case currentCase; + if (this.currentAttribute != null) { + + final CorrelationAttributeInstance currentAttributeInstance = this.currentAttribute.getInstances().get(0); + + String currentFullPath = currentAttributeInstance.getFilePath(); + String currentDataSource = currentAttributeInstance.getCorrelationDataSource().getName(); + + + if(this.dataSourceNameToIdMap.containsKey(currentDataSource)){ + Long dataSourceObjectId = this.dataSourceNameToIdMap.get(currentDataSource); + + try { + currentCase = Case.getCurrentCaseThrows(); + + SleuthkitCase tskDb = currentCase.getSleuthkitCase(); + + File fileFromPath = new File(currentFullPath); + 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); + List potentialAbstractFiles = tskDb.findAllFilesWhere(whereClause); + + if(potentialAbstractFiles.isEmpty()){ + return null; + } else if(potentialAbstractFiles.size() > 1){ + LOGGER.log(Level.WARNING, String.format("Unable to find an exact match for AbstractFile for record with filePath: %s. May have returned the wrong file.", new Object[]{currentFullPath})); + return potentialAbstractFiles.get(0); + } else { + 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 { + return null; + } + } + return null; + } + + @Override + public DisplayableItemNode[] generateNodes() { + + // @@@ We should be doing more of this work in teh generateKeys method. We want to do as little as possible in generateNodes + InterCaseSearchResultsProcessor eamDbAttrInst = new InterCaseSearchResultsProcessor(); + CorrelationAttribute corrAttr = eamDbAttrInst.findSingleCorrelationAttribute(crFileId); + List attrInstNodeList = new ArrayList<>(0); + String currCaseDbName = Case.getCurrentCase().getDisplayName(); + + try { + this.setCurrentAttributeInst(corrAttr); + + AbstractFile abstractFileForAttributeInstance = this.getAbstractFile(); + DisplayableItemNode generatedInstNode = AbstractCommonAttributeInstance.createNode(corrAttr, abstractFileForAttributeInstance, currCaseDbName); + attrInstNodeList.add(generatedInstNode); + + } catch (TskCoreException ex) { + LOGGER.log(Level.SEVERE, String.format("Unable to get DataSource for record with md5: %s. Node not created.", new Object[]{corrAttr.getCorrelationValue()}), ex); + } + + return attrInstNodeList.toArray(new DisplayableItemNode[attrInstNodeList.size()]); + } + + private Map invertMap(Map dataSourceIdToNameMap) { + HashMap invertedMap = new HashMap<>(); + for (Map.Entry entry : dataSourceIdToNameMap.entrySet()){ + invertedMap.put(entry.getValue(), entry.getKey()); + } + return invertedMap; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CentralRepoCommonAttributeInstanceNode.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CentralRepoCommonAttributeInstanceNode.java new file mode 100644 index 0000000000..e0b8e928d7 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CentralRepoCommonAttributeInstanceNode.java @@ -0,0 +1,116 @@ +/* + * + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.commonfilesearch; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import javax.swing.Action; +import org.openide.nodes.Children; +import org.openide.nodes.Sheet; +import org.openide.util.lookup.Lookups; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; +import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; +import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor; +import org.sleuthkit.autopsy.datamodel.NodeProperty; + +/** + * Used by the Common Files search feature to encapsulate instances of a given + * MD5s matched in the search. These nodes will be children of Md5Nodes. + * + * Use this type for files which are not in the current case, but from the + * Central Repo. Contrast with SleuthkitCase which should be used + * when the FileInstance was found in the case presently open in Autopsy. + */ +public class CentralRepoCommonAttributeInstanceNode extends DisplayableItemNode { + + private final CorrelationAttributeInstance crFile; + + CentralRepoCommonAttributeInstanceNode(CorrelationAttributeInstance content) { + super(Children.LEAF, Lookups.fixed(content)); + this.crFile = content; + this.setDisplayName(new File(this.crFile.getFilePath()).getName()); + } + + public CorrelationAttributeInstance getCorrelationAttributeInstance(){ + return this.crFile; + } + + @Override + public Action[] getActions(boolean context){ + List actionsList = new ArrayList<>(); + + actionsList.addAll(Arrays.asList(super.getActions(true))); + + return actionsList.toArray(new Action[actionsList.size()]); + } + + @Override + public T accept(DisplayableItemNodeVisitor visitor) { + return visitor.visit(this); + } + + @Override + public boolean isLeafTypeNode() { + return true; + } + + @Override + public String getItemType() { + //objects of type FileNode will co-occur in the treetable with objects + // of this type and they will need to provide the same key + return CaseDBCommonAttributeInstanceNode.class.getName(); + } + + @Override + protected Sheet createSheet(){ + Sheet sheet = new Sheet(); + Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES); + + if(sheetSet == null){ + sheetSet = Sheet.createPropertiesSet(); + sheet.put(sheetSet); + } + + final CorrelationAttributeInstance centralRepoFile = this.getCorrelationAttributeInstance(); + + final String fullPath = centralRepoFile.getFilePath(); + final File file = new File(fullPath); + + final String caseName = centralRepoFile.getCorrelationCase().getDisplayName(); + + final String name = file.getName(); + final String parent = file.getParent(); + + final String dataSourceName = centralRepoFile.getCorrelationDataSource().getName(); + + final String NO_DESCR = Bundle.CommonFilesSearchResultsViewerTable_noDescText(); + + sheetSet.put(new NodeProperty<>(org.sleuthkit.autopsy.commonfilesearch.Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), org.sleuthkit.autopsy.commonfilesearch.Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), NO_DESCR, name)); + sheetSet.put(new NodeProperty<>(org.sleuthkit.autopsy.commonfilesearch.Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), org.sleuthkit.autopsy.commonfilesearch.Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), NO_DESCR, parent)); + sheetSet.put(new NodeProperty<>(org.sleuthkit.autopsy.commonfilesearch.Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), org.sleuthkit.autopsy.commonfilesearch.Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), NO_DESCR, "")); + sheetSet.put(new NodeProperty<>(org.sleuthkit.autopsy.commonfilesearch.Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), org.sleuthkit.autopsy.commonfilesearch.Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), NO_DESCR, dataSourceName)); + sheetSet.put(new NodeProperty<>(org.sleuthkit.autopsy.commonfilesearch.Bundle.CommonFilesSearchResultsViewerTable_mimeTypeColLbl(), org.sleuthkit.autopsy.commonfilesearch.Bundle.CommonFilesSearchResultsViewerTable_mimeTypeColLbl(), NO_DESCR, "")); + sheetSet.put(new NodeProperty<>(org.sleuthkit.autopsy.commonfilesearch.Bundle.CommonFilesSearchResultsViewerTable_caseColLbl1(), org.sleuthkit.autopsy.commonfilesearch.Bundle.CommonFilesSearchResultsViewerTable_caseColLbl1(), NO_DESCR, caseName)); + + return sheet; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form new file mode 100644 index 0000000000..c08c3c2d78 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.form @@ -0,0 +1,301 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java new file mode 100644 index 0000000000..bf1765e310 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributePanel.java @@ -0,0 +1,720 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.commonfilesearch; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import java.util.logging.Level; +import javax.swing.JFrame; +import javax.swing.SwingUtilities; +import javax.swing.SwingWorker; +import org.netbeans.api.progress.ProgressHandle; +import org.openide.explorer.ExplorerManager; +import org.openide.util.NbBundle; +import org.openide.windows.WindowManager; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer; +import org.sleuthkit.autopsy.corecomponents.DataResultTopComponent; +import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable; +import org.sleuthkit.autopsy.corecomponents.TableFilterNode; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; +import org.sleuthkit.autopsy.directorytree.DataResultFilterNode; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Panel used for common files search configuration and configuration business + * logic. Nested within CommonFilesDialog. + */ +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives +public final class CommonAttributePanel extends javax.swing.JDialog { + + private static final long serialVersionUID = 1L; + + private static final Long NO_DATA_SOURCE_SELECTED = -1L; + + private static final Logger LOGGER = Logger.getLogger(CommonAttributePanel.class.getName()); + private boolean pictureViewCheckboxState; + private boolean documentsCheckboxState; + + /** + * Creates new form CommonFilesPanel + */ + @NbBundle.Messages({ + "CommonFilesPanel.title=Common Files Panel", + "CommonFilesPanel.exception=Unexpected Exception loading DataSources.", + "CommonFilesPanel.frame.title=Find Common Files", + "CommonFilesPanel.frame.msg=Find Common Files"}) + public CommonAttributePanel() { + super(new JFrame(Bundle.CommonFilesPanel_frame_title()), + Bundle.CommonFilesPanel_frame_msg(), true); + initComponents(); + this.setLocationRelativeTo(WindowManager.getDefault().getMainWindow()); + this.errorText.setVisible(false); + this.setupDataSources(); + + if (CommonAttributePanel.isEamDbAvailable()) { + this.setupCases(); + } else { + this.disableIntercaseSearch(); + } + } + + private static boolean isEamDbAvailable() { + try { + return EamDb.isEnabled() && + EamDb.getInstance() != null && + EamDb.getInstance().getCases().size() > 1 && + Case.isCaseOpen() && + Case.getCurrentCase() != null && + EamDb.getInstance().getCase(Case.getCurrentCase()) != null; + } catch (EamDbException ex) { + LOGGER.log(Level.SEVERE, "Unexpected exception while checking for EamDB enabled.", ex); + } + return false; + } + + private void disableIntercaseSearch() { + this.intraCaseRadio.setSelected(true); + this.interCaseRadio.setEnabled(false); + } + + @NbBundle.Messages({ + "CommonFilesPanel.search.results.titleAll=Common Files (All Data Sources)", + "CommonFilesPanel.search.results.titleSingle=Common Files (Match Within Data Source: %s)", + "CommonFilesPanel.search.results.pathText=Common Files Search Results", + "CommonFilesPanel.search.done.searchProgressGathering=Gathering Common Files Search Results.", + "CommonFilesPanel.search.done.searchProgressDisplay=Displaying Common Files Search Results.", + "CommonFilesPanel.search.done.tskCoreException=Unable to run query against DB.", + "CommonFilesPanel.search.done.noCurrentCaseException=Unable to open case file.", + "CommonFilesPanel.search.done.exception=Unexpected exception running Common Files Search.", + "CommonFilesPanel.search.done.interupted=Something went wrong finding common files.", + "CommonFilesPanel.search.done.sqlException=Unable to query db for files or data sources."}) + private void search() { + String pathText = Bundle.CommonFilesPanel_search_results_pathText(); + + new SwingWorker() { + + private String tabTitle; + private ProgressHandle progress; + + private void setTitleForAllDataSources() { + this.tabTitle = Bundle.CommonFilesPanel_search_results_titleAll(); + } + + private void setTitleForSingleSource(Long dataSourceId) { + final String CommonFilesPanel_search_results_titleSingle = Bundle.CommonFilesPanel_search_results_titleSingle(); + final Object[] dataSourceName = new Object[]{intraCasePanel.getDataSourceMap().get(dataSourceId)}; + + this.tabTitle = String.format(CommonFilesPanel_search_results_titleSingle, dataSourceName); + } + + @Override + @SuppressWarnings({"BoxedValueEquality", "NumberEquality"}) + protected CommonAttributeSearchResults doInBackground() throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException { + progress = ProgressHandle.createHandle(Bundle.CommonFilesPanel_search_done_searchProgressGathering()); + progress.start(); + progress.switchToIndeterminate(); + + Long dataSourceId = intraCasePanel.getSelectedDataSourceId(); + Integer caseId = interCasePanel.getSelectedCaseId(); + + AbstractCommonAttributeSearcher builder; + CommonAttributeSearchResults metadata; + + boolean filterByMedia = false; + boolean filterByDocuments = false; + if (selectedFileCategoriesButton.isSelected()) { + if (pictureVideoCheckbox.isSelected()) { + filterByMedia = true; + } + if (documentsCheckbox.isSelected()) { + filterByDocuments = true; + } + } + + if (CommonAttributePanel.this.interCaseRadio.isSelected()) { + + if (caseId == InterCasePanel.NO_CASE_SELECTED) { + builder = new AllInterCaseCommonAttributeSearcher(intraCasePanel.getDataSourceMap(), filterByMedia, filterByDocuments); + } else { + builder = new SingleInterCaseCommonAttributeSearcher(caseId, intraCasePanel.getDataSourceMap(), filterByMedia, filterByDocuments); + } + } else { + if (dataSourceId == CommonAttributePanel.NO_DATA_SOURCE_SELECTED) { + builder = new AllIntraCaseCommonAttributeSearcher(intraCasePanel.getDataSourceMap(), filterByMedia, filterByDocuments); + + setTitleForAllDataSources(); + } else { + builder = new SingleIntraCaseCommonAttributeSearcher(dataSourceId, intraCasePanel.getDataSourceMap(), filterByMedia, filterByDocuments); + + setTitleForSingleSource(dataSourceId); + } + } + metadata = builder.findFiles(); + this.tabTitle = builder.buildTabTitle(); + + return metadata; + } + + @Override + protected void done() { + try { + super.done(); + + CommonAttributeSearchResults metadata = this.get(); + + CommonAttributeSearchResultRootNode commonFilesNode = new CommonAttributeSearchResultRootNode(metadata); + + // -3969 + DataResultFilterNode dataResultFilterNode = new DataResultFilterNode(commonFilesNode, ExplorerManager.find(CommonAttributePanel.this)); + + TableFilterNode tableFilterWithDescendantsNode = new TableFilterNode(dataResultFilterNode, 3); + + DataResultViewerTable table = new CommonAttributesSearchResultsViewerTable(); + + Collection viewers = new ArrayList<>(1); + viewers.add(table); + + progress.setDisplayName(Bundle.CommonFilesPanel_search_done_searchProgressDisplay()); + DataResultTopComponent.createInstance(tabTitle, pathText, tableFilterWithDescendantsNode, metadata.size(), viewers); + progress.finish(); + + } catch (InterruptedException ex) { + LOGGER.log(Level.SEVERE, "Interrupted while loading Common Files", ex); + MessageNotifyUtil.Message.error(Bundle.CommonFilesPanel_search_done_interupted()); + } catch (ExecutionException ex) { + String errorMessage; + Throwable inner = ex.getCause(); + if (inner instanceof TskCoreException) { + LOGGER.log(Level.SEVERE, "Failed to load files from database.", ex); + errorMessage = Bundle.CommonFilesPanel_search_done_tskCoreException(); + } else if (inner instanceof NoCurrentCaseException) { + LOGGER.log(Level.SEVERE, "Current case has been closed.", ex); + errorMessage = Bundle.CommonFilesPanel_search_done_noCurrentCaseException(); + } else if (inner instanceof SQLException) { + LOGGER.log(Level.SEVERE, "Unable to query db for files.", ex); + errorMessage = Bundle.CommonFilesPanel_search_done_sqlException(); + } else { + LOGGER.log(Level.SEVERE, "Unexpected exception while running Common Files Search.", ex); + errorMessage = Bundle.CommonFilesPanel_search_done_exception(); + } + MessageNotifyUtil.Message.error(errorMessage); + } + } + }.execute(); + } + + + /** + * Sets up the data sources dropdown and returns the data sources map for + * future usage. + * + * @return a mapping of data correlationCase ids to data correlationCase + * names + */ + @NbBundle.Messages({ + "CommonFilesPanel.setupDataSources.done.tskCoreException=Unable to run query against DB.", + "CommonFilesPanel.setupDataSources.done.noCurrentCaseException=Unable to open case file.", + "CommonFilesPanel.setupDataSources.done.exception=Unexpected exception loading data sources.", + "CommonFilesPanel.setupDataSources.done.interupted=Something went wrong building the Common Files Search dialog box.", + "CommonFilesPanel.setupDataSources.done.sqlException=Unable to query db for data sources.", + "CommonFilesPanel.setupDataSources.updateUi.noDataSources=No data sources were found."}) + private void setupDataSources() { + + new SwingWorker, Void>() { + + private void updateUi() { + + final Map dataSourceMap = CommonAttributePanel.this.intraCasePanel.getDataSourceMap(); + + String[] dataSourcesNames = new String[dataSourceMap.size()]; + + //only enable all this stuff if we actually have datasources + if (dataSourcesNames.length > 0) { + dataSourcesNames = dataSourceMap.values().toArray(dataSourcesNames); + CommonAttributePanel.this.intraCasePanel.setDataModel(new DataSourceComboBoxModel(dataSourcesNames)); + + boolean multipleDataSources = this.caseHasMultipleSources(); + CommonAttributePanel.this.intraCasePanel.rigForMultipleDataSources(multipleDataSources); + + //TODO this should be attached to the intra/inter radio buttons + CommonAttributePanel.this.setSearchButtonEnabled(true); + } + } + + private boolean caseHasMultipleSources() { + return CommonAttributePanel.this.intraCasePanel.getDataSourceMap().size() > 2; + } + + @Override + protected Map doInBackground() throws NoCurrentCaseException, TskCoreException, SQLException { + DataSourceLoader loader = new DataSourceLoader(); + return loader.getDataSourceMap(); + } + + @Override + protected void done() { + + try { + CommonAttributePanel.this.intraCasePanel.setDataSourceMap(this.get()); + updateUi(); + + } catch (InterruptedException ex) { + LOGGER.log(Level.SEVERE, "Interrupted while building Common Files Search dialog.", ex); + MessageNotifyUtil.Message.error(Bundle.CommonFilesPanel_setupDataSources_done_interupted()); + } catch (ExecutionException ex) { + String errorMessage; + Throwable inner = ex.getCause(); + if (inner instanceof TskCoreException) { + LOGGER.log(Level.SEVERE, "Failed to load data sources from database.", ex); + errorMessage = Bundle.CommonFilesPanel_setupDataSources_done_tskCoreException(); + } else if (inner instanceof NoCurrentCaseException) { + LOGGER.log(Level.SEVERE, "Current case has been closed.", ex); + errorMessage = Bundle.CommonFilesPanel_setupDataSources_done_noCurrentCaseException(); + } else if (inner instanceof SQLException) { + LOGGER.log(Level.SEVERE, "Unable to query db for data sources.", ex); + errorMessage = Bundle.CommonFilesPanel_setupDataSources_done_sqlException(); + } else { + LOGGER.log(Level.SEVERE, "Unexpected exception while building Common Files Search dialog panel.", ex); + errorMessage = Bundle.CommonFilesPanel_setupDataSources_done_exception(); + } + MessageNotifyUtil.Message.error(errorMessage); + } + } + }.execute(); + } + + @NbBundle.Messages({ + "CommonFilesPanel.setupCases.done.interruptedException=Something went wrong building the Common Files Search dialog box.", + "CommonFilesPanel.setupCases.done.exeutionException=Unexpected exception loading cases."}) + private void setupCases() { + + new SwingWorker, Void>() { + + private void updateUi() { + + final Map caseMap = CommonAttributePanel.this.interCasePanel.getCaseMap(); + + String[] caseNames = new String[caseMap.size()]; + + if (caseNames.length > 0) { + caseNames = caseMap.values().toArray(caseNames); + CommonAttributePanel.this.interCasePanel.setCaseList(new DataSourceComboBoxModel(caseNames)); + + boolean multipleCases = this.centralRepoHasMultipleCases(); + CommonAttributePanel.this.interCasePanel.rigForMultipleCases(multipleCases); + + } else { + CommonAttributePanel.this.disableIntercaseSearch(); + } + } + + private Map mapDataSources(List cases) throws EamDbException { + Map casemap = new HashMap<>(); + CorrelationCase currentCorCase = EamDb.getInstance().getCase(Case.getCurrentCase()); + for (CorrelationCase correlationCase : cases) { + if (currentCorCase.getID() != correlationCase.getID()) { // if not the current Case + casemap.put(correlationCase.getID(), correlationCase.getDisplayName()); + } + } + + return casemap; + } + + @Override + protected Map doInBackground() throws EamDbException { + + List dataSources = EamDb.getInstance().getCases(); + Map caseMap = mapDataSources(dataSources); + + return caseMap; + } + + @Override + protected void done() { + try { + Map cases = this.get(); + CommonAttributePanel.this.interCasePanel.setCaseMap(cases); + this.updateUi(); + } catch (InterruptedException ex) { + LOGGER.log(Level.SEVERE, "Interrupted while building Common Files Search dialog.", ex); + MessageNotifyUtil.Message.error(Bundle.CommonFilesPanel_setupCases_done_interruptedException()); + } catch (ExecutionException ex) { + LOGGER.log(Level.SEVERE, "Unexpected exception while building Common Files Search dialog.", ex); + MessageNotifyUtil.Message.error(Bundle.CommonFilesPanel_setupCases_done_exeutionException()); + } + } + + private boolean centralRepoHasMultipleCases() { + return CommonAttributePanel.this.interCasePanel.centralRepoHasMultipleCases(); + } + + }.execute(); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + fileTypeFilterButtonGroup = new javax.swing.ButtonGroup(); + interIntraButtonGroup = new javax.swing.ButtonGroup(); + jPanel1 = new javax.swing.JPanel(); + commonFilesSearchLabel2 = new javax.swing.JLabel(); + searchButton = new javax.swing.JButton(); + cancelButton = new javax.swing.JButton(); + allFileCategoriesRadioButton = new javax.swing.JRadioButton(); + selectedFileCategoriesButton = new javax.swing.JRadioButton(); + pictureVideoCheckbox = new javax.swing.JCheckBox(); + documentsCheckbox = new javax.swing.JCheckBox(); + categoriesLabel = new javax.swing.JLabel(); + errorText = new javax.swing.JLabel(); + commonFilesSearchLabel1 = new javax.swing.JLabel(); + intraCaseRadio = new javax.swing.JRadioButton(); + interCaseRadio = new javax.swing.JRadioButton(); + layoutPanel = new java.awt.Panel(); + intraCasePanel = new org.sleuthkit.autopsy.commonfilesearch.IntraCasePanel(); + interCasePanel = new org.sleuthkit.autopsy.commonfilesearch.InterCasePanel(); + + setMinimumSize(new java.awt.Dimension(412, 350)); + setPreferredSize(new java.awt.Dimension(412, 350)); + setResizable(false); + addWindowListener(new java.awt.event.WindowAdapter() { + public void windowClosed(java.awt.event.WindowEvent evt) { + formWindowClosed(evt); + } + }); + + jPanel1.setPreferredSize(new java.awt.Dimension(412, 350)); + + org.openide.awt.Mnemonics.setLocalizedText(commonFilesSearchLabel2, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.commonFilesSearchLabel2.text")); // NOI18N + commonFilesSearchLabel2.setFocusable(false); + + org.openide.awt.Mnemonics.setLocalizedText(searchButton, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.searchButton.text")); // NOI18N + searchButton.setEnabled(false); + searchButton.setHorizontalTextPosition(javax.swing.SwingConstants.LEADING); + searchButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + searchButtonActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(cancelButton, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.cancelButton.text")); // NOI18N + cancelButton.setActionCommand(org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.cancelButton.actionCommand")); // NOI18N + cancelButton.setHorizontalTextPosition(javax.swing.SwingConstants.LEADING); + cancelButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + cancelButtonActionPerformed(evt); + } + }); + + fileTypeFilterButtonGroup.add(allFileCategoriesRadioButton); + org.openide.awt.Mnemonics.setLocalizedText(allFileCategoriesRadioButton, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.allFileCategoriesRadioButton.text")); // NOI18N + allFileCategoriesRadioButton.setToolTipText(org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.allFileCategoriesRadioButton.toolTipText")); // NOI18N + allFileCategoriesRadioButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + allFileCategoriesRadioButtonActionPerformed(evt); + } + }); + + fileTypeFilterButtonGroup.add(selectedFileCategoriesButton); + selectedFileCategoriesButton.setSelected(true); + org.openide.awt.Mnemonics.setLocalizedText(selectedFileCategoriesButton, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.selectedFileCategoriesButton.text")); // NOI18N + selectedFileCategoriesButton.setToolTipText(org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.selectedFileCategoriesButton.toolTipText")); // NOI18N + selectedFileCategoriesButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + selectedFileCategoriesButtonActionPerformed(evt); + } + }); + + pictureVideoCheckbox.setSelected(true); + org.openide.awt.Mnemonics.setLocalizedText(pictureVideoCheckbox, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.pictureVideoCheckbox.text")); // NOI18N + pictureVideoCheckbox.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + pictureVideoCheckboxActionPerformed(evt); + } + }); + + documentsCheckbox.setSelected(true); + org.openide.awt.Mnemonics.setLocalizedText(documentsCheckbox, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.documentsCheckbox.text")); // NOI18N + documentsCheckbox.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + documentsCheckboxActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(categoriesLabel, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.categoriesLabel.text")); // NOI18N + categoriesLabel.setName(""); // NOI18N + + errorText.setForeground(new java.awt.Color(255, 0, 0)); + org.openide.awt.Mnemonics.setLocalizedText(errorText, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.errorText.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(commonFilesSearchLabel1, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.commonFilesSearchLabel1.text")); // NOI18N + commonFilesSearchLabel1.setFocusable(false); + + interIntraButtonGroup.add(intraCaseRadio); + intraCaseRadio.setSelected(true); + org.openide.awt.Mnemonics.setLocalizedText(intraCaseRadio, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonAttributePanel.intraCaseRadio.text")); // NOI18N + intraCaseRadio.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + intraCaseRadioActionPerformed(evt); + } + }); + + interIntraButtonGroup.add(interCaseRadio); + org.openide.awt.Mnemonics.setLocalizedText(interCaseRadio, org.openide.util.NbBundle.getMessage(CommonAttributePanel.class, "CommonFilesPanel.jRadioButton2.text")); // NOI18N + interCaseRadio.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + interCaseRadioActionPerformed(evt); + } + }); + + layoutPanel.setLayout(new java.awt.CardLayout()); + layoutPanel.add(intraCasePanel, "card3"); + layoutPanel.add(interCasePanel, "card2"); + + javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1); + jPanel1.setLayout(jPanel1Layout); + jPanel1Layout.setHorizontalGroup( + jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup() + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup() + .addContainerGap() + .addComponent(searchButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(cancelButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(errorText)) + .addGroup(jPanel1Layout.createSequentialGroup() + .addContainerGap() + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(commonFilesSearchLabel2) + .addComponent(intraCaseRadio) + .addComponent(interCaseRadio) + .addComponent(commonFilesSearchLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(categoriesLabel) + .addComponent(selectedFileCategoriesButton))) + .addGroup(jPanel1Layout.createSequentialGroup() + .addGap(35, 35, 35) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(documentsCheckbox) + .addComponent(pictureVideoCheckbox))) + .addGroup(jPanel1Layout.createSequentialGroup() + .addContainerGap() + .addComponent(allFileCategoriesRadioButton))) + .addContainerGap()) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup() + .addGap(20, 20, 20) + .addComponent(layoutPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(10, 10, 10))) + ); + jPanel1Layout.setVerticalGroup( + jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup() + .addContainerGap() + .addComponent(commonFilesSearchLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(commonFilesSearchLabel2) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(intraCaseRadio) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(interCaseRadio) + .addGap(79, 79, 79) + .addComponent(categoriesLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(selectedFileCategoriesButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(pictureVideoCheckbox) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(documentsCheckbox) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(allFileCategoriesRadioButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(searchButton) + .addComponent(cancelButton) + .addComponent(errorText)) + .addContainerGap()) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup() + .addGap(98, 98, 98) + .addComponent(layoutPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(180, 180, 180))) + ); + + getContentPane().add(jPanel1, java.awt.BorderLayout.CENTER); + }// //GEN-END:initComponents + + private void searchButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchButtonActionPerformed + search(); + SwingUtilities.windowForComponent(this).dispose(); + }//GEN-LAST:event_searchButtonActionPerformed + + private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed + SwingUtilities.windowForComponent(this).dispose(); + }//GEN-LAST:event_cancelButtonActionPerformed + + private void allFileCategoriesRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_allFileCategoriesRadioButtonActionPerformed + this.manageCheckBoxState(); + this.toggleErrorTextAndSearchBox(); + }//GEN-LAST:event_allFileCategoriesRadioButtonActionPerformed + + private void selectedFileCategoriesButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_selectedFileCategoriesButtonActionPerformed + this.manageCheckBoxState(); + }//GEN-LAST:event_selectedFileCategoriesButtonActionPerformed + + private void pictureVideoCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pictureVideoCheckboxActionPerformed + this.toggleErrorTextAndSearchBox(); + }//GEN-LAST:event_pictureVideoCheckboxActionPerformed + + private void documentsCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_documentsCheckboxActionPerformed + this.toggleErrorTextAndSearchBox(); + }//GEN-LAST:event_documentsCheckboxActionPerformed + + private void intraCaseRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_intraCaseRadioActionPerformed + ((java.awt.CardLayout) this.layoutPanel.getLayout()).first(this.layoutPanel); + handleIntraCaseSearchCriteriaChanged(); + }//GEN-LAST:event_intraCaseRadioActionPerformed + + public void handleIntraCaseSearchCriteriaChanged() { + if (this.areIntraCaseSearchCriteriaMet()) { + this.searchButton.setEnabled(true); + this.hideErrorMessages(); + } else { + this.searchButton.setEnabled(false); + this.hideErrorMessages(); + this.showIntraCaseErrorMessage(); + } + } + + private void interCaseRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_interCaseRadioActionPerformed + ((java.awt.CardLayout) this.layoutPanel.getLayout()).last(this.layoutPanel); + handleInterCaseSearchCriteriaChanged(); + }//GEN-LAST:event_interCaseRadioActionPerformed + + private void formWindowClosed(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowClosed + SwingUtilities.windowForComponent(this).dispose(); + }//GEN-LAST:event_formWindowClosed + + public void handleInterCaseSearchCriteriaChanged() { + if (this.areInterCaseSearchCriteriaMet()) { + this.searchButton.setEnabled(true); + this.hideErrorMessages(); + } else { + this.searchButton.setEnabled(false); + this.hideErrorMessages(); + this.showInterCaseErrorMessage(); + } + } + + private void toggleErrorTextAndSearchBox() { + if (!this.pictureVideoCheckbox.isSelected() && !this.documentsCheckbox.isSelected() && !this.allFileCategoriesRadioButton.isSelected()) { + this.searchButton.setEnabled(false); + this.errorText.setVisible(true); + } else { + this.searchButton.setEnabled(true); + this.errorText.setVisible(false); + } + } + + private void manageCheckBoxState() { + + this.pictureViewCheckboxState = this.pictureVideoCheckbox.isSelected(); + this.documentsCheckboxState = this.documentsCheckbox.isSelected(); + + if (this.allFileCategoriesRadioButton.isSelected()) { + this.pictureVideoCheckbox.setEnabled(false); + this.documentsCheckbox.setEnabled(false); + } + + if (this.selectedFileCategoriesButton.isSelected()) { + + this.pictureVideoCheckbox.setSelected(this.pictureViewCheckboxState); + this.documentsCheckbox.setSelected(this.documentsCheckboxState); + + this.pictureVideoCheckbox.setEnabled(true); + this.documentsCheckbox.setEnabled(true); + + this.toggleErrorTextAndSearchBox(); + } + } + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JRadioButton allFileCategoriesRadioButton; + private javax.swing.JButton cancelButton; + private javax.swing.JLabel categoriesLabel; + private javax.swing.JLabel commonFilesSearchLabel1; + private javax.swing.JLabel commonFilesSearchLabel2; + private javax.swing.JCheckBox documentsCheckbox; + private javax.swing.JLabel errorText; + private javax.swing.ButtonGroup fileTypeFilterButtonGroup; + private org.sleuthkit.autopsy.commonfilesearch.InterCasePanel interCasePanel; + private javax.swing.JRadioButton interCaseRadio; + private javax.swing.ButtonGroup interIntraButtonGroup; + private org.sleuthkit.autopsy.commonfilesearch.IntraCasePanel intraCasePanel; + private javax.swing.JRadioButton intraCaseRadio; + private javax.swing.JPanel jPanel1; + private java.awt.Panel layoutPanel; + private javax.swing.JCheckBox pictureVideoCheckbox; + private javax.swing.JButton searchButton; + private javax.swing.JRadioButton selectedFileCategoriesButton; + // End of variables declaration//GEN-END:variables + + void setSearchButtonEnabled(boolean enabled) { + this.searchButton.setEnabled(enabled); + } + + private boolean areIntraCaseSearchCriteriaMet() { + return this.intraCasePanel.areSearchCriteriaMet(); + } + + private boolean areInterCaseSearchCriteriaMet() { + return this.interCasePanel.areSearchCriteriaMet(); + } + + private void hideErrorMessages() { + this.errorText.setVisible(false); + } + + private void showIntraCaseErrorMessage() { + this.errorText.setText(this.intraCasePanel.getErrorMessage()); + this.errorText.setVisible(true); + } + + private void showInterCaseErrorMessage() { + this.errorText.setText(this.interCasePanel.getErrorMessage()); + this.errorText.setVisible(true); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesNode.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResultRootNode.java similarity index 68% rename from Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesNode.java rename to Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResultRootNode.java index a07aa59d36..8e33401af7 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesNode.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResultRootNode.java @@ -27,13 +27,14 @@ import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor; /** - * Wrapper node for Md5Node used to display common files search - * results in the top right pane. Calls InstanceCountNodeFactory. + * Top-level node to store common file search results. Current structure is: + * - node for number of matches + * -- node for MD5/commmon attribute + * --- node for instance. */ -final public class CommonFilesNode extends DisplayableItemNode { - +final public class CommonAttributeSearchResultRootNode extends DisplayableItemNode { - CommonFilesNode(CommonFilesMetadata metadataList) { + CommonAttributeSearchResultRootNode(CommonAttributeSearchResults metadataList) { super(Children.create(new InstanceCountNodeFactory(metadataList), true)); } @@ -64,27 +65,27 @@ final public class CommonFilesNode extends DisplayableItemNode { */ static class InstanceCountNodeFactory extends ChildFactory{ - private final CommonFilesMetadata metadata; + private final CommonAttributeSearchResults searchResults; /** - * Build a factory which converts a CommonFilesMetadata + * Build a factory which converts a CommonAttributeSearchResults * object into DisplayableItemNodes. - * @param metadata + * @param searchResults */ - InstanceCountNodeFactory(CommonFilesMetadata metadata){ - this.metadata = metadata; + InstanceCountNodeFactory(CommonAttributeSearchResults searchResults){ + this.searchResults = searchResults; } - + @Override protected boolean createKeys(List list) { - list.addAll(this.metadata.getMetadata().keySet()); + list.addAll(this.searchResults.getMetadata().keySet()); return true; } @Override protected Node createNodeForKey(Integer instanceCount){ - List md5Metadata = this.metadata.getMetadataForMd5(instanceCount); - return new InstanceCountNode(instanceCount, md5Metadata); - } + List attributeValues = this.searchResults.getAttributeValuesForInstanceCount(instanceCount); + return new InstanceCountNode(instanceCount, attributeValues); + } } } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResults.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResults.java new file mode 100644 index 0000000000..8ff13e87f1 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeSearchResults.java @@ -0,0 +1,82 @@ +/* + * + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.commonfilesearch; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * Stores the results from the various types of common attribute searching + * Stores results based on how they are currently displayed in the UI + */ +final public class CommonAttributeSearchResults { + + // maps instance count to list of attribute values. + private final Map> instanceCountToAttributeValues; + + /** + * Create a values object which can be handed off to the node factories. + * + * @param values list of CommonAttributeValue indexed by size of + * CommonAttributeValue + */ + CommonAttributeSearchResults(Map> metadata){ + this.instanceCountToAttributeValues = metadata; + } + + /** + * Find the child node whose children have the specified number of children. + * + * This is a convenience method - you can also iterate over + * getValues(). + * + * @param isntanceCound key + * @return list of values which represent matches + */ + List getAttributeValuesForInstanceCount(Integer instanceCount) { + return this.instanceCountToAttributeValues.get(instanceCount); + } + + /** + * Get an unmodifiable collection of values, indexed by number of + * grandchildren, which represents the common attributes found in the + * search. + * @return map of sizes of children to list of matches + */ +public Map> getMetadata() { + return Collections.unmodifiableMap(this.instanceCountToAttributeValues); + } + + /** + * How many distinct common files exist for this search results? + * @return number of common files + */ + public int size() { + + int count = 0; + for (List data : this.instanceCountToAttributeValues.values()) { + for(CommonAttributeValue md5 : data){ + count += md5.getInstanceCount(); + } + } + return count; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/Md5Metadata.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValue.java similarity index 54% rename from Core/src/org/sleuthkit/autopsy/commonfilesearch/Md5Metadata.java rename to Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValue.java index 39c2cf8040..2e9dd4675a 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/Md5Metadata.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValue.java @@ -19,50 +19,70 @@ */ package org.sleuthkit.autopsy.commonfilesearch; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; /** - * Encapsulates data required to instantiate an Md5Node. + * Defines a value that was in the common file search results + * as well as information about its instances. */ -final public class Md5Metadata { - +final public class CommonAttributeValue { + private final String md5; - private final List fileInstances; - - Md5Metadata(String md5, List fileInstances){ + private final List fileInstances; + + CommonAttributeValue(String md5, List fileInstances) { this.md5 = md5; this.fileInstances = fileInstances; } - - public String getMd5(){ + + CommonAttributeValue(String md5) { + this.md5 = md5; + this.fileInstances = new ArrayList<>(); + } + + public String getValue() { return this.md5; } - - void addFileInstanceMetadata(FileInstanceMetadata metadata){ - this.fileInstances.add(metadata); - } - - public Collection getMetadata(){ - return Collections.unmodifiableCollection(this.fileInstances); - } - + /** - * How many distinct file instances exist for the MD5 represented by this object? - * @return number of instances + * concatenate cases this value was seen into a single string + * + * @return */ - public int size(){ - return this.fileInstances.size(); + public String getCases() { + return this.fileInstances.stream().map(AbstractCommonAttributeInstance::getCaseName).collect(Collectors.joining(", ")); } public String getDataSources() { - Set sources = new HashSet<> (); - for(FileInstanceMetadata data : this.fileInstances){ - sources.add(data.getDataSourceName()); + Set sources = new HashSet<>(); + for (AbstractCommonAttributeInstance data : this.fileInstances) { + sources.add(data.getDataSource()); } + return String.join(", ", sources); } + + void addInstance(AbstractCommonAttributeInstance metadata) { + this.fileInstances.add(metadata); + } + + public Collection getInstances() { + return Collections.unmodifiableCollection(this.fileInstances); + } + + /** + * How many distinct file instances exist for the MD5 represented by this + * object? + * + * @return number of instances + */ + public int getInstanceCount() { + return this.fileInstances.size(); + } } diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/Md5Node.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValueNode.java similarity index 64% rename from Core/src/org/sleuthkit/autopsy/commonfilesearch/Md5Node.java rename to Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValueNode.java index 0b10d73e30..c7c5dea58b 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/Md5Node.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributeValueNode.java @@ -20,34 +20,24 @@ package org.sleuthkit.autopsy.commonfilesearch; import java.util.List; -import java.util.logging.Level; import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.nodes.Sheet; import org.openide.util.NbBundle; -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor; import org.sleuthkit.autopsy.datamodel.NodeProperty; -import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.SleuthkitCase; -import org.sleuthkit.datamodel.TskCoreException; /** - * Represents a common files match - two or more files which appear to be the - * same file and appear as children of this node. This node will simply contain - * the MD5 of the matched files, the data sources those files were found within, - * and a count of the instances represented by the md5. + * Represents the layer in the tree for the value (such as MD5) that was in multiple places. + * Children are instances of that value. */ -public class Md5Node extends DisplayableItemNode { - - private static final Logger LOGGER = Logger.getLogger(Md5Node.class.getName()); - - private final String md5Hash; +public class CommonAttributeValueNode extends DisplayableItemNode { + + private final String value; private final int commonFileCount; + private final String cases; private final String dataSources; @NbBundle.Messages({ @@ -57,15 +47,17 @@ public class Md5Node extends DisplayableItemNode { * Create a Match node whose children will all have this object in common. * @param data the common feature, and the children */ - public Md5Node(Md5Metadata data) { + public CommonAttributeValueNode(CommonAttributeValue data) { super(Children.create( new FileInstanceNodeFactory(data), true)); - this.commonFileCount = data.size(); + this.commonFileCount = data.getInstanceCount(); + this.cases = data.getCases(); + // @@ We seem to be doing this string concat twice. We also do it in getDataSources() this.dataSources = String.join(", ", data.getDataSources()); - this.md5Hash = data.getMd5(); + this.value = data.getValue(); - this.setDisplayName(String.format(Bundle.Md5Node_Md5Node_format(), this.md5Hash)); + this.setDisplayName(String.format(Bundle.Md5Node_Md5Node_format(), this.value)); this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/fileset-icon-16.png"); //NON-NLS } @@ -76,6 +68,10 @@ public class Md5Node extends DisplayableItemNode { int getCommonFileCount() { return this.commonFileCount; } + + String getCases(){ + return this.cases; + } /** * Datasources where these matches occur. @@ -89,8 +85,8 @@ public class Md5Node extends DisplayableItemNode { * MD5 which is common to these matches * @return string md5 hash */ - public String getMd5() { - return this.md5Hash; + public String getValue() { + return this.value; } @NbBundle.Messages({"Md5Node.createSheet.noDescription= "}) @@ -129,35 +125,28 @@ public class Md5Node extends DisplayableItemNode { } /** - * Child generator for FileInstanceNode of Md5Node. + * Child generator for SleuthkitCaseFileInstanceNode of + * CommonAttributeValueNode. */ - static class FileInstanceNodeFactory extends ChildFactory { + static class FileInstanceNodeFactory extends ChildFactory { - private final Md5Metadata descendants; + private final CommonAttributeValue descendants; - FileInstanceNodeFactory(Md5Metadata descendants) { + FileInstanceNodeFactory(CommonAttributeValue descendants) { this.descendants = descendants; } @Override - protected Node createNodeForKey(FileInstanceMetadata file) { - try { - Case currentCase = Case.getCurrentCaseThrows(); - SleuthkitCase tskDb = currentCase.getSleuthkitCase(); - AbstractFile abstractFile = tskDb.findAllFilesWhere(String.format("obj_id in (%s)", file.getObjectId())).get(0); - - return new FileInstanceNode(abstractFile, file.getDataSourceName()); - } catch (NoCurrentCaseException | TskCoreException ex) { - LOGGER.log(Level.SEVERE, String.format("Unable to create node for file with obj_id: %s.", new Object[]{file.getObjectId()}), ex); - } - return null; - } - - @Override - protected boolean createKeys(List list) { - list.addAll(this.descendants.getMetadata()); + protected boolean createKeys(List list) { + list.addAll(this.descendants.getInstances()); return true; } - } + + @Override + protected Node[] createNodesForKey(AbstractCommonAttributeInstance searchResult) { + return searchResult.generateNodes(); + } -} \ No newline at end of file + + } +} diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesSearchResultsViewerTable.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributesSearchResultsViewerTable.java similarity index 86% rename from Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesSearchResultsViewerTable.java rename to Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributesSearchResultsViewerTable.java index cacbdd83f9..c7478f3fc4 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesSearchResultsViewerTable.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonAttributesSearchResultsViewerTable.java @@ -31,18 +31,18 @@ import org.openide.util.NbBundle; import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable; /** - * DataResultViewerTable which overrides the default column header - * width calculations. The CommonFilesSearchResultsViewerTable - * presents multiple tiers of data which are not always present and it may not - * make sense to try to calculate the column widths for such tables by sampling - * rows and looking for wide cells. Rather, we just pick some reasonable values. + * DataResultViewerTable which overrides the default column + * header width calculations. The CommonAttributesSearchResultsViewerTable + * presents multiple tiers of data which are not always present and it may not + * make sense to try to calculate the column widths for such tables by sampling + * rows and looking for wide cells. Rather, we just pick some reasonable values. */ -public class CommonFilesSearchResultsViewerTable extends DataResultViewerTable { +public class CommonAttributesSearchResultsViewerTable extends DataResultViewerTable { private static final Map COLUMN_WIDTHS; private static final long serialVersionUID = 1L; - private static final Logger LOGGER = Logger.getLogger(CommonFilesSearchResultsViewerTable.class.getName()); + private static final Logger LOGGER = Logger.getLogger(CommonAttributesSearchResultsViewerTable.class.getName()); private static final int DEFAULT_WIDTH = 100; @@ -51,6 +51,7 @@ public class CommonFilesSearchResultsViewerTable extends DataResultViewerTable { map.put(Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), 260); map.put(Bundle.CommonFilesSearchResultsViewerTable_instancesColLbl(), 65); map.put(Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), 300); + map.put(Bundle.CommonFilesSearchResultsViewerTable_caseColLbl1(), 200); map.put(Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), 200); map.put(Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), 100); map.put(Bundle.CommonFilesSearchResultsViewerTable_mimeTypeColLbl(), 130); @@ -60,11 +61,13 @@ public class CommonFilesSearchResultsViewerTable extends DataResultViewerTable { } @NbBundle.Messages({ + "CommonFilesSearchResultsViewerTable.noDescText= ", "CommonFilesSearchResultsViewerTable.filesColLbl=Files", "CommonFilesSearchResultsViewerTable.instancesColLbl=Instances", "CommonFilesSearchResultsViewerTable.pathColLbl=Parent Path", "CommonFilesSearchResultsViewerTable.hashsetHitsColLbl=Hash Set Hits", - "CommonFilesSearchResultsViewerTable.dataSourceColLbl=Data Source(s)", + "CommonFilesSearchResultsViewerTable.caseColLbl1=Case", + "CommonFilesSearchResultsViewerTable.dataSourceColLbl=Data Source", "CommonFilesSearchResultsViewerTable.mimeTypeColLbl=MIME Type", "CommonFilesSearchResultsViewerTable.tagsColLbl1=Tags" }) diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesDialog.form b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesDialog.form deleted file mode 100644 index 7a6e3dabe5..0000000000 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesDialog.form +++ /dev/null @@ -1,51 +0,0 @@ - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesDialog.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesDialog.java deleted file mode 100644 index 204b3136a9..0000000000 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesDialog.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2018 Basis Technology Corp. - * Contact: carrier sleuthkit org - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sleuthkit.autopsy.commonfilesearch; - -import javax.swing.JFrame; -import javax.swing.SwingUtilities; -import org.openide.util.NbBundle; -import org.openide.windows.WindowManager; - -/** - * Dialog box for configuring and running common files search. - */ -@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives -public final class CommonFilesDialog extends javax.swing.JDialog { - - private static final long serialVersionUID = 1L; - - /** - * Creates new form CommonFilesDialog - */ - @NbBundle.Messages({ - "CommonFilesDialog.frame.title=Find Common Files", - "CommonFilesDialog.frame.msg=Find Common Files"}) - public CommonFilesDialog() { - super(new JFrame(Bundle.CommonFilesDialog_frame_title()), - Bundle.CommonFilesDialog_frame_msg(), true); - initComponents(); - - this.setResizable(false); - this.setLocationRelativeTo(WindowManager.getDefault().getMainWindow()); - } - - /** - * This method is called from within the constructor to initialize the form. - * WARNING: Do NOT modify this code. The content of this method is always - * regenerated by the Form Editor. - */ - @SuppressWarnings("unchecked") - // //GEN-BEGIN:initComponents - private void initComponents() { - - commonFilesPanel1 = new org.sleuthkit.autopsy.commonfilesearch.CommonFilesPanel(); - - setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); - setSize(new java.awt.Dimension(340, 320)); - addWindowListener(new java.awt.event.WindowAdapter() { - public void windowClosed(java.awt.event.WindowEvent evt) { - formWindowClosed(evt); - } - }); - - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); - getContentPane().setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(commonFilesPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(0, 0, Short.MAX_VALUE)) - ); - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(commonFilesPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(0, 0, Short.MAX_VALUE)) - ); - - pack(); - setLocationRelativeTo(null); - }// //GEN-END:initComponents - - private void formWindowClosed(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowClosed - SwingUtilities.windowForComponent(this).dispose(); - }//GEN-LAST:event_formWindowClosed - - // Variables declaration - do not modify//GEN-BEGIN:variables - private org.sleuthkit.autopsy.commonfilesearch.CommonFilesPanel commonFilesPanel1; - // End of variables declaration//GEN-END:variables -} diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetadata.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetadata.java deleted file mode 100644 index b04792fb6b..0000000000 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetadata.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * - * Autopsy Forensic Browser - * - * Copyright 2018 Basis Technology Corp. - * Contact: carrier sleuthkit org - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sleuthkit.autopsy.commonfilesearch; - -import java.util.Collections; -import java.util.List; -import java.util.Map; - -/** - * Utility and wrapper model around data required for Common Files Search results. - * Subclass this to implement different selections of files from the case. - */ -final public class CommonFilesMetadata { - - private final Map> metadata; - - /** - * Create a metadata object which can be handed off to the node - * factories. - * - * @param metadata list of Md5Metadata indexed by size of Md5Metadata - */ - CommonFilesMetadata(Map> metadata){ - this.metadata = metadata; - } - - /** - * Find the meta data for the given md5. - * - * This is a convenience method - you can also iterate over - * getMetadata(). - * - * @param md5 key - * @return - */ - List getMetadataForMd5(Integer instanceCount) { - return this.metadata.get(instanceCount); - } - - public Map> getMetadata() { - return Collections.unmodifiableMap(this.metadata); - } - - /** - * How many distinct file instances exist for this metadata? - * @return number of file instances - */ - public int size() { - - int count = 0; - for (List data : this.metadata.values()) { - for(Md5Metadata md5 : data){ - count += md5.size(); - } - } - return count; - } -} diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetadataBuilder.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetadataBuilder.java deleted file mode 100644 index 0272278440..0000000000 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetadataBuilder.java +++ /dev/null @@ -1,277 +0,0 @@ -/* - * - * Autopsy Forensic Browser - * - * Copyright 2018 Basis Technology Corp. - * Contact: carrier sleuthkit org - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sleuthkit.autopsy.commonfilesearch; - -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import org.openide.util.NbBundle; -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.datamodel.HashUtility; -import org.sleuthkit.datamodel.SleuthkitCase; -import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery; -import org.sleuthkit.datamodel.TskCoreException; - -/** - * - * Generates a List when - * findCommonFiles() is called, which organizes files by md5 to - * prepare to display in viewer. - * - * This entire thing runs on a background thread where exceptions are handled. - */ -@SuppressWarnings("PMD.AbstractNaming") -public abstract class CommonFilesMetadataBuilder { - - private final Map dataSourceIdToNameMap; - private final boolean filterByMedia; - private final boolean filterByDoc; - 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 - - /* - * The set of the MIME types that will be checked for extension mismatches - * when checkType is ONLY_MEDIA. - * ".jpg", ".jpeg", ".png", ".psd", ".nef", ".tiff", ".bmp", ".tec" - * ".aaf", ".3gp", ".asf", ".avi", ".m1v", ".m2v", //NON-NLS - * ".m4v", ".mp4", ".mov", ".mpeg", ".mpg", ".mpe", ".mp4", ".rm", ".wmv", ".mpv", ".flv", ".swf" - */ - private static final Set MEDIA_PICS_VIDEO_MIME_TYPES = Stream.of( - "image/bmp", //NON-NLS - "image/gif", //NON-NLS - "image/jpeg", //NON-NLS - "image/png", //NON-NLS - "image/tiff", //NON-NLS - "image/vnd.adobe.photoshop", //NON-NLS - "image/x-raw-nikon", //NON-NLS - "image/x-ms-bmp", //NON-NLS - "image/x-icon", //NON-NLS - "video/webm", //NON-NLS - "video/3gpp", //NON-NLS - "video/3gpp2", //NON-NLS - "video/ogg", //NON-NLS - "video/mpeg", //NON-NLS - "video/mp4", //NON-NLS - "video/quicktime", //NON-NLS - "video/x-msvideo", //NON-NLS - "video/x-flv", //NON-NLS - "video/x-m4v", //NON-NLS - "video/x-ms-wmv", //NON-NLS - "application/vnd.ms-asf", //NON-NLS - "application/vnd.rn-realmedia", //NON-NLS - "application/x-shockwave-flash" //NON-NLS - ).collect(Collectors.toSet()); - - /* - * The set of the MIME types that will be checked for extension mismatches - * when checkType is ONLY_TEXT_FILES. - * ".doc", ".docx", ".odt", ".xls", ".xlsx", ".ppt", ".pptx" - * ".txt", ".rtf", ".log", ".text", ".xml" - * ".html", ".htm", ".css", ".js", ".php", ".aspx" - * ".pdf" - */ - private static final Set TEXT_FILES_MIME_TYPES = Stream.of( - "text/plain", //NON-NLS - "application/rtf", //NON-NLS - "application/pdf", //NON-NLS - "text/css", //NON-NLS - "text/html", //NON-NLS - "text/csv", //NON-NLS - "application/json", //NON-NLS - "application/javascript", //NON-NLS - "application/xml", //NON-NLS - "text/calendar", //NON-NLS - "application/x-msoffice", //NON-NLS - "application/x-ooxml", //NON-NLS - "application/msword", //NON-NLS - "application/vnd.openxmlformats-officedocument.wordprocessingml.document", //NON-NLS - "application/vnd.ms-powerpoint", //NON-NLS - "application/vnd.openxmlformats-officedocument.presentationml.presentation", //NON-NLS - "application/vnd.ms-excel", //NON-NLS - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", //NON-NLS - "application/vnd.oasis.opendocument.presentation", //NON-NLS - "application/vnd.oasis.opendocument.spreadsheet", //NON-NLS - "application/vnd.oasis.opendocument.text" //NON-NLS - ).collect(Collectors.toSet()); - - /** - * Subclass this to implement different algorithms for getting common files. - * - * @param dataSourceIdMap a map of obj_id to datasource name - * @param filterByMediaMimeType match only on files whose mime types can be - * broadly categorized as media types - * @param filterByDocMimeType match only on files whose mime types can be - * broadly categorized as document types - */ - CommonFilesMetadataBuilder(Map dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) { - dataSourceIdToNameMap = dataSourceIdMap; - filterByMedia = filterByMediaMimeType; - filterByDoc = filterByDocMimeType; - } - - /** - * Use this as a prefix when building the SQL select statement. - * - *
    - *
  • You only have to specify the WHERE clause if you use this.
  • - *
  • If you do not use this string, you must use at least the columns - * selected below, in that order.
  • - *
- */ - static final String SELECT_PREFIX = "SELECT obj_id, md5, data_source_obj_id from tsk_files where"; //NON-NLS - - /** - * Should build a SQL SELECT statement to be passed to - * SleuthkitCase.executeQuery(sql) which will select the desired file ids - * and MD5 hashes. - * - * The statement should select obj_id, md5, data_source_obj_id in that - * order. - * - * @return sql string select statement - */ - protected abstract String buildSqlSelectStatement(); - - /** - * Generate a meta data object which encapsulates everything need to add the - * tree table tab to the top component. - * - * @return a data object with all of the matched files in a hierarchical - * format - * @throws TskCoreException - * @throws NoCurrentCaseException - * @throws SQLException - */ - public CommonFilesMetadata findCommonFiles() throws TskCoreException, NoCurrentCaseException, SQLException { - - Map commonFiles = new HashMap<>(); - - SleuthkitCase sleuthkitCase = Case.getCurrentCaseThrows().getSleuthkitCase(); - String selectStatement = this.buildSqlSelectStatement(); - - try ( - CaseDbQuery query = sleuthkitCase.executeQuery(selectStatement); - ResultSet resultSet = query.getResultSet()) { - - while (resultSet.next()) { - Long objectId = resultSet.getLong(1); - String md5 = resultSet.getString(2); - Long dataSourceId = resultSet.getLong(3); - String dataSource = this.dataSourceIdToNameMap.get(dataSourceId); - - if (md5 == null || HashUtility.isNoDataMd5(md5)) { - continue; - } - - if (commonFiles.containsKey(md5)) { - final Md5Metadata md5Metadata = commonFiles.get(md5); - md5Metadata.addFileInstanceMetadata(new FileInstanceMetadata(objectId, dataSource)); - } else { - final List fileInstances = new ArrayList<>(); - fileInstances.add(new FileInstanceMetadata(objectId, dataSource)); - Md5Metadata md5Metadata = new Md5Metadata(md5, fileInstances); - commonFiles.put(md5, md5Metadata); - } - } - } - - Map> instanceCollatedCommonFiles = new TreeMap<>(); - - for(Md5Metadata md5Metadata : commonFiles.values()){ - Integer size = md5Metadata.size(); - - if(instanceCollatedCommonFiles.containsKey(size)){ - instanceCollatedCommonFiles.get(size).add(md5Metadata); - } else { - ArrayList value = new ArrayList<>(); - value.add(md5Metadata); - instanceCollatedCommonFiles.put(size, value); - } - } - - return new CommonFilesMetadata(instanceCollatedCommonFiles); - } - - /** - * Should be used by subclasses, in their - * buildSqlSelectStatement() function to create an SQL boolean - * expression which will filter our matches based on mime type. The - * expression will be conjoined to base query with an AND operator. - * - * @return sql fragment of the form: - * 'and "mime_type" in ( [comma delimited list of mime types] )' - * or empty string in the event that no types to filter on were given. - */ - String determineMimeTypeFilter() { - - Set mimeTypesToFilterOn = new HashSet<>(); - String mimeTypeString = ""; - if (filterByMedia) { - mimeTypesToFilterOn.addAll(MEDIA_PICS_VIDEO_MIME_TYPES); - } - if (filterByDoc) { - mimeTypesToFilterOn.addAll(TEXT_FILES_MIME_TYPES); - } - StringBuilder mimeTypeFilter = new StringBuilder(mimeTypesToFilterOn.size()); - if (!mimeTypesToFilterOn.isEmpty()) { - for (String mimeType : mimeTypesToFilterOn) { - mimeTypeFilter.append("'").append(mimeType).append("',"); - } - mimeTypeString = mimeTypeFilter.toString().substring(0, mimeTypeFilter.length() - 1); - mimeTypeString = String.format(FILTER_BY_MIME_TYPES_WHERE_CLAUSE, new Object[]{mimeTypeString}); - } - return mimeTypeString; - } - - @NbBundle.Messages({ - "CommonFilesMetadataBuilder.buildTabTitle.titleAll=Common Files (All Data Sources, %s)", - "CommonFilesMetadataBuilder.buildTabTitle.titleSingle=Common Files (Match Within Data Source: %s, %s)" - }) - protected abstract String buildTabTitle(); - - @NbBundle.Messages({ - "CommonFilesMetadataBuilder.buildCategorySelectionString.doc=Documents", - "CommonFilesMetadataBuilder.buildCategorySelectionString.media=Media", - "CommonFilesMetadataBuilder.buildCategorySelectionString.all=All File Categories" - }) - - protected String buildCategorySelectionString() { - if (!this.filterByDoc && !this.filterByMedia) { - return Bundle.CommonFilesMetadataBuilder_buildCategorySelectionString_all(); - } else { - List filters = new ArrayList<>(); - if (this.filterByDoc) { - filters.add(Bundle.CommonFilesMetadataBuilder_buildCategorySelectionString_doc()); - } - if (this.filterByMedia) { - filters.add(Bundle.CommonFilesMetadataBuilder_buildCategorySelectionString_media()); - } - return String.join(", ", filters); - } - } -} diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesPanel.form b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesPanel.form deleted file mode 100644 index c9d08c0536..0000000000 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesPanel.form +++ /dev/null @@ -1,261 +0,0 @@ - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesPanel.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesPanel.java deleted file mode 100644 index 0e94e561b2..0000000000 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesPanel.java +++ /dev/null @@ -1,580 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2018 Basis Technology Corp. - * Contact: carrier sleuthkit org - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sleuthkit.autopsy.commonfilesearch; - -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Map; -import java.util.Map.Entry; -import java.util.concurrent.ExecutionException; -import java.util.logging.Level; -import javax.swing.ComboBoxModel; -import javax.swing.SwingUtilities; -import javax.swing.SwingWorker; -import org.netbeans.api.progress.ProgressHandle; -import org.openide.explorer.ExplorerManager; -import org.openide.util.NbBundle; -import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; -import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer; -import org.sleuthkit.autopsy.corecomponents.DataResultTopComponent; -import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable; -import org.sleuthkit.autopsy.corecomponents.TableFilterNode; -import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; -import org.sleuthkit.autopsy.directorytree.DataResultFilterNode; -import org.sleuthkit.datamodel.TskCoreException; - -/** - * Panel used for common files search configuration and configuration business - * logic. Nested within CommonFilesDialog. - */ -@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives -public final class CommonFilesPanel extends javax.swing.JPanel { - - private static final long serialVersionUID = 1L; - - private static final Long NO_DATA_SOURCE_SELECTED = -1L; - - private ComboBoxModel dataSourcesList = new DataSourceComboBoxModel(); - private Map dataSourceMap; - - private static final Logger LOGGER = Logger.getLogger(CommonFilesPanel.class.getName()); - private boolean singleDataSource = false; - private String selectedDataSource = ""; - private boolean pictureViewCheckboxState; - private boolean documentsCheckboxState; - - /** - * Creates new form CommonFilesPanel - */ - @NbBundle.Messages({ - "CommonFilesPanel.title=Common Files Panel", - "CommonFilesPanel.exception=Unexpected Exception loading DataSources."}) - public CommonFilesPanel() { - initComponents(); - - this.setupDataSources(); - - this.errorText.setVisible(false); - } - - /** - * Sets up the data sources dropdown and returns the data sources map for - * future usage. - * - * @return a mapping of data source ids to data source names - */ - @NbBundle.Messages({ - "CommonFilesPanel.buildDataSourceMap.done.tskCoreException=Unable to run query against DB.", - "CommonFilesPanel.buildDataSourceMap.done.noCurrentCaseException=Unable to open case file.", - "CommonFilesPanel.buildDataSourceMap.done.exception=Unexpected exception building data sources map.", - "CommonFilesPanel.buildDataSourceMap.done.interupted=Something went wrong building the Common Files Search dialog box.", - "CommonFilesPanel.buildDataSourceMap.done.sqlException=Unable to query db for data sources.", - "CommonFilesPanel.buildDataSourcesMap.updateUi.noDataSources=No data sources were found."}) - private void setupDataSources() { - - new SwingWorker, Void>() { - - private void updateUi() { - - String[] dataSourcesNames = new String[CommonFilesPanel.this.dataSourceMap.size()]; - - //only enable all this stuff if we actually have datasources - if (dataSourcesNames.length > 0) { - dataSourcesNames = CommonFilesPanel.this.dataSourceMap.values().toArray(dataSourcesNames); - CommonFilesPanel.this.dataSourcesList = new DataSourceComboBoxModel(dataSourcesNames); - CommonFilesPanel.this.selectDataSourceComboBox.setModel(CommonFilesPanel.this.dataSourcesList); - - boolean multipleDataSources = this.caseHasMultipleSources(); - - CommonFilesPanel.this.allDataSourcesRadioButton.setEnabled(true); - CommonFilesPanel.this.allDataSourcesRadioButton.setSelected(true); - - if (!multipleDataSources) { - CommonFilesPanel.this.withinDataSourceRadioButton.setEnabled(false); - CommonFilesPanel.this.withinDataSourceRadioButton.setSelected(false); - withinDataSourceSelected(false); - CommonFilesPanel.this.selectDataSourceComboBox.setEnabled(false); - } - - CommonFilesPanel.this.searchButton.setEnabled(true); - } else { - MessageNotifyUtil.Message.info(Bundle.CommonFilesPanel_buildDataSourcesMap_updateUi_noDataSources()); - CommonFilesPanel.this.cancelButtonActionPerformed(null); - } - } - - private boolean caseHasMultipleSources() { - return CommonFilesPanel.this.dataSourceMap.size() >= 3; - } - - @Override - protected Map doInBackground() throws NoCurrentCaseException, TskCoreException, SQLException { - DataSourceLoader loader = new DataSourceLoader(); - return loader.getDataSourceMap(); - } - - @Override - protected void done() { - - try { - CommonFilesPanel.this.dataSourceMap = this.get(); - - updateUi(); - - } catch (InterruptedException ex) { - LOGGER.log(Level.SEVERE, "Interrupted while building Common Files Search dialog.", ex); - MessageNotifyUtil.Message.error(Bundle.CommonFilesPanel_buildDataSourceMap_done_interupted()); - } catch (ExecutionException ex) { - String errorMessage; - Throwable inner = ex.getCause(); - if (inner instanceof TskCoreException) { - LOGGER.log(Level.SEVERE, "Failed to load data sources from database.", ex); - errorMessage = Bundle.CommonFilesPanel_buildDataSourceMap_done_tskCoreException(); - } else if (inner instanceof NoCurrentCaseException) { - LOGGER.log(Level.SEVERE, "Current case has been closed.", ex); - errorMessage = Bundle.CommonFilesPanel_buildDataSourceMap_done_noCurrentCaseException(); - } else if (inner instanceof SQLException) { - LOGGER.log(Level.SEVERE, "Unable to query db for data sources.", ex); - errorMessage = Bundle.CommonFilesPanel_buildDataSourceMap_done_sqlException(); - } else { - LOGGER.log(Level.SEVERE, "Unexpected exception while building Common Files Search dialog panel.", ex); - errorMessage = Bundle.CommonFilesPanel_buildDataSourceMap_done_exception(); - } - MessageNotifyUtil.Message.error(errorMessage); - } - } - }.execute(); - } - - @NbBundle.Messages({ - "CommonFilesPanel.search.results.titleAll=Common Files (All Data Sources)", - "CommonFilesPanel.search.results.titleSingle=Common Files (Match Within Data Source: %s)", - "CommonFilesPanel.search.results.pathText=Common Files Search Results", - "CommonFilesPanel.search.done.searchProgressGathering=Gathering Common Files Search Results.", - "CommonFilesPanel.search.done.searchProgressDisplay=Displaying Common Files Search Results.", - "CommonFilesPanel.search.done.tskCoreException=Unable to run query against DB.", - "CommonFilesPanel.search.done.noCurrentCaseException=Unable to open case file.", - "CommonFilesPanel.search.done.exception=Unexpected exception running Common Files Search.", - "CommonFilesPanel.search.done.interupted=Something went wrong finding common files.", - "CommonFilesPanel.search.done.sqlException=Unable to query db for files or data sources."}) - private void search() { - String pathText = Bundle.CommonFilesPanel_search_results_pathText(); - - new SwingWorker() { - - private String tabTitle; - private ProgressHandle progress; - - private void setTitleForAllDataSources() { - this.tabTitle = Bundle.CommonFilesPanel_search_results_titleAll(); - } - - private void setTitleForSingleSource(Long dataSourceId) { - final String CommonFilesPanel_search_results_titleSingle = Bundle.CommonFilesPanel_search_results_titleSingle(); - final Object[] dataSourceName = new Object[]{dataSourceMap.get(dataSourceId)}; - - this.tabTitle = String.format(CommonFilesPanel_search_results_titleSingle, dataSourceName); - } - - private Long determineDataSourceId() { - Long selectedObjId = CommonFilesPanel.NO_DATA_SOURCE_SELECTED; - if (CommonFilesPanel.this.singleDataSource) { - for (Entry dataSource : CommonFilesPanel.this.dataSourceMap.entrySet()) { - if (dataSource.getValue().equals(CommonFilesPanel.this.selectedDataSource)) { - selectedObjId = dataSource.getKey(); - break; - } - } - } - return selectedObjId; - } - - @Override - @SuppressWarnings({"BoxedValueEquality", "NumberEquality"}) - protected CommonFilesMetadata doInBackground() throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException { - progress = ProgressHandle.createHandle(Bundle.CommonFilesPanel_search_done_searchProgressGathering()); - progress.start(); - progress.switchToIndeterminate(); - - Long dataSourceId = determineDataSourceId(); - - CommonFilesMetadataBuilder builder; - boolean filterByMedia = false; - boolean filterByDocuments = false; - if (selectedFileCategoriesButton.isSelected()) { - if (pictureVideoCheckbox.isSelected()) { - filterByMedia = true; - } - if (documentsCheckbox.isSelected()) { - filterByDocuments = true; - } - } - if (dataSourceId == CommonFilesPanel.NO_DATA_SOURCE_SELECTED) { - builder = new AllDataSourcesCommonFilesAlgorithm(CommonFilesPanel.this.dataSourceMap, filterByMedia, filterByDocuments); - - setTitleForAllDataSources(); - } else { - builder = new SingleDataSource(dataSourceId, CommonFilesPanel.this.dataSourceMap, filterByMedia, filterByDocuments); - - setTitleForSingleSource(dataSourceId); - } - - this.tabTitle = builder.buildTabTitle(); - - CommonFilesMetadata metadata = builder.findCommonFiles(); - - return metadata; - } - - @Override - protected void done() { - try { - super.done(); - CommonFilesMetadata metadata = get(); - - CommonFilesNode commonFilesNode = new CommonFilesNode(metadata); - - //TODO this could be enumerating the children!!! - DataResultFilterNode dataResultFilterNode = new DataResultFilterNode(commonFilesNode, ExplorerManager.find(CommonFilesPanel.this)); - - TableFilterNode tableFilterWithDescendantsNode = new TableFilterNode(dataResultFilterNode, 3); - - DataResultViewerTable table = new CommonFilesSearchResultsViewerTable(); - - Collection viewers = new ArrayList<>(1); - viewers.add(table); - progress.setDisplayName(Bundle.CommonFilesPanel_search_done_searchProgressDisplay()); - DataResultTopComponent.createInstance(tabTitle, pathText, tableFilterWithDescendantsNode, metadata.size(), viewers); - } catch (InterruptedException ex) { - LOGGER.log(Level.SEVERE, "Interrupted while loading Common Files", ex); - MessageNotifyUtil.Message.error(Bundle.CommonFilesPanel_search_done_interupted()); - } catch (ExecutionException ex) { - String errorMessage; - Throwable inner = ex.getCause(); - if (inner instanceof TskCoreException) { - LOGGER.log(Level.SEVERE, "Failed to load files from database.", ex); - errorMessage = Bundle.CommonFilesPanel_search_done_tskCoreException(); - } else if (inner instanceof NoCurrentCaseException) { - LOGGER.log(Level.SEVERE, "Current case has been closed.", ex); - errorMessage = Bundle.CommonFilesPanel_search_done_noCurrentCaseException(); - } else if (inner instanceof SQLException) { - LOGGER.log(Level.SEVERE, "Unable to query db for files.", ex); - errorMessage = Bundle.CommonFilesPanel_search_done_sqlException(); - } else { - LOGGER.log(Level.SEVERE, "Unexpected exception while running Common Files Search.", ex); - errorMessage = Bundle.CommonFilesPanel_search_done_exception(); - } - MessageNotifyUtil.Message.error(errorMessage); - } finally { - progress.finish(); - } - } - }.execute(); - } - - /** - * This method is called from within the constructor to initialize the form. - * WARNING: Do NOT modify this code. The content of this method is always - * regenerated by the Form Editor. - */ - @SuppressWarnings("unchecked") - // //GEN-BEGIN:initComponents - private void initComponents() { - - dataSourcesButtonGroup = new javax.swing.ButtonGroup(); - fileTypeFilterButtonGroup = new javax.swing.ButtonGroup(); - searchButton = new javax.swing.JButton(); - allDataSourcesRadioButton = new javax.swing.JRadioButton(); - withinDataSourceRadioButton = new javax.swing.JRadioButton(); - selectDataSourceComboBox = new javax.swing.JComboBox<>(); - commonFilesSearchLabel = new javax.swing.JLabel(); - cancelButton = new javax.swing.JButton(); - allFileCategoriesRadioButton = new javax.swing.JRadioButton(); - selectedFileCategoriesButton = new javax.swing.JRadioButton(); - pictureVideoCheckbox = new javax.swing.JCheckBox(); - documentsCheckbox = new javax.swing.JCheckBox(); - dataSourceLabel = new javax.swing.JLabel(); - categoriesLabel = new javax.swing.JLabel(); - errorText = new javax.swing.JLabel(); - - org.openide.awt.Mnemonics.setLocalizedText(searchButton, org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.searchButton.text")); // NOI18N - searchButton.setEnabled(false); - searchButton.setHorizontalTextPosition(javax.swing.SwingConstants.LEADING); - searchButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - searchButtonActionPerformed(evt); - } - }); - - dataSourcesButtonGroup.add(allDataSourcesRadioButton); - allDataSourcesRadioButton.setSelected(true); - org.openide.awt.Mnemonics.setLocalizedText(allDataSourcesRadioButton, org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.allDataSourcesRadioButton.text")); // NOI18N - allDataSourcesRadioButton.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); - allDataSourcesRadioButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - allDataSourcesRadioButtonActionPerformed(evt); - } - }); - - dataSourcesButtonGroup.add(withinDataSourceRadioButton); - org.openide.awt.Mnemonics.setLocalizedText(withinDataSourceRadioButton, org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.withinDataSourceRadioButton.text")); // NOI18N - withinDataSourceRadioButton.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); - withinDataSourceRadioButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - withinDataSourceRadioButtonActionPerformed(evt); - } - }); - - selectDataSourceComboBox.setModel(dataSourcesList); - selectDataSourceComboBox.setEnabled(false); - selectDataSourceComboBox.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - selectDataSourceComboBoxActionPerformed(evt); - } - }); - - org.openide.awt.Mnemonics.setLocalizedText(commonFilesSearchLabel, org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.commonFilesSearchLabel.text")); // NOI18N - commonFilesSearchLabel.setFocusable(false); - - org.openide.awt.Mnemonics.setLocalizedText(cancelButton, org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.cancelButton.text")); // NOI18N - cancelButton.setActionCommand(org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.cancelButton.actionCommand")); // NOI18N - cancelButton.setHorizontalTextPosition(javax.swing.SwingConstants.LEADING); - cancelButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - cancelButtonActionPerformed(evt); - } - }); - - fileTypeFilterButtonGroup.add(allFileCategoriesRadioButton); - org.openide.awt.Mnemonics.setLocalizedText(allFileCategoriesRadioButton, org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.allFileCategoriesRadioButton.text")); // NOI18N - allFileCategoriesRadioButton.setToolTipText(org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.allFileCategoriesRadioButton.toolTipText")); // NOI18N - allFileCategoriesRadioButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - allFileCategoriesRadioButtonActionPerformed(evt); - } - }); - - fileTypeFilterButtonGroup.add(selectedFileCategoriesButton); - selectedFileCategoriesButton.setSelected(true); - org.openide.awt.Mnemonics.setLocalizedText(selectedFileCategoriesButton, org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.selectedFileCategoriesButton.text")); // NOI18N - selectedFileCategoriesButton.setToolTipText(org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.selectedFileCategoriesButton.toolTipText")); // NOI18N - selectedFileCategoriesButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - selectedFileCategoriesButtonActionPerformed(evt); - } - }); - - pictureVideoCheckbox.setSelected(true); - org.openide.awt.Mnemonics.setLocalizedText(pictureVideoCheckbox, org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.pictureVideoCheckbox.text")); // NOI18N - pictureVideoCheckbox.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - pictureVideoCheckboxActionPerformed(evt); - } - }); - - documentsCheckbox.setSelected(true); - org.openide.awt.Mnemonics.setLocalizedText(documentsCheckbox, org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.documentsCheckbox.text")); // NOI18N - documentsCheckbox.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - documentsCheckboxActionPerformed(evt); - } - }); - - org.openide.awt.Mnemonics.setLocalizedText(dataSourceLabel, org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.text")); // NOI18N - dataSourceLabel.setName(""); // NOI18N - - org.openide.awt.Mnemonics.setLocalizedText(categoriesLabel, org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.categoriesLabel.text")); // NOI18N - categoriesLabel.setName(""); // NOI18N - - errorText.setForeground(new java.awt.Color(255, 0, 0)); - org.openide.awt.Mnemonics.setLocalizedText(errorText, org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.errorText.text")); // NOI18N - - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); - this.setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addComponent(errorText) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(searchButton) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(cancelButton) - .addContainerGap()) - .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(categoriesLabel) - .addComponent(commonFilesSearchLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(dataSourceLabel) - .addGroup(layout.createSequentialGroup() - .addGap(6, 6, 6) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addGap(29, 29, 29) - .addComponent(selectDataSourceComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 261, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addComponent(withinDataSourceRadioButton) - .addComponent(allDataSourcesRadioButton) - .addComponent(allFileCategoriesRadioButton) - .addComponent(selectedFileCategoriesButton) - .addGroup(layout.createSequentialGroup() - .addGap(21, 21, 21) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(pictureVideoCheckbox) - .addComponent(documentsCheckbox)))))) - .addGap(19, 19, 19)))) - ); - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addContainerGap() - .addComponent(commonFilesSearchLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(18, 18, 18) - .addComponent(dataSourceLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(allDataSourcesRadioButton) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(withinDataSourceRadioButton) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(selectDataSourceComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(18, 18, 18) - .addComponent(categoriesLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(selectedFileCategoriesButton) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(pictureVideoCheckbox) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(documentsCheckbox) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(allFileCategoriesRadioButton) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(cancelButton) - .addComponent(searchButton) - .addComponent(errorText))) - ); - }// //GEN-END:initComponents - - private void searchButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchButtonActionPerformed - search(); - SwingUtilities.windowForComponent(this).dispose(); - }//GEN-LAST:event_searchButtonActionPerformed - - private void allDataSourcesRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_allDataSourcesRadioButtonActionPerformed - selectDataSourceComboBox.setEnabled(!allDataSourcesRadioButton.isSelected()); - singleDataSource = false; - }//GEN-LAST:event_allDataSourcesRadioButtonActionPerformed - - private void selectDataSourceComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_selectDataSourceComboBoxActionPerformed - final Object selectedItem = selectDataSourceComboBox.getSelectedItem(); - if (selectedItem != null) { - selectedDataSource = selectedItem.toString(); - } else { - selectedDataSource = ""; - } - }//GEN-LAST:event_selectDataSourceComboBoxActionPerformed - - private void withinDataSourceRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_withinDataSourceRadioButtonActionPerformed - withinDataSourceSelected(withinDataSourceRadioButton.isSelected()); - }//GEN-LAST:event_withinDataSourceRadioButtonActionPerformed - - private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed - SwingUtilities.windowForComponent(this).dispose(); - }//GEN-LAST:event_cancelButtonActionPerformed - - private void allFileCategoriesRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_allFileCategoriesRadioButtonActionPerformed - this.manageCheckBoxState(); - this.toggleErrorTextAndSearchBox(); - }//GEN-LAST:event_allFileCategoriesRadioButtonActionPerformed - - private void selectedFileCategoriesButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_selectedFileCategoriesButtonActionPerformed - this.manageCheckBoxState(); - }//GEN-LAST:event_selectedFileCategoriesButtonActionPerformed - - private void pictureVideoCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pictureVideoCheckboxActionPerformed - this.toggleErrorTextAndSearchBox(); - }//GEN-LAST:event_pictureVideoCheckboxActionPerformed - - private void documentsCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_documentsCheckboxActionPerformed - this.toggleErrorTextAndSearchBox(); - }//GEN-LAST:event_documentsCheckboxActionPerformed - - private void toggleErrorTextAndSearchBox() { - if (!this.pictureVideoCheckbox.isSelected() && !this.documentsCheckbox.isSelected() && !this.allFileCategoriesRadioButton.isSelected()) { - this.searchButton.setEnabled(false); - this.errorText.setVisible(true); - } else { - this.searchButton.setEnabled(true); - this.errorText.setVisible(false); - } - } - - private void withinDataSourceSelected(boolean selected) { - selectDataSourceComboBox.setEnabled(selected); - if (selectDataSourceComboBox.isEnabled()) { - selectDataSourceComboBox.setSelectedIndex(0); - singleDataSource = true; - } - } - - private void manageCheckBoxState() { - - if (this.allFileCategoriesRadioButton.isSelected()) { - - this.pictureViewCheckboxState = this.pictureVideoCheckbox.isSelected(); - this.documentsCheckboxState = this.documentsCheckbox.isSelected(); - - this.pictureVideoCheckbox.setEnabled(false); - this.documentsCheckbox.setEnabled(false); - } - - if (this.selectedFileCategoriesButton.isSelected()) { - - this.pictureVideoCheckbox.setSelected(this.pictureViewCheckboxState); - this.documentsCheckbox.setSelected(this.documentsCheckboxState); - - this.pictureVideoCheckbox.setEnabled(true); - this.documentsCheckbox.setEnabled(true); - - this.toggleErrorTextAndSearchBox(); - } - } - - // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JRadioButton allDataSourcesRadioButton; - private javax.swing.JRadioButton allFileCategoriesRadioButton; - private javax.swing.JButton cancelButton; - private javax.swing.JLabel categoriesLabel; - private javax.swing.JLabel commonFilesSearchLabel; - private javax.swing.JLabel dataSourceLabel; - private javax.swing.ButtonGroup dataSourcesButtonGroup; - private javax.swing.JCheckBox documentsCheckbox; - private javax.swing.JLabel errorText; - private javax.swing.ButtonGroup fileTypeFilterButtonGroup; - private javax.swing.JCheckBox pictureVideoCheckbox; - private javax.swing.JButton searchButton; - private javax.swing.JComboBox selectDataSourceComboBox; - private javax.swing.JRadioButton selectedFileCategoriesButton; - private javax.swing.JRadioButton withinDataSourceRadioButton; - // End of variables declaration//GEN-END:variables -} diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesSearchAction.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesSearchAction.java index 5a3f887564..6cc00256dd 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesSearchAction.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesSearchAction.java @@ -24,6 +24,8 @@ import org.openide.util.HelpCtx; import org.openide.util.NbBundle; import org.openide.util.actions.CallableSystemAction; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.autopsy.coreutils.Logger; @@ -32,9 +34,10 @@ import org.sleuthkit.autopsy.coreutils.Logger; */ final public class CommonFilesSearchAction extends CallableSystemAction { + private static final Logger LOGGER = Logger.getLogger(CommonFilesSearchAction.class.getName()); + private static CommonFilesSearchAction instance = null; private static final long serialVersionUID = 1L; - private static final Logger logger = Logger.getLogger(CommonFilesSearchAction.class.getName()); CommonFilesSearchAction() { super(); @@ -45,9 +48,22 @@ final public class CommonFilesSearchAction extends CallableSystemAction { public boolean isEnabled(){ boolean shouldBeEnabled = false; try { - shouldBeEnabled = Case.isCaseOpen() && Case.getCurrentCase().getDataSources().size() > 1; + //dont refactor any of this to pull out common expressions - order of evaluation of each expression is significant + shouldBeEnabled = + (Case.isCaseOpen() && + Case.getCurrentCase().getDataSources().size() > 1) + || + (EamDb.isEnabled() && + EamDb.getInstance() != null && + EamDb.getInstance().getCases().size() > 1 && + Case.isCaseOpen() && + Case.getCurrentCase() != null && + EamDb.getInstance().getCase(Case.getCurrentCase()) != null); + } catch(TskCoreException ex) { - logger.log(Level.SEVERE, "Error getting data sources for action enabled check", ex); + LOGGER.log(Level.SEVERE, "Error getting data sources for action enabled check", ex); + } catch (EamDbException ex) { + LOGGER.log(Level.SEVERE, "Error getting CR cases for action enabled check", ex); } return super.isEnabled() && shouldBeEnabled; } @@ -61,12 +77,12 @@ final public class CommonFilesSearchAction extends CallableSystemAction { @Override public void actionPerformed(ActionEvent event) { - new CommonFilesDialog().setVisible(true); + new CommonAttributePanel().setVisible(true); } @Override public void performAction() { - new CommonFilesDialog().setVisible(true); + new CommonAttributePanel().setVisible(true); } @NbBundle.Messages({ diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/DataSourceComboBoxModel.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/DataSourceComboBoxModel.java index 657fedc53b..1b55e6bdc3 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/DataSourceComboBoxModel.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/DataSourceComboBoxModel.java @@ -30,7 +30,7 @@ public class DataSourceComboBoxModel extends AbstractListModel implement private static final long serialVersionUID = 1L; private final String[] dataSourceList; - String selection = null; + private String selection = null; /** * Use this to initialize the panel diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/FileInstanceMetadata.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/FileInstanceMetadata.java deleted file mode 100644 index 0e53cc6992..0000000000 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/FileInstanceMetadata.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * - * Autopsy Forensic Browser - * - * Copyright 2018 Basis Technology Corp. - * Contact: carrier sleuthkit org - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sleuthkit.autopsy.commonfilesearch; - -/** - * Encapsulates data required to instantiate a FileInstanceNode. - */ -final public class FileInstanceMetadata { - - private final Long objectId; - private final String dataSourceName; - - /** - * Create meta data required to find an abstract file and build a FileInstanceNode. - * @param objectId id of abstract file to find - * @param dataSourceName name of datasource where the object is found - */ - FileInstanceMetadata(Long objectId, String dataSourceName) { - this.objectId = objectId; - this.dataSourceName = dataSourceName; - } - - /** - * obj_id for the file represented by this object - * @return - */ - public Long getObjectId(){ - return this.objectId; - } - - /** - * Name of datasource where this instance was found. - * @return - */ - public String getDataSourceName(){ - return this.dataSourceName; - } - -} diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/InstanceCountNode.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InstanceCountNode.java index 987e6faa08..dbc3d362e9 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/InstanceCountNode.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InstanceCountNode.java @@ -40,24 +40,24 @@ import org.sleuthkit.autopsy.datamodel.NodeProperty; final public class InstanceCountNode extends DisplayableItemNode { final private int instanceCount; - final private List metadataList; + final private List attributeValues; /** * Create a node with the given number of instances, and the given * selection of metadata. * @param instanceCount - * @param md5Metadata + * @param attributeValues */ @NbBundle.Messages({ "InstanceCountNode.displayName=Files with %s instances (%s)" }) - public InstanceCountNode(int instanceCount, List md5Metadata) { - super(Children.create(new Md5NodeFactory(md5Metadata), true)); + public InstanceCountNode(int instanceCount, List attributeValues) { + super(Children.create(new CommonAttributeValueNodeFactory(attributeValues), true)); this.instanceCount = instanceCount; - this.metadataList = md5Metadata; + this.attributeValues = attributeValues; - this.setDisplayName(String.format(Bundle.InstanceCountNode_displayName(), Integer.toString(instanceCount), md5Metadata.size())); + this.setDisplayName(String.format(Bundle.InstanceCountNode_displayName(), Integer.toString(instanceCount), attributeValues.size())); this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/fileset-icon-16.png"); //NON-NLS } @@ -73,8 +73,8 @@ final public class InstanceCountNode extends DisplayableItemNode { * Get a list of metadata for the MD5s which are children of this object. * @return List */ - List getMetadata() { - return Collections.unmodifiableList(this.metadataList); + List getAttributeValues() { + return Collections.unmodifiableList(this.attributeValues); } @Override @@ -113,34 +113,36 @@ final public class InstanceCountNode extends DisplayableItemNode { * ChildFactory which builds CommonFileParentNodes from the * CommonFilesMetaaData models. */ - static class Md5NodeFactory extends ChildFactory { + static class CommonAttributeValueNodeFactory extends ChildFactory { /** * List of models, each of which is a parent node matching a single md5, * containing children FileNodes. */ - private final Map metadata; + // maps sting version of value to value Object (??) + private final Map metadata; - Md5NodeFactory(List metadata) { + CommonAttributeValueNodeFactory(List attributeValues) { this.metadata = new HashMap<>(); - Iterator iterator = metadata.iterator(); + Iterator iterator = attributeValues.iterator(); while (iterator.hasNext()) { - Md5Metadata md5Metadata = iterator.next(); - this.metadata.put(md5Metadata.getMd5(), md5Metadata); + CommonAttributeValue attributeValue = iterator.next(); + this.metadata.put(attributeValue.getValue(), attributeValue); } } - @Override - protected Node createNodeForKey(String md5) { - Md5Metadata md5Metadata = this.metadata.get(md5); - return new Md5Node(md5Metadata); - } - @Override protected boolean createKeys(List list) { + // @@@ We should just use CommonAttributeValue as the key... list.addAll(this.metadata.keySet()); return true; } + + @Override + protected Node createNodeForKey(String attributeValue) { + CommonAttributeValue md5Metadata = this.metadata.get(attributeValue); + return new CommonAttributeValueNode(md5Metadata); + } } } \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCaseCommonAttributeSearcher.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCaseCommonAttributeSearcher.java new file mode 100644 index 0000000000..2c745b5271 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCaseCommonAttributeSearcher.java @@ -0,0 +1,59 @@ +/* + * + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.commonfilesearch; + +import java.util.Map; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; + +/** + * Provides logic for selecting common files from all data sources and all cases + * in the Central Repo. + */ +abstract class InterCaseCommonAttributeSearcher extends AbstractCommonAttributeSearcher { + + private final EamDb dbManager; + + /** + * Implements the algorithm for getting common files across all data sources + * and all cases. Can filter on mime types conjoined by logical AND. + * + * @param filterByMediaMimeType match only on files whose mime types can be + * broadly categorized as media types + * @param filterByDocMimeType match only on files whose mime types can be + * broadly categorized as document types + * + * @throws EamDbException + */ + InterCaseCommonAttributeSearcher(Map dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) throws EamDbException { + super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType); + dbManager = EamDb.getInstance(); + } + + protected CorrelationCase getCorrelationCaseFromId(int correlationCaseId) throws EamDbException { + for (CorrelationCase cCase : this.dbManager.getCases()) { + if (cCase.getID() == correlationCaseId) { + return cCase; + } + } + throw new IllegalArgumentException("Cannot locate case."); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCasePanel.form b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCasePanel.form new file mode 100644 index 0000000000..8c62356803 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCasePanel.form @@ -0,0 +1,89 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCasePanel.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCasePanel.java new file mode 100644 index 0000000000..67aa40134b --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCasePanel.java @@ -0,0 +1,204 @@ +/* + * + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.commonfilesearch; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import javax.swing.ComboBoxModel; +import org.openide.util.NbBundle; + +/** + * UI controls for Common Files Search scenario where the user intends to find + * common files between cases in addition to the present case. + */ +public class InterCasePanel extends javax.swing.JPanel { + + private static final long serialVersionUID = 1L; + + static final int NO_CASE_SELECTED = -1; + + private ComboBoxModel casesList = new DataSourceComboBoxModel(); + + private final Map caseMap; + + private String errorMessage; + + //True if we are looking in any or all cases, + // false if we must find matches in a given case plus the current case + private boolean anyCase; + + /** + * Creates new form InterCasePanel + */ + public InterCasePanel() { + initComponents(); + this.errorMessage = ""; + this.caseMap = new HashMap<>(); + this.anyCase = true; + } + + private void specificCaseSelected(boolean selected) { + this.specificCentralRepoCaseRadio.setEnabled(selected); + if (this.specificCentralRepoCaseRadio.isEnabled()) { + this.caseComboBox.setEnabled(true); + this.caseComboBox.setSelectedIndex(0); + } + } + + String getErrorMessage(){ + return this.errorMessage; + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + buttonGroup = new javax.swing.ButtonGroup(); + anyCentralRepoCaseRadio = new javax.swing.JRadioButton(); + specificCentralRepoCaseRadio = new javax.swing.JRadioButton(); + caseComboBox = new javax.swing.JComboBox<>(); + + buttonGroup.add(anyCentralRepoCaseRadio); + anyCentralRepoCaseRadio.setSelected(true); + org.openide.awt.Mnemonics.setLocalizedText(anyCentralRepoCaseRadio, org.openide.util.NbBundle.getMessage(InterCasePanel.class, "InterCasePanel.anyCentralRepoCaseRadio.text")); // NOI18N + anyCentralRepoCaseRadio.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + anyCentralRepoCaseRadioActionPerformed(evt); + } + }); + + buttonGroup.add(specificCentralRepoCaseRadio); + org.openide.awt.Mnemonics.setLocalizedText(specificCentralRepoCaseRadio, org.openide.util.NbBundle.getMessage(InterCasePanel.class, "InterCasePanel.specificCentralRepoCaseRadio.text")); // NOI18N + specificCentralRepoCaseRadio.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + specificCentralRepoCaseRadioActionPerformed(evt); + } + }); + + caseComboBox.setModel(casesList); + caseComboBox.setEnabled(false); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(anyCentralRepoCaseRadio) + .addGroup(layout.createSequentialGroup() + .addGap(21, 21, 21) + .addComponent(caseComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 261, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(specificCentralRepoCaseRadio)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(anyCentralRepoCaseRadio) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(specificCentralRepoCaseRadio) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(caseComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + ); + }// //GEN-END:initComponents + + private void specificCentralRepoCaseRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_specificCentralRepoCaseRadioActionPerformed + this.caseComboBox.setEnabled(true); + if(this.caseComboBox.isEnabled() && this.caseComboBox.getSelectedItem() == null){ + this.caseComboBox.setSelectedIndex(0); + } + this.anyCase = false; + }//GEN-LAST:event_specificCentralRepoCaseRadioActionPerformed + + private void anyCentralRepoCaseRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_anyCentralRepoCaseRadioActionPerformed + this.caseComboBox.setEnabled(false); + this.anyCase = true; + }//GEN-LAST:event_anyCentralRepoCaseRadioActionPerformed + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JRadioButton anyCentralRepoCaseRadio; + private javax.swing.ButtonGroup buttonGroup; + private javax.swing.JComboBox caseComboBox; + private javax.swing.JRadioButton specificCentralRepoCaseRadio; + // End of variables declaration//GEN-END:variables + + Map getCaseMap() { + return Collections.unmodifiableMap(this.caseMap); + } + + void setCaseList(DataSourceComboBoxModel dataSourceComboBoxModel) { + this.casesList = dataSourceComboBoxModel; + this.caseComboBox.setModel(dataSourceComboBoxModel); + } + + void rigForMultipleCases(boolean multipleCases) { + this.anyCentralRepoCaseRadio.setEnabled(multipleCases); + this.anyCentralRepoCaseRadio.setEnabled(multipleCases); + + if(!multipleCases){ + this.specificCentralRepoCaseRadio.setSelected(true); + this.specificCaseSelected(true); + } + } + + void setCaseMap(Map caseMap) { + this.caseMap.clear(); + this.caseMap.putAll(caseMap); + } + + boolean centralRepoHasMultipleCases() { + return this.caseMap.size() >= 2; + } + + Integer getSelectedCaseId(){ + if(this.anyCase){ + return InterCasePanel.NO_CASE_SELECTED; + } + + for(Entry entry : this.caseMap.entrySet()){ + if(entry.getValue().equals(this.caseComboBox.getSelectedItem())){ + return entry.getKey(); + } + } + + return InterCasePanel.NO_CASE_SELECTED; + } + + @NbBundle.Messages({ + "InterCasePanel.showInterCaseErrorMessage.message=Cannot run intercase correlation search: no cases in Central Repository." + }) + boolean areSearchCriteriaMet() { + if(this.caseMap.isEmpty()){ + this.errorMessage = Bundle.InterCasePanel_showInterCaseErrorMessage_message(); + return false; + } else { + return true; + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCaseSearchResultsProcessor.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCaseSearchResultsProcessor.java new file mode 100644 index 0000000000..571094d14f --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/InterCaseSearchResultsProcessor.java @@ -0,0 +1,241 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.commonfilesearch; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationDataSource; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; +import org.sleuthkit.autopsy.centralrepository.datamodel.InstanceTableCallback; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.TskData; +import org.sleuthkit.datamodel.HashUtility; + +/** + * Used to process and return CorrelationCase md5s from the EamDB for + * CommonFilesSearch. + */ +final class InterCaseSearchResultsProcessor { + + private Map dataSources; + + private static final Logger LOGGER = Logger.getLogger(CommonAttributePanel.class.getName()); + + private final String interCaseWhereClause = "value IN (SELECT value FROM file_instances" + + " WHERE value IN (SELECT value FROM file_instances" + + " WHERE case_id=%s AND (known_status !=%s OR known_status IS NULL) GROUP BY value)" + + " GROUP BY value HAVING COUNT(DISTINCT case_id) > 1) ORDER BY value"; + + private final String singleInterCaseWhereClause = "value IN (SELECT value FROM file_instances " + + "WHERE value IN (SELECT value FROM file_instances " + + "WHERE case_id=%s AND (known_status !=%s OR known_status IS NULL) GROUP BY value) " + + "AND (case_id=%s OR case_id=%s) GROUP BY value HAVING COUNT(DISTINCT case_id) > 1) ORDER BY value"; + + /** + * Used in the InterCaseCommonAttributeSearchers to find common attribute instances and generate nodes at the UI level. + * @param dataSources + */ + InterCaseSearchResultsProcessor(Map dataSources){ + this.dataSources = dataSources; + } + + /** + * Used in the CentralRepoCommonAttributeInstance to find common attribute instances and generate nodes at the UI level. + */ + InterCaseSearchResultsProcessor(){ + //intentionally emtpy - we need a constructor which does not set the data sources field + } + + /** + * Finds a single CorrelationAttribute given an id. + * + * @param attrbuteId Row of CorrelationAttribute to retrieve from the EamDb + * @return CorrelationAttribute object representation of retrieved match + */ + CorrelationAttribute findSingleCorrelationAttribute(int attrbuteId) { + try { + InterCaseCommonAttributeRowCallback instancetableCallback = new InterCaseCommonAttributeRowCallback(); + EamDb DbManager = EamDb.getInstance(); + CorrelationAttribute.Type fileType = DbManager.getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID); + DbManager.processInstanceTableWhere(fileType, String.format("id = %s", attrbuteId), instancetableCallback); + + return instancetableCallback.getCorrelationAttribute(); + + } catch (EamDbException ex) { + LOGGER.log(Level.SEVERE, "Error accessing EamDb processing InstanceTable row.", ex); + } + + return null; + } + + /** + * Given the current case, fins all intercase common files from the EamDb + * and builds maps of obj id to md5 and case. + * + * @param currentCase The current TSK Case. + */ + Map> findInterCaseCommonAttributeValues(Case currentCase) { + try { + InterCaseCommonAttributesCallback instancetableCallback = new InterCaseCommonAttributesCallback(); + EamDb DbManager = EamDb.getInstance(); + CorrelationAttribute.Type fileType = DbManager.getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID); + int caseId = DbManager.getCase(currentCase).getID(); + + DbManager.processInstanceTableWhere(fileType, String.format(interCaseWhereClause, caseId, + TskData.FileKnown.KNOWN.getFileKnownValue()), + instancetableCallback); + + return instancetableCallback.getInstanceCollatedCommonFiles(); + + } catch (EamDbException ex) { + LOGGER.log(Level.SEVERE, "Error accessing EamDb processing CaseInstancesTable.", ex); + } + return new HashMap<>(); + } + + /** + * Given the current case, and a specific case of interest, finds common + * files which exist between cases from the EamDb. Builds maps of obj id to + * md5 and case. + * + * @param currentCase The current TSK Case. + * @param singleCase The case of interest. Matches must exist in this case. + */ + Map> findSingleInterCaseCommonAttributeValues(Case currentCase, CorrelationCase singleCase) { + try { + InterCaseCommonAttributesCallback instancetableCallback = new InterCaseCommonAttributesCallback(); + EamDb DbManager = EamDb.getInstance(); + CorrelationAttribute.Type fileType = DbManager.getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID); + int caseId = DbManager.getCase(currentCase).getID(); + int targetCaseId = singleCase.getID(); + DbManager.processInstanceTableWhere(fileType, String.format(singleInterCaseWhereClause, caseId, + TskData.FileKnown.KNOWN.getFileKnownValue(), caseId, targetCaseId), instancetableCallback); + return instancetableCallback.getInstanceCollatedCommonFiles(); + } catch (EamDbException ex) { + LOGGER.log(Level.SEVERE, "Error accessing EamDb processing CaseInstancesTable.", ex); + } + return new HashMap<>(); + } + + /** + * Callback to use with findInterCaseCommonAttributeValues which generates a + * list of md5s for common files search + */ + private class InterCaseCommonAttributesCallback implements InstanceTableCallback { + + final Map> instanceCollatedCommonFiles = new HashMap<>(); + + private CommonAttributeValue commonAttributeValue = null; + private String previousRowMd5 = ""; + + @Override + public void process(ResultSet resultSet) { + try { + while (resultSet.next()) { + + int resultId = InstanceTableCallback.getId(resultSet); + String md5Value = InstanceTableCallback.getValue(resultSet); + if (previousRowMd5.isEmpty()) { + previousRowMd5 = md5Value; + } + if (md5Value == null || HashUtility.isNoDataMd5(md5Value)) { + continue; + } + + countAndAddCommonAttributes(md5Value, resultId); + + } + } catch (SQLException ex) { + LOGGER.log(Level.WARNING, "Error getting artifact instances from database.", ex); // NON-NLS + } + } + + private void countAndAddCommonAttributes(String md5Value, int resultId) { + if (commonAttributeValue == null) { + commonAttributeValue = new CommonAttributeValue(md5Value); + } + if (!md5Value.equals(previousRowMd5)) { + int size = commonAttributeValue.getInstanceCount(); + if (instanceCollatedCommonFiles.containsKey(size)) { + instanceCollatedCommonFiles.get(size).add(commonAttributeValue); + } else { + ArrayList value = new ArrayList<>(); + value.add(commonAttributeValue); + instanceCollatedCommonFiles.put(size, value); + } + + commonAttributeValue = new CommonAttributeValue(md5Value); + previousRowMd5 = md5Value; + } + // 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 + // Later, when the FileInstanceNode is built. Therefore, build node generators for now. + AbstractCommonAttributeInstance searchResult = new CentralRepoCommonAttributeInstance(resultId, InterCaseSearchResultsProcessor.this.dataSources); + commonAttributeValue.addInstance(searchResult); + } + + Map> getInstanceCollatedCommonFiles() { + return Collections.unmodifiableMap(instanceCollatedCommonFiles); + } + } + + /** + * Callback to use with findSingleCorrelationAttribute which retrieves a + * single CorrelationAttribute from the EamDb. + */ + private class InterCaseCommonAttributeRowCallback implements InstanceTableCallback { + + CorrelationAttribute correlationAttribute = null; + + @Override + public void process(ResultSet resultSet) { + try { + EamDb DbManager = EamDb.getInstance(); + CorrelationAttribute.Type fileType = DbManager.getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID); + + while (resultSet.next()) { + CorrelationCase correlationCase = DbManager.getCaseById(InstanceTableCallback.getCaseId(resultSet)); + CorrelationDataSource dataSource = DbManager.getDataSourceById(correlationCase, InstanceTableCallback.getDataSourceId(resultSet)); + correlationAttribute = DbManager.getCorrelationAttribute(fileType, + correlationCase, + dataSource, + InstanceTableCallback.getValue(resultSet), + InstanceTableCallback.getFilePath(resultSet)); + + } + } catch (SQLException | EamDbException ex) { + LOGGER.log(Level.WARNING, "Error getting single correlation artifact instance from database.", ex); // NON-NLS + } + } + + CorrelationAttribute getCorrelationAttribute() { + return correlationAttribute; + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/IntraCaseCommonAttributeSearcher.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/IntraCaseCommonAttributeSearcher.java new file mode 100644 index 0000000000..172108d5ef --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/IntraCaseCommonAttributeSearcher.java @@ -0,0 +1,168 @@ +/* + * + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.commonfilesearch; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.datamodel.HashUtility; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * + * Generates a List when + * findFiles() is called, which organizes files by md5 to + * prepare to display in viewer. + * + * This entire thing runs on a background thread where exceptions are handled. + */ +@SuppressWarnings("PMD.AbstractNaming") +public abstract class IntraCaseCommonAttributeSearcher extends AbstractCommonAttributeSearcher { + + 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 + + /** + * Subclass this to implement different algorithms for getting common files. + * + * @param dataSourceIdMap a map of obj_id to datasource name + * @param filterByMediaMimeType match only on files whose mime types can be + * broadly categorized as media types + * @param filterByDocMimeType match only on files whose mime types can be + * broadly categorized as document types + */ + IntraCaseCommonAttributeSearcher(Map dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) { + super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType); + } + + /** + * Use this as a prefix when building the SQL select statement. + * + *
    + *
  • You only have to specify the WHERE clause if you use this.
  • + *
  • If you do not use this string, you must use at least the columns + * selected below, in that order.
  • + *
+ */ + static final String SELECT_PREFIX = "SELECT obj_id, md5, data_source_obj_id from tsk_files where"; //NON-NLS + + /** + * Should build a SQL SELECT statement to be passed to + * SleuthkitCase.executeQuery(sql) which will select the desired file ids + * and MD5 hashes. + * + * The statement should select obj_id, md5, data_source_obj_id in that + * order. + * + * @return sql string select statement + */ + protected abstract String buildSqlSelectStatement(); + + /** + * Generate a meta data object which encapsulates everything need to add the + * tree table tab to the top component. + * + * @return a data object with all of the matched files in a hierarchical + * format + * @throws TskCoreException + * @throws NoCurrentCaseException + * @throws SQLException + */ + @Override + public CommonAttributeSearchResults findFiles() throws TskCoreException, NoCurrentCaseException, SQLException { + Map commonFiles = new HashMap<>(); + + final Case currentCase = Case.getCurrentCaseThrows(); + final String caseName = currentCase.getDisplayName(); + + SleuthkitCase sleuthkitCase = currentCase.getSleuthkitCase(); + + String selectStatement = this.buildSqlSelectStatement(); + + try ( + CaseDbQuery query = sleuthkitCase.executeQuery(selectStatement); + ResultSet resultSet = query.getResultSet()) { + + while (resultSet.next()) { + Long objectId = resultSet.getLong(1); + String md5 = resultSet.getString(2); + Long dataSourceId = resultSet.getLong(3); + String dataSource = this.getDataSourceIdToNameMap().get(dataSourceId); + + if (md5 == null || HashUtility.isNoDataMd5(md5)) { + continue; + } + + if (commonFiles.containsKey(md5)) { + final CommonAttributeValue commonAttributeValue = commonFiles.get(md5); + commonAttributeValue.addInstance(new CaseDBCommonAttributeInstance(objectId, dataSource, caseName)); + } else { + final CommonAttributeValue commonAttributeValue = new CommonAttributeValue(md5); + commonAttributeValue.addInstance(new CaseDBCommonAttributeInstance(objectId, dataSource, caseName)); + commonFiles.put(md5, commonAttributeValue); + } + } + } + + Map> instanceCollatedCommonFiles = collateMatchesByNumberOfInstances(commonFiles); + + return new CommonAttributeSearchResults(instanceCollatedCommonFiles); + } + + /** + * Should be used by subclasses, in their + * buildSqlSelectStatement() function to create an SQL boolean + * expression which will filter our matches based on mime type. The + * expression will be conjoined to base query with an AND operator. + * + * @return sql fragment of the form: + * 'and "mime_type" in ( [comma delimited list of mime types] )' + * or empty string in the event that no types to filter on were given. + */ + String determineMimeTypeFilter() { + + Set mimeTypesToFilterOn = new HashSet<>(); + String mimeTypeString = ""; + if (isFilterByMedia()) { + mimeTypesToFilterOn.addAll(MEDIA_PICS_VIDEO_MIME_TYPES); + } + if (isFilterByDoc()) { + mimeTypesToFilterOn.addAll(TEXT_FILES_MIME_TYPES); + } + StringBuilder mimeTypeFilter = new StringBuilder(mimeTypesToFilterOn.size()); + if (!mimeTypesToFilterOn.isEmpty()) { + for (String mimeType : mimeTypesToFilterOn) { + mimeTypeFilter.append(SINGLE_QUOTE).append(mimeType).append(SINGLE_QUTOE_COMMA); + } + mimeTypeString = mimeTypeFilter.toString().substring(0, mimeTypeFilter.length() - 1); + mimeTypeString = String.format(FILTER_BY_MIME_TYPES_WHERE_CLAUSE, new Object[]{mimeTypeString}); + } + return mimeTypeString; + } + static final String SINGLE_QUTOE_COMMA = "',"; + static final String SINGLE_QUOTE = "'"; +} diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/IntraCasePanel.form b/Core/src/org/sleuthkit/autopsy/commonfilesearch/IntraCasePanel.form new file mode 100644 index 0000000000..7038ca5926 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/IntraCasePanel.form @@ -0,0 +1,97 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/IntraCasePanel.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/IntraCasePanel.java new file mode 100644 index 0000000000..c62552082b --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/IntraCasePanel.java @@ -0,0 +1,194 @@ +/* + * + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.commonfilesearch; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import javax.swing.ComboBoxModel; +import org.openide.util.NbBundle; + +/** + * UI controls for Common Files Search scenario where the user intends to find + * common files between datasources. It is an inner panel which provides the ability + * to select all datasources or a single datasource from a dropdown list of + * sources in the current case. + */ +public class IntraCasePanel extends javax.swing.JPanel { + + private static final long serialVersionUID = 1L; + static final long NO_DATA_SOURCE_SELECTED = -1; + + private boolean singleDataSource; + private ComboBoxModel dataSourcesList = new DataSourceComboBoxModel(); + private final Map dataSourceMap; + + private String errorMessage; + + /** + * Creates new form IntraCasePanel + */ + public IntraCasePanel() { + initComponents(); + this.errorMessage = ""; + this.dataSourceMap = new HashMap<>(); + this.singleDataSource = true; + } + + public Map getDataSourceMap(){ + return Collections.unmodifiableMap(this.dataSourceMap); + } + + Long getSelectedDataSourceId(){ + if(!this.singleDataSource){ + return IntraCasePanel.NO_DATA_SOURCE_SELECTED; + } + + for(Entry entry : this.dataSourceMap.entrySet()){ + if(entry.getValue().equals(this.selectDataSourceComboBox.getSelectedItem())){ + return entry.getKey(); + } + } + + return IntraCasePanel.NO_DATA_SOURCE_SELECTED; + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + buttonGroup = new javax.swing.ButtonGroup(); + allDataSourcesRadioButton = new javax.swing.JRadioButton(); + withinDataSourceRadioButton = new javax.swing.JRadioButton(); + selectDataSourceComboBox = new javax.swing.JComboBox<>(); + + buttonGroup.add(allDataSourcesRadioButton); + org.openide.awt.Mnemonics.setLocalizedText(allDataSourcesRadioButton, org.openide.util.NbBundle.getMessage(IntraCasePanel.class, "IntraCasePanel.allDataSourcesRadioButton.text")); // NOI18N + allDataSourcesRadioButton.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); + allDataSourcesRadioButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + allDataSourcesRadioButtonActionPerformed(evt); + } + }); + + buttonGroup.add(withinDataSourceRadioButton); + org.openide.awt.Mnemonics.setLocalizedText(withinDataSourceRadioButton, org.openide.util.NbBundle.getMessage(IntraCasePanel.class, "IntraCasePanel.withinDataSourceRadioButton.text")); // NOI18N + withinDataSourceRadioButton.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); + withinDataSourceRadioButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + withinDataSourceRadioButtonActionPerformed(evt); + } + }); + + selectDataSourceComboBox.setModel(dataSourcesList); + selectDataSourceComboBox.setActionCommand(org.openide.util.NbBundle.getMessage(IntraCasePanel.class, "IntraCasePanel.selectDataSourceComboBox.actionCommand")); // NOI18N + selectDataSourceComboBox.setEnabled(false); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(allDataSourcesRadioButton) + .addComponent(withinDataSourceRadioButton))) + .addGroup(layout.createSequentialGroup() + .addGap(27, 27, 27) + .addComponent(selectDataSourceComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 261, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(allDataSourcesRadioButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(withinDataSourceRadioButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(selectDataSourceComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + ); + }// //GEN-END:initComponents + + private void allDataSourcesRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_allDataSourcesRadioButtonActionPerformed + selectDataSourceComboBox.setEnabled(!allDataSourcesRadioButton.isSelected()); + singleDataSource = false; + }//GEN-LAST:event_allDataSourcesRadioButtonActionPerformed + + private void withinDataSourceRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_withinDataSourceRadioButtonActionPerformed + withinDataSourceSelected(withinDataSourceRadioButton.isSelected()); + }//GEN-LAST:event_withinDataSourceRadioButtonActionPerformed + + private void withinDataSourceSelected(boolean selected) { + selectDataSourceComboBox.setEnabled(selected); + if (selectDataSourceComboBox.isEnabled()) { + selectDataSourceComboBox.setSelectedIndex(0); + singleDataSource = true; + } + } + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JRadioButton allDataSourcesRadioButton; + private javax.swing.ButtonGroup buttonGroup; + private javax.swing.JComboBox selectDataSourceComboBox; + private javax.swing.JRadioButton withinDataSourceRadioButton; + // End of variables declaration//GEN-END:variables + + void setDataModel(DataSourceComboBoxModel dataSourceComboBoxModel) { + this.dataSourcesList = dataSourceComboBoxModel; + this.selectDataSourceComboBox.setModel(dataSourcesList); + } + + void rigForMultipleDataSources(boolean multipleDataSources) { + this.withinDataSourceRadioButton.setEnabled(multipleDataSources); + this.allDataSourcesRadioButton.setSelected(!multipleDataSources); + this.withinDataSourceRadioButton.setSelected(multipleDataSources); + this.withinDataSourceSelected(multipleDataSources); + + } + + void setDataSourceMap(Map dataSourceMap) { + this.dataSourceMap.clear(); + this.dataSourceMap.putAll(dataSourceMap); + } + + @NbBundle.Messages({ + "IntraCasePanel.areSearchCriteriaMet.message=Cannot run intra-case correlation search." + }) + boolean areSearchCriteriaMet() { + if(this.dataSourceMap.isEmpty()){ + this.errorMessage = Bundle.IntraCasePanel_areSearchCriteriaMet_message(); + return false; + } else { + return true; + } + } + + String getErrorMessage() { + return this.errorMessage; + } +} \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleInterCaseCommonAttributeSearcher.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleInterCaseCommonAttributeSearcher.java new file mode 100644 index 0000000000..6c06da8b38 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleInterCaseCommonAttributeSearcher.java @@ -0,0 +1,86 @@ +/* + * + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.commonfilesearch; + +import java.sql.SQLException; +import java.util.List; +import java.util.Map; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * + * + */ +public class SingleInterCaseCommonAttributeSearcher extends InterCaseCommonAttributeSearcher { + + private final int corrleationCaseId; + private String correlationCaseName; + + /** + * + * @param correlationCaseId + * @param filterByMediaMimeType + * @param filterByDocMimeType + * @throws EamDbException + */ + public SingleInterCaseCommonAttributeSearcher(int correlationCaseId, Map dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) throws EamDbException { + super(dataSourceIdMap,filterByMediaMimeType, filterByDocMimeType); + + this.corrleationCaseId = correlationCaseId; + this.correlationCaseName = ""; + } + + /** + * Collect metadata required to render the tree table where matches must + * occur in the case with the given ID. + * + * @param correlationCaseId id of case where matches must occur (no other matches will be shown) + * @return business object needed to populate tree table with results + * @throws TskCoreException + * @throws NoCurrentCaseException + * @throws SQLException + * @throws EamDbException + */ + @Override + public CommonAttributeSearchResults findFiles() throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException { + + CorrelationCase cCase = this.getCorrelationCaseFromId(this.corrleationCaseId); + correlationCaseName = cCase.getDisplayName(); + return this.findFiles(cCase); + } + + CommonAttributeSearchResults findFiles(CorrelationCase correlationCase) throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException { + InterCaseSearchResultsProcessor eamDbAttrInst = new InterCaseSearchResultsProcessor(this.getDataSourceIdToNameMap()); + Map> interCaseCommonFiles = eamDbAttrInst.findSingleInterCaseCommonAttributeValues(Case.getCurrentCase(), correlationCase); + + return new CommonAttributeSearchResults(interCaseCommonFiles); + } + + @Override + String buildTabTitle() { + final String buildCategorySelectionString = this.buildCategorySelectionString(); + final String titleTemplate = Bundle.AbstractCommonFilesMetadataBuilder_buildTabTitle_titleInterSingle(); + return String.format(titleTemplate, new Object[]{correlationCaseName, buildCategorySelectionString}); + } +} \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleDataSource.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleIntraCaseCommonAttributeSearcher.java similarity index 82% rename from Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleDataSource.java rename to Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleIntraCaseCommonAttributeSearcher.java index f22715960f..02bd480093 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleDataSource.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/SingleIntraCaseCommonAttributeSearcher.java @@ -25,7 +25,7 @@ import org.sleuthkit.datamodel.TskData.FileKnown; /** * Provides logic for selecting common files from a single data source. */ -final public class SingleDataSource extends CommonFilesMetadataBuilder { +final public class SingleIntraCaseCommonAttributeSearcher extends IntraCaseCommonAttributeSearcher { private static final String WHERE_CLAUSE = "%s md5 in (select md5 from tsk_files where md5 in (select md5 from tsk_files where (known != "+ FileKnown.KNOWN.getFileKnownValue() + " OR known IS NULL) and data_source_obj_id=%s%s) GROUP BY md5 HAVING COUNT(DISTINCT data_source_obj_id) > 1) order by md5"; //NON-NLS private final Long selectedDataSourceId; @@ -41,7 +41,7 @@ final public class SingleDataSource extends CommonFilesMetadataBuilder { * @param filterByDocMimeType match only on files whose mime types can be * broadly categorized as document types */ - public SingleDataSource(Long dataSourceId, Map dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) { + public SingleIntraCaseCommonAttributeSearcher(Long dataSourceId, Map dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) { super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType); this.selectedDataSourceId = dataSourceId; this.dataSourceName = dataSourceIdMap.get(this.selectedDataSourceId); @@ -50,13 +50,13 @@ final public class SingleDataSource extends CommonFilesMetadataBuilder { @Override protected String buildSqlSelectStatement() { Object[] args = new String[]{SELECT_PREFIX, Long.toString(this.selectedDataSourceId), determineMimeTypeFilter()}; - return String.format(SingleDataSource.WHERE_CLAUSE, args); + return String.format(SingleIntraCaseCommonAttributeSearcher.WHERE_CLAUSE, args); } @Override - protected String buildTabTitle() { + public String buildTabTitle() { final String buildCategorySelectionString = this.buildCategorySelectionString(); - final String titleTemplate = Bundle.CommonFilesMetadataBuilder_buildTabTitle_titleSingle(); + final String titleTemplate = Bundle.AbstractCommonFilesMetadataBuilder_buildTabTitle_titleIntraSingle(); return String.format(titleTemplate, new Object[]{this.dataSourceName, buildCategorySelectionString}); } -} +} \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java b/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java index 9cd13a2f4a..002f87acba 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java @@ -18,10 +18,11 @@ */ package org.sleuthkit.autopsy.datamodel; -import org.sleuthkit.autopsy.commonfilesearch.CommonFilesNode; -import org.sleuthkit.autopsy.commonfilesearch.FileInstanceNode; +import org.sleuthkit.autopsy.commonfilesearch.CentralRepoCommonAttributeInstanceNode; +import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeSearchResultRootNode; import org.sleuthkit.autopsy.commonfilesearch.InstanceCountNode; -import org.sleuthkit.autopsy.commonfilesearch.Md5Node; +import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeValueNode; +import org.sleuthkit.autopsy.commonfilesearch.CaseDBCommonAttributeInstanceNode; import org.sleuthkit.autopsy.datamodel.DeletedContent.DeletedContentsChildren.DeletedContentNode; import org.sleuthkit.autopsy.datamodel.DeletedContent.DeletedContentsNode; import org.sleuthkit.autopsy.datamodel.FileSize.FileSizeRootChildren.FileSizeNode; @@ -115,11 +116,13 @@ public interface DisplayableItemNodeVisitor { T visit(InterestingHits.SetNameNode ihsn); - T visit(Md5Node mn); + T visit(CommonAttributeValueNode cavn); - T visit(CommonFilesNode cfn); + T visit(CommonAttributeSearchResultRootNode cfn); - T visit(FileInstanceNode fin); + T visit(CaseDBCommonAttributeInstanceNode fin); + + T visit(CentralRepoCommonAttributeInstanceNode crfin); T visit(InstanceCountNode icn); @@ -192,17 +195,17 @@ public interface DisplayableItemNodeVisitor { protected abstract T defaultVisit(DisplayableItemNode c); @Override - public T visit(FileInstanceNode fin) { + public T visit(CaseDBCommonAttributeInstanceNode fin) { return defaultVisit(fin); } @Override - public T visit(Md5Node mn) { - return defaultVisit(mn); + public T visit(CommonAttributeValueNode cavn) { + return defaultVisit(cavn); } @Override - public T visit(CommonFilesNode cfn) { + public T visit(CommonAttributeSearchResultRootNode cfn) { return defaultVisit(cfn); } @@ -210,6 +213,11 @@ public interface DisplayableItemNodeVisitor { public T visit(InstanceCountNode icn){ return defaultVisit(icn); } + + @Override + public T visit(CentralRepoCommonAttributeInstanceNode crfin){ + return defaultVisit(crfin); + } @Override public T visit(DirectoryNode dn) { diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypeExtensions.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypeExtensions.java index eafa8377b2..f09238e507 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypeExtensions.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypeExtensions.java @@ -27,7 +27,7 @@ import java.util.List; */ public class FileTypeExtensions { - private final static List IMAGE_EXTENSIONS = Arrays.asList(".jpg", ".jpeg", ".png", ".psd", ".nef", ".tiff", ".bmp", ".tec"); //NON-NLS + private final static List IMAGE_EXTENSIONS = Arrays.asList(".jpg", ".jpeg", ".png", ".psd", ".nef", ".tiff", ".bmp", ".tec", ".tif"); //NON-NLS private final static List VIDEO_EXTENSIONS = Arrays.asList(".aaf", ".3gp", ".asf", ".avi", ".m1v", ".m2v", //NON-NLS ".m4v", ".mp4", ".mov", ".mpeg", ".mpg", ".mpe", ".mp4", ".rm", ".wmv", ".mpv", ".flv", ".swf"); //NON-NLS private final static List AUDIO_EXTENSIONS = Arrays.asList(".aiff", ".aif", ".flac", ".wav", ".m4a", ".ape", //NON-NLS diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java index fd13e30d62..098789597c 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java @@ -41,7 +41,6 @@ import org.sleuthkit.autopsy.actions.AddBlackboardArtifactTagAction; import org.sleuthkit.autopsy.actions.AddContentTagAction; import org.sleuthkit.autopsy.actions.DeleteFileBlackboardArtifactTagAction; import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction; -import org.sleuthkit.autopsy.commonfilesearch.FileInstanceNode; import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint; import org.sleuthkit.autopsy.coreutils.Logger; @@ -56,15 +55,17 @@ import org.sleuthkit.autopsy.datamodel.FileNode; import org.sleuthkit.autopsy.datamodel.FileTypeExtensions; import org.sleuthkit.autopsy.datamodel.FileTypes.FileTypesNode; import org.sleuthkit.autopsy.commonfilesearch.InstanceCountNode; -import org.sleuthkit.autopsy.commonfilesearch.Md5Node; +import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeValueNode; +import org.sleuthkit.autopsy.commonfilesearch.CentralRepoCommonAttributeInstanceNode; import org.sleuthkit.autopsy.datamodel.LayoutFileNode; import org.sleuthkit.autopsy.datamodel.LocalFileNode; import org.sleuthkit.autopsy.datamodel.LocalDirectoryNode; import org.sleuthkit.autopsy.datamodel.NodeSelectionInfo; import org.sleuthkit.autopsy.datamodel.Reports; import org.sleuthkit.autopsy.datamodel.SlackFileNode; +import org.sleuthkit.autopsy.commonfilesearch.CaseDBCommonAttributeInstanceNode; import org.sleuthkit.autopsy.datamodel.VirtualDirectoryNode; -import static org.sleuthkit.autopsy.directorytree.Bundle.*; +import static org.sleuthkit.autopsy.directorytree.Bundle.DataResultFilterNode_viewSourceArtifact_text; import org.sleuthkit.autopsy.modules.embeddedfileextractor.ExtractArchiveWithPasswordAction; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; @@ -529,12 +530,17 @@ public class DataResultFilterNode extends FilterNode { } @Override - public AbstractAction visit(Md5Node md5n){ + public AbstractAction visit(CommonAttributeValueNode md5n){ return null; } @Override - public AbstractAction visit(FileInstanceNode fin){ + public AbstractAction visit(CaseDBCommonAttributeInstanceNode fin){ + return null; + } + + @Override + public AbstractAction visit(CentralRepoCommonAttributeInstanceNode iccan){ return null; } diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepoDatamodelTest.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepoDatamodelTest.java index ce96fc0efe..05c454a962 100755 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepoDatamodelTest.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepoDatamodelTest.java @@ -838,7 +838,7 @@ public class CentralRepoDatamodelTest extends TestCase { // This is the expected behavior } - // Test getting instances with expected resuls + // Test getting instances with expected results try { List instances = EamDb.getInstance().getArtifactInstancesByTypeValue(fileType, inAllDataSourcesHash); assertTrue("getArtifactInstancesByTypeValue returned " + instances.size() + " results - expected 3", instances.size() == 3); @@ -1123,6 +1123,34 @@ public class CentralRepoDatamodelTest extends TestCase { } catch (EamDbException ex) { // This is the expected } + + // Test running processinstance which queries all rows from instances table + try { + // Add two instances to the central repository and use the callback query to verify we can see them + CorrelationAttribute attr = new CorrelationAttribute(fileType, callbackTestFileHash); + CorrelationAttributeInstance inst1 = new CorrelationAttributeInstance(case1, dataSource1fromCase1, callbackTestFilePath1); + CorrelationAttributeInstance inst2 = new CorrelationAttributeInstance(case1, dataSource1fromCase1, callbackTestFilePath2); + attr.addInstance(inst1); + attr.addInstance(inst2); + EamDb DbManager = EamDb.getInstance(); + DbManager.addArtifact(attr); + AttributeInstanceTableCallback instancetableCallback = new AttributeInstanceTableCallback(); + DbManager.processInstanceTableWhere(fileType, String.format("id = %s", attr.getID()), instancetableCallback); + int count1 = instancetableCallback.getCounter(); + int count2 = instancetableCallback.getCounterNamingConvention(); + assertTrue("Process Instance count with filepath naming convention: " + count2 + "-expected 2", count2 == 2); + assertTrue("Process Instance count with filepath without naming convention: " + count1 + "-expected greater than 0", count1 > 0); + } catch (EamDbException ex) { + Exceptions.printStackTrace(ex); + } + + try { + //test null inputs + EamDb.getInstance().processInstanceTableWhere(null, null, null); + Assert.fail("processinstance method failed to throw exception for null type value"); + } catch (EamDbException ex) { + // This is the expected + } } /** diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithHashAndFileType.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithHashAndFileType.java deleted file mode 100644 index a599763346..0000000000 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithHashAndFileType.java +++ /dev/null @@ -1,465 +0,0 @@ -/* - * - * Autopsy Forensic Browser - * - * Copyright 2018 Basis Technology Corp. - * Contact: carrier sleuthkit org - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sleuthkit.autopsy.commonfilessearch; - -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import junit.framework.Test; -import org.netbeans.junit.NbModuleSuite; -import org.netbeans.junit.NbTestCase; -import org.openide.util.Exceptions; -import org.python.icu.impl.Assert; -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.autopsy.commonfilesearch.AllDataSourcesCommonFilesAlgorithm; -import org.sleuthkit.autopsy.commonfilesearch.CommonFilesMetadata; -import org.sleuthkit.autopsy.commonfilesearch.CommonFilesMetadataBuilder; -import org.sleuthkit.autopsy.commonfilesearch.SingleDataSource; -import static org.sleuthkit.autopsy.commonfilessearch.IntraCaseUtils.*; -import org.sleuthkit.autopsy.ingest.IngestJobSettings; -import org.sleuthkit.autopsy.ingest.IngestJobSettings.IngestType; -import org.sleuthkit.autopsy.ingest.IngestModuleTemplate; -import org.sleuthkit.autopsy.modules.filetypeid.FileTypeIdModuleFactory; -import org.sleuthkit.autopsy.modules.hashdatabase.HashLookupModuleFactory; -import org.sleuthkit.autopsy.testutils.IngestUtils; -import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.TskCoreException; - -/** - * Add set 1, set 2, set 3, and set 4 to case and ingest with hash algorithm. - */ -public class IngestedWithHashAndFileType extends NbTestCase { - - public static Test suite() { - NbModuleSuite.Configuration conf = NbModuleSuite.createConfiguration(IngestedWithHashAndFileType.class). - clusters(".*"). - enableModules(".*"); - return conf.suite(); - } - - private final IntraCaseUtils utils; - - public IngestedWithHashAndFileType(String name) { - super(name); - - this.utils = new IntraCaseUtils(this, "IngestedWithHashAndFileTypeTests"); - } - - @Override - public void setUp() { - this.utils.setUp(); - - IngestModuleTemplate hashLookupTemplate = IngestUtils.getIngestModuleTemplate(new HashLookupModuleFactory()); - IngestModuleTemplate mimeTypeLookupTemplate = IngestUtils.getIngestModuleTemplate(new FileTypeIdModuleFactory()); - - ArrayList templates = new ArrayList<>(); - templates.add(hashLookupTemplate); - templates.add(mimeTypeLookupTemplate); - - IngestJobSettings ingestJobSettings = new IngestJobSettings(IngestedWithHashAndFileType.class.getCanonicalName(), IngestType.FILES_ONLY, templates); - - try { - IngestUtils.runIngestJob(Case.getCurrentCaseThrows().getDataSources(), ingestJobSettings); - } catch (NoCurrentCaseException | TskCoreException ex) { - Exceptions.printStackTrace(ex); - Assert.fail(ex); - } - } - - @Override - public void tearDown() { - this.utils.tearDown(); - } - - /** - * Find all matches & all file types. Confirm file.jpg is found on all three - * and file.docx is found on two. - */ - public void testOneA() { - try { - Map dataSources = this.utils.getDataSourceMap(); - - CommonFilesMetadataBuilder allSourcesBuilder = new AllDataSourcesCommonFilesAlgorithm(dataSources, false, false); - CommonFilesMetadata metadata = allSourcesBuilder.findCommonFiles(); - - Map objectIdToDataSource = IntraCaseUtils.mapFileInstancesToDataSources(metadata); - - List files = IntraCaseUtils.getFiles(objectIdToDataSource.keySet()); - - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 2)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 1)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 1)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0)); - - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 1)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET2, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 1)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0)); - - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0)); - assertTrue(IntraCaseUtils.verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0)); - - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0)); - - } catch (NoCurrentCaseException | TskCoreException | SQLException ex) { - Exceptions.printStackTrace(ex); - Assert.fail(ex); - } - } - - /** - * Find all matches & only image types. Confirm file.jpg is found on all - * three. - */ - public void testOneB() { - try { - Map dataSources = this.utils.getDataSourceMap(); - - CommonFilesMetadataBuilder allSourcesBuilder = new AllDataSourcesCommonFilesAlgorithm(dataSources, true, false); - CommonFilesMetadata metadata = allSourcesBuilder.findCommonFiles(); - - Map objectIdToDataSource = mapFileInstancesToDataSources(metadata); - - List files = getFiles(objectIdToDataSource.keySet()); - - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 2)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 1)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 1)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0)); - - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET2, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0)); - - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0)); - - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0)); - - } catch (NoCurrentCaseException | TskCoreException | SQLException ex) { - Exceptions.printStackTrace(ex); - Assert.fail(ex); - } - } - - /** - * Find all matches & only image types. Confirm file.jpg is found on all - * three. - */ - public void testOneC() { - try { - Map dataSources = this.utils.getDataSourceMap(); - - CommonFilesMetadataBuilder allSourcesBuilder = new AllDataSourcesCommonFilesAlgorithm(dataSources, false, true); - CommonFilesMetadata metadata = allSourcesBuilder.findCommonFiles(); - - Map objectIdToDataSource = mapFileInstancesToDataSources(metadata); - - List files = getFiles(objectIdToDataSource.keySet()); - - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0)); - - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 1)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET2, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 1)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0)); - - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0)); - - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0)); - - } catch (NoCurrentCaseException | TskCoreException | SQLException ex) { - Exceptions.printStackTrace(ex); - Assert.fail(ex); - } - } - - /** - * Find matches on set 1 & all file types. Confirm same results. - * - */ - public void testTwoA() { - try { - Map dataSources = this.utils.getDataSourceMap(); - Long first = getDataSourceIdByName(SET1, dataSources); - - CommonFilesMetadataBuilder singleSourceBuilder = new SingleDataSource(first, dataSources, false, false); - CommonFilesMetadata metadata = singleSourceBuilder.findCommonFiles(); - - Map objectIdToDataSource = mapFileInstancesToDataSources(metadata); - - List files = getFiles(objectIdToDataSource.keySet()); - - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 2)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 1)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 1)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0)); - - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 1)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET2, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 1)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0)); - - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0)); - - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0)); - - } catch (NoCurrentCaseException | TskCoreException | SQLException ex) { - Exceptions.printStackTrace(ex); - Assert.fail(ex); - } - } - - /** - * Find matches on set 1 & only media types. Confirm same results. - * - */ - public void testTwoB() { - try { - Map dataSources = this.utils.getDataSourceMap(); - Long first = getDataSourceIdByName(SET1, dataSources); - - CommonFilesMetadataBuilder singleSourceBuilder = new SingleDataSource(first, dataSources, true, false); - CommonFilesMetadata metadata = singleSourceBuilder.findCommonFiles(); - - Map objectIdToDataSource = mapFileInstancesToDataSources(metadata); - - List files = getFiles(objectIdToDataSource.keySet()); - - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 2)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 1)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 1)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0)); - - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET2, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0)); - - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0)); - - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0)); - - } catch (NoCurrentCaseException | TskCoreException | SQLException ex) { - Exceptions.printStackTrace(ex); - Assert.fail(ex); - } - } - - /** - * Find matches on set 1 & all file types. Confirm same results. - * - */ - public void testTwoC() { - try { - Map dataSources = this.utils.getDataSourceMap(); - Long first = getDataSourceIdByName(SET1, dataSources); - - CommonFilesMetadataBuilder singleSourceBuilder = new SingleDataSource(first, dataSources, false, true); - CommonFilesMetadata metadata = singleSourceBuilder.findCommonFiles(); - - Map objectIdToDataSource = mapFileInstancesToDataSources(metadata); - - List files = getFiles(objectIdToDataSource.keySet()); - - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0)); - - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 1)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET2, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 1)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0)); - - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0)); - - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0)); - - } catch (NoCurrentCaseException | TskCoreException | SQLException ex) { - Exceptions.printStackTrace(ex); - Assert.fail(ex); - } - } - - /** - * Find matches on set 2 & all file types: Confirm file.jpg. - * - */ - public void testThree() { - try { - Map dataSources = this.utils.getDataSourceMap(); - Long second = getDataSourceIdByName(SET2, dataSources); - - CommonFilesMetadataBuilder singleSourceBuilder = new SingleDataSource(second, dataSources, false, false); - CommonFilesMetadata metadata = singleSourceBuilder.findCommonFiles(); - - Map objectIdToDataSource = mapFileInstancesToDataSources(metadata); - - List files = getFiles(objectIdToDataSource.keySet()); - - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 2)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 1)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 1)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0)); - - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET2, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0)); - - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0)); - - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0)); - - } catch (NoCurrentCaseException | TskCoreException | SQLException ex) { - Exceptions.printStackTrace(ex); - Assert.fail(ex); - } - } - - /** - * Find matches on set 4 & all file types: Confirm nothing is found. - */ - public void testFour() { - try { - Map dataSources = this.utils.getDataSourceMap(); - Long last = getDataSourceIdByName(SET4, dataSources); - - CommonFilesMetadataBuilder singleSourceBuilder = new SingleDataSource(last, dataSources, false, false); - CommonFilesMetadata metadata = singleSourceBuilder.findCommonFiles(); - - Map objectIdToDataSource = mapFileInstancesToDataSources(metadata); - - List files = getFiles(objectIdToDataSource.keySet()); - - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0)); - - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0)); - - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0)); - - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0)); - - } catch (NoCurrentCaseException | TskCoreException | SQLException ex) { - Exceptions.printStackTrace(ex); - Assert.fail(ex); - } - } - - /** - * Find matches on set 3 & all file types: Confirm file.jpg and file.docx. - */ - public void testFive() { - try { - Map dataSources = this.utils.getDataSourceMap(); - Long third = getDataSourceIdByName(SET3, dataSources); - - CommonFilesMetadataBuilder singleSourceBuilder = new SingleDataSource(third, dataSources, false, false); - CommonFilesMetadata metadata = singleSourceBuilder.findCommonFiles(); - - Map objectIdToDataSource = mapFileInstancesToDataSources(metadata); - - List files = getFiles(objectIdToDataSource.keySet()); - - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 2)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 1)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 1)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0)); - - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 1)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET2, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 1)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0)); - - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0)); - - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0)); - assertTrue(verifyFileExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0)); - - } catch (NoCurrentCaseException | TskCoreException | SQLException ex) { - Exceptions.printStackTrace(ex); - Assert.fail(ex); - } - } -} diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithHashAndFileTypeInterCaseTests.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithHashAndFileTypeInterCaseTests.java new file mode 100644 index 0000000000..74a42172cb --- /dev/null +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithHashAndFileTypeInterCaseTests.java @@ -0,0 +1,185 @@ +/* + * + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.commonfilessearch; + +import java.sql.SQLException; +import java.util.Map; +import junit.framework.Test; +import org.netbeans.junit.NbModuleSuite; +import org.netbeans.junit.NbTestCase; +import org.openide.util.Exceptions; +import org.python.icu.impl.Assert; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; +import org.sleuthkit.autopsy.commonfilesearch.AbstractCommonAttributeSearcher; +import org.sleuthkit.autopsy.commonfilesearch.AllInterCaseCommonAttributeSearcher; +import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeSearchResults; +import org.sleuthkit.autopsy.commonfilesearch.SingleInterCaseCommonAttributeSearcher; +import static org.sleuthkit.autopsy.commonfilessearch.InterCaseTestUtils.*; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Tests with case 3 as the current case. + * + * If I use the search all cases option: One node for Hash A (1_1_A.jpg, + * 1_2_A.jpg, 3_1_A.jpg) If I search for matches only in Case 1: One node for + * Hash A (1_1_A.jpg, 1_2_A.jpg, 3_1_A.jpg) If I search for matches only in Case + * 2: No matches If I only search in the current case (existing mode), allowing + * all data sources: One node for Hash C (3_1_C.jpg, 3_2_C.jpg) + */ +public class IngestedWithHashAndFileTypeInterCaseTests extends NbTestCase { + + private final InterCaseTestUtils utils; + + public static Test suite() { + NbModuleSuite.Configuration conf = NbModuleSuite.createConfiguration(IngestedWithHashAndFileTypeInterCaseTests.class). + clusters(".*"). + enableModules(".*"); + return conf.suite(); + } + + public IngestedWithHashAndFileTypeInterCaseTests(String name) { + super(name); + this.utils = new InterCaseTestUtils(this); + } + + @Override + public void setUp(){ + this.utils.clearTestDir(); + try { + this.utils.enableCentralRepo(); + this.utils.createCases(this.utils.getIngestSettingsForHashAndFileType(), InterCaseTestUtils.CASE3); + } catch (TskCoreException | EamDbException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } + + @Override + public void tearDown(){ + this.utils.clearTestDir(); + this.utils.tearDown(); + } + + /** + * Search All cases with no file type filtering. + */ + public void testOne() { + try { + Map dataSources = this.utils.getDataSourceMap(); + + //note that the params false and false are presently meaningless because that feature is not supported yet + AbstractCommonAttributeSearcher builder = new AllInterCaseCommonAttributeSearcher(dataSources, false, false); + + CommonAttributeSearchResults metadata = builder.findFiles(); + + assertTrue("Results should not be empty", metadata.size() != 0); + + //case 1 data set 1 + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_0_DAT, CASE1_DATASET_1, CASE1, 0)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_PDF, CASE1_DATASET_1, CASE1, 1)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_JPG, CASE1_DATASET_1, CASE1, 1)); + + //case 1 data set 2 + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_0_DAT, CASE1_DATASET_2, CASE1, 0)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_PDF, CASE1_DATASET_2, CASE1, 1)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_JPG, CASE1_DATASET_2, CASE1, 1)); + + //case 2 data set 1 + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_B_PDF, CASE2_DATASET_1, CASE2, 0)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_B_JPG, CASE2_DATASET_1, CASE2, 0)); + + //case 2 data set 2 + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_PDF, CASE2_DATASET_2, CASE2, 1)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_JPG, CASE2_DATASET_2, CASE2, 1)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_D_DOC, CASE2_DATASET_2, CASE2, 1)); + + //case 3 data set 1 + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_JPG, CASE3_DATASET_1, CASE3, 1)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_PDF, CASE3_DATASET_1, CASE3, 1)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_C_JPG, CASE3_DATASET_1, CASE3, 0)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_C_PDF, CASE3_DATASET_1, CASE3, 0)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_D_JPG, CASE3_DATASET_1, CASE3, 0)); + + //case 3 data set 2 + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_C_JPG, CASE3_DATASET_2, CASE3, 0)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_C_PDF, CASE3_DATASET_2, CASE3, 0)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_D_DOC, CASE3_DATASET_2, CASE3, 1)); + + + } catch (TskCoreException | NoCurrentCaseException | SQLException | EamDbException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } + + /** + * Search All cases with no file type filtering. + */ + public void testTwo() { + try { + Map dataSources = this.utils.getDataSourceMap(); + + int matchesMustAlsoBeFoundInThisCase = this.utils.getCaseMap().get(CASE2); + + AbstractCommonAttributeSearcher builder = new SingleInterCaseCommonAttributeSearcher(matchesMustAlsoBeFoundInThisCase, dataSources, false, false); + + CommonAttributeSearchResults metadata = builder.findFiles(); + + assertTrue("Results should not be empty", metadata.size() != 0); + + //case 1 data set 1 + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_0_DAT, CASE1_DATASET_1, CASE1, 0)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_PDF, CASE1_DATASET_1, CASE1, 1)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_JPG, CASE1_DATASET_1, CASE1, 1)); + + //case 1 data set 2 + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_0_DAT, CASE1_DATASET_2, CASE1, 0)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_PDF, CASE1_DATASET_2, CASE1, 1)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_JPG, CASE1_DATASET_2, CASE1, 1)); + + //case 2 data set 1 + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_B_PDF, CASE2_DATASET_1, CASE2, 0)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_B_JPG, CASE2_DATASET_1, CASE2, 0)); + + //case 2 data set 2 + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_PDF, CASE2_DATASET_2, CASE2, 1)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_JPG, CASE2_DATASET_2, CASE2, 1)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_D_DOC, CASE2_DATASET_2, CASE2, 1)); + + //case 3 data set 1 + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_JPG, CASE3_DATASET_1, CASE3, 1)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_A_PDF, CASE3_DATASET_1, CASE3, 1)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_C_JPG, CASE3_DATASET_1, CASE3, 0)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_C_PDF, CASE3_DATASET_1, CASE3, 0)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_D_JPG, CASE3_DATASET_1, CASE3, 0)); + + //case 3 data set 2 + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_C_JPG, CASE3_DATASET_2, CASE3, 0)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_C_PDF, CASE3_DATASET_2, CASE3, 0)); + assertTrue(verifyInstanceExistanceAndCount(metadata, HASH_D_DOC, CASE3_DATASET_2, CASE3, 1)); + + + } catch (TskCoreException | NoCurrentCaseException | SQLException | EamDbException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } +} diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithHashAndFileTypeIntraCaseTests.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithHashAndFileTypeIntraCaseTests.java new file mode 100644 index 0000000000..d1a004d281 --- /dev/null +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithHashAndFileTypeIntraCaseTests.java @@ -0,0 +1,466 @@ +/* + * + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.commonfilessearch; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import junit.framework.Test; +import org.netbeans.junit.NbModuleSuite; +import org.netbeans.junit.NbTestCase; +import org.openide.util.Exceptions; +import org.python.icu.impl.Assert; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; +import org.sleuthkit.autopsy.commonfilesearch.AbstractCommonAttributeSearcher; +import org.sleuthkit.autopsy.commonfilesearch.AllIntraCaseCommonAttributeSearcher; +import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeSearchResults; +import org.sleuthkit.autopsy.commonfilesearch.SingleIntraCaseCommonAttributeSearcher; +import static org.sleuthkit.autopsy.commonfilessearch.IntraCaseTestUtils.*; +import org.sleuthkit.autopsy.ingest.IngestJobSettings; +import org.sleuthkit.autopsy.ingest.IngestJobSettings.IngestType; +import org.sleuthkit.autopsy.ingest.IngestModuleTemplate; +import org.sleuthkit.autopsy.modules.filetypeid.FileTypeIdModuleFactory; +import org.sleuthkit.autopsy.modules.hashdatabase.HashLookupModuleFactory; +import org.sleuthkit.autopsy.testutils.IngestUtils; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Add set 1, set 2, set 3, and set 4 to case and ingest with hash algorithm. + */ +public class IngestedWithHashAndFileTypeIntraCaseTests extends NbTestCase { + + public static Test suite() { + NbModuleSuite.Configuration conf = NbModuleSuite.createConfiguration(IngestedWithHashAndFileTypeIntraCaseTests.class). + clusters(".*"). + enableModules(".*"); + return conf.suite(); + } + + private final IntraCaseTestUtils utils; + + public IngestedWithHashAndFileTypeIntraCaseTests(String name) { + super(name); + + this.utils = new IntraCaseTestUtils(this, "IngestedWithHashAndFileTypeTests"); + } + + @Override + public void setUp() { + this.utils.setUp(); + + IngestModuleTemplate hashLookupTemplate = IngestUtils.getIngestModuleTemplate(new HashLookupModuleFactory()); + IngestModuleTemplate mimeTypeLookupTemplate = IngestUtils.getIngestModuleTemplate(new FileTypeIdModuleFactory()); + + ArrayList templates = new ArrayList<>(); + templates.add(hashLookupTemplate); + templates.add(mimeTypeLookupTemplate); + + IngestJobSettings ingestJobSettings = new IngestJobSettings(IngestedWithHashAndFileTypeIntraCaseTests.class.getCanonicalName(), IngestType.FILES_ONLY, templates); + + try { + IngestUtils.runIngestJob(Case.getCurrentCaseThrows().getDataSources(), ingestJobSettings); + } catch (NoCurrentCaseException | TskCoreException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } + + @Override + public void tearDown() { + this.utils.tearDown(); + } + + /** + * Find all matches & all file types. Confirm file.jpg is found on all three + * and file.docx is found on two. + */ + public void testOneA() { + try { + Map dataSources = this.utils.getDataSourceMap(); + + AbstractCommonAttributeSearcher allSourcesBuilder = new AllIntraCaseCommonAttributeSearcher(dataSources, false, false); + CommonAttributeSearchResults metadata = allSourcesBuilder.findFiles(); + + Map objectIdToDataSource = IntraCaseTestUtils.mapFileInstancesToDataSources(metadata); + + List files = IntraCaseTestUtils.getFiles(objectIdToDataSource.keySet()); + + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 2)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 1)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 1)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0)); + + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 1)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET2, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 1)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0)); + + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0)); + + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0)); + + } catch (NoCurrentCaseException | TskCoreException | SQLException | EamDbException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } + + /** + * Find all matches & only image types. Confirm file.jpg is found on all + * three. + */ + public void testOneB() { + try { + Map dataSources = this.utils.getDataSourceMap(); + + AbstractCommonAttributeSearcher allSourcesBuilder = new AllIntraCaseCommonAttributeSearcher(dataSources, true, false); + CommonAttributeSearchResults metadata = allSourcesBuilder.findFiles(); + + Map objectIdToDataSource = mapFileInstancesToDataSources(metadata); + + List files = getFiles(objectIdToDataSource.keySet()); + + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 2)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 1)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 1)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0)); + + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET2, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0)); + + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0)); + + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0)); + + } catch (NoCurrentCaseException | TskCoreException | SQLException | EamDbException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } + + /** + * Find all matches & only image types. Confirm file.jpg is found on all + * three. + */ + public void testOneC() { + try { + Map dataSources = this.utils.getDataSourceMap(); + + AbstractCommonAttributeSearcher allSourcesBuilder = new AllIntraCaseCommonAttributeSearcher(dataSources, false, true); + CommonAttributeSearchResults metadata = allSourcesBuilder.findFiles(); + + Map objectIdToDataSource = mapFileInstancesToDataSources(metadata); + + List files = getFiles(objectIdToDataSource.keySet()); + + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0)); + + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 1)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET2, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 1)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0)); + + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0)); + + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0)); + + } catch (NoCurrentCaseException | TskCoreException | SQLException | EamDbException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } + + /** + * Find matches on set 1 & all file types. Confirm same results. + * + */ + public void testTwoA() { + try { + Map dataSources = this.utils.getDataSourceMap(); + Long first = getDataSourceIdByName(SET1, dataSources); + + AbstractCommonAttributeSearcher singleSourceBuilder = new SingleIntraCaseCommonAttributeSearcher(first, dataSources, false, false); + CommonAttributeSearchResults metadata = singleSourceBuilder.findFiles(); + + Map objectIdToDataSource = mapFileInstancesToDataSources(metadata); + + List files = getFiles(objectIdToDataSource.keySet()); + + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 2)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 1)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 1)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0)); + + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 1)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET2, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 1)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0)); + + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0)); + + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0)); + + } catch (NoCurrentCaseException | TskCoreException | SQLException | EamDbException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } + + /** + * Find matches on set 1 & only media types. Confirm same results. + * + */ + public void testTwoB() { + try { + Map dataSources = this.utils.getDataSourceMap(); + Long first = getDataSourceIdByName(SET1, dataSources); + + AbstractCommonAttributeSearcher singleSourceBuilder = new SingleIntraCaseCommonAttributeSearcher(first, dataSources, true, false); + CommonAttributeSearchResults metadata = singleSourceBuilder.findFiles(); + + Map objectIdToDataSource = mapFileInstancesToDataSources(metadata); + + List files = getFiles(objectIdToDataSource.keySet()); + + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 2)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 1)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 1)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0)); + + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET2, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0)); + + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0)); + + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0)); + + } catch (NoCurrentCaseException | TskCoreException | SQLException | EamDbException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } + + /** + * Find matches on set 1 & all file types. Confirm same results. + * + */ + public void testTwoC() { + try { + Map dataSources = this.utils.getDataSourceMap(); + Long first = getDataSourceIdByName(SET1, dataSources); + + AbstractCommonAttributeSearcher singleSourceBuilder = new SingleIntraCaseCommonAttributeSearcher(first, dataSources, false, true); + CommonAttributeSearchResults metadata = singleSourceBuilder.findFiles(); + + Map objectIdToDataSource = mapFileInstancesToDataSources(metadata); + + List files = getFiles(objectIdToDataSource.keySet()); + + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0)); + + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 1)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET2, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 1)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0)); + + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0)); + + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0)); + + } catch (NoCurrentCaseException | TskCoreException | SQLException | EamDbException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } + + /** + * Find matches on set 2 & all file types: Confirm file.jpg. + * + */ + public void testThree() { + try { + Map dataSources = this.utils.getDataSourceMap(); + Long second = getDataSourceIdByName(SET2, dataSources); + + AbstractCommonAttributeSearcher singleSourceBuilder = new SingleIntraCaseCommonAttributeSearcher(second, dataSources, false, false); + CommonAttributeSearchResults metadata = singleSourceBuilder.findFiles(); + + Map objectIdToDataSource = mapFileInstancesToDataSources(metadata); + + List files = getFiles(objectIdToDataSource.keySet()); + + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 2)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 1)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 1)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0)); + + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET2, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0)); + + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0)); + + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0)); + + } catch (NoCurrentCaseException | TskCoreException | SQLException | EamDbException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } + + /** + * Find matches on set 4 & all file types: Confirm nothing is found. + */ + public void testFour() { + try { + Map dataSources = this.utils.getDataSourceMap(); + Long last = getDataSourceIdByName(SET4, dataSources); + + AbstractCommonAttributeSearcher singleSourceBuilder = new SingleIntraCaseCommonAttributeSearcher(last, dataSources, false, false); + CommonAttributeSearchResults metadata = singleSourceBuilder.findFiles(); + + Map objectIdToDataSource = mapFileInstancesToDataSources(metadata); + + List files = getFiles(objectIdToDataSource.keySet()); + + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0)); + + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0)); + + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0)); + + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0)); + + } catch (NoCurrentCaseException | TskCoreException | SQLException | EamDbException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } + + /** + * Find matches on set 3 & all file types: Confirm file.jpg and file.docx. + */ + public void testFive() { + try { + Map dataSources = this.utils.getDataSourceMap(); + Long third = getDataSourceIdByName(SET3, dataSources); + + AbstractCommonAttributeSearcher singleSourceBuilder = new SingleIntraCaseCommonAttributeSearcher(third, dataSources, false, false); + CommonAttributeSearchResults metadata = singleSourceBuilder.findFiles(); + + Map objectIdToDataSource = mapFileInstancesToDataSources(metadata); + + List files = getFiles(objectIdToDataSource.keySet()); + + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET1, 2)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET2, 1)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET3, 1)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, IMG, SET4, 0)); + + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET1, 1)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET2, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET3, 1)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, DOC, SET4, 0)); + + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET1, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET2, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET3, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, PDF, SET4, 0)); + + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET1, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET2, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET3, 0)); + assertTrue(verifyInstanceExistanceAndCount(files, objectIdToDataSource, EMPTY, SET4, 0)); + + } catch (NoCurrentCaseException | TskCoreException | SQLException | EamDbException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } +} diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithNoFileTypes.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithNoFileTypesIntraCaseTests.java similarity index 65% rename from Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithNoFileTypes.java rename to Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithNoFileTypesIntraCaseTests.java index 0090d0f699..d51dfda2fa 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithNoFileTypes.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IngestedWithNoFileTypesIntraCaseTests.java @@ -31,10 +31,10 @@ import org.openide.util.Exceptions; import org.python.icu.impl.Assert; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.autopsy.commonfilesearch.AllDataSourcesCommonFilesAlgorithm; -import org.sleuthkit.autopsy.commonfilesearch.CommonFilesMetadata; -import org.sleuthkit.autopsy.commonfilesearch.CommonFilesMetadataBuilder; -import org.sleuthkit.autopsy.commonfilesearch.SingleDataSource; +import org.sleuthkit.autopsy.commonfilesearch.AllIntraCaseCommonAttributeSearcher; +import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeSearchResults; +import org.sleuthkit.autopsy.commonfilesearch.IntraCaseCommonAttributeSearcher; +import org.sleuthkit.autopsy.commonfilesearch.SingleIntraCaseCommonAttributeSearcher; import org.sleuthkit.autopsy.ingest.IngestJobSettings; import org.sleuthkit.autopsy.ingest.IngestModuleTemplate; import org.sleuthkit.autopsy.modules.hashdatabase.HashLookupModuleFactory; @@ -51,21 +51,21 @@ import org.sleuthkit.datamodel.TskCoreException; * Add images set 1, set 2, set 3, and set 4 to case. Do not run mime type * module. */ -public class IngestedWithNoFileTypes extends NbTestCase { +public class IngestedWithNoFileTypesIntraCaseTests extends NbTestCase { public static Test suite() { - NbModuleSuite.Configuration conf = NbModuleSuite.createConfiguration(IngestedWithNoFileTypes.class). + NbModuleSuite.Configuration conf = NbModuleSuite.createConfiguration(IngestedWithNoFileTypesIntraCaseTests.class). clusters(".*"). enableModules(".*"); return conf.suite(); } - private final IntraCaseUtils utils; + private final IntraCaseTestUtils utils; - public IngestedWithNoFileTypes(String name) { + public IngestedWithNoFileTypesIntraCaseTests(String name) { super(name); - this.utils = new IntraCaseUtils(this, "IngestedWithNoFileTypes"); + this.utils = new IntraCaseTestUtils(this, "IngestedWithNoFileTypes"); } @Override @@ -77,7 +77,7 @@ public class IngestedWithNoFileTypes extends NbTestCase { ArrayList templates = new ArrayList<>(); templates.add(hashLookupTemplate); - IngestJobSettings ingestJobSettings = new IngestJobSettings(IngestedWithHashAndFileType.class.getCanonicalName(), IngestJobSettings.IngestType.FILES_ONLY, templates); + IngestJobSettings ingestJobSettings = new IngestJobSettings(IngestedWithNoFileTypesIntraCaseTests.class.getCanonicalName(), IngestJobSettings.IngestType.FILES_ONLY, templates); try { IngestUtils.runIngestJob(Case.getCurrentCaseThrows().getDataSources(), ingestJobSettings); @@ -97,20 +97,22 @@ public class IngestedWithNoFileTypes extends NbTestCase { * find nothing and no errors should arise. */ public void testOne() { + try { Map dataSources = this.utils.getDataSourceMap(); - CommonFilesMetadataBuilder allSourcesBuilder = new AllDataSourcesCommonFilesAlgorithm(dataSources, true, false); - CommonFilesMetadata metadata = allSourcesBuilder.findCommonFiles(); + IntraCaseCommonAttributeSearcher allSourcesBuilder = new AllIntraCaseCommonAttributeSearcher(dataSources, true, false); + CommonAttributeSearchResults metadata = allSourcesBuilder.findFiles(); - Map objectIdToDataSource = IntraCaseUtils.mapFileInstancesToDataSources(metadata); + Map objectIdToDataSource = IntraCaseTestUtils.mapFileInstancesToDataSources(metadata); - List files = IntraCaseUtils.getFiles(objectIdToDataSource.keySet()); + List files = IntraCaseTestUtils.getFiles(objectIdToDataSource.keySet()); assertTrue(files.isEmpty()); - } catch (NoCurrentCaseException | TskCoreException | SQLException ex) { + } catch (TskCoreException | NoCurrentCaseException | SQLException ex) { Exceptions.printStackTrace(ex); + Assert.fail(ex); } } @@ -121,18 +123,18 @@ public class IngestedWithNoFileTypes extends NbTestCase { public void testTwo() { try { Map dataSources = this.utils.getDataSourceMap(); - Long third = IntraCaseUtils.getDataSourceIdByName(IntraCaseUtils.SET3, dataSources); + Long third = IntraCaseTestUtils.getDataSourceIdByName(IntraCaseTestUtils.SET3, dataSources); - CommonFilesMetadataBuilder singleSourceBuilder = new SingleDataSource(third, dataSources, true, false); - CommonFilesMetadata metadata = singleSourceBuilder.findCommonFiles(); + IntraCaseCommonAttributeSearcher singleSourceBuilder = new SingleIntraCaseCommonAttributeSearcher(third, dataSources, true, false); + CommonAttributeSearchResults metadata = singleSourceBuilder.findFiles(); - Map objectIdToDataSource = IntraCaseUtils.mapFileInstancesToDataSources(metadata); + Map objectIdToDataSource = IntraCaseTestUtils.mapFileInstancesToDataSources(metadata); - List files = IntraCaseUtils.getFiles(objectIdToDataSource.keySet()); + List files = IntraCaseTestUtils.getFiles(objectIdToDataSource.keySet()); assertTrue(files.isEmpty()); - } catch (NoCurrentCaseException | TskCoreException | SQLException ex) { + } catch (Exception ex) { Exceptions.printStackTrace(ex); Assert.fail(ex); } diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/InterCaseTestUtils.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/InterCaseTestUtils.java new file mode 100644 index 0000000000..cf6bd36fbc --- /dev/null +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/InterCaseTestUtils.java @@ -0,0 +1,392 @@ +/* + * + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this testFile except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.commonfilessearch; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.apache.commons.io.FileUtils; +import org.netbeans.junit.NbTestCase; +import org.openide.util.Exceptions; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.ImageDSProcessor; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbPlatformEnum; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbUtil; +import org.sleuthkit.autopsy.centralrepository.datamodel.SqliteEamDbSettings; +import org.sleuthkit.autopsy.ingest.IngestJobSettings; +import org.sleuthkit.autopsy.ingest.IngestJobSettings.IngestType; +import org.sleuthkit.autopsy.ingest.IngestModuleTemplate; +import org.sleuthkit.autopsy.modules.filetypeid.FileTypeIdModuleFactory; +import org.sleuthkit.autopsy.modules.hashdatabase.HashLookupModuleFactory; +import org.sleuthkit.autopsy.testutils.CaseUtils; +import org.sleuthkit.autopsy.testutils.IngestUtils; +import org.sleuthkit.datamodel.TskCoreException; +import org.python.icu.impl.Assert; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; +import org.sleuthkit.autopsy.commonfilesearch.AbstractCommonAttributeInstance; +import org.sleuthkit.autopsy.commonfilesearch.CaseDBCommonAttributeInstanceNode; +import org.sleuthkit.autopsy.commonfilesearch.CentralRepoCommonAttributeInstance; +import org.sleuthkit.autopsy.commonfilesearch.CentralRepoCommonAttributeInstanceNode; +import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeSearchResults; +import org.sleuthkit.autopsy.commonfilesearch.DataSourceLoader; +import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeValue; +import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; +import org.sleuthkit.datamodel.AbstractFile; + +/** + * Utilities for testing intercase correlation feature. + * + * This will be more useful when we add more flush out the intercase + * correlation features and need to add more tests. In particular, + * testing scenarios where we need different cases to be the current case + * will suggest that we create additional test classes, and we will want to + * import this utility in each new intercase test file. + * + * Description of Test Data: + (Note: files of the same name and extension are identical; + files of the same name and differing extension are not identical.) + + Case 1 + +Data Set 1 + - Hash-0.dat [testFile of size 0] + - Hash-A.jpg + - Hash-A.pdf + +Data Set2 + - Hash-0.dat [testFile of size 0] + - Hash-A.jpg + - Hash-A.pdf + Case 2 + +Data Set 1 + - Hash-B.jpg + - Hash-B.pdf + +Data Set 2 + - Hash-A.jpg + - Hash-A.pdf + - Hash_D.doc + Case 3 + +Data Set 1 + - Hash-A.jpg + - Hash-A.pdf + - Hash-C.jpg + - Hash-C.pdf + - Hash-D.jpg + +Data Set 2 + - Hash-C.jpg + - Hash-C.pdf + - Hash-D.doc + */ +class InterCaseTestUtils { + + private static final Path CASE_DIRECTORY_PATH = Paths.get(System.getProperty("java.io.tmpdir"), "InterCaseCommonFilesSearchTest"); + private static final String CR_DB_NAME = "testcentralrepo.db"; + + static final String CASE1 = "Case1"; + static final String CASE2 = "Case2"; + static final String CASE3 = "Case3"; + static final String CASE4 = "Case4"; + + final Path case1DataSet1Path; + final Path case1DataSet2Path; + final Path case2DataSet1Path; + final Path case2DataSet2Path; + final Path case3DataSet1Path; + final Path case3DataSet2Path; + + static final String HASH_0_DAT = "Hash-0.dat"; + static final String HASH_A_JPG = "Hash-A.jpg"; + static final String HASH_A_PDF = "Hash-A.pdf"; + static final String HASH_B_JPG = "Hash-B.jpg"; + static final String HASH_B_PDF = "Hash-B.pdf"; + static final String HASH_C_JPG = "Hash-C.jpg"; + static final String HASH_C_PDF = "Hash-C.pdf"; + static final String HASH_D_JPG = "Hash-D.jpg"; + static final String HASH_D_DOC = "Hash-D.doc"; + + static final String CASE1_DATASET_1 = "c1ds1_v1.vhd"; + static final String CASE1_DATASET_2 = "c1ds2_v1.vhd"; + static final String CASE2_DATASET_1 = "c2ds1_v1.vhd"; + static final String CASE2_DATASET_2 = "c2ds2_v1.vhd"; + static final String CASE3_DATASET_1 = "c3ds1_v1.vhd"; + static final String CASE3_DATASET_2 = "c3ds2_v1.vhd"; + + private final ImageDSProcessor imageDSProcessor; + + private final IngestJobSettings hashAndFileType; + private final IngestJobSettings hashAndNoFileType; + private final DataSourceLoader dataSourceLoader; + + InterCaseTestUtils(NbTestCase testCase) { + + this.case1DataSet1Path = Paths.get(testCase.getDataDir().toString(), CASE1_DATASET_1); + this.case1DataSet2Path = Paths.get(testCase.getDataDir().toString(), CASE1_DATASET_2); + this.case2DataSet1Path = Paths.get(testCase.getDataDir().toString(), CASE1_DATASET_1); + this.case2DataSet2Path = Paths.get(testCase.getDataDir().toString(), CASE2_DATASET_2); + this.case3DataSet1Path = Paths.get(testCase.getDataDir().toString(), CASE3_DATASET_1); + this.case3DataSet2Path = Paths.get(testCase.getDataDir().toString(), CASE3_DATASET_2); + + this.imageDSProcessor = new ImageDSProcessor(); + + final IngestModuleTemplate hashLookupTemplate = IngestUtils.getIngestModuleTemplate(new HashLookupModuleFactory()); + final IngestModuleTemplate mimeTypeLookupTemplate = IngestUtils.getIngestModuleTemplate(new FileTypeIdModuleFactory()); + final IngestModuleTemplate eamDbTemplate = IngestUtils.getIngestModuleTemplate(new org.sleuthkit.autopsy.centralrepository.ingestmodule.IngestModuleFactory()); + + ArrayList hashAndMimeTemplate = new ArrayList<>(2); + hashAndMimeTemplate.add(hashLookupTemplate); + hashAndMimeTemplate.add(mimeTypeLookupTemplate); + hashAndMimeTemplate.add(eamDbTemplate); + + this.hashAndFileType = new IngestJobSettings(InterCaseTestUtils.class.getCanonicalName(), IngestType.FILES_ONLY, hashAndMimeTemplate); + + ArrayList hashAndNoMimeTemplate = new ArrayList<>(1); + hashAndNoMimeTemplate.add(hashLookupTemplate); + hashAndMimeTemplate.add(eamDbTemplate); + + this.hashAndNoFileType = new IngestJobSettings(InterCaseTestUtils.class.getCanonicalName(), IngestType.FILES_ONLY, hashAndNoMimeTemplate); + + this.dataSourceLoader = new DataSourceLoader(); + } + + void clearTestDir(){ + if(CASE_DIRECTORY_PATH.toFile().exists()){ + try{ + if(EamDb.isEnabled()) { + EamDb.getInstance().shutdownConnections(); + } + FileUtils.deleteDirectory(CASE_DIRECTORY_PATH.toFile()); + } catch(IOException | EamDbException ex){ + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } + CASE_DIRECTORY_PATH.toFile().exists(); + } + + Map getDataSourceMap() throws NoCurrentCaseException, TskCoreException, SQLException { + return this.dataSourceLoader.getDataSourceMap(); + } + + Map getCaseMap() throws EamDbException { + + if (EamDb.isEnabled()) { + Map mapOfCaseIdsToCase = new HashMap<>(); + + for (CorrelationCase caze : EamDb.getInstance().getCases()) { + mapOfCaseIdsToCase.put(caze.getDisplayName(), caze.getID()); + } + return mapOfCaseIdsToCase; + } else { + //it is reasonable that this might happen... + // for example when we test the feature in the absence of an enabled eamdb + return new HashMap<>(0); + } + } + + IngestJobSettings getIngestSettingsForHashAndFileType() { + return this.hashAndFileType; + } + + IngestJobSettings getIngestSettingsForHashAndNoFileType() { + return this.hashAndNoFileType; + } + + void enableCentralRepo() throws EamDbException { + + SqliteEamDbSettings crSettings = new SqliteEamDbSettings(); + crSettings.setDbName(CR_DB_NAME); + crSettings.setDbDirectory(CASE_DIRECTORY_PATH.toString()); + if (!crSettings.dbDirectoryExists()) { + crSettings.createDbDirectory(); + } + + crSettings.initializeDatabaseSchema(); + crSettings.insertDefaultDatabaseContent(); + + crSettings.saveSettings(); + + EamDbUtil.setUseCentralRepo(true); + EamDbPlatformEnum.setSelectedPlatform(EamDbPlatformEnum.SQLITE.name()); + EamDbPlatformEnum.saveSelectedPlatform(); + } + + /** + * Create 3 cases and ingest each with the given settings. Null settings are + * permitted but IngestUtils will not be run. + * + * @param ingestJobSettings HashLookup FileType etc... + * @param caseReferenceToStore + */ + Case createCases(IngestJobSettings ingestJobSettings, String caseReferenceToStore) throws TskCoreException { + + Case currentCase = null; + + String[] cases = new String[]{ + CASE1, + CASE2, + CASE3}; + + Path[][] paths = { + {this.case1DataSet1Path, this.case1DataSet2Path}, + {this.case2DataSet1Path, this.case2DataSet2Path}, + {this.case3DataSet1Path, this.case3DataSet2Path}}; + + String lastCaseName = null; + Path[] lastPathsForCase = null; + //iterate over the collections above, creating cases, and storing + // just one of them for future reference + for (int i = 0; i < cases.length; i++) { + String caseName = cases[i]; + Path[] pathsForCase = paths[i]; + + if (caseName.equals(caseReferenceToStore)) { + //put aside and do this one last so we can hang onto the case + lastCaseName = caseName; + lastPathsForCase = pathsForCase; + } else { + //dont hang onto this case; close it + this.createCase(caseName, ingestJobSettings, false, pathsForCase); + } + } + + if (lastCaseName != null && lastPathsForCase != null) { + //hang onto this caes and dont close it + currentCase = this.createCase(lastCaseName, ingestJobSettings, true, lastPathsForCase); + } + + if (currentCase == null) { + Assert.fail(new IllegalArgumentException("caseReferenceToStore should be one of: CASE1, CASE2, CASE3")); + return null; + } else { + return currentCase; + } + } + + private Case createCase(String caseName, IngestJobSettings ingestJobSettings, boolean keepAlive, Path... dataSetPaths) throws TskCoreException { + + Case caze = CaseUtils.createAsCurrentCase(caseName); + for (Path dataSetPath : dataSetPaths) { + IngestUtils.addDataSource(this.imageDSProcessor, dataSetPath); + } + if (ingestJobSettings != null) { + IngestUtils.runIngestJob(caze.getDataSources(), ingestJobSettings); + } + if (keepAlive) { + return caze; + } else { + CaseUtils.closeCurrentCase(false); + return null; + } + } + + //TODO refactor + static boolean verifyInstanceExistanceAndCount(CommonAttributeSearchResults searchDomain, String fileName, String dataSource, String crCase, int instanceCount){ + + int tally = 0; + + for(Map.Entry> entry : searchDomain.getMetadata().entrySet()){ + + for(CommonAttributeValue value : entry.getValue()){ + + for(AbstractCommonAttributeInstance commonAttribute : value.getInstances()){ + + if(commonAttribute instanceof CentralRepoCommonAttributeInstance){ + CentralRepoCommonAttributeInstance results = (CentralRepoCommonAttributeInstance) commonAttribute; + for (DisplayableItemNode din : results.generateNodes()){ + + if(din instanceof CentralRepoCommonAttributeInstanceNode){ + + CentralRepoCommonAttributeInstanceNode node = (CentralRepoCommonAttributeInstanceNode) din; + CorrelationAttributeInstance instance = node.getCorrelationAttributeInstance(); + + final String fullPath = instance.getFilePath(); + final File testFile = new File(fullPath); + + final String testCaseName = instance.getCorrelationCase().getDisplayName(); + + final String testFileName = testFile.getName(); + + final String testDataSource = instance.getCorrelationDataSource().getName(); + + boolean sameFileName = testFileName.equalsIgnoreCase(fileName); + boolean sameDataSource = testDataSource.equalsIgnoreCase(dataSource); + boolean sameCrCase = testCaseName.equalsIgnoreCase(crCase); + + if( sameFileName && sameDataSource && sameCrCase){ + tally++; + } + } + + if(din instanceof CaseDBCommonAttributeInstanceNode){ + + CaseDBCommonAttributeInstanceNode node = (CaseDBCommonAttributeInstanceNode) din; + AbstractFile file = node.getContent(); + + final String testFileName = file.getName(); + final String testCaseName = node.getCase(); + final String testDataSource = node.getDataSource(); + + boolean sameFileName = testFileName.equalsIgnoreCase(fileName); + boolean sameCaseName = testCaseName.equalsIgnoreCase(crCase); + boolean sameDataSource = testDataSource.equalsIgnoreCase(dataSource); + + if(sameFileName && sameDataSource && sameCaseName){ + tally++; + } + } + } + } else { + Assert.fail("Unable to cast AbstractCommonAttributeInstanceNode to InterCaseCommonAttributeSearchResults."); + } + } + } + } + + return tally == instanceCount; + } + + /** + * Close the currently open case, delete the case directory, delete the + * central repo db. + */ + void tearDown() { + + CaseUtils.closeCurrentCase(false); + + String[] cases = new String[]{CASE1,CASE2,CASE3}; + + try { + for(String caze : cases){ + CaseUtils.deleteCaseDir(new File(caze)); + } + } catch (IOException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } +} diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IntraCaseUtils.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IntraCaseTestUtils.java similarity index 74% rename from Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IntraCaseUtils.java rename to Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IntraCaseTestUtils.java index f81e561b38..82c38cdf56 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IntraCaseUtils.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/IntraCaseTestUtils.java @@ -33,10 +33,10 @@ import org.python.icu.impl.Assert; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.ImageDSProcessor; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.autopsy.commonfilesearch.CommonFilesMetadata; +import org.sleuthkit.autopsy.commonfilesearch.AbstractCommonAttributeInstance; +import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeSearchResults; import org.sleuthkit.autopsy.commonfilesearch.DataSourceLoader; -import org.sleuthkit.autopsy.commonfilesearch.FileInstanceMetadata; -import org.sleuthkit.autopsy.commonfilesearch.Md5Metadata; +import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeValue; import org.sleuthkit.autopsy.testutils.CaseUtils; import org.sleuthkit.autopsy.testutils.IngestUtils; import org.sleuthkit.datamodel.AbstractFile; @@ -68,7 +68,7 @@ import org.sleuthkit.datamodel.TskCoreException; * set 4 * - file.dat (empty file) */ -class IntraCaseUtils { +class IntraCaseTestUtils { private static final String CASE_NAME = "IntraCaseCommonFilesSearchTest"; static final Path CASE_DIRECTORY_PATH = Paths.get(System.getProperty("java.io.tmpdir"), CASE_NAME); @@ -91,13 +91,13 @@ class IntraCaseUtils { private final DataSourceLoader dataSourceLoader; private final String caseName; - - IntraCaseUtils(NbTestCase nbTestCase, String caseName) { - imagePath1 = Paths.get(nbTestCase.getDataDir().toString(), SET1); - imagePath2 = Paths.get(nbTestCase.getDataDir().toString(), SET2); - imagePath3 = Paths.get(nbTestCase.getDataDir().toString(), SET3); - imagePath4 = Paths.get(nbTestCase.getDataDir().toString(), SET4); - + + IntraCaseTestUtils(NbTestCase nbTestCase, String caseName){ + this.imagePath1 = Paths.get(nbTestCase.getDataDir().toString(), SET1); + this.imagePath2 = Paths.get(nbTestCase.getDataDir().toString(), SET2); + this.imagePath3 = Paths.get(nbTestCase.getDataDir().toString(), SET3); + this.imagePath4 = Paths.get(nbTestCase.getDataDir().toString(), SET4); + this.dataSourceLoader = new DataSourceLoader(); this.caseName = caseName; @@ -152,32 +152,32 @@ class IntraCaseUtils { * Verify that the given file appears a precise number times in the given * data source. * - * @param files search domain - * @param objectIdToDataSource mapping of file ids to data source names - * @param name name of file to search for + * @param searchDomain search domain + * @param objectIdToDataSourceMap mapping of file ids to data source names + * @param fileName name of file to search for * @param dataSource name of data source where file should appear - * @param count number of appearances of the given file + * @param instanceCount number of appearances of the given file * @return true if a file with the given name exists the specified number of * times in the given data source */ - static boolean verifyFileExistanceAndCount(List files, Map objectIdToDataSource, String name, String dataSource, int count) { + static boolean verifyInstanceExistanceAndCount(List searchDomain, Map objectIdToDataSourceMap, String fileName, String dataSource, int instanceCount) { int tally = 0; - for (AbstractFile file : files) { + for (AbstractFile file : searchDomain) { Long objectId = file.getId(); - String fileName = file.getName(); + String name = file.getName(); - String dataSourceName = objectIdToDataSource.get(objectId); + String dataSourceName = objectIdToDataSourceMap.get(objectId); - if (fileName.equals(name) && dataSourceName.equals(dataSource)) { + if (name.equals(name) && dataSourceName.equals(dataSource)) { tally++; } } - return tally == count; + return tally == instanceCount; } /** @@ -191,17 +191,24 @@ class IntraCaseUtils { * @return true if a file with the given name exists once in the given data * source */ - static boolean verifySingularFileExistance(List files, Map objectIdToDataSource, String name, String dataSource) { - return verifyFileExistanceAndCount(files, objectIdToDataSource, name, dataSource, 1); + static boolean verifySingularInstanceExistance(List files, Map objectIdToDataSource, String name, String dataSource) { + return verifyInstanceExistanceAndCount(files, objectIdToDataSource, name, dataSource, 1); } - static Map mapFileInstancesToDataSources(CommonFilesMetadata metadata) { + /** + * Create a convenience lookup table mapping file instance object ids to + * the data source they appear in. + * + * @param metadata object returned by the code under test + * @return mapping of objectId to data source name + */ + static Map mapFileInstancesToDataSources(CommonAttributeSearchResults metadata) { Map instanceIdToDataSource = new HashMap<>(); - for (Map.Entry> entry : metadata.getMetadata().entrySet()) { - for (Md5Metadata md : entry.getValue()) { - for (FileInstanceMetadata fim : md.getMetadata()) { - instanceIdToDataSource.put(fim.getObjectId(), fim.getDataSourceName()); + for (Map.Entry> entry : metadata.getMetadata().entrySet()) { + for (CommonAttributeValue md : entry.getValue()) { + for (AbstractCommonAttributeInstance fim : md.getInstances()) { + instanceIdToDataSource.put(fim.getAbstractFileObjectId(), fim.getDataSource()); } } } @@ -209,6 +216,7 @@ class IntraCaseUtils { return instanceIdToDataSource; } + static List getFiles(Set objectIds) { List files = new ArrayList<>(objectIds.size()); diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/MatchesInAtLeastTwoSources.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/MatchesInAtLeastTwoSourcesIntraCaseTests.java similarity index 64% rename from Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/MatchesInAtLeastTwoSources.java rename to Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/MatchesInAtLeastTwoSourcesIntraCaseTests.java index ba178a59f6..f4c397af8d 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/MatchesInAtLeastTwoSources.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/MatchesInAtLeastTwoSourcesIntraCaseTests.java @@ -31,10 +31,11 @@ import org.python.icu.impl.Assert; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.ImageDSProcessor; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.autopsy.commonfilesearch.AllDataSourcesCommonFilesAlgorithm; -import org.sleuthkit.autopsy.commonfilesearch.CommonFilesMetadata; -import org.sleuthkit.autopsy.commonfilesearch.CommonFilesMetadataBuilder; -import static org.sleuthkit.autopsy.commonfilessearch.IntraCaseUtils.*; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; +import org.sleuthkit.autopsy.commonfilesearch.AbstractCommonAttributeSearcher; +import org.sleuthkit.autopsy.commonfilesearch.AllIntraCaseCommonAttributeSearcher; +import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeSearchResults; +import static org.sleuthkit.autopsy.commonfilessearch.IntraCaseTestUtils.*; import org.sleuthkit.autopsy.ingest.IngestJobSettings; import org.sleuthkit.autopsy.ingest.IngestModuleTemplate; import org.sleuthkit.autopsy.modules.filetypeid.FileTypeIdModuleFactory; @@ -52,21 +53,21 @@ import org.sleuthkit.datamodel.TskCoreException; * * None of the test files should be found in the results of this test. */ -public class MatchesInAtLeastTwoSources extends NbTestCase { +public class MatchesInAtLeastTwoSourcesIntraCaseTests extends NbTestCase { public static Test suite() { - NbModuleSuite.Configuration conf = NbModuleSuite.createConfiguration(MatchesInAtLeastTwoSources.class). + NbModuleSuite.Configuration conf = NbModuleSuite.createConfiguration(MatchesInAtLeastTwoSourcesIntraCaseTests.class). clusters(".*"). enableModules(".*"); return conf.suite(); } - private final IntraCaseUtils utils; + private final IntraCaseTestUtils utils; - public MatchesInAtLeastTwoSources(String name) { + public MatchesInAtLeastTwoSourcesIntraCaseTests(String name) { super(name); - this.utils = new IntraCaseUtils(this, "MatchesInAtLeastTwoSources"); + this.utils = new IntraCaseTestUtils(this, "MatchesInAtLeastTwoSources"); } @Override @@ -85,7 +86,7 @@ public class MatchesInAtLeastTwoSources extends NbTestCase { templates.add(hashLookupTemplate); templates.add(mimeTypeLookupTemplate); - IngestJobSettings ingestJobSettings = new IngestJobSettings(IngestedWithHashAndFileType.class.getCanonicalName(), IngestJobSettings.IngestType.FILES_ONLY, templates); + IngestJobSettings ingestJobSettings = new IngestJobSettings(IngestedWithHashAndFileTypeIntraCaseTests.class.getCanonicalName(), IngestJobSettings.IngestType.FILES_ONLY, templates); try { IngestUtils.runIngestJob(Case.getCurrentCaseThrows().getDataSources(), ingestJobSettings); @@ -104,23 +105,23 @@ public class MatchesInAtLeastTwoSources extends NbTestCase { try { Map dataSources = this.utils.getDataSourceMap(); - CommonFilesMetadataBuilder allSourcesBuilder = new AllDataSourcesCommonFilesAlgorithm(dataSources, false, false); - CommonFilesMetadata metadata = allSourcesBuilder.findCommonFiles(); + AbstractCommonAttributeSearcher allSourcesBuilder = new AllIntraCaseCommonAttributeSearcher(dataSources, false, false); + CommonAttributeSearchResults metadata = allSourcesBuilder.findFiles(); - Map objectIdToDataSource = IntraCaseUtils.mapFileInstancesToDataSources(metadata); + Map objectIdToDataSource = IntraCaseTestUtils.mapFileInstancesToDataSources(metadata); - List files = IntraCaseUtils.getFiles(objectIdToDataSource.keySet()); + List files = IntraCaseTestUtils.getFiles(objectIdToDataSource.keySet()); - assertTrue(IntraCaseUtils.verifyFileExistanceAndCount(files, dataSources, IMG, SET1, 0)); - assertTrue(IntraCaseUtils.verifyFileExistanceAndCount(files, dataSources, IMG, SET4, 0)); + assertTrue(IntraCaseTestUtils.verifyInstanceExistanceAndCount(files, dataSources, IMG, SET1, 0)); + assertTrue(IntraCaseTestUtils.verifyInstanceExistanceAndCount(files, dataSources, IMG, SET4, 0)); - assertTrue(IntraCaseUtils.verifyFileExistanceAndCount(files, dataSources, DOC, SET1, 0)); - assertTrue(IntraCaseUtils.verifyFileExistanceAndCount(files, dataSources, DOC, SET4, 0)); + assertTrue(IntraCaseTestUtils.verifyInstanceExistanceAndCount(files, dataSources, DOC, SET1, 0)); + assertTrue(IntraCaseTestUtils.verifyInstanceExistanceAndCount(files, dataSources, DOC, SET4, 0)); - assertTrue(IntraCaseUtils.verifyFileExistanceAndCount(files, dataSources, EMPTY, SET1, 0)); - assertTrue(IntraCaseUtils.verifyFileExistanceAndCount(files, dataSources, EMPTY, SET4, 0)); + assertTrue(IntraCaseTestUtils.verifyInstanceExistanceAndCount(files, dataSources, EMPTY, SET1, 0)); + assertTrue(IntraCaseTestUtils.verifyInstanceExistanceAndCount(files, dataSources, EMPTY, SET4, 0)); - } catch (NoCurrentCaseException | TskCoreException | SQLException ex) { + } catch (NoCurrentCaseException | TskCoreException | SQLException | EamDbException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex); } diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/UningestedCases.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/UningestedCasesIntraCaseTests.java similarity index 64% rename from Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/UningestedCases.java rename to Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/UningestedCasesIntraCaseTests.java index 9bd7a02bae..b55707c771 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/UningestedCases.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/commonfilessearch/UningestedCasesIntraCaseTests.java @@ -28,12 +28,13 @@ import org.netbeans.junit.NbTestCase; import org.openide.util.Exceptions; import org.python.icu.impl.Assert; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.autopsy.commonfilesearch.AllDataSourcesCommonFilesAlgorithm; -import org.sleuthkit.autopsy.commonfilesearch.CommonFilesMetadata; -import org.sleuthkit.autopsy.commonfilesearch.CommonFilesMetadataBuilder; -import org.sleuthkit.autopsy.commonfilesearch.SingleDataSource; -import static org.sleuthkit.autopsy.commonfilessearch.IntraCaseUtils.SET1; -import static org.sleuthkit.autopsy.commonfilessearch.IntraCaseUtils.getDataSourceIdByName; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; +import org.sleuthkit.autopsy.commonfilesearch.AllIntraCaseCommonAttributeSearcher; +import org.sleuthkit.autopsy.commonfilesearch.CommonAttributeSearchResults; +import org.sleuthkit.autopsy.commonfilesearch.IntraCaseCommonAttributeSearcher; +import org.sleuthkit.autopsy.commonfilesearch.SingleIntraCaseCommonAttributeSearcher; +import static org.sleuthkit.autopsy.commonfilessearch.IntraCaseTestUtils.SET1; +import static org.sleuthkit.autopsy.commonfilessearch.IntraCaseTestUtils.getDataSourceIdByName; import org.sleuthkit.datamodel.TskCoreException; /** @@ -45,21 +46,21 @@ import org.sleuthkit.datamodel.TskCoreException; * Add images set 1, set 2, set 3, and set 4 to case. Do not ingest. * */ -public class UningestedCases extends NbTestCase { +public class UningestedCasesIntraCaseTests extends NbTestCase { public static Test suite() { - NbModuleSuite.Configuration conf = NbModuleSuite.createConfiguration(UningestedCases.class). + NbModuleSuite.Configuration conf = NbModuleSuite.createConfiguration(UningestedCasesIntraCaseTests.class). clusters(".*"). enableModules(".*"); return conf.suite(); } - private final IntraCaseUtils utils; + private final IntraCaseTestUtils utils; - public UningestedCases(String name) { + public UningestedCasesIntraCaseTests(String name) { super(name); - this.utils = new IntraCaseUtils(this, "UningestedCasesTests"); + this.utils = new IntraCaseTestUtils(this, "UningestedCasesTests"); } @Override @@ -80,13 +81,13 @@ public class UningestedCases extends NbTestCase { try { Map dataSources = this.utils.getDataSourceMap(); - CommonFilesMetadataBuilder allSourcesBuilder = new AllDataSourcesCommonFilesAlgorithm(dataSources, false, false); - CommonFilesMetadata metadata = allSourcesBuilder.findCommonFiles(); + IntraCaseCommonAttributeSearcher allSourcesBuilder = new AllIntraCaseCommonAttributeSearcher(dataSources, false, false); + CommonAttributeSearchResults metadata = allSourcesBuilder.findFiles(); int resultCount = metadata.size(); assertEquals(resultCount, 0); - } catch (NoCurrentCaseException | TskCoreException | SQLException ex) { + } catch (TskCoreException | NoCurrentCaseException | SQLException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex); } @@ -100,13 +101,13 @@ public class UningestedCases extends NbTestCase { Map dataSources = this.utils.getDataSourceMap(); Long first = getDataSourceIdByName(SET1, dataSources); - CommonFilesMetadataBuilder singleSourceBuilder = new SingleDataSource(first, dataSources, false, false); - CommonFilesMetadata metadata = singleSourceBuilder.findCommonFiles(); + IntraCaseCommonAttributeSearcher singleSourceBuilder = new SingleIntraCaseCommonAttributeSearcher(first, dataSources, false, false); + CommonAttributeSearchResults metadata = singleSourceBuilder.findFiles(); int resultCount = metadata.size(); assertEquals(resultCount, 0); - } catch (NoCurrentCaseException | TskCoreException | SQLException ex) { + } catch (TskCoreException | NoCurrentCaseException | SQLException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex); }