diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java index a6e078f565..91b3dda535 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java @@ -178,7 +178,7 @@ public abstract class AbstractSqlEamDb implements EamDb { */ @Override public synchronized CorrelationCase newCase(CorrelationCase eamCase) throws EamDbException { - + // check if there is already an existing CorrelationCase for this Case CorrelationCase cRCase = getCaseByUUID(eamCase.getCaseUUID()); if (cRCase != null) { @@ -279,10 +279,10 @@ public abstract class AbstractSqlEamDb implements EamDb { */ @Override public void updateCase(CorrelationCase eamCase) throws EamDbException { - if(eamCase == null) { + if (eamCase == null) { throw new EamDbException("CorrelationCase argument is null"); } - + Connection conn = connect(); PreparedStatement preparedStatement = null; @@ -457,10 +457,10 @@ public abstract class AbstractSqlEamDb implements EamDb { */ @Override public CorrelationDataSource getDataSource(CorrelationCase correlationCase, String dataSourceDeviceId) throws EamDbException { - if(correlationCase == null) { + if (correlationCase == null) { throw new EamDbException("CorrelationCase argument is null"); } - + Connection conn = connect(); CorrelationDataSource eamDataSourceResult = null; @@ -530,16 +530,16 @@ public abstract class AbstractSqlEamDb implements EamDb { */ @Override public void addArtifact(CorrelationAttribute eamArtifact) throws EamDbException { - if(eamArtifact == null) { + if (eamArtifact == null) { throw new EamDbException("CorrelationAttribute is null"); } - if(eamArtifact.getCorrelationType() == null) { + if (eamArtifact.getCorrelationType() == null) { throw new EamDbException("Correlation type is null"); } - if(eamArtifact.getCorrelationValue() == null) { + if (eamArtifact.getCorrelationValue() == null) { throw new EamDbException("Correlation value is null"); } - + Connection conn = connect(); List eamInstances = eamArtifact.getInstances(); @@ -554,21 +554,21 @@ public abstract class AbstractSqlEamDb implements EamDb { sql.append("VALUES ((SELECT id FROM cases WHERE case_uid=? LIMIT 1), "); sql.append("(SELECT id FROM data_sources WHERE device_id=? AND case_id=? LIMIT 1), ?, ?, ?, ?) "); sql.append(getConflictClause()); - + try { preparedStatement = conn.prepareStatement(sql.toString()); for (CorrelationAttributeInstance eamInstance : eamInstances) { if (!eamArtifact.getCorrelationValue().isEmpty()) { - if(eamInstance.getCorrelationCase() == null) { + if (eamInstance.getCorrelationCase() == null) { throw new EamDbException("CorrelationAttributeInstance has null case"); } - if(eamInstance.getCorrelationDataSource() == null) { + if (eamInstance.getCorrelationDataSource() == null) { throw new EamDbException("CorrelationAttributeInstance has null data source"); } - if(eamInstance.getKnownStatus() == null) { + if (eamInstance.getKnownStatus() == null) { throw new EamDbException("CorrelationAttributeInstance has null known status"); } - + preparedStatement.setString(1, eamInstance.getCorrelationCase().getCaseUUID()); preparedStatement.setString(2, eamInstance.getCorrelationDataSource().getDeviceID()); preparedStatement.setInt(3, eamInstance.getCorrelationDataSource().getCaseID()); @@ -605,7 +605,7 @@ public abstract class AbstractSqlEamDb implements EamDb { */ @Override public List getArtifactInstancesByTypeValue(CorrelationAttribute.Type aType, String value) throws EamDbException { - if(aType == null) { + if (aType == null) { throw new EamDbException("Correlation type is null"); } Connection conn = connect(); @@ -647,6 +647,82 @@ public abstract class AbstractSqlEamDb implements EamDb { return artifactInstances; } + /** + * Retrieves eamArtiifact instances from the database that match + * the given list of MD5 values and optionally filters by given case. + * + * @param correlationCase Case id to search on, if null, searches all cases + * @param values List of ArtifactInstance MD5 values to find matches of. + * + * @return List of artifact instances for a given list of MD5 values + */ + @Override + public List getArtifactInstancesByCaseValues(CorrelationCase correlationCase, List values) throws EamDbException { + CorrelationAttribute.Type aType = CorrelationAttribute.getDefaultCorrelationTypes().get(0); // Files type + if (aType == null) { + throw new EamDbException("Correlation Type is null"); + } + boolean singleCase = false; + if (correlationCase != null) { + singleCase = false; + } + Connection conn = connect(); + + List artifactInstances = new ArrayList<>(); + + CorrelationAttributeCommonInstance artifactInstance; + PreparedStatement preparedStatement = null; + ResultSet resultSet = null; + + String valuesString = ""; + StringBuilder valuesFilter = new StringBuilder(values.size()); + if (!values.isEmpty()) { + for (String value : values) { + valuesFilter.append("'").append(value).append("',"); + } + valuesString = valuesFilter.toString().substring(0, valuesFilter.length() - 1); + } + + String tableName = EamDbUtil.correlationTypeToInstanceTableName(aType); + StringBuilder sql = new StringBuilder(9); + sql.append("SELECT cases.case_name, cases.case_uid, data_sources.name, device_id, file_path, known_status, comment, data_sources.case_id, value FROM "); + sql.append(tableName); + sql.append(" LEFT JOIN cases ON "); + sql.append(tableName); + sql.append(".case_id=cases.id"); + sql.append(" LEFT JOIN data_sources ON "); + sql.append(tableName); + sql.append(".data_source_id=data_sources.id"); + sql.append(" WHERE value IN (?)"); + if (singleCase) { + sql.append(" AND "); + sql.append(tableName); + sql.append(".case_id = ?"); + } + + try { + preparedStatement = conn.prepareStatement(sql.toString()); + preparedStatement.setString(1, valuesString); + if (singleCase && correlationCase != null) { + preparedStatement.setString(2, String.valueOf(correlationCase.getID())); + } + + resultSet = preparedStatement.executeQuery(); + while (resultSet.next()) { + artifactInstance = getCommonEamArtifactInstanceFromResultSet(resultSet); + artifactInstances.add(artifactInstance); + } + } catch (SQLException ex) { + throw new EamDbException("Error getting artifact instances by artifactType and artifactValue.", ex); // NON-NLS + } finally { + EamDbUtil.closePreparedStatement(preparedStatement); + EamDbUtil.closeResultSet(resultSet); + EamDbUtil.closeConnection(conn); + } + + return artifactInstances; + } + /** * Retrieves eamArtifact instances from the database that are associated * with the aType and filePath @@ -660,10 +736,10 @@ public abstract class AbstractSqlEamDb implements EamDb { */ @Override public List getArtifactInstancesByPath(CorrelationAttribute.Type aType, String filePath) throws EamDbException { - if(aType == null) { + if (aType == null) { throw new EamDbException("Correlation type is null"); } - if(filePath == null) { + if (filePath == null) { throw new EamDbException("Correlation value is null"); } Connection conn = connect(); @@ -717,13 +793,13 @@ public abstract class AbstractSqlEamDb implements EamDb { */ @Override public Long getCountArtifactInstancesByTypeValue(CorrelationAttribute.Type aType, String value) throws EamDbException { - if(aType == null) { + if (aType == null) { throw new EamDbException("Correlation type is null"); } - if(value == null) { + if (value == null) { throw new EamDbException("Correlation value is null"); } - + Connection conn = connect(); Long instanceCount = 0L; @@ -776,10 +852,10 @@ public abstract class AbstractSqlEamDb implements EamDb { */ @Override public Long getCountUniqueCaseDataSourceTuplesHavingTypeValue(CorrelationAttribute.Type aType, String value) throws EamDbException { - if(aType == null) { + if (aType == null) { throw new EamDbException("Correlation type is null"); } - + Connection conn = connect(); Long instanceCount = 0L; @@ -901,10 +977,10 @@ public abstract class AbstractSqlEamDb implements EamDb { @Override public void prepareBulkArtifact(CorrelationAttribute eamArtifact) throws EamDbException { - if(eamArtifact.getCorrelationType() == null) { + if (eamArtifact.getCorrelationType() == null) { throw new EamDbException("Correlation type is null"); } - + synchronized (bulkArtifacts) { bulkArtifacts.get(eamArtifact.getCorrelationType().getDbTableName()).add(eamArtifact); bulkArtifactsCount++; @@ -958,17 +1034,17 @@ public abstract class AbstractSqlEamDb implements EamDb { for (CorrelationAttributeInstance eamInstance : eamInstances) { if (!eamArtifact.getCorrelationValue().isEmpty()) { - - if(eamInstance.getCorrelationCase() == null) { + + if (eamInstance.getCorrelationCase() == null) { throw new EamDbException("Correlation attribute instance has null case"); } - if(eamInstance.getCorrelationDataSource() == null) { + if (eamInstance.getCorrelationDataSource() == null) { throw new EamDbException("Correlation attribute instance has null data source"); } - if(eamInstance.getKnownStatus()== null) { + if (eamInstance.getKnownStatus() == null) { throw new EamDbException("Correlation attribute instance has null known known status"); } - + bulkPs.setString(1, eamInstance.getCorrelationCase().getCaseUUID()); bulkPs.setString(2, eamInstance.getCorrelationDataSource().getDeviceID()); bulkPs.setInt(3, eamInstance.getCorrelationDataSource().getCaseID()); @@ -1005,16 +1081,16 @@ public abstract class AbstractSqlEamDb implements EamDb { */ @Override public void bulkInsertCases(List cases) throws EamDbException { - if(cases == null) { + if (cases == null) { throw new EamDbException("cases argument is null"); } - + if (cases.isEmpty()) { return; } Connection conn = connect(); - + int counter = 0; PreparedStatement bulkPs = null; try { @@ -1081,38 +1157,38 @@ public abstract class AbstractSqlEamDb implements EamDb { } /** - * 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 { - if(eamArtifact == null) { + if (eamArtifact == null) { throw new EamDbException("Correlation attribute is null"); } - if(knownStatus == null) { + if (knownStatus == null) { throw new EamDbException("Known status is null"); } if (1 != eamArtifact.getInstances().size()) { throw new EamDbException("Error: Artifact must have exactly one (1) Artifact Instance to set as notable."); // NON-NLS } - + List eamInstances = eamArtifact.getInstances(); CorrelationAttributeInstance eamInstance = eamInstances.get(0); - if(eamInstance.getCorrelationCase() == null) { + if (eamInstance.getCorrelationCase() == null) { throw new EamDbException("Correlation case is null"); } - if(eamInstance.getCorrelationDataSource() == null) { + if (eamInstance.getCorrelationDataSource() == null) { throw new EamDbException("Correlation data source is null"); } - - Connection conn = connect(); + + Connection conn = connect(); PreparedStatement preparedUpdate = null; PreparedStatement preparedQuery = null; @@ -1196,10 +1272,10 @@ public abstract class AbstractSqlEamDb implements EamDb { */ @Override public List getArtifactInstancesKnownBad(CorrelationAttribute.Type aType, String value) throws EamDbException { - if(aType == null) { + if (aType == null) { throw new EamDbException("Correlation type is null"); } - + Connection conn = connect(); List artifactInstances = new ArrayList<>(); @@ -1250,10 +1326,10 @@ public abstract class AbstractSqlEamDb implements EamDb { */ @Override public Long getCountArtifactInstancesKnownBad(CorrelationAttribute.Type aType, String value) throws EamDbException { - if(aType == null) { + if (aType == null) { throw new EamDbException("Correlation type is null"); } - + Connection conn = connect(); Long badInstances = 0L; @@ -1298,10 +1374,10 @@ public abstract class AbstractSqlEamDb implements EamDb { */ @Override public List getListCasesHavingArtifactInstancesKnownBad(CorrelationAttribute.Type aType, String value) throws EamDbException { - if(aType == null) { + if (aType == null) { throw new EamDbException("Correlation type is null"); } - + Connection conn = connect(); Collection caseNames = new LinkedHashSet<>(); @@ -1418,7 +1494,7 @@ public abstract class AbstractSqlEamDb implements EamDb { @Override public boolean referenceSetIsValid(int referenceSetID, String setName, String version) throws EamDbException { EamGlobalSet refSet = this.getReferenceSetByID(referenceSetID); - if(refSet == null) { + if (refSet == null) { return false; } @@ -1487,7 +1563,7 @@ public abstract class AbstractSqlEamDb implements EamDb { */ @Override public boolean isArtifactKnownBadByReference(CorrelationAttribute.Type aType, String value) throws EamDbException { - if(aType == null) { + if (aType == null) { throw new EamDbException("null correlation type"); } @@ -1532,10 +1608,10 @@ public abstract class AbstractSqlEamDb implements EamDb { */ @Override public long newOrganization(EamOrganization eamOrg) throws EamDbException { - if(eamOrg == null) { + if (eamOrg == null) { throw new EamDbException("EamOrganization is null"); } - + Connection conn = connect(); ResultSet generatedKeys = null; PreparedStatement preparedStatement = null; @@ -1642,7 +1718,7 @@ public abstract class AbstractSqlEamDb implements EamDb { public EamOrganization getReferenceSetOrganization(int referenceSetID) throws EamDbException { EamGlobalSet globalSet = getReferenceSetByID(referenceSetID); - if(globalSet == null) { + if (globalSet == null) { throw new EamDbException("Reference set with ID " + referenceSetID + " not found"); } return (getOrganizationByID(globalSet.getOrgID())); @@ -1658,10 +1734,10 @@ public abstract class AbstractSqlEamDb implements EamDb { */ @Override public void updateOrganization(EamOrganization updatedOrganization) throws EamDbException { - if(updatedOrganization == null) { + if (updatedOrganization == null) { throw new EamDbException("null updatedOrganization"); } - + Connection conn = connect(); PreparedStatement preparedStatement = null; String sql = "UPDATE organizations SET org_name = ?, poc_name = ?, poc_email = ?, poc_phone = ? WHERE id = ?"; @@ -1686,10 +1762,10 @@ public abstract class AbstractSqlEamDb implements EamDb { "AbstractSqlEamDb.deleteOrganization.errorDeleting.message=Error executing query when attempting to delete organization by id."}) @Override public void deleteOrganization(EamOrganization organizationToDelete) throws EamDbException { - if(organizationToDelete == null) { + if (organizationToDelete == null) { throw new EamDbException("Organization to delete is null"); } - + Connection conn = connect(); PreparedStatement checkIfUsedStatement = null; ResultSet resultSet = null; @@ -1729,18 +1805,18 @@ public abstract class AbstractSqlEamDb implements EamDb { */ @Override public int newReferenceSet(EamGlobalSet eamGlobalSet) throws EamDbException { - if(eamGlobalSet == null){ + if (eamGlobalSet == null) { throw new EamDbException("EamGlobalSet argument is null"); } - - if(eamGlobalSet.getFileKnownStatus() == null){ + + if (eamGlobalSet.getFileKnownStatus() == null) { throw new EamDbException("File known status on the EamGlobalSet is null"); } - - if(eamGlobalSet.getType() == null){ + + if (eamGlobalSet.getType() == null) { throw new EamDbException("Type on the EamGlobalSet is null"); } - + Connection conn = connect(); PreparedStatement preparedStatement1 = null; @@ -1803,7 +1879,7 @@ public abstract class AbstractSqlEamDb implements EamDb { preparedStatement1 = conn.prepareStatement(sql1); preparedStatement1.setInt(1, referenceSetID); resultSet = preparedStatement1.executeQuery(); - if(resultSet.next()) { + if (resultSet.next()) { return getEamGlobalSetFromResultSet(resultSet); } else { return null; @@ -1822,18 +1898,18 @@ public abstract class AbstractSqlEamDb implements EamDb { * 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 { - - if(correlationType == null){ + + if (correlationType == null) { throw new EamDbException("Correlation type is null"); } - + List results = new ArrayList<>(); Connection conn = connect(); @@ -1868,13 +1944,13 @@ public abstract class AbstractSqlEamDb implements EamDb { */ @Override public void addReferenceInstance(EamGlobalFileInstance eamGlobalFileInstance, CorrelationAttribute.Type correlationType) throws EamDbException { - if(eamGlobalFileInstance.getKnownStatus() == null){ + if (eamGlobalFileInstance.getKnownStatus() == null) { throw new EamDbException("known status of EamGlobalFileInstance is null"); } - if(correlationType == null){ + if (correlationType == null) { throw new EamDbException("Correlation type is null"); } - + Connection conn = connect(); PreparedStatement preparedStatement = null; @@ -1939,10 +2015,10 @@ public abstract class AbstractSqlEamDb implements EamDb { */ @Override public void bulkInsertReferenceTypeEntries(Set globalInstances, CorrelationAttribute.Type contentType) throws EamDbException { - if(contentType == null) { + if (contentType == null) { throw new EamDbException("Null correlation type"); } - if(globalInstances == null) { + if (globalInstances == null) { throw new EamDbException("Null set of EamGlobalFileInstance"); } @@ -1959,10 +2035,10 @@ public abstract class AbstractSqlEamDb implements EamDb { bulkPs = conn.prepareStatement(String.format(sql, EamDbUtil.correlationTypeToReferenceTableName(contentType))); for (EamGlobalFileInstance globalInstance : globalInstances) { - if(globalInstance.getKnownStatus() == null){ + if (globalInstance.getKnownStatus() == null) { throw new EamDbException("EamGlobalFileInstance with value " + globalInstance.getMD5Hash() + " has null known status"); } - + bulkPs.setInt(1, globalInstance.getGlobalSetID()); bulkPs.setString(2, globalInstance.getMD5Hash()); bulkPs.setByte(3, globalInstance.getKnownStatus().getFileKnownValue()); @@ -1997,10 +2073,10 @@ public abstract class AbstractSqlEamDb implements EamDb { */ @Override public List getReferenceInstancesByTypeValue(CorrelationAttribute.Type aType, String aValue) throws EamDbException { - if(aType == null) { + if (aType == null) { throw new EamDbException("correlation type is null"); } - + Connection conn = connect(); List globalFileInstances = new ArrayList<>(); @@ -2040,7 +2116,7 @@ public abstract class AbstractSqlEamDb implements EamDb { if (newType == null) { throw new EamDbException("null correlation type"); } - + Connection conn = connect(); PreparedStatement preparedStatement = null; @@ -2245,7 +2321,7 @@ public abstract class AbstractSqlEamDb implements EamDb { preparedStatement = conn.prepareStatement(sql); preparedStatement.setInt(1, typeId); resultSet = preparedStatement.executeQuery(); - if(resultSet.next()) { + if (resultSet.next()) { aType = getCorrelationTypeFromResultSet(resultSet); return aType; } else { @@ -2356,6 +2432,32 @@ public abstract class AbstractSqlEamDb implements EamDb { return eamArtifactInstance; } + /** + * Convert a ResultSet to a Common EamArtifactInstance object + * + * @param resultSet A resultSet with a set of values to create a + * EamArtifactInstance object. + * + * @return fully populated EamArtifactInstance, or null + * + * @throws SQLException when an expected column name is not in the resultSet + */ + private CorrelationAttributeCommonInstance getCommonEamArtifactInstanceFromResultSet(ResultSet resultSet) throws SQLException, EamDbException { + if (null == resultSet) { + return null; + } + CorrelationAttributeCommonInstance eamArtifactInstance = new CorrelationAttributeCommonInstance( + new CorrelationCase(resultSet.getInt("case_id"), resultSet.getString("case_uid"), resultSet.getString("case_name")), + new CorrelationDataSource(-1, resultSet.getInt("case_id"), resultSet.getString("device_id"), resultSet.getString("name")), + resultSet.getString("file_path"), + resultSet.getString("comment"), + TskData.FileKnown.valueOf(resultSet.getByte("known_status")), + resultSet.getString("value") + ); + + return eamArtifactInstance; + } + private EamOrganization getEamOrganizationFromResultSet(ResultSet resultSet) throws SQLException { if (null == resultSet) { return null; diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeCommonInstance.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeCommonInstance.java new file mode 100644 index 0000000000..ff03b23f5b --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeCommonInstance.java @@ -0,0 +1,46 @@ +/* + * Central Repository + * + * Copyright 2015-2017 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.centralrepository.datamodel; + +import org.sleuthkit.datamodel.TskData; + + +/** + * Common Files Search usage which extends CorrelationAttributeInstance + * by adding the MD5 value to match on for the results table. + */ +public class CorrelationAttributeCommonInstance extends CorrelationAttributeInstance { + + private static final long serialVersionUID = 1L; + + /** + * The common MD5 value + */ + private final String value; + + public CorrelationAttributeCommonInstance(CorrelationCase eamCase, CorrelationDataSource eamDataSource, String filePath, String comment, TskData.FileKnown knownStatus, String value) throws EamDbException { + super(eamCase, eamDataSource, filePath, comment, knownStatus); + this.value = value; + } + + public String getValue() { + return value; + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDb.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDb.java index 2b05cb1d70..24af255eea 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDb.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamDb.java @@ -178,7 +178,7 @@ public interface EamDb { * @return List of cases */ List getCases() throws EamDbException; - + /** * Creates new Data Source in the database * @@ -223,6 +223,17 @@ public interface EamDb { */ List getArtifactInstancesByTypeValue(CorrelationAttribute.Type aType, String value) throws EamDbException; + /** + * Retrieves eamArtiifact instances from the database that match the given + * list of MD5 values; + * + * @param correlationCase Case id to search on + * @param values List of ArtifactInstance MD5 values to find matches of. + * + * @return List of artifact instances for a given list of MD5 values + */ + List getArtifactInstancesByCaseValues(CorrelationCase correlationCase, List values) throws EamDbException; + /** * Retrieves eamArtifact instances from the database that are associated * with the aType and filePath diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresEamDb.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresEamDb.java index 46d579ad87..af57e6afa9 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresEamDb.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresEamDb.java @@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.centralrepository.datamodel; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; +import java.util.List; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import org.apache.commons.dbcp2.BasicDataSource; diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDb.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDb.java index d9d1ea2fa3..0917cedc60 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDb.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDb.java @@ -34,9 +34,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. */ public class SqliteEamDb extends AbstractSqlEamDb { @@ -47,17 +47,18 @@ public 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) { @@ -68,9 +69,9 @@ public 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(); @@ -80,7 +81,7 @@ public 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() @@ -90,7 +91,7 @@ public class SqliteEamDb extends AbstractSqlEamDb { throw new EamDbException("Failed to close existing database connections.", ex); // NON-NLS } } - + @Override public void updateSettings() { synchronized (this) { @@ -108,9 +109,9 @@ public class SqliteEamDb extends AbstractSqlEamDb { @Override public void reset() throws EamDbException { - try{ + try { acquireExclusiveLock(); - + Connection conn = connect(); try { @@ -151,11 +152,11 @@ public 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()); @@ -201,25 +202,24 @@ public 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. * @@ -231,47 +231,47 @@ public 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 * @@ -281,14 +281,14 @@ public 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 * @@ -296,14 +296,14 @@ public 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 * @@ -313,14 +313,14 @@ public class SqliteEamDb extends AbstractSqlEamDb { */ @Override public CorrelationCase getCaseByUUID(String caseUUID) throws EamDbException { - try{ + try { acquireSharedLock(); return super.getCaseByUUID(caseUUID); } finally { releaseSharedLock(); - } - } - + } + } + /** * Retrieves cases that are in DB. * @@ -328,14 +328,14 @@ public 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 * @@ -343,32 +343,33 @@ public 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(); - } - } - + } + } + /** * Return a list of data sources in the DB * @@ -376,14 +377,14 @@ public 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. @@ -392,38 +393,57 @@ public 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 eamArtiifact instances from the database that match the given + * list of MD5 values; + * + * @param correlationCase Case id to search on + * @param values List of ArtifactInstance MD5 values to find matches of. + * + * @return List of artifact instances for a given list of MD5 values + */ + @Override + public List getArtifactInstancesByCaseValues(CorrelationCase correlationCase, List values) throws EamDbException { + try { + acquireSharedLock(); + return super.getArtifactInstancesByCaseValues(correlationCase, values); + } 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 @@ -432,14 +452,14 @@ public 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. @@ -448,29 +468,29 @@ public 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 @@ -484,112 +504,111 @@ public 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(); - } - } - + } + } + /** * Count matching eamArtifacts instances that have knownStatus = "Bad". * @@ -600,14 +619,14 @@ public 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". @@ -616,37 +635,39 @@ public 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 @@ -654,32 +675,34 @@ public 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(); - } + } } - + /** - * 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. + * 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? * @@ -689,34 +712,34 @@ public 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 long newOrganization(EamOrganization eamOrg) throws EamDbException { - try{ + try { acquireExclusiveLock(); return super.newOrganization(eamOrg); } finally { releaseExclusiveLock(); - } - } - + } + } + /** * Get all organizations * @@ -726,14 +749,14 @@ public 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 * @@ -745,33 +768,34 @@ public 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 * @@ -783,14 +807,14 @@ public 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 * @@ -802,52 +826,51 @@ public 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 * @@ -855,18 +878,18 @@ public 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 @@ -875,14 +898,14 @@ public 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. * @@ -894,71 +917,71 @@ public 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. * @@ -968,14 +991,14 @@ public 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. * @@ -987,73 +1010,76 @@ public 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/AllDataSourcesEamDbCommonFilesAlgorithm.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AllDataSourcesEamDbCommonFilesAlgorithm.java new file mode 100644 index 0000000000..ea52971f20 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/AllDataSourcesEamDbCommonFilesAlgorithm.java @@ -0,0 +1,58 @@ +/* + * + * 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 static org.sleuthkit.autopsy.commonfilesearch.CommonFilesMetadataBuilder.SELECT_PREFIX; + + +/** + * Provides logic for selecting common files from all data sources for all files to source for EamDB query. + */ +public class AllDataSourcesEamDbCommonFilesAlgorithm extends CommonFilesMetadataBuilder { + + private static final String WHERE_CLAUSE = "%s md5 in (select md5 from tsk_files where (known != 1 OR known IS NULL)%s GROUP BY md5) order by md5"; //NON-NLS + + /** + * Implements the algorithm for getting common files across all data + * sources. + * + * @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 + */ + AllDataSourcesEamDbCommonFilesAlgorithm(Map dataSourceIdMap, boolean filterByMediaMimeType, boolean filterByDocMimeType) { + super(dataSourceIdMap, filterByMediaMimeType, filterByDocMimeType); + + } + + @Override + protected String buildSqlSelectStatement() { + Object[] args = new String[]{SELECT_PREFIX, determineMimeTypeFilter()}; + return String.format(WHERE_CLAUSE, args); + } + + @Override + protected String buildTabTitle() { + final String buildCategorySelectionString = this.buildCategorySelectionString(); + final String titleTemplate = Bundle.CommonFilesMetadataBuilder_buildTabTitle_titleEamDb(); + 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 72ddffb748..e9ba24bc1e 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/Bundle.properties @@ -1,15 +1,21 @@ 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.withinDataSourceRadioButton.text=At least one match must appear in the data source selected below: +CommonFilesPanel.allDataSourcesRadioButton.text=Matches may be from 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 duplicate files 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.commonFilesSearchLabel1.text=Find Common Files. +CommonFilesPanel.jRadioButton1.text=jRadioButton1 +CommonFilesPanel.jRadioButton2.text=Correlate amongst external cases (compares files in current case with Central Repo) +CommonFilesPanel.intraCaseRadio.label=Correlate within current case only +CommonFilesPanel.interCaseRadio.label=Correlate amongst all known cases (uses Central Repo) +CommonFilesPanel.anCentralRepoCaseRadio.text_1=Matches may be from any Central Repo case +CommonFilesPanel.specificCentralRepoCaseRadio.text_1=Matches must be from the following Central Repo case: diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetadataBuilder.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetadataBuilder.java index 2d279179ee..47f477d82a 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetadataBuilder.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesMetadataBuilder.java @@ -22,16 +22,25 @@ package org.sleuthkit.autopsy.commonfilesearch; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.logging.Level; 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.autopsy.centralrepository.datamodel.CorrelationAttributeCommonInstance; +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.centralrepository.datamodel.EamDbException; +import static org.sleuthkit.autopsy.timeline.datamodel.eventtype.ArtifactEventType.LOGGER; import org.sleuthkit.datamodel.HashUtility; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery; @@ -201,6 +210,64 @@ abstract class CommonFilesMetadataBuilder { return new CommonFilesMetadata(commonFiles); } + + /** + * TODO Refactor, abstract shared code above, call this method via new AllDataSourcesEamDbCommonFilesAlgorithm Class + * @param correlationCase Optionally null, otherwise a case, or could be a CR case ID + * @return + * @throws TskCoreException + * @throws NoCurrentCaseException + * @throws SQLException + * @throws EamDbException + */ + public CommonFilesMetadata findEamDbCommonFiles(CorrelationCase correlationCase) throws TskCoreException, NoCurrentCaseException, SQLException, EamDbException { + CommonFilesMetadata metaData = this.findCommonFiles(); + Map commonFiles = metaData.getMetadata(); + List values = Arrays.asList((String[]) commonFiles.keySet().toArray()); + + Map interCaseCommonFiles = metaData.getMetadata(); + try { + + EamDb dbManager = EamDb.getInstance(); + Collection artifactInstances = dbManager.getArtifactInstancesByCaseValues(correlationCase, values).stream() + .collect(Collectors.toList()); + + + for (CorrelationAttributeCommonInstance instance : artifactInstances) { + //Long objectId = 1L; //TODO, need to retrieve ALL (even count < 2) AbstractFiles from this case to us for objectId for CR matches; + String md5 = instance.getValue(); + String dataSource = instance.getCorrelationDataSource().getName(); + + if (md5 == null || HashUtility.isNoDataMd5(md5)) { + continue; + } + //Builds a 3rd list which contains instances which are in commonFiles map, uses current case objectId + if (commonFiles.containsKey(md5)) { + // TODO sloppy, but we don't *have* all the information for the rows in the CR, so what do we do? + Long objectId = commonFiles.get(md5).getMetadata().iterator().next().getObjectId(); + if(interCaseCommonFiles.containsKey(md5)) { + //Add to intercase metaData + final Md5Metadata md5Metadata = interCaseCommonFiles.get(md5); + md5Metadata.addFileInstanceMetadata(new FileInstanceMetadata(objectId, dataSource)); + + } else { + // Create new intercase metadata + final Md5Metadata md5Metadata = commonFiles.get(md5); + md5Metadata.addFileInstanceMetadata(new FileInstanceMetadata(objectId, dataSource)); + interCaseCommonFiles.put(md5, md5Metadata); + } + } else { + // TODO This should never happen. All current case files with potential matches are in comonFiles Map. + } + } + + } catch (EamDbException ex) { + LOGGER.log(Level.SEVERE, "Error getting artifact instances from database.", ex); // NON-NLS + } + // Builds intercase-only matches metadata + return new CommonFilesMetadata(interCaseCommonFiles); + + } /** * Should be used by subclasses, in their @@ -235,7 +302,8 @@ abstract class CommonFilesMetadataBuilder { @NbBundle.Messages({ "CommonFilesMetadataBuilder.buildTabTitle.titleAll=Common Files (All Data Sources, %s)", - "CommonFilesMetadataBuilder.buildTabTitle.titleSingle=Common Files (Match Within Data Source: %s, %s)" + "CommonFilesMetadataBuilder.buildTabTitle.titleSingle=Common Files (Match Within Data Source: %s, %s)", + "CommonFilesMetadataBuilder.buildTabTitle.titleEamDb=Common Files (Central Repository Source(s), %s)", }) protected abstract String buildTabTitle(); diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesPanel.form b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesPanel.form index c9d08c0536..8c82dd1626 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesPanel.form +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesPanel.form @@ -6,6 +6,10 @@ + + + + @@ -23,45 +27,72 @@ - - - - - - - - - - - + + - - - - + + + - - + + + + + + + + + + + + - - - - + + + + + + + + + + + + + + + + + + - - + + + + + + - + + + + + + + + + + @@ -69,16 +100,24 @@ - - - + + + - + + + + + + + + + @@ -94,6 +133,7 @@ + @@ -116,7 +156,6 @@ - @@ -154,14 +193,6 @@ - - - - - - - - @@ -231,14 +262,6 @@ - - - - - - - - @@ -257,5 +280,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesPanel.java b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesPanel.java index ad278a4cb5..26939ff840 100644 --- a/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/commonfilesearch/CommonFilesPanel.java @@ -273,7 +273,10 @@ public final class CommonFilesPanel extends javax.swing.JPanel { builder = new SingleDataSource(dataSourceId, CommonFilesPanel.this.dataSourceMap, filterByMedia, filterByDocuments); setTitleForSingleSource(dataSourceId); - } + }// else if(false) { + // TODO, is CR cases, add option chosen CorrelationCase ID lookup + // builder = new AllDataSourcesEamDbCommonFilesAlgorithm(CommonFilesPanel.this.dataSourceMap, filterByMedia, filterByDocuments); + //} this.tabTitle = builder.buildTabTitle(); @@ -338,19 +341,25 @@ public final class CommonFilesPanel extends javax.swing.JPanel { dataSourcesButtonGroup = new javax.swing.ButtonGroup(); fileTypeFilterButtonGroup = new javax.swing.ButtonGroup(); + interIntraButtonGroup = new javax.swing.ButtonGroup(); + caseSelectionButtonGroup = 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(); + commonFilesSearchLabel1 = new javax.swing.JLabel(); + intraCaseRadio = new javax.swing.JRadioButton(); + interCaseRadio = new javax.swing.JRadioButton(); + anCentralRepoCaseRadio = new javax.swing.JRadioButton(); + specificCentralRepoCaseRadio = new javax.swing.JRadioButton(); + caseComboBox = new javax.swing.JComboBox<>(); org.openide.awt.Mnemonics.setLocalizedText(searchButton, org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.searchButton.text")); // NOI18N searchButton.setEnabled(false); @@ -362,7 +371,6 @@ public final class CommonFilesPanel extends javax.swing.JPanel { }); 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() { @@ -388,9 +396,6 @@ public final class CommonFilesPanel extends javax.swing.JPanel { } }); - 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); @@ -435,65 +440,123 @@ public final class CommonFilesPanel extends javax.swing.JPanel { } }); - 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 + org.openide.awt.Mnemonics.setLocalizedText(commonFilesSearchLabel1, org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.commonFilesSearchLabel1.text")); // NOI18N + commonFilesSearchLabel1.setFocusable(false); + + interIntraButtonGroup.add(intraCaseRadio); + intraCaseRadio.setSelected(true); + intraCaseRadio.setLabel(org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.intraCaseRadio.label")); // 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(CommonFilesPanel.class, "CommonFilesPanel.jRadioButton2.text")); // NOI18N + interCaseRadio.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + interCaseRadioActionPerformed(evt); + } + }); + + caseSelectionButtonGroup.add(anCentralRepoCaseRadio); + anCentralRepoCaseRadio.setSelected(true); + org.openide.awt.Mnemonics.setLocalizedText(anCentralRepoCaseRadio, org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.anCentralRepoCaseRadio.text_1")); // NOI18N + anCentralRepoCaseRadio.setEnabled(false); + + caseSelectionButtonGroup.add(specificCentralRepoCaseRadio); + org.openide.awt.Mnemonics.setLocalizedText(specificCentralRepoCaseRadio, org.openide.util.NbBundle.getMessage(CommonFilesPanel.class, "CommonFilesPanel.specificCentralRepoCaseRadio.text_1")); // NOI18N + specificCentralRepoCaseRadio.setEnabled(false); + + caseComboBox.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" })); + 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, 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.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .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) + .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(commonFilesSearchLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .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.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(categoriesLabel) + .addGroup(layout.createSequentialGroup() + .addGap(6, 6, 6) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(allFileCategoriesRadioButton) + .addComponent(selectedFileCategoriesButton) + .addGroup(layout.createSequentialGroup() + .addGap(21, 21, 21) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(pictureVideoCheckbox) + .addComponent(documentsCheckbox)))))) + .addGap(277, 277, 277)) + .addComponent(intraCaseRadio) .addGroup(layout.createSequentialGroup() .addGap(21, 21, 21) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(pictureVideoCheckbox) - .addComponent(documentsCheckbox)))))) - .addGap(19, 19, 19)))) + .addComponent(withinDataSourceRadioButton) + .addComponent(allDataSourcesRadioButton))))) + .addGroup(layout.createSequentialGroup() + .addGap(47, 47, 47) + .addComponent(selectDataSourceComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 261, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(interCaseRadio) + .addGroup(layout.createSequentialGroup() + .addGap(21, 21, 21) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(anCentralRepoCaseRadio) + .addComponent(specificCentralRepoCaseRadio) + .addGroup(layout.createSequentialGroup() + .addGap(21, 21, 21) + .addComponent(caseComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 261, javax.swing.GroupLayout.PREFERRED_SIZE))))))) + .addGap(0, 0, Short.MAX_VALUE)) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addGap(0, 0, Short.MAX_VALUE) + .addComponent(errorText) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(searchButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(cancelButton))) + .addContainerGap()) ); 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) + .addComponent(commonFilesSearchLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(18, 18, 18) - .addComponent(dataSourceLabel) + .addComponent(intraCaseRadio) .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) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(interCaseRadio) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(anCentralRepoCaseRadio) + .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) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(categoriesLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(selectedFileCategoriesButton) @@ -507,7 +570,8 @@ public final class CommonFilesPanel extends javax.swing.JPanel { .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(cancelButton) .addComponent(searchButton) - .addComponent(errorText))) + .addComponent(errorText)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); }// //GEN-END:initComponents @@ -555,6 +619,27 @@ public final class CommonFilesPanel extends javax.swing.JPanel { this.toggleErrorTextAndSearchBox(); }//GEN-LAST:event_documentsCheckboxActionPerformed + private void intraCaseRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_intraCaseRadioActionPerformed + this.allDataSourcesRadioButton.setEnabled(true); + this.withinDataSourceRadioButton.setEnabled(true); + this.selectDataSourceComboBox.setEnabled(true); + + this.anCentralRepoCaseRadio.setEnabled(false); + this.specificCentralRepoCaseRadio.setEnabled(false); + this.caseComboBox.setEnabled(false); + }//GEN-LAST:event_intraCaseRadioActionPerformed + + private void interCaseRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_interCaseRadioActionPerformed + this.anCentralRepoCaseRadio.setEnabled(true); + this.specificCentralRepoCaseRadio.setEnabled(true); + this.caseComboBox.setEnabled(true); + + this.allDataSourcesRadioButton.setEnabled(false); + this.withinDataSourceRadioButton.setEnabled(false); + this.selectDataSourceComboBox.setEnabled(false); + + }//GEN-LAST:event_interCaseRadioActionPerformed + private void toggleErrorTextAndSearchBox() { if (!this.pictureVideoCheckbox.isSelected() && !this.documentsCheckbox.isSelected() && !this.allFileCategoriesRadioButton.isSelected()) { this.searchButton.setEnabled(false); @@ -599,18 +684,24 @@ public final class CommonFilesPanel extends javax.swing.JPanel { // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JRadioButton allDataSourcesRadioButton; private javax.swing.JRadioButton allFileCategoriesRadioButton; + private javax.swing.JRadioButton anCentralRepoCaseRadio; private javax.swing.JButton cancelButton; + private javax.swing.JComboBox caseComboBox; + private javax.swing.ButtonGroup caseSelectionButtonGroup; private javax.swing.JLabel categoriesLabel; - private javax.swing.JLabel commonFilesSearchLabel; - private javax.swing.JLabel dataSourceLabel; + private javax.swing.JLabel commonFilesSearchLabel1; private javax.swing.ButtonGroup dataSourcesButtonGroup; private javax.swing.JCheckBox documentsCheckbox; private javax.swing.JLabel errorText; private javax.swing.ButtonGroup fileTypeFilterButtonGroup; + private javax.swing.JRadioButton interCaseRadio; + private javax.swing.ButtonGroup interIntraButtonGroup; + private javax.swing.JRadioButton intraCaseRadio; private javax.swing.JCheckBox pictureVideoCheckbox; private javax.swing.JButton searchButton; private javax.swing.JComboBox selectDataSourceComboBox; private javax.swing.JRadioButton selectedFileCategoriesButton; + private javax.swing.JRadioButton specificCentralRepoCaseRadio; private javax.swing.JRadioButton withinDataSourceRadioButton; // End of variables declaration//GEN-END:variables }