diff --git a/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCasesTableCellRenderer.java b/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCasesTableCellRenderer.java index 0d93082ff5..3a657e3d02 100644 --- a/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCasesTableCellRenderer.java +++ b/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCasesTableCellRenderer.java @@ -24,6 +24,7 @@ import javax.swing.JLabel; import javax.swing.JTable; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.TableCellRenderer; +import org.sleuthkit.datamodel.TskData; /** * Renderer for cells in data content viewer table @@ -49,19 +50,15 @@ public class DataContentViewerOtherCasesTableCellRenderer implements TableCellRe background = Color.BLUE; } else { String known_status = (String) table.getModel().getValueAt(row, 5); - switch (known_status) { - case "Bad": + if (known_status.equals(TskData.FileKnown.BAD.getName())) { foreground = Color.WHITE; background = Color.RED; - break; - case "Unknown": + } else if (known_status.equals(TskData.FileKnown.UNKNOWN.getName())) { foreground = Color.BLACK; background = Color.YELLOW; - break; - default: + } else { foreground = Color.BLACK; background = Color.WHITE; - break; } } renderer.setForeground(foreground); diff --git a/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCasesTableModel.java b/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCasesTableModel.java index b8721d2390..1d48d2d03c 100644 --- a/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCasesTableModel.java +++ b/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCasesTableModel.java @@ -48,7 +48,7 @@ public class DataContentViewerOtherCasesTableModel extends AbstractTableModel { DEVICE(Bundle.DataContentViewerOtherCasesTableModel_device(), 145), TYPE(Bundle.DataContentViewerOtherCasesTableModel_type(), 40), VALUE(Bundle.DataContentViewerOtherCasesTableModel_value(), 145), - KNOWN(Bundle.DataContentViewerOtherCasesTableModel_known(), 25), + KNOWN(Bundle.DataContentViewerOtherCasesTableModel_known(), 45), SCOPE(Bundle.DataContentViewerOtherCasesTableModel_scope(), 20), COMMENT(Bundle.DataContentViewerOtherCasesTableModel_comment(), 200), FILE_PATH(Bundle.DataContentViewerOtherCasesTableModel_path(), 250); @@ -160,7 +160,7 @@ public class DataContentViewerOtherCasesTableModel extends AbstractTableModel { value = eamArtifactInstance.getGlobalStatus().toString(); break; case KNOWN: - value = eamArtifactInstance.getKnownStatus().toString(); + value = eamArtifactInstance.getKnownStatus().getName(); break; case COMMENT: value = eamArtifactInstance.getComment(); diff --git a/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java b/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java index 2fa4d1b3ad..8cac3ed03d 100644 --- a/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java +++ b/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java @@ -35,6 +35,7 @@ import java.util.Map; import java.util.Set; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.TskData; /** * @@ -100,7 +101,6 @@ public abstract class AbstractSqlEamDb implements EamDb { // } // // else, schema is current // } - /** * Setup and create a connection to the selected database implementation */ @@ -550,8 +550,8 @@ public abstract class AbstractSqlEamDb implements EamDb { } /** - * Retrieves eamArtifact instances from the database that are associated with - * the eamArtifactType and eamArtifactValue of the given eamArtifact. + * Retrieves eamArtifact instances from the database that are associated + * with the eamArtifactType and eamArtifactValue of the given eamArtifact. * * @param eamArtifact The type/value to look up (artifact with 0 instances) * @@ -599,8 +599,8 @@ public abstract class AbstractSqlEamDb implements EamDb { } /** - * Retrieves eamArtifact instances from the database that are associated with - * the aType and filePath + * Retrieves eamArtifact instances from the database that are associated + * with the aType and filePath * * @param aType EamArtifact.Type to search for * @param filePath File path to search for @@ -655,7 +655,7 @@ public abstract class AbstractSqlEamDb implements EamDb { * associated with the ArtifactType and artifactValue of the given artifact. * * @param eamArtifact Artifact with artifactType and artifactValue to search - * for + * for * * @return Number of artifact instances having ArtifactType and * ArtifactValue. @@ -698,7 +698,7 @@ public abstract class AbstractSqlEamDb implements EamDb { * case_id/datasource_id tuples in the database) expressed as a percentage. * * @param eamArtifact Artifact with artifactType and artifactValue to search - * for + * for * * @return Int between 0 and 100 */ @@ -716,7 +716,7 @@ public abstract class AbstractSqlEamDb implements EamDb { * the given artifact. * * @param eamArtifact Artifact with artifactType and artifactValue to search - * for + * for * * @return Number of unique tuples */ @@ -806,7 +806,7 @@ public abstract class AbstractSqlEamDb implements EamDb { * @param eamInstance Instance with caseName and dataSource to search for * * @param eamInstance Instance with caseDisplayName and dataSource to search - * for + * for * * @return Number of artifact instances having caseDisplayName and * dataSource @@ -874,6 +874,13 @@ public abstract class AbstractSqlEamDb implements EamDb { } } + /** + * Get the conflict clause for bulk update statements + * + * @return The conflict clause for bulk update statements + */ + protected abstract String getConflictClause(); + /** * Executes a bulk insert of the eamArtifacts added from the * prepareBulkArtifact() method @@ -899,7 +906,8 @@ public abstract class AbstractSqlEamDb implements EamDb { sql.append(tableName); sql.append(" (case_id, data_source_id, value, file_path, known_status, comment) "); sql.append("VALUES ((SELECT id FROM cases WHERE case_uid=? LIMIT 1), "); - sql.append("(SELECT id FROM data_sources WHERE device_id=? LIMIT 1), ?, ?, ?, ?)"); + sql.append("(SELECT id FROM data_sources WHERE device_id=? LIMIT 1), ?, ?, ?, ?) "); + sql.append(getConflictClause()); bulkPs = conn.prepareStatement(sql.toString()); @@ -949,7 +957,8 @@ public abstract class AbstractSqlEamDb implements EamDb { try { String sql = "INSERT INTO cases(case_uid, org_id, case_name, creation_date, case_number, " + "examiner_name, examiner_email, examiner_phone, notes) " - + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"; + + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) " + + getConflictClause(); bulkPs = conn.prepareStatement(sql); for (EamCase eamCase : cases) { @@ -987,8 +996,8 @@ public abstract class AbstractSqlEamDb implements EamDb { } /** - * Sets an eamArtifact instance as knownStatus = "Bad". If eamArtifact exists, - * it is updated. If eamArtifact does not exist nothing happens + * Sets an eamArtifact instance as knownStatus = "Bad". If eamArtifact + * exists, it is updated. If eamArtifact does not exist nothing happens * * @param eamArtifact Artifact containing exactly one (1) ArtifactInstance. */ @@ -1034,13 +1043,13 @@ public abstract class AbstractSqlEamDb implements EamDb { int instance_id = resultSet.getInt("id"); preparedUpdate = conn.prepareStatement(sqlUpdate.toString()); - preparedUpdate.setString(1, EamArtifactInstance.KnownStatus.BAD.name()); + preparedUpdate.setString(1, TskData.FileKnown.BAD.name()); preparedUpdate.setString(2, eamInstance.getComment()); preparedUpdate.setInt(3, instance_id); preparedUpdate.executeUpdate(); } else { - eamArtifact.getInstances().get(0).setKnownStatus(EamArtifactInstance.KnownStatus.BAD); + eamArtifact.getInstances().get(0).setKnownStatus(TskData.FileKnown.BAD); addArtifact(eamArtifact); } @@ -1055,7 +1064,8 @@ public abstract class AbstractSqlEamDb implements EamDb { } /** - * Gets list of matching eamArtifact instances that have knownStatus = "Bad". + * Gets list of matching eamArtifact instances that have knownStatus = + * "Bad". * * @param eamArtifact Artifact containing Type and Value * @@ -1086,7 +1096,7 @@ public abstract class AbstractSqlEamDb implements EamDb { try { preparedStatement = conn.prepareStatement(sql.toString()); preparedStatement.setString(1, eamArtifact.getArtifactValue()); - preparedStatement.setString(2, EamArtifactInstance.KnownStatus.BAD.name()); + preparedStatement.setString(2, TskData.FileKnown.BAD.name()); resultSet = preparedStatement.executeQuery(); while (resultSet.next()) { artifactInstance = getEamArtifactInstanceFromResultSet(resultSet); @@ -1127,7 +1137,7 @@ public abstract class AbstractSqlEamDb implements EamDb { try { preparedStatement = conn.prepareStatement(sql.toString()); preparedStatement.setString(1, eamArtifact.getArtifactValue()); - preparedStatement.setString(2, EamArtifactInstance.KnownStatus.BAD.name()); + preparedStatement.setString(2, TskData.FileKnown.BAD.name()); resultSet = preparedStatement.executeQuery(); resultSet.next(); badInstances = resultSet.getLong(1); @@ -1177,7 +1187,7 @@ public abstract class AbstractSqlEamDb implements EamDb { try { preparedStatement = conn.prepareStatement(sql.toString()); preparedStatement.setString(1, eamArtifact.getArtifactValue()); - preparedStatement.setString(2, EamArtifactInstance.KnownStatus.BAD.name()); + preparedStatement.setString(2, TskData.FileKnown.BAD.name()); resultSet = preparedStatement.executeQuery(); while (resultSet.next()) { caseNames.add(resultSet.getString("case_name")); @@ -1218,7 +1228,7 @@ public abstract class AbstractSqlEamDb implements EamDb { try { preparedStatement = conn.prepareStatement(sql); preparedStatement.setString(1, eamArtifact.getArtifactValue()); - preparedStatement.setString(2, EamArtifactInstance.KnownStatus.BAD.name()); + preparedStatement.setString(2, TskData.FileKnown.BAD.name()); resultSet = preparedStatement.executeQuery(); resultSet.next(); badInstances = resultSet.getLong(1); @@ -1440,28 +1450,10 @@ public abstract class AbstractSqlEamDb implements EamDb { } } - /** - * Add a new global file instance to the bulk collection - * - * @param eamGlobalFileInstance The global file instance to add - * @throws EamDbException - */ -// @Override -// public void prepareGlobalFileInstance(EamGlobalFileInstance eamGlobalFileInstance) throws EamDbException { -// synchronized (bulkGlobalArtifacts) { -// bulkGlobalArtifacts.get("FILES").add(eamGlobalFileInstance); // NON-NLS -// bulkGlobalArtifactsCount++; -// -// if (bulkGlobalArtifactsCount >= bulkArtifactsThreshold) { -// bulkInsertGlobalFileInstances(); -// } -// } -// } - /** * Insert the bulk collection of Global File Instances - * - * @throws EamDbException + * + * @throws EamDbException */ @Override public void bulkInsertGlobalFileInstances(Set globalInstances, EamArtifact.Type contentType) throws EamDbException { @@ -1470,7 +1462,8 @@ public abstract class AbstractSqlEamDb implements EamDb { PreparedStatement bulkPs = null; try { // FUTURE: have a separate global_files table for each Type. - String sql = "INSERT INTO global_files(global_reference_set_id, value, known_status, comment) VALUES (?, ?, ?, ?)"; + String sql = "INSERT INTO global_files(global_reference_set_id, value, known_status, comment) VALUES (?, ?, ?, ?) " + + getConflictClause(); bulkPs = conn.prepareStatement(sql); @@ -1493,10 +1486,12 @@ public abstract class AbstractSqlEamDb implements EamDb { /** * Get all global file instances having a given MD5 hash - * + * * @param MD5Hash The hash to lookup + * * @return List of all global file instances with a given hash - * @throws EamDbException + * + * @throws EamDbException */ @Override public List getGlobalFileInstancesByHash(String MD5Hash) throws EamDbException { @@ -1529,7 +1524,8 @@ public abstract class AbstractSqlEamDb implements EamDb { * Add a new EamArtifact.Type to the db. * * @param newType New type to add. - * @throws EamDbException + * + * @throws EamDbException */ @Override public void newCorrelationArtifactType(EamArtifact.Type newType) throws EamDbException { @@ -1561,7 +1557,8 @@ public abstract class AbstractSqlEamDb implements EamDb { * * @return List of EamArtifact.Type's. If none are defined in the database, * the default list will be returned. - * @throws EamDbException + * + * @throws EamDbException */ @Override public List getCorrelationArtifactTypes() throws EamDbException { @@ -1595,7 +1592,8 @@ public abstract class AbstractSqlEamDb implements EamDb { * * @return List of enabled EamArtifact.Type's. If none are defined in the * database, the default list will be returned. - * @throws EamDbException + * + * @throws EamDbException */ @Override public List getEnabledCorrelationArtifactTypes() throws EamDbException { @@ -1629,7 +1627,8 @@ public abstract class AbstractSqlEamDb implements EamDb { * * @return List of supported EamArtifact.Type's. If none are defined in the * database, the default list will be returned. - * @throws EamDbException + * + * @throws EamDbException */ @Override public List getSupportedCorrelationArtifactTypes() throws EamDbException { @@ -1661,7 +1660,8 @@ public abstract class AbstractSqlEamDb implements EamDb { * Update a EamArtifact.Type. * * @param aType EamArtifact.Type to update. - * @throws EamDbException + * + * @throws EamDbException */ @Override public void updateCorrelationArtifactType(EamArtifact.Type aType) throws EamDbException { @@ -1693,7 +1693,8 @@ public abstract class AbstractSqlEamDb implements EamDb { * @param typeName Name of Type to get * * @return EamArtifact.Type or null if it doesn't exist. - * @throws EamDbException + * + * @throws EamDbException */ @Override public EamArtifact.Type getCorrelationArtifactTypeByName(String typeName) throws EamDbException { @@ -1810,7 +1811,7 @@ public abstract class AbstractSqlEamDb implements EamDb { new EamDataSource(resultSet.getString("device_id"), resultSet.getString("name")), resultSet.getString("file_path"), resultSet.getString("comment"), - EamArtifactInstance.KnownStatus.valueOf(resultSet.getString("known_status")), + TskData.FileKnown.valueOf(resultSet.getString("known_status")), EamArtifactInstance.GlobalStatus.LOCAL ); @@ -1858,7 +1859,7 @@ public abstract class AbstractSqlEamDb implements EamDb { resultSet.getInt("id"), resultSet.getInt("global_reference_set_id"), resultSet.getString("value"), - EamArtifactInstance.KnownStatus.valueOf(resultSet.getString("known_status")), + TskData.FileKnown.valueOf(resultSet.getString("known_status")), resultSet.getString("comment") ); diff --git a/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamArtifactInstance.java b/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamArtifactInstance.java index a66d239f5e..27fe3ebee2 100644 --- a/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamArtifactInstance.java +++ b/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamArtifactInstance.java @@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.centralrepository.datamodel; import java.io.Serializable; import org.openide.util.NbBundle.Messages; +import org.sleuthkit.datamodel.TskData; /** * @@ -49,22 +50,22 @@ public class EamArtifactInstance implements Serializable { } } - public enum KnownStatus { - UNKNOWN(Bundle.EamArtifactInstances_knownStatus_unknown()), - KNOWN(Bundle.EamArtifactInstances_knownStatus_known()), - BAD(Bundle.EamArtifactInstances_knownStatus_bad()); - - private final String knownStatus; - - private KnownStatus(String knownStatus) { - this.knownStatus = knownStatus; - } - - @Override - public String toString() { - return knownStatus; - } - } +// public enum FileKnown { +// UNKNOWN(Bundle.EamArtifactInstances_knownStatus_unknown()), +// KNOWN(Bundle.EamArtifactInstances_knownStatus_known()), +// BAD(Bundle.EamArtifactInstances_knownStatus_bad()); +// +// private final String knownStatus; +// +// private FileKnown(String knownStatus) { +// this.knownStatus = knownStatus; +// } +// +// @Override +// public String toString() { +// return knownStatus; +// } +// } private static final long serialVersionUID = 1L; @@ -73,14 +74,14 @@ public class EamArtifactInstance implements Serializable { private EamDataSource eamDataSource; private String filePath; private String comment; - private KnownStatus knownStatus; + private TskData.FileKnown knownStatus; private GlobalStatus globalStatus; public EamArtifactInstance( EamCase eamCase, EamDataSource eamDataSource ) { - this("", eamCase, eamDataSource, "", "", KnownStatus.UNKNOWN, GlobalStatus.LOCAL); + this("", eamCase, eamDataSource, "", "", TskData.FileKnown.UNKNOWN, GlobalStatus.LOCAL); } public EamArtifactInstance( @@ -88,7 +89,7 @@ public class EamArtifactInstance implements Serializable { EamDataSource eamDataSource, String filePath ) { - this("", eamCase, eamDataSource, filePath, "", KnownStatus.UNKNOWN, GlobalStatus.LOCAL); + this("", eamCase, eamDataSource, filePath, "", TskData.FileKnown.UNKNOWN, GlobalStatus.LOCAL); } public EamArtifactInstance( @@ -97,7 +98,7 @@ public class EamArtifactInstance implements Serializable { String filePath, String comment ) { - this("", eamCase, eamDataSource, filePath, comment, KnownStatus.UNKNOWN, GlobalStatus.LOCAL); + this("", eamCase, eamDataSource, filePath, comment, TskData.FileKnown.UNKNOWN, GlobalStatus.LOCAL); } public EamArtifactInstance( @@ -105,7 +106,7 @@ public class EamArtifactInstance implements Serializable { EamDataSource eamDataSource, String filePath, String comment, - KnownStatus knownStatus, + TskData.FileKnown knownStatus, GlobalStatus globalStatus ) { this("", eamCase, eamDataSource, filePath, comment, knownStatus, globalStatus); @@ -117,7 +118,7 @@ public class EamArtifactInstance implements Serializable { EamDataSource eamDataSource, String filePath, String comment, - KnownStatus knownStatus, + TskData.FileKnown knownStatus, GlobalStatus globalStatus ) { this.ID = ID; @@ -223,14 +224,14 @@ public class EamArtifactInstance implements Serializable { /** * @return the knownStatus */ - public KnownStatus getKnownStatus() { + public TskData.FileKnown getKnownStatus() { return knownStatus; } /** * @param knownStatus the knownStatus to set */ - public void setKnownStatus(KnownStatus knownStatus) { + public void setKnownStatus(TskData.FileKnown knownStatus) { this.knownStatus = knownStatus; } diff --git a/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamArtifactUtil.java b/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamArtifactUtil.java index 378ad9687b..0d0568bb9a 100644 --- a/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamArtifactUtil.java +++ b/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamArtifactUtil.java @@ -27,6 +27,7 @@ import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.TskDataException; /** @@ -88,7 +89,7 @@ public class EamArtifactUtil { new EamDataSource(deviceId, af.getDataSource().getName()), af.getParentPath() + af.getName(), "", - EamArtifactInstance.KnownStatus.UNKNOWN, + TskData.FileKnown.UNKNOWN, EamArtifactInstance.GlobalStatus.LOCAL ); eamArtifact.addInstance(eamInstance); diff --git a/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamGlobalFileInstance.java b/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamGlobalFileInstance.java index 170ca503ac..2a1e4ce24d 100644 --- a/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamGlobalFileInstance.java +++ b/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamGlobalFileInstance.java @@ -19,7 +19,7 @@ package org.sleuthkit.autopsy.centralrepository.datamodel; import java.util.Objects; -import org.sleuthkit.autopsy.centralrepository.datamodel.EamArtifactInstance.KnownStatus; +import org.sleuthkit.datamodel.TskData; /** * Global file hash instance @@ -29,14 +29,14 @@ public class EamGlobalFileInstance { private int instanceID; private int globalSetID; private String MD5Hash; - private KnownStatus knownStatus; + private TskData.FileKnown knownStatus; private String comment; public EamGlobalFileInstance( int instanceID, int globalSetID, String MD5Hash, - KnownStatus knownStatus, + TskData.FileKnown knownStatus, String comment) { this.instanceID = instanceID; this.globalSetID = globalSetID; @@ -48,7 +48,7 @@ public class EamGlobalFileInstance { public EamGlobalFileInstance( int globalSetID, String MD5Hash, - KnownStatus knownStatus, + TskData.FileKnown knownStatus, String comment) { this(-1, globalSetID, MD5Hash, knownStatus, comment); } @@ -69,7 +69,7 @@ public class EamGlobalFileInstance { int hash = 5; hash = 59 * hash + this.globalSetID; hash = 59 * hash + Objects.hashCode(this.MD5Hash); - hash = 59 * hash + Objects.hashCode(this.knownStatus); + hash = 59 * hash + this.knownStatus.hashCode(); return hash; } /** @@ -117,14 +117,14 @@ public class EamGlobalFileInstance { /** * @return the knownStatus */ - public KnownStatus getKnownStatus() { + public TskData.FileKnown getKnownStatus() { return knownStatus; } /** * @param knownStatus the knownStatus to set */ - public void setKnownStatus(KnownStatus knownStatus) { + public void setKnownStatus(TskData.FileKnown knownStatus) { this.knownStatus = knownStatus; } diff --git a/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresEamDb.java b/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresEamDb.java index 85e976082f..f8397d17e4 100644 --- a/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresEamDb.java +++ b/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresEamDb.java @@ -33,6 +33,8 @@ public class PostgresEamDb extends AbstractSqlEamDb { private final static Logger LOGGER = Logger.getLogger(PostgresEamDb.class.getName()); + private final static String CONFLICT_CLAUSE = "ON CONFLICT DO NOTHING"; + private static PostgresEamDb instance; private static final int CONN_POOL_SIZE = 10; @@ -162,6 +164,11 @@ public class PostgresEamDb extends AbstractSqlEamDb { } } + @Override + protected String getConflictClause() { + return CONFLICT_CLAUSE; + } + @Override public List getBadTags() { return dbSettings.getBadTags(); diff --git a/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresEamDbSettings.java b/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresEamDbSettings.java index 82720b55f8..a884320511 100644 --- a/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresEamDbSettings.java +++ b/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresEamDbSettings.java @@ -36,8 +36,7 @@ import org.sleuthkit.autopsy.coreutils.TextConverter; import org.sleuthkit.autopsy.coreutils.TextConverterException; /** - * Settings for the Postgres implementation of the Central Repository - * database + * Settings for the Postgres implementation of the Central Repository database */ public final class PostgresEamDbSettings { @@ -148,8 +147,8 @@ public final class PostgresEamDbSettings { * Get the full connection URL as a String * * @param usePostgresDb Connect to the 'postgres' database when testing - * connectivity and creating the main database. - * + * connectivity and creating the main database. + * * @return */ public String getConnectionURL(boolean usePostgresDb) { @@ -167,8 +166,9 @@ public final class PostgresEamDbSettings { } /** - * Use the current settings to get an ephemeral client connection for testing. - * + * Use the current settings to get an ephemeral client connection for + * testing. + * * @return Connection or null. */ private Connection getEphemeralConnection(boolean usePostgresDb) { @@ -191,9 +191,9 @@ public final class PostgresEamDbSettings { } /** - * Use the current settings and the validation query - * to test the connection to the database. - * + * Use the current settings and the validation query to test the connection + * to the database. + * * @return true if successfull connection, else false. */ public boolean verifyConnection() { @@ -201,7 +201,7 @@ public final class PostgresEamDbSettings { if (null == conn) { return false; } - + boolean result = EamDbUtil.executeValidationQuery(conn, VALIDATION_QUERY); EamDbUtil.closeConnection(conn); return result; @@ -209,7 +209,7 @@ public final class PostgresEamDbSettings { /** * Check to see if the database exists. - * + * * @return true if exists, else false */ public boolean verifyDatabaseExists() { @@ -238,11 +238,11 @@ public final class PostgresEamDbSettings { } return false; } - + /** - * Use the current settings and the schema version query - * to test the database schema. - * + * Use the current settings and the schema version query to test the + * database schema. + * * @return true if successfull connection, else false. */ public boolean verifyDatabaseSchema() { @@ -275,8 +275,9 @@ public final class PostgresEamDbSettings { EamDbUtil.closeConnection(conn); } return true; - + } + /** * Initialize the database schema. * @@ -305,7 +306,6 @@ public final class PostgresEamDbSettings { // NOTE: The organizations will only have a small number of rows, so // an index is probably not worthwhile. - StringBuilder createCasesTable = new StringBuilder(); createCasesTable.append("CREATE TABLE IF NOT EXISTS cases ("); createCasesTable.append("id SERIAL PRIMARY KEY,"); @@ -402,7 +402,6 @@ public final class PostgresEamDbSettings { // NOTE: the db_info table currenly only has 1 row, so having an index // provides no benefit. - Connection conn = null; try { conn = getEphemeralConnection(false); @@ -514,9 +513,9 @@ public final class PostgresEamDbSettings { } /** - * To prevent issues where one command can honor case and another cannot, - * we will force the dbname to lower case. - * + * To prevent issues where one command can honor case and another cannot, we + * will force the dbname to lower case. + * * @return the dbName */ public String getDbName() { diff --git a/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDb.java b/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDb.java index 51c11280a5..5415c452dd 100644 --- a/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDb.java +++ b/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDb.java @@ -25,7 +25,6 @@ import java.sql.Statement; import java.util.List; import java.util.logging.Level; import org.apache.commons.dbcp2.BasicDataSource; -import org.openide.util.Exceptions; import org.sleuthkit.autopsy.coreutils.Logger; /** @@ -162,6 +161,12 @@ public class SqliteEamDb extends AbstractSqlEamDb { } } + @Override + protected String getConflictClause() { + // For sqlite, our conflict clause is part of the table schema + return ""; + } + @Override public List getBadTags() { return dbSettings.getBadTags(); diff --git a/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDbSettings.java b/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDbSettings.java index 6d6c39f368..50910a01a8 100644 --- a/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDbSettings.java +++ b/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/datamodel/SqliteEamDbSettings.java @@ -256,8 +256,8 @@ public final class SqliteEamDbSettings { createCasesTable.append("examiner_email text NOT NULL,"); createCasesTable.append("examiner_phone text NOT NULL,"); createCasesTable.append("notes text NOT NULL,"); - createCasesTable.append("foreign key (org_id) references organizations(id) on update set null on delete set null,"); - createCasesTable.append("CONSTRAINT case_uid_unique UNIQUE(case_uid)"); + createCasesTable.append("CONSTRAINT case_uid_unique UNIQUE(case_uid) ON CONFLICT IGNORE,"); + createCasesTable.append("foreign key (org_id) references organizations(id) ON UPDATE SET NULL ON DELETE SET NULL"); createCasesTable.append(")"); // NOTE: when there are few cases in the cases table, these indices may not be worthwhile @@ -281,7 +281,7 @@ public final class SqliteEamDbSettings { createGlobalReferenceSetsTable.append("set_name text NOT NULL,"); createGlobalReferenceSetsTable.append("version text NOT NULL,"); createGlobalReferenceSetsTable.append("import_date text NOT NULL,"); - createGlobalReferenceSetsTable.append("foreign key (org_id) references organizations(id) on update set null on delete set null"); + createGlobalReferenceSetsTable.append("foreign key (org_id) references organizations(id) ON UPDATE SET NULL ON DELETE SET NULL"); createGlobalReferenceSetsTable.append(")"); String globalReferenceSetsIdx1 = "CREATE INDEX IF NOT EXISTS global_reference_sets_org_id ON global_reference_sets (org_id)"; @@ -293,8 +293,8 @@ public final class SqliteEamDbSettings { createGlobalFilesTable.append("value text NOT NULL,"); createGlobalFilesTable.append("known_status text NOT NULL,"); createGlobalFilesTable.append("comment text NOT NULL,"); - createGlobalFilesTable.append("CONSTRAINT global_files_multi_unique UNIQUE(global_reference_set_id, value)"); - createGlobalFilesTable.append("foreign key (global_reference_set_id) references global_reference_sets(id) on update set null on delete set null"); + createGlobalFilesTable.append("CONSTRAINT global_files_multi_unique UNIQUE(global_reference_set_id, value) ON CONFLICT IGNORE,"); + createGlobalFilesTable.append("foreign key (global_reference_set_id) references global_reference_sets(id) ON UPDATE SET NULL ON DELETE SET NULL"); createGlobalFilesTable.append(")"); String globalFilesIdx1 = "CREATE INDEX IF NOT EXISTS global_files_value ON global_files (value)"; @@ -321,9 +321,9 @@ public final class SqliteEamDbSettings { createArtifactInstancesTableTemplate.append("file_path text NOT NULL,"); createArtifactInstancesTableTemplate.append("known_status text NOT NULL,"); createArtifactInstancesTableTemplate.append("comment text NOT NULL,"); - createArtifactInstancesTableTemplate.append("CONSTRAINT %s_instances_multi_unique UNIQUE(case_id, data_source_id, value, file_path),"); - createArtifactInstancesTableTemplate.append("foreign key (case_id) references cases(id) on update set null on delete set null,"); - createArtifactInstancesTableTemplate.append("foreign key (data_source_id) references data_sources(id) on update set null on delete set null"); + createArtifactInstancesTableTemplate.append("CONSTRAINT %s_instances_multi_unique UNIQUE(case_id, data_source_id, value, file_path) ON CONFLICT IGNORE,"); + createArtifactInstancesTableTemplate.append("foreign key (case_id) references cases(id) ON UPDATE SET NULL ON DELETE SET NULL,"); + createArtifactInstancesTableTemplate.append("foreign key (data_source_id) references data_sources(id) ON UPDATE SET NULL ON DELETE SET NULL"); createArtifactInstancesTableTemplate.append(")"); // TODO: do we need any more indices? diff --git a/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/CaseEventListener.java b/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/CaseEventListener.java index b9f11f7820..152cf2573f 100644 --- a/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/CaseEventListener.java +++ b/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/CaseEventListener.java @@ -48,8 +48,8 @@ import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.TskDataException; /** - * Listen for case events and update entries in the Central Repository - * database accordingly + * Listen for case events and update entries in the Central Repository database + * accordingly */ @Messages({"caseeventlistener.evidencetag=Evidence"}) public class CaseEventListener implements PropertyChangeListener { @@ -75,7 +75,8 @@ public class CaseEventListener implements PropertyChangeListener { || (af.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS) || (af.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.SLACK) || (af.getKnown() == TskData.FileKnown.KNOWN) - || (af.isDir() == true)) { + || (af.isDir() == true) + || (!af.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.ALLOC))) { break; } @@ -108,7 +109,7 @@ public class CaseEventListener implements PropertyChangeListener { new EamDataSource(deviceId, dsName), af.getParentPath() + af.getName(), tagAdded.getComment(), - EamArtifactInstance.KnownStatus.BAD, + TskData.FileKnown.BAD, EamArtifactInstance.GlobalStatus.LOCAL ); eamArtifact.addInstance(cei); diff --git a/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestModule.java b/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestModule.java index fdc0961da0..be332d2b28 100644 --- a/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestModule.java +++ b/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestModule.java @@ -49,8 +49,8 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.EamOrganization; import org.sleuthkit.datamodel.TskDataException; /** - * Ingest module for inserting entries into the Central Repository - * database on ingest of a data source + * Ingest module for inserting entries into the Central Repository database on + * ingest of a data source */ @Messages({"IngestModule.prevcases.text=Previous Cases"}) class IngestModule implements FileIngestModule { @@ -83,7 +83,8 @@ class IngestModule implements FileIngestModule { || (af.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS) || (af.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.SLACK) || (af.getKnown() == TskData.FileKnown.KNOWN) - || (af.isDir() == true)) { + || (af.isDir() == true) + || (!af.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.ALLOC))) { return ProcessResult.OK; } @@ -134,7 +135,7 @@ class IngestModule implements FileIngestModule { eamDataSource, af.getParentPath() + af.getName(), "", - EamArtifactInstance.KnownStatus.UNKNOWN, + TskData.FileKnown.UNKNOWN, EamArtifactInstance.GlobalStatus.LOCAL ); eamArtifact.addInstance(cefi); @@ -158,7 +159,7 @@ class IngestModule implements FileIngestModule { */ return; } - + EamDb dbManager = EamDb.getInstance(); try { dbManager.bulkInsertArtifacts(); diff --git a/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties b/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties index b57e54dd90..22a0ed7a5c 100644 --- a/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties +++ b/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties @@ -49,7 +49,7 @@ AddNewOrganizationDialog.bnOK.text=OK AddNewOrganizationDialog.tfName.tooltip=POC Name ManageTagsDialog.okButton.text=OK ManageTagsDialog.cancelButton.text=Cancel -ManageArtifactTypesDialog.taInstructionsMsg.text=Enable one or more correlation properties to use for correlation during Ingest. +ManageArtifactTypesDialog.taInstructionsMsg.text=Enable one or more correlation properties to use for correlation during ingest. Note, these properties are global and impact all users of the central repository. EamSqliteSettingsDialog.bnOk.text=OK EamPostgresSettingsDialog.bnSave.text=Save EamDbSettingsDialog.pnDatabaseConnectionSettings.border.title=Database Settings diff --git a/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ImportHashDatabaseDialog.java b/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ImportHashDatabaseDialog.java index 6656108771..9b6345d939 100644 --- a/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ImportHashDatabaseDialog.java +++ b/CentralRepository/src/org/sleuthkit/autopsy/centralrepository/optionspanel/ImportHashDatabaseDialog.java @@ -60,6 +60,7 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.EamGlobalSet; import org.sleuthkit.autopsy.centralrepository.datamodel.EamOrganization; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; +import org.sleuthkit.datamodel.TskData; /** * Instances of this class allow a user to select an existing hash database and @@ -515,11 +516,11 @@ final class ImportHashDatabaseDialog extends javax.swing.JDialog { } // insert hashes - EamArtifactInstance.KnownStatus knownStatus = EamArtifactInstance.KnownStatus.UNKNOWN; + TskData.FileKnown knownStatus = TskData.FileKnown.UNKNOWN; if (knownRadioButton.isSelected()) { - knownStatus = EamArtifactInstance.KnownStatus.KNOWN; + knownStatus = TskData.FileKnown.KNOWN; } else if (knownBadRadioButton.isSelected()) { - knownStatus = EamArtifactInstance.KnownStatus.BAD; + knownStatus = TskData.FileKnown.BAD; } String errorMessage = Bundle.ImportHashDatabaseDialog_errorMessage_failedToOpenHashDbMsg(selectedFilePath); @@ -529,7 +530,7 @@ final class ImportHashDatabaseDialog extends javax.swing.JDialog { // run in the background and close dialog SwingUtilities.invokeLater(new ImportHashDatabaseWorker(selectedFilePath, knownStatus, globalSetID, contentType)::execute); dispose(); - } catch (EamDbException ex) { + } catch (EamDbException | UnknownHostException ex) { Logger.getLogger(ImportHashDatabaseDialog.class.getName()).log(Level.SEVERE, errorMessage, ex); lbWarningMsg.setText(ex.getMessage()); } @@ -564,12 +565,12 @@ final class ImportHashDatabaseDialog extends javax.swing.JDialog { private class ImportHashDatabaseWorker extends SwingWorker { private final File file; - private final EamArtifactInstance.KnownStatus knownStatus; + private final TskData.FileKnown knownStatus; private final int globalSetID; private final ProgressHandle progress; private final EamArtifact.Type contentType; - public ImportHashDatabaseWorker(String filename, EamArtifactInstance.KnownStatus knownStatus, int globalSetID, EamArtifact.Type contentType) throws EamDbException { + public ImportHashDatabaseWorker(String filename, TskData.FileKnown knownStatus, int globalSetID, EamArtifact.Type contentType) throws EamDbException, UnknownHostException { this.file = new File(filename); this.knownStatus = knownStatus; this.globalSetID = globalSetID; diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java index 8ad63473d4..2c9fa00b0d 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java @@ -32,8 +32,14 @@ import java.util.concurrent.ExecutionException; import java.util.logging.Level; import javax.swing.JMenuItem; import javax.swing.SwingWorker; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.TableColumnModelEvent; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableColumn; +import javax.swing.event.TableColumnModelListener; +import javax.swing.text.JTextComponent; +import javax.swing.text.View; import org.apache.commons.lang.StringUtils; import org.openide.nodes.Node; import org.openide.util.Lookup; @@ -52,8 +58,8 @@ import org.netbeans.swing.etable.ETable; /** * Instances of this class display the BlackboardArtifacts associated with the - * Content represented by a Node. Each BlackboardArtifact is rendered displayed in a JTable - * representation of its BlackboardAttributes. + * Content represented by a Node. Each BlackboardArtifact is rendered displayed + * in a JTable representation of its BlackboardAttributes. */ @ServiceProvider(service = DataContentViewer.class, position = 3) public class DataContentViewerArtifact extends javax.swing.JPanel implements DataContentViewer { @@ -78,6 +84,7 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat Bundle.DataContentViewerArtifact_attrsTableHeader_value(), Bundle.DataContentViewerArtifact_attrsTableHeader_sources()}; private static final int[] COLUMN_WIDTHS = {100, 800, 100}; + private static final int CELL_BOTTOM_MARGIN = 5; public DataContentViewerArtifact() { initResultsTable(); @@ -85,6 +92,7 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat resultsTableScrollPane.setViewportView(resultsTable); customizeComponents(); resetComponents(); + resultsTable.setDefaultRenderer(Object.class, new MultiLineTableCellRenderer()); } private void initResultsTable() { @@ -100,11 +108,71 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat resultsTable.getTableHeader().setReorderingAllowed(false); resultsTable.setColumnHidingAllowed(false); resultsTable.getColumnModel().getSelectionModel().setSelectionMode(javax.swing.ListSelectionModel.SINGLE_INTERVAL_SELECTION); - updateColumnSizes(); + resultsTable.getColumnModel().addColumnModelListener(new TableColumnModelListener() { + + @Override + public void columnAdded(TableColumnModelEvent e) { + } + + @Override + public void columnRemoved(TableColumnModelEvent e) { + } + + @Override + public void columnMoved(TableColumnModelEvent e) { + + } + + @Override + public void columnMarginChanged(ChangeEvent e) { + updateRowHeights(); //When the user changes column width we may need to resize row height + } + + @Override + public void columnSelectionChanged(ListSelectionEvent e) { + } + }); + resultsTable.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_NEXT_COLUMN); + } + /** + * Sets the row heights to the heights of the content in their Value column. + */ + private void updateRowHeights() { + int valueColIndex = -1; + for (int col = 0; col < resultsTable.getColumnCount(); col++) { + if (resultsTable.getColumnName(col).equals(COLUMN_HEADERS[1])) { + valueColIndex = col; + } + } + if (valueColIndex != -1) { + for (int row = 0; row < resultsTable.getRowCount(); row++) { + Component comp = resultsTable.prepareRenderer( + resultsTable.getCellRenderer(row, valueColIndex), row, valueColIndex); + final int rowHeight; + if (comp instanceof JTextComponent) { + final JTextComponent tc = (JTextComponent) comp; + final View rootView = tc.getUI().getRootView(tc); + java.awt.Insets i = tc.getInsets(null); + rootView.setSize(resultsTable.getColumnModel().getColumn(valueColIndex) + .getPreferredWidth() - i.left - i.right, + Integer.MAX_VALUE); + rowHeight = (int) rootView.getPreferredSpan(View.Y_AXIS); + } else { + rowHeight = comp.getPreferredSize().height; + } + if (rowHeight > 0) { + resultsTable.setRowHeight(row, rowHeight + CELL_BOTTOM_MARGIN); + } + } + } + } + + /** + * Update the column widths so that the Value column has most of the space. + */ private void updateColumnSizes() { - resultsTable.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_NEXT_COLUMN); Enumeration columns = resultsTable.getColumnModel().getColumns(); while (columns.hasMoreElements()) { TableColumn col = columns.nextElement(); @@ -551,6 +619,7 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat DefaultTableModel tModel = ((DefaultTableModel) resultsTable.getModel()); tModel.setDataVector(viewUpdate.tableContents.getRows(), COLUMN_HEADERS); updateColumnSizes(); + updateRowHeights(); resultsTable.clearSelection(); this.setCursor(null); @@ -568,6 +637,7 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat DefaultTableModel tModel = ((DefaultTableModel) resultsTable.getModel()); tModel.setDataVector(waitRow, COLUMN_HEADERS); updateColumnSizes(); + updateRowHeights(); resultsTable.clearSelection(); // The output of the previous task is no longer relevant. if (currentTask != null) { @@ -758,4 +828,27 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat } } } + + /** + * TableCellRenderer for displaying multiline text. + */ + private class MultiLineTableCellRenderer implements javax.swing.table.TableCellRenderer { + + @Override + public Component getTableCellRendererComponent(javax.swing.JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + javax.swing.JTextArea jtex = new javax.swing.JTextArea(); + if (value instanceof String) { + jtex.setText((String) value); + jtex.setLineWrap(true); + jtex.setWrapStyleWord(true); + } + //cell backgroud color when selected + if (isSelected) { + jtex.setBackground(javax.swing.UIManager.getColor("Table.selectionBackground")); + } else { + jtex.setBackground(javax.swing.UIManager.getColor("Table.background")); + } + return jtex; + } + } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java index 5cfddc7f7a..8f0802007e 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java @@ -51,7 +51,9 @@ import org.sleuthkit.datamodel.TskData; /** * Filters database results by file extension. */ - public final class FileTypesByExtension implements AutopsyVisitableItem { +public final class FileTypesByExtension implements AutopsyVisitableItem { + + private static final Logger LOGGER = Logger.getLogger(FileTypesByExtension.class.getName()); private final SleuthkitCase skCase; @@ -72,13 +74,63 @@ import org.sleuthkit.datamodel.TskData; * Listens for case and ingest invest. Updates observers when events are * fired. FileType and FileTypes nodes are all listening to this. */ - private static class FileTypesByExtObservable extends Observable { + static private class FileTypesByExtObservable extends Observable { - private FileTypesByExtObservable() { + private boolean showCounts = true; + private final PropertyChangeListener pcl; + + private FileTypesByExtObservable(SleuthkitCase skCase) { super(); + this.pcl = (PropertyChangeEvent evt) -> { + String eventType = evt.getPropertyName(); + if (eventType.equals(IngestManager.IngestModuleEvent.CONTENT_CHANGED.toString()) || eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString()) || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString()) || eventType.equals(Case.Events.DATA_SOURCE_ADDED.toString())) { + /** + * Checking for a current case is a stop gap measure until a + * different way of handling the closing of cases is worked + * out. Currently, remote events may be received for a case + * that is already closed. + */ + try { + Case.getCurrentCase(); + shouldShowCounts(skCase); + update(); + } catch (IllegalStateException notUsed) { + /** + * Case is closed, do nothing. + */ + } + } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) { + // case was closed. Remove listeners so that we don't get called with a stale case handle + if (evt.getNewValue() == null) { + removeListeners(); + } + } + }; + IngestManager.getInstance().addIngestJobEventListener(pcl); IngestManager.getInstance().addIngestModuleEventListener(pcl); Case.addPropertyChangeListener(pcl); + + } + + /** + * Should the nodes show counts? + * + * + * @return True, unless the DB has more than 200k rows. + */ + private boolean shouldShowCounts(SleuthkitCase skCase) { + if (showCounts) { + try { + if (skCase.countFilesWhere("1=1") > 200000) { + showCounts = false; + } + } catch (TskCoreException tskCoreException) { + showCounts = false; + LOGGER.log(Level.SEVERE, "Error counting files.", tskCoreException); + } + } + return showCounts; } private void removeListeners() { @@ -88,31 +140,6 @@ import org.sleuthkit.datamodel.TskData; Case.removePropertyChangeListener(pcl); } - private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> { - String eventType = evt.getPropertyName(); - if (eventType.equals(IngestManager.IngestModuleEvent.CONTENT_CHANGED.toString()) || eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString()) || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString()) || eventType.equals(Case.Events.DATA_SOURCE_ADDED.toString())) { - /** - * Checking for a current case is a stop gap measure until a - * different way of handling the closing of cases is worked out. - * Currently, remote events may be received for a case that is - * already closed. - */ - try { - Case.getCurrentCase(); - update(); - } catch (IllegalStateException notUsed) { - /** - * Case is closed, do nothing. - */ - } - } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) { - // case was closed. Remove listeners so that we don't get called with a stale case handle - if (evt.getNewValue() == null) { - removeListeners(); - } - } - }; - private void update() { setChanged(); notifyObservers(); @@ -146,7 +173,7 @@ import org.sleuthkit.datamodel.TskData; * @param o Observable that was created by a higher-level node that * provides updates on events */ - private FileTypesByExtNode(SleuthkitCase skCase, FileTypesByExtension.RootFilter filter, Observable o) { + private FileTypesByExtNode(SleuthkitCase skCase, FileTypesByExtension.RootFilter filter, FileTypesByExtObservable o) { super(Children.create(new FileTypesByExtNodeChildren(skCase, filter, o), true), Lookups.singleton(filter == null ? FNAME : filter.getName())); this.filter = filter; init(); @@ -206,7 +233,7 @@ import org.sleuthkit.datamodel.TskData; private final SleuthkitCase skCase; private final FileTypesByExtension.RootFilter filter; - private final Observable notifier; + private final FileTypesByExtObservable notifier; /** * @@ -215,12 +242,12 @@ import org.sleuthkit.datamodel.TskData; * @param o Observable that provides updates based on events * being fired (or null if one needs to be created) */ - private FileTypesByExtNodeChildren(SleuthkitCase skCase, FileTypesByExtension.RootFilter filter, Observable o) { + private FileTypesByExtNodeChildren(SleuthkitCase skCase, FileTypesByExtension.RootFilter filter, FileTypesByExtObservable o) { super(); this.skCase = skCase; this.filter = filter; if (o == null) { - this.notifier = new FileTypesByExtObservable(); + this.notifier = new FileTypesByExtObservable(skCase); } else { this.notifier = o; } @@ -263,6 +290,7 @@ import org.sleuthkit.datamodel.TskData; FileTypesByExtension.SearchFilterInterface filter; SleuthkitCase skCase; + private final FileTypesByExtObservable notifier; /** * @@ -271,10 +299,11 @@ import org.sleuthkit.datamodel.TskData; * @param o Observable that sends updates when the child factories * should refresh */ - FileExtensionNode(FileTypesByExtension.SearchFilterInterface filter, SleuthkitCase skCase, Observable o) { + FileExtensionNode(FileTypesByExtension.SearchFilterInterface filter, SleuthkitCase skCase, FileTypesByExtObservable o) { super(Children.create(new FileExtensionNodeChildren(filter, skCase, o), true), Lookups.singleton(filter.getDisplayName())); this.filter = filter; this.skCase = skCase; + this.notifier = o; init(); o.addObserver(new ByExtNodeObserver()); } @@ -295,8 +324,10 @@ import org.sleuthkit.datamodel.TskData; } private void updateDisplayName() { - final long count = FileExtensionNodeChildren.calculateItems(skCase, filter); - super.setDisplayName(filter.getDisplayName() + " (" + count + ")"); + final String count = notifier.shouldShowCounts(skCase) + ? " (" + Long.toString(FileExtensionNodeChildren.calculateItems(skCase, filter)) + ")" + : ""; + super.setDisplayName(filter.getDisplayName() + count); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java index 1549d6bdc1..2ccc1dc1e8 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java @@ -71,41 +71,18 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi private final HashMap> existingMimeTypes = new HashMap<>(); private static final Logger LOGGER = Logger.getLogger(FileTypesByMimeType.class.getName()); + private boolean showCounts = true; + private void removeListeners() { deleteObservers(); IngestManager.getInstance().removeIngestJobEventListener(pcl); Case.removePropertyChangeListener(pcl); } - /* * The pcl is in the class because it has the easiest mechanisms to add and * remove itself during its life cycles. */ - private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> { - String eventType = evt.getPropertyName(); - if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString()) - || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) { - - /** - * Checking for a current case is a stop gap measure until a - * different way of handling the closing of cases is worked out. - * Currently, remote events may be received for a case that is - * already closed. - */ - try { - Case.getCurrentCase(); - populateHashMap(); - } catch (IllegalStateException notUsed) { - /** - * Case is closed, do nothing. - */ - } - } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) { - if (evt.getNewValue() == null) { - removeListeners(); - } - } - }; + private final PropertyChangeListener pcl; /** * Retrieve the media types by retrieving the keyset from the hashmap. @@ -140,7 +117,6 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi existingMimeTypes.clear(); if (skCase == null) { - return; } try (SleuthkitCase.CaseDbQuery dbQuery = skCase.executeQuery(allDistinctMimeTypesQuery.toString())) { @@ -170,12 +146,59 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi } FileTypesByMimeType(SleuthkitCase skCase) { + this.pcl = (PropertyChangeEvent evt) -> { + String eventType = evt.getPropertyName(); + if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString()) + || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) { + + /** + * Checking for a current case is a stop gap measure until a + * different way of handling the closing of cases is worked out. + * Currently, remote events may be received for a case that is + * already closed. + */ + try { + Case.getCurrentCase(); + shouldShowCounts(skCase); + + populateHashMap(); + } catch (IllegalStateException notUsed) { + /** + * Case is closed, do nothing. + */ + } + } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) { + if (evt.getNewValue() == null) { + removeListeners(); + } + } + }; IngestManager.getInstance().addIngestJobEventListener(pcl); Case.addPropertyChangeListener(pcl); this.skCase = skCase; populateHashMap(); } + /** + * Should the nodes show counts? + * + * + * @return True, unless the DB has more than 200k rows. + */ + private boolean shouldShowCounts(final SleuthkitCase skCase) { + if (showCounts) { + try { + if (skCase.countFilesWhere("1=1") > 200000) { + showCounts = false; + } + } catch (TskCoreException tskCoreException) { + showCounts = false; + LOGGER.log(Level.SEVERE, "Error counting files.", tskCoreException); + } + } + return showCounts; + } + @Override public T accept(AutopsyItemVisitor v) { return v.visit(this); @@ -358,10 +381,12 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi * results */ private void updateDisplayName(String mimeType) { - final long count = new MediaSubTypeNodeChildren(mimeType).calculateItems(skCase, mimeType); + final String count = shouldShowCounts(skCase) + ? " (" + Long.toString(new MediaSubTypeNodeChildren(mimeType).calculateItems(skCase, mimeType)) + ")" + : ""; String[] mimeTypeParts = mimeType.split("/"); //joins up all remaining parts of the mimeType into one sub-type string - super.setDisplayName(StringUtils.join(ArrayUtils.subarray(mimeTypeParts, 1, mimeTypeParts.length), "/") + " (" + count + ")"); + super.setDisplayName(StringUtils.join(ArrayUtils.subarray(mimeTypeParts, 1, mimeTypeParts.length), "/") + count); } /** diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties index 1a14ddf308..99d88938ce 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties @@ -236,10 +236,10 @@ KeywordSearchGlobalLanguageSettingsPanel.ingestSettingsLabel.text=Ingest setting KeywordSearchGlobalLanguageSettingsPanel.enableUTF16Checkbox.text=Enable UTF16LE and UTF16BE string extraction KeywordSearchGlobalLanguageSettingsPanel.languagesLabel.text=Enabled scripts (languages): KeywordSearchGlobalSearchSettingsPanel.timeRadioButton1.toolTipText=20 mins. (fastest ingest time) -KeywordSearchGlobalSearchSettingsPanel.timeRadioButton1.text=20 minutes +KeywordSearchGlobalSearchSettingsPanel.timeRadioButton1.text=20 minutes (slowest feedback, fastest ingest) KeywordSearchGlobalSearchSettingsPanel.timeRadioButton2.toolTipText=10 minutes (faster overall ingest time than default) -KeywordSearchGlobalSearchSettingsPanel.timeRadioButton2.text=10 minutes -KeywordSearchGlobalSearchSettingsPanel.frequencyLabel.text=Results update frequency during ingest (we have not seen significant performance differences between 5, 10, or 20 minute intervals): +KeywordSearchGlobalSearchSettingsPanel.timeRadioButton2.text=10 minutes (slower feedback, faster ingest) +KeywordSearchGlobalSearchSettingsPanel.frequencyLabel.text=Results update frequency during ingest: KeywordSearchGlobalSearchSettingsPanel.skipNSRLCheckBox.toolTipText=Requires Hash DB service to had run previously, or be selected for next ingest. KeywordSearchGlobalSearchSettingsPanel.skipNSRLCheckBox.text=Do not add files in NSRL (known files) to keyword index during ingest KeywordSearchGlobalSearchSettingsPanel.informationLabel.text=Information @@ -249,7 +249,7 @@ KeywordSearchGlobalSearchSettingsPanel.filesIndexedLabel.text=Files in keyword i KeywordSearchGlobalSearchSettingsPanel.showSnippetsCB.text=Show Keyword Preview in Keyword Search Results (will result in longer search times) KeywordSearchGlobalSearchSettingsPanel.chunksValLabel.text=0 KeywordSearchGlobalSearchSettingsPanel.timeRadioButton4.toolTipText=1 minute (overall ingest time will be longest) -KeywordSearchGlobalSearchSettingsPanel.timeRadioButton4.text_1=1 minute +KeywordSearchGlobalSearchSettingsPanel.timeRadioButton4.text_1=1 minute (faster feedback, longest ingest) KeywordSearchGlobalSearchSettingsPanel.chunksLabel.text=Chunks in keyword index: KeywordSearchGlobalSearchSettingsPanel.timeRadioButton3.toolTipText=5 minutes (overall ingest time will be longer) KeywordSearchGlobalSearchSettingsPanel.timeRadioButton3.text=5 minutes (default)