Merge in develop branch

This commit is contained in:
Richard Cordovano 2017-09-07 11:17:51 -04:00
commit 8e4d7e2c4f
91 changed files with 2145 additions and 1797 deletions

View File

@ -20,12 +20,14 @@ package org.sleuthkit.autopsy.casemodule;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.autopsy.casemodule.services.FileManager;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.LocalFilesDataSource;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskDataException;
@ -37,6 +39,7 @@ import org.sleuthkit.datamodel.TskDataException;
*/
class AddLocalFilesTask implements Runnable {
private static final Logger LOGGER = Logger.getLogger(AddLocalFilesTask.class.getName());
private final String deviceId;
private final String rootVirtualDirectoryName;
private final List<String> localFilePaths;
@ -89,6 +92,7 @@ class AddLocalFilesTask implements Runnable {
newDataSources.add(newDataSource.getRootDirectory());
} catch (TskDataException | TskCoreException ex) {
errors.add(ex.getMessage());
LOGGER.log(Level.SEVERE, String.format("Failed to add datasource: %s", ex.getMessage()), ex);
} finally {
DataSourceProcessorCallback.DataSourceProcessorResult result;
if (!errors.isEmpty()) {

View File

@ -377,7 +377,9 @@ public class Case {
*
* @param eventNames The events the subscriber is interested in.
* @param subscriber The subscriber (PropertyChangeListener) to add.
* @deprecated Use addEventTypeSubscriber instead.
*/
@Deprecated
public static void addEventSubscriber(Set<String> eventNames, PropertyChangeListener subscriber) {
eventPublisher.addSubscriber(eventNames, subscriber);
}
@ -385,9 +387,23 @@ public class Case {
/**
* Adds a subscriber to specific case events.
*
* @param eventName The event the subscriber is interested in.
* @param eventTypes The events the subscriber is interested in.
* @param subscriber The subscriber (PropertyChangeListener) to add.
*/
public static void addEventTypeSubscriber(Set<Events> eventTypes, PropertyChangeListener subscriber) {
eventTypes.forEach((Events event) -> {
eventPublisher.addSubscriber(event.toString(), subscriber);
});
}
/**
* Adds a subscriber to specific case events.
*
* @param eventName The event the subscriber is interested in.
* @param subscriber The subscriber (PropertyChangeListener) to add.
* @deprecated Use addEventTypeSubscriber instead.
*/
@Deprecated
public static void addEventSubscriber(String eventName, PropertyChangeListener subscriber) {
eventPublisher.addSubscriber(eventName, subscriber);
}
@ -412,6 +428,18 @@ public class Case {
eventPublisher.removeSubscriber(eventNames, subscriber);
}
/**
* Removes a subscriber to specific case events.
*
* @param eventTypes The events the subscriber is no longer interested in.
* @param subscriber The subscriber (PropertyChangeListener) to remove.
*/
public static void removeEventTypeSubscriber(Set<Events> eventTypes, PropertyChangeListener subscriber) {
eventTypes.forEach((Events event) -> {
eventPublisher.removeSubscriber(event.toString(), subscriber);
});
}
/**
* Checks if a case display name is valid, i.e., does not include any
* characters that cannot be used in file names.

View File

@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.casemodule;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.util.EnumSet;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import javax.swing.Action;
@ -49,7 +50,7 @@ final class CaseDeleteAction extends CallableSystemAction {
CaseDeleteAction() {
putValue(Action.NAME, NbBundle.getMessage(CaseDeleteAction.class, "CTL_CaseDeleteAction"));
this.setEnabled(false);
Case.addEventSubscriber(Case.Events.CURRENT_CASE.toString(), (PropertyChangeEvent evt) -> {
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), (PropertyChangeEvent evt) -> {
setEnabled(null != evt.getNewValue() && UserPreferences.getMode() != UserPreferences.SelectedMode.REVIEW);
});
}

View File

@ -22,6 +22,7 @@ import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.util.EnumSet;
import javax.swing.Action;
import javax.swing.JDialog;
import javax.swing.SwingUtilities;
@ -42,7 +43,7 @@ final class CasePropertiesAction extends CallableSystemAction {
CasePropertiesAction() {
putValue(Action.NAME, NbBundle.getMessage(CasePropertiesAction.class, "CTL_CasePropertiesAction"));
this.setEnabled(false);
Case.addEventSubscriber(Case.Events.CURRENT_CASE.toString(), (PropertyChangeEvent evt) -> {
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), (PropertyChangeEvent evt) -> {
setEnabled(null != evt.getNewValue());
});
}

View File

@ -24,9 +24,8 @@ import java.beans.PropertyChangeListener;
import java.io.Serializable;
import java.time.Duration;
import java.time.Instant;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
@ -57,7 +56,8 @@ final class CollaborationMonitor {
private static final String EVENT_CHANNEL_NAME = "%s-Collaboration-Monitor-Events"; //NON-NLS
private static final String COLLABORATION_MONITOR_EVENT = "COLLABORATION_MONITOR_EVENT"; //NON-NLS
private static final Set<String> CASE_EVENTS_OF_INTEREST = new HashSet<>(Arrays.asList(new String[]{Case.Events.ADDING_DATA_SOURCE.toString(), Case.Events.DATA_SOURCE_ADDED.toString(), Case.Events.ADDING_DATA_SOURCE_FAILED.toString()}));
private static final Set<Case.Events> CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.ADDING_DATA_SOURCE,
Case.Events.DATA_SOURCE_ADDED, Case.Events.ADDING_DATA_SOURCE_FAILED);
private static final int NUMBER_OF_PERIODIC_TASK_THREADS = 2;
private static final String PERIODIC_TASK_THREAD_NAME = "collab-monitor-periodic-tasks-%d"; //NON-NLS
private static final long HEARTBEAT_INTERVAL_MINUTES = 1;
@ -113,7 +113,7 @@ final class CollaborationMonitor {
*/
localTasksManager = new LocalTasksManager();
IngestManager.getInstance().addIngestJobEventListener(localTasksManager);
Case.addEventSubscriber(CASE_EVENTS_OF_INTEREST, localTasksManager);
Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, localTasksManager);
/**
* Start periodic tasks that:
@ -141,7 +141,7 @@ final class CollaborationMonitor {
}
}
Case.removeEventSubscriber(CASE_EVENTS_OF_INTEREST, localTasksManager);
Case.removeEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, localTasksManager);
IngestManager.getInstance().removeIngestJobEventListener(localTasksManager);
if (null != eventPublisher) {

View File

@ -1524,9 +1524,11 @@ public abstract class AbstractSqlEamDb implements EamDb {
@Override
public void bulkInsertReferenceTypeEntries(Set<EamGlobalFileInstance> globalInstances, EamArtifact.Type contentType) throws EamDbException {
Connection conn = connect();
PreparedStatement bulkPs = null;
try {
conn.setAutoCommit(false);
// FUTURE: have a separate global_files table for each Type.
String sql = "INSERT INTO %s(reference_set_id, value, known_status, comment) VALUES (?, ?, ?, ?) "
+ getConflictClause();
@ -1542,8 +1544,14 @@ public abstract class AbstractSqlEamDb implements EamDb {
}
bulkPs.executeBatch();
conn.commit();
} catch (SQLException ex) {
throw new EamDbException("Error inserting bulk artifacts.", ex); // NON-NLS
try{
conn.rollback();
} catch (SQLException ex2){
// We're alredy in an error state
}
throw new EamDbException("Error inserting bulk artifacts.", ex); // NON-NLS
} finally {
EamDbUtil.closePreparedStatement(bulkPs);
EamDbUtil.closeConnection(conn);

View File

@ -36,8 +36,11 @@ public interface EamDb {
* @throws EamDbException
*/
static EamDb getInstance() throws EamDbException {
EamDbPlatformEnum selectedPlatform = EamDbPlatformEnum.getSelectedPlatform();
EamDbPlatformEnum selectedPlatform = EamDbPlatformEnum.DISABLED;
if (EamDbUtil.useCentralRepo()) {
selectedPlatform = EamDbPlatformEnum.getSelectedPlatform();
}
switch (selectedPlatform) {
case POSTGRESQL:
return PostgresEamDb.getInstance();
@ -86,7 +89,8 @@ public interface EamDb {
* @return Is the database enabled
*/
static boolean isEnabled() {
return EamDbPlatformEnum.getSelectedPlatform() != EamDbPlatformEnum.DISABLED;
return EamDbUtil.useCentralRepo()
&& EamDbPlatformEnum.getSelectedPlatform() != EamDbPlatformEnum.DISABLED;
}
/**

View File

@ -3,7 +3,6 @@
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.sleuthkit.autopsy.centralrepository.datamodel;
import java.sql.Connection;
@ -15,13 +14,17 @@ import java.util.List;
import java.util.logging.Level;
import static org.sleuthkit.autopsy.centralrepository.datamodel.EamDb.SCHEMA_VERSION;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
/**
*
*/
public class EamDbUtil {
private final static Logger LOGGER = Logger.getLogger(EamDbUtil.class.getName());
private static final String CENTRAL_REPO_NAME = "CentralRepository";
private static final String CENTRAL_REPO_USE_KEY = "db.useCentralRepo";
/**
* Close the prepared statement.
*
@ -72,11 +75,12 @@ public class EamDbUtil {
}
}
}
/**
* Insert the default correlation types into the database.
*
*
* @param conn Open connection to use.
*
* @return true on success, else false
*/
public static boolean insertDefaultCorrelationTypes(Connection conn) {
@ -104,7 +108,7 @@ public class EamDbUtil {
}
return true;
}
/**
* Store the schema version into the db_info table.
*
@ -112,6 +116,7 @@ public class EamDbUtil {
* loaded.
*
* @param conn Open connection to use.
*
* @return true on success, else false
*/
public static boolean insertSchemaVersion(Connection conn) {
@ -133,14 +138,14 @@ public class EamDbUtil {
/**
* Query to see if the SCHEMA_VERSION is set in the db.
*
*
* @return true if set, else false.
*/
public static boolean schemaVersionIsSet(Connection conn) {
if (null == conn) {
return false;
}
ResultSet resultSet = null;
try {
Statement tester = conn.createStatement();
@ -157,17 +162,38 @@ public class EamDbUtil {
return true;
}
/**
* Use the current settings and the validation query
* to test the connection to the database.
*
/**
* If the Central Repos use has been enabled.
*
* @return true if the Central Repo may be configured, false if it should
* not be able to be
*/
public static boolean useCentralRepo() {
return Boolean.parseBoolean(ModuleSettings.getConfigSetting(CENTRAL_REPO_NAME, CENTRAL_REPO_USE_KEY));
}
/**
* Saves the setting for whether the Central Repo should be able to be
* configured.
*
* @param centralRepoCheckBoxIsSelected - true if the central repo can be
* used
*/
public static void setUseCentralRepo(boolean centralRepoCheckBoxIsSelected) {
ModuleSettings.setConfigSetting(CENTRAL_REPO_NAME, CENTRAL_REPO_USE_KEY, Boolean.toString(centralRepoCheckBoxIsSelected));
}
/**
* Use the current settings and the validation query to test the connection
* to the database.
*
* @return true if successfull query execution, else false.
*/
public static boolean executeValidationQuery(Connection conn, String validationQuery) {
if (null == conn) {
return false;
}
ResultSet resultSet = null;
try {
Statement tester = conn.createStatement();
@ -183,22 +209,23 @@ public class EamDbUtil {
return false;
}
/**
* Conver thte Type's DbTableName string to the *_instances table name.
*
*
* @param type Correlation Type
* @return Instance table name for this Type.
*
* @return Instance table name for this Type.
*/
public static String correlationTypeToInstanceTableName(EamArtifact.Type type) {
return type.getDbTableName() + "_instances";
}
/**
* Convert the Type's DbTableName string to the reference_* table name.
*
*
* @param type Correlation Type
*
* @return Reference table name for this Type.
*/
public static String correlationTypeToReferenceTableName(EamArtifact.Type type) {

View File

@ -72,8 +72,10 @@ public class PostgresEamDb extends AbstractSqlEamDb {
public void shutdownConnections() throws EamDbException {
try {
synchronized(this) {
connectionPool.close();
connectionPool = null; // force it to be re-created on next connect()
if(connectionPool != null){
connectionPool.close();
connectionPool = null; // force it to be re-created on next connect()
}
}
} catch (SQLException ex) {
throw new EamDbException("Failed to close existing database connections.", ex); // NON-NLS

View File

@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.centralrepository.datamodel;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
@ -93,7 +94,7 @@ public final class SqliteEamDbSettings {
if (badTagsStr == null) {
badTagsStr = DEFAULT_BAD_TAGS;
}
if(badTagsStr.isEmpty()){
if (badTagsStr.isEmpty()) {
badTags = new ArrayList<>();
} else {
badTags = new ArrayList<>(Arrays.asList(badTagsStr.split(",")));
@ -111,13 +112,13 @@ public final class SqliteEamDbSettings {
/**
* Verify that the db directory path exists.
*
*
* @return true if exists, else false
*/
public boolean dbDirectoryExists() {
// Ensure dbDirectory is a valid directory
File dbDir = new File(getDbDirectory());
if (!dbDir.exists()) {
return false;
} else if (!dbDir.isDirectory()) {
@ -130,7 +131,7 @@ public final class SqliteEamDbSettings {
/**
* Create the db directory if it does not exist.
*
*
* @return true is successfully created or already exists, else false
*/
public boolean createDbDirectory() {
@ -139,7 +140,7 @@ public final class SqliteEamDbSettings {
File dbDir = new File(getDbDirectory());
Files.createDirectories(dbDir.toPath());
LOGGER.log(Level.INFO, "sqlite directory did not exist, created it at {0}.", getDbDirectory()); // NON-NLS
} catch (IOException ex) {
} catch (IOException | InvalidPathException | SecurityException ex) {
LOGGER.log(Level.SEVERE, "Failed to create sqlite database directory.", ex); // NON-NLS
return false;
}
@ -162,10 +163,11 @@ public final class SqliteEamDbSettings {
}
/**
* Use the current settings to get an ephemeral client connection for testing.
*
* Use the current settings to get an ephemeral client connection for
* testing.
*
* If the directory path does not exist, it will return null.
*
*
* @return Connection or null.
*/
private Connection getEphemeralConnection() {
@ -186,9 +188,9 @@ public final class SqliteEamDbSettings {
}
/**
* 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() {
@ -196,16 +198,16 @@ public final class SqliteEamDbSettings {
if (null == conn) {
return false;
}
boolean result = EamDbUtil.executeValidationQuery(conn, VALIDATION_QUERY);
EamDbUtil.closeConnection(conn);
return result;
}
/**
* 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() {
@ -247,7 +249,6 @@ public final class SqliteEamDbSettings {
// 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 integer primary key autoincrement NOT NULL,");
@ -346,7 +347,6 @@ public final class SqliteEamDbSettings {
// NOTE: the db_info table currenly only has 1 row, so having an index
// provides no benefit.
Connection conn = null;
try {
conn = getEphemeralConnection();
@ -385,7 +385,7 @@ public final class SqliteEamDbSettings {
for (EamArtifact.Type type : DEFAULT_CORRELATION_TYPES) {
reference_type_dbname = EamDbUtil.correlationTypeToReferenceTableName(type);
instance_type_dbname = EamDbUtil.correlationTypeToInstanceTableName(type);
stmt.execute(String.format(createArtifactInstancesTableTemplate.toString(), instance_type_dbname, instance_type_dbname));
stmt.execute(String.format(instancesIdx1, instance_type_dbname, instance_type_dbname));
stmt.execute(String.format(instancesIdx2, instance_type_dbname, instance_type_dbname));
@ -397,7 +397,7 @@ public final class SqliteEamDbSettings {
stmt.execute(String.format(createReferenceTypesTableTemplate.toString(), reference_type_dbname, reference_type_dbname));
stmt.execute(String.format(referenceTypesIdx1, reference_type_dbname, reference_type_dbname));
stmt.execute(String.format(referenceTypesIdx2, reference_type_dbname, reference_type_dbname));
}
}
}
} catch (SQLException ex) {
LOGGER.log(Level.SEVERE, "Error initializing db schema.", ex); // NON-NLS
@ -422,7 +422,7 @@ public final class SqliteEamDbSettings {
EamDbUtil.closeConnection(conn);
return result;
}
public boolean isChanged() {
String dbNameString = ModuleSettings.getConfigSetting("CentralRepository", "db.sqlite.dbName"); // NON-NLS
String dbDirectoryString = ModuleSettings.getConfigSetting("CentralRepository", "db.sqlite.dbDirectory"); // NON-NLS

View File

@ -13,7 +13,7 @@ EamSqliteSettingsDialog.bnCancel.text=Cancel
EamSqliteSettingsDialog.lbTestDatabase.text=
EamSqliteSettingsDialog.bnTestDatabase.text=Test Connection
EamSqliteSettingsDialog.lbTestDatabaseWarning.text=
EamSqliteSettingsDialog.bnDatabasePathFileOpen.text=Open...
EamSqliteSettingsDialog.bnDatabasePathFileOpen.text=Browse...
EamSqliteSettingsDialog.tfDatabasePath.toolTipText=Filename and path to store SQLite db file
EamSqliteSettingsDialog.tfDatabasePath.text=
EamSqliteSettingsDialog.lbDatabasePath.text=Database Path :
@ -52,24 +52,16 @@ ManageTagsDialog.cancelButton.text=Cancel
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
EamDbSettingsDialog.rdioBnPostgreSQL.text=PostgreSQL
EamDbSettingsDialog.rdioBnSQLite.text=SQLite
EamDbSettingsDialog.bnDatabasePathFileOpen.text=Open...
EamDbSettingsDialog.bnDatabasePathFileOpen.text=Browse...
EamDbSettingsDialog.tfDatabasePath.toolTipText=Filename and path to store SQLite db file
EamDbSettingsDialog.tfDatabasePath.text=
EamDbSettingsDialog.lbDatabasePath.text=Database Path :
EamDbSettingsDialog.rdioBnDisabled.text=Disabled
EamDbSettingsDialog.bnCancel.text=Cancel
EamDbSettingsDialog.bnOk.text=OK
EamDbSettingsDialog.bnTest.text=Test
EamDbSettingsDialog.lbHostName.text=Host Name / IP :
EamDbSettingsDialog.lbDatabaseName.text=Database name :
EamDbSettingsDialog.lbUserPassword.text=User Password :
EamDbSettingsDialog.lbUserName.text=User Name :
EamDbSettingsDialog.lbPort.text=Port :
EamDbSettingsDialog.bnCreateDb.text=Create
EamDbSettingsDialog.pnSetupGuidance.border.title=Setup Guidance
GlobalSettingsPanel.pnDatabaseConfiguration.title=Database Configuration
GlobalSettingsPanel.lbDbPlatformTypeLabel.text=Type:
GlobalSettingsPanel.lbDbNameLabel.text=Name:

View File

@ -29,12 +29,11 @@
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="1" attributes="0">
<Component id="pnSetupGuidance" max="32767" attributes="0"/>
<Component id="pnDatabaseConnectionSettings" alignment="0" max="32767" attributes="0"/>
<Component id="pnButtons" alignment="1" max="32767" attributes="0"/>
<Group type="102" attributes="0">
<EmptySpace min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="pnButtons" max="32767" attributes="0"/>
<Component id="pnSQLiteSettings" alignment="0" max="32767" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
@ -43,303 +42,22 @@
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="pnSetupGuidance" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="10" max="-2" attributes="0"/>
<Component id="pnSQLiteSettings" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Component id="pnDatabaseConnectionSettings" min="-2" pref="348" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="pnButtons" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="10" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javax.swing.JPanel" name="pnDatabaseConnectionSettings">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo">
<TitledBorder title="Database Settings">
<ResourceString PropertyName="titleX" bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="EamDbSettingsDialog.pnDatabaseConnectionSettings.border.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<Font PropertyName="font" name="Tahoma" size="12" style="0"/>
</TitledBorder>
</Border>
</Property>
<Property name="name" type="java.lang.String" value="" noResource="true"/>
</Properties>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="rdioBnPostgreSQL" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="rdioBnSQLite" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="rdioBnDisabled" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="32767" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="pnSQLiteSettings" alignment="0" max="32767" attributes="0"/>
<Component id="pnPostgreSQLSettings" alignment="1" max="32767" attributes="0"/>
</Group>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<EmptySpace max="32767" attributes="0"/>
<Component id="rdioBnDisabled" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="13" max="-2" attributes="0"/>
<Component id="rdioBnSQLite" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="pnSQLiteSettings" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="12" max="-2" attributes="0"/>
<Component id="rdioBnPostgreSQL" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="pnPostgreSQLSettings" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="329" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javax.swing.JPanel" name="pnSQLiteSettings">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.EtchedBorderInfo">
<EtchetBorder/>
</Border>
</Property>
</Properties>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="lbDatabasePath" min="-2" max="-2" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="tfDatabasePath" min="-2" pref="343" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Component id="bnDatabasePathFileOpen" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="lbDatabasePath" alignment="3" min="-2" pref="23" max="-2" attributes="0"/>
<Component id="tfDatabasePath" alignment="3" min="-2" pref="23" max="-2" attributes="0"/>
<Component id="bnDatabasePathFileOpen" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="lbDatabasePath">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="EamDbSettingsDialog.lbDatabasePath.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="tfDatabasePath">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="EamDbSettingsDialog.tfDatabasePath.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="EamDbSettingsDialog.tfDatabasePath.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="focusLost" listener="java.awt.event.FocusListener" parameters="java.awt.event.FocusEvent" handler="tfDatabasePathFocusLost"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="bnDatabasePathFileOpen">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="EamDbSettingsDialog.bnDatabasePathFileOpen.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnDatabasePathFileOpenActionPerformed"/>
</Events>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="pnPostgreSQLSettings">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.EtchedBorderInfo">
<EtchetBorder/>
</Border>
</Property>
</Properties>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="lbHostName" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="lbPort" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="lbDatabaseName" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="lbUserName" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="lbUserPassword" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" max="-2" attributes="0">
<Component id="tbDbUsername" alignment="0" pref="439" max="32767" attributes="0"/>
<Component id="tbDbName" alignment="0" max="32767" attributes="0"/>
<Component id="tbDbPort" alignment="0" max="32767" attributes="0"/>
<Component id="tbDbHostname" max="32767" attributes="0"/>
<Component id="jpDbPassword" max="32767" attributes="0"/>
</Group>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="1" attributes="0">
<Component id="tbDbHostname" alignment="1" min="-2" max="-2" attributes="0"/>
<Component id="lbHostName" alignment="1" min="-2" pref="22" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" max="-2" attributes="0">
<Component id="tbDbPort" alignment="0" max="32767" attributes="0"/>
<Component id="lbPort" alignment="0" min="-2" pref="20" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" max="-2" attributes="0">
<Component id="tbDbName" alignment="0" max="32767" attributes="0"/>
<Component id="lbDatabaseName" alignment="0" min="-2" pref="20" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" max="-2" attributes="0">
<Component id="tbDbUsername" alignment="0" max="32767" attributes="0"/>
<Component id="lbUserName" alignment="0" min="-2" pref="20" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="lbUserPassword" alignment="3" min="-2" pref="20" max="-2" attributes="0"/>
<Component id="jpDbPassword" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace pref="19" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="lbHostName">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="EamDbSettingsDialog.lbHostName.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="lbPort">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="EamDbSettingsDialog.lbPort.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="lbUserName">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="EamDbSettingsDialog.lbUserName.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="lbUserPassword">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="EamDbSettingsDialog.lbUserPassword.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="lbDatabaseName">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="EamDbSettingsDialog.lbDatabaseName.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="tbDbHostname">
</Component>
<Component class="javax.swing.JTextField" name="tbDbPort">
</Component>
<Component class="javax.swing.JTextField" name="tbDbName">
</Component>
<Component class="javax.swing.JTextField" name="tbDbUsername">
</Component>
<Component class="javax.swing.JPasswordField" name="jpDbPassword">
</Component>
</SubComponents>
</Container>
<Component class="javax.swing.JRadioButton" name="rdioBnSQLite">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="EamDbSettingsDialog.rdioBnSQLite.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="rdioBnSQLiteActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JRadioButton" name="rdioBnPostgreSQL">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="EamDbSettingsDialog.rdioBnPostgreSQL.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="rdioBnPostgreSQLActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JRadioButton" name="rdioBnDisabled">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="EamDbSettingsDialog.rdioBnDisabled.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="rdioBnDisabledActionPerformed"/>
</Events>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="pnButtons">
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="bnTest" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="lbTestIcon" min="-2" pref="20" max="-2" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="bnCreateDb" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="lbCreateIcon" min="-2" pref="21" max="-2" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="32767" attributes="0"/>
<Component id="bnOk" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="11" max="-2" attributes="0"/>
@ -352,20 +70,11 @@
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="lbCreateIcon" max="32767" attributes="0"/>
<Component id="lbTestIcon" alignment="1" max="32767" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Group type="103" groupAlignment="3" attributes="0">
<Component id="bnOk" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="bnCancel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="bnTest" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="bnCreateDb" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="bnOk" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="bnCancel" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
@ -391,40 +100,13 @@
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnOkActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="bnTest">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="EamDbSettingsDialog.bnTest.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnTestActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="bnCreateDb">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="EamDbSettingsDialog.bnCreateDb.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnCreateDbActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="lbTestIcon">
</Component>
<Component class="javax.swing.JLabel" name="lbCreateIcon">
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="pnSetupGuidance">
<Container class="javax.swing.JPanel" name="pnSQLiteSettings">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo">
<TitledBorder title="Setup Guidance">
<ResourceString PropertyName="titleX" bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="EamDbSettingsDialog.pnSetupGuidance.border.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<Font PropertyName="font" name="Tahoma" size="12" style="0"/>
</TitledBorder>
<Border info="org.netbeans.modules.form.compat2.border.EtchedBorderInfo">
<EtchetBorder/>
</Border>
</Property>
</Properties>
@ -434,55 +116,176 @@
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="jScrollPane1" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="lbHostName" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="lbPort" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="lbUserName" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="lbDatabaseType" alignment="0" min="-2" pref="82" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="lbDatabasePath" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="lbUserPassword" alignment="1" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace min="-2" pref="10" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Component id="cbDatabaseType" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="lbSingleUserSqLite" pref="467" max="32767" attributes="0"/>
<EmptySpace min="-2" pref="9" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<Component id="tfDatabasePath" max="32767" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="bnDatabasePathFileOpen" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="11" max="-2" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="tbDbHostname" max="32767" attributes="0"/>
<Component id="jpDbPassword" alignment="0" max="32767" attributes="0"/>
<Component id="tbDbUsername" alignment="1" max="32767" attributes="0"/>
<Component id="tbDbPort" alignment="0" max="32767" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="10" max="-2" attributes="0"/>
</Group>
</Group>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="6" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="103" groupAlignment="3" attributes="0">
<Component id="cbDatabaseType" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="lbSingleUserSqLite" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="lbDatabaseType" alignment="1" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="jScrollPane1" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="lbDatabasePath" alignment="3" min="-2" pref="23" max="-2" attributes="0"/>
<Component id="tfDatabasePath" alignment="3" min="-2" pref="23" max="-2" attributes="0"/>
<Component id="bnDatabasePathFileOpen" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="tbDbHostname" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="lbHostName" alignment="3" min="-2" pref="22" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="tbDbPort" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="lbPort" alignment="3" min="-2" pref="20" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="tbDbUsername" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="lbUserName" alignment="3" min="-2" pref="20" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="lbUserPassword" alignment="3" min="-2" pref="20" max="-2" attributes="0"/>
<Component id="jpDbPassword" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="10" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javax.swing.JScrollPane" name="jScrollPane1">
<Component class="javax.swing.JLabel" name="lbDatabasePath">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="null"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="EamDbSettingsDialog.lbDatabasePath.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JTextArea" name="taSetupGuidance">
<Properties>
<Property name="editable" type="boolean" value="false"/>
<Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="f0" green="f0" red="f0" type="rgb"/>
</Property>
<Property name="columns" type="int" value="20"/>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Monospaced" size="12" style="0"/>
</Property>
<Property name="lineWrap" type="boolean" value="true"/>
<Property name="rows" type="int" value="3"/>
<Property name="tabSize" type="int" value="4"/>
<Property name="wrapStyleWord" type="boolean" value="true"/>
<Property name="autoscrolls" type="boolean" value="false"/>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="null"/>
</Property>
<Property name="requestFocusEnabled" type="boolean" value="false"/>
<Property name="verifyInputWhenFocusTarget" type="boolean" value="false"/>
</Properties>
</Component>
</SubComponents>
</Container>
</Component>
<Component class="javax.swing.JTextField" name="tfDatabasePath">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="EamDbSettingsDialog.tfDatabasePath.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="EamDbSettingsDialog.tfDatabasePath.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="bnDatabasePathFileOpen">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="EamDbSettingsDialog.bnDatabasePathFileOpen.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnDatabasePathFileOpenActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="lbHostName">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="EamDbSettingsDialog.lbHostName.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="tbDbHostname">
</Component>
<Component class="javax.swing.JLabel" name="lbPort">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="EamDbSettingsDialog.lbPort.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="tbDbPort">
</Component>
<Component class="javax.swing.JLabel" name="lbUserName">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="EamDbSettingsDialog.lbUserName.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="tbDbUsername">
</Component>
<Component class="javax.swing.JLabel" name="lbUserPassword">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="EamDbSettingsDialog.lbUserPassword.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JPasswordField" name="jpDbPassword">
</Component>
<Component class="javax.swing.JComboBox" name="cbDatabaseType">
<Properties>
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new javax.swing.DefaultComboBoxModel&lt;&gt;(new EamDbPlatformEnum[]{EamDbPlatformEnum.POSTGRESQL, EamDbPlatformEnum.SQLITE})" type="code"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cbDatabaseTypeActionPerformed"/>
</Events>
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;EamDbPlatformEnum&gt;"/>
</AuxValues>
</Component>
<Component class="javax.swing.JLabel" name="lbSingleUserSqLite">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="EamDbSettingsDialog.lbSingleUserSqLite.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="lbDatabaseType">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="EamDbSettingsDialog.lbDatabaseType.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
</SubComponents>

View File

@ -25,6 +25,10 @@
<Component id="pnDatabaseContentButtons" max="32767" attributes="0"/>
<Component id="tbOops" alignment="1" max="32767" attributes="0"/>
<Component id="pnDatabaseConfiguration" max="32767" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Component id="cbUseCentralRepo" min="-2" pref="186" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
@ -33,13 +37,14 @@
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="cbUseCentralRepo" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="pnDatabaseConfiguration" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="tbOops" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="pnDatabaseContentButtons" min="-2" pref="50" max="-2" attributes="0"/>
<EmptySpace min="0" pref="18" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
@ -240,5 +245,15 @@
</Property>
</Properties>
</Component>
<Component class="javax.swing.JCheckBox" name="cbUseCentralRepo">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties" key="GlobalSettingsPanel.cbUseCentralRepo.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cbUseCentralRepoActionPerformed"/>
</Events>
</Component>
</SubComponents>
</Form>

View File

@ -29,6 +29,8 @@ import org.sleuthkit.autopsy.events.AutopsyEvent;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.IngestModuleGlobalSettingsPanel;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbPlatformEnum;
import static org.sleuthkit.autopsy.centralrepository.datamodel.EamDbPlatformEnum.DISABLED;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbUtil;
import org.sleuthkit.autopsy.centralrepository.datamodel.PostgresEamDbSettings;
import org.sleuthkit.autopsy.centralrepository.datamodel.SqliteEamDbSettings;
@ -53,7 +55,8 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
addIngestJobEventsListener();
}
@Messages({"GlobalSettingsPanel.title=Central Repository Settings"})
@Messages({"GlobalSettingsPanel.title=Central Repository Settings",
"GlobalSettingsPanel.cbUseCentralRepo.text=Use a Central Repository"})
private void customizeComponents() {
setName(Bundle.GlobalSettingsPanel_title());
}
@ -85,6 +88,7 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
bnManageTags = new javax.swing.JButton();
bnManageTypes = new javax.swing.JButton();
tbOops = new javax.swing.JTextField();
cbUseCentralRepo = new javax.swing.JCheckBox();
setName(""); // NOI18N
@ -198,6 +202,13 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
tbOops.setText(org.openide.util.NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.tbOops.text")); // NOI18N
tbOops.setBorder(null);
org.openide.awt.Mnemonics.setLocalizedText(cbUseCentralRepo, org.openide.util.NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.cbUseCentralRepo.text")); // NOI18N
cbUseCentralRepo.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
cbUseCentralRepoActionPerformed(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
@ -207,50 +218,65 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(pnDatabaseContentButtons, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(tbOops, javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(pnDatabaseConfiguration, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addComponent(pnDatabaseConfiguration, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup()
.addComponent(cbUseCentralRepo, javax.swing.GroupLayout.PREFERRED_SIZE, 186, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 0, Short.MAX_VALUE)))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap()
.addComponent(cbUseCentralRepo)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(pnDatabaseConfiguration, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(tbOops, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(pnDatabaseContentButtons, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 18, Short.MAX_VALUE))
.addContainerGap())
);
}// </editor-fold>//GEN-END:initComponents
private void bnImportDatabaseActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnImportDatabaseActionPerformed
store();
ImportHashDatabaseDialog dialog = new ImportHashDatabaseDialog();
firePropertyChange(OptionsPanelController.PROP_VALID, null, null);
}//GEN-LAST:event_bnImportDatabaseActionPerformed
private void bnManageTagsActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnManageTagsActionPerformed
store();
ManageTagsDialog dialog = new ManageTagsDialog();
firePropertyChange(OptionsPanelController.PROP_VALID, null, null);
}//GEN-LAST:event_bnManageTagsActionPerformed
private void bnManageTypesActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnManageTypesActionPerformed
store();
ManageCorrelationPropertiesDialog dialog = new ManageCorrelationPropertiesDialog();
firePropertyChange(OptionsPanelController.PROP_VALID, null, null);
}//GEN-LAST:event_bnManageTypesActionPerformed
private void bnDbConfigureActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnDbConfigureActionPerformed
store();
EamDbSettingsDialog dialog = new EamDbSettingsDialog();
load(); // reload db settings content and update buttons
}//GEN-LAST:event_bnDbConfigureActionPerformed
private void cbUseCentralRepoActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbUseCentralRepoActionPerformed
//if saved setting is disabled checkbox should be disabled already
enableDatabaseConfigureButton(cbUseCentralRepo.isSelected());
enableButtonSubComponents(cbUseCentralRepo.isSelected() && !EamDbPlatformEnum.getSelectedPlatform().equals(DISABLED));
this.ingestStateUpdated();
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}//GEN-LAST:event_cbUseCentralRepoActionPerformed
@Override
@Messages({"GlobalSettingsPanel.validationerrMsg.mustConfigure=Configure the database to enable this module."})
public void load() {
tbOops.setText("");
enableAllSubComponents(false);
EamDbPlatformEnum selectedPlatform = EamDbPlatformEnum.getSelectedPlatform();
cbUseCentralRepo.setSelected(EamDbUtil.useCentralRepo()); // NON-NLS
switch (selectedPlatform) {
case POSTGRESQL:
PostgresEamDbSettings dbSettingsPg = new PostgresEamDbSettings();
@ -270,16 +296,16 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
lbDbPlatformValue.setText(EamDbPlatformEnum.DISABLED.toString());
lbDbNameValue.setText("");
lbDbLocationValue.setText("");
enableDatabaseConfigureButton(true);
enableDatabaseConfigureButton(cbUseCentralRepo.isSelected());
tbOops.setText(Bundle.GlobalSettingsPanel_validationerrMsg_mustConfigure());
break;
}
this.ingestStateUpdated();
}
@Override
public void store() { // Click OK or Apply on Options Panel
EamDbUtil.setUseCentralRepo(cbUseCentralRepo.isSelected());
}
/**
@ -293,6 +319,7 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
@Override
public void saveSettings() { // Click OK on Global Settings Panel
store();
}
@Override
@ -334,7 +361,10 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
if (IngestManager.getInstance().isIngestRunning()) {
tbOops.setText(Bundle.GlobalSettingsPanel_validationErrMsg_ingestRunning());
enableAllSubComponents(false);
cbUseCentralRepo.setEnabled(false);
} else if (!cbUseCentralRepo.isEnabled()) {
cbUseCentralRepo.setEnabled(true);
load();
}
}
@ -347,8 +377,8 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
* @return True
*/
private boolean enableAllSubComponents(Boolean enable) {
enableDatabaseConfigureButton(enable);
enableButtonSubComponents(enable);
enableDatabaseConfigureButton(cbUseCentralRepo.isSelected() && enable);
enableButtonSubComponents(cbUseCentralRepo.isSelected() && enable);
return true;
}
@ -359,9 +389,18 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
*
* @return True
*/
private boolean enableDatabaseConfigureButton(Boolean enable) {
bnDbConfigure.setEnabled(enable);
return true;
private void enableDatabaseConfigureButton(Boolean enable) {
boolean ingestRunning = IngestManager.getInstance().isIngestRunning();
pnDatabaseConfiguration.setEnabled(enable && !ingestRunning);
pnDatabaseContentButtons.setEnabled(enable && !ingestRunning);
bnDbConfigure.setEnabled(enable && !ingestRunning);
lbDbLocationLabel.setEnabled(enable && !ingestRunning);
lbDbLocationValue.setEnabled(enable && !ingestRunning);
lbDbNameLabel.setEnabled(enable && !ingestRunning);
lbDbNameValue.setEnabled(enable && !ingestRunning);
lbDbPlatformTypeLabel.setEnabled(enable && !ingestRunning);
lbDbPlatformValue.setEnabled(enable && !ingestRunning);
tbOops.setEnabled(enable && !ingestRunning);
}
/**
@ -373,9 +412,10 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
* @return True
*/
private boolean enableButtonSubComponents(Boolean enable) {
bnManageTypes.setEnabled(enable);
bnImportDatabase.setEnabled(enable);
bnManageTags.setEnabled(enable);
boolean ingestRunning = IngestManager.getInstance().isIngestRunning();
bnManageTypes.setEnabled(enable && !ingestRunning);
bnImportDatabase.setEnabled(enable && !ingestRunning);
bnManageTags.setEnabled(enable && !ingestRunning);
return true;
}
@ -384,6 +424,7 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
private javax.swing.JButton bnImportDatabase;
private javax.swing.JButton bnManageTags;
private javax.swing.JButton bnManageTypes;
private javax.swing.JCheckBox cbUseCentralRepo;
private javax.swing.JLabel lbDbLocationLabel;
private javax.swing.JLabel lbDbLocationValue;
private javax.swing.JLabel lbDbNameLabel;

View File

@ -72,6 +72,7 @@ final class ImportHashDatabaseDialog extends javax.swing.JDialog {
private final JFileChooser fileChooser = new JFileChooser();
private final static String LAST_FILE_PATH_KEY = "CentralRepositoryImport_Path"; // NON-NLS
private final int HASH_IMPORT_THRESHOLD = 10000;
private EamOrganization selectedOrg = null;
private List<EamOrganization> orgs = null;
private final Collection<JTextField> textBoxes;
@ -533,7 +534,8 @@ final class ImportHashDatabaseDialog extends javax.swing.JDialog {
String errorMessage = Bundle.ImportHashDatabaseDialog_errorMessage_failedToOpenHashDbMsg(selectedFilePath);
// Future, make UI handle more than the "FILES" type.
try {
EamArtifact.Type contentType = EamArtifact.getDefaultCorrelationTypes().get(0); // get "FILES" type
EamDb dbManager = EamDb.getInstance();
EamArtifact.Type contentType = dbManager.getCorrelationTypeById(EamArtifact.FILES_TYPE_ID); // get "FILES" type
// run in the background and close dialog
SwingUtilities.invokeLater(new ImportHashDatabaseWorker(selectedFilePath, knownStatus, globalSetID, contentType)::execute);
dispose();
@ -629,6 +631,7 @@ final class ImportHashDatabaseDialog extends javax.swing.JDialog {
}
int numLines = 0;
LOGGER.log(Level.INFO, "Importing hash database {0}", file.getName());
while ((line = reader.readLine()) != null) {
progress.progress(++numLines);
@ -646,9 +649,16 @@ final class ImportHashDatabaseDialog extends javax.swing.JDialog {
"");
globalInstances.add(eamGlobalFileInstance);
if(numLines % HASH_IMPORT_THRESHOLD == 0){
dbManager.bulkInsertReferenceTypeEntries(globalInstances, contentType);
globalInstances.clear();
}
}
dbManager.bulkInsertReferenceTypeEntries(globalInstances, contentType);
LOGGER.log(Level.INFO, "Finished importing hash database. Total entries: {0}", numLines);
}
}

View File

@ -35,6 +35,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.List;
import static java.util.Objects.nonNull;
@ -153,7 +154,7 @@ public class ImageUtils {
SUPPORTED_IMAGE_MIME_TYPES.removeIf("application/octet-stream"::equals); //NON-NLS
//Clear the file map when the case changes, so we don't accidentaly get images from the old case.
Case.addEventSubscriber(Case.Events.CURRENT_CASE.toString(), evt -> cacheFileMap.clear());
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), evt -> cacheFileMap.clear());
}
/**

View File

@ -21,8 +21,10 @@ package org.sleuthkit.autopsy.datamodel;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
@ -53,6 +55,9 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
@NbBundle.Messages("AbstractAbstractFileNode.addFileProperty.desc=no description")
private static final String NO_DESCR = AbstractAbstractFileNode_addFileProperty_desc();
private static final Set<Case.Events> CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.CURRENT_CASE,
Case.Events.CONTENT_TAG_ADDED, Case.Events.CONTENT_TAG_DELETED);
/**
* @param abstractFile file to wrap
*/
@ -67,13 +72,14 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
IngestManager.getInstance().addIngestModuleEventListener(pcl);
}
}
// Listen for case events so that we can detect when case is closed
Case.addPropertyChangeListener(pcl);
// Listen for case events so that we can detect when the case is closed
// or when tags are added.
Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl);
}
private void removeListeners() {
IngestManager.getInstance().removeIngestModuleEventListener(pcl);
Case.removePropertyChangeListener(pcl);
Case.removeEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl);
}
private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> {
@ -95,7 +101,11 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
// If so, refresh our children.
try {
Children parentsChildren = getParentNode().getChildren();
if (parentsChildren != null) {
// We only want to refresh our parents children if we are in the
// data sources branch of the tree. The parent nodes in other
// branches of the tree (e.g. File Types and Deleted Files) do
// not need to be refreshed.
if (parentsChildren instanceof ContentChildren) {
((ContentChildren) parentsChildren).refreshChildren();
parentsChildren.getNodesCount();
}

View File

@ -18,14 +18,20 @@
*/
package org.sleuthkit.autopsy.datamodel;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.logging.Level;
import org.openide.util.lookup.Lookups;
import org.openide.util.Lookup;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.datamodel.TskException;
/**
@ -84,6 +90,47 @@ public abstract class AbstractContentNode<T extends Content> extends ContentNode
return super.getName();
}
/**
* Return true if the underlying content object has children Useful for lazy
* loading.
*
* @return true if has children
*/
public boolean hasVisibleContentChildren() {
return contentHasVisibleContentChildren(content);
}
/**
* Return true if the given content object has children. Useful for lazy
* loading.
*
* @param c The content object to look for children on
* @return true if has children
*/
public static boolean contentHasVisibleContentChildren(Content c){
if (c != null) {
String query = "SELECT COUNT(obj_id) AS count FROM "
+ " ( SELECT obj_id FROM tsk_objects WHERE par_obj_id = " + c.getId() + " AND type = "
+ TskData.ObjectType.ARTIFACT.getObjectType()
+ " INTERSECT SELECT artifact_obj_id FROM blackboard_artifacts WHERE obj_id = " + c.getId()
+ " AND (artifact_type_id = " + ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()
+ " OR artifact_type_id = " + ARTIFACT_TYPE.TSK_MESSAGE.getTypeID() + ") "
+ " UNION SELECT obj_id FROM tsk_objects WHERE par_obj_id = " + c.getId()
+ " AND type = " + TskData.ObjectType.ABSTRACTFILE.getObjectType() + ")"; //NON-NLS;
try (SleuthkitCase.CaseDbQuery dbQuery = Case.getCurrentCase().getSleuthkitCase().executeQuery(query)) {
ResultSet resultSet = dbQuery.getResultSet();
if(resultSet.next()){
return (0 < resultSet.getInt("count"));
}
} catch (TskCoreException | SQLException ex) {
logger.log(Level.SEVERE, "Error checking if the node has children, for content: " + c, ex); //NON-NLS
}
}
return false;
}
/**
* Return true if the underlying content object has children Useful for lazy
* loading.
@ -103,7 +150,7 @@ public abstract class AbstractContentNode<T extends Content> extends ContentNode
return hasChildren;
}
/**
* Return ids of children of the underlying content. The ids can be treated
* as keys - useful for lazy loading.

View File

@ -25,31 +25,29 @@ import java.beans.PropertyChangeListener;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.EnumSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.stream.Collectors;
import javax.swing.Action;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.nodes.Sheet;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.openide.util.WeakListeners;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagAddedEvent;
import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagDeletedEvent;
import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent;
import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent;
import static org.sleuthkit.autopsy.datamodel.DataModelActionsFactory.VIEW_IN_NEW_WINDOW;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import static org.sleuthkit.autopsy.datamodel.DisplayableItemNode.findLinked;
import org.sleuthkit.autopsy.directorytree.NewWindowViewAction;
import org.sleuthkit.autopsy.timeline.actions.ViewArtifactInTimelineAction;
import org.sleuthkit.autopsy.timeline.actions.ViewFileInTimelineAction;
import org.sleuthkit.datamodel.AbstractFile;
@ -68,6 +66,11 @@ import org.sleuthkit.datamodel.TskCoreException;
public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifact> {
private static final Logger LOGGER = Logger.getLogger(BlackboardArtifactNode.class.getName());
private static final Set<Case.Events> CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED,
Case.Events.BLACKBOARD_ARTIFACT_TAG_DELETED,
Case.Events.CONTENT_TAG_ADDED,
Case.Events.CONTENT_TAG_DELETED,
Case.Events.CURRENT_CASE);
private static Cache<Long, Content> contentCache = CacheBuilder.newBuilder()
.expireAfterWrite(1, TimeUnit.MINUTES).
@ -76,7 +79,7 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
private final BlackboardArtifact artifact;
private Content associated = null;
private List<NodeProperty<? extends Object>> customProperties;
/*
* Artifact types which should have the full unique path of the associated
* content as a property.
@ -128,8 +131,9 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
};
/**
* Construct blackboard artifact node from an artifact and using provided
* icon
* Construct blackboard artifact node from an artifact, overriding the
* standard icon with the one at the path provided.
*
*
* @param artifact artifact to encapsulate
* @param iconPath icon to use for the artifact
@ -138,19 +142,19 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
super(artifact, createLookup(artifact));
this.artifact = artifact;
// Look for associated Content i.e. the source file for the artifact
for (Content content : this.getLookup().lookupAll(Content.class)) {
if ( (content != null) && (!(content instanceof BlackboardArtifact)) ){
this.associated = content;
for (Content lookupContent : this.getLookup().lookupAll(Content.class)) {
if ((lookupContent != null) && (!(lookupContent instanceof BlackboardArtifact))) {
this.associated = lookupContent;
break;
}
}
this.setName(Long.toString(artifact.getArtifactID()));
this.setDisplayName();
this.setIconBaseWithExtension(iconPath);
Case.addPropertyChangeListener(pcl);
Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, WeakListeners.propertyChange(pcl, null));
}
/**
@ -160,18 +164,17 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
* @param artifact artifact to encapsulate
*/
public BlackboardArtifactNode(BlackboardArtifact artifact) {
this(artifact, ExtractedContent.getIconFilePath(artifact.getArtifactTypeID()));
}
private void removeListeners() {
Case.removePropertyChangeListener(pcl);
Case.removeEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl);
}
public BlackboardArtifact getArtifact() {
return this.artifact;
}
@Override
@NbBundle.Messages({
"BlackboardArtifactNode.getAction.errorTitle=Error getting actions",
@ -221,7 +224,7 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
*/
private void setDisplayName() {
String displayName = ""; //NON-NLS
// If this is a node for a keyword hit on an artifact, we set the
// display name to be the artifact type name followed by " Artifact"
// e.g. "Messages Artifact".
@ -245,30 +248,29 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
// Do nothing since the display name will be set to the file name.
}
}
if (displayName.isEmpty() && artifact != null) {
displayName = artifact.getName();
}
this.setDisplayName(displayName);
}
/**
* Return the name of the associated source file/content
*
* Return the name of the associated source file/content
*
* @return source file/content name
*/
public String getSrcName() {
String srcName = "";
if (associated != null) {
srcName = associated.getName();
srcName = associated.getName();
}
return srcName;
}
@NbBundle.Messages({
"BlackboardArtifactNode.createSheet.artifactType.displayName=Artifact Type",
"BlackboardArtifactNode.createSheet.artifactType.name=Artifact Type",
@ -476,11 +478,9 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|| attributeTypeID == ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT.getTypeID()
|| attributeTypeID == ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()
|| attributeTypeID == ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE.getTypeID()) {
}
else if (artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()) {
addEmailMsgProperty (map, attribute);
}
else if (attribute.getAttributeType().getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME) {
} else if (artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()) {
addEmailMsgProperty(map, attribute);
} else if (attribute.getAttributeType().getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME) {
map.put(attribute.getAttributeType().getDisplayName(), ContentUtils.getStringTime(attribute.getValueLong(), associated));
} else if (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_TOOL_OUTPUT.getTypeID()
&& attributeTypeID == ATTRIBUTE_TYPE.TSK_TEXT.getTypeID()) {
@ -496,8 +496,7 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
value = value.substring(0, 512);
}
map.put(attribute.getAttributeType().getDisplayName(), value);
}
else {
} else {
map.put(attribute.getAttributeType().getDisplayName(), attribute.getDisplayString());
}
}
@ -506,14 +505,14 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
}
}
/**
/**
* Fill map with EmailMsg properties, not all attributes are filled
*
* @param map map with preserved ordering, where property names/values
* are put
* @param attribute attribute to check/fill as property
*/
private void addEmailMsgProperty(Map<String, Object> map, BlackboardAttribute attribute ) {
private void addEmailMsgProperty(Map<String, Object> map, BlackboardAttribute attribute) {
final int attributeTypeID = attribute.getAttributeType().getTypeID();
@ -523,23 +522,19 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|| attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_RTF.getTypeID()
|| attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_BCC.getTypeID()
|| attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CC.getTypeID()
|| attributeTypeID == ATTRIBUTE_TYPE.TSK_HEADERS.getTypeID()
) {
|| attributeTypeID == ATTRIBUTE_TYPE.TSK_HEADERS.getTypeID()) {
// do nothing
}
else if (attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_PLAIN.getTypeID()) {
} else if (attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_PLAIN.getTypeID()) {
String value = attribute.getDisplayString();
if (value.length() > 160) {
value = value.substring(0, 160) + "...";
}
map.put(attribute.getAttributeType().getDisplayName(), value);
}
else if (attribute.getAttributeType().getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME) {
} else if (attribute.getAttributeType().getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME) {
map.put(attribute.getAttributeType().getDisplayName(), ContentUtils.getStringTime(attribute.getValueLong(), associated));
}
else {
} else {
map.put(attribute.getAttributeType().getDisplayName(), attribute.getDisplayString());
}
@ -586,6 +581,6 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
@Override
public <T> T accept(ContentNodeVisitor<T> v) {
return v.visit(this);
return v.visit(this);
}
}

View File

@ -22,6 +22,7 @@ import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.logging.Level;
import org.openide.nodes.Sheet;
@ -88,13 +89,13 @@ public class DataSourcesNode extends DisplayableItemNode {
@Override
protected void addNotify() {
Case.addPropertyChangeListener(pcl);
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED), pcl);
reloadKeys();
}
@Override
protected void removeNotify() {
Case.removePropertyChangeListener(pcl);
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED), pcl);
currentKeys.clear();
setKeys(Collections.<Content>emptySet());
}

View File

@ -22,9 +22,11 @@ import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;
import java.util.logging.Level;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
@ -173,68 +175,69 @@ public class DeletedContent implements AutopsyVisitableItem {
* Listens for case and ingest invest. Updates observers when events are
* fired. Other nodes are listening to this for changes.
*/
private final class DeletedContentsChildrenObservable extends Observable {
private static final class DeletedContentsChildrenObservable extends Observable {
private static final Set<Case.Events> CASE_EVENTS_OF_INTEREST = EnumSet.of(
Case.Events.DATA_SOURCE_ADDED,
Case.Events.CURRENT_CASE
);
DeletedContentsChildrenObservable() {
IngestManager.getInstance().addIngestJobEventListener(pcl);
IngestManager.getInstance().addIngestModuleEventListener(pcl);
Case.addPropertyChangeListener(pcl);
Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl);
}
private void removeListeners() {
deleteObservers();
IngestManager.getInstance().removeIngestJobEventListener(pcl);
IngestManager.getInstance().removeIngestModuleEventListener(pcl);
Case.removePropertyChangeListener(pcl);
Case.removeEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl);
}
private final PropertyChangeListener pcl = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
String eventType = evt.getPropertyName();
if (eventType.equals(IngestManager.IngestModuleEvent.CONTENT_CHANGED.toString())) {
private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> {
String eventType = evt.getPropertyName();
if (eventType.equals(IngestManager.IngestModuleEvent.CONTENT_CHANGED.toString())) {
/**
* + // @@@ COULD CHECK If the new file is deleted
* before notifying... Checking for a current case is a
* stop gap measure + update(); 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();
// new file was added
// @@@ COULD CHECK If the new file is deleted before notifying...
update();
} catch (IllegalStateException notUsed) {
/**
* + // @@@ COULD CHECK If the new file is deleted
* before notifying... Checking for a current case is a
* stop gap measure + update(); 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.
* Case is closed, do nothing.
*/
try {
Case.getCurrentCase();
// new file was added
// @@@ COULD CHECK If the new file is deleted before notifying...
update();
} catch (IllegalStateException notUsed) {
/**
* Case is closed, do nothing.
*/
}
} else if (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();
}
maxFilesDialogShown = false;
}
} else if (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();
}
maxFilesDialogShown = false;
}
};

View File

@ -23,6 +23,7 @@ import java.beans.PropertyChangeListener;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
@ -285,7 +286,7 @@ public class EmailExtracted implements AutopsyVisitableItem {
protected void addNotify() {
IngestManager.getInstance().addIngestJobEventListener(pcl);
IngestManager.getInstance().addIngestModuleEventListener(pcl);
Case.addPropertyChangeListener(pcl);
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
emailResults.update();
emailResults.addObserver(this);
}
@ -294,7 +295,7 @@ public class EmailExtracted implements AutopsyVisitableItem {
protected void removeNotify() {
IngestManager.getInstance().removeIngestJobEventListener(pcl);
IngestManager.getInstance().removeIngestModuleEventListener(pcl);
Case.removePropertyChangeListener(pcl);
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
emailResults.deleteObserver(this);
}

View File

@ -23,6 +23,7 @@ import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.logging.Level;
@ -251,14 +252,14 @@ public class ExtractedContent implements AutopsyVisitableItem {
protected void addNotify() {
IngestManager.getInstance().addIngestJobEventListener(pcl);
IngestManager.getInstance().addIngestModuleEventListener(pcl);
Case.addPropertyChangeListener(pcl);
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
}
@Override
protected void removeNotify() {
IngestManager.getInstance().removeIngestJobEventListener(pcl);
IngestManager.getInstance().removeIngestModuleEventListener(pcl);
Case.removePropertyChangeListener(pcl);
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
typeNodeList.clear();
}
@ -309,7 +310,7 @@ public class ExtractedContent implements AutopsyVisitableItem {
*/
public class TypeNode extends DisplayableItemNode {
private BlackboardArtifact.Type type;
private final BlackboardArtifact.Type type;
private long childCount = 0;
TypeNode(BlackboardArtifact.Type type) {

View File

@ -22,9 +22,11 @@ import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;
import java.util.logging.Level;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.ChildFactory;
@ -169,65 +171,64 @@ public class FileSize implements AutopsyVisitableItem {
* Listens for case and ingest invest. Updates observers when events are
* fired. Size-based nodes are listening to this for changes.
*/
private final class FileSizeRootChildrenObservable extends Observable {
private static final class FileSizeRootChildrenObservable extends Observable {
private static final Set<Case.Events> CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.DATA_SOURCE_ADDED, Case.Events.CURRENT_CASE);
FileSizeRootChildrenObservable() {
IngestManager.getInstance().addIngestJobEventListener(pcl);
IngestManager.getInstance().addIngestModuleEventListener(pcl);
Case.addPropertyChangeListener(pcl);
Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl);
}
private void removeListeners() {
deleteObservers();
IngestManager.getInstance().removeIngestJobEventListener(pcl);
IngestManager.getInstance().removeIngestModuleEventListener(pcl);
Case.removePropertyChangeListener(pcl);
Case.removeEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl);
}
private final PropertyChangeListener pcl = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
String eventType = evt.getPropertyName();
private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> {
String eventType = evt.getPropertyName();
if (eventType.equals(IngestManager.IngestModuleEvent.CONTENT_CHANGED.toString())) {
if (eventType.equals(IngestManager.IngestModuleEvent.CONTENT_CHANGED.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 {
// new file was added
// @@@ could check the size here and only fire off updates if we know the file meets the min size criteria
Case.getCurrentCase();
update();
} catch (IllegalStateException notUsed) {
/**
* 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.
* Case is closed, do nothing.
*/
try {
// new file was added
// @@@ could check the size here and only fire off updates if we know the file meets the min size criteria
Case.getCurrentCase();
update();
} catch (IllegalStateException notUsed) {
/**
* Case is closed, do nothing.
*/
}
} else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
|| eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())
|| eventType.equals(Case.Events.DATA_SOURCE_ADDED.toString())) {
}
} else if (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) {
/**
* 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.
* Case is closed, do nothing.
*/
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();
}
}
} 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();
}
}
};

View File

@ -21,10 +21,11 @@ package org.sleuthkit.autopsy.datamodel;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import java.util.function.Function;
import java.util.Set;
import java.util.logging.Level;
import java.util.stream.Collectors;
import org.apache.commons.lang.StringUtils;
@ -39,8 +40,6 @@ import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.FileTypes.FileTypesKey;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
@ -75,9 +74,11 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
private class FileTypesByExtObservable extends Observable {
private final PropertyChangeListener pcl;
private final Set<Case.Events> CASE_EVENTS_OF_INTEREST;
private FileTypesByExtObservable() {
super();
this.CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.DATA_SOURCE_ADDED, Case.Events.CURRENT_CASE);
this.pcl = (PropertyChangeEvent evt) -> {
String eventType = evt.getPropertyName();
if (eventType.equals(IngestManager.IngestModuleEvent.CONTENT_CHANGED.toString())
@ -109,15 +110,14 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
IngestManager.getInstance().addIngestJobEventListener(pcl);
IngestManager.getInstance().addIngestModuleEventListener(pcl);
Case.addPropertyChangeListener(pcl);
Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl);
}
private void removeListeners() {
deleteObservers();
IngestManager.getInstance().removeIngestJobEventListener(pcl);
IngestManager.getInstance().removeIngestModuleEventListener(pcl);
Case.removePropertyChangeListener(pcl);
Case.removeEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl);
}
private void update() {

View File

@ -24,11 +24,13 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;
import java.util.logging.Level;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
@ -44,7 +46,6 @@ import static org.sleuthkit.autopsy.core.UserPreferences.hideSlackFilesInViewsTr
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.FileTypes.FileTypesKey;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
@ -79,6 +80,8 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
*/
private final PropertyChangeListener pcl;
private static final Set<Case.Events> CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.DATA_SOURCE_ADDED, Case.Events.CURRENT_CASE);
/**
* Create the base expression used as the where clause in the queries for
* files by mime type. Filters out certain kinds of files and directories,
@ -102,7 +105,7 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
private void removeListeners() {
deleteObservers();
IngestManager.getInstance().removeIngestJobEventListener(pcl);
Case.removePropertyChangeListener(pcl);
Case.removeEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl);
}
/**
@ -175,7 +178,7 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
}
};
IngestManager.getInstance().addIngestJobEventListener(pcl);
Case.addPropertyChangeListener(pcl);
Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl);
populateHashMap();
}

View File

@ -24,6 +24,7 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
@ -252,7 +253,7 @@ public class HashsetHits implements AutopsyVisitableItem {
protected void addNotify() {
IngestManager.getInstance().addIngestJobEventListener(pcl);
IngestManager.getInstance().addIngestModuleEventListener(pcl);
Case.addPropertyChangeListener(pcl);
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
hashsetResults.update();
hashsetResults.addObserver(this);
}
@ -261,7 +262,7 @@ public class HashsetHits implements AutopsyVisitableItem {
protected void removeNotify() {
IngestManager.getInstance().removeIngestJobEventListener(pcl);
IngestManager.getInstance().removeIngestModuleEventListener(pcl);
Case.removePropertyChangeListener(pcl);
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
hashsetResults.deleteObserver(this);
}

View File

@ -24,6 +24,7 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.logging.Level;
import javax.swing.Action;
@ -79,12 +80,12 @@ public class ImageNode extends AbstractContentNode<Image> {
// Listen for ingest events so that we can detect new added files (e.g. carved)
IngestManager.getInstance().addIngestModuleEventListener(pcl);
// Listen for case events so that we can detect when case is closed
Case.addPropertyChangeListener(pcl);
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
}
private void removeListeners() {
IngestManager.getInstance().removeIngestModuleEventListener(pcl);
Case.removePropertyChangeListener(pcl);
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
}
/**

View File

@ -24,6 +24,7 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
@ -188,57 +189,54 @@ public class InterestingHits implements AutopsyVisitableItem {
* nice methods for its startup and shutdown, so it seemed like a
* cleaner place to register the property change listener.
*/
private final PropertyChangeListener pcl = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
String eventType = evt.getPropertyName();
if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> {
String eventType = evt.getPropertyName();
if (eventType.equals(IngestManager.IngestModuleEvent.DATA_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();
/**
* 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.
* Even with the check above, it is still possible that
* the case will be closed in a different thread before
* this code executes. If that happens, it is possible
* for the event to have a null oldValue.
*/
try {
Case.getCurrentCase();
/**
* Even with the check above, it is still possible that
* the case will be closed in a different thread before
* this code executes. If that happens, it is possible
* for the event to have a null oldValue.
*/
ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
if (null != eventData && (eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()
|| eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID())) {
interestingResults.update();
}
} catch (IllegalStateException notUsed) {
/**
* Case is closed, do nothing.
*/
}
} else 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();
ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
if (null != eventData && (eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()
|| eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID())) {
interestingResults.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) {
removeNotify();
skCase = null;
}
} catch (IllegalStateException notUsed) {
/**
* Case is closed, do nothing.
*/
}
} else 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();
interestingResults.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) {
removeNotify();
skCase = null;
}
}
};
@ -247,7 +245,7 @@ public class InterestingHits implements AutopsyVisitableItem {
protected void addNotify() {
IngestManager.getInstance().addIngestJobEventListener(pcl);
IngestManager.getInstance().addIngestModuleEventListener(pcl);
Case.addPropertyChangeListener(pcl);
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
interestingResults.update();
interestingResults.addObserver(this);
}
@ -256,7 +254,7 @@ public class InterestingHits implements AutopsyVisitableItem {
protected void removeNotify() {
IngestManager.getInstance().removeIngestJobEventListener(pcl);
IngestManager.getInstance().removeIngestModuleEventListener(pcl);
Case.removePropertyChangeListener(pcl);
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
interestingResults.deleteObserver(this);
}

View File

@ -24,6 +24,7 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
@ -455,7 +456,7 @@ public class KeywordHits implements AutopsyVisitableItem {
protected void addNotify() {
IngestManager.getInstance().addIngestJobEventListener(pcl);
IngestManager.getInstance().addIngestModuleEventListener(pcl);
Case.addPropertyChangeListener(pcl);
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
keywordResults.update();
super.addNotify();
}
@ -464,7 +465,7 @@ public class KeywordHits implements AutopsyVisitableItem {
protected void removeNotify() {
IngestManager.getInstance().removeIngestJobEventListener(pcl);
IngestManager.getInstance().removeIngestModuleEventListener(pcl);
Case.removePropertyChangeListener(pcl);
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
super.removeNotify();
}

View File

@ -28,7 +28,9 @@ import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import javax.swing.AbstractAction;
import javax.swing.Action;
@ -103,8 +105,10 @@ public final class Reports implements AutopsyVisitableItem {
*/
private static final class ReportNodeFactory extends ChildFactory<Report> {
private static final Set<Case.Events> CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.REPORT_ADDED, Case.Events.REPORT_DELETED);
ReportNodeFactory() {
Case.addPropertyChangeListener((PropertyChangeEvent evt) -> {
Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, (PropertyChangeEvent evt) -> {
String eventType = evt.getPropertyName();
if (eventType.equals(Case.Events.REPORT_ADDED.toString()) || eventType.equals(Case.Events.REPORT_DELETED.toString())) {
/**

View File

@ -21,9 +21,11 @@ package org.sleuthkit.autopsy.datamodel;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;
import java.util.logging.Level;
import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children;
@ -118,6 +120,12 @@ public class Tags implements AutopsyVisitableItem {
private class TagNameNodeFactory extends ChildFactory.Detachable<TagName> implements Observer {
private final Set<Case.Events> CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED,
Case.Events.BLACKBOARD_ARTIFACT_TAG_DELETED,
Case.Events.CONTENT_TAG_ADDED,
Case.Events.CONTENT_TAG_DELETED,
Case.Events.CURRENT_CASE);
private final PropertyChangeListener pcl = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
@ -171,7 +179,7 @@ public class Tags implements AutopsyVisitableItem {
protected void addNotify() {
IngestManager.getInstance().addIngestJobEventListener(pcl);
IngestManager.getInstance().addIngestModuleEventListener(pcl);
Case.addPropertyChangeListener(pcl);
Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl);
tagResults.update();
tagResults.addObserver(this);
}
@ -180,7 +188,7 @@ public class Tags implements AutopsyVisitableItem {
protected void removeNotify() {
IngestManager.getInstance().removeIngestJobEventListener(pcl);
IngestManager.getInstance().removeIngestModuleEventListener(pcl);
Case.removePropertyChangeListener(pcl);
Case.removeEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl);
tagResults.deleteObserver(this);
}

View File

@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.datamodel;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import javax.swing.Action;
import org.openide.nodes.Children;
@ -72,12 +73,12 @@ public class VolumeNode extends AbstractContentNode<Volume> {
// Listen for ingest events so that we can detect new added files (e.g. carved)
IngestManager.getInstance().addIngestModuleEventListener(pcl);
// Listen for case events so that we can detect when case is closed
Case.addPropertyChangeListener(pcl);
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
}
private void removeListeners() {
IngestManager.getInstance().removeIngestModuleEventListener(pcl);
Case.removePropertyChangeListener(pcl);
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
}
/*

View File

@ -32,6 +32,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
@ -311,7 +312,7 @@ final public class Accounts implements AutopsyVisitableItem {
protected void removeNotify() {
IngestManager.getInstance().removeIngestJobEventListener(pcl);
IngestManager.getInstance().removeIngestModuleEventListener(pcl);
Case.removePropertyChangeListener(pcl);
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
super.removeNotify();
}
@ -319,7 +320,7 @@ final public class Accounts implements AutopsyVisitableItem {
protected void addNotify() {
IngestManager.getInstance().addIngestJobEventListener(pcl);
IngestManager.getInstance().addIngestModuleEventListener(pcl);
Case.addPropertyChangeListener(pcl);
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
super.addNotify();
refreshKeys();
}

View File

@ -24,6 +24,7 @@ import java.io.File;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor;
@ -129,8 +130,10 @@ final class AddRawImageTask implements Runnable {
File imageFile = Paths.get(imageFilePath).toFile();
if (!imageFile.exists()) {
errorMessages.add(Bundle.AddRawImageTask_image_critical_error_adding() + imageFilePath + Bundle.AddRawImageTask_for_device()
+ deviceId + Bundle.AddRawImageTask_image_notExisting());
String errorMessage = Bundle.AddRawImageTask_image_critical_error_adding() + imageFilePath + Bundle.AddRawImageTask_for_device()
+ deviceId + Bundle.AddRawImageTask_image_notExisting();
errorMessages.add(errorMessage);
logger.log(Level.SEVERE, errorMessage);
criticalErrorOccurred = true;
return;
}
@ -173,7 +176,9 @@ final class AddRawImageTask implements Runnable {
caseDatabase.addLayoutFiles(dataSource, fileRanges);
} catch (TskCoreException ex) {
errorMessages.add(Bundle.AddRawImageTask_image_critical_error_adding() + imageFilePaths + Bundle.AddRawImageTask_for_device() + deviceId + ":" + ex.getLocalizedMessage());
String errorMessage = Bundle.AddRawImageTask_image_critical_error_adding() + imageFilePaths + Bundle.AddRawImageTask_for_device() + deviceId + ":" + ex.getLocalizedMessage();
errorMessages.add(errorMessage);
logger.log(Level.SEVERE, errorMessage, ex);
criticalErrorOccurred = true;
} finally {
caseDatabase.releaseExclusiveLock();

View File

@ -26,6 +26,7 @@ import org.sleuthkit.autopsy.datamodel.DirectoryNode;
import org.openide.nodes.FilterNode;
import org.openide.nodes.Node;
import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode;
import org.sleuthkit.autopsy.datamodel.AbstractContentNode;
import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode;
import org.sleuthkit.autopsy.datamodel.DisplayableItemNode;
import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor;
@ -42,7 +43,6 @@ import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.Directory;
import org.sleuthkit.datamodel.LayoutFile;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskException;
import org.sleuthkit.datamodel.VirtualDirectory;
import org.sleuthkit.datamodel.Volume;
@ -120,7 +120,7 @@ class DirectoryTreeFilterChildren extends FilterNode.Children {
&& !((Directory) c).getName().equals(".."))) {
ret = false;
break;
} else if (c.hasChildren()) {
} else if(AbstractContentNode.contentHasVisibleContentChildren(c)){
//fie has children, such as derived files
ret = false;
break;
@ -204,12 +204,8 @@ class DirectoryTreeFilterChildren extends FilterNode.Children {
if ((childContent instanceof AbstractFile) && ((AbstractFile) childContent).isDir()) {
return false;
} else {
try {
if (childContent.hasChildren()) {
return false;
}
} catch (TskCoreException e) {
logger.log(Level.SEVERE, "Error checking if file node is leaf.", e); //NON-NLS
if(AbstractContentNode.contentHasVisibleContentChildren(childContent)){
return false;
}
}
}
@ -244,7 +240,6 @@ class DirectoryTreeFilterChildren extends FilterNode.Children {
@Override
public Boolean visit(VirtualDirectoryNode vdn) {
return visitDeep(vdn);
//return ! vdn.hasContentChildren();
}
@Override
@ -286,7 +281,7 @@ class DirectoryTreeFilterChildren extends FilterNode.Children {
@Override
public Boolean visit(FileNode fn) {
return fn.hasContentChildren();
return fn.hasVisibleContentChildren();
}
@Override

View File

@ -25,7 +25,6 @@ import java.util.logging.Level;
import javax.swing.Action;
import org.openide.nodes.FilterNode;
import org.openide.nodes.Node;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle;
import org.openide.util.lookup.Lookups;
import org.openide.util.lookup.ProxyLookup;
@ -36,6 +35,7 @@ import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode;
import org.sleuthkit.autopsy.ingest.runIngestModuleWizard.RunIngestModulesAction;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.Directory;
import org.sleuthkit.datamodel.Image;
@ -120,7 +120,7 @@ class DirectoryTreeFilterNode extends FilterNode {
private int getVisibleChildCount(AbstractFile file) throws TskCoreException {
List<Content> childList = file.getChildren();
int numVisibleChildren = file.getChildrenCount();
int numVisibleChildren = childList.size();
boolean purgeKnownFiles = UserPreferences.hideKnownFilesInDataSourcesTree();
boolean purgeSlackFiles = UserPreferences.hideSlackFilesInDataSourcesTree();
@ -134,6 +134,14 @@ class DirectoryTreeFilterNode extends FilterNode {
|| (purgeSlackFiles && childFile.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.SLACK)) {
numVisibleChildren--;
}
} else if(child instanceof BlackboardArtifact){
BlackboardArtifact bba = (BlackboardArtifact) child;
// Only message type artifacts are displayed in the tree
if((bba.getArtifactTypeID() != ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID())
&& (bba.getArtifactTypeID() != ARTIFACT_TYPE.TSK_MESSAGE.getTypeID())){
numVisibleChildren--;
}
}
}
}

View File

@ -26,6 +26,7 @@ import java.beans.PropertyVetoException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
@ -151,7 +152,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
}
}
});
Case.addEventSubscriber(new HashSet<>(Arrays.asList(Case.Events.CURRENT_CASE.toString(), Case.Events.DATA_SOURCE_ADDED.toString())), this);
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE, Case.Events.DATA_SOURCE_ADDED), this);
this.em.addPropertyChangeListener(this);
IngestManager.getInstance().addIngestJobEventListener(this);
IngestManager.getInstance().addIngestModuleEventListener(this);

View File

@ -56,3 +56,5 @@ SizeSearchPanel.sizeCompareComboBox.lessThan=less than
MimeTypePanel.jLabel1.text=*Note: Multiple MIME types can be selected
FileSearchPanel.searchButton.text=Search
MimeTypePanel.mimeTypeCheckBox.text=MIME Type:
HashSearchPanel.md5CheckBox.text=MD5:
HashSearchPanel.emptyHashMsg.text=Must enter something for hash search.

View File

@ -28,6 +28,7 @@ import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
@ -53,6 +54,9 @@ class DateSearchFilter extends AbstractFileSearchFilter<DateSearchPanel> {
private static final DateFormat DATE_FORMAT = new SimpleDateFormat("MM/dd/yyyy");
private static final String SEPARATOR = "SEPARATOR"; //NON-NLS
private static final Set<Case.Events> CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.CURRENT_CASE,
Case.Events.DATA_SOURCE_ADDED, Case.Events.DATA_SOURCE_DELETED);
/**
* New DateSearchFilter with the default panel
*/
@ -62,7 +66,7 @@ class DateSearchFilter extends AbstractFileSearchFilter<DateSearchPanel> {
private DateSearchFilter(DateSearchPanel panel) {
super(panel);
Case.addPropertyChangeListener(this.new CasePropertyChangeListener());
Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, this.new CasePropertyChangeListener());
}
@Override

View File

@ -20,7 +20,7 @@ package org.sleuthkit.autopsy.filesearch;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.EnumSet;
import org.openide.util.HelpCtx;
import org.openide.util.NbBundle;
import org.openide.util.actions.CallableSystemAction;
@ -35,14 +35,10 @@ final class FileSearchAction extends CallableSystemAction implements FileSearchP
FileSearchAction() {
super();
setEnabled(Case.isCaseOpen());
Case.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals(Case.Events.CURRENT_CASE.toString())) {
setEnabled(evt.getNewValue() != null);
}
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), (PropertyChangeEvent evt) -> {
if (evt.getPropertyName().equals(Case.Events.CURRENT_CASE.toString())) {
setEnabled(evt.getNewValue() != null);
}
});
}

View File

@ -94,6 +94,8 @@ class FileSearchPanel extends javax.swing.JPanel {
this.filterAreas.add(new FilterArea(NbBundle.getMessage(this.getClass(), "FileSearchPanel.filterTitle.metadata"), metadataFilters));
this.filterAreas.add(new FilterArea(NbBundle.getMessage(this.getClass(), "FileSearchPanel.filterTitle.knownStatus"), new KnownStatusSearchFilter()));
this.filterAreas.add(new FilterArea(NbBundle.getMessage(this.getClass(), "HashSearchPanel.md5CheckBox.text"), new HashSearchFilter()));
for (FilterArea fa : this.filterAreas) {
fa.setMaximumSize(new Dimension(Integer.MAX_VALUE, fa.getMinimumSize().height));

View File

@ -0,0 +1,66 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2017 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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.filesearch;
import java.awt.event.ActionListener;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.filesearch.FileSearchFilter.FilterValidationException;
/**
*
*/
class HashSearchFilter extends AbstractFileSearchFilter<HashSearchPanel> {
private static final String EMPTY_HASH_MESSAGE = NbBundle
.getMessage(HashSearchFilter.class, "HashSearchPanel.emptyHashMsg.text");
public HashSearchFilter() {
this(new HashSearchPanel());
}
public HashSearchFilter(HashSearchPanel component) {
super(component);
}
@Override
public boolean isEnabled() {
return this.getComponent().getHashCheckBox().isSelected();
}
@Override
public String getPredicate() throws FilterValidationException {
String md5Hash = this.getComponent().getSearchTextField().getText();
if (md5Hash.isEmpty()) {
throw new FilterValidationException(EMPTY_HASH_MESSAGE);
}
return "md5 = '" + md5Hash.toLowerCase() + "'"; //NON-NLS
}
@Override
public void addActionListener(ActionListener l) {
getComponent().addActionListener(l);
}
@Override
public boolean isValid() {
return !this.getComponent().getSearchTextField().getText().isEmpty();
}
}

View File

@ -0,0 +1,101 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<NonVisualComponents>
<Container class="javax.swing.JPopupMenu" name="rightClickMenu">
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout">
<Property name="useNullLayout" type="boolean" value="true"/>
</Layout>
<SubComponents>
<MenuItem class="javax.swing.JMenuItem" name="cutMenuItem">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/filesearch/Bundle.properties" key="NameSearchPanel.cutMenuItem.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</MenuItem>
<MenuItem class="javax.swing.JMenuItem" name="copyMenuItem">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/filesearch/Bundle.properties" key="NameSearchPanel.copyMenuItem.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</MenuItem>
<MenuItem class="javax.swing.JMenuItem" name="pasteMenuItem">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/filesearch/Bundle.properties" key="NameSearchPanel.pasteMenuItem.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</MenuItem>
<MenuItem class="javax.swing.JMenuItem" name="selectAllMenuItem">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/filesearch/Bundle.properties" key="NameSearchPanel.selectAllMenuItem.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</MenuItem>
</SubComponents>
</Container>
</NonVisualComponents>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
<Component id="hashCheckBox" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="searchTextField" min="-2" pref="247" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="103" groupAlignment="3" attributes="0">
<Component id="hashCheckBox" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="searchTextField" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JCheckBox" name="hashCheckBox">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
<FontInfo relative="true">
<Font bold="false" component="hashCheckBox" property="font" relativeSize="false" size="11"/>
</FontInfo>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/filesearch/Bundle.properties" key="HashSearchPanel.md5CheckBox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="hashCheckBoxActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JTextField" name="searchTextField">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
<FontInfo relative="true">
<Font bold="false" component="searchTextField" property="font" relativeSize="false" size="11"/>
</FontInfo>
</Property>
</Properties>
</Component>
</SubComponents>
</Form>

View File

@ -0,0 +1,175 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2017 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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.filesearch;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JCheckBox;
import javax.swing.JMenuItem;
import javax.swing.JTextField;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
/**
*
*/
class HashSearchPanel extends javax.swing.JPanel {
private static final long serialVersionUID = 1L;
/**
* Creates new form HashSearchPanel
*/
HashSearchPanel() {
initComponents();
customizeComponents();
setComponentsEnabled();
}
private void customizeComponents() {
searchTextField.setComponentPopupMenu(rightClickMenu);
ActionListener actList = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
JMenuItem jmi = (JMenuItem) e.getSource();
if (jmi.equals(cutMenuItem)) {
searchTextField.cut();
} else if (jmi.equals(copyMenuItem)) {
searchTextField.copy();
} else if (jmi.equals(pasteMenuItem)) {
searchTextField.paste();
} else if (jmi.equals(selectAllMenuItem)) {
searchTextField.selectAll();
}
}
};
cutMenuItem.addActionListener(actList);
copyMenuItem.addActionListener(actList);
pasteMenuItem.addActionListener(actList);
selectAllMenuItem.addActionListener(actList);
this.searchTextField.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent e) {
firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null);
}
@Override
public void removeUpdate(DocumentEvent e) {
firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null);
}
@Override
public void changedUpdate(DocumentEvent e) {
firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null);
}
});
}
JCheckBox getHashCheckBox() {
return hashCheckBox;
}
JTextField getSearchTextField() {
return searchTextField;
}
void setComponentsEnabled() {
boolean enabled = hashCheckBox.isSelected();
this.searchTextField.setEnabled(enabled);
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
rightClickMenu = new javax.swing.JPopupMenu();
cutMenuItem = new javax.swing.JMenuItem();
copyMenuItem = new javax.swing.JMenuItem();
pasteMenuItem = new javax.swing.JMenuItem();
selectAllMenuItem = new javax.swing.JMenuItem();
hashCheckBox = new javax.swing.JCheckBox();
searchTextField = new javax.swing.JTextField();
cutMenuItem.setText(org.openide.util.NbBundle.getMessage(HashSearchPanel.class, "NameSearchPanel.cutMenuItem.text")); // NOI18N
rightClickMenu.add(cutMenuItem);
copyMenuItem.setText(org.openide.util.NbBundle.getMessage(HashSearchPanel.class, "NameSearchPanel.copyMenuItem.text")); // NOI18N
rightClickMenu.add(copyMenuItem);
pasteMenuItem.setText(org.openide.util.NbBundle.getMessage(HashSearchPanel.class, "NameSearchPanel.pasteMenuItem.text")); // NOI18N
rightClickMenu.add(pasteMenuItem);
selectAllMenuItem.setText(org.openide.util.NbBundle.getMessage(HashSearchPanel.class, "NameSearchPanel.selectAllMenuItem.text")); // NOI18N
rightClickMenu.add(selectAllMenuItem);
hashCheckBox.setFont(hashCheckBox.getFont().deriveFont(hashCheckBox.getFont().getStyle() & ~java.awt.Font.BOLD, 11));
hashCheckBox.setText(org.openide.util.NbBundle.getMessage(HashSearchPanel.class, "HashSearchPanel.md5CheckBox.text")); // NOI18N
hashCheckBox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
hashCheckBoxActionPerformed(evt);
}
});
searchTextField.setFont(searchTextField.getFont().deriveFont(searchTextField.getFont().getStyle() & ~java.awt.Font.BOLD, 11));
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(0, 0, 0)
.addComponent(hashCheckBox)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(searchTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 247, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 0, 0))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(hashCheckBox)
.addComponent(searchTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
);
}// </editor-fold>//GEN-END:initComponents
private void hashCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_hashCheckBoxActionPerformed
setComponentsEnabled();
firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null);
}//GEN-LAST:event_hashCheckBoxActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JMenuItem copyMenuItem;
private javax.swing.JMenuItem cutMenuItem;
private javax.swing.JCheckBox hashCheckBox;
private javax.swing.JMenuItem pasteMenuItem;
private javax.swing.JPopupMenu rightClickMenu;
private javax.swing.JTextField searchTextField;
private javax.swing.JMenuItem selectAllMenuItem;
// End of variables declaration//GEN-END:variables
void addActionListener(ActionListener l) {
searchTextField.addActionListener(l);
}
}

View File

@ -26,6 +26,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@ -229,7 +230,7 @@ public class IngestManager {
* opened/closed) events.
*/
private void subscribeToCaseEvents() {
Case.addEventSubscriber(Case.Events.CURRENT_CASE.toString(), (PropertyChangeEvent event) -> {
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), (PropertyChangeEvent event) -> {
if (event.getNewValue() != null) {
handleCaseOpened();
} else {

View File

@ -25,6 +25,7 @@ import java.awt.Font;
import java.awt.Graphics;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.EnumSet;
import javax.swing.JButton;
import org.openide.util.NbBundle;
import org.openide.windows.Mode;
@ -128,7 +129,7 @@ class IngestMessagesToolbar extends javax.swing.JPanel {
}
});
Case.addPropertyChangeListener((PropertyChangeEvent evt) -> {
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), (PropertyChangeEvent evt) -> {
if (evt.getPropertyName().equals(Case.Events.CURRENT_CASE.toString())) {
setEnabled(evt.getNewValue() != null && RuntimeProperties.runningWithGUI());
}

View File

@ -23,6 +23,7 @@ import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.io.File;
import java.io.IOException;
import java.util.EnumSet;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.SimpleFormatter;
@ -121,7 +122,7 @@ public final class IngestMonitor {
MonitorTimerAction() {
findRootDirectoryForCurrentCase();
Case.addEventSubscriber(Case.Events.CURRENT_CASE.toString(), (PropertyChangeEvent evt) -> {
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), (PropertyChangeEvent evt) -> {
if (evt instanceof AutopsyEvent) {
AutopsyEvent event = (AutopsyEvent) evt;
if (AutopsyEvent.SourceType.LOCAL == event.getSourceType() && event.getPropertyName().equals(Case.Events.CURRENT_CASE.toString())) {

View File

@ -23,7 +23,7 @@ import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.EnumSet;
import org.openide.util.HelpCtx;
import org.openide.util.NbBundle;
import org.openide.util.actions.CallableSystemAction;
@ -43,13 +43,9 @@ class HashDbPanelSearchAction extends CallableSystemAction {
HashDbPanelSearchAction() {
super();
setEnabled(Case.isCaseOpen());
Case.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals(Case.Events.CURRENT_CASE.toString())) {
setEnabled(evt.getNewValue() != null);
}
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), (PropertyChangeEvent evt) -> {
if (evt.getPropertyName().equals(Case.Events.CURRENT_CASE.toString())) {
setEnabled(evt.getNewValue() != null);
}
});
}

View File

@ -27,6 +27,7 @@ import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.text.MessageFormat;
import java.util.EnumSet;
import java.util.Map;
import javax.swing.ImageIcon;
import javax.swing.JButton;
@ -81,7 +82,7 @@ public final class ReportWizardAction extends CallableSystemAction implements Pr
public ReportWizardAction() {
setEnabled(false);
Case.addPropertyChangeListener((PropertyChangeEvent evt) -> {
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), (PropertyChangeEvent evt) -> {
if (evt.getPropertyName().equals(Case.Events.CURRENT_CASE.toString())) {
Case newCase = (Case) evt.getNewValue();
setEnabled(newCase != null && RuntimeProperties.runningWithGUI());

View File

@ -24,7 +24,7 @@
Note there is no namespace collision with ver 3 -->
<dependency conf="autopsy_core->*" org="commons-lang" name="commons-lang" rev="2.6"/>
<dependency conf="autopsy_core->*" org="commons-logging" name="commons-logging" rev="1.1.2"/>
<dependency conf="autopsy_core->*" org="commons-io" name="commons-io" rev="2.4"/>
<dependency conf="autopsy_core->*" org="commons-io" name="commons-io" rev="2.5"/>
<dependency conf="autopsy_core->*" org="log4j" name="log4j" rev="1.2.17"/>
<!-- <dependency conf="autopsy_core->*" org="org.jdom" name="jdom" rev="1.1.3"/> -->

View File

@ -10,6 +10,7 @@ file.reference.commons-codec-1.10.jar=release/modules/ext/commons-codec-1.10.jar
file.reference.commons-collections4-4.1.jar=release/modules/ext/commons-collections4-4.1.jar
file.reference.commons-csv-1.4.jar=release/modules/ext/commons-csv-1.4.jar
file.reference.commons-io-2.4.jar=release/modules/ext/commons-io-2.4.jar
file.reference.commons-io-2.5.jar=release/modules/ext/commons-io-2.5.jar
file.reference.commons-lang-2.6.jar=release/modules/ext/commons-lang-2.6.jar
file.reference.commons-lang3-3.0-javadoc.jar=release/modules/ext/commons-lang3-3.0-javadoc.jar
file.reference.commons-lang3-3.0-sources.jar=release/modules/ext/commons-lang3-3.0-sources.jar
@ -74,6 +75,7 @@ file.reference.xmlbeans-2.6.0.jar=release/modules/ext/xmlbeans-2.6.0.jar
javac.source=1.8
javac.compilerargs=-Xlint -Xlint:-serial
javadoc.reference.commons-csv-1.4.jar=release/modules/ext/commons-csv-1.4-javadoc.jar
javadoc.reference.commons-io-2.5.jar=release/modules/ext/commons-io-2.5-javadoc.jar
javadoc.reference.compiler-0.9.1.jar=release/modules/ext/compiler-0.9.1-javadoc.jar
javadoc.reference.controlsfx-8.40.11.jar=release/modules/ext/controlsfx-8.40.11-javadoc.jar
javadoc.reference.guava-19.0.jar=release/modules/ext/guava-19.0-javadoc.jar
@ -82,6 +84,7 @@ javadoc.reference.jfxtras-controls-8.0-r4.jar=release/modules/ext/jfxtras-contro
javadoc.reference.jfxtras-fxml-8.0-r4.jar=release/modules/ext/jfxtras-fxml-8.0-r4-javadoc.jar
nbm.needs.restart=true
source.reference.commons-csv-1.4.jar=release/modules/ext/commons-csv-1.4-sources.jar
source.reference.commons-io-2.5.jar=release/modules/ext/commons-io-2.5-sources.jar
source.reference.compiler-0.9.1.jar=release/modules/ext/compiler-0.9.1-sources.jar
source.reference.controlsfx-8.40.11.jar=release/modules/ext/controlsfx-8.40.11-sources.jar
source.reference.guava-19.0.jar=release/modules/ext/guava-19.0-sources.jar

View File

@ -699,18 +699,10 @@
<runtime-relative-path>ext/sigar-1.6.4.jar</runtime-relative-path>
<binary-origin>release/modules/ext/sigar-1.6.4.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/xmlbeans-2.6.0.jar</runtime-relative-path>
<binary-origin>release/modules/ext/xmlbeans-2.6.0.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/jna-3.4.0.jar</runtime-relative-path>
<binary-origin>release/modules/ext/jna-3.4.0.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/poi-ooxml-schemas-3.15.jar</runtime-relative-path>
<binary-origin>release/modules/ext/poi-ooxml-schemas-3.15.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/gson-1.4.jar</runtime-relative-path>
<binary-origin>release/modules/ext/gson-1.4.jar</binary-origin>
@ -731,6 +723,10 @@
<runtime-relative-path>ext/imgscalr-lib-4.2.jar</runtime-relative-path>
<binary-origin>release/modules/ext/imgscalr-lib-4.2.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/xmlbeans-2.6.0.jar</runtime-relative-path>
<binary-origin>release/modules/ext/xmlbeans-2.6.0.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/common-io-3.2.jar</runtime-relative-path>
<binary-origin>release/modules/ext/common-io-3.2.jar</binary-origin>
@ -764,12 +760,12 @@
<binary-origin>release/modules/ext/joda-time-2.4-javadoc.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/jcalendarbutton-1.4.6.jar</runtime-relative-path>
<binary-origin>release/modules/ext/jcalendarbutton-1.4.6.jar</binary-origin>
<runtime-relative-path>ext/poi-excelant-3.15.jar</runtime-relative-path>
<binary-origin>release/modules/ext/poi-excelant-3.15.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/poi-ooxml-3.15.jar</runtime-relative-path>
<binary-origin>release/modules/ext/poi-ooxml-3.15.jar</binary-origin>
<runtime-relative-path>ext/jcalendarbutton-1.4.6.jar</runtime-relative-path>
<binary-origin>release/modules/ext/jcalendarbutton-1.4.6.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/imageio-psd-3.2.jar</runtime-relative-path>
@ -779,18 +775,10 @@
<runtime-relative-path>ext/stax-api-1.0.1.jar</runtime-relative-path>
<binary-origin>release/modules/ext/stax-api-1.0.1.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/commons-collections4-4.1.jar</runtime-relative-path>
<binary-origin>release/modules/ext/commons-collections4-4.1.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/servlet-api-2.5.jar</runtime-relative-path>
<binary-origin>release/modules/ext/servlet-api-2.5.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/poi-excelant-3.15.jar</runtime-relative-path>
<binary-origin>release/modules/ext/poi-excelant-3.15.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/imageio-pcx-3.2.jar</runtime-relative-path>
<binary-origin>release/modules/ext/imageio-pcx-3.2.jar</binary-origin>
@ -827,10 +815,6 @@
<runtime-relative-path>ext/geronimo-jms_1.1_spec-1.0.jar</runtime-relative-path>
<binary-origin>release/modules/ext/geronimo-jms_1.1_spec-1.0.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/poi-scratchpad-3.15.jar</runtime-relative-path>
<binary-origin>release/modules/ext/poi-scratchpad-3.15.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/joda-time-2.4-sources.jar</runtime-relative-path>
<binary-origin>release/modules/ext/joda-time-2.4-sources.jar</binary-origin>
@ -839,14 +823,26 @@
<runtime-relative-path>ext/jfxtras-fxml-8.0-r4.jar</runtime-relative-path>
<binary-origin>release/modules/ext/jfxtras-fxml-8.0-r4.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/poi-ooxml-3.15.jar</runtime-relative-path>
<binary-origin>release/modules/ext/poi-ooxml-3.15.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/joda-time-2.4.jar</runtime-relative-path>
<binary-origin>release/modules/ext/joda-time-2.4.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/commons-collections4-4.1.jar</runtime-relative-path>
<binary-origin>release/modules/ext/commons-collections4-4.1.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/commons-logging-1.1.2-javadoc.jar</runtime-relative-path>
<binary-origin>release/modules/ext/commons-logging-1.1.2-javadoc.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/commons-codec-1.10.jar</runtime-relative-path>
<binary-origin>release/modules/ext/commons-codec-1.10.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/slf4j-simple-1.6.1.jar</runtime-relative-path>
<binary-origin>release/modules/ext/slf4j-simple-1.6.1.jar</binary-origin>
@ -855,6 +851,18 @@
<runtime-relative-path>ext/guava-19.0.jar</runtime-relative-path>
<binary-origin>release/modules/ext/guava-19.0.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/commons-io-2.5.jar</runtime-relative-path>
<binary-origin>release/modules/ext/commons-io-2.5.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/poi-ooxml-schemas-3.15.jar</runtime-relative-path>
<binary-origin>release/modules/ext/poi-ooxml-schemas-3.15.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/poi-scratchpad-3.15.jar</runtime-relative-path>
<binary-origin>release/modules/ext/poi-scratchpad-3.15.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/imageio-bmp-3.2.jar</runtime-relative-path>
<binary-origin>release/modules/ext/imageio-bmp-3.2.jar</binary-origin>
@ -879,10 +887,6 @@
<runtime-relative-path>ext/ant-1.8.2.jar</runtime-relative-path>
<binary-origin>release/modules/ext/ant-1.8.2.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/commons-codec-1.10.jar</runtime-relative-path>
<binary-origin>release/modules/ext/commons-codec-1.10.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/javassist-3.12.1.GA.jar</runtime-relative-path>
<binary-origin>release/modules/ext/javassist-3.12.1.GA.jar</binary-origin>
@ -895,14 +899,6 @@
<runtime-relative-path>ext/commons-logging-1.1.2.jar</runtime-relative-path>
<binary-origin>release/modules/ext/commons-logging-1.1.2.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/commons-io-2.4.jar</runtime-relative-path>
<binary-origin>release/modules/ext/commons-io-2.4.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/poi-3.15.jar</runtime-relative-path>
<binary-origin>release/modules/ext/poi-3.15.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/controlsfx-8.40.11.jar</runtime-relative-path>
<binary-origin>release/modules/ext/controlsfx-8.40.11.jar</binary-origin>
@ -915,6 +911,10 @@
<runtime-relative-path>ext/javaee-api-5.0-2.jar</runtime-relative-path>
<binary-origin>release/modules/ext/javaee-api-5.0-2.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/poi-3.15.jar</runtime-relative-path>
<binary-origin>release/modules/ext/poi-3.15.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/common-image-3.2.jar</runtime-relative-path>
<binary-origin>release/modules/ext/common-image-3.2.jar</binary-origin>

View File

@ -4,4 +4,4 @@ OpenIDE-Module: org.sleuthkit.autopsy.experimental
OpenIDE-Module-Layer: org/sleuthkit/autopsy/experimental/autoingest/layer.xml
OpenIDE-Module-Localizing-Bundle: org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties
OpenIDE-Module-Requires: org.openide.windows.WindowManager
OpenIDE-Module-Specification-Version: 1.0
OpenIDE-Module-Specification-Version: 1.0

View File

@ -167,4 +167,4 @@
</class-path-extension>
</data>
</configuration>
</project>
</project>

View File

@ -145,6 +145,29 @@
</Component>
</SubComponents>
</Container>
<<<<<<< HEAD
=======
<Component class="javax.swing.JButton" name="bnCancelJob">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnCancelJob.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnCancelJob.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="bnDeleteCase">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnDeleteCase.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnDeleteCase.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
>>>>>>> upstream/develop
<Component class="javax.swing.JLabel" name="lbPending">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
@ -181,6 +204,7 @@
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.refreshButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<<<<<<< HEAD
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.refreshButton.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
@ -188,6 +212,119 @@
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="refreshButtonActionPerformed"/>
</Events>
=======
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnRefresh.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="bnCancelModule">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnCancelModule.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnCancelModule.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="bnExit">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnExit.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnExit.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="bnOptions">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnOptions.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnOptions.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="bnShowProgress">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnShowProgress.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnShowProgress.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="bnPause">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnPause.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnPause.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="bnPrioritizeCase">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnPrioritizeCase.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnPrioritizeCase.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="bnShowCaseLog">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnShowCaseLog.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnShowCaseLog.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="tbStatusMessage">
<Properties>
<Property name="editable" type="boolean" value="false"/>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="12" style="1"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.tbStatusMessage.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="null"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="lbStatus">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="14" style="0"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.lbStatus.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="bnPrioritizeJob">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnPrioritizeJob.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnPrioritizeJob.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="actionCommand" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnPrioritizeJob.actionCommand" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
>>>>>>> upstream/develop
</Component>
<Component class="javax.swing.JLabel" name="lbServicesStatus">
<Properties>
@ -218,14 +355,26 @@
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.prioritizeButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<<<<<<< HEAD
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.prioritizeButton.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
=======
</Properties>
</Component>
<Component class="javax.swing.JButton" name="bnReprocessJob">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties" key="AutoIngestDashboard.bnReprocessJob.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
>>>>>>> upstream/develop
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
<<<<<<< HEAD
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="prioritizeButtonActionPerformed"/>
</Events>
=======
>>>>>>> upstream/develop
</Component>
</SubComponents>
</Form>
</Form>

View File

@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.experimental.autoingest;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.EventQueue;
import java.nio.file.Path;
@ -743,7 +744,7 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
}
}//GEN-LAST:event_prioritizeButtonActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
// Variables declaration - do not modify
private javax.swing.JScrollPane completedScrollPane;
private javax.swing.JTable completedTable;
private javax.swing.JLabel lbCompleted;
@ -757,7 +758,7 @@ public final class AutoIngestDashboard extends JPanel implements Observer {
private javax.swing.JScrollPane runningScrollPane;
private javax.swing.JTable runningTable;
private javax.swing.JTextField tbServicesStatusMessage;
// End of variables declaration//GEN-END:variables
// End of variables declaration
/*
* The enum is used in conjunction with the DefaultTableModel class to

View File

@ -25,4 +25,8 @@
</Group>
</DimensionLayout>
</Layout>
<<<<<<< HEAD
</Form>
=======
</Form>
>>>>>>> upstream/develop

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011 - 2017 Basis Technology Corp.
* Copyright 2011-2017 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -18,24 +18,18 @@
*/
package org.sleuthkit.autopsy.experimental.autoingest;
import java.util.List;
import java.util.logging.Level;
import org.netbeans.api.settings.ConvertAsProperties;
import org.openide.awt.ActionID;
import org.openide.awt.ActionReference;
import org.openide.util.Exceptions;
import org.openide.windows.TopComponent;
import java.util.stream.Collectors;
import org.openide.util.NbBundle.Messages;
import org.openide.windows.Mode;
import org.openide.windows.TopComponent;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.coreutils.Logger;
/**
* Top component which displays the Auto Ingest Dashboard interface.
*/
@ConvertAsProperties(
dtd = "-//org.sleuthkit.autopsy.experimental.autoingest//AutoIngestDashboard//EN",
autostore = false
)
@TopComponent.Description(
preferredID = "AutoIngestDashboardTopComponent",
//iconBase="SET/PATH/TO/ICON/HERE",
@ -44,13 +38,11 @@ import org.sleuthkit.autopsy.coreutils.Logger;
@TopComponent.Registration(mode = "dashboard", openAtStartup = false)
@Messages({
"CTL_AutoIngestDashboardAction=Auto Ingest Dashboard",
"CTL_AutoIngestDashboardTopComponent=Auto Ingest Dashboard",
"HINT_AutoIngestDashboardTopComponent=This is an Auto Ingest Dashboard window"
})
"CTL_AutoIngestDashboardTopComponent=Auto Ingest Dashboard"})
public final class AutoIngestDashboardTopComponent extends TopComponent {
public final static String PREFERRED_ID = "AutoIngestDashboardTopComponent"; // NON-NLS
private static final Logger LOGGER = Logger.getLogger(AutoIngestDashboardTopComponent.class.getName());
private static final Logger logger = Logger.getLogger(AutoIngestDashboardTopComponent.class.getName());
private static boolean topComponentInitialized = false;
public static void openTopComponent() {
@ -68,8 +60,11 @@ public final class AutoIngestDashboardTopComponent extends TopComponent {
dashboard = AutoIngestDashboard.createDashboard();
tc.add(dashboard);
dashboard.setSize(dashboard.getPreferredSize());
if (tc.isOpened() == false) {
tc.open();
tc.requestActive();
}
tc.toFront();
tc.requestActive();
} catch (AutoIngestDashboard.AutoIngestDashboardException ex) {
// DLG: Catch the exeption, log it, and pop up an error dialog
// with a user-friendly message
@ -79,12 +74,12 @@ public final class AutoIngestDashboardTopComponent extends TopComponent {
public static void closeTopComponent() {
if (topComponentInitialized) {
final TopComponent etc = WindowManager.getDefault().findTopComponent(PREFERRED_ID);
if (etc != null) {
final TopComponent tc = WindowManager.getDefault().findTopComponent(PREFERRED_ID);
if (tc != null) {
try {
etc.close();
tc.close();
} catch (Exception e) {
LOGGER.log(Level.SEVERE, "failed to close " + PREFERRED_ID, e); // NON-NLS
logger.log(Level.SEVERE, "Failed to close " + PREFERRED_ID, e); // NON-NLS
}
}
}
@ -93,7 +88,23 @@ public final class AutoIngestDashboardTopComponent extends TopComponent {
public AutoIngestDashboardTopComponent() {
initComponents();
setName(Bundle.CTL_AutoIngestDashboardTopComponent());
setToolTipText(Bundle.HINT_AutoIngestDashboardTopComponent());
}
@Override
public List<Mode> availableModes(List<Mode> modes) {
/*
* This looks like the right thing to do, but online discussions seems
* to indicate this method is effectively deprecated. A break point
* placed here was never hit.
*/
return modes.stream().filter(mode -> mode.getName().equals("dashboard") || mode.getName().equals("ImageGallery"))
.collect(Collectors.toList());
}
@Override
public void componentOpened() {
super.componentOpened();
WindowManager.getDefault().setTopComponentFloating(this, true);
}
/**
@ -118,25 +129,5 @@ public final class AutoIngestDashboardTopComponent extends TopComponent {
// Variables declaration - do not modify//GEN-BEGIN:variables
// End of variables declaration//GEN-END:variables
@Override
public void componentOpened() {
// TODO add custom code on component opening
}
@Override
public void componentClosed() {
// TODO add custom code on component closing
}
void writeProperties(java.util.Properties p) {
// better to version settings since initial version as advocated at
// http://wiki.apidesign.org/wiki/PropertyFiles
p.setProperty("version", "1.0");
// TODO store your settings
}
void readProperties(java.util.Properties p) {
String version = p.getProperty("version");
// TODO read your settings according to their version
}
}

View File

@ -2646,8 +2646,6 @@ public final class AutoIngestManager extends Observable implements PropertyChang
* back into hostNamesToRunningJobs as a result of
* processing the job status update.
*/
SYS_LOGGER.log(Level.WARNING, "Auto ingest node {0} timed out while processing folder {1}",
new Object[]{job.getNodeName(), job.getNodeData().getManifestFilePath().toString()});
hostNamesToRunningJobs.remove(job.getNodeName());
setChanged();
notifyObservers(Event.JOB_COMPLETED);

View File

@ -35,7 +35,7 @@ import org.sleuthkit.autopsy.coreutils.PlatformUtil;
*/
final class AutoIngestSystemLogger {
private static final int LOG_SIZE = 10000000; // In bytes, zero is unlimited, set to roughly 10mb currently
private static final int LOG_SIZE = 50000000; // In bytes, zero is unlimited, set to roughly 10mb currently
private static final int LOG_FILE_COUNT = 10;
private static final Logger LOGGER = Logger.getLogger("AutoIngest"); //NON-NLS
private static final String NEWLINE = System.lineSeparator();

View File

@ -305,9 +305,13 @@ AutoIngestControlPanel.lbServicesStatus.text=Services Status:
AutoIngestControlPanel.bnPrioritizeJob.actionCommand=<AutoIngestDashboard.bnPrioritizeJob.text>
AutoIngestControlPanel.bnPrioritizeJob.toolTipText=Move this folder to the top of the Pending queue.
AutoIngestControlPanel.bnPrioritizeJob.text=Prioritize Job
<<<<<<< HEAD
AutoIngestControlPanel.lbStatus.text=Status:
AutoIngestControlPanel.PauseDueToSystemError=Paused due to system error, please consult the auto ingest system log
AutoIngestDashboard.prioritizeButton.toolTipText=Prioritizes the selected job
AutoIngestDashboard.prioritizeButton.text=&Prioritize
AutoIngestDashboard.refreshButton.toolTipText=Refresh displayed tables
AutoIngestDashboard.refreshButton.text=&Refresh
=======
AutoIngestControlPanel.lbStatus.text=Status:
>>>>>>> upstream/develop

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<mode version="2.4">
<name unique="dashboard"/>
<kind type="editor"/>
<state type="separated"/>
<bounds x="76" y="68" width="996" height="672"/>
<frame state="0"/>
<empty-behavior permanent="false"/>
</mode>

View File

@ -31,5 +31,11 @@
</file>
</folder>
</folder>
<folder name="Windows2">
<folder name="Modes">
<file name="dashboard.wsmode" url="dashboardWsmode.xml"/>
</folder>
</folder>
</filesystem>

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011 Basis Technology Corp.
* Copyright 2011-2017 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");

View File

@ -73,7 +73,7 @@ class AccountsText implements IndexedText {
private final long solrObjectId;
private final Collection<? extends BlackboardArtifact> artifacts;
private final Set<String> accountNumbers = new HashSet<>();
private final String displayName;
private final String title;
@GuardedBy("this")
private boolean isPageInfoLoaded = false;
@ -105,7 +105,7 @@ class AccountsText implements IndexedText {
AccountsText(long objectID, Collection<? extends BlackboardArtifact> artifacts) {
this.solrObjectId = objectID;
this.artifacts = artifacts;
displayName = artifacts.size() == 1
title = artifacts.size() == 1
? Bundle.AccountsText_creditCardNumber()
: Bundle.AccountsText_creditCardNumbers();
}
@ -227,11 +227,17 @@ class AccountsText implements IndexedText {
}
//add both the canonical form and the form in the text as accountNumbers to highlight.
this.accountNumbers.add(artifact.getAttribute(TSK_KEYWORD).getValueString());
this.accountNumbers.add(artifact.getAttribute(TSK_CARD_NUMBER).getValueString());
BlackboardAttribute attribute = artifact.getAttribute(TSK_KEYWORD);
if (attribute != null) {
this.accountNumbers.add(attribute.getValueString());
}
attribute = artifact.getAttribute(TSK_CARD_NUMBER);
if (attribute != null) {
this.accountNumbers.add(attribute.getValueString());
}
//if the chunk id is present just use that.
Optional<Integer> chunkID =
Optional<Integer> chunkID =
Optional.ofNullable(artifact.getAttribute(TSK_KEYWORD_SEARCH_DOCUMENT_ID))
.map(BlackboardAttribute::getValueString)
.map(String::trim)
@ -245,10 +251,10 @@ class AccountsText implements IndexedText {
needsQuery = true;
}
}
if (needsQuery) {
// Run a query to figure out which chunks for the current object have hits.
Keyword queryKeyword = new Keyword(CCN_REGEX, false, false);
Keyword queryKeyword = new Keyword(CCN_REGEX, false, false);
KeywordSearchQuery chunksQuery = KeywordSearchUtil.getQueryForKeyword(queryKeyword, new KeywordList(Arrays.asList(queryKeyword)));
chunksQuery.addFilter(new KeywordQueryFilter(KeywordQueryFilter.FilterType.CHUNK, this.solrObjectId));
//load the chunks/pages from the result of the query.
@ -353,7 +359,7 @@ class AccountsText implements IndexedText {
@Override
public String toString() {
return displayName;
return title;
}
@Override

View File

@ -60,7 +60,6 @@ AbstractKeywordSearchPerformer.search.ingestInProgressBody=<html>Keyword Search
AbstractKeywordSearchPerformer.search.emptyKeywordErrorBody=Keyword list is empty, please add at least one keyword to the list
AbstractKeywordSearchPerformer.search.noFilesInIdxMsg=<html>No files are in index yet. <br />Try again later. Index is updated every {0} minutes.</html>
AbstractKeywordSearchPerformer.search.noFilesIdxdMsg=<html>No files were indexed.<br />Re-ingest the image with the Keyword Search Module enabled. </html>
ExtractedContentPanel.setMarkup.panelTxt=<span style\='font-style\:italic'>Loading text... Please wait</span>
ExtractedContentViewer.toolTip=Displays extracted text from files and keyword-search results. Requires Keyword Search ingest to be run on a file to activate this viewer.
ExtractedContentViewer.getTitle=Indexed Text
ExtractedContentViewer.getSolrContent.knownFileMsg=<p style\=''font-style\:italic''>{0} is a known file (based on MD5 hash) and does not have text in the index.</p>
@ -161,8 +160,6 @@ DropdownSearchPanel.copyMenuItem.text=Copy
AbstractFileStringContentStream.getSize.exception.msg=Cannot tell how many chars in converted string, until entire string is converted
AbstractFileStringContentStream.getSrcInfo.text=File\:{0}
ByteContentStream.getSrcInfo.text=File\:{0}
ExtractedContentPanel.SetMarkup.progress.loading=Loading text
ExtractedContentPanel.SetMarkup.progress.displayName=Loading text
ExtractedContentViewer.nextPage.exception.msg=No next page.
ExtractedContentViewer.previousPage.exception.msg=No previous page.
ExtractedContentViewer.hasNextItem.exception.msg=Not supported, not a searchable source.

View File

@ -23,6 +23,7 @@ import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.EnumSet;
import java.util.logging.Level;
import javax.swing.SwingUtilities;
import javax.swing.event.PopupMenuEvent;
@ -76,7 +77,7 @@ class DropdownToolbar extends javax.swing.JPanel {
private void customizeComponents() {
searchSettingsChangeListener = new SearchSettingsChangeListener();
KeywordSearch.getServer().addServerActionListener(searchSettingsChangeListener);
Case.addPropertyChangeListener(searchSettingsChangeListener);
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), searchSettingsChangeListener);
DropdownListSearchPanel listsPanel = DropdownListSearchPanel.getDefault();
listsPanel.addSearchButtonActionListener((ActionEvent e) -> {

View File

@ -18,17 +18,15 @@
*/
package org.sleuthkit.autopsy.keywordsearch;
import com.google.common.base.Strings;
import java.awt.ComponentOrientation;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import javax.swing.JMenuItem;
import javax.swing.JTextPane;
import javax.swing.SizeRequirements;
import javax.swing.SwingWorker;
@ -45,6 +43,7 @@ import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.EscapeUtil;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.TextUtil;
import static org.sleuthkit.autopsy.keywordsearch.Bundle.*;
/**
* Panel displays HTML content sent to ExtractedContentViewer, and provides a
@ -52,7 +51,8 @@ import org.sleuthkit.autopsy.coreutils.TextUtil;
*/
class ExtractedContentPanel extends javax.swing.JPanel {
private static Logger logger = Logger.getLogger(ExtractedContentPanel.class.getName());
private static final Logger logger = Logger.getLogger(ExtractedContentPanel.class.getName());
private String contentName;
ExtractedContentPanel() {
initComponents();
@ -124,32 +124,17 @@ class ExtractedContentPanel extends javax.swing.JPanel {
extractedTextPane.setEditorKit(editorKit);
sourceComboBox.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED) {
IndexedText source = (IndexedText) e.getItem();
setMarkup(source);
}
sourceComboBox.addItemListener((ItemEvent e) -> {
if (e.getStateChange() == ItemEvent.SELECTED) {
setMarkup((IndexedText) e.getItem());
}
});
setSources(new ArrayList<IndexedText>());
setSources("",new ArrayList<>());
extractedTextPane.setComponentPopupMenu(rightClickMenu);
ActionListener actList = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
JMenuItem jmi = (JMenuItem) e.getSource();
if (jmi.equals(copyMenuItem)) {
extractedTextPane.copy();
} else if (jmi.equals(selectAllMenuItem)) {
extractedTextPane.selectAll();
}
}
};
copyMenuItem.addActionListener(actList);
selectAllMenuItem.addActionListener(actList);
copyMenuItem.addActionListener(actionEvent -> extractedTextPane.copy());
selectAllMenuItem.addActionListener(actionEvent -> extractedTextPane.selectAll());
}
/**
@ -364,8 +349,7 @@ class ExtractedContentPanel extends javax.swing.JPanel {
// End of variables declaration//GEN-END:variables
void refreshCurrentMarkup() {
IndexedText ms = (IndexedText) sourceComboBox.getSelectedItem();
setMarkup(ms);
setMarkup(getSelectedSource());
}
/**
@ -374,13 +358,12 @@ class ExtractedContentPanel extends javax.swing.JPanel {
*
* @param sources
*/
void setSources(List<IndexedText> sources) {
void setSources(String contentName, List<IndexedText> sources) {
this.contentName = contentName;
sourceComboBox.removeAllItems();
setPanelText(null, false);
for (IndexedText ms : sources) {
sourceComboBox.addItem(ms);
}
sources.forEach(sourceComboBox::addItem);
if (!sources.isEmpty()) {
sourceComboBox.setSelectedIndex(0);
@ -411,9 +394,8 @@ class ExtractedContentPanel extends javax.swing.JPanel {
}
private void setPanelText(String text, boolean detectDirection) {
if (text == null) {
text = "";
}
text = Strings.nullToEmpty(text);
if (detectDirection) {
//detect text direction using first 1024 chars and set it
@ -640,9 +622,10 @@ class ExtractedContentPanel extends javax.swing.JPanel {
* text). Updates GUI in GUI thread and gets markup in background thread. To
* be invoked from GUI thread only.
*/
@NbBundle.Messages("ExtractedContentPanel.setMarkup.panelTxt=<span style='font-style:italic'>Loading text... Please wait</span>")
private void setMarkup(IndexedText source) {
setPanelText(NbBundle.getMessage(this.getClass(), "ExtractedContentPanel.setMarkup.panelTxt"), false);
new SetMarkupWorker(source).execute();
setPanelText(ExtractedContentPanel_setMarkup_panelTxt(), false);
new SetMarkupWorker(contentName,source).execute();
}
/**
@ -652,18 +635,21 @@ class ExtractedContentPanel extends javax.swing.JPanel {
*/
private final class SetMarkupWorker extends SwingWorker<String, Void> {
private final String contentName;
private final IndexedText source;
private ProgressHandle progress;
SetMarkupWorker(IndexedText source) {
SetMarkupWorker(String contentName,IndexedText source) {
this.contentName = contentName;
this.source = source;
}
@Override
@NbBundle.Messages({"# 0 - Content name","ExtractedContentPanel.SetMarkup.progress.loading=Loading text for {0}"})
protected String doInBackground() throws Exception {
progress = ProgressHandle.createHandle(NbBundle.getMessage(this.getClass(), "ExtractedContentPanel.SetMarkup.progress.loading"));
progress.setDisplayName(NbBundle.getMessage(this.getClass(), "ExtractedContentPanel.SetMarkup.progress.displayName"));
progress = ProgressHandle.createHandle(ExtractedContentPanel_SetMarkup_progress_loading(contentName));
progress.start();
progress.switchToIndeterminate();

View File

@ -27,13 +27,13 @@ import java.util.Collection;
import java.util.List;
import java.util.logging.Level;
import org.openide.nodes.Node;
import org.openide.util.Exceptions;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT;
@ -91,7 +91,7 @@ public class ExtractedContentViewer implements DataContentViewer {
}
Lookup nodeLookup = node.getLookup();
Content content = nodeLookup.lookup(Content.class);
AbstractFile content = nodeLookup.lookup(AbstractFile.class);
/*
* Assemble a collection of all of the indexed text "sources" for the
@ -173,7 +173,7 @@ public class ExtractedContentViewer implements DataContentViewer {
}
}
panel.updateControls(currentSource);
setPanel(sources);
setPanel(content.getName(),sources);
}
static private IndexedText getRawArtifactText(Lookup nodeLookup) throws TskCoreException {
@ -254,7 +254,7 @@ public class ExtractedContentViewer implements DataContentViewer {
@Override
public void resetComponent() {
setPanel(new ArrayList<>());
setPanel("",new ArrayList<>());
panel.resetDisplay();
currentNode = null;
currentSource = null;
@ -312,9 +312,10 @@ public class ExtractedContentViewer implements DataContentViewer {
*
* @param sources
*/
private void setPanel(List<IndexedText> sources) {
private void setPanel(String contentName, List<IndexedText> sources) {
if (panel != null) {
panel.setSources(sources);
panel.setSources(contentName, sources);
}
}

View File

@ -18,8 +18,6 @@
*/
package org.sleuthkit.autopsy.keywordsearch;
import java.util.LinkedHashMap;
/**
* Interface to provide HTML text to display in ExtractedContentViewer. There is
* a SOLR implementation of this that interfaces with SOLR to highlight the
@ -138,4 +136,5 @@ interface IndexedText {
* @return the current item number
*/
int currentItem();
}

View File

@ -1,63 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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.keywordsearch;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
/**
* result of writing keyword search result to blackboard (cached artifact and
* attributes) This is mainly to cache the attributes, so that we don't query
* the DB to get them back again.
*/
class KeywordCachedArtifact {
private BlackboardArtifact artifact;
private Map<Integer, BlackboardAttribute> attributes;
KeywordCachedArtifact(BlackboardArtifact artifact) {
this.artifact = artifact;
attributes = new HashMap<Integer, BlackboardAttribute>();
}
BlackboardArtifact getArtifact() {
return artifact;
}
Collection<BlackboardAttribute> getAttributes() {
return attributes.values();
}
BlackboardAttribute getAttribute(Integer attrTypeID) {
return attributes.get(attrTypeID);
}
void add(BlackboardAttribute attribute) {
attributes.put(attribute.getAttributeType().getTypeID(), attribute);
}
void add(Collection<BlackboardAttribute> attributes) {
for (BlackboardAttribute attr : attributes) {
this.attributes.put(attr.getAttributeType().getTypeID(), attr);
}
}
}

View File

@ -18,17 +18,18 @@
*/
package org.sleuthkit.autopsy.keywordsearch;
import java.util.Comparator;
import org.apache.commons.lang3.StringUtils;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Stores the fact that file or an artifact associated with a file had a keyword
* hit. All instances make both the document id of the Solr document where the
* keyword was found and the file available to clients. Artifact keyword hits
* also make the artifact available to clients.
* keyword was found and the object Id available to clients. Artifact keyword
* hits also make the artifact available to clients.
*/
class KeywordHit implements Comparable<KeywordHit> {
@ -36,7 +37,7 @@ class KeywordHit implements Comparable<KeywordHit> {
private final long solrObjectId;
private final int chunkId;
private final String snippet;
private final Content content;
private final long contentID;
private final BlackboardArtifact artifact;
private final String hit;
@ -44,14 +45,9 @@ class KeywordHit implements Comparable<KeywordHit> {
return hit;
}
KeywordHit(String solrDocumentId, String snippet) throws TskCoreException {
this(solrDocumentId, snippet, null);
}
KeywordHit(String solrDocumentId, String snippet, String hit) throws TskCoreException {
/**
* Store the Solr document id.
*/
this.snippet = StringUtils.stripToEmpty(snippet);
this.hit = hit;
this.solrDocumentId = solrDocumentId;
/**
@ -72,27 +68,19 @@ class KeywordHit implements Comparable<KeywordHit> {
this.chunkId = 0;
}
/**
* Look up the file associated with the keyword hit. If the high order
* bit of the object id is set, the hit was for an artifact. In this
* case, look up the artifact as well.
/*
* If the high order bit of the object id is set (ie, it is negative),
* the hit was in an artifact, look up the artifact.
*/
SleuthkitCase caseDb = Case.getCurrentCase().getSleuthkitCase();
long fileId;
if (this.solrObjectId < 0) {
SleuthkitCase caseDb = Case.getCurrentCase().getSleuthkitCase();
this.artifact = caseDb.getBlackboardArtifact(this.solrObjectId);
fileId = artifact.getObjectID();
contentID = artifact.getObjectID();
} else {
//else the object id is for content.
this.artifact = null;
fileId = this.solrObjectId;
contentID = this.solrObjectId;
}
this.content = caseDb.getContentById(fileId);
/**
* Store the text snippet.
*/
this.snippet = snippet;
this.hit = hit;
}
String getSolrDocumentId() {
@ -103,24 +91,20 @@ class KeywordHit implements Comparable<KeywordHit> {
return this.solrObjectId;
}
boolean hasChunkId() {
return this.chunkId != 0;
}
int getChunkId() {
return this.chunkId;
}
boolean hasSnippet() {
return !this.snippet.isEmpty();
return StringUtils.isNotBlank(this.snippet);
}
String getSnippet() {
return this.snippet;
}
Content getContent() {
return this.content;
long getContentID() {
return this.contentID;
}
/**
@ -151,7 +135,7 @@ class KeywordHit implements Comparable<KeywordHit> {
return false;
}
final KeywordHit other = (KeywordHit) obj;
return (this.solrObjectId == other.solrObjectId && this.chunkId == other.chunkId);
return this.compareTo(other) == 0;
}
@Override
@ -163,21 +147,8 @@ class KeywordHit implements Comparable<KeywordHit> {
@Override
public int compareTo(KeywordHit o) {
if (this.solrObjectId < o.solrObjectId) {
// Out object id is less than the other object id
return -1;
} else if (this.solrObjectId == o.solrObjectId) {
// Hits have same object id
if (this.chunkId < o.chunkId) {
// Our chunk id is lower than the other chunk id
return -1;
} else {
// Our chunk id is either greater than or equal to the other chunk id
return this.chunkId == o.chunkId ? 0 : 1;
}
} else {
// Our object id is greater than the other object id
return 1;
}
return Comparator.comparing(KeywordHit::getSolrObjectId)
.thenComparing(KeywordHit::getChunkId)
.compare(this, o);
}
}

View File

@ -18,8 +18,11 @@
*/
package org.sleuthkit.autopsy.keywordsearch;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.Content;
/**
* Interface for kewyord search queries.
* Interface for kewyord search queries.
*/
interface KeywordSearchQuery {
@ -30,18 +33,20 @@ interface KeywordSearchQuery {
*
* @return true if the query passed validation
*/
boolean validate();
boolean validate();
/**
* execute query and return results without publishing them return results
* for all matching terms
*
* @throws KeywordSearchModuleException error while executing Solr term query
* @throws NoOpenCoreException if query failed due to server error, this
* could be a notification to stop processing
* @throws KeywordSearchModuleException error while executing Solr term
* query
* @throws NoOpenCoreException if query failed due to server error,
* this could be a notification to stop
* processing
* @return
*/
QueryResults performQuery() throws KeywordSearchModuleException, NoOpenCoreException;
QueryResults performQuery() throws KeywordSearchModuleException, NoOpenCoreException;
/**
* Set an optional filter to narrow down the search Adding multiple filters
@ -49,54 +54,67 @@ interface KeywordSearchQuery {
*
* @param filter filter to set on the query
*/
void addFilter(KeywordQueryFilter filter);
void addFilter(KeywordQueryFilter filter);
/**
* Set an optional SOLR field to narrow down the search
*
* @param field field to set on the query
*/
void setField(String field);
void setField(String field);
/**
* Modify the query string to be searched as a substring instead of a whole
* word
*
* @param isSubstring
*/
void setSubstringQuery();
void setSubstringQuery();
/**
* escape the query string and use the escaped string in the query
*/
void escape();
void escape();
/**
*
* @return true if query was escaped
*/
boolean isEscaped();
boolean isEscaped();
/**
*
* @return true if query is a literal query (non regex)
*/
boolean isLiteral();
boolean isLiteral();
/**
* return original keyword/query string
*
* @return the query String supplied originally
*/
String getQueryString();
String getQueryString();
/**
* return escaped keyword/query string if escaping was done
*
* @return the escaped query string, or original string if no escaping done
*/
String getEscapedQueryString();
KeywordCachedArtifact writeSingleFileHitsToBlackBoard(Keyword keyword, KeywordHit hit, String snippet, String listName);
String getEscapedQueryString();
/**
* Converts the keyword hits for a given search term into artifacts.
*
* @param content The Content object associated with the hit.
* @param foundKeyword The keyword that was found by the search, this may be
* different than the Keyword that was searched if, for
* example, it was a RegexQuery.
* @param hit The keyword hit.
* @param snippet The document snippet that contains the hit.
* @param listName The name of the keyword list that contained the
* keyword for which the hit was found.
*
*
* @return The newly created artifact or Null if there was a problem
* creating it.
*/
BlackboardArtifact writeSingleFileHitsToBlackBoard(Content content, Keyword foundKeyword, KeywordHit hit, String snippet, String listName);
}

View File

@ -18,13 +18,10 @@
*/
package org.sleuthkit.autopsy.keywordsearch;
import com.google.common.collect.SetMultimap;
import com.google.common.collect.TreeMultimap;
import java.awt.EventQueue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
@ -41,6 +38,7 @@ import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.util.NbBundle;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode;
@ -50,12 +48,13 @@ import org.sleuthkit.autopsy.datamodel.KeyValue;
import org.sleuthkit.autopsy.datamodel.KeyValueNode;
import org.sleuthkit.autopsy.keywordsearch.KeywordSearchResultFactory.KeyValueQueryContent;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Node factory that performs the keyword search and creates children nodes for
@ -69,16 +68,16 @@ class KeywordSearchResultFactory extends ChildFactory<KeyValueQueryContent> {
private static final Logger logger = Logger.getLogger(KeywordSearchResultFactory.class.getName());
//common properties (superset of all Node properties) to be displayed as columns
static final List<String> COMMON_PROPERTIES
= Stream.concat(
static final List<String> COMMON_PROPERTIES =
Stream.concat(
Stream.of(
TSK_KEYWORD,
TSK_KEYWORD_REGEXP,
TSK_KEYWORD_PREVIEW)
.map(BlackboardAttribute.ATTRIBUTE_TYPE::getDisplayName),
.map(BlackboardAttribute.ATTRIBUTE_TYPE::getDisplayName),
Arrays.stream(AbstractAbstractFileNode.AbstractFilePropertyType.values())
.map(Object::toString))
.collect(Collectors.toList());
.map(Object::toString))
.collect(Collectors.toList());
private final Collection<QueryRequest> queryRequests;
@ -91,7 +90,7 @@ class KeywordSearchResultFactory extends ChildFactory<KeyValueQueryContent> {
* properties are displayed as columns (since we are doing lazy child Node
* load we need to preinitialize properties when sending parent Node)
*
* @param toSet property set map for a Node
* @param toPopulate property set map for a Node
*/
@Override
protected boolean createKeys(List<KeyValueQueryContent> toPopulate) {
@ -144,6 +143,13 @@ class KeywordSearchResultFactory extends ChildFactory<KeyValueQueryContent> {
MessageNotifyUtil.Notify.error(Bundle.KeywordSearchResultFactory_query_exception_msg() + queryRequest.getQueryString(), ex.getCause().getMessage());
return false;
}
SleuthkitCase tskCase = null;
try {
tskCase = Case.getCurrentCase().getSleuthkitCase();
} catch (IllegalStateException ex) {
logger.log(Level.SEVERE, "There was no case open.", ex); //NON-NLS
return false;
}
int hitNumber = 0;
List<KeyValueQueryContent> tempList = new ArrayList<>();
@ -153,8 +159,20 @@ class KeywordSearchResultFactory extends ChildFactory<KeyValueQueryContent> {
* Get file properties.
*/
Map<String, Object> properties = new LinkedHashMap<>();
Content content = hit.getContent();
String contentName = content.getName();
Content content = null;
String contentName = "";
try {
content = tskCase.getContentById(hit.getContentID());
if (content == null) {
logger.log(Level.SEVERE, "There was a error getting content by id."); //NON-NLS
return false;
}
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "There was a error getting content by id.", ex); //NON-NLS
return false;
}
contentName = content.getName();
if (content instanceof AbstractFile) {
AbstractFsContentNode.fillPropertyMap(properties, (AbstractFile) content);
} else {
@ -222,6 +240,7 @@ class KeywordSearchResultFactory extends ChildFactory<KeyValueQueryContent> {
//wrap in KeywordSearchFilterNode for the markup content, might need to override FilterNode for more customization
return new KeywordSearchFilterNode(hits, kvNode);
}
/**
@ -240,13 +259,14 @@ class KeywordSearchResultFactory extends ChildFactory<KeyValueQueryContent> {
* NOTE Parameters are defined based on how they are currently used in
* practice
*
* @param name File name that has hit.
* @param map Contains content metadata, snippets, etc. (property
* map)
* @param id User incremented ID
* @param content File that had the hit.
* @param query Query used in search
* @param hits Full set of search results (for all files! @@@)
* @param name File name that has hit.
* @param map Contains content metadata, snippets, etc.
* (property map)
* @param id User incremented ID
* @param solrObjectId
* @param content File that had the hit.
* @param query Query used in search
* @param hits Full set of search results (for all files! @@@)
*/
KeyValueQueryContent(String name, Map<String, Object> map, int id, long solrObjectId, Content content, KeywordSearchQuery query, QueryResults hits) {
super(name, map, id);
@ -278,13 +298,12 @@ class KeywordSearchResultFactory extends ChildFactory<KeyValueQueryContent> {
* worker for writing results to bb, with progress bar, cancellation, and
* central registry of workers to be stopped when case is closed
*/
static class BlackboardResultWriter extends SwingWorker<Object, Void> {
static class BlackboardResultWriter extends SwingWorker<Void, Void> {
private static final List<BlackboardResultWriter> writers = new ArrayList<>();
private ProgressHandle progress;
private final KeywordSearchQuery query;
private final QueryResults hits;
private Collection<BlackboardArtifact> newArtifacts = new ArrayList<>();
private static final int QUERY_DISPLAY_LEN = 40;
BlackboardResultWriter(QueryResults hits, String listName) {
@ -298,13 +317,13 @@ class KeywordSearchResultFactory extends ChildFactory<KeyValueQueryContent> {
}
@Override
protected Object doInBackground() throws Exception {
protected Void doInBackground() throws Exception {
registerWriter(this); //register (synchronized on class) outside of writerLock to prevent deadlock
final String queryStr = query.getQueryString();
final String queryDisp = queryStr.length() > QUERY_DISPLAY_LEN ? queryStr.substring(0, QUERY_DISPLAY_LEN - 1) + " ..." : queryStr;
try {
progress = ProgressHandle.createHandle(NbBundle.getMessage(this.getClass(), "KeywordSearchResultFactory.progress.saving", queryDisp), () -> BlackboardResultWriter.this.cancel(true));
newArtifacts = hits.writeAllHitsToBlackBoard(progress, null, this, false);
hits.writeAllHitsToBlackBoard(progress, null, this, false);
} finally {
finalizeWorker();
}

View File

@ -20,7 +20,6 @@ package org.sleuthkit.autopsy.keywordsearch;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
@ -40,6 +39,7 @@ import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskException;
@ -192,15 +192,13 @@ class LuceneQuery implements KeywordSearchQuery {
}
@Override
public KeywordCachedArtifact writeSingleFileHitsToBlackBoard(Keyword foundKeyword, KeywordHit hit, String snippet, String listName) {
public BlackboardArtifact writeSingleFileHitsToBlackBoard(Content content, Keyword foundKeyword, KeywordHit hit, String snippet, String listName) {
final String MODULE_NAME = KeywordSearchModuleFactory.getModuleName();
Collection<BlackboardAttribute> attributes = new ArrayList<>();
BlackboardArtifact bba;
KeywordCachedArtifact writeResult;
try {
bba = hit.getContent().newArtifact(ARTIFACT_TYPE.TSK_KEYWORD_HIT);
writeResult = new KeywordCachedArtifact(bba);
bba = content.newArtifact(ARTIFACT_TYPE.TSK_KEYWORD_HIT);
} catch (TskCoreException e) {
logger.log(Level.WARNING, "Error adding bb artifact for keyword hit", e); //NON-NLS
return null;
@ -233,8 +231,7 @@ class LuceneQuery implements KeywordSearchQuery {
try {
bba.addAttributes(attributes); //write out to bb
writeResult.add(attributes);
return writeResult;
return bba;
} catch (TskCoreException e) {
logger.log(Level.WARNING, "Error adding bb attributes to artifact", e); //NON-NLS
return null;

View File

@ -31,6 +31,7 @@ import org.apache.commons.lang.StringUtils;
import org.netbeans.api.progress.ProgressHandle;
import org.netbeans.api.progress.aggregate.ProgressContributor;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.EscapeUtil;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.ingest.IngestMessage;
@ -40,6 +41,8 @@ import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Stores the results from running a Solr query (which could contain multiple
@ -60,8 +63,6 @@ class QueryResults {
*/
private final Map<Keyword, List<KeywordHit>> results = new HashMap<>();
QueryResults(KeywordSearchQuery query) {
this.keywordSearchQuery = query;
}
@ -70,8 +71,6 @@ class QueryResults {
results.put(keyword, hits);
}
KeywordSearchQuery getQuery() {
return keywordSearchQuery;
}
@ -99,7 +98,7 @@ class QueryResults {
*
* @return The artifacts that were created.
*/
Collection<BlackboardArtifact> writeAllHitsToBlackBoard(ProgressHandle progress, ProgressContributor subProgress, SwingWorker<Object, Void> worker, boolean notifyInbox) {
Collection<BlackboardArtifact> writeAllHitsToBlackBoard(ProgressHandle progress, ProgressContributor subProgress, SwingWorker<?, ?> worker, boolean notifyInbox) {
final Collection<BlackboardArtifact> newArtifacts = new ArrayList<>();
if (progress != null) {
progress.start(getKeywords().size());
@ -145,14 +144,26 @@ class QueryResults {
continue;
}
}
KeywordCachedArtifact writeResult = keywordSearchQuery.writeSingleFileHitsToBlackBoard(keyword, hit, snippet, keywordSearchQuery.getKeywordList().getName());
Content content = null;
try {
SleuthkitCase tskCase = Case.getCurrentCase().getSleuthkitCase();
content = tskCase.getContentById(hit.getContentID());
} catch (TskCoreException | IllegalStateException tskCoreException) {
logger.log(Level.SEVERE, "Error adding artifact for keyword hit to blackboard", tskCoreException); //NON-NLS
return null;
}
BlackboardArtifact writeResult = keywordSearchQuery.writeSingleFileHitsToBlackBoard(content, keyword, hit, snippet, keywordSearchQuery.getKeywordList().getName());
if (writeResult != null) {
newArtifacts.add(writeResult.getArtifact());
newArtifacts.add(writeResult);
if (notifyInbox) {
writeSingleFileInboxMessage(writeResult, hit.getContent());
try {
writeSingleFileInboxMessage(writeResult, content);
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Error posting message to Ingest Inbox", ex); //NON-NLS
}
}
} else {
logger.log(Level.WARNING, "BB artifact for keyword hit not written, file: {0}, hit: {1}", new Object[]{hit.getContent(), keyword.toString()}); //NON-NLS
logger.log(Level.WARNING, "BB artifact for keyword hit not written, file: {0}, hit: {1}", new Object[]{content, keyword.toString()}); //NON-NLS
}
}
++unitProgress;
@ -181,7 +192,6 @@ class QueryResults {
* SolrObjectID-ChunkID pairs.
*/
private Collection<KeywordHit> getOneHitPerObject(Keyword keyword) {
HashMap<Long, KeywordHit> hits = new HashMap<>();
// create a list of KeywordHits. KeywordHits with lowest chunkID is added the the list.
@ -196,12 +206,16 @@ class QueryResults {
}
/**
* Generate an ingest inbox message for given keyword in given file
* Generate and post an ingest inbox message for the given keyword in the
* given content.
*
* @param written
* @param hitFile
* @param artifact The keyword hit artifact.
* @param hitContent The content that the hit is in.
*
* @throws TskCoreException If there is a problem generating or posting the
* inbox message.
*/
private void writeSingleFileInboxMessage(KeywordCachedArtifact written, Content hitContent) {
private void writeSingleFileInboxMessage(BlackboardArtifact artifact, Content hitContent) throws TskCoreException {
StringBuilder subjectSb = new StringBuilder();
StringBuilder detailsSb = new StringBuilder();
@ -210,24 +224,24 @@ class QueryResults {
} else {
subjectSb.append(NbBundle.getMessage(this.getClass(), "KeywordSearchIngestModule.kwHitLbl"));
}
String uniqueKey = null;
BlackboardAttribute attr = written.getAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID());
BlackboardAttribute attr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD));
if (attr != null) {
final String keyword = attr.getValueString();
subjectSb.append(keyword);
uniqueKey = keyword.toLowerCase();
//details
detailsSb.append("<table border='0' cellpadding='4' width='280'>"); //NON-NLS
//hit
detailsSb.append("<tr>"); //NON-NLS
detailsSb.append(NbBundle.getMessage(this.getClass(), "KeywordSearchIngestModule.kwHitThLbl"));
detailsSb.append("<td>").append(EscapeUtil.escapeHtml(keyword)).append("</td>"); //NON-NLS
detailsSb.append("</tr>"); //NON-NLS
}
//details
detailsSb.append("<table border='0' cellpadding='4' width='280'>"); //NON-NLS
//hit
detailsSb.append("<tr>"); //NON-NLS
detailsSb.append(NbBundle.getMessage(this.getClass(), "KeywordSearchIngestModule.kwHitThLbl"));
detailsSb.append("<td>").append(EscapeUtil.escapeHtml(attr.getValueString())).append("</td>"); //NON-NLS
detailsSb.append("</tr>"); //NON-NLS
//preview
attr = written.getAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW.getTypeID());
attr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW));
if (attr != null) {
detailsSb.append("<tr>"); //NON-NLS
detailsSb.append(NbBundle.getMessage(this.getClass(), "KeywordSearchIngestModule.previewThLbl"));
@ -247,16 +261,17 @@ class QueryResults {
detailsSb.append("</tr>"); //NON-NLS
//list
attr = written.getAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID());
attr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME));
if (attr != null) {
detailsSb.append("<tr>"); //NON-NLS
detailsSb.append(NbBundle.getMessage(this.getClass(), "KeywordSearchIngestModule.listThLbl"));
detailsSb.append("<td>").append(attr.getValueString()).append("</td>"); //NON-NLS
detailsSb.append("</tr>"); //NON-NLS
}
//regex
if (!keywordSearchQuery.isLiteral()) {
attr = written.getAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID());
attr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP));
if (attr != null) {
detailsSb.append("<tr>"); //NON-NLS
detailsSb.append(NbBundle.getMessage(this.getClass(), "KeywordSearchIngestModule.regExThLbl"));
@ -264,9 +279,9 @@ class QueryResults {
detailsSb.append("</tr>"); //NON-NLS
}
}
detailsSb.append("</table>"); //NON-NLS
IngestServices.getInstance().postMessage(IngestMessage.createDataMessage(MODULE_NAME, subjectSb.toString(), detailsSb.toString(), uniqueKey, written.getArtifact()));
IngestServices.getInstance().postMessage(IngestMessage.createDataMessage(MODULE_NAME, subjectSb.toString(), detailsSb.toString(), uniqueKey, artifact));
}
}

View File

@ -19,15 +19,11 @@
package org.sleuthkit.autopsy.keywordsearch;
import com.google.common.base.CharMatcher;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;
import java.util.ArrayList;
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.regex.Matcher;
import java.util.regex.Pattern;
@ -52,11 +48,9 @@ import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Account;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.datamodel.TskException;
/**
* The RegexQuery class supports issuing regular expression queries against a
@ -87,8 +81,6 @@ final class RegexQuery implements KeywordSearchQuery {
private final int MIN_EMAIL_ADDR_LENGTH = 8;
private final ListMultimap<Keyword, KeywordHit> hitsMultiMap = ArrayListMultimap.create();
// Lucene regular expressions do not support the following Java predefined
// and POSIX character classes. There are other valid Java character classes
// that are not supported by Lucene but we do not check for all of them.
@ -191,8 +183,9 @@ final class RegexQuery implements KeywordSearchQuery {
solrQuery.setSort(SortClause.asc(Server.Schema.ID.toString()));
String cursorMark = CursorMarkParams.CURSOR_MARK_START;
SolrDocumentList resultList ;
SolrDocumentList resultList;
boolean allResultsProcessed = false;
QueryResults results = new QueryResults(this);
while (!allResultsProcessed) {
try {
@ -204,9 +197,15 @@ final class RegexQuery implements KeywordSearchQuery {
try {
List<KeywordHit> keywordHits = createKeywordHits(resultDoc);
for (KeywordHit hit : keywordHits) {
hitsMultiMap.put(new Keyword(hit.getHit(), true, true, originalKeyword.getListName(), originalKeyword.getOriginalTerm()), hit);
Keyword keywordInstance = new Keyword(hit.getHit(), true, true, originalKeyword.getListName(), originalKeyword.getOriginalTerm());
List<KeywordHit> hitsForKeyword = results.getResults(keywordInstance);
if (hitsForKeyword == null) {
hitsForKeyword = new ArrayList<>();
results.addResult(keywordInstance, hitsForKeyword);
}
hitsForKeyword.add(hit);
}
} catch (TskCoreException ex) {
} catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Error creating keyword hits", ex); //NON-NLS
}
}
@ -221,10 +220,7 @@ final class RegexQuery implements KeywordSearchQuery {
MessageNotifyUtil.Notify.error(NbBundle.getMessage(Server.class, "Server.query.exception.msg", keywordString), ex.getCause().getMessage());
}
}
QueryResults results = new QueryResults(this);
for (Keyword k : hitsMultiMap.keySet()) {
results.addResult(k, hitsMultiMap.get(k));
}
return results;
}
@ -287,8 +283,8 @@ final class RegexQuery implements KeywordSearchQuery {
}
/*
* If searching for credit card account numbers, do a Luhn check
* on the term and discard it if it does not pass.
* If searching for credit card account numbers, do a Luhn
* check on the term and discard it if it does not pass.
*/
if (originalKeyword.getArtifactAttributeType() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER) {
Matcher ccnMatcher = CREDIT_CARD_NUM_PATTERN.matcher(hit);
@ -319,10 +315,14 @@ final class RegexQuery implements KeywordSearchQuery {
} catch (TskCoreException ex) {
throw ex;
} catch (Throwable error) {
/* NOTE: Matcher.find() is known to throw StackOverflowError in rare cases (see JIRA-2700).
StackOverflowError is an error, not an exception, and therefore needs to be caught
as a Throwable. When this occurs we should re-throw the error as TskCoreException so that it is
logged by the calling method and move on to the next Solr document. */
/*
* NOTE: Matcher.find() is known to throw StackOverflowError in rare
* cases (see JIRA-2700). StackOverflowError is an error, not an
* exception, and therefore needs to be caught as a Throwable. When
* this occurs we should re-throw the error as TskCoreException so
* that it is logged by the calling method and move on to the next
* Solr document.
*/
throw new TskCoreException("Failed to create keyword hits for Solr document id " + docId + " due to " + error.getMessage());
}
return hits;
@ -373,50 +373,15 @@ final class RegexQuery implements KeywordSearchQuery {
return escapedQuery;
}
/**
* Get a unique, comma separated list of document ids that match the given
* hit for the same object.
*
* @param keyword The keyword object that resulted in one or more hits.
* @param hit The specific hit for which we want to identify all other
* chunks that match the keyword
*
* @return A comma separated list of unique document ids.
*/
private String getDocumentIds(Keyword keyword, KeywordHit hit) {
Set<String> documentIds = new HashSet<>();
for (KeywordHit h : hitsMultiMap.get(keyword)) {
// Add the document id only if it is for the same object as the
// given hit and we haven't already seen it.
if (h.getSolrObjectId() == hit.getSolrObjectId() && !documentIds.contains(h.getSolrDocumentId())) {
documentIds.add(h.getSolrDocumentId());
}
}
return StringUtils.join(documentIds, ",");
}
/**
* Converts the keyword hits for a given search term into artifacts.
*
* @param foundKeyword The keyword that was found by the regex search.
* @param hit The keyword hit.
* @param snippet The document snippet that contains the hit
* @param listName The name of the keyword list that contained the
* keyword for which the hit was found.
*
*
*
* @return An object that wraps an artifact and a mapping by id of its
* attributes.
*/
// TODO: Are we actually making meaningful use of the KeywordCachedArtifact
// class?
@Override
public KeywordCachedArtifact writeSingleFileHitsToBlackBoard(Keyword foundKeyword, KeywordHit hit, String snippet, String listName) {
public BlackboardArtifact writeSingleFileHitsToBlackBoard(Content content, Keyword foundKeyword, KeywordHit hit, String snippet, String listName) {
final String MODULE_NAME = KeywordSearchModuleFactory.getModuleName();
if (content == null) {
LOGGER.log(Level.WARNING, "Error adding artifact for keyword hit to blackboard"); //NON-NLS
return null;
}
/*
* Create either a "plain vanilla" keyword hit artifact with keyword and
* regex attributes, or a credit card account artifact with attributes
@ -429,8 +394,7 @@ final class RegexQuery implements KeywordSearchQuery {
attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD, MODULE_NAME, foundKeyword.getSearchTerm()));
attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP, MODULE_NAME, getQueryString()));
try {
newArtifact = hit.getContent().newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT);
newArtifact = content.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT);
} catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Error adding artifact for keyword hit to blackboard", ex); //NON-NLS
return null;
@ -455,7 +419,7 @@ final class RegexQuery implements KeywordSearchQuery {
if (hit.isArtifactHit()) {
LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for artifact keyword hit: term = %s, snippet = '%s', artifact id = %d", foundKeyword.getSearchTerm(), hit.getSnippet(), hit.getArtifact().getArtifactID())); //NON-NLS
} else {
LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for content keyword hit: term = %s, snippet = '%s', object id = %d", foundKeyword.getSearchTerm(), hit.getSnippet(), hit.getContent().getId())); //NON-NLS
LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for content keyword hit: term = %s, snippet = '%s', object id = %d", foundKeyword.getSearchTerm(), hit.getSnippet(), hit.getContentID())); //NON-NLS
}
return null;
}
@ -491,8 +455,8 @@ final class RegexQuery implements KeywordSearchQuery {
* document id to support showing just the chunk that contained the
* hit.
*/
if (hit.getContent() instanceof AbstractFile) {
AbstractFile file = (AbstractFile) hit.getContent();
if (content instanceof AbstractFile) {
AbstractFile file = (AbstractFile) content;
if (file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS
|| file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS) {
attributes.add(new BlackboardAttribute(KEYWORD_SEARCH_DOCUMENT_ID, MODULE_NAME, hit.getSolrDocumentId()));
@ -503,7 +467,7 @@ final class RegexQuery implements KeywordSearchQuery {
* Create an account artifact.
*/
try {
newArtifact = hit.getContent().newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT);
newArtifact = content.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT);
} catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Error adding artifact for account to blackboard", ex); //NON-NLS
return null;
@ -524,9 +488,7 @@ final class RegexQuery implements KeywordSearchQuery {
try {
newArtifact.addAttributes(attributes);
KeywordCachedArtifact writeResult = new KeywordCachedArtifact(newArtifact);
writeResult.add(attributes);
return writeResult;
return newArtifact;
} catch (TskCoreException e) {
LOGGER.log(Level.SEVERE, "Error adding bb attributes for terms search artifact", e); //NON-NLS
return null;
@ -554,7 +516,7 @@ final class RegexQuery implements KeywordSearchQuery {
* hit and turns them into artifact attributes. The track 1 data has the
* same fields as the track two data, plus the account holder's name.
*
* @param attributesMap A map of artifact attribute objects, used to avoid
* @param attributeMap A map of artifact attribute objects, used to avoid
* creating duplicate attributes.
* @param matcher A matcher for the snippet.
*/
@ -567,12 +529,13 @@ final class RegexQuery implements KeywordSearchQuery {
* Creates an attribute of the the given type to the given artifact with a
* value parsed from the snippet for a credit account number hit.
*
* @param attributesMap A map of artifact attribute objects, used to avoid
* @param attributeMap A map of artifact attribute objects, used to avoid
* creating duplicate attributes.
* @param attrType The type of attribute to create.
* @param groupName The group name of the regular expression that was
* used to parse the attribute data.
* @param matcher A matcher for the snippet.
*/
static private void addAttributeIfNotAlreadyCaptured(Map<BlackboardAttribute.Type, BlackboardAttribute> attributeMap, BlackboardAttribute.ATTRIBUTE_TYPE attrType, String groupName, Matcher matcher) {
BlackboardAttribute.Type type = new BlackboardAttribute.Type(attrType);
@ -589,5 +552,4 @@ final class RegexQuery implements KeywordSearchQuery {
return null;
});
}
}

View File

@ -19,7 +19,6 @@
package org.sleuthkit.autopsy.keywordsearch;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@ -46,7 +45,6 @@ import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.coreutils.StopWatch;
import org.sleuthkit.autopsy.ingest.IngestMessage;
import org.sleuthkit.autopsy.ingest.IngestServices;
import org.sleuthkit.datamodel.BlackboardArtifact;
/**
* Singleton keyword search manager: Launches search threads for each job and
@ -482,9 +480,7 @@ public final class SearchRunner {
if (!newResults.getKeywords().isEmpty()) {
// Write results to BB
//new artifacts created, to report to listeners
Collection<BlackboardArtifact> newArtifacts = new ArrayList<>();
//scale progress bar more more granular, per result sub-progress, within per keyword
int totalUnits = newResults.getKeywords().size();
subProgresses[keywordsSearched].start(totalUnits);
@ -496,7 +492,7 @@ public final class SearchRunner {
subProgresses[keywordsSearched].progress(keywordList.getName() + ": " + queryDisplayStr, unitProgress);
// Create blackboard artifacts
newArtifacts = newResults.writeAllHitsToBlackBoard(null, subProgresses[keywordsSearched], this, keywordList.getIngestMessages());
newResults.writeAllHitsToBlackBoard(null, subProgresses[keywordsSearched], this, keywordList.getIngestMessages());
} //if has results

View File

@ -824,7 +824,7 @@ public class Server {
return new Core(coreName, theCase.getCaseType(), index);
} catch (SolrServerException | SolrException | IOException ex) {
} catch (Exception ex) {
throw new KeywordSearchModuleException(NbBundle.getMessage(this.getClass(), "Server.openCore.exception.cantOpen.msg"), ex);
}
}
@ -1231,6 +1231,8 @@ public class Server {
// the server to access a core needs to be built from a URL with the
// core in it, and is only good for core-specific operations
private final HttpSolrServer solrCore;
private final int QUERY_TIMEOUT_MILLISECONDS = 86400000; // 24 Hours = 86,400,000 Milliseconds
private Core(String name, CaseType caseType, Index index) {
this.name = name;
@ -1240,7 +1242,8 @@ public class Server {
this.solrCore = new HttpSolrServer(currentSolrServer.getBaseURL() + "/" + name); //NON-NLS
//TODO test these settings
//solrCore.setSoTimeout(1000 * 60); // socket read timeout, make large enough so can index larger files
// socket read timeout, make large enough so can index larger files
solrCore.setSoTimeout(QUERY_TIMEOUT_MILLISECONDS);
//solrCore.setConnectionTimeout(1000);
solrCore.setDefaultMaxConnectionsPerHost(2);
solrCore.setMaxTotalConnections(5);

View File

@ -42,6 +42,7 @@ import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
@ -96,7 +97,7 @@ final class TermsComponentQuery implements KeywordSearchQuery {
+ "(?:\\?" // end sentinal: ? //NON-NLS
+ "(?<LRC>.)" //longitudinal redundancy check //NON-NLS
+ "?)?)?)?)?)?");//close nested optional groups //NON-NLS
static final Pattern CREDIT_CARD_TRACK2_PATTERN = Pattern.compile(
static final Pattern CREDIT_CARD_TRACK2_PATTERN = Pattern.compile(
/*
* Track 2 is numeric plus six punctuation symbolls :;<=>?
*
@ -115,7 +116,7 @@ final class TermsComponentQuery implements KeywordSearchQuery {
+ "(?:[:;<=>?]" //end sentinel //NON-NLS
+ "(?<LRC>.)" //longitudinal redundancy check //NON-NLS
+ "?)?)?)?)?)?"); //close nested optional groups //NON-NLS
static final BlackboardAttribute.Type KEYWORD_SEARCH_DOCUMENT_ID = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_DOCUMENT_ID);
static final BlackboardAttribute.Type KEYWORD_SEARCH_DOCUMENT_ID = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_DOCUMENT_ID);
/**
* Constructs an object that implements a regex query that will be performed
@ -315,27 +316,12 @@ final class TermsComponentQuery implements KeywordSearchQuery {
}
results.addResult(new Keyword(term.getTerm(), false, true, originalKeyword.getListName(), originalKeyword.getOriginalTerm()), new ArrayList<>(termHits));
}
return results;
return results;
}
/**
* Converts the keyword hits for a given search term into artifacts.
*
* @param foundKeyword The keyword that was found by the search.
* @param hit The keyword hit.
* @param snippet The document snippet that contains the hit
* @param listName The name of the keyword list that contained the keyword
* for which the hit was found.
*
*
*
* @return An object that wraps an artifact and a mapping by id of its
* attributes.
*/
// TODO: Are we actually making meaningful use of the KeywordCachedArtifact
// class?
@Override
public KeywordCachedArtifact writeSingleFileHitsToBlackBoard(Keyword foundKeyword, KeywordHit hit, String snippet, String listName) {
public BlackboardArtifact writeSingleFileHitsToBlackBoard(Content content, Keyword foundKeyword, KeywordHit hit, String snippet, String listName) {
/*
* Create either a "plain vanilla" keyword hit artifact with keyword and
* regex attributes, or a credit card account artifact with attributes
@ -347,9 +333,9 @@ final class TermsComponentQuery implements KeywordSearchQuery {
if (originalKeyword.getArtifactAttributeType() != ATTRIBUTE_TYPE.TSK_CARD_NUMBER) {
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD, MODULE_NAME, foundKeyword.getSearchTerm()));
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP, MODULE_NAME, originalKeyword.getSearchTerm()));
try {
newArtifact = hit.getContent().newArtifact(ARTIFACT_TYPE.TSK_KEYWORD_HIT);
newArtifact = content.newArtifact(ARTIFACT_TYPE.TSK_KEYWORD_HIT);
} catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Error adding artifact for keyword hit to blackboard", ex); //NON-NLS
@ -375,7 +361,7 @@ final class TermsComponentQuery implements KeywordSearchQuery {
if (hit.isArtifactHit()) {
LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for artifact keyword hit: term = %s, snippet = '%s', artifact id = %d", searchTerm, hit.getSnippet(), hit.getArtifact().getArtifactID())); //NON-NLS
} else {
LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for content keyword hit: term = %s, snippet = '%s', object id = %d", searchTerm, hit.getSnippet(), hit.getContent().getId())); //NON-NLS
LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for content keyword hit: term = %s, snippet = '%s', object id = %d", searchTerm, hit.getSnippet(), hit.getContentID())); //NON-NLS
}
return null;
}
@ -411,8 +397,8 @@ final class TermsComponentQuery implements KeywordSearchQuery {
* document id to support showing just the chunk that contained the
* hit.
*/
if (hit.getContent() instanceof AbstractFile) {
AbstractFile file = (AbstractFile) hit.getContent();
if (content instanceof AbstractFile) {
AbstractFile file = (AbstractFile)content;
if (file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS
|| file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS) {
attributes.add(new BlackboardAttribute(KEYWORD_SEARCH_DOCUMENT_ID, MODULE_NAME, hit.getSolrDocumentId()));
@ -423,7 +409,7 @@ final class TermsComponentQuery implements KeywordSearchQuery {
* Create an account artifact.
*/
try {
newArtifact = hit.getContent().newArtifact(ARTIFACT_TYPE.TSK_ACCOUNT);
newArtifact = content.newArtifact(ARTIFACT_TYPE.TSK_ACCOUNT);
} catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Error adding artifact for account to blackboard", ex); //NON-NLS
return null;
@ -442,12 +428,10 @@ final class TermsComponentQuery implements KeywordSearchQuery {
// TermsComponentQuery is now being used exclusively for substring searches.
attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE, MODULE_NAME, KeywordSearch.QueryType.SUBSTRING.ordinal()));
try {
newArtifact.addAttributes(attributes);
KeywordCachedArtifact writeResult = new KeywordCachedArtifact(newArtifact);
writeResult.add(attributes);
return writeResult;
return newArtifact;
} catch (TskCoreException e) {
LOGGER.log(Level.SEVERE, "Error adding bb attributes for terms search artifact", e); //NON-NLS
return null;

View File

@ -94,6 +94,7 @@ public class AutopsyTestCases {
public void testNewCaseWizardOpen(String title) {
logger.info("New Case");
resetTimeouts("WindowWaiter.WaitWindowTimeout", 240000);
NbDialogOperator nbdo = new NbDialogOperator(title);
JButtonOperator jbo = new JButtonOperator(nbdo, 0); // the "New Case" button
jbo.pushNoBlock();
@ -121,8 +122,8 @@ public class AutopsyTestCases {
*/
new Timeout("pausing", 120000).sleep();
logger.info("Starting Add Image process");
resetTimeouts("WindowWaiter.WaitWindowTimeOut", 240000);
WizardOperator wo = new WizardOperator("Add Data Source");
wo.setTimeouts(resetTimeouts("WindowWaiter.WaitWindowTimeOut", 240000));
while(!wo.btNext().isEnabled()){
new Timeout("pausing", 1000).sleep(); // give it a second till the Add Data Source dialog enabled
}
@ -299,6 +300,7 @@ public class AutopsyTestCases {
public void testGenerateReportButton() throws IOException {
logger.info("Generate Report Button");
resetTimeouts("ComponentOperator.WaitComponentTimeout", 240000);
JDialog reportDialog = JDialogOperator.waitJDialog("Generate Report", false, false);
JDialogOperator reportDialogOperator = new JDialogOperator(reportDialog);
JListOperator listOperator = new JListOperator(reportDialogOperator);
@ -307,12 +309,10 @@ public class AutopsyTestCases {
Date date = new Date();
String datenotime = dateFormat.format(date);
listOperator.clickOnItem(0, 1);
new Timeout("pausing", 2000).sleep();
jbo0.pushNoBlock();
new Timeout("pausing", 2000).sleep();
JButtonOperator jbo1 = new JButtonOperator(reportDialogOperator, "Finish");
jbo1.pushNoBlock();
new Timeout("pausing", 1000).sleep();
JDialog previewDialog = JDialogOperator.waitJDialog("Progress", false, false);
screenshot("Progress");
JDialogOperator previewDialogOperator = new JDialogOperator(previewDialog);

View File

@ -0,0 +1,11 @@
#Updated by build script
#Thu, 22 Jun 2017 08:50:21 -0400
LBL_splash_window_title=Starting Autopsy
SPLASH_HEIGHT=314
SPLASH_WIDTH=538
SplashProgressBarBounds=0,308,538,6
SplashRunningTextBounds=0,289,538,18
SplashRunningTextColor=0x0
SplashRunningTextFontSize=19
currentVersion=Autopsy 4.4.1

View File

@ -0,0 +1,4 @@
#Updated by build script
#Thu, 22 Jun 2017 08:50:21 -0400
CTL_MainWindow_Title=Autopsy 4.4.1
CTL_MainWindow_Title_No_Project=Autopsy 4.4.1

View File

@ -170,7 +170,7 @@
<!-- Update configuration file to include jre -->
<property name="inst.property.file" value="${inst-path}/etc/${app.name}.conf" />
<!-- Sets max heap size to be ${jvm.max.mem} which is set in the run-ai-(32/64) target -->
<var name="jvm.args" value="&quot;--branding ${app.name} -J-Xms24m -J-Xmx${jvm.max.mem}m -J-XX:MaxPermSize=128M -J-Xverify:none &quot;" />
<var name="jvm.args" value="&quot;--branding ${app.name} -J-Xms24m -J-Xmx${jvm.max.mem}m -J-XX:MaxPermSize=128M -J-Xverify:none -J-XX:+UseG1GC -J-XX:+UseStringDeduplication &quot;" />
<propertyfile file="${inst.property.file}">
<!-- Note: can be higher on 64 bit systems, should be in sync with project.properties -->
<entry key="default_options" value="@JVM_OPTIONS" />

View File

@ -92,7 +92,7 @@
<property name="app.property.file" value="${zip-tmp}/${app.name}/etc/${app.name}.conf" />
<!-- for Japanese localized version add option: -Duser.language=ja -->
<property name="jvm.options" value="&quot;--branding ${app.name} -J-Xms24m -J-XX:MaxPermSize=128M -J-Xverify:none -J-Xdock:name=${app.title}&quot;" />
<property name="jvm.options" value="&quot;--branding ${app.name} -J-Xms24m -J-XX:MaxPermSize=128M -J-Xverify:none -J-XX:+UseG1GC -J-XX:+UseStringDeduplication -J-Xdock:name=${app.title}&quot;" />
<propertyfile file="${app.property.file}">
<!-- Note: can be higher on 64 bit systems, should be in sync with project.properties -->
<entry key="default_options" value="@JVM_OPTIONS" />

View File

@ -4,7 +4,7 @@ app.title=Autopsy
### lowercase version of above
app.name=${branding.token}
### if left unset, version will default to today's date
app.version=4.4.1
app.version=4.4.2
### build.type must be one of: DEVELOPMENT, RELEASE
#build.type=RELEASE
build.type=DEVELOPMENT
@ -16,7 +16,7 @@ update_versions=false
#custom JVM options
#Note: can be higher on 64 bit systems, should be in sync with build.xml
# for Japanese version add: -J-Duser.language=ja
run.args.extra=-J-Xms24m -J-XX:MaxPermSize=128M -J-Xverify:none
run.args.extra=-J-Xms24m -J-XX:MaxPermSize=128M -J-Xverify:none -J-XX:+UseG1GC -J-XX:+UseStringDeduplication
auxiliary.org-netbeans-modules-apisupport-installer.license-type=apache.v2
auxiliary.org-netbeans-modules-apisupport-installer.os-linux=false
auxiliary.org-netbeans-modules-apisupport-installer.os-macosx=false

13
ruleset.xml Normal file
View File

@ -0,0 +1,13 @@
<?xml version="1.0"?>
<ruleset name="Custom ruleset"
xmlns="http://pmd.sf.net/ruleset/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd"
xsi:noNamespaceSchemaLocation="http://pmd.sf.net/ruleset_xml_schema.xsd">
<description>
Ruleset used by Autopsy
</description>
<rule ref="rulesets/java/unusedcode.xml"/>
</ruleset>

View File

@ -17,7 +17,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from tskdbdiff import TskDbDiff, TskDbDiffException
from tskdbdiff import TskDbDiff, TskDbDiffException, PGSettings
import codecs
import datetime
import logging
@ -132,17 +132,9 @@ class TestRunner(object):
"""
if isMultiUser:
if test_config.output_parent_dir.endswith("single_user"):
test_config.output_parent_dir = test_config.output_parent_dir.replace("single_user", "multi_user")
else:
test_config.output_parent_dir += "\\multi_user"
test_config.userCaseType='multi'
test_config.testUserCase='multi'
else:
if test_config.output_parent_dir.endswith("multi_user"):
test_config.output_parent_dir = test_config.output_parent_dir.replace("multi_user", "single_user")
else:
test_config.output_parent_dir += "\\single_user"
test_config.userCaseType='single'
test_config.testUserCase='single'
test_config._init_logs()
@ -191,7 +183,7 @@ class TestRunner(object):
Errors.print_error("No image had any gold; Regression did not run")
exit(1)
if not all([ test_data.overall_passed for test_data in test_data_list ]):
if not (test_config.args.rebuild or all([ test_data.overall_passed for test_data in test_data_list ])):
html = open(test_config.html_log)
Errors.add_errors_out(html.name)
html.close()
@ -224,16 +216,17 @@ class TestRunner(object):
if ant_line.startswith("BUILD FAILED") or "fatal error" in ant_ignoreCase or "crashed" in ant_ignoreCase:
Errors.print_error("Autopsy test failed. Please check the build log antlog.txt for details.")
sys.exit(1)
# exit if .db was not created
if not file_exists(test_data.get_db_path(DBType.OUTPUT)):
# exit if a single-user case and the local .db file was not created
if not file_exists(test_data.get_db_path(DBType.OUTPUT)) and not test_data.isMultiUser:
Errors.print_error("Autopsy did not run properly; No .db file was created")
sys.exit(1)
try:
# Dump the database before we diff or use it for rebuild
TskDbDiff.dump_output_db(test_data.get_db_path(DBType.OUTPUT), test_data.get_db_dump_path(DBType.OUTPUT),
test_data.get_sorted_data_path(DBType.OUTPUT))
db_file = test_data.get_db_path(DBType.OUTPUT)
TskDbDiff.dump_output_db(db_file, test_data.get_db_dump_path(DBType.OUTPUT),
test_data.get_sorted_data_path(DBType.OUTPUT), test_data.isMultiUser, test_data.pgSettings)
except sqlite3.OperationalError as e:
Errors.print_error("Ingest did not run properly.\nMake sure no other instances of Autopsy are open and try again.")
Errors.print_error("Ingest did not run properly.\nMake sure no other instances of Autopsy are open and try again." + str(e))
sys.exit(1)
# merges logs into a single log for later diff / rebuild
@ -374,7 +367,8 @@ class TestRunner(object):
# Copy files to gold
try:
shutil.copy(dbinpth, dboutpth)
if not test_data.isMultiUser: # This find the local .db file and copy it for single-user case. Multi-user case doesn't have a local db file.
shutil.copy(dbinpth, dboutpth)
if file_exists(test_data.get_sorted_data_path(DBType.OUTPUT)):
shutil.copy(test_data.get_sorted_data_path(DBType.OUTPUT), dataoutpth)
shutil.copy(dbdumpinpth, dbdumpoutpth)
@ -444,7 +438,7 @@ class TestRunner(object):
test_data.ant.append("-Dkeyword_path=" + test_config.keyword_path)
test_data.ant.append("-Dnsrl_path=" + test_config.nsrl_path)
test_data.ant.append("-Dgold_path=" + test_config.gold)
if re.match('^[\w]:', test_data.output_path) == None or test_data.output_path.startswith('/'):
if (re.match('^[\w]:', test_data.output_path) == None and not test_data.output_path.startswith("\\\\")) or test_data.output_path.startswith('/'):
test_data.ant.append("-Dout_path=" + make_local_path(test_data.output_path))
else:
test_data.ant.append("-Dout_path=" + test_data.output_path)
@ -459,7 +453,7 @@ class TestRunner(object):
test_data.ant.append("-DsolrPort=" + str(test_config.solrPort))
test_data.ant.append("-DmessageServiceHost=" + test_config.messageServiceHost)
test_data.ant.append("-DmessageServicePort=" + str(test_config.messageServicePort))
if test_config.userCaseType == "multi":
if test_data.isMultiUser:
test_data.ant.append("-DisMultiUser=true")
# Note: test_data has autopys_version attribute, but we couldn't see it from here. It's set after run ingest.
autopsyVersionPath = os.path.join("..", "..", "nbproject", "project.properties")
@ -478,7 +472,7 @@ class TestRunner(object):
Errors.print_out("Ingesting Image:\n" + test_data.image_file + "\n")
Errors.print_out("CMD: " + " ".join(test_data.ant))
Errors.print_out("Starting test...\n")
if re.match('^[\w]:', test_data.main_config.output_dir) == None or test_data.main_config.output_dir.startswith('/'):
if (re.match('^[\w]:', test_data.main_config.output_dir) == None and not test_data.main_config.output_dir.startswith("\\\\")) or test_data.main_config.output_dir.startswith('/'):
antoutpth = make_local_path(test_data.main_config.output_dir, "antRunOutput.txt")
else:
antoutpth = test_data.main_config.output_dir + "\\antRunOutput.txt"
@ -572,6 +566,8 @@ class TestData(object):
self.image_file = str(image)
self.image = get_image_name(self.image_file)
self.image_name = self.image
# userCaseType
self.isMultiUser = True if self.main_config.testUserCase == "multi" else False
# Directory structure and files
self.output_path = make_path(self.main_config.output_dir, self.image_name)
self.autopsy_data_file = make_path(self.output_path, self.image_name + "Autopsy_data.txt")
@ -580,13 +576,16 @@ class TestData(object):
self.test_dbdump = make_path(self.output_path, self.image_name +
"-DBDump.txt")
self.common_log_path = make_path(self.output_path, self.image_name + "-Exceptions.txt")
self.reports_dir = make_path(self.output_path, AUTOPSY_TEST_CASE, "Reports")
if self.isMultiUser:
self.reports_dir = make_path(self.output_path, AUTOPSY_TEST_CASE, socket.gethostname(), "Reports")
self.solr_index = make_path(self.output_path, AUTOPSY_TEST_CASE, socket.gethostname(), "ModuleOutput", "KeywordSearch")
else:
self.reports_dir = make_path(self.output_path, AUTOPSY_TEST_CASE, "Reports")
self.solr_index = make_path(self.output_path, AUTOPSY_TEST_CASE, "ModuleOutput", "KeywordSearch")
self.gold_data_dir = make_path(self.main_config.gold, self.image_name)
self.gold_archive = make_path(self.main_config.gold,
self.image_name + "-archive.zip")
self.logs_dir = make_path(self.output_path, "logs")
self.solr_index = make_path(self.output_path, AUTOPSY_TEST_CASE,
"ModuleOutput", "KeywordSearch")
# Results and Info
self.html_report_passed = False
self.errors_diff_passed = False
@ -611,6 +610,8 @@ class TestData(object):
self.printout = []
# autopsyPlatform
self.autopsyPlatform = str(self.main_config.autopsyPlatform)
# postgreSQL db connection data settings
self.pgSettings = PGSettings(self.main_config.dbHost, self.main_config.dbPort, self.main_config.dbUserName, self.main_config.dbPassword)
def ant_to_string(self):
string = ""
@ -627,7 +628,12 @@ class TestData(object):
if(db_type == DBType.GOLD):
db_path = make_path(self.gold_data_dir, DB_FILENAME)
elif(db_type == DBType.OUTPUT):
db_path = make_path(self.main_config.output_dir, self.image_name, AUTOPSY_TEST_CASE, DB_FILENAME)
if self.isMultiUser:
case_path = make_path(self.main_config.output_dir, self.image_name, AUTOPSY_TEST_CASE, "AutopsyTestCase.aut")
parsed = parse(case_path)
db_path = parsed.getElementsByTagName("CaseDatabase")[0].firstChild.data
else:
db_path = make_path(self.main_config.output_dir, self.image_name, AUTOPSY_TEST_CASE, DB_FILENAME)
else:
db_path = make_path(self.main_config.output_dir, self.image_name, AUTOPSY_TEST_CASE, BACKUP_DB_FILENAME)
return db_path
@ -735,9 +741,11 @@ class TestConfiguration(object):
self.args = args
# Default output parent dir
self.output_parent_dir = make_path("..", "output", "results")
self.output_dir = ""
self.output_dir = ""
self.singleUser_outdir = ""
self.input_dir = make_local_path("..","input")
self.gold = make_path("..", "output", "gold")
self.gold = ""
self.singleUser_gold = make_path("..", "output", "gold", "single_user")
# Logs:
self.csv = ""
self.global_csv = ""
@ -769,7 +777,11 @@ class TestConfiguration(object):
self.messageServiceHost = ""
self.messageServicePort = ""
self.userCaseType = "Both"
self.multiUser_gold = make_path("..", "output", "gold", "multi_user")
self.multiUser_outdir = ""
# Test runner user case:
self.testUserCase = ""
if not self.args.single:
self._load_config_file(self.args.config_file)
else:
@ -789,21 +801,21 @@ class TestConfiguration(object):
parsed_config = parse(config_file)
logres = []
counts = {}
if parsed_config.getElementsByTagName("userCaseType"):
self.userCaseType = parsed_config.getElementsByTagName("userCaseType")[0].getAttribute("value").encode().decode("utf_8")
if parsed_config.getElementsByTagName("indir"):
self.input_dir = parsed_config.getElementsByTagName("indir")[0].getAttribute("value").encode().decode("utf_8")
if parsed_config.getElementsByTagName("outdir"):
self.output_parent_dir = parsed_config.getElementsByTagName("outdir")[0].getAttribute("value").encode().decode("utf_8")
if parsed_config.getElementsByTagName("global_csv"):
self.global_csv = parsed_config.getElementsByTagName("global_csv")[0].getAttribute("value").encode().decode("utf_8")
if re.match('^[\w]:', self.global_csv) == None or self.global_csv.startswith('/'):
self.global_csv = make_local_path(self.global_csv)
if parsed_config.getElementsByTagName("golddir"):
self.gold = parsed_config.getElementsByTagName("golddir")[0].getAttribute("value").encode().decode("utf_8")
if parsed_config.getElementsByTagName("singleUser_outdir"):
self.singleUser_outdir = parsed_config.getElementsByTagName("singleUser_outdir")[0].getAttribute("value").encode().decode("utf_8")
if parsed_config.getElementsByTagName("singleUser_golddir"):
self.singleUser_gold = parsed_config.getElementsByTagName("singleUser_golddir")[0].getAttribute("value").encode().decode("utf_8")
if parsed_config.getElementsByTagName("timing"):
self.timing = parsed_config.getElementsByTagName("timing")[0].getAttribute("value").encode().decode("utf_8")
if parsed_config.getElementsByTagName("autopsyPlatform"):
self.autopsyPlatform = parsed_config.getElementsByTagName("autopsyPlatform")[0].getAttribute("value").encode().decode("utf_8")
# Multi-user settings
if parsed_config.getElementsByTagName("multiUser_golddir"):
self.multiUser_gold = parsed_config.getElementsByTagName("multiUser_golddir")[0].getAttribute("value").encode().decode("utf_8")
if parsed_config.getElementsByTagName("dbHost"):
self.dbHost = parsed_config.getElementsByTagName("dbHost")[0].getAttribute("value").encode().decode("utf_8")
if parsed_config.getElementsByTagName("dbPort"):
@ -820,8 +832,8 @@ class TestConfiguration(object):
self.messageServiceHost = parsed_config.getElementsByTagName("messageServiceHost")[0].getAttribute("value").encode().decode("utf_8")
if parsed_config.getElementsByTagName("messageServicePort"):
self.messageServicePort = parsed_config.getElementsByTagName("messageServicePort")[0].getAttribute("value").encode().decode("utf_8")
if parsed_config.getElementsByTagName("userCaseType"):
self.userCaseType = parsed_config.getElementsByTagName("userCaseType")[0].getAttribute("value").encode().decode("utf_8")
if parsed_config.getElementsByTagName("multiUser_outdir"):
self.multiUser_outdir = parsed_config.getElementsByTagName("multiUser_outdir")[0].getAttribute("value").encode().decode("utf_8")
self._init_imgs(parsed_config)
self._init_build_info(parsed_config)
@ -831,7 +843,7 @@ class TestConfiguration(object):
logging.critical(traceback.format_exc())
print(traceback.format_exc())
if self.userCaseType.lower().startswith("multi"):
if self.userCaseType.lower().startswith("multi") or self.userCaseType.lower().startswith("both"):
if not self.dbHost.strip() or not self.dbPort.strip() or not self.dbUserName.strip() or not self.dbPassword.strip():
Errors.print_error("Please provide database connection information via configuration file. ")
sys.exit(1)
@ -841,18 +853,31 @@ class TestConfiguration(object):
if not self.messageServiceHost.strip() or not self.messageServicePort.strip():
Errors.print_error("Please provide ActiveMQ host name and port number via configuration file. ")
sys.exit(1)
if not self.multiUser_outdir.strip():
Errors.print_error("Please provide a shared output directory for multi-user test. ")
sys.exit(1)
def _init_logs(self):
"""Setup output folder, logs, and reporting infrastructure."""
user_case_type = self.userCaseType.lower()
if self.testUserCase == "multi":
self.output_parent_dir = self.multiUser_outdir
self.gold = self.multiUser_gold
else:
self.output_parent_dir = self.singleUser_outdir
self.gold = self.singleUser_gold
if not dir_exists(self.output_parent_dir):
print(_platform)
print(self.output_parent_dir)
os.makedirs(make_os_path(_platform, self.output_parent_dir))
self.global_csv = make_path(os.path.join(self.output_parent_dir, "Global_CSV.log"))
self.output_dir = make_path(self.output_parent_dir, time.strftime("%Y.%m.%d-%H.%M.%S"))
os.makedirs(self.output_dir)
self.csv = make_path(self.output_dir, "CSV.txt")
self.html_log = make_path(self.output_dir, "AutopsyTestCase.html")
log_name = ''
if SYS is OS.CYGWIN and (re.match('^[\w]:', self.output_dir) != None or not self.output_dir.startswith('/')):
if SYS is OS.CYGWIN and ((re.match('^[\w]:', self.output_dir) != None and self.output_dir.startswith("\\\\")) or not self.output_dir.startswith('/')):
a = ["cygpath", "-u", self.output_dir]
cygpath_output_dir = subprocess.check_output(a).decode('utf-8')
log_name = cygpath_output_dir.rstrip() + "/regression.log"
@ -860,6 +885,22 @@ class TestConfiguration(object):
log_name = self.output_dir + "\\regression.log"
logging.basicConfig(filename=log_name, level=logging.DEBUG)
# Sanity check to see if there are obvious gold images that we are not testing
if not dir_exists(self.gold):
print(self.gold)
Errors.print_error("Gold folder does not exist")
sys.exit(1)
gold_count = 0
for file in os.listdir(self.gold):
if not(file == 'tmp'):
gold_count+=1
image_count = len(self.images)
if (image_count > gold_count):
print("******Alert: There are more input images than gold standards, some images will not be properly tested.\n")
elif (image_count < gold_count):
print("******Alert: There are more gold standards than input images, this will not check all gold Standards.\n")
def _init_build_info(self, parsed_config):
"""Initializes paths that point to information necessary to run the AutopsyIngest."""
build_elements = parsed_config.getElementsByTagName("build")
@ -878,22 +919,6 @@ class TestConfiguration(object):
else:
msg = "File: " + value + " doesn't exist"
Errors.print_error(msg)
image_count = len(self.images)
# Sanity check to see if there are obvious gold images that we are not testing
if not dir_exists(self.gold):
Errors.print_error("Gold folder does not exist")
sys.exit(1)
gold_count = 0
for file in os.listdir(self.gold):
if not(file == 'tmp'):
gold_count+=1
if (image_count > gold_count):
print("******Alert: There are more input images than gold standards, some images will not be properly tested.\n")
elif (image_count < gold_count):
print("******Alert: There are more gold standards than input images, this will not check all gold Standards.\n")
#-------------------------------------------------#
# Functions relating to comparing outputs #
@ -915,7 +940,7 @@ class TestResultsDiffer(object):
gold_bb_dump = test_data.get_sorted_data_path(DBType.GOLD)
gold_dump = test_data.get_db_dump_path(DBType.GOLD)
test_data.db_diff_passed = all(TskDbDiff(output_db, gold_db, output_dir=output_dir, gold_bb_dump=gold_bb_dump,
gold_dump=gold_dump).run_diff())
gold_dump=gold_dump, isMultiUser=test_data.isMultiUser, pgSettings=test_data.pgSettings).run_diff())
# Compare Exceptions
# replace is a fucntion that replaces strings of digits with 'd'
@ -982,7 +1007,7 @@ class TestResultsDiffer(object):
# create file path for gold files inside report output folder. In case of diff, both gold and current run
# Exception.txt files are available in the report output folder. Prefix Gold- is added to the filename.
gold_file_in_output_dir = output_file[:output_file.rfind("\\")] + "Gold-" + output_file[output_file.rfind("\\")+1:]
gold_file_in_output_dir = output_file[:output_file.rfind("\\")] + "\\Gold-" + output_file[output_file.rfind("\\")+1:]
shutil.copy(gold_file, gold_file_in_output_dir)
return False
@ -1579,7 +1604,10 @@ def copy_logs(test_data):
"""
try:
# copy logs from autopsy case's Log folder
log_dir = os.path.join(test_data.output_path, AUTOPSY_TEST_CASE, "Log")
if test_data.isMultiUser:
log_dir = os.path.join(test_data.output_path, AUTOPSY_TEST_CASE, socket.gethostname(), "Log")
else:
log_dir = os.path.join(test_data.output_path, AUTOPSY_TEST_CASE, "Log")
shutil.copytree(log_dir, test_data.logs_dir)
# copy logs from userdir0/var/log

View File

@ -8,6 +8,9 @@ import os
import codecs
import datetime
import sys
import psycopg2
import psycopg2.extras
import socket
class TskDbDiff(object):
"""Compares two TSK/Autospy SQLite databases.
@ -27,7 +30,7 @@ class TskDbDiff(object):
autopsy_db_file:
gold_db_file:
"""
def __init__(self, output_db, gold_db, output_dir=None, gold_bb_dump=None, gold_dump=None, verbose=False):
def __init__(self, output_db, gold_db, output_dir=None, gold_bb_dump=None, gold_dump=None, verbose=False, isMultiUser=False, pgSettings=None):
"""Constructor for TskDbDiff.
Args:
@ -51,6 +54,12 @@ class TskDbDiff(object):
self._bb_dump = ""
self._dump = ""
self.verbose = verbose
self.isMultiUser = isMultiUser
self.pgSettings = pgSettings
if self.isMultiUser and not self.pgSettings:
print("Missing PostgreSQL database connection settings data.")
sys.exit(1)
if self.gold_bb_dump is None:
self._generate_gold_bb_dump = True
@ -68,13 +77,13 @@ class TskDbDiff(object):
# generate the gold database dumps if necessary
if self._generate_gold_dump:
TskDbDiff._dump_output_db_nonbb(self.gold_db_file, self.gold_dump)
TskDbDiff._dump_output_db_nonbb(self.gold_db_file, self.gold_dump, self.isMultiUser, self.pgSettings)
if self._generate_gold_bb_dump:
TskDbDiff._dump_output_db_bb(self.gold_db_file, self.gold_bb_dump)
TskDbDiff._dump_output_db_bb(self.gold_db_file, self.gold_bb_dump, self.isMultiUser, self.pgSettings)
# generate the output database dumps (both DB and BB)
TskDbDiff._dump_output_db_nonbb(self.output_db_file, self._dump)
TskDbDiff._dump_output_db_bb(self.output_db_file, self._bb_dump)
TskDbDiff._dump_output_db_nonbb(self.output_db_file, self._dump, self.isMultiUser, self.pgSettings)
TskDbDiff._dump_output_db_bb(self.output_db_file, self._bb_dump, self.isMultiUser, self.pgSettings)
# Compare non-BB
dump_diff_pass = self._diff(self._dump, self.gold_dump, self._dump_diff)
@ -163,7 +172,7 @@ class TskDbDiff(object):
return False
def _dump_output_db_bb(db_file, bb_dump_file):
def _dump_output_db_bb(db_file, bb_dump_file, isMultiUser, pgSettings):
"""Dumps sorted text results to the given output location.
Smart method that deals with a blackboard comparison to avoid issues
@ -175,12 +184,14 @@ class TskDbDiff(object):
"""
unsorted_dump = TskDbDiff._get_tmp_file("dump_data", ".txt")
conn = sqlite3.connect(db_file)
conn.text_factory = lambda x: x.decode("utf-8", "ignore")
conn.row_factory = sqlite3.Row
artifact_cursor = conn.cursor()
if isMultiUser:
conn, unused_db = db_connect(db_file, isMultiUser, pgSettings)
artifact_cursor = conn.cursor(cursor_factory=psycopg2.extras.DictCursor)
else: # Use Sqlite
conn = sqlite3.connect(db_file)
conn.text_factory = lambda x: x.decode("utf-8", "ignore")
conn.row_factory = sqlite3.Row
artifact_cursor = conn.cursor()
# Get the list of all artifacts (along with type and associated file)
# @@@ Could add a SORT by parent_path in here since that is how we are going to later sort it.
artifact_cursor.execute("SELECT tsk_files.parent_path, tsk_files.name, blackboard_artifact_types.display_name, blackboard_artifacts.artifact_id FROM blackboard_artifact_types INNER JOIN blackboard_artifacts ON blackboard_artifact_types.artifact_type_id = blackboard_artifacts.artifact_type_id INNER JOIN tsk_files ON tsk_files.obj_id = blackboard_artifacts.obj_id")
@ -201,14 +212,22 @@ class TskDbDiff(object):
else:
database_log.write(row["name"] + ' <artifact type="' + row["display_name"] + '" > ')
# Get attributes for this artifact
attribute_cursor = conn.cursor()
if isMultiUser:
attribute_cursor = conn.cursor(cursor_factory=psycopg2.extras.DictCursor)
else:
attribute_cursor = conn.cursor()
looptry = True
artifact_count += 1
try:
art_id = ""
art_id = str(row["artifact_id"])
attribute_cursor.execute("SELECT blackboard_attributes.source, blackboard_attribute_types.display_name, blackboard_attributes.value_type, blackboard_attributes.value_text, blackboard_attributes.value_int32, blackboard_attributes.value_int64, blackboard_attributes.value_double FROM blackboard_attributes INNER JOIN blackboard_attribute_types ON blackboard_attributes.attribute_type_id = blackboard_attribute_types.attribute_type_id WHERE artifact_id =? ORDER BY blackboard_attributes.source, blackboard_attribute_types.display_name, blackboard_attributes.value_type, blackboard_attributes.value_text, blackboard_attributes.value_int32, blackboard_attributes.value_int64, blackboard_attributes.value_double", [art_id])
# Get attributes for this artifact
if isMultiUser:
attribute_cursor.execute("SELECT blackboard_attributes.source, blackboard_attribute_types.display_name, blackboard_attributes.value_type, blackboard_attributes.value_text, blackboard_attributes.value_int32, blackboard_attributes.value_int64, blackboard_attributes.value_double FROM blackboard_attributes INNER JOIN blackboard_attribute_types ON blackboard_attributes.attribute_type_id = blackboard_attribute_types.attribute_type_id WHERE artifact_id = %s ORDER BY blackboard_attributes.source, blackboard_attribute_types.display_name, blackboard_attributes.value_type, blackboard_attributes.value_text, blackboard_attributes.value_int32, blackboard_attributes.value_int64, blackboard_attributes.value_double", [art_id])
else:
attribute_cursor.execute("SELECT blackboard_attributes.source, blackboard_attribute_types.display_name, blackboard_attributes.value_type, blackboard_attributes.value_text, blackboard_attributes.value_int32, blackboard_attributes.value_int64, blackboard_attributes.value_double FROM blackboard_attributes INNER JOIN blackboard_attribute_types ON blackboard_attributes.attribute_type_id = blackboard_attribute_types.attribute_type_id WHERE artifact_id =? ORDER BY blackboard_attributes.source, blackboard_attribute_types.display_name, blackboard_attributes.value_type, blackboard_attributes.value_text, blackboard_attributes.value_int32, blackboard_attributes.value_int64, blackboard_attributes.value_double", [art_id])
attributes = attribute_cursor.fetchall()
# Print attributes
@ -238,13 +257,13 @@ class TskDbDiff(object):
elif attr["value_type"] == 2:
attr_value_as_string = str(attr["value_int64"])
elif attr["value_type"] == 3:
attr_value_as_string = str(attr["value_double"])
attr_value_as_string = "%20.10f" % float((attr["value_double"])) #use exact format from db schema to avoid python auto format double value to (0E-10) scientific style
elif attr["value_type"] == 4:
attr_value_as_string = "bytes"
elif attr["value_type"] == 5:
attr_value_as_string = str(attr["value_int64"])
if attr["display_name"] == "Associated Artifact":
attr_value_as_string = getAssociatedArtifactType(db_file, attr_value_as_string)
attr_value_as_string = getAssociatedArtifactType(attribute_cursor, attr_value_as_string, isMultiUser)
patrn = re.compile("[\n\0\a\b\r\f]")
attr_value_as_string = re.sub(patrn, ' ', attr_value_as_string)
database_log.write('<attribute source="' + attr["source"] + '" type="' + attr["display_name"] + '" value="' + attr_value_as_string + '" />')
@ -283,7 +302,7 @@ class TskDbDiff(object):
subprocess.call(srtcmdlst)
def _dump_output_db_nonbb(db_file, dump_file):
def _dump_output_db_nonbb(db_file, dump_file, isMultiUser, pgSettings):
"""Dumps a database to a text file.
Does not dump the artifact and attributes.
@ -293,43 +312,59 @@ class TskDbDiff(object):
dump_file: a pathto_File, the location to dump the non-blackboard database items
"""
# Make a copy that we can modify
backup_db_file = TskDbDiff._get_tmp_file("tsk_backup_db", ".db")
shutil.copy(db_file, backup_db_file)
# We sometimes get situations with messed up permissions
os.chmod (backup_db_file, 0o777)
conn = sqlite3.connect(backup_db_file)
id_files_table = build_id_files_table(conn.cursor())
id_vs_parts_table = build_id_vs_parts_table(conn.cursor())
id_vs_info_table = build_id_vs_info_table(conn.cursor())
id_fs_info_table = build_id_fs_info_table(conn.cursor())
id_objects_table = build_id_objects_table(conn.cursor())
id_artifact_types_table = build_id_artifact_types_table(conn.cursor())
conn, backup_db_file = db_connect(db_file, isMultiUser, pgSettings)
id_files_table = build_id_files_table(conn.cursor(), isMultiUser)
id_vs_parts_table = build_id_vs_parts_table(conn.cursor(), isMultiUser)
id_vs_info_table = build_id_vs_info_table(conn.cursor(), isMultiUser)
id_fs_info_table = build_id_fs_info_table(conn.cursor(), isMultiUser)
id_objects_table = build_id_objects_table(conn.cursor(), isMultiUser)
id_artifact_types_table = build_id_artifact_types_table(conn.cursor(), isMultiUser)
id_obj_path_table = build_id_obj_path_table(id_files_table, id_objects_table, id_artifact_types_table)
conn.text_factory = lambda x: x.decode("utf-8", "ignore")
# Delete the blackboard tables
conn.execute("DROP TABLE blackboard_artifacts")
conn.execute("DROP TABLE blackboard_attributes")
# Write to the database dump
with codecs.open(dump_file, "wb", "utf_8") as db_log:
for line in conn.iterdump():
line = normalize_db_entry(line, id_obj_path_table, id_vs_parts_table, id_vs_info_table, id_fs_info_table, id_objects_table)
db_log.write('%s\n' % line)
# Now sort the file
#line = normalize_db_entry(line, id_obj_path_table, id_vs_parts_table, id_vs_info_table, id_fs_info_table, id_objects_table)
if isMultiUser: # Use PostgreSQL
os.environ['PGPASSWORD']=pgSettings.password
pgDump = ["pg_dump", "--inserts", "-U", pgSettings.username, "-h", pgSettings.pgHost, "-p", pgSettings.pgPort, "-d", db_file, "-E", "utf-8", "-T", "blackboard_artifacts", "-T", "blackboard_attributes", "-f", "postgreSQLDump.sql"]
subprocess.call(pgDump)
postgreSQL_db = codecs.open("postgreSQLDump.sql", "r", "utf-8")
# Write to the database dump
with codecs.open(dump_file, "wb", "utf_8") as db_log:
dump_line = ''
for line in postgreSQL_db:
line = line.strip('\r\n ')
# Deal with pg_dump result file
if line.startswith('--') or line.lower().startswith('alter') or not line: # It's comment or alter statement or empty line
continue
elif not line.endswith(';'): # Statement not finished
dump_line += line
continue
else:
dump_line += line
dump_line = normalize_db_entry(dump_line, id_obj_path_table, id_vs_parts_table, id_vs_info_table, id_fs_info_table, id_objects_table)
db_log.write('%s\n' % dump_line)
dump_line = ''
postgreSQL_db.close()
else: # use Sqlite
# Delete the blackboard tables
conn.text_factory = lambda x: x.decode("utf-8", "ignore")
conn.execute("DROP TABLE blackboard_artifacts")
conn.execute("DROP TABLE blackboard_attributes")
# Write to the database dump
with codecs.open(dump_file, "wb", "utf_8") as db_log:
for line in conn.iterdump():
line = normalize_db_entry(line, id_obj_path_table, id_vs_parts_table, id_vs_info_table, id_fs_info_table, id_objects_table)
db_log.write('%s\n' % line)
# Now sort the file
srtcmdlst = ["sort", dump_file, "-o", dump_file]
subprocess.call(srtcmdlst)
conn.close()
# cleanup the backup
os.remove(backup_db_file)
if backup_db_file:
os.remove(backup_db_file)
def dump_output_db(db_file, dump_file, bb_dump_file):
def dump_output_db(db_file, dump_file, bb_dump_file, isMultiUser, pgSettings):
"""Dumps the given database to text files for later comparison.
Args:
@ -337,8 +372,8 @@ class TskDbDiff(object):
dump_file: a pathto_File, the location to dump the non-blackboard database items
bb_dump_file: a pathto_File, the location to dump the blackboard database items
"""
TskDbDiff._dump_output_db_nonbb(db_file, dump_file)
TskDbDiff._dump_output_db_bb(db_file, bb_dump_file)
TskDbDiff._dump_output_db_nonbb(db_file, dump_file, isMultiUser, pgSettings)
TskDbDiff._dump_output_db_bb(db_file, bb_dump_file, isMultiUser, pgSettings)
def _get_tmp_file(base, ext):
@ -349,6 +384,26 @@ class TskDbDiff(object):
class TskDbDiffException(Exception):
pass
class PGSettings(object):
def __init__(self, pgHost=None, pgPort=5432, user=None, password=None):
self.pgHost = pgHost
self.pgPort = pgPort
self.username = user
self.password = password
def get_pgHost():
return self.pgHost
def get_pgPort():
return self.pgPort
def get_username():
return self.username
def get_password():
return self.password
def normalize_db_entry(line, files_table, vs_parts_table, vs_info_table, fs_info_table, objects_table):
""" Make testing more consistent and reasonable by doctoring certain db entries.
@ -357,25 +412,23 @@ def normalize_db_entry(line, files_table, vs_parts_table, vs_info_table, fs_info
files_table: a map from object ids to file paths.
"""
files_index = line.find('INSERT INTO "tsk_files"')
path_index = line.find('INSERT INTO "tsk_files_path"')
object_index = line.find('INSERT INTO "tsk_objects"')
report_index = line.find('INSERT INTO "reports"')
layout_index = line.find('INSERT INTO "tsk_file_layout"')
data_source_info_index = line.find('INSERT INTO "data_source_info"')
ingest_job_index = line.find('INSERT INTO "ingest_jobs"')
# Sqlite statement use double quotes for table name, PostgreSQL doesn't. We check both databases results for normalization.
files_index = line.find('INSERT INTO "tsk_files"') > -1 or line.find('INSERT INTO tsk_files ') > -1
path_index = line.find('INSERT INTO "tsk_files_path"') > -1 or line.find('INSERT INTO tsk_files_path ') > -1
object_index = line.find('INSERT INTO "tsk_objects"') > -1 or line.find('INSERT INTO tsk_objects ') > -1
report_index = line.find('INSERT INTO "reports"') > -1 or line.find('INSERT INTO reports ') > -1
layout_index = line.find('INSERT INTO "tsk_file_layout"') > -1 or line.find('INSERT INTO tsk_file_layout ') > -1
data_source_info_index = line.find('INSERT INTO "data_source_info"') > -1 or line.find('INSERT INTO data_source_info ') > -1
ingest_job_index = line.find('INSERT INTO "ingest_jobs"') > -1 or line.find('INSERT INTO ingest_jobs ') > -1
parens = line[line.find('(') + 1 : line.rfind(')')]
fields_list = parens.replace(" ", "").split(',')
# remove object ID
if (files_index != -1):
obj_id = fields_list[0]
path = files_table[int(obj_id)]
if files_index:
newLine = ('INSERT INTO "tsk_files" VALUES(' + ', '.join(fields_list[1:]) + ');')
return newLine
# remove object ID
elif (path_index != -1):
elif path_index:
obj_id = int(fields_list[0])
objValue = files_table[obj_id]
# remove the obj_id from ModuleOutput/EmbeddedFileExtractor directory
@ -387,16 +440,21 @@ def normalize_db_entry(line, files_table, vs_parts_table, vs_info_table, fs_info
pathValue = fields_list[1][:idx_pre+1] + dir_to_replace + fields_list[1][idx_pos:]
else:
pathValue = fields_list[1]
# remove localhost from postgres par_obj_name
multiOutput_idx = pathValue.find('ModuleOutput')
if multiOutput_idx > -1:
pathValue = "'" + pathValue[pathValue.find('ModuleOutput'):] #postgres par_obj_name include losthost
newLine = ('INSERT INTO "tsk_files_path" VALUES(' + objValue + ', ' + pathValue + ', ' + ', '.join(fields_list[2:]) + ');')
return newLine
# remove object ID
elif (layout_index != -1):
elif layout_index:
obj_id = fields_list[0]
path= files_table[int(obj_id)]
newLine = ('INSERT INTO "tsk_file_layout" VALUES(' + path + ', ' + ', '.join(fields_list[1:]) + ');')
return newLine
# remove object ID
elif (object_index != -1):
elif object_index:
obj_id = fields_list[0]
parent_id = fields_list[1]
newLine = 'INSERT INTO "tsk_objects" VALUES('
@ -434,47 +492,38 @@ def normalize_db_entry(line, files_table, vs_parts_table, vs_info_table, fs_info
else:
return line
# remove time-based information, ie Test_6/11/14 -> Test
elif (report_index != -1):
elif report_index:
fields_list[1] = "AutopsyTestCase"
fields_list[2] = "0"
newLine = ('INSERT INTO "reports" VALUES(' + ','.join(fields_list) + ');')
newLine = ('INSERT INTO "reports" VALUES(' + ','.join(fields_list[1:]) + ');') # remove report_id
return newLine
elif (data_source_info_index != -1):
elif data_source_info_index:
fields_list[1] = "{device id}"
newLine = ('INSERT INTO "data_source_info" VALUES(' + ','.join(fields_list) + ');')
return newLine
elif (ingest_job_index != -1):
elif ingest_job_index:
fields_list[2] = "{host_name}"
start_time = int(fields_list[3])
end_time = int(fields_list[4])
if (start_time <= end_time):
fields_list[3] = "0"
fields_list[4] = "0"
newLine = ('INSERT INTO "injest_jobs" VALUES(' + ','.join(fields_list) + ');')
newLine = ('INSERT INTO "ingest_jobs" VALUES(' + ','.join(fields_list) + ');')
return newLine
else:
return line
def getAssociatedArtifactType(db_file, artifact_id):
# Make a copy that we can modify
backup_db_file = TskDbDiff._get_tmp_file("tsk_backup_db", ".db")
shutil.copy(db_file, backup_db_file)
# We sometimes get situations with messed up permissions
os.chmod (backup_db_file, 0o777)
def getAssociatedArtifactType(cur, artifact_id, isMultiUser):
if isMultiUser:
cur.execute("SELECT tsk_files.parent_path, blackboard_artifact_types.display_name FROM blackboard_artifact_types INNER JOIN blackboard_artifacts ON blackboard_artifact_types.artifact_type_id = blackboard_artifacts.artifact_type_id INNER JOIN tsk_files ON tsk_files.obj_id = blackboard_artifacts.obj_id WHERE artifact_id=%s",[artifact_id])
else:
cur.execute("SELECT tsk_files.parent_path, blackboard_artifact_types.display_name FROM blackboard_artifact_types INNER JOIN blackboard_artifacts ON blackboard_artifact_types.artifact_type_id = blackboard_artifacts.artifact_type_id INNER JOIN tsk_files ON tsk_files.obj_id = blackboard_artifacts.obj_id WHERE artifact_id=?",[artifact_id])
conn = sqlite3.connect(backup_db_file)
cur = conn.cursor()
#artifact_cursor.execute("SELECT display_name FROM blackboard_artifact_types WHERE artifact_id=?",[artifact_id])
cur.execute("SELECT tsk_files.parent_path, blackboard_artifact_types.display_name FROM blackboard_artifact_types INNER JOIN blackboard_artifacts ON blackboard_artifact_types.artifact_type_id = blackboard_artifacts.artifact_type_id INNER JOIN tsk_files ON tsk_files.obj_id = blackboard_artifacts.obj_id WHERE artifact_id=?",[artifact_id])
info = cur.fetchone()
conn.close()
# cleanup the backup
os.remove(backup_db_file)
return "File path: " + info[0] + " Artifact Type: " + info[1]
def build_id_files_table(db_cursor):
def build_id_files_table(db_cursor, isPostgreSQL):
"""Build the map of object ids to file paths.
Args:
@ -482,10 +531,10 @@ def build_id_files_table(db_cursor):
"""
# for each row in the db, take the object id, parent path, and name, then create a tuple in the dictionary
# with the object id as the key and the full file path (parent + name) as the value
mapping = dict([(row[0], str(row[1]) + str(row[2])) for row in db_cursor.execute("SELECT obj_id, parent_path, name FROM tsk_files")])
mapping = dict([(row[0], str(row[1]) + str(row[2])) for row in sql_select_execute(db_cursor, isPostgreSQL, "SELECT obj_id, parent_path, name FROM tsk_files")])
return mapping
def build_id_vs_parts_table(db_cursor):
def build_id_vs_parts_table(db_cursor, isPostgreSQL):
"""Build the map of object ids to vs_parts.
Args:
@ -493,10 +542,10 @@ def build_id_vs_parts_table(db_cursor):
"""
# for each row in the db, take the object id, addr, and start, then create a tuple in the dictionary
# with the object id as the key and (addr + start) as the value
mapping = dict([(row[0], str(row[1]) + '_' + str(row[2])) for row in db_cursor.execute("SELECT obj_id, addr, start FROM tsk_vs_parts")])
mapping = dict([(row[0], str(row[1]) + '_' + str(row[2])) for row in sql_select_execute(db_cursor, isPostgreSQL, "SELECT obj_id, addr, start FROM tsk_vs_parts")])
return mapping
def build_id_vs_info_table(db_cursor):
def build_id_vs_info_table(db_cursor, isPostgreSQL):
"""Build the map of object ids to vs_info.
Args:
@ -504,11 +553,11 @@ def build_id_vs_info_table(db_cursor):
"""
# for each row in the db, take the object id, vs_type, and img_offset, then create a tuple in the dictionary
# with the object id as the key and (vs_type + img_offset) as the value
mapping = dict([(row[0], str(row[1]) + '_' + str(row[2])) for row in db_cursor.execute("SELECT obj_id, vs_type, img_offset FROM tsk_vs_info")])
mapping = dict([(row[0], str(row[1]) + '_' + str(row[2])) for row in sql_select_execute(db_cursor, isPostgreSQL, "SELECT obj_id, vs_type, img_offset FROM tsk_vs_info")])
return mapping
def build_id_fs_info_table(db_cursor):
def build_id_fs_info_table(db_cursor, isPostgreSQL):
"""Build the map of object ids to fs_info.
Args:
@ -516,10 +565,10 @@ def build_id_fs_info_table(db_cursor):
"""
# for each row in the db, take the object id, img_offset, and fs_type, then create a tuple in the dictionary
# with the object id as the key and (img_offset + fs_type) as the value
mapping = dict([(row[0], str(row[1]) + '_' + str(row[2])) for row in db_cursor.execute("SELECT obj_id, img_offset, fs_type FROM tsk_fs_info")])
mapping = dict([(row[0], str(row[1]) + '_' + str(row[2])) for row in sql_select_execute(db_cursor, isPostgreSQL, "SELECT obj_id, img_offset, fs_type FROM tsk_fs_info")])
return mapping
def build_id_objects_table(db_cursor):
def build_id_objects_table(db_cursor, isPostgreSQL):
"""Build the map of object ids to par_id.
Args:
@ -527,10 +576,10 @@ def build_id_objects_table(db_cursor):
"""
# for each row in the db, take the object id, par_obj_id, then create a tuple in the dictionary
# with the object id as the key and par_obj_id, type as the value
mapping = dict([(row[0], [row[1], row[2]]) for row in db_cursor.execute("SELECT * FROM tsk_objects")])
mapping = dict([(row[0], [row[1], row[2]]) for row in sql_select_execute(db_cursor, isPostgreSQL, "SELECT * FROM tsk_objects")])
return mapping
def build_id_artifact_types_table(db_cursor):
def build_id_artifact_types_table(db_cursor, isPostgreSQL):
"""Build the map of object ids to artifact ids.
Args:
@ -538,7 +587,7 @@ def build_id_artifact_types_table(db_cursor):
"""
# for each row in the db, take the object id, par_obj_id, then create a tuple in the dictionary
# with the object id as the key and artifact type as the value
mapping = dict([(row[0], row[1]) for row in db_cursor.execute("SELECT blackboard_artifacts.artifact_obj_id, blackboard_artifact_types.type_name FROM blackboard_artifacts INNER JOIN blackboard_artifact_types ON blackboard_artifact_types.artifact_type_id = blackboard_artifacts.artifact_type_id ")])
mapping = dict([(row[0], row[1]) for row in sql_select_execute(db_cursor, isPostgreSQL, "SELECT blackboard_artifacts.artifact_obj_id, blackboard_artifact_types.type_name FROM blackboard_artifacts INNER JOIN blackboard_artifact_types ON blackboard_artifact_types.artifact_type_id = blackboard_artifacts.artifact_type_id ")])
return mapping
@ -565,6 +614,27 @@ def build_id_obj_path_table(files_table, objects_table, artifacts_table):
mapping[k] = path + "/" + artifacts_table[v[0]]
return mapping
def db_connect(db_file, isMultiUser, pgSettings=None):
if isMultiUser: # use PostgreSQL
try:
return psycopg2.connect("dbname=" + db_file + " user=" + pgSettings.username + " host=" + pgSettings.pgHost + " password=" + pgSettings.password), None
except:
print("Failed to connect to the database: " + db_file)
else: # Sqlite
# Make a copy that we can modify
backup_db_file = TskDbDiff._get_tmp_file("tsk_backup_db", ".db")
shutil.copy(db_file, backup_db_file)
# We sometimes get situations with messed up permissions
os.chmod (backup_db_file, 0o777)
return sqlite3.connect(backup_db_file), backup_db_file
def sql_select_execute(cursor, isPostgreSQL, sql_stmt):
if isPostgreSQL:
cursor.execute(sql_stmt)
return cursor.fetchall()
else:
return cursor.execute(sql_stmt)
def main():
try:
sys.argv.pop(0)