Merge branch 'develop' into 6020-Viber-Parser-produces-an-IllegalArgumentException-from-CommHelper

This commit is contained in:
Mark McKinnon 2020-03-11 09:53:26 -04:00
commit d6bc88d0d4
70 changed files with 2960 additions and 1944 deletions

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2018 Basis Technology Corp. * Copyright 2018-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -61,10 +61,10 @@ public final class AddEditCentralRepoCommentAction extends AbstractAction {
*/ */
public AddEditCentralRepoCommentAction(AbstractFile file) { public AddEditCentralRepoCommentAction(AbstractFile file) {
fileId = file.getId(); fileId = file.getId();
correlationAttributeInstance = CorrelationAttributeUtil.getInstanceFromContent(file); correlationAttributeInstance = CorrelationAttributeUtil.getCorrAttrForFile(file);
if (correlationAttributeInstance == null) { if (correlationAttributeInstance == null) {
addToDatabase = true; addToDatabase = true;
correlationAttributeInstance = CorrelationAttributeUtil.makeInstanceFromContent(file); correlationAttributeInstance = CorrelationAttributeUtil.makeCorrAttrFromFile(file);
} }
if (file.getSize() == 0) { if (file.getSize() == 0) {
putValue(Action.NAME, Bundle.AddEditCentralRepoCommentAction_menuItemText_addEditCentralRepoCommentEmptyFile()); putValue(Action.NAME, Bundle.AddEditCentralRepoCommentAction_menuItemText_addEditCentralRepoCommentEmptyFile());

View File

@ -1,7 +1,7 @@
/* /*
* Central Repository * Central Repository
* *
* Copyright 2017-2019 Basis Technology Corp. * Copyright 2017-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -464,7 +464,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
// correlate on blackboard artifact attributes if they exist and supported // correlate on blackboard artifact attributes if they exist and supported
BlackboardArtifact bbArtifact = getBlackboardArtifactFromNode(node); BlackboardArtifact bbArtifact = getBlackboardArtifactFromNode(node);
if (bbArtifact != null && CentralRepository.isEnabled()) { if (bbArtifact != null && CentralRepository.isEnabled()) {
ret.addAll(CorrelationAttributeUtil.makeInstancesFromBlackboardArtifact(bbArtifact, false)); ret.addAll(CorrelationAttributeUtil.makeCorrAttrsFromArtifact(bbArtifact));
} }
// we can correlate based on the MD5 if it is enabled // we can correlate based on the MD5 if it is enabled

View File

@ -7,8 +7,10 @@ AbstractSqlEamDb.cannotUpgrage.message=Currently selected database platform "{0}
AbstractSqlEamDb.failedToReadMajorVersion.message=Failed to read schema version for Central Repository. AbstractSqlEamDb.failedToReadMajorVersion.message=Failed to read schema version for Central Repository.
AbstractSqlEamDb.failedToReadMinorVersion.message=Failed to read schema minor version for Central Repository. AbstractSqlEamDb.failedToReadMinorVersion.message=Failed to read schema minor version for Central Repository.
AbstractSqlEamDb.upgradeSchema.incompatible=The selected Central Repository is not compatible with the current version of the application, please upgrade the application if you wish to use this Central Repository. AbstractSqlEamDb.upgradeSchema.incompatible=The selected Central Repository is not compatible with the current version of the application, please upgrade the application if you wish to use this Central Repository.
CentralRepoDbManager.connectionErrorMsg.text=Failed to connect to central repository database.
CorrelationAttributeInstance.invalidName.message=Invalid database table name. Name must start with a lowercase letter and can only contain lowercase letters, numbers, and '_'. CorrelationAttributeInstance.invalidName.message=Invalid database table name. Name must start with a lowercase letter and can only contain lowercase letters, numbers, and '_'.
CorrelationAttributeInstance.nullName.message=Database name is null. CorrelationAttributeInstance.nullName.message=Database name is null.
CorrelationAttributeUtil.emailaddresses.text=Email Addresses
CorrelationType.DOMAIN.displayName=Domains CorrelationType.DOMAIN.displayName=Domains
CorrelationType.EMAIL.displayName=Email Addresses CorrelationType.EMAIL.displayName=Email Addresses
CorrelationType.FILES.displayName=Files CorrelationType.FILES.displayName=Files
@ -23,7 +25,6 @@ DataSourceUpdateService.serviceName.text=Update Central Repository Data Sources
EamArtifactInstances.knownStatus.bad=Bad EamArtifactInstances.knownStatus.bad=Bad
EamArtifactInstances.knownStatus.known=Known EamArtifactInstances.knownStatus.known=Known
EamArtifactInstances.knownStatus.unknown=Unknown EamArtifactInstances.knownStatus.unknown=Unknown
EamArtifactUtil.emailaddresses.text=Email Addresses
EamCase.title.caseDisplayName=Case Name: EamCase.title.caseDisplayName=Case Name:
EamCase.title.caseNumber=Case Number: EamCase.title.caseNumber=Case Number:
EamCase.title.caseUUID=Case UUID: EamCase.title.caseUUID=Case UUID:

View File

@ -0,0 +1,137 @@
/*
* Central Repository
*
* Copyright 2020 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.centralrepository.datamodel;
import org.sleuthkit.datamodel.Account;
/**
* This class abstracts an Account as stored in the CR database.
*/
public final class CentralRepoAccount {
// primary key in the Accounts table in CR database
private final long accountId;
private final CentralRepoAccountType accountType;
// type specifc unique account id
private final String typeSpecificId;
/**
* Encapsulates a central repo account type and the correlation type
* that it maps to.
*/
public static final class CentralRepoAccountType {
// id is the primary key in the account_types table
private final int accountTypeId;
private final Account.Type acctType;
private final int correlationTypeId;
CentralRepoAccountType(int acctTypeID, Account.Type acctType, int correlationTypeId) {
this.acctType = acctType;
this.correlationTypeId = correlationTypeId;
this.accountTypeId = acctTypeID;
}
/**
* @return the acctType
*/
public Account.Type getAcctType() {
return acctType;
}
public int getCorrelationTypeId() {
return this.correlationTypeId;
}
public int getAccountTypeId() {
return this.accountTypeId;
}
}
public CentralRepoAccount(long accountId, CentralRepoAccountType accountType, String typeSpecificId) {
this.accountId = accountId;
this.accountType = accountType;
this.typeSpecificId = typeSpecificId;
}
/**
* Gets unique identifier (assigned by a provider) for the account. Example
* includes an email address, a phone number, or a website username.
*
* @return type specific account id.
*/
public String getTypeSpecificId() {
return this.typeSpecificId;
}
/**
* Gets the account type
*
* @return account type
*/
public CentralRepoAccountType getAccountType() {
return this.accountType;
}
/**
* Gets the unique row id for this account in the database.
*
* @return unique row id.
*/
public long getAccountId() {
return this.accountId;
}
@Override
public int hashCode() {
int hash = 5;
hash = 43 * hash + (int) (this.accountId ^ (this.accountId >>> 32));
hash = 43 * hash + (this.accountType != null ? this.accountType.hashCode() : 0);
hash = 43 * hash + (this.typeSpecificId != null ? this.typeSpecificId.hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final CentralRepoAccount other = (CentralRepoAccount) obj;
if (this.accountId != other.getAccountId()) {
return false;
}
if ((this.typeSpecificId == null) ? (other.getTypeSpecificId() != null) : !this.typeSpecificId.equals(other.getTypeSpecificId())) {
return false;
}
if (this.accountType != other.getAccountType() && (this.accountType == null || !this.accountType.equals(other.getAccountType()))) {
return false;
}
return true;
}
}

View File

@ -0,0 +1,407 @@
/*
* Central Repository
*
* Copyright 2015-2020 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.centralrepository.datamodel;
import java.io.File;
import java.sql.SQLException;
import java.util.logging.Level;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coordinationservice.CoordinationService;
import org.sleuthkit.autopsy.coreutils.Logger;
/**
* Contains business logic for saving and validating settings for central repo
*/
public class CentralRepoDbManager {
private static final Logger logger = Logger.getLogger(CentralRepoDbManager.class.getName());
private static final String CENTRAL_REPO_DB_NAME = "central_repository";
/**
* obtains the database connectivity for central repository
*
* @return the CentralRepository object to connect to
* @throws CentralRepoException
*/
private static CentralRepository obtainCentralRepository() throws CentralRepoException {
//get connection
try {
return CentralRepository.getInstance();
} catch (CentralRepoException ex) {
logger.log(Level.SEVERE, "Error updating central repository, unable to make connection", ex);
onUpgradeError("Error updating central repository, unable to make connection",
Bundle.EamDbUtil_centralRepoConnectionFailed_message() + Bundle.EamDbUtil_centralRepoDisabled_message(), ex);
}
// will never be reached
return null;
}
/**
* obtains central repository lock
*
* @param db the database connection
* @return the lock if acquired
* @throws CentralRepoException
*/
private static CoordinationService.Lock obtainCentralRepoLock(CentralRepository db) throws CentralRepoException {
try {
// This may return null if locking isn't supported, which is fine. It will
// throw an exception if locking is supported but we can't get the lock
// (meaning the database is in use by another user)
return db.getExclusiveMultiUserDbLock();
//perform upgrade
} catch (CentralRepoException ex) {
logger.log(Level.SEVERE, "Error updating central repository, unable to acquire exclusive lock", ex);
onUpgradeError("Error updating central repository, unable to acquire exclusive lock",
Bundle.EamDbUtil_exclusiveLockAquisitionFailure_message() + Bundle.EamDbUtil_centralRepoDisabled_message(), ex);
}
// will never be reached
return null;
}
/**
* updates central repository schema if necessary
*
* @param db the database connectivity
* @param lock the acquired lock
* @throws CentralRepoException
*/
private static void updatedDbSchema(CentralRepository db, CoordinationService.Lock lock) throws CentralRepoException {
try {
db.upgradeSchema();
} catch (CentralRepoException ex) {
logger.log(Level.SEVERE, "Error updating central repository", ex);
onUpgradeError("Error updating central repository", ex.getUserMessage() + Bundle.EamDbUtil_centralRepoDisabled_message(), ex);
} catch (SQLException ex) {
logger.log(Level.SEVERE, "Error updating central repository", ex);
onUpgradeError("Error updating central repository",
Bundle.EamDbUtil_centralRepoUpgradeFailed_message() + Bundle.EamDbUtil_centralRepoDisabled_message(), ex);
} catch (IncompatibleCentralRepoException ex) {
logger.log(Level.SEVERE, "Error updating central repository", ex);
onUpgradeError("Error updating central repository",
ex.getMessage() + "\n\n" + Bundle.EamDbUtil_centralRepoUpgradeFailed_message() + Bundle.EamDbUtil_centralRepoDisabled_message(), ex);
} finally {
if (lock != null) {
try {
lock.release();
} catch (CoordinationService.CoordinationServiceException ex) {
logger.log(Level.SEVERE, "Error releasing database lock", ex);
}
}
}
}
/**
* Upgrade the current Central Reposity schema to the newest version. If the
* upgrade fails, the Central Repository will be disabled and the current
* settings will be cleared.
*/
@NbBundle.Messages(value = {"EamDbUtil.centralRepoDisabled.message= The Central Repository has been disabled.", "EamDbUtil.centralRepoUpgradeFailed.message=Failed to upgrade Central Repository.", "EamDbUtil.centralRepoConnectionFailed.message=Unable to connect to Central Repository.", "EamDbUtil.exclusiveLockAquisitionFailure.message=Unable to acquire exclusive lock for Central Repository."})
public static void upgradeDatabase() throws CentralRepoException {
if (!CentralRepository.isEnabled()) {
return;
}
CentralRepository db = obtainCentralRepository();
//get lock necessary for upgrade
if (db != null) {
CoordinationService.Lock lock = obtainCentralRepoLock(db);
updatedDbSchema(db, lock);
} else {
onUpgradeError("Unable to connect to database",
Bundle.EamDbUtil_centralRepoConnectionFailed_message() + Bundle.EamDbUtil_centralRepoDisabled_message(), null);
}
}
private static void onUpgradeError(String message, String desc, Exception innerException) throws CentralRepoException {
// Disable the central repo and clear the current settings.
try {
if (null != CentralRepository.getInstance()) {
CentralRepository.getInstance().shutdownConnections();
}
} catch (CentralRepoException ex2) {
logger.log(Level.SEVERE, "Error shutting down central repo connection pool", ex2);
}
CentralRepoPlatforms.setSelectedPlatform(CentralRepoPlatforms.DISABLED.name());
CentralRepoPlatforms.saveSelectedPlatform();
if (innerException == null) {
throw new CentralRepoException(message, desc);
} else {
throw new CentralRepoException(message, desc, innerException);
}
}
private DatabaseTestResult testingStatus;
private CentralRepoPlatforms selectedPlatform;
private final PostgresCentralRepoSettings dbSettingsPostgres;
private final SqliteCentralRepoSettings dbSettingsSqlite;
private boolean configurationChanged = false;
public CentralRepoDbManager() {
dbSettingsPostgres = new PostgresCentralRepoSettings();
dbSettingsSqlite = new SqliteCentralRepoSettings();
selectedPlatform = CentralRepoPlatforms.getSelectedPlatform();
// set the default selected platform for displaying in the ui of EamDbSettingsDialog
// if selected option is not applicable
if (selectedPlatform == null || selectedPlatform.equals(CentralRepoPlatforms.DISABLED)) {
selectedPlatform = CentralRepoPlatforms.POSTGRESQL;
}
}
public PostgresCentralRepoSettings getDbSettingsPostgres() {
return dbSettingsPostgres;
}
public SqliteCentralRepoSettings getDbSettingsSqlite() {
return dbSettingsSqlite;
}
/**
* setup sqlite db with default settings
* @throws CentralRepoException if unable to successfully set up database
*/
public void setupDefaultSqliteDb() throws CentralRepoException {
// change in-memory settings to default sqlite
selectedPlatform = CentralRepoPlatforms.SQLITE;
dbSettingsSqlite.setupDefaultSettings();
// if db is not present, attempt to create it
DatabaseTestResult curStatus = testStatus();
if (curStatus == DatabaseTestResult.DB_DOES_NOT_EXIST) {
createDb();
curStatus = testStatus();
}
// the only successful setup status is tested ok
if (curStatus != DatabaseTestResult.TESTEDOK) {
throw new CentralRepoException("Unable to successfully create sqlite database");
}
// if successfully got here, then save the settings
CentralRepoDbUtil.setUseCentralRepo(true);
saveNewCentralRepo();
}
/**
* Returns if changes to the central repository configuration were
* successfully applied
*
* @return true if the database configuration was successfully changed false
* if it was not
*/
public boolean wasConfigurationChanged() {
return configurationChanged;
}
private CentralRepoDbSettings getSelectedSettings() throws CentralRepoException {
switch (selectedPlatform) {
case POSTGRESQL:
return dbSettingsPostgres;
case SQLITE:
return dbSettingsSqlite;
case DISABLED:
return null;
default:
throw new CentralRepoException("Unknown database type: " + selectedPlatform);
}
}
private RdbmsCentralRepoFactory getDbFactory() throws CentralRepoException {
switch (selectedPlatform) {
case POSTGRESQL:
return new RdbmsCentralRepoFactory(selectedPlatform, dbSettingsPostgres);
case SQLITE:
return new RdbmsCentralRepoFactory(selectedPlatform, dbSettingsSqlite);
case DISABLED:
return null;
default:
throw new CentralRepoException("Unknown database type: " + selectedPlatform);
}
}
public boolean createDb() throws CentralRepoException {
boolean result = false;
boolean dbCreated = true;
CentralRepoDbSettings selectedDbSettings = getSelectedSettings();
if (!selectedDbSettings.verifyDatabaseExists()) {
dbCreated = selectedDbSettings.createDatabase();
}
if (dbCreated) {
try {
RdbmsCentralRepoFactory centralRepoSchemaFactory = getDbFactory();
result = centralRepoSchemaFactory.initializeDatabaseSchema()
&& centralRepoSchemaFactory.insertDefaultDatabaseContent();
} catch (CentralRepoException ex) {
logger.log(Level.SEVERE, "Unable to create database for central repository with settings " + selectedDbSettings, ex);
throw ex;
}
}
if (!result) {
// Remove the incomplete database
if (dbCreated) {
// RAMAN TBD: migrate deleteDatabase() to RdbmsCentralRepoFactory
selectedDbSettings.deleteDatabase();
}
String schemaError = "Unable to initialize database schema or insert contents into central repository.";
logger.severe(schemaError);
throw new CentralRepoException(schemaError);
}
testingStatus = DatabaseTestResult.TESTEDOK;
return true;
}
/**
* saves a new central repository based on current settings
*/
@NbBundle.Messages({"CentralRepoDbManager.connectionErrorMsg.text=Failed to connect to central repository database."})
public void saveNewCentralRepo() throws CentralRepoException {
/**
* We have to shutdown the previous platform's connection pool first;
* assuming it wasn't DISABLED. This will close any existing idle
* connections.
*
* The next use of an EamDb API method will start a new connection pool
* using those new settings.
*/
try {
CentralRepository previousDbManager = CentralRepository.getInstance();
if (null != previousDbManager) {
// NOTE: do not set/save the seleted platform before calling this.
CentralRepository.getInstance().shutdownConnections();
}
} catch (CentralRepoException ex) {
logger.log(Level.SEVERE, "Failed to close database connections in previously selected platform.", ex); // NON-NLS
throw ex;
}
// Even if we fail to close the existing connections, make sure that we
// save the new connection settings, so an Autopsy restart will correctly
// start with the new settings.
CentralRepoPlatforms.setSelectedPlatform(selectedPlatform.name());
CentralRepoPlatforms.saveSelectedPlatform();
CentralRepoDbSettings selectedDbSettings = getSelectedSettings();
// save the new settings
selectedDbSettings.saveSettings();
// Load those newly saved settings into the postgres db manager instance
// in case we are still using the same instance.
if (selectedPlatform == CentralRepoPlatforms.POSTGRESQL || selectedPlatform == CentralRepoPlatforms.SQLITE) {
try {
logger.info("Creating central repo db with settings: " + selectedDbSettings);
CentralRepository.getInstance().updateSettings();
configurationChanged = true;
} catch (CentralRepoException ex) {
logger.log(Level.SEVERE, Bundle.CentralRepoDbManager_connectionErrorMsg_text(), ex); //NON-NLS
return;
}
}
}
public DatabaseTestResult getStatus() {
return testingStatus;
}
public CentralRepoPlatforms getSelectedPlatform() {
return selectedPlatform;
}
public void clearStatus() {
testingStatus = DatabaseTestResult.UNTESTED;
}
public void setSelectedPlatform(CentralRepoPlatforms newSelected) {
selectedPlatform = newSelected;
testingStatus = DatabaseTestResult.UNTESTED;
}
/**
* Tests whether or not the database settings are valid.
*
* @return True or false.
*/
public boolean testDatabaseSettingsAreValid(
String tbDbHostname, String tbDbPort, String tbDbUsername, String tfDatabasePath, String jpDbPassword) throws CentralRepoException, NumberFormatException {
switch (selectedPlatform) {
case POSTGRESQL:
dbSettingsPostgres.setHost(tbDbHostname);
dbSettingsPostgres.setPort(Integer.parseInt(tbDbPort));
dbSettingsPostgres.setDbName(CENTRAL_REPO_DB_NAME);
dbSettingsPostgres.setUserName(tbDbUsername);
dbSettingsPostgres.setPassword(jpDbPassword);
break;
case SQLITE:
File databasePath = new File(tfDatabasePath);
dbSettingsSqlite.setDbName(SqliteCentralRepoSettings.DEFAULT_DBNAME);
dbSettingsSqlite.setDbDirectory(databasePath.getPath());
break;
default:
throw new IllegalStateException("Central Repo has an unknown selected platform: " + selectedPlatform);
}
return true;
}
public DatabaseTestResult testStatus() {
if (selectedPlatform == CentralRepoPlatforms.POSTGRESQL) {
if (dbSettingsPostgres.verifyConnection()) {
if (dbSettingsPostgres.verifyDatabaseExists()) {
if (dbSettingsPostgres.verifyDatabaseSchema()) {
testingStatus = DatabaseTestResult.TESTEDOK;
} else {
testingStatus = DatabaseTestResult.SCHEMA_INVALID;
}
} else {
testingStatus = DatabaseTestResult.DB_DOES_NOT_EXIST;
}
} else {
testingStatus = DatabaseTestResult.CONNECTION_FAILED;
}
} else if (selectedPlatform == CentralRepoPlatforms.SQLITE) {
if (dbSettingsSqlite.dbFileExists()) {
if (dbSettingsSqlite.verifyConnection()) {
if (dbSettingsSqlite.verifyDatabaseSchema()) {
testingStatus = DatabaseTestResult.TESTEDOK;
} else {
testingStatus = DatabaseTestResult.SCHEMA_INVALID;
}
} else {
testingStatus = DatabaseTestResult.SCHEMA_INVALID;
}
} else {
testingStatus = DatabaseTestResult.DB_DOES_NOT_EXIST;
}
}
return testingStatus;
}
}

View File

@ -0,0 +1,55 @@
/*
* Central Repository
*
* Copyright 2015-2020 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.centralrepository.datamodel;
/**
* common interface for settings pertaining to the database in central repository
*/
public interface CentralRepoDbSettings {
void saveSettings();
boolean createDatabase();
boolean deleteDatabase();
/**
* Use the current settings and the validation query to test the connection
* to the database.
*
* @return true if successfull connection, else false.
*/
boolean verifyConnection();
/**
* Check to see if the database exists.
*
* @return true if exists, else false
*/
boolean verifyDatabaseExists();
/**
* Use the current settings and the schema version query to test the
* database schema.
*
* @return true if successful connection, else false.
*/
boolean verifyDatabaseSchema();
}

View File

@ -25,9 +25,6 @@ import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.util.List; import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.coordinationservice.CoordinationService;
import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CoordinationServiceException;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ModuleSettings; import org.sleuthkit.autopsy.coreutils.ModuleSettings;
import static org.sleuthkit.autopsy.centralrepository.datamodel.RdbmsCentralRepo.SOFTWARE_CR_DB_SCHEMA_VERSION; import static org.sleuthkit.autopsy.centralrepository.datamodel.RdbmsCentralRepo.SOFTWARE_CR_DB_SCHEMA_VERSION;
@ -166,80 +163,6 @@ public class CentralRepoDbUtil {
return true; return true;
} }
/**
* Upgrade the current Central Reposity schema to the newest version. If the
* upgrade fails, the Central Repository will be disabled and the current
* settings will be cleared.
*/
@Messages({"EamDbUtil.centralRepoDisabled.message= The Central Repository has been disabled.",
"EamDbUtil.centralRepoUpgradeFailed.message=Failed to upgrade Central Repository.",
"EamDbUtil.centralRepoConnectionFailed.message=Unable to connect to Central Repository.",
"EamDbUtil.exclusiveLockAquisitionFailure.message=Unable to acquire exclusive lock for Central Repository."})
public static void upgradeDatabase() throws CentralRepoException {
if (!CentralRepository.isEnabled()) {
return;
}
CentralRepository db = null;
CoordinationService.Lock lock = null;
//get connection
try {
try {
db = CentralRepository.getInstance();
} catch (CentralRepoException ex) {
LOGGER.log(Level.SEVERE, "Error updating central repository, unable to make connection", ex);
throw new CentralRepoException("Error updating central repository, unable to make connection", Bundle.EamDbUtil_centralRepoConnectionFailed_message() + Bundle.EamDbUtil_centralRepoDisabled_message(), ex);
}
//get lock necessary for upgrade
if (db != null) {
try {
// This may return null if locking isn't supported, which is fine. It will
// throw an exception if locking is supported but we can't get the lock
// (meaning the database is in use by another user)
lock = db.getExclusiveMultiUserDbLock();
//perform upgrade
} catch (CentralRepoException ex) {
LOGGER.log(Level.SEVERE, "Error updating central repository, unable to acquire exclusive lock", ex);
throw new CentralRepoException("Error updating central repository, unable to acquire exclusive lock", Bundle.EamDbUtil_exclusiveLockAquisitionFailure_message() + Bundle.EamDbUtil_centralRepoDisabled_message(), ex);
}
try {
db.upgradeSchema();
} catch (CentralRepoException ex) {
LOGGER.log(Level.SEVERE, "Error updating central repository", ex);
throw new CentralRepoException("Error updating central repository", ex.getUserMessage() + Bundle.EamDbUtil_centralRepoDisabled_message(), ex);
} catch (SQLException ex) {
LOGGER.log(Level.SEVERE, "Error updating central repository", ex);
throw new CentralRepoException("Error updating central repository", Bundle.EamDbUtil_centralRepoUpgradeFailed_message() + Bundle.EamDbUtil_centralRepoDisabled_message(), ex);
} catch (IncompatibleCentralRepoException ex) {
LOGGER.log(Level.SEVERE, "Error updating central repository", ex);
throw new CentralRepoException("Error updating central repository", ex.getMessage() + "\n\n" + Bundle.EamDbUtil_centralRepoUpgradeFailed_message() + Bundle.EamDbUtil_centralRepoDisabled_message(), ex);
} finally {
if (lock != null) {
try {
lock.release();
} catch (CoordinationServiceException ex) {
LOGGER.log(Level.SEVERE, "Error releasing database lock", ex);
}
}
}
} else {
throw new CentralRepoException("Unable to connect to database", Bundle.EamDbUtil_centralRepoConnectionFailed_message() + Bundle.EamDbUtil_centralRepoDisabled_message());
}
} catch (CentralRepoException ex) {
// Disable the central repo and clear the current settings.
try {
if (null != CentralRepository.getInstance()) {
CentralRepository.getInstance().shutdownConnections();
}
} catch (CentralRepoException ex2) {
LOGGER.log(Level.SEVERE, "Error shutting down central repo connection pool", ex2);
}
CentralRepoPlatforms.setSelectedPlatform(CentralRepoPlatforms.DISABLED.name());
CentralRepoPlatforms.saveSelectedPlatform();
throw ex;
}
}
/** /**
* Get the default organization name * Get the default organization name

View File

@ -23,6 +23,7 @@ import java.util.List;
import java.util.Set; import java.util.Set;
import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount.CentralRepoAccountType;
import org.sleuthkit.autopsy.coordinationservice.CoordinationService; import org.sleuthkit.autopsy.coordinationservice.CoordinationService;
/** /**
@ -803,4 +804,37 @@ public interface CentralRepository {
* @throws CentralRepoException * @throws CentralRepoException
*/ */
public void processSelectClause(String selectClause, InstanceTableCallback instanceTableCallback) throws CentralRepoException; public void processSelectClause(String selectClause, InstanceTableCallback instanceTableCallback) throws CentralRepoException;
/**
* Returns list of all correlation types.
*
* @return list of Correlation types
* @throws CentralRepoException
*/
List<CorrelationAttributeInstance.Type> getCorrelationTypes() throws CentralRepoException;
/**
* Get account type by type name.
*
* @param accountTypeName account type name to look for
* @return CR account type
* @throws CentralRepoException
*/
CentralRepoAccountType getAccountTypeByName(String accountTypeName) throws CentralRepoException;
/**
* Get an account from the accounts table matching the given type/ID.
* Inserts a row if one doesn't exists.
*
* @param crAccountType CR account type to look for or create
* @param accountUniqueID type specific unique account id
* @return CR account
*
* @throws CentralRepoException
*/
CentralRepoAccount getOrCreateAccount(CentralRepoAccount.CentralRepoAccountType crAccountType, String accountUniqueID) throws CentralRepoException;
} }

View File

@ -1,7 +1,7 @@
/* /*
* Central Repository * Central Repository
* *
* Copyright 2015-2018 Basis Technology Corp. * Copyright 2015-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -24,6 +24,7 @@ import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.datamodel.Account;
import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.TskData;
/** /**
@ -49,6 +50,7 @@ public class CorrelationAttributeInstance implements Serializable {
private String comment; private String comment;
private TskData.FileKnown knownStatus; private TskData.FileKnown knownStatus;
private Long objectId; private Long objectId;
private Long accountId;
public CorrelationAttributeInstance( public CorrelationAttributeInstance(
CorrelationAttributeInstance.Type correlationType, CorrelationAttributeInstance.Type correlationType,
@ -72,6 +74,20 @@ public class CorrelationAttributeInstance implements Serializable {
String comment, String comment,
TskData.FileKnown knownStatus, TskData.FileKnown knownStatus,
Long fileObjectId Long fileObjectId
) throws CentralRepoException, CorrelationAttributeNormalizationException {
this(type, value, -1, eamCase, eamDataSource, filePath, comment, knownStatus, fileObjectId, (long)-1);
}
CorrelationAttributeInstance(
Type type,
String value,
int instanceId,
CorrelationCase eamCase,
CorrelationDataSource eamDataSource,
String filePath,
String comment,
TskData.FileKnown knownStatus,
Long fileObjectId,
Long accountId
) throws CentralRepoException, CorrelationAttributeNormalizationException { ) throws CentralRepoException, CorrelationAttributeNormalizationException {
if (filePath == null) { if (filePath == null) {
throw new CentralRepoException("file path is null"); throw new CentralRepoException("file path is null");
@ -87,6 +103,7 @@ public class CorrelationAttributeInstance implements Serializable {
this.comment = comment; this.comment = comment;
this.knownStatus = knownStatus; this.knownStatus = knownStatus;
this.objectId = fileObjectId; this.objectId = fileObjectId;
this.accountId = accountId;
} }
public Boolean equals(CorrelationAttributeInstance otherInstance) { public Boolean equals(CorrelationAttributeInstance otherInstance) {
@ -97,7 +114,8 @@ public class CorrelationAttributeInstance implements Serializable {
&& (this.getCorrelationDataSource().equals(otherInstance.getCorrelationDataSource())) && (this.getCorrelationDataSource().equals(otherInstance.getCorrelationDataSource()))
&& (this.getFilePath().equals(otherInstance.getFilePath())) && (this.getFilePath().equals(otherInstance.getFilePath()))
&& (this.getKnownStatus().equals(otherInstance.getKnownStatus())) && (this.getKnownStatus().equals(otherInstance.getKnownStatus()))
&& (this.getComment().equals(otherInstance.getComment()))); && (this.getComment().equals(otherInstance.getComment()))
&& (this.getAccountId().equals(otherInstance.getAccountId())));
} }
@Override @Override
@ -105,6 +123,7 @@ public class CorrelationAttributeInstance implements Serializable {
return this.getID() return this.getID()
+ this.getCorrelationCase().getCaseUUID() + this.getCorrelationCase().getCaseUUID()
+ this.getCorrelationDataSource().getDeviceID() + this.getCorrelationDataSource().getDeviceID()
+ this.getAccountId()
+ this.getFilePath() + this.getFilePath()
+ this.getCorrelationType().toString() + this.getCorrelationType().toString()
+ this.getCorrelationValue() + this.getCorrelationValue()
@ -209,6 +228,24 @@ public class CorrelationAttributeInstance implements Serializable {
return objectId; return objectId;
} }
/**
* Get the accountId of the account associated with the correlation
* attribute.
*
* @return the accountId of the account
*/
public Long getAccountId() {
return accountId;
}
/**
* Set the accountId of the account associated with this correlation
* attribute.
*/
void setAccountId(Long accountId) {
this.accountId = accountId;
}
// Type ID's for Default Correlation Types // Type ID's for Default Correlation Types
public static final int FILES_TYPE_ID = 0; public static final int FILES_TYPE_ID = 0;
public static final int DOMAIN_TYPE_ID = 1; public static final int DOMAIN_TYPE_ID = 1;
@ -221,6 +258,9 @@ public class CorrelationAttributeInstance implements Serializable {
public static final int IMSI_TYPE_ID = 8; public static final int IMSI_TYPE_ID = 8;
public static final int ICCID_TYPE_ID = 9; public static final int ICCID_TYPE_ID = 9;
// An offset to assign Ids for additional correlation types.
public static final int ADDITIONAL_TYPES_BASE_ID = 1000;
/** /**
* Load the default correlation types * Load the default correlation types
* *
@ -238,18 +278,30 @@ public class CorrelationAttributeInstance implements Serializable {
"CorrelationType.IMSI.displayName=IMSI Number", "CorrelationType.IMSI.displayName=IMSI Number",
"CorrelationType.ICCID.displayName=ICCID Number"}) "CorrelationType.ICCID.displayName=ICCID Number"})
public static List<CorrelationAttributeInstance.Type> getDefaultCorrelationTypes() throws CentralRepoException { public static List<CorrelationAttributeInstance.Type> getDefaultCorrelationTypes() throws CentralRepoException {
List<CorrelationAttributeInstance.Type> DEFAULT_CORRELATION_TYPES = new ArrayList<>(); List<CorrelationAttributeInstance.Type> defaultCorrelationTypes = new ArrayList<>();
DEFAULT_CORRELATION_TYPES.add(new CorrelationAttributeInstance.Type(FILES_TYPE_ID, Bundle.CorrelationType_FILES_displayName(), "file", true, true)); // NON-NLS
DEFAULT_CORRELATION_TYPES.add(new CorrelationAttributeInstance.Type(DOMAIN_TYPE_ID, Bundle.CorrelationType_DOMAIN_displayName(), "domain", true, true)); // NON-NLS defaultCorrelationTypes.add(new CorrelationAttributeInstance.Type(FILES_TYPE_ID, Bundle.CorrelationType_FILES_displayName(), "file", true, true)); // NON-NLS
DEFAULT_CORRELATION_TYPES.add(new CorrelationAttributeInstance.Type(EMAIL_TYPE_ID, Bundle.CorrelationType_EMAIL_displayName(), "email_address", true, true)); // NON-NLS defaultCorrelationTypes.add(new CorrelationAttributeInstance.Type(DOMAIN_TYPE_ID, Bundle.CorrelationType_DOMAIN_displayName(), "domain", true, true)); // NON-NLS
DEFAULT_CORRELATION_TYPES.add(new CorrelationAttributeInstance.Type(PHONE_TYPE_ID, Bundle.CorrelationType_PHONE_displayName(), "phone_number", true, true)); // NON-NLS defaultCorrelationTypes.add(new CorrelationAttributeInstance.Type(EMAIL_TYPE_ID, Bundle.CorrelationType_EMAIL_displayName(), "email_address", true, true)); // NON-NLS
DEFAULT_CORRELATION_TYPES.add(new CorrelationAttributeInstance.Type(USBID_TYPE_ID, Bundle.CorrelationType_USBID_displayName(), "usb_devices", true, true)); // NON-NLS defaultCorrelationTypes.add(new CorrelationAttributeInstance.Type(PHONE_TYPE_ID, Bundle.CorrelationType_PHONE_displayName(), "phone_number", true, true)); // NON-NLS
DEFAULT_CORRELATION_TYPES.add(new CorrelationAttributeInstance.Type(SSID_TYPE_ID, Bundle.CorrelationType_SSID_displayName(), "wireless_networks", true, true)); // NON-NLS defaultCorrelationTypes.add(new CorrelationAttributeInstance.Type(USBID_TYPE_ID, Bundle.CorrelationType_USBID_displayName(), "usb_devices", true, true)); // NON-NLS
DEFAULT_CORRELATION_TYPES.add(new CorrelationAttributeInstance.Type(MAC_TYPE_ID, Bundle.CorrelationType_MAC_displayName(), "mac_address", true, true)); //NON-NLS defaultCorrelationTypes.add(new CorrelationAttributeInstance.Type(SSID_TYPE_ID, Bundle.CorrelationType_SSID_displayName(), "wireless_networks", true, true)); // NON-NLS
DEFAULT_CORRELATION_TYPES.add(new CorrelationAttributeInstance.Type(IMEI_TYPE_ID, Bundle.CorrelationType_IMEI_displayName(), "imei_number", true, true)); //NON-NLS defaultCorrelationTypes.add(new CorrelationAttributeInstance.Type(MAC_TYPE_ID, Bundle.CorrelationType_MAC_displayName(), "mac_address", true, true)); //NON-NLS
DEFAULT_CORRELATION_TYPES.add(new CorrelationAttributeInstance.Type(IMSI_TYPE_ID, Bundle.CorrelationType_IMSI_displayName(), "imsi_number", true, true)); //NON-NLS defaultCorrelationTypes.add(new CorrelationAttributeInstance.Type(IMEI_TYPE_ID, Bundle.CorrelationType_IMEI_displayName(), "imei_number", true, true)); //NON-NLS
DEFAULT_CORRELATION_TYPES.add(new CorrelationAttributeInstance.Type(ICCID_TYPE_ID, Bundle.CorrelationType_ICCID_displayName(), "iccid_number", true, true)); //NON-NLS defaultCorrelationTypes.add(new CorrelationAttributeInstance.Type(IMSI_TYPE_ID, Bundle.CorrelationType_IMSI_displayName(), "imsi_number", true, true)); //NON-NLS
return DEFAULT_CORRELATION_TYPES; defaultCorrelationTypes.add(new CorrelationAttributeInstance.Type(ICCID_TYPE_ID, Bundle.CorrelationType_ICCID_displayName(), "iccid_number", true, true)); //NON-NLS
// Create Correlation Types for Accounts.
int correlationTypeId = ADDITIONAL_TYPES_BASE_ID;
for (Account.Type type : Account.Type.PREDEFINED_ACCOUNT_TYPES) {
// Skip Phone and Email accounts as there are already Correlation types defined for those.
if (type != Account.Type.EMAIL && type != Account.Type.PHONE) {
defaultCorrelationTypes.add(new CorrelationAttributeInstance.Type(correlationTypeId, type.getDisplayName(), type.getTypeName().toLowerCase() + "_acct", true, true)); //NON-NLS
correlationTypeId++;
}
}
return defaultCorrelationTypes;
} }
/** /**

View File

@ -76,10 +76,22 @@ final public class CorrelationAttributeNormalizer {
return normalizeIccid(trimmedData); return normalizeIccid(trimmedData);
default: default:
try {
// If the atttribute is not one of the above
// but is one of the other default correlation types, then let the data go as is
List<CorrelationAttributeInstance.Type> defaultCorrelationTypes = CorrelationAttributeInstance.getDefaultCorrelationTypes();
for (CorrelationAttributeInstance.Type defaultCorrelationType : defaultCorrelationTypes) {
if (defaultCorrelationType.getId() == attributeType.getId()) {
return trimmedData;
}
}
final String errorMessage = String.format( final String errorMessage = String.format(
"Validator function not found for attribute type: %s", "Validator function not found for attribute type: %s",
attributeType.getDisplayName()); attributeType.getDisplayName());
throw new CorrelationAttributeNormalizationException(errorMessage); throw new CorrelationAttributeNormalizationException(errorMessage);
} catch (CentralRepoException ex) {
throw new CorrelationAttributeNormalizationException("Failed to get default correlation types.", ex);
}
} }
} }

View File

@ -1,7 +1,7 @@
/* /*
* Central Repository * Central Repository
* *
* Copyright 2015-2020 Basis Technology Corp. * Copyright 2017-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -24,182 +24,307 @@ import java.util.logging.Level;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount.CentralRepoAccountType;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.HashUtility; import org.sleuthkit.datamodel.HashUtility;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.TskData;
/** /**
* Utility class for correlation attributes in the central repository * Utility class for working with correlation attributes in the central
* repository.
*/ */
public class CorrelationAttributeUtil { public class CorrelationAttributeUtil {
private static final Logger logger = Logger.getLogger(CorrelationAttributeUtil.class.getName()); private static final Logger logger = Logger.getLogger(CorrelationAttributeUtil.class.getName());
@Messages({"EamArtifactUtil.emailaddresses.text=Email Addresses"}) /**
public static String getEmailAddressAttrString() { * Gets a string that is expected to be the same string that is stored in
return Bundle.EamArtifactUtil_emailaddresses_text(); * the correlation_types table in the central repository as the display name
* for the email address correlation attribute type. This string is
* duplicated in the CorrelationAttributeInstance class.
*
* TODO (Jira-6088): We should not have multiple deifnitions of this string.
*
* @return The display name of the email address correlation attribute type.
*/
@Messages({"CorrelationAttributeUtil.emailaddresses.text=Email Addresses"})
private static String getEmailAddressAttrDisplayName() {
return Bundle.CorrelationAttributeUtil_emailaddresses_text();
} }
/** /**
* Static factory method to examine a BlackboardArtifact to determine if it * Makes zero to many correlation attribute instances from the attributes of
* has contents that can be used for Correlation. If so, return a * an artifact.
* EamArtifact with a single EamArtifactInstance within. If not, return
* null.
* *
* @param artifact BlackboardArtifact to examine * IMPORTANT: The correlation attribute instances are NOT added to the
* @param checkEnabled If true, only create a CorrelationAttribute if it is * central repository by this method.
* enabled
* *
* @return List of EamArtifacts * TODO (Jira-6088): The methods in this low-level, utility class should
* throw exceptions instead of logging them. The reason for this is that the
* clients of the utility class, not the utility class itself, should be in
* charge of error handling policy, per the Autopsy Coding Standard. Note
* that clients of several of these methods currently cannot determine
* whether receiving a null return value is an error or not, plus null
* checking is easy to forget, while catching exceptions is enforced.
*
* @param artifact An artifact.
*
* @return A list, possibly empty, of correlation attribute instances for
* the artifact.
*/ */
public static List<CorrelationAttributeInstance> makeInstancesFromBlackboardArtifact(BlackboardArtifact artifact, public static List<CorrelationAttributeInstance> makeCorrAttrsFromArtifact(BlackboardArtifact artifact) {
boolean checkEnabled) { List<CorrelationAttributeInstance> correlationAttrs = new ArrayList<>();
List<CorrelationAttributeInstance> eamArtifacts = new ArrayList<>();
try { try {
BlackboardArtifact artifactForInstance = null; BlackboardArtifact sourceArtifact = getCorrAttrSourceArtifact(artifact);
if (BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID() == artifact.getArtifactTypeID()) { if (sourceArtifact != null) {
// Get the associated artifactForInstance int artifactTypeID = sourceArtifact.getArtifactTypeID();
BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT));
if (attribute != null) {
artifactForInstance = Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboardArtifact(attribute.getValueLong());
}
} else {
artifactForInstance = artifact;
}
if (artifactForInstance != null) {
int artifactTypeID = artifactForInstance.getArtifactTypeID();
if (artifactTypeID == ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()) { if (artifactTypeID == ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()) {
BlackboardAttribute setNameAttr = artifactForInstance.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME)); BlackboardAttribute setNameAttr = sourceArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME));
if (setNameAttr != null if (setNameAttr != null && CorrelationAttributeUtil.getEmailAddressAttrDisplayName().equals(setNameAttr.getValueString())) {
&& CorrelationAttributeUtil.getEmailAddressAttrString().equals(setNameAttr.getValueString())) { makeCorrAttrFromArtifactAttr(correlationAttrs, sourceArtifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD, CorrelationAttributeInstance.EMAIL_TYPE_ID);
addCorrelationAttributeToList(eamArtifacts, artifactForInstance, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD, CorrelationAttributeInstance.EMAIL_TYPE_ID);
} }
} else if (artifactTypeID == ARTIFACT_TYPE.TSK_WEB_BOOKMARK.getTypeID() } else if (artifactTypeID == ARTIFACT_TYPE.TSK_WEB_BOOKMARK.getTypeID()
|| artifactTypeID == ARTIFACT_TYPE.TSK_WEB_COOKIE.getTypeID() || artifactTypeID == ARTIFACT_TYPE.TSK_WEB_COOKIE.getTypeID()
|| artifactTypeID == ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID() || artifactTypeID == ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID()
|| artifactTypeID == ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID()) { || artifactTypeID == ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID()) {
addCorrelationAttributeToList(eamArtifacts, artifactForInstance, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN, CorrelationAttributeInstance.DOMAIN_TYPE_ID); makeCorrAttrFromArtifactAttr(correlationAttrs, sourceArtifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN, CorrelationAttributeInstance.DOMAIN_TYPE_ID);
} else if (artifactTypeID == ARTIFACT_TYPE.TSK_CONTACT.getTypeID() } else if (artifactTypeID == ARTIFACT_TYPE.TSK_CONTACT.getTypeID()
|| artifactTypeID == ARTIFACT_TYPE.TSK_CALLLOG.getTypeID() || artifactTypeID == ARTIFACT_TYPE.TSK_CALLLOG.getTypeID()
|| artifactTypeID == ARTIFACT_TYPE.TSK_MESSAGE.getTypeID()) { || artifactTypeID == ARTIFACT_TYPE.TSK_MESSAGE.getTypeID()) {
makeCorrAttrFromArtifactPhoneAttr(sourceArtifact);
String value = null; } else if (artifactTypeID == ARTIFACT_TYPE.TSK_DEVICE_ATTACHED.getTypeID()) {
if (null != artifactForInstance.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER))) { makeCorrAttrFromArtifactAttr(correlationAttrs, sourceArtifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_ID, CorrelationAttributeInstance.USBID_TYPE_ID);
value = artifactForInstance.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER)).getValueString(); makeCorrAttrFromArtifactAttr(correlationAttrs, sourceArtifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MAC_ADDRESS, CorrelationAttributeInstance.MAC_TYPE_ID);
} else if (null != artifactForInstance.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM))) {
value = artifactForInstance.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM)).getValueString(); } else if (artifactTypeID == ARTIFACT_TYPE.TSK_WIFI_NETWORK.getTypeID()) {
} else if (null != artifactForInstance.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO))) { makeCorrAttrFromArtifactAttr(correlationAttrs, sourceArtifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SSID, CorrelationAttributeInstance.SSID_TYPE_ID);
value = artifactForInstance.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO)).getValueString();
} else if (artifactTypeID == ARTIFACT_TYPE.TSK_WIFI_NETWORK_ADAPTER.getTypeID()
|| artifactTypeID == ARTIFACT_TYPE.TSK_BLUETOOTH_PAIRING.getTypeID()
|| artifactTypeID == ARTIFACT_TYPE.TSK_BLUETOOTH_ADAPTER.getTypeID()) {
makeCorrAttrFromArtifactAttr(correlationAttrs, sourceArtifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MAC_ADDRESS, CorrelationAttributeInstance.MAC_TYPE_ID);
} else if (artifactTypeID == ARTIFACT_TYPE.TSK_DEVICE_INFO.getTypeID()) {
makeCorrAttrFromArtifactAttr(correlationAttrs, sourceArtifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_IMEI, CorrelationAttributeInstance.IMEI_TYPE_ID);
makeCorrAttrFromArtifactAttr(correlationAttrs, sourceArtifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_IMSI, CorrelationAttributeInstance.IMSI_TYPE_ID);
makeCorrAttrFromArtifactAttr(correlationAttrs, sourceArtifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ICCID, CorrelationAttributeInstance.ICCID_TYPE_ID);
} else if (artifactTypeID == ARTIFACT_TYPE.TSK_SIM_ATTACHED.getTypeID()) {
makeCorrAttrFromArtifactAttr(correlationAttrs, sourceArtifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_IMSI, CorrelationAttributeInstance.IMSI_TYPE_ID);
makeCorrAttrFromArtifactAttr(correlationAttrs, sourceArtifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ICCID, CorrelationAttributeInstance.ICCID_TYPE_ID);
} else if (artifactTypeID == ARTIFACT_TYPE.TSK_WEB_FORM_ADDRESS.getTypeID()) {
makeCorrAttrFromArtifactAttr(correlationAttrs, sourceArtifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER, CorrelationAttributeInstance.PHONE_TYPE_ID);
makeCorrAttrFromArtifactAttr(correlationAttrs, sourceArtifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL, CorrelationAttributeInstance.EMAIL_TYPE_ID);
} else if (artifactTypeID == ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) {
makeCorrAttrFromAcctArtifact(correlationAttrs, sourceArtifact);
} }
// Remove all non-numeric symbols to semi-normalize phone numbers, preserving leading "+" character }
} catch (CentralRepoException ex) {
logger.log(Level.SEVERE, String.format("Error querying central repository (%s)", artifact), ex); // NON-NLS
return correlationAttrs;
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Error getting querying case database (%s)", artifact), ex); // NON-NLS
return correlationAttrs;
} catch (NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "Error getting current case", ex); // NON-NLS
return correlationAttrs;
}
return correlationAttrs;
}
/**
* Gets the associated artifact of a "meta-artifact" such as an interesting
* artifact hit artifact.
*
* @param artifact An artifact.
*
* @return The associated artifact if the input artifact is a
* "meta-artifact", otherwise the input artifact.
*
* @throws NoCurrentCaseException If there is no open case.
* @throws TskCoreException If there is an error querying thew case
* database.
*/
private static BlackboardArtifact getCorrAttrSourceArtifact(BlackboardArtifact artifact) throws NoCurrentCaseException, TskCoreException {
BlackboardArtifact sourceArtifact = null;
if (BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID() == artifact.getArtifactTypeID()) {
BlackboardAttribute assocArtifactAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT));
if (assocArtifactAttr != null) {
sourceArtifact = Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboardArtifact(assocArtifactAttr.getValueLong());
}
} else {
sourceArtifact = artifact;
}
return sourceArtifact;
}
/**
* Makes a correlation attribute instance from a phone number attribute of an
* artifact.
*
* @param artifact An artifact with a phone number attribute.
*
* @return The correlation instance artifact or null, if the phone number is
* not a valid correlation attribute.
*
* @throws TskCoreException If there is an error querying the case
* database.
* @throws CentralRepoException If there is an error querying the central
* repository.
*/
private static CorrelationAttributeInstance makeCorrAttrFromArtifactPhoneAttr(BlackboardArtifact artifact) throws TskCoreException, CentralRepoException {
CorrelationAttributeInstance corrAttr = null;
/*
* Extract the phone number from the artifact attribute.
*/
String value = null;
if (null != artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER))) {
value = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER)).getValueString();
} else if (null != artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM))) {
value = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM)).getValueString();
} else if (null != artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO))) {
value = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO)).getValueString();
}
/*
* Normalize the phone number.
*/
if (value != null) { if (value != null) {
String newValue = value.replaceAll("\\D", ""); String newValue = value.replaceAll("\\D", "");
if (value.startsWith("+")) { if (value.startsWith("+")) {
newValue = "+" + newValue; newValue = "+" + newValue;
} }
value = newValue; value = newValue;
// Only add the correlation attribute if the resulting phone number large enough to be of use
// (these 3-5 digit numbers can be valid, but are not useful for correlation) /*
* Validate the phone number. Three to five digit phone numbers may
* be valid, but they are too short to use as correlation
* attributes.
*/
if (value.length() > 5) { if (value.length() > 5) {
CorrelationAttributeInstance inst = makeCorrelationAttributeInstanceUsingTypeValue(artifactForInstance, CentralRepository.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.PHONE_TYPE_ID), value); corrAttr = makeCorrAttr(artifact, CentralRepository.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.PHONE_TYPE_ID), value);
if (inst != null) {
eamArtifacts.add(inst);
} }
} }
}
} else if (artifactTypeID == ARTIFACT_TYPE.TSK_DEVICE_ATTACHED.getTypeID()) { return corrAttr;
addCorrelationAttributeToList(eamArtifacts, artifactForInstance, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_ID, CorrelationAttributeInstance.USBID_TYPE_ID);
addCorrelationAttributeToList(eamArtifacts, artifactForInstance, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MAC_ADDRESS, CorrelationAttributeInstance.MAC_TYPE_ID);
} else if (artifactTypeID == ARTIFACT_TYPE.TSK_WIFI_NETWORK.getTypeID()) {
addCorrelationAttributeToList(eamArtifacts, artifactForInstance, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SSID, CorrelationAttributeInstance.SSID_TYPE_ID);
} else if (artifactTypeID == ARTIFACT_TYPE.TSK_WIFI_NETWORK_ADAPTER.getTypeID()
|| artifactTypeID == ARTIFACT_TYPE.TSK_BLUETOOTH_PAIRING.getTypeID()
|| artifactTypeID == ARTIFACT_TYPE.TSK_BLUETOOTH_ADAPTER.getTypeID()) {
addCorrelationAttributeToList(eamArtifacts, artifactForInstance, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MAC_ADDRESS, CorrelationAttributeInstance.MAC_TYPE_ID);
} else if (artifactTypeID == ARTIFACT_TYPE.TSK_DEVICE_INFO.getTypeID()) {
addCorrelationAttributeToList(eamArtifacts, artifactForInstance, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_IMEI, CorrelationAttributeInstance.IMEI_TYPE_ID);
addCorrelationAttributeToList(eamArtifacts, artifactForInstance, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_IMSI, CorrelationAttributeInstance.IMSI_TYPE_ID);
addCorrelationAttributeToList(eamArtifacts, artifactForInstance, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ICCID, CorrelationAttributeInstance.ICCID_TYPE_ID);
} else if (artifactTypeID == ARTIFACT_TYPE.TSK_SIM_ATTACHED.getTypeID()) {
addCorrelationAttributeToList(eamArtifacts, artifactForInstance, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_IMSI, CorrelationAttributeInstance.IMSI_TYPE_ID);
addCorrelationAttributeToList(eamArtifacts, artifactForInstance, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ICCID, CorrelationAttributeInstance.ICCID_TYPE_ID);
} else if (artifactTypeID == ARTIFACT_TYPE.TSK_WEB_FORM_ADDRESS.getTypeID()) {
addCorrelationAttributeToList(eamArtifacts, artifactForInstance, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER, CorrelationAttributeInstance.PHONE_TYPE_ID);
addCorrelationAttributeToList(eamArtifacts, artifactForInstance, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL, CorrelationAttributeInstance.EMAIL_TYPE_ID);
}
}
} catch (CentralRepoException ex) {
logger.log(Level.SEVERE, "Error getting defined correlation types.", ex); // NON-NLS
return eamArtifacts;
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error getting attribute while getting type from BlackboardArtifact.", ex); // NON-NLS
return null;
} catch (NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "Exception while getting open case.", ex); // NON-NLS
return null;
}
return eamArtifacts;
} }
/** /**
* Add a CorrelationAttributeInstance of the specified type to the provided * Makes a correlation attribute instance for an account artifact.
* list if the artifactForInstance has an Attribute of the given type with a
* non empty value.
* *
* @param eamArtifacts the list of CorrelationAttributeInstance objects * Also creates an account in the CR DB if it doesn't exist.
* which should be added to
* @param artifact the blackboard artifactForInstance which we are
* creating a CorrelationAttributeInstance for
* @param bbAttributeType the type of BlackboardAttribute we expect to exist
* for a CorrelationAttributeInstance of this type
* generated from this Blackboard Artifact
* @param typeId the integer type id of the
* CorrelationAttributeInstance type
* *
* @throws CentralRepoException * IMPORTANT: The correlation attribute instance is NOT added to the central
* @throws TskCoreException * repository by this method.
*
* TODO (Jira-6088): The methods in this low-level, utility class should
* throw exceptions instead of logging them. The reason for this is that the
* clients of the utility class, not the utility class itself, should be in
* charge of error handling policy, per the Autopsy Coding Standard. Note
* that clients of several of these methods currently cannot determine
* whether receiving a null return value is an error or not, plus null
* checking is easy to forget, while catching exceptions is enforced.
*
* @param corrAttrInstances A list of correlation attribute instances.
* @param acctArtifact An account artifact.
*
* @return The correlation attribute instance.
*/ */
private static void addCorrelationAttributeToList(List<CorrelationAttributeInstance> eamArtifacts, BlackboardArtifact artifact, ATTRIBUTE_TYPE bbAttributeType, int typeId) throws CentralRepoException, TskCoreException { private static void makeCorrAttrFromAcctArtifact(List<CorrelationAttributeInstance> corrAttrInstances, BlackboardArtifact acctArtifact) throws TskCoreException, CentralRepoException {
BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(bbAttributeType));
// Get the account type from the artifact
BlackboardAttribute accountTypeAttribute = acctArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE));
String accountTypeStr = accountTypeAttribute.getValueString();
// Get the corresponding CentralRepoAccountType from the database.
CentralRepoAccountType crAccountType = CentralRepository.getInstance().getAccountTypeByName(accountTypeStr);
int corrTypeId = crAccountType.getCorrelationTypeId();
CorrelationAttributeInstance.Type corrType = CentralRepository.getInstance().getCorrelationTypeById(corrTypeId);
// Get the account identifier
BlackboardAttribute accountIdAttribute = acctArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ID));
String accountIdStr = accountIdAttribute.getValueString();
// add/get the account and get its accountId.
CentralRepoAccount crAccount = CentralRepository.getInstance().getOrCreateAccount(crAccountType, accountIdStr);
CorrelationAttributeInstance corrAttr = makeCorrAttr(acctArtifact, corrType, accountIdStr);
if (corrAttr != null) {
// set the account_id in correlation attribute
corrAttr.setAccountId(crAccount.getAccountId());
corrAttrInstances.add(corrAttr);
}
}
/**
* Makes a correlation attribute instance from a specified attribute of an
* artifact. The correlation attribute instance is added to an input list.
*
* @param corrAttrInstances A list of correlation attribute instances.
* @param artifact An artifact.
* @param artAttrType The type of the atrribute of the artifact that
* is to be made into a correlatin attribute
* instance.
* @param typeId The type ID for the desired correlation
* attribute instance.
*
* @throws CentralRepoException If there is an error querying the central
* repository.
* @throws TskCoreException If there is an error querying the case
* database.
*/
private static void makeCorrAttrFromArtifactAttr(List<CorrelationAttributeInstance> corrAttrInstances, BlackboardArtifact artifact, ATTRIBUTE_TYPE artAttrType, int typeId) throws CentralRepoException, TskCoreException {
BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(artAttrType));
if (attribute != null) { if (attribute != null) {
String value = attribute.getValueString(); String value = attribute.getValueString();
if ((null != value) && (value.isEmpty() == false)) { if ((null != value) && (value.isEmpty() == false)) {
CorrelationAttributeInstance inst = makeCorrelationAttributeInstanceUsingTypeValue(artifact, CentralRepository.getInstance().getCorrelationTypeById(typeId), value); CorrelationAttributeInstance inst = makeCorrAttr(artifact, CentralRepository.getInstance().getCorrelationTypeById(typeId), value);
if (inst != null) { if (inst != null) {
eamArtifacts.add(inst); corrAttrInstances.add(inst);
} }
} }
} }
} }
/** /**
* Uses the determined type and vallue, then looks up instance details to * Makes a correlation attribute instance of a given type from an artifact.
* create proper CorrelationAttributeInstance.
* *
* @param bbArtifact the blackboard artifactForInstance * @param artifact The artifact.
* @param correlationType the given type * @param correlationType the correlation attribute type.
* @param value the artifactForInstance value * @param value The correlation attribute value.
* *
* @return CorrelationAttributeInstance from details, or null if validation * TODO (Jira-6088): The methods in this low-level, utility class should
* failed or another error occurred * throw exceptions instead of logging them. The reason for this is that the
* clients of the utility class, not the utility class itself, should be in
* charge of error handling policy, per the Autopsy Coding Standard. Note
* that clients of several of these methods currently cannot determine
* whether receiving a null return value is an error or not, plus null
* checking is easy to forget, while catching exceptions is enforced.
*
* @return The correlation attribute instance or null, if an error occurred.
*/ */
private static CorrelationAttributeInstance makeCorrelationAttributeInstanceUsingTypeValue(BlackboardArtifact bbArtifact, CorrelationAttributeInstance.Type correlationType, String value) { private static CorrelationAttributeInstance makeCorrAttr(BlackboardArtifact artifact, CorrelationAttributeInstance.Type correlationType, String value) {
try { try {
Case currentCase = Case.getCurrentCaseThrows(); Case currentCase = Case.getCurrentCaseThrows();
AbstractFile bbSourceFile = currentCase.getSleuthkitCase().getAbstractFileById(bbArtifact.getObjectID()); AbstractFile bbSourceFile = currentCase.getSleuthkitCase().getAbstractFileById(artifact.getObjectID());
if (null == bbSourceFile) { if (null == bbSourceFile) {
logger.log(Level.SEVERE, "Error creating artifact instance. Abstract File was null."); // NON-NLS logger.log(Level.SEVERE, "Error creating artifact instance. Abstract File was null."); // NON-NLS
return null; return null;
} }
// make an instance for the BB source file
CorrelationCase correlationCase = CentralRepository.getInstance().getCase(Case.getCurrentCaseThrows()); CorrelationCase correlationCase = CentralRepository.getInstance().getCase(Case.getCurrentCaseThrows());
return new CorrelationAttributeInstance( return new CorrelationAttributeInstance(
correlationType, correlationType,
@ -212,31 +337,34 @@ public class CorrelationAttributeUtil {
bbSourceFile.getId()); bbSourceFile.getId());
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error getting AbstractFile for artifact: " + bbArtifact.toString(), ex); // NON-NLS logger.log(Level.SEVERE, String.format("Error getting querying case database (%s)", artifact), ex); // NON-NLS
return null; return null;
} catch (CentralRepoException | CorrelationAttributeNormalizationException ex) { } catch (CentralRepoException | CorrelationAttributeNormalizationException ex) {
logger.log(Level.WARNING, "Error creating artifact instance for artifact: " + bbArtifact.toString(), ex); // NON-NLS logger.log(Level.SEVERE, String.format("Error querying central repository (%s)", artifact), ex); // NON-NLS
return null; return null;
} catch (NoCurrentCaseException ex) { } catch (NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "Case is closed.", ex); // NON-NLS logger.log(Level.SEVERE, "Error getting current case", ex); // NON-NLS
return null; return null;
} }
} }
/** /**
* Retrieve CorrelationAttribute from the given Content. * Gets the correlation attribute instance for a file.
* *
* @param content The content object * @param file The file.
* *
* @return The new CorrelationAttribute, or null if retrieval failed. * TODO (Jira-6088): The methods in this low-level, utility class should
* throw exceptions instead of logging them. The reason for this is that the
* clients of the utility class, not the utility class itself, should be in
* charge of error handling policy, per the Autopsy Coding Standard. Note
* that clients of several of these methods currently cannot determine
* whether receiving a null return value is an error or not, plus null
* checking is easy to forget, while catching exceptions is enforced.
*
* @return The correlation attribute instance or null, if no such
* correlation attribute instance was found or an error occurred.
*/ */
public static CorrelationAttributeInstance getInstanceFromContent(Content content) { public static CorrelationAttributeInstance getCorrAttrForFile(AbstractFile file) {
if (!(content instanceof AbstractFile)) {
return null;
}
final AbstractFile file = (AbstractFile) content;
if (!isSupportedAbstractFileType(file)) { if (!isSupportedAbstractFileType(file)) {
return null; return null;
@ -254,11 +382,14 @@ public class CorrelationAttributeUtil {
return null; return null;
} }
correlationDataSource = CorrelationDataSource.fromTSKDataSource(correlationCase, file.getDataSource()); correlationDataSource = CorrelationDataSource.fromTSKDataSource(correlationCase, file.getDataSource());
} catch (TskCoreException | CentralRepoException ex) { } catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error retrieving correlation attribute.", ex); logger.log(Level.SEVERE, String.format("Error getting querying case database (%s)", file), ex); // NON-NLS
return null;
} catch (CentralRepoException ex) {
logger.log(Level.SEVERE, String.format("Error querying central repository (%s)", file), ex); // NON-NLS
return null; return null;
} catch (NoCurrentCaseException ex) { } catch (NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "Case is closed.", ex); logger.log(Level.SEVERE, "Error getting current case", ex); // NON-NLS
return null; return null;
} }
@ -266,20 +397,22 @@ public class CorrelationAttributeUtil {
try { try {
correlationAttributeInstance = CentralRepository.getInstance().getCorrelationAttributeInstance(type, correlationCase, correlationDataSource, file.getId()); correlationAttributeInstance = CentralRepository.getInstance().getCorrelationAttributeInstance(type, correlationCase, correlationDataSource, file.getId());
} catch (CentralRepoException | CorrelationAttributeNormalizationException ex) { } catch (CentralRepoException | CorrelationAttributeNormalizationException ex) {
logger.log(Level.WARNING, String.format( logger.log(Level.SEVERE, String.format("Error querying central repository (%s)", file), ex); // NON-NLS
"Correlation attribute could not be retrieved for '%s' (id=%d): ",
content.getName(), content.getId()), ex);
return null; return null;
} }
//if there was no correlation attribute found for the item using object_id then check for attributes added with schema 1,1 which lack object_id
/*
* If no correlation attribute instance was found when querying by file
* object ID, try searching by file path instead. This is necessary
* because file object IDs were not stored in the central repository in
* early versions of its schema.
*/
if (correlationAttributeInstance == null && file.getMd5Hash() != null) { if (correlationAttributeInstance == null && file.getMd5Hash() != null) {
String filePath = (file.getParentPath() + file.getName()).toLowerCase(); String filePath = (file.getParentPath() + file.getName()).toLowerCase();
try { try {
correlationAttributeInstance = CentralRepository.getInstance().getCorrelationAttributeInstance(type, correlationCase, correlationDataSource, file.getMd5Hash(), filePath); correlationAttributeInstance = CentralRepository.getInstance().getCorrelationAttributeInstance(type, correlationCase, correlationDataSource, file.getMd5Hash(), filePath);
} catch (CentralRepoException | CorrelationAttributeNormalizationException ex) { } catch (CentralRepoException | CorrelationAttributeNormalizationException ex) {
logger.log(Level.WARNING, String.format( logger.log(Level.SEVERE, String.format("Error querying central repository (%s)", file), ex); // NON-NLS
"Correlation attribute could not be retrieved for '%s' (id=%d): ",
content.getName(), content.getId()), ex);
return null; return null;
} }
} }
@ -288,32 +421,31 @@ public class CorrelationAttributeUtil {
} }
/** /**
* Create an EamArtifact from the given Content. Will return null if an * Makes a correlation attribute instance for a file.
* artifactForInstance can not be created - this is not necessarily an error
* case, it just means an artifactForInstance can't be made. If creation
* fails due to an error (and not that the file is the wrong type or it has
* no hash), the error will be logged before returning.
* *
* Does not add the artifactForInstance to the database. * IMPORTANT: The correlation attribute instance is NOT added to the central
* repository by this method.
* *
* @param content The content object * TODO (Jira-6088): The methods in this low-level, utility class should
* throw exceptions instead of logging them. The reason for this is that the
* clients of the utility class, not the utility class itself, should be in
* charge of error handling policy, per the Autopsy Coding Standard. Note
* that clients of several of these methods currently cannot determine
* whether receiving a null return value is an error or not, plus null
* checking is easy to forget, while catching exceptions is enforced.
* *
* @return The new EamArtifact or null if creation failed * @param file The file.
*
* @return The correlation attribute instance or null, if an error occurred.
*/ */
public static CorrelationAttributeInstance makeInstanceFromContent(Content content) { public static CorrelationAttributeInstance makeCorrAttrFromFile(AbstractFile file) {
if (!(content instanceof AbstractFile)) { if (!isSupportedAbstractFileType(file)) {
return null; return null;
} }
final AbstractFile af = (AbstractFile) content; // We need a hash to make the correlation artifact instance.
String md5 = file.getMd5Hash();
if (!isSupportedAbstractFileType(af)) {
return null;
}
// We need a hash to make the artifactForInstance
String md5 = af.getMd5Hash();
if (md5 == null || md5.isEmpty() || HashUtility.isNoDataMd5(md5)) { if (md5 == null || md5.isEmpty() || HashUtility.isNoDataMd5(md5)) {
return null; return null;
} }
@ -324,31 +456,33 @@ public class CorrelationAttributeUtil {
CorrelationCase correlationCase = CentralRepository.getInstance().getCase(Case.getCurrentCaseThrows()); CorrelationCase correlationCase = CentralRepository.getInstance().getCase(Case.getCurrentCaseThrows());
return new CorrelationAttributeInstance( return new CorrelationAttributeInstance(
filesType, filesType,
af.getMd5Hash(), file.getMd5Hash(),
correlationCase, correlationCase,
CorrelationDataSource.fromTSKDataSource(correlationCase, af.getDataSource()), CorrelationDataSource.fromTSKDataSource(correlationCase, file.getDataSource()),
af.getParentPath() + af.getName(), file.getParentPath() + file.getName(),
"", "",
TskData.FileKnown.UNKNOWN, TskData.FileKnown.UNKNOWN,
af.getId()); file.getId());
} catch (TskCoreException | CentralRepoException | CorrelationAttributeNormalizationException ex) { } catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error making correlation attribute.", ex); logger.log(Level.SEVERE, String.format("Error querying case database (%s)", file), ex); // NON-NLS
return null;
} catch (CentralRepoException | CorrelationAttributeNormalizationException ex) {
logger.log(Level.SEVERE, String.format("Error querying central repository (%s)", file), ex); // NON-NLS
return null; return null;
} catch (NoCurrentCaseException ex) { } catch (NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "Case is closed.", ex); logger.log(Level.SEVERE, "Error getting current case", ex); // NON-NLS
return null; return null;
} }
} }
/** /**
* Check whether the given abstract file should be processed for the central * Checks whether or not a file is of a type that can be added to the
* repository. * central repository as a correlation attribute instance.
* *
* @param file The file to test * @param file A file.
* *
* @return true if the file should be added to the central repo, false * @return True or false.
* otherwise
*/ */
public static boolean isSupportedAbstractFileType(AbstractFile file) { public static boolean isSupportedAbstractFileType(AbstractFile file) {
if (file == null) { if (file == null) {
@ -375,9 +509,9 @@ public class CorrelationAttributeUtil {
} }
/** /**
* Constructs a new EamArtifactUtil * Prevent instantiation of this utility class.
*/ */
private CorrelationAttributeUtil() { private CorrelationAttributeUtil() {
//empty constructor
} }
} }

View File

@ -0,0 +1,31 @@
/*
* Central Repository
*
* Copyright 2015-2020 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.centralrepository.datamodel;
/**
* provides the status of the database after attempting to validate central repo settings
*/
public enum DatabaseTestResult {
UNTESTED,
CONNECTION_FAILED,
SCHEMA_INVALID,
DB_DOES_NOT_EXIST,
TESTEDOK;
}

View File

@ -0,0 +1,87 @@
/*
* Central Repository
*
* Copyright 2020 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.centralrepository.datamodel;
/**
* This class abstracts a persona.
*
* An examiner may create a persona from an account.
*
*/
class Persona {
/**
* Defines level of confidence in assigning a persona to an account.
*/
public enum Confidence {
UNKNOWN(1, "Unknown"),
LOW(2, "Low confidence"),
MEDIUM(3, "Medium confidence"),
HIGH(4, "High confidence"),
DERIVED(5, "Derived directly");
private final String name;
private final int level_id;
Confidence(int level, String name) {
this.name = name;
this.level_id = level;
}
@Override
public String toString() {
return name;
}
public int getLevel() {
return this.level_id;
}
}
/**
* Defines status of a persona.
*/
public enum PersonaStatus {
UNKNOWN(1, "Unknown"),
ACTIVE(2, "Active"),
MERGED(3, "Merged"),
SPLIT(4, "Split"),
DELETED(5, "Deleted");
private final String description;
private final int status_id;
PersonaStatus(int status, String description) {
this.status_id = status;
this.description = description;
}
@Override
public String toString() {
return description;
}
public int getStatus() {
return this.status_id;
}
}
}

View File

@ -40,7 +40,7 @@ import static org.sleuthkit.autopsy.centralrepository.datamodel.RdbmsCentralRepo
* NOTE: This is public scope because the options panel calls it directly to * NOTE: This is public scope because the options panel calls it directly to
* set/get * set/get
*/ */
public final class PostgresCentralRepoSettings { public final class PostgresCentralRepoSettings implements CentralRepoDbSettings {
private final static Logger LOGGER = Logger.getLogger(PostgresCentralRepoSettings.class.getName()); private final static Logger LOGGER = Logger.getLogger(PostgresCentralRepoSettings.class.getName());
private final static String DEFAULT_HOST = ""; // NON-NLS private final static String DEFAULT_HOST = ""; // NON-NLS
@ -64,6 +64,12 @@ public final class PostgresCentralRepoSettings {
loadSettings(); loadSettings();
} }
@Override
public String toString() {
return String.format("PostgresCentralRepoSettings: [db type: postgres, host: %s:%d, db name: %s, username: %s]",
getHost(), getPort(), getDbName(), getUserName());
}
public void loadSettings() { public void loadSettings() {
host = ModuleSettings.getConfigSetting("CentralRepository", "db.postgresql.host"); // NON-NLS host = ModuleSettings.getConfigSetting("CentralRepository", "db.postgresql.host"); // NON-NLS
if (host == null || host.isEmpty()) { if (host == null || host.isEmpty()) {
@ -187,6 +193,7 @@ public final class PostgresCentralRepoSettings {
* *
* @return true if successfull connection, else false. * @return true if successfull connection, else false.
*/ */
@Override
public boolean verifyConnection() { public boolean verifyConnection() {
Connection conn = getEphemeralConnection(true); Connection conn = getEphemeralConnection(true);
if (null == conn) { if (null == conn) {
@ -203,6 +210,7 @@ public final class PostgresCentralRepoSettings {
* *
* @return true if exists, else false * @return true if exists, else false
*/ */
@Override
public boolean verifyDatabaseExists() { public boolean verifyDatabaseExists() {
Connection conn = getEphemeralConnection(true); Connection conn = getEphemeralConnection(true);
if (null == conn) { if (null == conn) {
@ -236,6 +244,7 @@ public final class PostgresCentralRepoSettings {
* *
* @return true if successful connection, else false. * @return true if successful connection, else false.
*/ */
@Override
public boolean verifyDatabaseSchema() { public boolean verifyDatabaseSchema() {
Connection conn = getEphemeralConnection(false); Connection conn = getEphemeralConnection(false);
if (null == conn) { if (null == conn) {
@ -248,6 +257,7 @@ public final class PostgresCentralRepoSettings {
return result; return result;
} }
@Override
public boolean createDatabase() { public boolean createDatabase() {
Connection conn = getEphemeralConnection(true); Connection conn = getEphemeralConnection(true);
if (null == conn) { if (null == conn) {
@ -269,6 +279,7 @@ public final class PostgresCentralRepoSettings {
} }
@Override
public boolean deleteDatabase() { public boolean deleteDatabase() {
Connection conn = getEphemeralConnection(true); Connection conn = getEphemeralConnection(true);
if (null == conn) { if (null == conn) {

View File

@ -41,12 +41,15 @@ import java.util.Set;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.logging.Level; import java.util.logging.Level;
import org.apache.commons.lang3.tuple.Pair;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount.CentralRepoAccountType;
import static org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoDbUtil.updateSchemaVersion; import static org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoDbUtil.updateSchemaVersion;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.healthmonitor.HealthMonitor; import org.sleuthkit.autopsy.healthmonitor.HealthMonitor;
import org.sleuthkit.autopsy.healthmonitor.TimingMetric; import org.sleuthkit.autopsy.healthmonitor.TimingMetric;
import org.sleuthkit.datamodel.Account;
import org.sleuthkit.datamodel.CaseDbSchemaVersionNumber; import org.sleuthkit.datamodel.CaseDbSchemaVersionNumber;
import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.TskData;
@ -71,6 +74,12 @@ abstract class RdbmsCentralRepo implements CentralRepository {
private final Map<String, Collection<CorrelationAttributeInstance>> bulkArtifacts; private final Map<String, Collection<CorrelationAttributeInstance>> bulkArtifacts;
private static final int CASE_CACHE_TIMEOUT = 5; private static final int CASE_CACHE_TIMEOUT = 5;
private static final int DATA_SOURCE_CACHE_TIMEOUT = 5; private static final int DATA_SOURCE_CACHE_TIMEOUT = 5;
private static final int ACCOUNTS_CACHE_TIMEOUT = 5;
private static final Cache<String, CentralRepoAccountType> accountTypesCache = CacheBuilder.newBuilder().build();
private static final Cache<Pair<CentralRepoAccountType, String>, CentralRepoAccount> accountsCache = CacheBuilder.newBuilder()
.expireAfterWrite(ACCOUNTS_CACHE_TIMEOUT, TimeUnit.MINUTES).
build();
private static final Cache<Integer, CorrelationAttributeInstance.Type> typeCache = CacheBuilder.newBuilder().build(); private static final Cache<Integer, CorrelationAttributeInstance.Type> typeCache = CacheBuilder.newBuilder().build();
private static final Cache<String, CorrelationCase> caseCacheByUUID = CacheBuilder.newBuilder() private static final Cache<String, CorrelationCase> caseCacheByUUID = CacheBuilder.newBuilder()
.expireAfterWrite(CASE_CACHE_TIMEOUT, TimeUnit.MINUTES). .expireAfterWrite(CASE_CACHE_TIMEOUT, TimeUnit.MINUTES).
@ -993,21 +1002,21 @@ abstract class RdbmsCentralRepo implements CentralRepository {
public void addArtifactInstance(CorrelationAttributeInstance eamArtifact) throws CentralRepoException { public void addArtifactInstance(CorrelationAttributeInstance eamArtifact) throws CentralRepoException {
checkAddArtifactInstanceNulls(eamArtifact); checkAddArtifactInstanceNulls(eamArtifact);
Connection conn = connect();
PreparedStatement preparedStatement = null;
// @@@ We should cache the case and data source IDs in memory // @@@ We should cache the case and data source IDs in memory
String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(eamArtifact.getCorrelationType()); String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(eamArtifact.getCorrelationType());
String sql String sql
= "INSERT INTO " = "INSERT INTO "
+ tableName + tableName
+ "(case_id, data_source_id, value, file_path, known_status, comment, file_obj_id) " + "(case_id, data_source_id, value, file_path, known_status, comment, file_obj_id, account_id) "
+ "VALUES (?, ?, ?, ?, ?, ?, ?) " + "VALUES (?, ?, ?, ?, ?, ?, ?, ?) "
+ getConflictClause(); + getConflictClause();
try { try (Connection conn = connect();
preparedStatement = conn.prepareStatement(sql); PreparedStatement preparedStatement = conn.prepareStatement(sql);) {
if (!eamArtifact.getCorrelationValue().isEmpty()) { if (!eamArtifact.getCorrelationValue().isEmpty()) {
preparedStatement.setInt(1, eamArtifact.getCorrelationCase().getID()); preparedStatement.setInt(1, eamArtifact.getCorrelationCase().getID());
@ -1023,17 +1032,162 @@ abstract class RdbmsCentralRepo implements CentralRepository {
} }
preparedStatement.setLong(7, eamArtifact.getFileObjectId()); preparedStatement.setLong(7, eamArtifact.getFileObjectId());
if (eamArtifact.getAccountId() >= 0) {
preparedStatement.setLong(8, eamArtifact.getAccountId());
} else {
preparedStatement.setNull(8, Types.INTEGER);
}
preparedStatement.executeUpdate(); preparedStatement.executeUpdate();
} }
} catch (SQLException ex) { } catch (SQLException ex) {
throw new CentralRepoException("Error inserting new artifact into artifacts table.", ex); // NON-NLS throw new CentralRepoException("Error inserting new artifact into artifacts table.", ex); // NON-NLS
} finally {
CentralRepoDbUtil.closeStatement(preparedStatement);
CentralRepoDbUtil.closeConnection(conn);
} }
} }
/**
* Gets the Central Repo account for the given account type and account ID.
* Create a new account first, if one doesn't exist
*
* @param accountType account type
* @param accountUniqueID unique account identifier
*
* @return A matching account, either existing or newly created.
*
* @throws TskCoreException exception thrown if a critical error occurs
* within TSK core
*/
@Override
public CentralRepoAccount getOrCreateAccount(CentralRepoAccountType crAccountType, String accountUniqueID) throws CentralRepoException {
// Get the account fom the accounts table
CentralRepoAccount account = getAccount(crAccountType, accountUniqueID);
// account not found in the table, create it
if (null == account) {
String query = "INSERT INTO accounts (account_type_id, account_unique_identifier) "
+ "VALUES ( " + crAccountType.getAccountTypeId() + ", '"
+ accountUniqueID + "' )";
try (Connection connection = connect();
Statement s = connection.createStatement();) {
s.execute(query);
// get the account from the db - should exist now.
account = getAccount(crAccountType, accountUniqueID);
} catch (SQLException ex) {
throw new CentralRepoException("Error adding an account to CR database.", ex);
}
}
return account;
}
@Override
public CentralRepoAccountType getAccountTypeByName(String accountTypeName) throws CentralRepoException {
try {
return accountTypesCache.get(accountTypeName, () -> getCRAccountTypeFromDb(accountTypeName));
} catch (CacheLoader.InvalidCacheLoadException | ExecutionException ex) {
throw new CentralRepoException("Error looking up CR account type in cache.", ex);
}
}
/**
* Gets the CR account type for the specified type name.
*
* @param accountTypeName account type name to look for
* @return CR account type
*
* @throws CentralRepoException
*/
private CentralRepoAccountType getCRAccountTypeFromDb(String accountTypeName) throws CentralRepoException {
String sql = "SELECT * FROM account_types WHERE type_name = ?";
try ( Connection conn = connect();
PreparedStatement preparedStatement = conn.prepareStatement(sql);) {
preparedStatement.setString(1, accountTypeName);
try (ResultSet resultSet = preparedStatement.executeQuery();) {
if (resultSet.next()) {
Account.Type acctType = new Account.Type(accountTypeName, resultSet.getString("display_name"));
CentralRepoAccountType crAccountType = new CentralRepoAccountType(resultSet.getInt("id"), acctType, resultSet.getInt("correlation_type_id"));
accountTypesCache.put(accountTypeName, crAccountType);
return crAccountType;
} else {
throw new CentralRepoException("Failed to find entry for account type = " + accountTypeName);
}
}
} catch (SQLException ex) {
throw new CentralRepoException("Error getting correlation type by id.", ex); // NON-NLS
}
}
/**
* Get the CR account with the given account type and the unique account identifier.
* Looks in the cache first.
* If not found in cache, reads from the database and saves in cache.
*
* Returns null if the account is not found in the cache and not in the database.
*
* @param crAccountType account type to look for
* @param accountUniqueID unique account id
* @return CentralRepoAccount for the give type/id. May return null if not found.
*
* @throws CentralRepoException
*/
private CentralRepoAccount getAccount(CentralRepoAccountType crAccountType, String accountUniqueID) throws CentralRepoException {
CentralRepoAccount crAccount = accountsCache.getIfPresent(Pair.of(crAccountType, accountUniqueID));
if (crAccount == null) {
crAccount = getCRAccountFromDb(crAccountType, accountUniqueID);
if (crAccount != null) {
accountsCache.put(Pair.of(crAccountType, accountUniqueID), crAccount);
}
}
return crAccount;
}
/**
* Get the Account with the given account type and account identifier,
* from the database.
*
* @param accountType account type
* @param accountUniqueID unique account identifier
*
* @return Account, returns NULL is no matching account found
*
* @throws TskCoreException exception thrown if a critical error occurs
* within TSK core
*/
private CentralRepoAccount getCRAccountFromDb(CentralRepoAccountType crAccountType, String accountUniqueID) throws CentralRepoException {
CentralRepoAccount account = null;
String sql = "SELECT * FROM accounts WHERE account_type_id = ? AND account_unique_identifier = ?";
try ( Connection connection = connect();
PreparedStatement preparedStatement = connection.prepareStatement(sql);) {
preparedStatement.setInt(1, crAccountType.getAccountTypeId());
preparedStatement.setString(2, accountUniqueID);
try (ResultSet resultSet = preparedStatement.executeQuery();) {
if (resultSet.next()) {
account = new CentralRepoAccount(resultSet.getInt("id"), crAccountType, resultSet.getString("account_unique_identifier")); //NON-NLS
}
}
} catch (SQLException ex) {
throw new CentralRepoException("Error getting account type id", ex);
}
return account;
}
private void checkAddArtifactInstanceNulls(CorrelationAttributeInstance eamArtifact) throws CentralRepoException { private void checkAddArtifactInstanceNulls(CorrelationAttributeInstance eamArtifact) throws CentralRepoException {
if (eamArtifact == null) { if (eamArtifact == null) {
throw new CentralRepoException("CorrelationAttribute is null"); throw new CentralRepoException("CorrelationAttribute is null");
@ -1373,6 +1527,9 @@ abstract class RdbmsCentralRepo implements CentralRepository {
} }
synchronized (bulkArtifacts) { synchronized (bulkArtifacts) {
if (bulkArtifacts.get(CentralRepoDbUtil.correlationTypeToInstanceTableName(eamArtifact.getCorrelationType())) == null) {
bulkArtifacts.put(CentralRepoDbUtil.correlationTypeToInstanceTableName(eamArtifact.getCorrelationType()), new ArrayList<>());
}
bulkArtifacts.get(CentralRepoDbUtil.correlationTypeToInstanceTableName(eamArtifact.getCorrelationType())).add(eamArtifact); bulkArtifacts.get(CentralRepoDbUtil.correlationTypeToInstanceTableName(eamArtifact.getCorrelationType())).add(eamArtifact);
bulkArtifactsCount++; bulkArtifactsCount++;
@ -2845,6 +3002,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
typeId = newCorrelationTypeKnownId(newType); typeId = newCorrelationTypeKnownId(newType);
} }
typeCache.put(newType.getId(), newType);
return typeId; return typeId;
} }
@ -3105,6 +3263,45 @@ abstract class RdbmsCentralRepo implements CentralRepository {
} }
} }
/**
* Returns a list of all correlation types. It uses the cache to build the
* list. If the cache is empty, it reads from the database and loads up the
* cache.
*
* @return List of correlation types.
* @throws CentralRepoException
*/
@Override
public List<CorrelationAttributeInstance.Type> getCorrelationTypes() throws CentralRepoException {
if (typeCache.size() == 0) {
getCorrelationTypesFromCr();
}
return new ArrayList<>(typeCache.asMap().values());
}
/**
* Gets a Correlation type with the specified name.
*
* @param correlationtypeName Correlation type name
* @return Correlation type matching the given name, null if none matches.
*
* @throws CentralRepoException
*/
public CorrelationAttributeInstance.Type getCorrelationTypeByName(String correlationtypeName) throws CentralRepoException {
List<CorrelationAttributeInstance.Type> correlationTypesList = getCorrelationTypes();
CorrelationAttributeInstance.Type correlationType
= correlationTypesList.stream()
.filter(x -> correlationtypeName.equalsIgnoreCase(x.getDisplayName()))
.findAny()
.orElse(null);
return null;
}
/** /**
* Get the EamArtifact.Type that has the given Type.Id from the central repo * Get the EamArtifact.Type that has the given Type.Id from the central repo
* *
@ -3142,6 +3339,30 @@ abstract class RdbmsCentralRepo implements CentralRepository {
} }
} }
/**
* Reads the correlation types from the database and loads them up in the cache.
*
* @throws CentralRepoException If there is an error.
*/
private void getCorrelationTypesFromCr() throws CentralRepoException {
// clear out the cache
typeCache.invalidateAll();
String sql = "SELECT * FROM correlation_types";
try ( Connection conn = connect();
PreparedStatement preparedStatement = conn.prepareStatement(sql);
ResultSet resultSet = preparedStatement.executeQuery();) {
while (resultSet.next()) {
CorrelationAttributeInstance.Type aType = getCorrelationTypeFromResultSet(resultSet);
typeCache.put(aType.getId(), aType);
}
} catch (SQLException ex) {
throw new CentralRepoException("Error getting correlation types.", ex); // NON-NLS
}
}
/** /**
* Convert a ResultSet to a EamCase object * Convert a ResultSet to a EamCase object
* *

View File

@ -19,12 +19,17 @@
package org.sleuthkit.autopsy.centralrepository.datamodel; package org.sleuthkit.autopsy.centralrepository.datamodel;
import java.sql.Connection; import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.util.List; import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
import org.sleuthkit.autopsy.centralrepository.datamodel.Persona.Confidence;
import org.sleuthkit.autopsy.centralrepository.datamodel.Persona.PersonaStatus;
import static org.sleuthkit.autopsy.centralrepository.datamodel.RdbmsCentralRepo.SOFTWARE_CR_DB_SCHEMA_VERSION; import static org.sleuthkit.autopsy.centralrepository.datamodel.RdbmsCentralRepo.SOFTWARE_CR_DB_SCHEMA_VERSION;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.Account;
/** /**
* Creates the CR schema and populates it with initial data. * Creates the CR schema and populates it with initial data.
@ -87,16 +92,14 @@ public class RdbmsCentralRepoFactory {
// NOTE: the db_info table currenly only has 1 row, so having an index // NOTE: the db_info table currenly only has 1 row, so having an index
// provides no benefit. // provides no benefit.
Connection conn = null; try (Connection conn = this.getEphemeralConnection();) {
Statement stmt = null;
try {
conn = this.getEphemeralConnection();
if (null == conn) { if (null == conn) {
LOGGER.log(Level.SEVERE, "Cannot initialize CR database, don't have a valid connection."); // NON-NLS LOGGER.log(Level.SEVERE, "Cannot initialize CR database, don't have a valid connection."); // NON-NLS
return false; return false;
} }
stmt = conn.createStatement(); try (Statement stmt = conn.createStatement();) {
// these setting PRAGMAs are SQLIte spcific // these setting PRAGMAs are SQLIte spcific
if (selectedPlatform == CentralRepoPlatforms.SQLITE) { if (selectedPlatform == CentralRepoPlatforms.SQLITE) {
@ -131,12 +134,16 @@ public class RdbmsCentralRepoFactory {
stmt.execute("INSERT INTO db_info (name, value) VALUES ('" + RdbmsCentralRepo.CREATION_SCHEMA_MAJOR_VERSION_KEY + "', '" + SOFTWARE_CR_DB_SCHEMA_VERSION.getMajor() + "')"); stmt.execute("INSERT INTO db_info (name, value) VALUES ('" + RdbmsCentralRepo.CREATION_SCHEMA_MAJOR_VERSION_KEY + "', '" + SOFTWARE_CR_DB_SCHEMA_VERSION.getMajor() + "')");
stmt.execute("INSERT INTO db_info (name, value) VALUES ('" + RdbmsCentralRepo.CREATION_SCHEMA_MINOR_VERSION_KEY + "', '" + SOFTWARE_CR_DB_SCHEMA_VERSION.getMinor() + "')"); stmt.execute("INSERT INTO db_info (name, value) VALUES ('" + RdbmsCentralRepo.CREATION_SCHEMA_MINOR_VERSION_KEY + "', '" + SOFTWARE_CR_DB_SCHEMA_VERSION.getMinor() + "')");
// Create account_types and accounts tables which are referred by X_instances tables
stmt.execute(getCreateAccountTypesTableStatement(selectedPlatform));
stmt.execute(getCreateAccountsTableStatement(selectedPlatform));
// Create a separate instance and reference table for each artifact type // Create a separate instance and reference table for each artifact type
List<CorrelationAttributeInstance.Type> DEFAULT_CORRELATION_TYPES = CorrelationAttributeInstance.getDefaultCorrelationTypes(); List<CorrelationAttributeInstance.Type> defaultCorrelationTypes = CorrelationAttributeInstance.getDefaultCorrelationTypes();
String reference_type_dbname; String reference_type_dbname;
String instance_type_dbname; String instance_type_dbname;
for (CorrelationAttributeInstance.Type type : DEFAULT_CORRELATION_TYPES) { for (CorrelationAttributeInstance.Type type : defaultCorrelationTypes) {
reference_type_dbname = CentralRepoDbUtil.correlationTypeToReferenceTableName(type); reference_type_dbname = CentralRepoDbUtil.correlationTypeToReferenceTableName(type);
instance_type_dbname = CentralRepoDbUtil.correlationTypeToInstanceTableName(type); instance_type_dbname = CentralRepoDbUtil.correlationTypeToInstanceTableName(type);
@ -154,22 +161,20 @@ public class RdbmsCentralRepoFactory {
stmt.execute(String.format(getReferenceTypeValueKnownstatusIndexTemplate(), reference_type_dbname, reference_type_dbname)); stmt.execute(String.format(getReferenceTypeValueKnownstatusIndexTemplate(), reference_type_dbname, reference_type_dbname));
} }
} }
// @TODO: uncomment this when ready to create Persona tables.
//createPersonaTables(stmt);
} catch (SQLException ex) { } catch (SQLException ex) {
LOGGER.log(Level.SEVERE, "Error initializing db schema.", ex); // NON-NLS LOGGER.log(Level.SEVERE, "Error initializing db schema.", ex); // NON-NLS
return false; return false;
} catch (CentralRepoException ex) { } catch (CentralRepoException ex) {
LOGGER.log(Level.SEVERE, "Error getting default correlation types. Likely due to one or more Type's with an invalid db table name."); // NON-NLS LOGGER.log(Level.SEVERE, "Error getting default correlation types. Likely due to one or more Type's with an invalid db table name."); // NON-NLS
return false; return false;
} finally {
if (stmt != null) {
try {
stmt.close();
} catch (SQLException ex2) {
LOGGER.log(Level.SEVERE, "Error closing statement.", ex2);
} }
} catch (SQLException ex) {
LOGGER.log(Level.SEVERE, "Error connecting to database.", ex); // NON-NLS
return false;
} }
CentralRepoDbUtil.closeConnection(conn);
}
return true; return true;
} }
@ -179,13 +184,24 @@ public class RdbmsCentralRepoFactory {
* @return True if success, False otherwise. * @return True if success, False otherwise.
*/ */
public boolean insertDefaultDatabaseContent() { public boolean insertDefaultDatabaseContent() {
Connection conn = this.getEphemeralConnection();
boolean result;
try (Connection conn = this.getEphemeralConnection();) {
if (null == conn) { if (null == conn) {
return false; return false;
} }
boolean result = CentralRepoDbUtil.insertDefaultCorrelationTypes(conn) && CentralRepoDbUtil.insertDefaultOrganization(conn); result = CentralRepoDbUtil.insertDefaultCorrelationTypes(conn)
CentralRepoDbUtil.closeConnection(conn); && CentralRepoDbUtil.insertDefaultOrganization(conn) &&
insertDefaultAccountsTablesContent(conn);
// @TODO: uncomment when ready to create/populate persona tables
// && insertDefaultPersonaTablesContent(conn);
} catch (SQLException ex) {
LOGGER.log(Level.SEVERE, String.format("Failed to populate default data in CR tables."), ex);
return false;
}
return result; return result;
} }
@ -353,12 +369,14 @@ public class RdbmsCentralRepoFactory {
+ getNumericPrimaryKeyClause("id", selectedPlatform) + getNumericPrimaryKeyClause("id", selectedPlatform)
+ "case_id integer NOT NULL," + "case_id integer NOT NULL,"
+ "data_source_id integer NOT NULL," + "data_source_id integer NOT NULL,"
+ "account_id " + getBigIntType(selectedPlatform) + " DEFAULT NULL,"
+ "value text NOT NULL," + "value text NOT NULL,"
+ "file_path text NOT NULL," + "file_path text NOT NULL,"
+ "known_status integer NOT NULL," + "known_status integer NOT NULL,"
+ "comment text," + "comment text,"
+ "file_obj_id " + getBigIntType(selectedPlatform) + " ," + "file_obj_id " + getBigIntType(selectedPlatform) + " ,"
+ "CONSTRAINT %s_multi_unique UNIQUE(data_source_id, value, file_path)" + getOnConflictIgnoreClause(selectedPlatform) + "," + "CONSTRAINT %s_multi_unique UNIQUE(data_source_id, value, file_path)" + getOnConflictIgnoreClause(selectedPlatform) + ","
+ "foreign key (account_id) references accounts(id),"
+ "foreign key (case_id) references cases(id) ON UPDATE SET NULL ON DELETE SET NULL," + "foreign key (case_id) references cases(id) ON UPDATE SET NULL ON DELETE SET NULL,"
+ "foreign key (data_source_id) references data_sources(id) ON UPDATE SET NULL ON DELETE SET NULL)"; + "foreign key (data_source_id) references data_sources(id) ON UPDATE SET NULL ON DELETE SET NULL)";
} }
@ -413,7 +431,7 @@ public class RdbmsCentralRepoFactory {
/** /**
* Get the template for creating an index on the value column of an instance * Get the template for creating an index on the value column of an instance
* table. %s will exist in the template where the name of the new table will * table. %s will exist in the template where the name of the new table will
* be addedd. * be added.
* *
* @return a String which is a template for adding an index to the value * @return a String which is a template for adding an index to the value
* column of a _instances table * column of a _instances table
@ -426,7 +444,7 @@ public class RdbmsCentralRepoFactory {
/** /**
* Get the template for creating an index on the known_status column of an * Get the template for creating an index on the known_status column of an
* instance table. %s will exist in the template where the name of the new * instance table. %s will exist in the template where the name of the new
* table will be addedd. * table will be added.
* *
* @return a String which is a template for adding an index to the * @return a String which is a template for adding an index to the
* known_status column of a _instances table * known_status column of a _instances table
@ -439,7 +457,7 @@ public class RdbmsCentralRepoFactory {
/** /**
* Get the template for creating an index on the file_obj_id column of an * Get the template for creating an index on the file_obj_id column of an
* instance table. %s will exist in the template where the name of the new * instance table. %s will exist in the template where the name of the new
* table will be addedd. * table will be added.
* *
* @return a String which is a template for adding an index to the * @return a String which is a template for adding an index to the
* file_obj_id column of a _instances table * file_obj_id column of a _instances table
@ -525,7 +543,16 @@ public class RdbmsCentralRepoFactory {
} }
} }
private static String getOnConflictDoNothingClause(CentralRepoPlatforms selectedPlatform) {
switch (selectedPlatform) {
case POSTGRESQL:
return "ON CONFLICT DO NOTHING";
case SQLITE:
return "";
default:
return "";
}
}
/** /**
* Returns an ephemeral connection to the CR database. * Returns an ephemeral connection to the CR database.
* *
@ -541,4 +568,293 @@ public class RdbmsCentralRepoFactory {
return null; return null;
} }
} }
/**
* Creates the tables for Persona.
*
* @return True if success, False otherwise.
*/
private boolean createPersonaTables(Statement stmt) throws SQLException {
stmt.execute(getCreateConfidenceTableStatement(selectedPlatform));
stmt.execute(getCreateExaminersTableStatement(selectedPlatform));
stmt.execute(getCreatePersonaStatusTableStatement(selectedPlatform));
stmt.execute(getCreatePersonasTableStatement(selectedPlatform));
stmt.execute(getCreatePersonaAliasTableStatement(selectedPlatform));
stmt.execute(getCreatePersonaMetadataTableStatement(selectedPlatform));
stmt.execute(getCreatePersonaAccountsTableStatement(selectedPlatform));
return true;
}
/**
* Get the SQL string for creating a new account_types table in a central
* repository.
*
* @return SQL string for creating account_types table
*/
static String getCreateAccountTypesTableStatement(CentralRepoPlatforms selectedPlatform) {
return "CREATE TABLE IF NOT EXISTS account_types ("
+ getNumericPrimaryKeyClause("id", selectedPlatform)
+ "type_name TEXT NOT NULL,"
+ "display_name TEXT NOT NULL,"
+ "correlation_type_id " + getBigIntType(selectedPlatform) + " ,"
+ "CONSTRAINT type_name_unique UNIQUE (type_name),"
+ "FOREIGN KEY (correlation_type_id) REFERENCES correlation_types(id)"
+ ")";
}
/**
* Get the SQL String for creating a new confidence table in a central
* repository.
*
* @return SQL string for creating confidence table
*/
static String getCreateConfidenceTableStatement(CentralRepoPlatforms selectedPlatform) {
return "CREATE TABLE IF NOT EXISTS confidence ("
+ getNumericPrimaryKeyClause("id", selectedPlatform)
+ "confidence_id integer NOT NULL,"
+ "description TEXT,"
+ "CONSTRAINT level_unique UNIQUE (confidence_id)"
+ ")";
}
/**
* Get the SQL String for creating a new examiners table in a central
* repository.
*
* @return SQL string for creating examiners table
*/
static String getCreateExaminersTableStatement(CentralRepoPlatforms selectedPlatform) {
return "CREATE TABLE IF NOT EXISTS examiners ("
+ getNumericPrimaryKeyClause("id", selectedPlatform)
+ "login_name TEXT NOT NULL,"
+ "display_name TEXT,"
+ "CONSTRAINT login_name_unique UNIQUE(login_name)"
+ ")";
}
/**
* Get the SQL String for creating a new persona_status table in a central
* repository.
*
* @return SQL string for creating persona_status table
*/
static String getCreatePersonaStatusTableStatement(CentralRepoPlatforms selectedPlatform) {
return "CREATE TABLE IF NOT EXISTS persona_status ("
+ getNumericPrimaryKeyClause("id", selectedPlatform)
+ "status_id integer NOT NULL,"
+ "status TEXT NOT NULL,"
+ "CONSTRAINT status_unique UNIQUE(status_id)"
+ ")";
}
/**
* Get the SQL String for creating a new accounts table in a central
* repository.
*
* @return SQL string for creating accounts table
*/
static String getCreateAccountsTableStatement(CentralRepoPlatforms selectedPlatform) {
return "CREATE TABLE IF NOT EXISTS accounts ("
+ getNumericPrimaryKeyClause("id", selectedPlatform)
+ "account_type_id integer NOT NULL,"
+ "account_unique_identifier TEXT NOT NULL,"
+ "CONSTRAINT account_unique UNIQUE(account_type_id, account_unique_identifier),"
+ "FOREIGN KEY (account_type_id) REFERENCES account_types(id)"
+ ")";
}
/**
* Get the SQL String for creating a new personas table in a central
* repository.
*
* @return SQL string for creating personas table
*/
static String getCreatePersonasTableStatement(CentralRepoPlatforms selectedPlatform) {
return "CREATE TABLE IF NOT EXISTS personas ("
+ getNumericPrimaryKeyClause("id", selectedPlatform)
+ "uuid TEXT NOT NULL,"
+ "comment TEXT NOT NULL,"
+ "name TEXT NOT NULL,"
+ "created_date " + getBigIntType(selectedPlatform) + " ,"
+ "modified_date " + getBigIntType(selectedPlatform) + " ,"
+ "status_id integer NOT NULL,"
+ "examiner_id integer NOT NULL,"
+ "CONSTRAINT uuid_unique UNIQUE(uuid),"
+ "FOREIGN KEY (status_id) REFERENCES persona_status(status_id), "
+ "FOREIGN KEY (examiner_id) REFERENCES examiners(id)"
+ ")";
}
/**
* Get the SQL String for creating a new persona_alias table in a central
* repository.
*
* @return SQL string for creating persona_alias table
*/
static String getCreatePersonaAliasTableStatement(CentralRepoPlatforms selectedPlatform) {
return "CREATE TABLE IF NOT EXISTS persona_alias ("
+ getNumericPrimaryKeyClause("id", selectedPlatform)
+ "persona_id " + getBigIntType(selectedPlatform) + " ,"
+ "alias TEXT NOT NULL, "
+ "justification TEXT NOT NULL,"
+ "confidence_id integer NOT NULL,"
+ "date_added " + getBigIntType(selectedPlatform) + " ,"
+ "examiner_id integer NOT NULL,"
+ "FOREIGN KEY (persona_id) REFERENCES personas(id),"
+ "FOREIGN KEY (confidence_id) REFERENCES confidence(confidence_id),"
+ "FOREIGN KEY (examiner_id) REFERENCES examiners(id)"
+ ")";
}
/**
* Get the SQL String for creating a new persona_metadata table in a central
* repository.
*
* @return SQL string for creating persona_metadata table
*/
static String getCreatePersonaMetadataTableStatement(CentralRepoPlatforms selectedPlatform) {
return "CREATE TABLE IF NOT EXISTS persona_metadata ("
+ getNumericPrimaryKeyClause("id", selectedPlatform)
+ "persona_id " + getBigIntType(selectedPlatform) + " ,"
+ "name TEXT NOT NULL,"
+ "value TEXT NOT NULL,"
+ "justification TEXT NOT NULL,"
+ "confidence_id integer NOT NULL,"
+ "date_added " + getBigIntType(selectedPlatform) + " ,"
+ "examiner_id integer NOT NULL,"
+ "CONSTRAINT unique_metadata UNIQUE(persona_id, name),"
+ "FOREIGN KEY (persona_id) REFERENCES personas(id),"
+ "FOREIGN KEY (confidence_id) REFERENCES confidence(confidence_id),"
+ "FOREIGN KEY (examiner_id) REFERENCES examiners(id)"
+ ")";
}
/**
* Get the SQL String for creating a new persona_accounts table in a central
* repository.
*
* @return SQL string for creating persona_accounts table
*/
static String getCreatePersonaAccountsTableStatement(CentralRepoPlatforms selectedPlatform) {
return "CREATE TABLE IF NOT EXISTS persona_accounts ("
+ getNumericPrimaryKeyClause("id", selectedPlatform)
+ "persona_id " + getBigIntType(selectedPlatform) + " ,"
+ "account_id " + getBigIntType(selectedPlatform) + " ,"
+ "justification TEXT NOT NULL,"
+ "confidence_id integer NOT NULL,"
+ "date_added " + getBigIntType(selectedPlatform) + " ,"
+ "examiner_id integer NOT NULL,"
+ "FOREIGN KEY (persona_id) REFERENCES personas(id),"
+ "FOREIGN KEY (account_id) REFERENCES accounts(id),"
+ "FOREIGN KEY (confidence_id) REFERENCES confidence(confidence_id),"
+ "FOREIGN KEY (examiner_id) REFERENCES examiners(id)"
+ ")";
}
/**
* Inserts the default content in accounts related tables.
*
* @param conn Database connection to use.
*
* @return True if success, false otherwise.
*/
private boolean insertDefaultAccountsTablesContent(Connection conn) {
try (Statement stmt = conn.createStatement()) {
// Populate the account_types table
for (Account.Type type : Account.Type.PREDEFINED_ACCOUNT_TYPES) {
int correlationTypeId = getCorrelationTypeIdForAccountType(conn, type);
if (correlationTypeId > 0) {
String sqlString = String.format("INSERT INTO account_types (type_name, display_name, correlation_type_id) VALUES ('%s', '%s', %d)" + getOnConflictDoNothingClause(selectedPlatform),
type.getTypeName(), type.getDisplayName(), correlationTypeId);
stmt.execute(sqlString);
}
}
} catch (SQLException ex) {
LOGGER.log(Level.SEVERE, String.format("Failed to populate default data in Accounts tables."), ex);
return false;
}
return true;
}
/**
* Inserts the default content in persona related tables.
*
* @param conn Database connection to use.
*
* @return True if success, false otherwise.
*/
private boolean insertDefaultPersonaTablesContent(Connection conn) {
try (Statement stmt = conn.createStatement()) {
// populate the confidence table
for (Confidence confidence : Persona.Confidence.values()) {
String sqlString = "INSERT INTO confidence (confidence_id, description) VALUES ( " + confidence.getLevel() + ", '" + confidence.toString() + "')" //NON-NLS
+ getOnConflictDoNothingClause(selectedPlatform);
stmt.execute(sqlString);
}
// populate the persona_status table
for (PersonaStatus status : Persona.PersonaStatus.values()) {
String sqlString = "INSERT INTO persona_status (status_id, status) VALUES ( " + status.getStatus() + ", '" + status.toString() + "')" //NON-NLS
+ getOnConflictDoNothingClause(selectedPlatform);
stmt.execute(sqlString);
}
} catch (SQLException ex) {
LOGGER.log(Level.SEVERE, String.format("Failed to populate default data in Persona tables."), ex);
return false;
}
return true;
}
/**
* Returns the correlation type id for the given account type,
* from the correlation_types table.
*
* @param conn Connection to use for database query.
* @param accountType Account type to look for.
* '
* @return correlation type id.
*/
private int getCorrelationTypeIdForAccountType(Connection conn, Account.Type accountType) {
int typeId = -1;
if (accountType == Account.Type.EMAIL) {
typeId = CorrelationAttributeInstance.EMAIL_TYPE_ID;
} else if (accountType == Account.Type.PHONE) {
typeId = CorrelationAttributeInstance.PHONE_TYPE_ID;
} else {
String querySql = "SELECT * FROM correlation_types WHERE display_name=?";
try ( PreparedStatement preparedStatementQuery = conn.prepareStatement(querySql)) {
preparedStatementQuery.setString(1, accountType.getDisplayName());
try (ResultSet resultSet = preparedStatementQuery.executeQuery();) {
if (resultSet.next()) {
typeId = resultSet.getInt("id");
}
}
} catch (SQLException ex) {
LOGGER.log(Level.SEVERE, String.format("Failed to get correlation typeId for account type %s.", accountType.getTypeName()), ex);
}
}
return typeId;
}
} }

View File

@ -37,10 +37,10 @@ import org.sleuthkit.autopsy.coreutils.PlatformUtil;
* NOTE: This is public scope because the options panel calls it directly to * NOTE: This is public scope because the options panel calls it directly to
* set/get * set/get
*/ */
public final class SqliteCentralRepoSettings { public final class SqliteCentralRepoSettings implements CentralRepoDbSettings {
public final static String DEFAULT_DBNAME = "central_repository.db"; // NON-NLS
private final static Logger LOGGER = Logger.getLogger(SqliteCentralRepoSettings.class.getName()); private final static Logger LOGGER = Logger.getLogger(SqliteCentralRepoSettings.class.getName());
private final static String DEFAULT_DBNAME = "central_repository.db"; // NON-NLS
private final static String DEFAULT_DBDIRECTORY = PlatformUtil.getUserDirectory() + File.separator + "central_repository"; // NON-NLS private final static String DEFAULT_DBDIRECTORY = PlatformUtil.getUserDirectory() + File.separator + "central_repository"; // NON-NLS
private final static String JDBC_DRIVER = "org.sqlite.JDBC"; // NON-NLS private final static String JDBC_DRIVER = "org.sqlite.JDBC"; // NON-NLS
private final static String JDBC_BASE_URI = "jdbc:sqlite:"; // NON-NLS private final static String JDBC_BASE_URI = "jdbc:sqlite:"; // NON-NLS
@ -81,6 +81,18 @@ public final class SqliteCentralRepoSettings {
} }
} }
public String toString() {
return String.format("SqliteCentralRepoSettings: [db type: sqlite, directory: %s, name: %s]", getDbDirectory(), getDbName());
}
/**
* sets database directory and name to defaults
*/
public void setupDefaultSettings() {
dbName = DEFAULT_DBNAME;
dbDirectory = DEFAULT_DBDIRECTORY;
}
public void saveSettings() { public void saveSettings() {
createDbDirectory(); createDbDirectory();
@ -103,6 +115,13 @@ public final class SqliteCentralRepoSettings {
return (!dbFile.isDirectory()); return (!dbFile.isDirectory());
} }
@Override
public boolean verifyDatabaseExists() {
return dbDirectoryExists();
}
/** /**
* Verify that the db directory path exists. * Verify that the db directory path exists.
* *
@ -122,6 +141,16 @@ public final class SqliteCentralRepoSettings {
} }
/**
* creates database directory for sqlite database if it does not exist
* @return whether or not operation occurred successfully
*/
@Override
public boolean createDatabase() {
return createDbDirectory();
}
/** /**
* Create the db directory if it does not exist. * Create the db directory if it does not exist.
* *
@ -325,5 +354,4 @@ public final class SqliteCentralRepoSettings {
String getJDBCBaseURI() { String getJDBCBaseURI() {
return JDBC_BASE_URI; return JDBC_BASE_URI;
} }
} }

View File

@ -7,3 +7,6 @@ IngestEventsListener.prevCount.text=Number of previous {0}: {1}
IngestEventsListener.prevExists.text=Previously Seen Devices (Central Repository) IngestEventsListener.prevExists.text=Previously Seen Devices (Central Repository)
IngestEventsListener.prevTaggedSet.text=Previously Tagged As Notable (Central Repository) IngestEventsListener.prevTaggedSet.text=Previously Tagged As Notable (Central Repository)
Installer.centralRepoUpgradeFailed.title=Central repository disabled Installer.centralRepoUpgradeFailed.title=Central repository disabled
Installer.initialCreateSqlite.messageDesc=It will store information about all hashes and identifiers that you process. You can use this to ignore previously seen files and make connections between cases.
Installer.initialCreateSqlite.messageHeader=The Central Repository is not enabled. Would you like to enable it?
Installer.initialCreateSqlite.title=Enable Central Repository?

View File

@ -1,7 +1,7 @@
/* /*
* Central Repository * Central Repository
* *
* Copyright 2015-2018 Basis Technology Corp. * Copyright 2017-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -52,7 +52,6 @@ import org.sleuthkit.datamodel.ContentTag;
import org.sleuthkit.datamodel.TagName; import org.sleuthkit.datamodel.TagName;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.datamodel.TskDataException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
/** /**
@ -197,7 +196,7 @@ final class CaseEventListener implements PropertyChangeListener {
} }
} }
final CorrelationAttributeInstance eamArtifact = CorrelationAttributeUtil.makeInstanceFromContent(af); final CorrelationAttributeInstance eamArtifact = CorrelationAttributeUtil.makeCorrAttrFromFile(af);
if (eamArtifact != null) { if (eamArtifact != null) {
// send update to Central Repository db // send update to Central Repository db
@ -297,7 +296,7 @@ final class CaseEventListener implements PropertyChangeListener {
return; return;
} }
List<CorrelationAttributeInstance> convertedArtifacts = CorrelationAttributeUtil.makeInstancesFromBlackboardArtifact(bbArtifact, true); List<CorrelationAttributeInstance> convertedArtifacts = CorrelationAttributeUtil.makeCorrAttrsFromArtifact(bbArtifact);
for (CorrelationAttributeInstance eamArtifact : convertedArtifacts) { for (CorrelationAttributeInstance eamArtifact : convertedArtifacts) {
eamArtifact.setComment(comment); eamArtifact.setComment(comment);
try { try {
@ -370,7 +369,7 @@ final class CaseEventListener implements PropertyChangeListener {
if (!hasTagWithConflictingKnownStatus) { if (!hasTagWithConflictingKnownStatus) {
//Get the correlation atttributes that correspond to the current BlackboardArtifactTag if their status should be changed //Get the correlation atttributes that correspond to the current BlackboardArtifactTag if their status should be changed
//with the initial set of correlation attributes this should be a single correlation attribute //with the initial set of correlation attributes this should be a single correlation attribute
List<CorrelationAttributeInstance> convertedArtifacts = CorrelationAttributeUtil.makeInstancesFromBlackboardArtifact(bbTag.getArtifact(), true); List<CorrelationAttributeInstance> convertedArtifacts = CorrelationAttributeUtil.makeCorrAttrsFromArtifact(bbTag.getArtifact());
for (CorrelationAttributeInstance eamArtifact : convertedArtifacts) { for (CorrelationAttributeInstance eamArtifact : convertedArtifacts) {
CentralRepository.getInstance().setAttributeInstanceKnownStatus(eamArtifact, tagName.getKnownStatus()); CentralRepository.getInstance().setAttributeInstanceKnownStatus(eamArtifact, tagName.getKnownStatus());
} }
@ -406,12 +405,15 @@ final class CaseEventListener implements PropertyChangeListener {
} }
//if the file will have no tags with a status which would prevent the current status from being changed //if the file will have no tags with a status which would prevent the current status from being changed
if (!hasTagWithConflictingKnownStatus) { if (!hasTagWithConflictingKnownStatus) {
final CorrelationAttributeInstance eamArtifact = CorrelationAttributeUtil.makeInstanceFromContent(contentTag.getContent()); Content taggedContent = contentTag.getContent();
if (taggedContent instanceof AbstractFile) {
final CorrelationAttributeInstance eamArtifact = CorrelationAttributeUtil.makeCorrAttrFromFile((AbstractFile)taggedContent);
if (eamArtifact != null) { if (eamArtifact != null) {
CentralRepository.getInstance().setAttributeInstanceKnownStatus(eamArtifact, tagName.getKnownStatus()); CentralRepository.getInstance().setAttributeInstanceKnownStatus(eamArtifact, tagName.getKnownStatus());
} }
} }
} }
}
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Cannot update known status in central repository for tag: " + modifiedTagName, ex); //NON-NLS LOGGER.log(Level.SEVERE, "Cannot update known status in central repository for tag: " + modifiedTagName, ex); //NON-NLS
} catch (CentralRepoException ex) { } catch (CentralRepoException ex) {

View File

@ -1,7 +1,7 @@
/* /*
* Central Repository * Central Repository
* *
* Copyright 2015-2019 Basis Technology Corp. * Copyright 2017-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -456,7 +456,7 @@ public class IngestEventsListener {
for (BlackboardArtifact bbArtifact : bbArtifacts) { for (BlackboardArtifact bbArtifact : bbArtifacts) {
// eamArtifact will be null OR a EamArtifact containing one EamArtifactInstance. // eamArtifact will be null OR a EamArtifact containing one EamArtifactInstance.
List<CorrelationAttributeInstance> convertedArtifacts = CorrelationAttributeUtil.makeInstancesFromBlackboardArtifact(bbArtifact, true); List<CorrelationAttributeInstance> convertedArtifacts = CorrelationAttributeUtil.makeCorrAttrsFromArtifact(bbArtifact);
for (CorrelationAttributeInstance eamArtifact : convertedArtifacts) { for (CorrelationAttributeInstance eamArtifact : convertedArtifacts) {
try { try {
// Only do something with this artifact if it's unique within the job // Only do something with this artifact if it's unique within the job

View File

@ -1,7 +1,7 @@
/* /*
* Central Repository * Autopsy Forensic Browser
* *
* Copyright 2015-2017 Basis Technology Corp. * Copyright 2017-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -18,28 +18,54 @@
*/ */
package org.sleuthkit.autopsy.centralrepository.eventlisteners; package org.sleuthkit.autopsy.centralrepository.eventlisteners;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import java.util.logging.Level;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import org.openide.modules.ModuleInstall; import org.openide.modules.ModuleInstall;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.windows.WindowManager; import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoDbManager;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoDbUtil;
import org.sleuthkit.autopsy.core.RuntimeProperties; import org.sleuthkit.autopsy.core.RuntimeProperties;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
import org.sleuthkit.autopsy.coreutils.Version;
/** /**
* Install event listeners during module initialization * Adds/removes application event listeners responsible for adding data to the
* central repository, sets up a default, single-user SQLite central repository
* if no central repository is configured, and updates the central repository
* schema as required.
*
* TODO (Jira-6108): At first glance, this package seems to have become a rather
* strange package for the "package installer" for the CR to reside in. The
* org.sleuthkit.autopsy.centralrepository package would seem to be more
* appropriate with so much going on. However, having a central repository
* schema update occur in a "package installer" with no user feedback is not
* optimal. Furthermore, for a multi-user (collaborative) installation, a schema
* update should be done in a more controlled way by acquiring an exclusive
* coordination service lock and requiring shared locks to be acquired by nodes
* with open cases.
*/ */
public class Installer extends ModuleInstall { public class Installer extends ModuleInstall {
private static final Logger LOGGER = Logger.getLogger(Installer.class.getName()); private static final Logger logger = Logger.getLogger(Installer.class.getName());
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private final CaseEventListener pcl = new CaseEventListener();
private final IngestEventsListener ieListener = new IngestEventsListener();
private static Installer instance; private static Installer instance;
private final CaseEventListener caseEventListener = new CaseEventListener();
private final IngestEventsListener ingestEventListener = new IngestEventsListener();
/**
* Gets the singleton "package installer" used by the registered Installer
* for the Autopsy-Core module located in the org.sleuthkit.autopsy.core
* package.
*
* @return The "package installer" singleton for the
* org.sleuthkit.autopsy.centralrepository.eventlisteners package.
*/
public synchronized static Installer getDefault() { public synchronized static Installer getDefault() {
if (instance == null) { if (instance == null) {
instance = new Installer(); instance = new Installer();
@ -47,48 +73,189 @@ public class Installer extends ModuleInstall {
return instance; return instance;
} }
/**
* Constructs the singleton "package installer" used by the registered
* Installer for the Autopsy-Core module located in the
* org.sleuthkit.autopsy.core package.
*/
private Installer() { private Installer() {
super(); super();
} }
@NbBundle.Messages({"Installer.centralRepoUpgradeFailed.title=Central repository disabled"}) /*
* Adds/removes application event listeners responsible for adding data to
* the central repository, sets up a default, single-user SQLite central
* repository if no central repository is configured, and updates the
* central repository schema as required.
*
* Called by the registered Installer for the Autopsy-Core module located in
* the org.sleuthkit.autopsy.core package when the already installed
* Autopsy-Core module is restored (during application startup).
*/
@NbBundle.Messages({
"Installer.initialCreateSqlite.title=Enable Central Repository?",
"Installer.initialCreateSqlite.messageHeader=The Central Repository is not enabled. Would you like to enable it?",
"Installer.initialCreateSqlite.messageDesc=It will store information about all hashes and identifiers that you process. "
+ "You can use this to ignore previously seen files and make connections between cases."
})
@Override @Override
public void restored() { public void restored() {
Case.addPropertyChangeListener(pcl); addApplicationEventListeners();
ieListener.installListeners();
// Perform the database upgrade and inform the user if it fails if (Version.getBuildType() == Version.Type.RELEASE) {
try { setupDefaultCentralRepository();
CentralRepoDbUtil.upgradeDatabase(); }
} catch (CentralRepoException ex) {
updateCentralRepoSchema();
}
/**
* Adds the application event listeners responsible for adding data to the
* central repository.
*/
private void addApplicationEventListeners() {
Case.addPropertyChangeListener(caseEventListener);
ingestEventListener.installListeners();
}
/**
* Checks if the central repository has been set up and configured. If not,
* either offers to perform set up (running with a GUI) or does the set up
* unconditionally (not running with a GUI, e.g., in an automated ingest
* node).
*/
private void setupDefaultCentralRepository() {
Map<String, String> centralRepoSettings = ModuleSettings.getConfigSettings("CentralRepository");
String initializedStr = centralRepoSettings.get("initialized");
// check to see if the repo has been initialized asking to setup cr
boolean initialized = Boolean.parseBoolean(initializedStr);
// if it hasn't received that flag, check for a previous install where cr is already setup
if (!initialized) {
boolean prevRepo = Boolean.parseBoolean(centralRepoSettings.get("db.useCentralRepo"));
// if it has been previously set up and is in use, mark as previously initialized and save the settings
if (prevRepo) {
initialized = true;
ModuleSettings.setConfigSetting("CentralRepository", "initialized", "true");
}
}
// if central repository hasn't been previously initialized, initialize it
if (!initialized) {
// if running with a GUI, prompt the user
if (RuntimeProperties.runningWithGUI()) { if (RuntimeProperties.runningWithGUI()) {
WindowManager.getDefault().invokeWhenUIReady(() -> { try {
SwingUtilities.invokeAndWait(() -> {
try {
String dialogText
= "<html><body>"
+ "<div style='width: 400px;'>"
+ "<p>" + NbBundle.getMessage(this.getClass(), "Installer.initialCreateSqlite.messageHeader") + "</p>"
+ "<p style='margin-top: 10px'>" + NbBundle.getMessage(this.getClass(), "Installer.initialCreateSqlite.messageDesc") + "</p>"
+ "</div>"
+ "</body></html>";
if (JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(WindowManager.getDefault().getMainWindow(),
dialogText,
NbBundle.getMessage(this.getClass(), "Installer.initialCreateSqlite.title"),
JOptionPane.YES_NO_OPTION)) {
setupDefaultSqliteCentralRepo();
}
} catch (CentralRepoException ex) {
logger.log(Level.SEVERE, "There was an error while initializing the central repository database", ex);
doMessageBoxIfRunningInGUI(ex);
}
});
} catch (InterruptedException | InvocationTargetException ex) {
logger.log(Level.SEVERE, "There was an error while running the swing utility invoke later while creating the central repository database", ex);
}
} // if no GUI, just initialize
else {
try {
setupDefaultSqliteCentralRepo();
} catch (CentralRepoException ex) {
logger.log(Level.SEVERE, "There was an error while initializing the central repository database", ex);
doMessageBoxIfRunningInGUI(ex);
}
}
ModuleSettings.setConfigSetting("CentralRepository", "initialized", "true");
}
}
/**
* Sets up a default single-user SQLite central repository.
*
* @throws CentralRepoException If there is an error setting up teh central
* repository.
*/
private void setupDefaultSqliteCentralRepo() throws CentralRepoException {
CentralRepoDbManager manager = new CentralRepoDbManager();
manager.setupDefaultSqliteDb();
}
/**
* Update the central repository schema.
*/
private void updateCentralRepoSchema() {
try {
CentralRepoDbManager.upgradeDatabase();
} catch (CentralRepoException ex) {
logger.log(Level.SEVERE, "An error occurred updating the central repository schema", ex);
if (RuntimeProperties.runningWithGUI()) {
doMessageBoxIfRunningInGUI(ex);
}
}
}
/**
* Display a central repository exception in a message box if running with a
* GUI.
*
* @param ex The exception.
*/
@NbBundle.Messages({"Installer.centralRepoUpgradeFailed.title=Central repository disabled"})
private void doMessageBoxIfRunningInGUI(CentralRepoException ex) {
if (RuntimeProperties.runningWithGUI()) {
try {
SwingUtilities.invokeAndWait(() -> {
JOptionPane.showMessageDialog(null, JOptionPane.showMessageDialog(null,
ex.getUserMessage(), ex.getUserMessage(),
NbBundle.getMessage(this.getClass(), NbBundle.getMessage(this.getClass(), "Installer.centralRepoUpgradeFailed.title"),
"Installer.centralRepoUpgradeFailed.title"),
JOptionPane.ERROR_MESSAGE); JOptionPane.ERROR_MESSAGE);
}); });
} catch (InterruptedException | InvocationTargetException e) {
logger.log(Level.WARNING, e.getMessage(), e);
} }
} }
} }
@Override
public boolean closing() {
//platform about to close
return true;
}
@Override @Override
public void uninstalled() { public void uninstalled() {
//module is being unloaded /*
* TODO (Jira-6108): This code is erronoeous. As documented at
Case.removePropertyChangeListener(pcl); * http://bits.netbeans.org/dev/javadoc/org-openide-modules/org/openide/modules/ModuleInstall.html#uninstalled--
pcl.shutdown(); *
ieListener.shutdown(); * "Called when the module is disabled while the application is still
ieListener.uninstallListeners(); * running. Should remove whatever functionality that it had registered
* in ModuleInstall.restored().
// TODO: remove thread pool *
* Beware: in practice there is no way to
* ensure that this method will really be called. The module might
* simply be deleted or disabled while the application is not running.
* In fact this is always the case in NetBeans 6.0; the Plugin Manager
* only uninstalls or disables modules between restarts. This method
* will still be called if you reload a module during development."
*
* THIS CODE IS NEVER EXECUTED.
*/
Case.removePropertyChangeListener(caseEventListener);
caseEventListener.shutdown();
ingestEventListener.shutdown();
ingestEventListener.uninstallListeners();
} }
} }

View File

@ -67,7 +67,7 @@ final class CentralRepoIngestModule implements FileIngestModule {
private static final String MODULE_NAME = CentralRepoIngestModuleFactory.getModuleName(); private static final String MODULE_NAME = CentralRepoIngestModuleFactory.getModuleName();
static final boolean DEFAULT_FLAG_TAGGED_NOTABLE_ITEMS = true; static final boolean DEFAULT_FLAG_TAGGED_NOTABLE_ITEMS = true;
static final boolean DEFAULT_FLAG_PREVIOUS_DEVICES = true; static final boolean DEFAULT_FLAG_PREVIOUS_DEVICES = false;
static final boolean DEFAULT_CREATE_CR_PROPERTIES = true; static final boolean DEFAULT_CREATE_CR_PROPERTIES = true;
private final static Logger logger = Logger.getLogger(CentralRepoIngestModule.class.getName()); private final static Logger logger = Logger.getLogger(CentralRepoIngestModule.class.getName());

View File

@ -35,14 +35,15 @@ import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener; import javax.swing.event.DocumentListener;
import javax.swing.filechooser.FileFilter; import javax.swing.filechooser.FileFilter;
import org.netbeans.spi.options.OptionsPanelController; import org.netbeans.spi.options.OptionsPanelController;
import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.openide.windows.WindowManager; import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoDbManager;
import org.sleuthkit.autopsy.corecomponents.TextPrompt; import org.sleuthkit.autopsy.corecomponents.TextPrompt;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoPlatforms; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoPlatforms;
import static org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoPlatforms.SQLITE; import org.sleuthkit.autopsy.centralrepository.datamodel.DatabaseTestResult;
import org.sleuthkit.autopsy.centralrepository.datamodel.PostgresCentralRepoSettings;
import org.sleuthkit.autopsy.centralrepository.datamodel.SqliteCentralRepoSettings; import org.sleuthkit.autopsy.centralrepository.datamodel.SqliteCentralRepoSettings;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.centralrepository.datamodel.RdbmsCentralRepoFactory; import org.sleuthkit.autopsy.centralrepository.datamodel.RdbmsCentralRepoFactory;
@ -54,17 +55,12 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.RdbmsCentralRepoFactory
public class EamDbSettingsDialog extends JDialog { public class EamDbSettingsDialog extends JDialog {
private static final Logger logger = Logger.getLogger(EamDbSettingsDialog.class.getName()); private static final Logger logger = Logger.getLogger(EamDbSettingsDialog.class.getName());
private static final String CENTRAL_REPO_DB_NAME = "central_repository";
private static final String CENTRAL_REPO_SQLITE_EXT = ".db";
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private final Collection<JTextField> textBoxes; private final Collection<JTextField> textBoxes;
private final TextBoxChangedListener textBoxChangedListener; private final TextBoxChangedListener textBoxChangedListener;
private final CentralRepoDbManager manager = new CentralRepoDbManager();
private final PostgresCentralRepoSettings dbSettingsPostgres;
private final SqliteCentralRepoSettings dbSettingsSqlite;
private DatabaseTestResult testingStatus;
private CentralRepoPlatforms selectedPlatform;
private boolean configurationChanged = false;
/** /**
* Creates new form EamDbSettingsDialog * Creates new form EamDbSettingsDialog
@ -73,7 +69,6 @@ public class EamDbSettingsDialog extends JDialog {
"EamDbSettingsDialog.lbSingleUserSqLite.text=SQLite should only be used by one examiner at a time.", "EamDbSettingsDialog.lbSingleUserSqLite.text=SQLite should only be used by one examiner at a time.",
"EamDbSettingsDialog.lbDatabaseType.text=Database Type :", "EamDbSettingsDialog.lbDatabaseType.text=Database Type :",
"EamDbSettingsDialog.fcDatabasePath.title=Select location for central_repository.db"}) "EamDbSettingsDialog.fcDatabasePath.title=Select location for central_repository.db"})
public EamDbSettingsDialog() { public EamDbSettingsDialog() {
super((JFrame) WindowManager.getDefault().getMainWindow(), super((JFrame) WindowManager.getDefault().getMainWindow(),
Bundle.EamDbSettingsDialog_title_text(), Bundle.EamDbSettingsDialog_title_text(),
@ -81,12 +76,6 @@ public class EamDbSettingsDialog extends JDialog {
textBoxes = new ArrayList<>(); textBoxes = new ArrayList<>();
textBoxChangedListener = new TextBoxChangedListener(); textBoxChangedListener = new TextBoxChangedListener();
dbSettingsPostgres = new PostgresCentralRepoSettings();
dbSettingsSqlite = new SqliteCentralRepoSettings();
selectedPlatform = CentralRepoPlatforms.getSelectedPlatform();
if (selectedPlatform == null || selectedPlatform.equals(CentralRepoPlatforms.DISABLED)) {
selectedPlatform = CentralRepoPlatforms.POSTGRESQL;
}
initComponents(); initComponents();
fcDatabasePath.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); fcDatabasePath.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
@ -98,7 +87,7 @@ public class EamDbSettingsDialog extends JDialog {
if (pathname.isDirectory()) { if (pathname.isDirectory()) {
return true; return true;
} }
return pathname.getName().toLowerCase().equals((CENTRAL_REPO_DB_NAME + CENTRAL_REPO_SQLITE_EXT).toLowerCase()); return pathname.getName().equalsIgnoreCase(SqliteCentralRepoSettings.DEFAULT_DBNAME);
} }
@Override @Override
@ -106,13 +95,78 @@ public class EamDbSettingsDialog extends JDialog {
return "Directories and Central Repository databases"; return "Directories and Central Repository databases";
} }
}); });
cbDatabaseType.setSelectedItem(selectedPlatform); cbDatabaseType.setSelectedItem(manager.getSelectedPlatform());
customizeComponents(); customizeComponents();
valid(); valid();
display(); display();
} }
/**
* prompts user based on testing status (i.e. failure to connect, invalid schema, db does not exist, etc.)
* @return whether or not the ultimate status after prompts is okay to continue
*/
@NbBundle.Messages({"EamDbSettingsDialog.okButton.corruptDatabaseExists.title=Error Loading Database",
"EamDbSettingsDialog.okButton.corruptDatabaseExists.message=Database exists but is not the right format. Manually delete it or choose a different path (if applicable).",
"EamDbSettingsDialog.okButton.createDbDialog.title=Database Does Not Exist",
"EamDbSettingsDialog.okButton.createDbDialog.message=Database does not exist, would you like to create it?",
"EamDbSettingsDialog.okButton.databaseConnectionFailed.title=Database Connection Failed",
"EamDbSettingsDialog.okButton.databaseConnectionFailed.message=Unable to connect to database please check your settings and try again.",
"EamDbSettingsDialog.okButton.createSQLiteDbError.message=Unable to create SQLite Database, please ensure location exists and you have write permissions and try again.",
"EamDbSettingsDialog.okButton.createPostgresDbError.message=Unable to create Postgres Database, please ensure address, port, and login credentials are correct for Postgres server and try again.",
"EamDbSettingsDialog.okButton.createDbError.title=Unable to Create Database"})
private boolean promptTestStatusWarnings() {
if (manager.getStatus() == DatabaseTestResult.CONNECTION_FAILED) {
JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
Bundle.EamDbSettingsDialog_okButton_databaseConnectionFailed_message(),
Bundle.EamDbSettingsDialog_okButton_databaseConnectionFailed_title(),
JOptionPane.WARNING_MESSAGE);
} else if (manager.getStatus() == DatabaseTestResult.SCHEMA_INVALID) {
// There's an existing database or file, but it's not in our format.
JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
Bundle.EamDbSettingsDialog_okButton_corruptDatabaseExists_message(),
Bundle.EamDbSettingsDialog_okButton_corruptDatabaseExists_title(),
JOptionPane.WARNING_MESSAGE);
} else if (manager.getStatus() == DatabaseTestResult.DB_DOES_NOT_EXIST) {
//database doesn't exist. do you want to create?
if (JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(WindowManager.getDefault().getMainWindow(),
Bundle.EamDbSettingsDialog_okButton_createDbDialog_message(),
Bundle.EamDbSettingsDialog_okButton_createDbDialog_title(),
JOptionPane.YES_NO_OPTION)) {
try {
manager.createDb();
}
catch (CentralRepoException e) {
// in the event that there is a failure to connect, notify user with corresponding message
String errorMessage;
switch (manager.getSelectedPlatform()) {
case POSTGRESQL:
errorMessage = Bundle.EamDbSettingsDialog_okButton_createPostgresDbError_message();
break;
case SQLITE:
errorMessage = Bundle.EamDbSettingsDialog_okButton_createSQLiteDbError_message();
break;
default:
errorMessage = "";
break;
}
JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
errorMessage,
Bundle.EamDbSettingsDialog_okButton_createDbError_title(),
JOptionPane.WARNING_MESSAGE);
}
valid();
}
}
return (manager.getStatus() == DatabaseTestResult.TESTEDOK);
}
/** /**
* This method is called from within the constructor to initialize the form. * 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 * WARNING: Do NOT modify this code. The content of this method is always
@ -359,21 +413,16 @@ public class EamDbSettingsDialog extends JDialog {
private void customizeComponents() { private void customizeComponents() {
setTextPrompts(); setTextPrompts();
setTextBoxListeners(); setTextBoxListeners();
switch (selectedPlatform) { manager.clearStatus();
case SQLITE: if (manager.getSelectedPlatform() == CentralRepoPlatforms.SQLITE) {
testingStatus = DatabaseTestResult.UNTESTED;
updatePostgresFields(false); updatePostgresFields(false);
updateSqliteFields(true); updateSqliteFields(true);
break; }
default: else {
POSTGRESQL:
testingStatus = DatabaseTestResult.UNTESTED;
updatePostgresFields(true); updatePostgresFields(true);
updateSqliteFields(false); updateSqliteFields(false);
break;
} }
displayDatabaseSettings(selectedPlatform.equals(CentralRepoPlatforms.POSTGRESQL)); displayDatabaseSettings(CentralRepoPlatforms.POSTGRESQL.equals(manager.getSelectedPlatform()));
} }
private void display() { private void display() {
@ -383,7 +432,7 @@ public class EamDbSettingsDialog extends JDialog {
@Messages({"EamDbSettingsDialog.chooserPath.failedToGetDbPathMsg=Selected database path is invalid. Try again."}) @Messages({"EamDbSettingsDialog.chooserPath.failedToGetDbPathMsg=Selected database path is invalid. Try again."})
private void bnDatabasePathFileOpenActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnDatabasePathFileOpenActionPerformed private void bnDatabasePathFileOpenActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnDatabasePathFileOpenActionPerformed
fcDatabasePath.setSelectedFile(new File(dbSettingsSqlite.getDbDirectory())); fcDatabasePath.setSelectedFile(new File(manager.getDbSettingsSqlite().getDbDirectory()));
if (fcDatabasePath.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { if (fcDatabasePath.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
File databaseFile = fcDatabasePath.getSelectedFile(); File databaseFile = fcDatabasePath.getSelectedFile();
if (databaseFile.isFile()) { if (databaseFile.isFile()) {
@ -399,176 +448,24 @@ public class EamDbSettingsDialog extends JDialog {
} }
}//GEN-LAST:event_bnDatabasePathFileOpenActionPerformed }//GEN-LAST:event_bnDatabasePathFileOpenActionPerformed
private void testDbSettings() { @NbBundle.Messages({"EamDbSettingsDialog.okButton.errorTitle.text=Restart Required.",
switch (selectedPlatform) {
case POSTGRESQL:
if (dbSettingsPostgres.verifyConnection()) {
if (dbSettingsPostgres.verifyDatabaseExists()) {
if (dbSettingsPostgres.verifyDatabaseSchema()) {
testingStatus = DatabaseTestResult.TESTEDOK;
} else {
testingStatus = DatabaseTestResult.SCHEMA_INVALID;
}
} else {
testingStatus = DatabaseTestResult.DB_DOES_NOT_EXIST;
}
} else {
testingStatus = DatabaseTestResult.CONNECTION_FAILED;
}
break;
case SQLITE:
if (dbSettingsSqlite.dbFileExists()) {
if (dbSettingsSqlite.verifyConnection()) {
if (dbSettingsSqlite.verifyDatabaseSchema()) {
testingStatus = DatabaseTestResult.TESTEDOK;
} else {
testingStatus = DatabaseTestResult.SCHEMA_INVALID;
}
} else {
testingStatus = DatabaseTestResult.SCHEMA_INVALID;
}
} else {
testingStatus = DatabaseTestResult.DB_DOES_NOT_EXIST;
}
break;
}
valid();
}
@Messages({"EamDbSettingsDialog.okButton.createDbError.title=Unable to Create Database",
"EamDbSettingsDialog.okButton.createSQLiteDbError.message=Unable to create SQLite Database, please ensure location exists and you have write permissions and try again.",
"EamDbSettingsDialog.okButton.createPostgresDbError.message=Unable to create Postgres Database, please ensure address, port, and login credentials are correct for Postgres server and try again."})
private void createDb() {
boolean result = false;
boolean dbCreated = true;
switch (selectedPlatform) {
case POSTGRESQL:
if (!dbSettingsPostgres.verifyDatabaseExists()) {
dbCreated = dbSettingsPostgres.createDatabase();
}
if (dbCreated) {
try {
RdbmsCentralRepoFactory centralRepoSchemaFactory = new RdbmsCentralRepoFactory(selectedPlatform, dbSettingsPostgres);
result = centralRepoSchemaFactory.initializeDatabaseSchema()
&& centralRepoSchemaFactory.insertDefaultDatabaseContent();
} catch (CentralRepoException ex) {
logger.log(Level.SEVERE, "Unable to initialize database schema or insert contents into Postgres central repository.", ex);
}
}
if (!result) {
// Remove the incomplete database
if (dbCreated) {
// RAMAN TBD: migrate deleteDatabase() to RdbmsCentralRepoFactory
dbSettingsPostgres.deleteDatabase();
}
JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
Bundle.EamDbSettingsDialog_okButton_createPostgresDbError_message(),
Bundle.EamDbSettingsDialog_okButton_createDbError_title(),
JOptionPane.WARNING_MESSAGE);
logger.severe("Unable to initialize database schema or insert contents into central repository.");
return;
}
break;
case SQLITE:
if (!dbSettingsSqlite.dbDirectoryExists()) {
dbCreated = dbSettingsSqlite.createDbDirectory();
}
if (dbCreated) {
try {
RdbmsCentralRepoFactory centralRepoSchemaFactory = new RdbmsCentralRepoFactory(selectedPlatform, dbSettingsSqlite);
result = centralRepoSchemaFactory.initializeDatabaseSchema()
&& centralRepoSchemaFactory.insertDefaultDatabaseContent();
} catch (CentralRepoException ex) {
logger.log(Level.SEVERE, "Unable to initialize database schema or insert contents into SQLite central repository.", ex);
}
}
if (!result) {
if (dbCreated) {
dbSettingsSqlite.deleteDatabase();
}
JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
Bundle.EamDbSettingsDialog_okButton_createSQLiteDbError_message(),
Bundle.EamDbSettingsDialog_okButton_createDbError_title(),
JOptionPane.WARNING_MESSAGE);
logger.severe("Unable to initialize database schema or insert contents into central repository.");
return;
}
break;
}
testingStatus = DatabaseTestResult.TESTEDOK;
valid();
}
/**
* Returns if changes to the central repository configuration were
* successfully applied
*
* @return true if the database configuration was successfully changed false
* if it was not
*/
boolean wasConfigurationChanged() {
return configurationChanged;
}
@Messages({"EamDbSettingsDialog.okButton.errorTitle.text=Restart Required.",
"EamDbSettingsDialog.okButton.errorMsg.text=Please restart Autopsy to begin using the new database platform.", "EamDbSettingsDialog.okButton.errorMsg.text=Please restart Autopsy to begin using the new database platform.",
"EamDbSettingsDialog.okButton.connectionErrorMsg.text=Failed to connect to central repository database.", "EamDbSettingsDialog.okButton.connectionErrorMsg.text=Failed to connect to central repository database."})
"EamDbSettingsDialog.okButton.corruptDatabaseExists.title=Error Loading Database",
"EamDbSettingsDialog.okButton.corruptDatabaseExists.message=Database exists but is not the right format. Manually delete it or choose a different path (if applicable).",
"EamDbSettingsDialog.okButton.createDbDialog.title=Database Does Not Exist",
"EamDbSettingsDialog.okButton.createDbDialog.message=Database does not exist, would you like to create it?",
"EamDbSettingsDialog.okButton.databaseConnectionFailed.title=Database Connection Failed",
"EamDbSettingsDialog.okButton.databaseConnectionFailed.message=Unable to connect to database please check your settings and try again."})
private void bnOkActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnOkActionPerformed private void bnOkActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnOkActionPerformed
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
testDbSettings(); manager.testStatus();
if (testingStatus == DatabaseTestResult.CONNECTION_FAILED) { valid();
JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
Bundle.EamDbSettingsDialog_okButton_databaseConnectionFailed_message(),
Bundle.EamDbSettingsDialog_okButton_databaseConnectionFailed_title(),
JOptionPane.WARNING_MESSAGE);
} else if (testingStatus == DatabaseTestResult.SCHEMA_INVALID) {
// There's an existing database or file, but it's not in our format.
JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
Bundle.EamDbSettingsDialog_okButton_corruptDatabaseExists_message(),
Bundle.EamDbSettingsDialog_okButton_corruptDatabaseExists_title(),
JOptionPane.WARNING_MESSAGE);
} else if (testingStatus == DatabaseTestResult.DB_DOES_NOT_EXIST) {
//database doesn't exist do you want to create
if (JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(WindowManager.getDefault().getMainWindow(),
Bundle.EamDbSettingsDialog_okButton_createDbDialog_message(),
Bundle.EamDbSettingsDialog_okButton_createDbDialog_title(),
JOptionPane.YES_NO_OPTION)) {
createDb();
}
}
if (testingStatus != DatabaseTestResult.TESTEDOK) { boolean testedOk = promptTestStatusWarnings();
if (!testedOk) {
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
return; return;
} }
/**
* We have to shutdown the previous platform's connection pool first;
* assuming it wasn't DISABLED. This will close any existing idle
* connections.
*
* The next use of an EamDb API method will start a new connection pool
* using those new settings.
*/
try{ try{
CentralRepository previousDbManager = CentralRepository.getInstance(); manager.saveNewCentralRepo();
if (null != previousDbManager) {
// NOTE: do not set/save the seleted platform before calling this.
CentralRepository.getInstance().shutdownConnections();
} }
} catch (CentralRepoException ex) { catch (CentralRepoException e) {
logger.log(Level.SEVERE, "Failed to close database connections in previously selected platform.", ex); // NON-NLS
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(() -> {
JOptionPane.showMessageDialog(this, JOptionPane.showMessageDialog(this,
Bundle.EamDbSettingsDialog_okButton_errorMsg_text(), Bundle.EamDbSettingsDialog_okButton_errorMsg_text(),
@ -577,62 +474,35 @@ public class EamDbSettingsDialog extends JDialog {
}); });
} }
// Even if we fail to close the existing connections, make sure that we
// save the new connection settings, so an Autopsy restart will correctly
// start with the new settings.
CentralRepoPlatforms.setSelectedPlatform(selectedPlatform.name());
CentralRepoPlatforms.saveSelectedPlatform();
switch (selectedPlatform) {
case POSTGRESQL:
// save the new PostgreSQL settings
dbSettingsPostgres.saveSettings();
// Load those newly saved settings into the postgres db manager instance
// in case we are still using the same instance.
try {
CentralRepository.getInstance().updateSettings();
configurationChanged = true;
} catch (CentralRepoException ex) {
logger.log(Level.SEVERE, Bundle.EamDbSettingsDialog_okButton_connectionErrorMsg_text(), ex); //NON-NLS
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
return;
}
break;
case SQLITE:
// save the new SQLite settings
dbSettingsSqlite.saveSettings();
// Load those newly saved settings into the sqlite db manager instance
// in case we are still using the same instance.
try {
CentralRepository.getInstance().updateSettings();
configurationChanged = true;
} catch (CentralRepoException ex) {
logger.log(Level.SEVERE, Bundle.EamDbSettingsDialog_okButton_connectionErrorMsg_text(), ex); //NON-NLS
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
return;
}
break;
case DISABLED:
break;
}
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
dispose(); dispose();
}//GEN-LAST:event_bnOkActionPerformed }//GEN-LAST:event_bnOkActionPerformed
/**
* Returns if changes to the central repository configuration were
* successfully applied
*
* @return true if the database configuration was successfully changed false
* if it was not
*/
public boolean wasConfigurationChanged() {
return manager.wasConfigurationChanged();
}
private void bnCancelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnCancelActionPerformed private void bnCancelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnCancelActionPerformed
dispose(); dispose();
}//GEN-LAST:event_bnCancelActionPerformed }//GEN-LAST:event_bnCancelActionPerformed
private void cbDatabaseTypeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbDatabaseTypeActionPerformed private void cbDatabaseTypeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbDatabaseTypeActionPerformed
selectedPlatform = (CentralRepoPlatforms) cbDatabaseType.getSelectedItem(); manager.setSelectedPlatform((CentralRepoPlatforms) cbDatabaseType.getSelectedItem());
customizeComponents(); customizeComponents();
}//GEN-LAST:event_cbDatabaseTypeActionPerformed }//GEN-LAST:event_cbDatabaseTypeActionPerformed
private void updateFullDbPath() { private void updateFullDbPath() {
dataBaseFileTextArea.setText(tfDatabasePath.getText() + File.separator + CENTRAL_REPO_DB_NAME + CENTRAL_REPO_SQLITE_EXT); dataBaseFileTextArea.setText(tfDatabasePath.getText() + File.separator + SqliteCentralRepoSettings.DEFAULT_DBNAME);
dataBaseFileTextArea.setCaretPosition(dataBaseFileTextArea.getText().length()); dataBaseFileTextArea.setCaretPosition(dataBaseFileTextArea.getText().length());
} }
@ -670,13 +540,13 @@ public class EamDbSettingsDialog extends JDialog {
} }
private void updatePostgresFields(boolean enabled) { private void updatePostgresFields(boolean enabled) {
tbDbHostname.setText(enabled ? dbSettingsPostgres.getHost() : ""); tbDbHostname.setText(enabled ? manager.getDbSettingsPostgres().getHost() : "");
tbDbHostname.setEnabled(enabled); tbDbHostname.setEnabled(enabled);
tbDbPort.setText(enabled ? Integer.toString(dbSettingsPostgres.getPort()) : ""); tbDbPort.setText(enabled ? Integer.toString(manager.getDbSettingsPostgres().getPort()) : "");
tbDbPort.setEnabled(enabled); tbDbPort.setEnabled(enabled);
tbDbUsername.setText(enabled ? dbSettingsPostgres.getUserName() : ""); tbDbUsername.setText(enabled ? manager.getDbSettingsPostgres().getUserName() : "");
tbDbUsername.setEnabled(enabled); tbDbUsername.setEnabled(enabled);
jpDbPassword.setText(enabled ? dbSettingsPostgres.getPassword() : ""); jpDbPassword.setText(enabled ? manager.getDbSettingsPostgres().getPassword() : "");
jpDbPassword.setEnabled(enabled); jpDbPassword.setEnabled(enabled);
} }
@ -687,7 +557,7 @@ public class EamDbSettingsDialog extends JDialog {
* @param enabled * @param enabled
*/ */
private void updateSqliteFields(boolean enabled) { private void updateSqliteFields(boolean enabled) {
tfDatabasePath.setText(enabled ? dbSettingsSqlite.getDbDirectory() : ""); tfDatabasePath.setText(enabled ? manager.getDbSettingsSqlite().getDbDirectory() : "");
tfDatabasePath.setEnabled(enabled); tfDatabasePath.setEnabled(enabled);
bnDatabasePathFileOpen.setEnabled(enabled); bnDatabasePathFileOpen.setEnabled(enabled);
} }
@ -740,22 +610,15 @@ public class EamDbSettingsDialog extends JDialog {
@Messages({"EamDbSettingsDialog.validation.incompleteFields=Fill in all values for the selected database."}) @Messages({"EamDbSettingsDialog.validation.incompleteFields=Fill in all values for the selected database."})
private boolean databaseFieldsArePopulated() { private boolean databaseFieldsArePopulated() {
boolean result = true; boolean result = true;
switch (selectedPlatform) { if (manager.getSelectedPlatform() == CentralRepoPlatforms.POSTGRESQL) {
case POSTGRESQL:
result = !tbDbHostname.getText().trim().isEmpty() result = !tbDbHostname.getText().trim().isEmpty()
&& !tbDbPort.getText().trim().isEmpty() && !tbDbPort.getText().trim().isEmpty()
// && !tbDbName.getText().trim().isEmpty() // && !tbDbName.getText().trim().isEmpty()
&& !tbDbUsername.getText().trim().isEmpty() && !tbDbUsername.getText().trim().isEmpty()
&& 0 < jpDbPassword.getPassword().length; && 0 < jpDbPassword.getPassword().length;
break;
case SQLITE:
result = !tfDatabasePath.getText().trim().isEmpty();
break;
} }
else if (manager.getSelectedPlatform() == CentralRepoPlatforms.SQLITE) {
if (!result) { result = !tfDatabasePath.getText().trim().isEmpty();
} }
return result; return result;
@ -771,66 +634,6 @@ public class EamDbSettingsDialog extends JDialog {
&& databaseSettingsAreValid(); && databaseSettingsAreValid();
} }
/**
* Tests whether or not the database settings are valid.
*
* @return True or false.
*/
private boolean databaseSettingsAreValid() {
boolean result = true;
StringBuilder guidanceText = new StringBuilder();
switch (selectedPlatform) {
case POSTGRESQL:
try {
dbSettingsPostgres.setHost(tbDbHostname.getText().trim());
} catch (CentralRepoException ex) {
guidanceText.append(ex.getMessage());
result = false;
}
try {
dbSettingsPostgres.setPort(Integer.valueOf(tbDbPort.getText().trim()));
} catch (NumberFormatException | CentralRepoException ex) {
guidanceText.append(ex.getMessage());
result = false;
}
try {
dbSettingsPostgres.setDbName(CENTRAL_REPO_DB_NAME);
} catch (CentralRepoException ex) {
guidanceText.append(ex.getMessage());
result = false;
}
try {
dbSettingsPostgres.setUserName(tbDbUsername.getText().trim());
} catch (CentralRepoException ex) {
guidanceText.append(ex.getMessage());
result = false;
}
try {
dbSettingsPostgres.setPassword(new String(jpDbPassword.getPassword()));
} catch (CentralRepoException ex) {
guidanceText.append(ex.getMessage());
result = false;
}
break;
case SQLITE:
try {
File databasePath = new File(tfDatabasePath.getText());
dbSettingsSqlite.setDbName(CENTRAL_REPO_DB_NAME + CENTRAL_REPO_SQLITE_EXT);
dbSettingsSqlite.setDbDirectory(databasePath.getPath());
} catch (CentralRepoException ex) {
guidanceText.append(ex.getMessage());
result = false;
}
break;
}
return result;
}
/** /**
* Validates that the form is filled out correctly for our usage. * Validates that the form is filled out correctly for our usage.
@ -858,6 +661,29 @@ public class EamDbSettingsDialog extends JDialog {
} }
/**
* Tests whether or not the database settings are valid.
*
* @return True or false.
*/
private boolean databaseSettingsAreValid() {
try {
manager.testDatabaseSettingsAreValid(
tbDbHostname.getText().trim(),
tbDbPort.getText().trim(),
tbDbUsername.getText().trim(),
tfDatabasePath.getText().trim(),
new String(jpDbPassword.getPassword()));
}
catch (CentralRepoException | NumberFormatException | IllegalStateException e) {
return false;
}
return true;
}
/** /**
* Used to listen for changes in text boxes. It lets the panel know things * Used to listen for changes in text boxes. It lets the panel know things
* have been updated and that validation needs to happen. * have been updated and that validation needs to happen.
@ -867,7 +693,7 @@ public class EamDbSettingsDialog extends JDialog {
@Override @Override
public void changedUpdate(DocumentEvent e) { public void changedUpdate(DocumentEvent e) {
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
testingStatus = DatabaseTestResult.UNTESTED; manager.clearStatus();
updateFullDbPath(); updateFullDbPath();
valid(); valid();
} }
@ -875,7 +701,7 @@ public class EamDbSettingsDialog extends JDialog {
@Override @Override
public void insertUpdate(DocumentEvent e) { public void insertUpdate(DocumentEvent e) {
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
testingStatus = DatabaseTestResult.UNTESTED; manager.clearStatus();
updateFullDbPath(); updateFullDbPath();
valid(); valid();
} }
@ -883,20 +709,13 @@ public class EamDbSettingsDialog extends JDialog {
@Override @Override
public void removeUpdate(DocumentEvent e) { public void removeUpdate(DocumentEvent e) {
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
testingStatus = DatabaseTestResult.UNTESTED; manager.clearStatus();
updateFullDbPath(); updateFullDbPath();
valid(); valid();
} }
} }
private enum DatabaseTestResult {
UNTESTED,
CONNECTION_FAILED,
SCHEMA_INVALID,
DB_DOES_NOT_EXIST,
TESTEDOK;
}
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton bnCancel; private javax.swing.JButton bnCancel;

View File

@ -31,6 +31,7 @@ import org.netbeans.spi.options.OptionsPanelController;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoDbManager;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
import org.sleuthkit.autopsy.corecomponents.OptionsPanel; import org.sleuthkit.autopsy.corecomponents.OptionsPanel;
import org.sleuthkit.autopsy.events.AutopsyEvent; import org.sleuthkit.autopsy.events.AutopsyEvent;
@ -86,7 +87,7 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
try { try {
CentralRepoDbUtil.upgradeDatabase(); CentralRepoDbManager.upgradeDatabase();
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
} catch (CentralRepoException ex) { } catch (CentralRepoException ex) {
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));

View File

@ -2,7 +2,7 @@
* *
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2018-2019 Basis Technology Corp. * Copyright 2018-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -123,8 +123,7 @@ final public class CommonAttributeCaseSearchResults {
if (currentCaseDataSourceMap == null) { //there are no results if (currentCaseDataSourceMap == null) { //there are no results
return filteredCaseNameToDataSourcesTree; return filteredCaseNameToDataSourcesTree;
} }
CorrelationAttributeInstance.Type attributeType = CorrelationAttributeInstance CorrelationAttributeInstance.Type attributeType = CentralRepository.getInstance().getCorrelationTypes()
.getDefaultCorrelationTypes()
.stream() .stream()
.filter(filterType -> filterType.getId() == resultTypeId) .filter(filterType -> filterType.getId() == resultTypeId)
.findFirst().get(); .findFirst().get();

View File

@ -128,13 +128,12 @@ final public class CommonAttributeCountSearchResults {
return; return;
} }
CorrelationAttributeInstance.Type attributeType = CorrelationAttributeInstance CentralRepository eamDb = CentralRepository.getInstance();
.getDefaultCorrelationTypes() CorrelationAttributeInstance.Type attributeType = eamDb.getCorrelationTypes()
.stream() .stream()
.filter(filterType -> filterType.getId() == this.resultTypeId) .filter(filterType -> filterType.getId() == this.resultTypeId)
.findFirst().get(); .findFirst().get();
CentralRepository eamDb = CentralRepository.getInstance();
Map<Integer, List<CommonAttributeValue>> itemsToRemove = new HashMap<>(); Map<Integer, List<CommonAttributeValue>> itemsToRemove = new HashMap<>();
//Call countUniqueDataSources once to reduce the number of DB queries needed to get //Call countUniqueDataSources once to reduce the number of DB queries needed to get

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2018 Basis Technology Corp. * Copyright 2018-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -255,7 +255,7 @@ final class CommonAttributePanel extends javax.swing.JDialog implements Observer
filterByDocuments = interCasePanel.documentsCheckboxIsSelected(); filterByDocuments = interCasePanel.documentsCheckboxIsSelected();
} }
if (corType == null) { if (corType == null) {
corType = CorrelationAttributeInstance.getDefaultCorrelationTypes().get(0); corType = CentralRepository.getInstance().getCorrelationTypes().get(0);
} }
if (caseId == InterCasePanel.NO_CASE_SELECTED) { if (caseId == InterCasePanel.NO_CASE_SELECTED) {
builder = new AllInterCaseCommonAttributeSearcher(filterByMedia, filterByDocuments, corType, percentageThreshold); builder = new AllInterCaseCommonAttributeSearcher(filterByMedia, filterByDocuments, corType, percentageThreshold);
@ -366,7 +366,7 @@ final class CommonAttributePanel extends javax.swing.JDialog implements Observer
filterByDocuments = interCasePanel.documentsCheckboxIsSelected(); filterByDocuments = interCasePanel.documentsCheckboxIsSelected();
} }
if (corType == null) { if (corType == null) {
corType = CorrelationAttributeInstance.getDefaultCorrelationTypes().get(0); corType = CentralRepository.getInstance().getCorrelationTypes().get(0);
} }
if (caseId == InterCasePanel.NO_CASE_SELECTED) { if (caseId == InterCasePanel.NO_CASE_SELECTED) {
builder = new AllInterCaseCommonAttributeSearcher(filterByMedia, filterByDocuments, corType, percentageThreshold); builder = new AllInterCaseCommonAttributeSearcher(filterByMedia, filterByDocuments, corType, percentageThreshold);

View File

@ -2,7 +2,7 @@
* *
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2018-2019 Basis Technology Corp. * Copyright 2018-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -31,6 +31,7 @@ import java.util.logging.Level;
import javax.swing.ComboBoxModel; import javax.swing.ComboBoxModel;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
/** /**
@ -117,7 +118,7 @@ public final class InterCasePanel extends javax.swing.JPanel {
void setupCorrelationTypeFilter() { void setupCorrelationTypeFilter() {
this.correlationTypeFilters = new HashMap<>(); this.correlationTypeFilters = new HashMap<>();
try { try {
List<CorrelationAttributeInstance.Type> types = CorrelationAttributeInstance.getDefaultCorrelationTypes(); List<CorrelationAttributeInstance.Type> types = CentralRepository.getInstance().getCorrelationTypes();
for (CorrelationAttributeInstance.Type type : types) { for (CorrelationAttributeInstance.Type type : types) {
correlationTypeFilters.put(type.getDisplayName(), type); correlationTypeFilters.put(type.getDisplayName(), type);
this.correlationTypeComboBox.addItem(type.getDisplayName()); this.correlationTypeComboBox.addItem(type.getDisplayName());

View File

@ -16,9 +16,9 @@
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/> <Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
<SubComponents> <SubComponents>
<Container class="javax.swing.JTabbedPane" name="filterTabPane"> <Container class="javax.swing.JTabbedPane" name="filterTabbedPane">
<Events> <Events>
<EventHandler event="mouseClicked" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="filterTabPaneMouseClicked"/> <EventHandler event="mouseClicked" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="filterTabbedPaneMouseClicked"/>
</Events> </Events>
<Constraints> <Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription"> <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
@ -40,10 +40,6 @@
</Constraints> </Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/> <Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
<SubComponents>
<Component class="org.sleuthkit.autopsy.communications.FiltersPanel" name="filtersPane">
</Component>
</SubComponents>
</Container> </Container>
</SubComponents> </SubComponents>
</Container> </Container>
@ -78,42 +74,6 @@
</Constraints> </Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout"/> <Layout class="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout"/>
<SubComponents>
<Component class="org.sleuthkit.autopsy.communications.AccountsBrowser" name="accountsBrowser">
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new org.sleuthkit.autopsy.communications.AccountsBrowser(relationshipBrowser)"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
<JTabbedPaneConstraints tabName="Browse">
<Property name="tabTitle" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="CVTTopComponent.accountsBrowser.TabConstraints.tabTitle_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="tabIcon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/communications/images/table.png"/>
</Property>
</JTabbedPaneConstraints>
</Constraint>
</Constraints>
</Component>
<Component class="org.sleuthkit.autopsy.communications.VisualizationPanel" name="vizPanel">
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new org.sleuthkit.autopsy.communications.VisualizationPanel(relationshipBrowser)"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
<JTabbedPaneConstraints tabName="Visualize">
<Property name="tabTitle" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="CVTTopComponent.vizPanel.TabConstraints.tabTitle_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="tabIcon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/communications/images/emblem-web.png"/>
</Property>
</JTabbedPaneConstraints>
</Constraint>
</Constraints>
</Component>
</SubComponents>
</Container> </Container>
</SubComponents> </SubComponents>
</Container> </Container>

View File

@ -55,7 +55,10 @@ public final class CVTTopComponent extends TopComponent {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private boolean filtersVisible = true; private boolean filtersVisible = true;
private final RelationshipBrowser relationshipBrowser = new RelationshipBrowser(); private final RelationshipBrowser relationshipBrowser = new RelationshipBrowser();
private final AccountsBrowser accountsBrowser = new AccountsBrowser(relationshipBrowser);
private CommunicationsFilter currentFilter; private CommunicationsFilter currentFilter;
private final VisualizationPanel vizPanel = new VisualizationPanel(relationshipBrowser);
private final FiltersPanel filtersPane = new FiltersPanel();
@ThreadConfined(type = ThreadConfined.ThreadType.AWT) @ThreadConfined(type = ThreadConfined.ThreadType.AWT)
public CVTTopComponent() { public CVTTopComponent() {
@ -84,7 +87,10 @@ public final class CVTTopComponent extends TopComponent {
relationshipBrowser.setSelectionInfo(new SelectionInfo(new HashSet<>(), new HashSet<>(), currentFilter)); relationshipBrowser.setSelectionInfo(new SelectionInfo(new HashSet<>(), new HashSet<>(), currentFilter));
}); });
filterTabPanel.setLayout(new BorderLayout());
filterTabPanel.add(filtersPane, BorderLayout.CENTER);
browseVisualizeTabPane.addTab(NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.accountsBrowser.TabConstraints.tabTitle_1"), new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/table.png")), accountsBrowser); // NOI18N
browseVisualizeTabPane.addTab(NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.vizPanel.TabConstraints.tabTitle_1"), new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/emblem-web.png")), vizPanel); // NOI18N
/* /*
* Connect the filtersPane to the accountsBrowser and visualizaionPanel * Connect the filtersPane to the accountsBrowser and visualizaionPanel
* via an Eventbus * via an Eventbus
@ -96,6 +102,7 @@ public final class CVTTopComponent extends TopComponent {
filterTabbedPane.setIconAt(0, new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/arrow-left.png"))); filterTabbedPane.setIconAt(0, new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/arrow-left.png")));
filterTabbedPane.setTitleAt(0, ""); filterTabbedPane.setTitleAt(0, "");
} }
@Subscribe @Subscribe
@ -118,11 +125,8 @@ public final class CVTTopComponent extends TopComponent {
filterTabbedPane = new JTabbedPane(); filterTabbedPane = new JTabbedPane();
filterTabPanel = new JPanel(); filterTabPanel = new JPanel();
filtersPane = new FiltersPanel();
splitPane = new JSplitPane(); splitPane = new JSplitPane();
browseVisualizeTabPane = new JTabbedPane(); browseVisualizeTabPane = new JTabbedPane();
accountsBrowser = new AccountsBrowser(relationshipBrowser);
vizPanel = new VisualizationPanel(relationshipBrowser);
setLayout(new BorderLayout()); setLayout(new BorderLayout());
@ -131,10 +135,6 @@ public final class CVTTopComponent extends TopComponent {
filterTabbedPaneMouseClicked(evt); filterTabbedPaneMouseClicked(evt);
} }
}); });
filterTabPanel.setLayout(new BorderLayout());
filterTabPanel.add(filtersPane, BorderLayout.CENTER);
filterTabbedPane.addTab(NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.filterTabPanel.TabConstraints.tabTitle"), filterTabPanel); // NOI18N filterTabbedPane.addTab(NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.filterTabPanel.TabConstraints.tabTitle"), filterTabPanel); // NOI18N
add(filterTabbedPane, BorderLayout.WEST); add(filterTabbedPane, BorderLayout.WEST);
@ -143,9 +143,6 @@ public final class CVTTopComponent extends TopComponent {
splitPane.setResizeWeight(0.25); splitPane.setResizeWeight(0.25);
browseVisualizeTabPane.setFont(new Font("Tahoma", 0, 18)); // NOI18N browseVisualizeTabPane.setFont(new Font("Tahoma", 0, 18)); // NOI18N
browseVisualizeTabPane.addTab(NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.accountsBrowser.TabConstraints.tabTitle_1"), new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/table.png")), accountsBrowser); // NOI18N
browseVisualizeTabPane.addTab(NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.vizPanel.TabConstraints.tabTitle_1"), new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/emblem-web.png")), vizPanel); // NOI18N
splitPane.setLeftComponent(browseVisualizeTabPane); splitPane.setLeftComponent(browseVisualizeTabPane);
browseVisualizeTabPane.getAccessibleContext().setAccessibleName(NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.browseVisualizeTabPane.AccessibleContext.accessibleName")); // NOI18N browseVisualizeTabPane.getAccessibleContext().setAccessibleName(NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.browseVisualizeTabPane.AccessibleContext.accessibleName")); // NOI18N
@ -171,13 +168,10 @@ public final class CVTTopComponent extends TopComponent {
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private AccountsBrowser accountsBrowser;
private JTabbedPane browseVisualizeTabPane; private JTabbedPane browseVisualizeTabPane;
private JTabbedPane filterTabbedPane;
private JPanel filterTabPanel; private JPanel filterTabPanel;
private FiltersPanel filtersPane; private JTabbedPane filterTabbedPane;
private JSplitPane splitPane; private JSplitPane splitPane;
private VisualizationPanel vizPanel;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
@Override @Override

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2019 Basis Technology Corp. * Copyright 2019-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -111,7 +111,7 @@ final class CorrelationCaseChildNodeFactory extends ChildFactory<CorrelationCase
private CorrelationAttributeInstance.Type getCorrelationType(Account.Type accountType) throws CentralRepoException { private CorrelationAttributeInstance.Type getCorrelationType(Account.Type accountType) throws CentralRepoException {
if (correlationTypeMap == null) { if (correlationTypeMap == null) {
correlationTypeMap = new HashMap<>(); correlationTypeMap = new HashMap<>();
List<CorrelationAttributeInstance.Type> correcationTypeList = CorrelationAttributeInstance.getDefaultCorrelationTypes(); List<CorrelationAttributeInstance.Type> correcationTypeList = CentralRepository.getInstance().getCorrelationTypes();
correcationTypeList.forEach((type) -> { correcationTypeList.forEach((type) -> {
correlationTypeMap.put(type.getId(), type); correlationTypeMap.put(type.getId(), type);
}); });

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2018 Basis Technology Corp. * Copyright 2018-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -198,7 +198,7 @@ public class AnnotationsContentViewer extends javax.swing.JPanel implements Data
startSection(html, "Central Repository Comments"); startSection(html, "Central Repository Comments");
List<CorrelationAttributeInstance> instancesList = new ArrayList<>(); List<CorrelationAttributeInstance> instancesList = new ArrayList<>();
if (artifact != null) { if (artifact != null) {
instancesList.addAll(CorrelationAttributeUtil.makeInstancesFromBlackboardArtifact(artifact, false)); instancesList.addAll(CorrelationAttributeUtil.makeCorrAttrsFromArtifact(artifact));
} }
try { try {
List<CorrelationAttributeInstance.Type> artifactTypes = CentralRepository.getInstance().getDefinedCorrelationTypes(); List<CorrelationAttributeInstance.Type> artifactTypes = CentralRepository.getInstance().getDefinedCorrelationTypes();

View File

@ -2,3 +2,7 @@ ContextViewer.jSourceGoToResultButton.text=Go to Result
ContextViewer.jSourceTextLabel.text=jLabel2 ContextViewer.jSourceTextLabel.text=jLabel2
ContextViewer.jSourceNameLabel.text=jSourceNameLabel ContextViewer.jSourceNameLabel.text=jSourceNameLabel
ContextViewer.jSourceLabel.text=Source ContextViewer.jSourceLabel.text=Source
ContextViewer.jUsageGoToResultButton.text=Go to Result
ContextViewer.jUsageLabel.text=Usage
ContextViewer.jUsageNameLabel.text=jSourceNameLabel
ContextViewer.jUsageTextLabel.text=jLabel2

View File

@ -7,9 +7,16 @@ ContextViewer.jSourceGoToResultButton.text=Go to Result
ContextViewer.jSourceTextLabel.text=jLabel2 ContextViewer.jSourceTextLabel.text=jLabel2
ContextViewer.jSourceNameLabel.text=jSourceNameLabel ContextViewer.jSourceNameLabel.text=jSourceNameLabel
ContextViewer.jSourceLabel.text=Source ContextViewer.jSourceLabel.text=Source
ContextViewer.jUsageGoToResultButton.text=Go to Result
ContextViewer.jUsageLabel.text=Usage
ContextViewer.jUsageNameLabel.text=jSourceNameLabel
ContextViewer.jUsageTextLabel.text=jLabel2
ContextViewer.message=Message ContextViewer.message=Message
ContextViewer.messageFrom=From ContextViewer.messageFrom=From
ContextViewer.messageOn=On ContextViewer.messageOn=On
ContextViewer.messageTo=To ContextViewer.messageTo=To
ContextViewer.on=Opened at
ContextViewer.recentDocs=Recent Documents:
ContextViewer.title=Context ContextViewer.title=Context
ContextViewer.toolTip=Displays context for selected file. ContextViewer.toolTip=Displays context for selected file.
ContextViewer.unknown=Opened at unknown time

View File

@ -23,26 +23,41 @@
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0"> <Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Component id="jSourceLabel" min="-2" max="-2" attributes="0"/> <Component id="jSourceLabel" min="-2" max="-2" attributes="0"/>
<Component id="jUsageLabel" min="-2" max="-2" attributes="0"/>
<Group type="102" attributes="0">
<EmptySpace min="6" pref="6" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="6" max="-2" attributes="0"/>
<Component id="jSourceNameLabel" min="-2" max="-2" attributes="0"/> <Component id="jSourceNameLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="jSourceTextLabel" pref="192" max="32767" attributes="0"/> <Component id="jSourceTextLabel" pref="192" max="32767" attributes="0"/>
</Group> </Group>
<Group type="102" attributes="0">
<Component id="jUsageNameLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="jUsageTextLabel" pref="192" max="32767" attributes="0"/>
</Group>
</Group>
</Group>
</Group> </Group>
<EmptySpace min="-2" pref="36" max="-2" attributes="0"/> <EmptySpace min="-2" pref="36" max="-2" attributes="0"/>
</Group> </Group>
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace min="-2" pref="45" max="-2" attributes="0"/>
<Component id="jUsageGoToResultButton" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="46" max="-2" attributes="0"/>
<Component id="jSourceGoToResultButton" min="-2" max="-2" attributes="0"/> <Component id="jSourceGoToResultButton" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace max="32767" attributes="0"/> <EmptySpace max="32767" attributes="0"/>
</Group> </Group>
</Group> </Group>
</Group>
</Group>
</DimensionLayout> </DimensionLayout>
<DimensionLayout dim="1"> <DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
@ -56,7 +71,16 @@
</Group> </Group>
<EmptySpace type="separate" max="-2" attributes="0"/> <EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="jSourceGoToResultButton" min="-2" max="-2" attributes="0"/> <Component id="jSourceGoToResultButton" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="203" max="32767" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="jUsageLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="jUsageNameLabel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="jUsageTextLabel" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="jUsageGoToResultButton" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="62" max="32767" attributes="0"/>
</Group> </Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
@ -96,5 +120,39 @@
</Property> </Property>
</Properties> </Properties>
</Component> </Component>
<Component class="javax.swing.JButton" name="jUsageGoToResultButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/contextviewer/Bundle.properties" key="ContextViewer.jUsageGoToResultButton.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="jUsageGoToResultButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="jUsageLabel">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Dialog" size="14" style="1"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/contextviewer/Bundle.properties" key="ContextViewer.jUsageLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="jUsageNameLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/contextviewer/Bundle.properties" key="ContextViewer.jUsageNameLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="jUsageTextLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/contextviewer/Bundle.properties" key="ContextViewer.jUsageTextLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents> </SubComponents>
</Form> </Form>

View File

@ -83,6 +83,10 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte
jSourceLabel = new javax.swing.JLabel(); jSourceLabel = new javax.swing.JLabel();
jSourceNameLabel = new javax.swing.JLabel(); jSourceNameLabel = new javax.swing.JLabel();
jSourceTextLabel = new javax.swing.JLabel(); jSourceTextLabel = new javax.swing.JLabel();
jUsageGoToResultButton = new javax.swing.JButton();
jUsageLabel = new javax.swing.JLabel();
jUsageNameLabel = new javax.swing.JLabel();
jUsageTextLabel = new javax.swing.JLabel();
setBackground(new java.awt.Color(255, 255, 255)); setBackground(new java.awt.Color(255, 255, 255));
@ -100,25 +104,50 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte
org.openide.awt.Mnemonics.setLocalizedText(jSourceTextLabel, org.openide.util.NbBundle.getMessage(ContextViewer.class, "ContextViewer.jSourceTextLabel.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(jSourceTextLabel, org.openide.util.NbBundle.getMessage(ContextViewer.class, "ContextViewer.jSourceTextLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(jUsageGoToResultButton, org.openide.util.NbBundle.getMessage(ContextViewer.class, "ContextViewer.jUsageGoToResultButton.text")); // NOI18N
jUsageGoToResultButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jUsageGoToResultButtonActionPerformed(evt);
}
});
jUsageLabel.setFont(new java.awt.Font("Dialog", 1, 14)); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(jUsageLabel, org.openide.util.NbBundle.getMessage(ContextViewer.class, "ContextViewer.jUsageLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(jUsageNameLabel, org.openide.util.NbBundle.getMessage(ContextViewer.class, "ContextViewer.jUsageNameLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(jUsageTextLabel, org.openide.util.NbBundle.getMessage(ContextViewer.class, "ContextViewer.jUsageTextLabel.text")); // NOI18N
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout); this.setLayout(layout);
layout.setHorizontalGroup( layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addContainerGap() .addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jSourceLabel) .addComponent(jSourceLabel)
.addComponent(jUsageLabel)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addGap(6, 6, 6) .addGap(6, 6, 6)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(jSourceNameLabel) .addComponent(jSourceNameLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jSourceTextLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 192, Short.MAX_VALUE))) .addComponent(jSourceTextLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 192, Short.MAX_VALUE))
.addGroup(layout.createSequentialGroup()
.addComponent(jUsageNameLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jUsageTextLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 192, Short.MAX_VALUE)))))
.addGap(36, 36, 36)) .addGap(36, 36, 36))
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addComponent(jSourceGoToResultButton) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))) .addGroup(layout.createSequentialGroup()
.addGap(45, 45, 45)
.addComponent(jUsageGoToResultButton))
.addGroup(layout.createSequentialGroup()
.addGap(46, 46, 46)
.addComponent(jSourceGoToResultButton)))
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
); );
layout.setVerticalGroup( layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
@ -131,7 +160,15 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte
.addComponent(jSourceTextLabel)) .addComponent(jSourceTextLabel))
.addGap(18, 18, 18) .addGap(18, 18, 18)
.addComponent(jSourceGoToResultButton) .addComponent(jSourceGoToResultButton)
.addGap(0, 203, Short.MAX_VALUE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jUsageLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jUsageNameLabel)
.addComponent(jUsageTextLabel))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jUsageGoToResultButton)
.addGap(0, 62, Short.MAX_VALUE))
); );
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
@ -146,6 +183,15 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte
}//GEN-LAST:event_jSourceGoToResultButtonActionPerformed }//GEN-LAST:event_jSourceGoToResultButtonActionPerformed
private void jUsageGoToResultButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jUsageGoToResultButtonActionPerformed
final DirectoryTreeTopComponent dtc = DirectoryTreeTopComponent.findInstance();
// Navigate to the source context artifact.
if (sourceContextArtifact != null) {
dtc.viewArtifact(sourceContextArtifact);
}
}//GEN-LAST:event_jUsageGoToResultButtonActionPerformed
@Override @Override
public void setNode(Node selectedNode) { public void setNode(Node selectedNode) {
if ((selectedNode == null) || (!isSupported(selectedNode))) { if ((selectedNode == null) || (!isSupported(selectedNode))) {
@ -246,9 +292,12 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte
} }
} }
jSourceGoToResultButton.setVisible(true); jSourceGoToResultButton.setVisible(true);
jUsageGoToResultButton.setVisible(true);
if (foundASource == false) { if (foundASource == false) {
setSourceName("Unknown"); setSourceName("Unknown");
showSourceText(false); showSourceText(false);
setUsageName("Unknown");
showUsageText(false);
} }
} }
@ -263,6 +312,11 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte
* @throws TskCoreException * @throws TskCoreException
*/ */
private void addSourceEntry(BlackboardArtifact artifact) throws TskCoreException { private void addSourceEntry(BlackboardArtifact artifact) throws TskCoreException {
setSourceName("Unknown");
showSourceText(false);
setUsageName("Unknown");
showUsageText(false);
if (BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT.getTypeID() == artifact.getArtifactTypeID()) { if (BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT.getTypeID() == artifact.getArtifactTypeID()) {
BlackboardAttribute associatedArtifactAttribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT)); BlackboardAttribute associatedArtifactAttribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT));
if (associatedArtifactAttribute != null) { if (associatedArtifactAttribute != null) {
@ -287,7 +341,8 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte
*/ */
@NbBundle.Messages({ @NbBundle.Messages({
"ContextViewer.attachmentSource=Attached to: ", "ContextViewer.attachmentSource=Attached to: ",
"ContextViewer.downloadSource=Downloaded from: " "ContextViewer.downloadSource=Downloaded from: ",
"ContextViewer.recentDocs=Recent Documents: "
}) })
private void setSourceFields(BlackboardArtifact associatedArtifact) throws TskCoreException { private void setSourceFields(BlackboardArtifact associatedArtifact) throws TskCoreException {
if (BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE.getTypeID() == associatedArtifact.getArtifactTypeID() if (BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE.getTypeID() == associatedArtifact.getArtifactTypeID()
@ -301,6 +356,10 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte
setSourceName(Bundle.ContextViewer_downloadSource()); setSourceName(Bundle.ContextViewer_downloadSource());
setSourceText(webDownloadArtifactToString(associatedArtifact)); setSourceText(webDownloadArtifactToString(associatedArtifact));
} else if (BlackboardArtifact.ARTIFACT_TYPE.TSK_RECENT_OBJECT.getTypeID() == associatedArtifact.getArtifactTypeID()) {
setUsageName(Bundle.ContextViewer_recentDocs());
setUsageText(recentDocArtifactToString(associatedArtifact));
} }
} }
@ -313,6 +372,15 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte
jSourceNameLabel.setText(nameLabel); jSourceNameLabel.setText(nameLabel);
} }
/**
* Sets the usage label string.
*
* @param nameLabel String value for usage label.
*/
private void setUsageName(String nameLabel) {
jUsageNameLabel.setText(nameLabel);
}
/** /**
* Sets the source text string. * Sets the source text string.
* *
@ -327,6 +395,24 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte
jSourceTextLabel.setVisible(show); jSourceTextLabel.setVisible(show);
jSourceGoToResultButton.setEnabled(show); jSourceGoToResultButton.setEnabled(show);
jSourceLabel.setVisible(show); jSourceLabel.setVisible(show);
jUsageLabel.setVisible(show);
}
/**
* Sets the Usage text string.
*
* @param text String value for Usage text.
*/
private void setUsageText(String text) {
jUsageTextLabel.setText(text);
showUsageText(!text.isEmpty());
}
private void showUsageText(boolean show) {
jUsageTextLabel.setVisible(show);
jUsageGoToResultButton.setEnabled(show);
jUsageLabel.setVisible(show);
jSourceLabel.setVisible(show);
} }
/** /**
@ -355,6 +441,36 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte
return sb.toString(); return sb.toString();
} }
/**
* Returns a display string with recent Doc
* artifact.
*
* @param artifact artifact to get doc from.
*
* @return Display string with download URL and date/time.
*
* @throws TskCoreException
*/
@NbBundle.Messages({
"ContextViewer.on=Opened at",
"ContextViewer.unknown=Opened at unknown time"
})
private String recentDocArtifactToString(BlackboardArtifact artifact) throws TskCoreException {
StringBuilder sb = new StringBuilder(ARTIFACT_STR_MAX_LEN);
Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> attributesMap = getAttributesMap(artifact);
BlackboardAttribute attribute = attributesMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME);
if (BlackboardArtifact.ARTIFACT_TYPE.TSK_RECENT_OBJECT.getTypeID() == artifact.getArtifactTypeID()) {
if (attribute.getValueLong() > 0) {
appendAttributeString(sb, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, attributesMap, Bundle.ContextViewer_on());
} else {
sb.append(Bundle.ContextViewer_unknown());
}
}
return sb.toString();
}
/** /**
* Returns a abbreviated display string for a message artifact. * Returns a abbreviated display string for a message artifact.
* *
@ -442,5 +558,9 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte
private javax.swing.JLabel jSourceLabel; private javax.swing.JLabel jSourceLabel;
private javax.swing.JLabel jSourceNameLabel; private javax.swing.JLabel jSourceNameLabel;
private javax.swing.JLabel jSourceTextLabel; private javax.swing.JLabel jSourceTextLabel;
private javax.swing.JButton jUsageGoToResultButton;
private javax.swing.JLabel jUsageLabel;
private javax.swing.JLabel jUsageNameLabel;
private javax.swing.JLabel jUsageTextLabel;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
} }

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2020 Basis Technology Corp. * Copyright 2012-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -549,7 +549,7 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
protected CorrelationAttributeInstance getCorrelationAttributeInstance() { protected CorrelationAttributeInstance getCorrelationAttributeInstance() {
CorrelationAttributeInstance attribute = null; CorrelationAttributeInstance attribute = null;
if (CentralRepository.isEnabled() && !UserPreferences.getHideSCOColumns()) { if (CentralRepository.isEnabled() && !UserPreferences.getHideSCOColumns()) {
attribute = CorrelationAttributeUtil.getInstanceFromContent(content); attribute = CorrelationAttributeUtil.getCorrAttrForFile(content);
} }
return attribute; return attribute;
} }

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2020 Basis Technology Corp. * Copyright 2012-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -323,7 +323,8 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
if (displayName.isEmpty() && artifact != null) { if (displayName.isEmpty() && artifact != null) {
try { try {
displayName = Case.getCurrentCaseThrows().getSleuthkitCase().getAbstractFileById(this.artifact.getObjectID()).getName(); Content content = Case.getCurrentCaseThrows().getSleuthkitCase().getContentById(this.artifact.getObjectID());
displayName = (content == null) ? artifact.getName() : content.getName();
} catch (TskCoreException | NoCurrentCaseException ex) { } catch (TskCoreException | NoCurrentCaseException ex) {
displayName = artifact.getName(); displayName = artifact.getName();
} }
@ -605,8 +606,8 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
@Override @Override
protected final CorrelationAttributeInstance getCorrelationAttributeInstance() { protected final CorrelationAttributeInstance getCorrelationAttributeInstance() {
CorrelationAttributeInstance correlationAttribute = null; CorrelationAttributeInstance correlationAttribute = null;
if (CentralRepository.isEnabled()) { if (CentralRepository.isEnabled() && associated instanceof AbstractFile) {
correlationAttribute = CorrelationAttributeUtil.getInstanceFromContent(associated); correlationAttribute = CorrelationAttributeUtil.getCorrAttrForFile((AbstractFile)associated);
} }
return correlationAttribute; return correlationAttribute;
} }

View File

@ -97,7 +97,7 @@ class GetSCOTask implements Runnable {
logger.log(Level.WARNING, "Unable to get correlation type or value to determine value for O column for artifact", ex); logger.log(Level.WARNING, "Unable to get correlation type or value to determine value for O column for artifact", ex);
} }
} else { } else {
List<CorrelationAttributeInstance> listOfPossibleAttributes = CorrelationAttributeUtil.makeInstancesFromBlackboardArtifact(bbArtifact, false); List<CorrelationAttributeInstance> listOfPossibleAttributes = CorrelationAttributeUtil.makeCorrAttrsFromArtifact(bbArtifact);
if (listOfPossibleAttributes.size() > 1) { if (listOfPossibleAttributes.size() > 1) {
//Don't display anything if there is more than 1 correlation property for an artifact but let the user know //Don't display anything if there is more than 1 correlation property for an artifact but let the user know
description = Bundle.GetSCOTask_occurrences_multipleProperties(); description = Bundle.GetSCOTask_occurrences_multipleProperties();

View File

@ -40,6 +40,7 @@ import org.sleuthkit.autopsy.directorytree.NewWindowViewAction;
import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.ModuleContentEvent; import org.sleuthkit.autopsy.ingest.ModuleContentEvent;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.Pool;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.VirtualDirectory; import org.sleuthkit.datamodel.VirtualDirectory;
import org.sleuthkit.datamodel.Volume; import org.sleuthkit.datamodel.Volume;
@ -76,9 +77,19 @@ public class VolumeNode extends AbstractContentNode<Volume> {
// set name, display name, and icon // set name, display name, and icon
String volName = nameForVolume(vol); String volName = nameForVolume(vol);
long end = vol.getStart() + (vol.getLength() - 1); long end = vol.getStart() + (vol.getLength() - 1);
String tempVolName = volName + " (" + vol.getDescription() + ": " + vol.getStart() + "-" + end + ")"; String tempVolName = volName + " (" + vol.getDescription() + ": " + vol.getStart() + "-" + end + ")";
// If this is a pool volume use a custom display name
try {
if (vol.getParent() != null &&
vol.getParent().getParent() instanceof Pool) {
// Pool volumes are not contiguous so printing a range of blocks is inaccurate
tempVolName = volName + " (" + vol.getDescription() + ": " + vol.getStart() + ")";
}
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Error looking up parent(s) of volume with obj ID = "+ vol.getId(), ex);
}
this.setDisplayName(tempVolName); this.setDisplayName(tempVolName);
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/vol-icon.png"); //NON-NLS this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/vol-icon.png"); //NON-NLS

View File

@ -78,6 +78,7 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.textextractors.TextExtractor; import org.sleuthkit.autopsy.textextractors.TextExtractor;
import org.sleuthkit.autopsy.textextractors.TextExtractorFactory; import org.sleuthkit.autopsy.textextractors.TextExtractorFactory;
import org.sleuthkit.autopsy.textsummarizer.TextSummarizer; import org.sleuthkit.autopsy.textsummarizer.TextSummarizer;
import org.sleuthkit.autopsy.textsummarizer.TextSummary;
/** /**
* Main class to perform the file search. * Main class to perform the file search.
@ -258,8 +259,8 @@ class FileSearch {
*/ */
@NbBundle.Messages({"FileSearch.documentSummary.noPreview=No preview available.", @NbBundle.Messages({"FileSearch.documentSummary.noPreview=No preview available.",
"FileSearch.documentSummary.noBytes=No bytes read for document, unable to display preview."}) "FileSearch.documentSummary.noBytes=No bytes read for document, unable to display preview."})
static String summarize(AbstractFile file) { static TextSummary summarize(AbstractFile file) {
String summary = null; TextSummary summary = null;
TextSummarizer localSummarizer = summarizerToUse; TextSummarizer localSummarizer = summarizerToUse;
if (localSummarizer == null) { if (localSummarizer == null) {
synchronized (searchCache) { synchronized (searchCache) {
@ -273,12 +274,12 @@ class FileSearch {
//a summary of length 40 seems to fit without vertical scroll bars //a summary of length 40 seems to fit without vertical scroll bars
summary = localSummarizer.summarize(file, 40); summary = localSummarizer.summarize(file, 40);
} catch (IOException ex) { } catch (IOException ex) {
return Bundle.FileSearch_documentSummary_noPreview(); return new TextSummary(Bundle.FileSearch_documentSummary_noPreview(), null, 0);
} }
} }
if (StringUtils.isBlank(summary)) { if (summary == null || StringUtils.isBlank(summary.getSummaryText())) {
//no summarizer was found or summary was empty just grab the beginning of the file //summary text was empty grab the beginning of the file
summary = getFirstLines(file); summary = new TextSummary(getFirstLines(file), null, 0);
} }
return summary; return summary;
} }
@ -291,13 +292,21 @@ class FileSearch {
* @return The beginning of text from the specified AbstractFile. * @return The beginning of text from the specified AbstractFile.
*/ */
private static String getFirstLines(AbstractFile file) { private static String getFirstLines(AbstractFile file) {
try (Reader reader = TextExtractorFactory.getExtractor(file, null).getReader()) { TextExtractor extractor;
try {
extractor = TextExtractorFactory.getExtractor(file, null);
} catch (TextExtractorFactory.NoTextExtractorFound ignored) {
//no extractor found, use Strings Extractor
extractor = TextExtractorFactory.getStringsExtractor(file, null);
}
try (Reader reader = extractor.getReader()) {
char[] cbuf = new char[PREVIEW_SIZE]; char[] cbuf = new char[PREVIEW_SIZE];
reader.read(cbuf, 0, PREVIEW_SIZE); reader.read(cbuf, 0, PREVIEW_SIZE);
return new String(cbuf); return new String(cbuf);
} catch (IOException ex) { } catch (IOException ex) {
return Bundle.FileSearch_documentSummary_noBytes(); return Bundle.FileSearch_documentSummary_noBytes();
} catch (TextExtractorFactory.NoTextExtractorFound | TextExtractor.InitReaderException ex) { } catch (TextExtractor.InitReaderException ex) {
return Bundle.FileSearch_documentSummary_noPreview(); return Bundle.FileSearch_documentSummary_noPreview();
} }
} }

View File

@ -55,6 +55,7 @@ import org.sleuthkit.autopsy.modules.hashdatabase.AddContentToHashDbAction;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.textsummarizer.TextSummary;
/** /**
* Panel for displaying of file discovery results and handling the paging of * Panel for displaying of file discovery results and handling the paging of
@ -777,11 +778,11 @@ public class ResultsPanel extends javax.swing.JPanel {
@Messages({"ResultsPanel.unableToCreate.text=Unable to create summary."}) @Messages({"ResultsPanel.unableToCreate.text=Unable to create summary."})
@Override @Override
protected Void doInBackground() throws Exception { protected Void doInBackground() throws Exception {
String preview = FileSearch.summarize(documentWrapper.getResultFile().getFirstInstance()); TextSummary preview = FileSearch.summarize(documentWrapper.getResultFile().getFirstInstance());
if (preview == null) { if (preview == null) {
preview = Bundle.ResultsPanel_unableToCreate_text(); preview = new TextSummary(Bundle.ResultsPanel_unableToCreate_text(), null, 0);
} }
documentWrapper.setPreview(preview); documentWrapper.setPreview(preview.getSummaryText());
return null; return null;
} }

View File

@ -30,6 +30,8 @@ GeoTopComponent_no_waypoints_returned_mgs=Applied filter failed to find waypoint
GeoTopComponent_no_waypoints_returned_Title=No Waypoints Found GeoTopComponent_no_waypoints_returned_Title=No Waypoints Found
GLTopComponent_initilzation_error=An error occurred during waypoint initilization. Geolocation data maybe incomplete. GLTopComponent_initilzation_error=An error occurred during waypoint initilization. Geolocation data maybe incomplete.
GLTopComponent_name=Geolocation GLTopComponent_name=Geolocation
GLTopComponent_No_dataSource_message=There are no data sources with Geolocation artifacts found.
GLTopComponent_No_dataSource_Title=No Geolocation artifacts found
HidingPane_default_title=Filters HidingPane_default_title=Filters
MapPanel_connection_failure_message=Failed to connect to new geolocation map tile source. MapPanel_connection_failure_message=Failed to connect to new geolocation map tile source.
MapPanel_connection_failure_message_title=Connection Failure MapPanel_connection_failure_message_title=Connection Failure

View File

@ -52,6 +52,7 @@ class GeoFilterPanel extends javax.swing.JPanel {
private final CheckBoxListPanel<DataSource> checkboxPanel; private final CheckBoxListPanel<DataSource> checkboxPanel;
// Make sure to update if // Make sure to update if
@SuppressWarnings("deprecation")
private static final BlackboardArtifact.ARTIFACT_TYPE[] GPS_ARTIFACT_TYPES = { private static final BlackboardArtifact.ARTIFACT_TYPE[] GPS_ARTIFACT_TYPES = {
BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_BOOKMARK, BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_BOOKMARK,
BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_LAST_KNOWN_LOCATION, BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_LAST_KNOWN_LOCATION,

View File

@ -3,6 +3,9 @@ GEOTrack_point_label_header=Trackpoint for track: {0}
LastKnownWaypoint_Label=Last Known Location LastKnownWaypoint_Label=Last Known Location
Route_End_Label=End Route_End_Label=End
Route_Label=As-the-crow-flies Route Route_Label=As-the-crow-flies Route
Route_point_label=Waypoints for route
Route_Start_Label=Start Route_Start_Label=Start
SearchWaypoint_DisplayLabel=GPS Search SearchWaypoint_DisplayLabel=GPS Search
Track_distanceFromHome_displayName=Distance from home point
Track_distanceTraveled_displayName=Distance traveled
TrackpointWaypoint_DisplayLabel=GPS Trackpoint TrackpointWaypoint_DisplayLabel=GPS Trackpoint

View File

@ -25,20 +25,26 @@ import java.util.Map;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoWaypointsUtil;
import org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoWaypointsUtil.GeoWaypointList.GeoWaypoint;
import org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoWaypointsUtil.GeoWaypointList;
/** /**
* A Route represents a TSK_GPS_ROUTE artifact which has a start and end point * A Route represents a TSK_GPS_ROUTE artifact which has a start and end point
* however the class was written with the assumption that routes may have * however the class was written with the assumption that routes may have more
* more that two points. * than two points.
* *
*/ */
public class Route extends GeoPath { public class Route extends GeoPath {
private final Long timestamp; private final Long timestamp;
// This list is not expected to change after construction so the // This list is not expected to change after construction so the
// constructor will take care of creating an unmodifiable List // constructor will take care of creating an unmodifiable List
private final List<Waypoint.Property> propertiesList; private final List<Waypoint.Property> propertiesList;
private static final TskGeoWaypointsUtil attributeUtil = new TskGeoWaypointsUtil();
/** /**
* Construct a route for the given artifact. * Construct a route for the given artifact.
* *
@ -54,8 +60,7 @@ public class Route extends GeoPath{
Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> attributeMap = Waypoint.getAttributesFromArtifactAsMap(artifact); Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> attributeMap = Waypoint.getAttributesFromArtifactAsMap(artifact);
addToPath(getRouteStartPoint(artifact, attributeMap)); createRoute(artifact, attributeMap);
addToPath(getRouteEndPoint(artifact, attributeMap));
BlackboardAttribute attribute = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME); BlackboardAttribute attribute = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME);
timestamp = attribute != null ? attribute.getValueLong() : null; timestamp = attribute != null ? attribute.getValueLong() : null;
@ -82,10 +87,52 @@ public class Route extends GeoPath{
return Collections.unmodifiableList(propertiesList); return Collections.unmodifiableList(propertiesList);
} }
/**
* Returns the route timestamp.
*
* @return Route timestamp
*/
public Long getTimestamp() { public Long getTimestamp() {
return timestamp; return timestamp;
} }
/**
* Gets the route waypoint attributes from the map and creates the list of
* route waypoints.
*
* @param artifact Route artifact
* @param attributeMap Map of artifact attributes
*
* @throws GeoLocationDataException
*/
@Messages({
"Route_point_label=Waypoints for route"
})
private void createRoute(BlackboardArtifact artifact, Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> attributeMap) throws GeoLocationDataException {
BlackboardAttribute attribute = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_WAYPOINTS);
String label = getLabel();
if (label == null || label.isEmpty()) {
label = Bundle.Route_point_label();
} else {
label = String.format("%s: %s", Bundle.Route_point_label(), label);
}
if (attribute != null) {
GeoWaypointList waypoints = attributeUtil.fromAttribute(attribute);
for(GeoWaypoint waypoint: waypoints) {
addToPath(new Waypoint(artifact, label, null, waypoint.getLatitude(), waypoint.getLongitude(), waypoint.getAltitude(), null, attributeMap, this));
}
} else {
Waypoint start = getRouteStartPoint(artifact, attributeMap);
Waypoint end = getRouteEndPoint(artifact, attributeMap);
addToPath(start);
addToPath(end);
}
}
/** /**
* Get the route start point. * Get the route start point.
* *
@ -106,16 +153,14 @@ public class Route extends GeoPath{
BlackboardAttribute latitude = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_START); BlackboardAttribute latitude = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_START);
BlackboardAttribute longitude = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_START); BlackboardAttribute longitude = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_START);
BlackboardAttribute altitude = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE); BlackboardAttribute altitude = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE);
BlackboardAttribute pointTimestamp = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME);
if (latitude != null && longitude != null) { if (latitude != null && longitude != null) {
return new Waypoint(artifact, return new RoutePoint(artifact,
Bundle.Route_Start_Label(), Bundle.Route_Start_Label(),
pointTimestamp != null ? pointTimestamp.getValueLong() : null,
latitude.getValueDouble(), latitude.getValueDouble(),
longitude.getValueDouble(), longitude.getValueDouble(),
altitude != null ? altitude.getValueDouble() : null, altitude != null ? altitude.getValueDouble() : null,
null, attributeMap, this); attributeMap);
} else { } else {
throw new GeoLocationDataException("Unable to create route start point, invalid longitude and/or latitude"); throw new GeoLocationDataException("Unable to create route start point, invalid longitude and/or latitude");
} }
@ -140,19 +185,52 @@ public class Route extends GeoPath{
BlackboardAttribute latitude = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_END); BlackboardAttribute latitude = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_END);
BlackboardAttribute longitude = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_END); BlackboardAttribute longitude = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_END);
BlackboardAttribute altitude = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE); BlackboardAttribute altitude = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE);
BlackboardAttribute pointTimestamp = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME);
if (latitude != null && longitude != null) { if (latitude != null && longitude != null) {
return new Waypoint(artifact, return new RoutePoint(artifact,
Bundle.Route_End_Label(), Bundle.Route_End_Label(),
pointTimestamp != null ? pointTimestamp.getValueLong() : null,
latitude.getValueDouble(), latitude.getValueDouble(),
longitude.getValueDouble(), longitude.getValueDouble(),
altitude != null ? altitude.getValueDouble() : null, altitude != null ? altitude.getValueDouble() : null,
null, attributeMap, this); attributeMap);
} else { } else {
throw new GeoLocationDataException("Unable to create route end point, invalid longitude and/or latitude"); throw new GeoLocationDataException("Unable to create route end point, invalid longitude and/or latitude");
} }
} }
/**
* Route waypoint specific implementation of Waypoint.
*/
private class RoutePoint extends Waypoint {
/**
* Construct a RoutePoint
*
* @param artifact BlackboardArtifact for this waypoint
* @param label String waypoint label
* @param latitude Double waypoint latitude
* @param longitude Double waypoint longitude
*
* @param attributeMap A Map of attributes for the given artifact
*
* @throws GeoLocationDataException
*/
RoutePoint(BlackboardArtifact artifact, String label, Double latitude, Double longitude, Double altitude, Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> attributeMap) throws GeoLocationDataException {
super(artifact,
label,
null,
latitude,
longitude,
altitude,
null,
attributeMap,
Route.this);
}
@Override
public Long getTimestamp() {
return ((Route) getParentGeoPath()).getTimestamp();
}
}
} }

View File

@ -26,8 +26,9 @@ import java.util.Map;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.blackboardutils.attributes.GeoTrackPoints; import org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoTrackpointsUtil;
import org.sleuthkit.datamodel.blackboardutils.attributes.GeoWaypoint.GeoTrackPoint; import org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoTrackpointsUtil.GeoTrackPointList;
import org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoTrackpointsUtil.GeoTrackPointList.GeoTrackPoint;
/** /**
* A GPS track with which wraps the TSK_GPS_TRACK artifact. * A GPS track with which wraps the TSK_GPS_TRACK artifact.
@ -37,6 +38,8 @@ public final class Track extends GeoPath{
private final Long startTimestamp; private final Long startTimestamp;
private final Long endTimeStamp; private final Long endTimeStamp;
private static final TskGeoTrackpointsUtil attributeUtil = new TskGeoTrackpointsUtil();
/** /**
* Construct a new Track for the given artifact. * Construct a new Track for the given artifact.
* *
@ -59,11 +62,11 @@ public final class Track extends GeoPath{
private Track(BlackboardArtifact artifact, Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> attributeMap) throws GeoLocationDataException { private Track(BlackboardArtifact artifact, Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> attributeMap) throws GeoLocationDataException {
super(artifact, getTrackName(attributeMap)); super(artifact, getTrackName(attributeMap));
List<GeoTrackPoint> points = getPointsList(attributeMap); GeoTrackPointList points = getPointsList(attributeMap);
buildPath(points); buildPath(points);
startTimestamp = findStartTime(points); startTimestamp = points.getStartTime();
endTimeStamp = findEndTime(points); endTimeStamp = points.getEndTime();
} }
/** /**
@ -111,7 +114,7 @@ public final class Track extends GeoPath{
"# {0} - track name", "# {0} - track name",
"GEOTrack_point_label_header=Trackpoint for track: {0}" "GEOTrack_point_label_header=Trackpoint for track: {0}"
}) })
private void buildPath(List<GeoTrackPoint> points) throws GeoLocationDataException { private void buildPath(GeoTrackPointList points) throws GeoLocationDataException {
for(GeoTrackPoint point: points) { for(GeoTrackPoint point: points) {
addToPath(new TrackWaypoint(Bundle.GEOTrack_point_label_header(getLabel()), point)); addToPath(new TrackWaypoint(Bundle.GEOTrack_point_label_header(getLabel()), point));
} }
@ -125,52 +128,12 @@ public final class Track extends GeoPath{
* *
* @return GeoTrackPoint list empty list if the attribute was not found. * @return GeoTrackPoint list empty list if the attribute was not found.
*/ */
private List<GeoTrackPoint> getPointsList(Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> attributeMap) { private GeoTrackPointList getPointsList(Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> attributeMap) {
BlackboardAttribute attribute = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_TRACKPOINTS); BlackboardAttribute attribute = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_TRACKPOINTS);
if (attribute != null) { if (attribute != null) {
String value = attribute.getValueString(); return attributeUtil.fromAttribute(attribute);
return GeoTrackPoints.deserializePoints(value);
} }
return new ArrayList<>();
}
/**
* Return the start time for the track. Assumes the points are in time
* order.
*
* @param points List of GeoTrackPoints.
*
* @return First non-null time stamp or null, if one was not found.
*/
private Long findStartTime(List<GeoTrackPoint> points) {
if (points != null) {
for (GeoTrackPoint point : points) {
if (point.getTimeStamp() != null) {
return point.getTimeStamp();
}
}
}
return null;
}
/**
* Return the ends time for the track. Assumes the points are in time
* order.
*
* @param points List of GeoTrackPoints.
*
* @return First non-null time stamp or null, if one was not found.
*/
private Long findEndTime(List<GeoTrackPoint> points) {
if (points != null) {
for (int index = points.size() - 1; index >= 0; index--) {
GeoTrackPoint point = points.get(index);
if (point.getTimeStamp() != null) {
return point.getTimeStamp();
}
}
}
return null; return null;
} }
@ -219,6 +182,10 @@ public final class Track extends GeoPath{
* *
* @return A list of Waypoint.properies. * @return A list of Waypoint.properies.
*/ */
@Messages({
"Track_distanceTraveled_displayName=Distance traveled",
"Track_distanceFromHome_displayName=Distance from home point"
})
private List<Waypoint.Property> createPropertyList(GeoTrackPoint point) { private List<Waypoint.Property> createPropertyList(GeoTrackPoint point) {
List<Waypoint.Property> list = new ArrayList<>(); List<Waypoint.Property> list = new ArrayList<>();
@ -234,12 +201,12 @@ public final class Track extends GeoPath{
value = point.getDistanceTraveled(); value = point.getDistanceTraveled();
if (value != null) { if (value != null) {
list.add(new Property(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_DISTANCE_TRAVELED.getDisplayName(), value.toString())); list.add(new Property(Bundle.Track_distanceTraveled_displayName(), value.toString()));
} }
value = point.getDistanceFromHP(); value = point.getDistanceFromHomePoint();
if (value != null) { if (value != null) {
list.add(new Property(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_DISTANCE_FROM_HOME_POINT.getDisplayName(), value.toString())); list.add(new Property(Bundle.Track_distanceFromHome_displayName(), value.toString()));
} }
return list; return list;

View File

@ -43,7 +43,7 @@ public class Waypoint {
final private String label; final private String label;
final private AbstractFile image; final private AbstractFile image;
final private BlackboardArtifact artifact; final private BlackboardArtifact artifact;
final private GeoPath path; final private GeoPath parentGeoPath;
final private List<Waypoint.Property> propertiesList; final private List<Waypoint.Property> propertiesList;
@ -78,7 +78,7 @@ public class Waypoint {
* @throws GeoLocationDataException Exception will be thrown if artifact did * @throws GeoLocationDataException Exception will be thrown if artifact did
* not have a valid longitude and latitude. * not have a valid longitude and latitude.
*/ */
Waypoint(BlackboardArtifact artifact, String label, Long timestamp, Double latitude, Double longitude, Double altitude, AbstractFile image, Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> attributeMap, GeoPath path) throws GeoLocationDataException { Waypoint(BlackboardArtifact artifact, String label, Long timestamp, Double latitude, Double longitude, Double altitude, AbstractFile image, Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> attributeMap, GeoPath parentGeoPath) throws GeoLocationDataException {
if (longitude == null || latitude == null) { if (longitude == null || latitude == null) {
throw new GeoLocationDataException("Invalid waypoint, null value passed for longitude or latitude"); throw new GeoLocationDataException("Invalid waypoint, null value passed for longitude or latitude");
} }
@ -90,7 +90,7 @@ public class Waypoint {
this.longitude = longitude; this.longitude = longitude;
this.latitude = latitude; this.latitude = latitude;
this.altitude = altitude; this.altitude = altitude;
this.path = path; this.parentGeoPath = parentGeoPath;
propertiesList = createGeolocationProperties(attributeMap); propertiesList = createGeolocationProperties(attributeMap);
} }
@ -173,13 +173,13 @@ public class Waypoint {
} }
/** /**
* Returns the route that this waypoint is apart of . * Returns the GeoPath that this waypoint is apart of .
* *
* @return The waypoint route or null if the waypoint is not apart of a * @return The waypoint route or null if the waypoint is not apart of a
* route. * route.
*/ */
public GeoPath getPath() { public GeoPath getParentGeoPath() {
return path; return parentGeoPath;
} }
/** /**
@ -231,6 +231,10 @@ public class Waypoint {
} }
for (BlackboardAttribute.ATTRIBUTE_TYPE type : keys) { for (BlackboardAttribute.ATTRIBUTE_TYPE type : keys) {
// Don't add JSON properties to this list.
if (type.getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.JSON) {
continue;
}
String key = type.getDisplayName(); String key = type.getDisplayName();
String value = attributeMap.get(type).getDisplayString(); String value = attributeMap.get(type).getDisplayString();

View File

@ -40,38 +40,48 @@ public final class WaypointBuilder {
private static final Logger logger = Logger.getLogger(WaypointBuilder.class.getName()); private static final Logger logger = Logger.getLogger(WaypointBuilder.class.getName());
// SELECT statement for getting a list of waypoints. private final static String TIME_TYPE_IDS = String.format("%d, %d",
final static String GEO_ARTIFACT_QUERY BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID(),
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED.getTypeID());
private final static String GEO_ATTRIBUTE_TYPE_IDS = String.format("%d, %d, %d",
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE.getTypeID(),
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_START.getTypeID(),
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_WAYPOINTS.getTypeID());
// SELECT statement for getting a list of waypoints where %s is a comma separated list
// of attribute type ids.
private final static String GEO_ARTIFACT_QUERY
= "SELECT artifact_id, artifact_type_id " = "SELECT artifact_id, artifact_type_id "
+ "FROM blackboard_attributes " + "FROM blackboard_attributes "
+ "WHERE attribute_type_id IN (%d, %d) "; //NON-NLS + "WHERE attribute_type_id IN (%s) "; //NON-NLS
// SELECT statement to get only artifact_ids // SELECT statement to get only artifact_ids
final static String GEO_ARTIFACT_QUERY_ID_ONLY private final static String GEO_ARTIFACT_QUERY_ID_ONLY
= "SELECT artifact_id " = "SELECT artifact_id "
+ "FROM blackboard_attributes " + "FROM blackboard_attributes "
+ "WHERE attribute_type_id IN (%d, %d) "; //NON-NLS + "WHERE attribute_type_id IN (%s) "; //NON-NLS
// This Query will return a list of waypoint artifacts // This Query will return a list of waypoint artifacts
final static String GEO_ARTIFACT_WITH_DATA_SOURCES_QUERY private final static String GEO_ARTIFACT_WITH_DATA_SOURCES_QUERY
= "SELECT blackboard_attributes.artifact_id " = "SELECT blackboard_attributes.artifact_id "
+ "FROM blackboard_attributes, blackboard_artifacts " + "FROM blackboard_attributes, blackboard_artifacts "
+ "WHERE blackboard_attributes.artifact_id = blackboard_artifacts.artifact_id " + "WHERE blackboard_attributes.artifact_id = blackboard_artifacts.artifact_id "
+ "AND blackboard_attributes.attribute_type_id IN(%d, %d) " + "AND blackboard_attributes.attribute_type_id IN(%s) "
+ "AND data_source_obj_id IN (%s)"; //NON-NLS + "AND data_source_obj_id IN (%s)"; //NON-NLS
// Select will return the "most recent" timestamp from all waypoings // Select will return the "most recent" timestamp from all waypoings
final static String MOST_RECENT_TIME private final static String MOST_RECENT_TIME
= "SELECT MAX(value_int64) - (%d * 86400)" //86400 is the number of seconds in a day. = "SELECT MAX(value_int64) - (%d * 86400)" //86400 is the number of seconds in a day.
+ "FROM blackboard_attributes " + "FROM blackboard_attributes "
+ "WHERE attribute_type_id IN(%d, %d) " + "WHERE attribute_type_id IN(%s) "
+ "AND artifact_id " + "AND artifact_id "
+ "IN ( " + "IN ( "
+ "%s" //GEO_ARTIFACT with or without data source + "%s" //GEO_ARTIFACT with or without data source
+ " )"; + " )";
// Returns a list of artifacts with no time stamp // Returns a list of artifacts with no time stamp
final static String SELECT_WO_TIMESTAMP private final static String SELECT_WO_TIMESTAMP
= "SELECT DISTINCT artifact_id, artifact_type_id " = "SELECT DISTINCT artifact_id, artifact_type_id "
+ "FROM blackboard_attributes " + "FROM blackboard_attributes "
+ "WHERE artifact_id NOT IN (%s) " + "WHERE artifact_id NOT IN (%s) "
@ -132,7 +142,7 @@ public final class WaypointBuilder {
public static List<Route> getRoutes(List<Waypoint> waypoints) { public static List<Route> getRoutes(List<Waypoint> waypoints) {
List<Route> routeList = new ArrayList<>(); List<Route> routeList = new ArrayList<>();
for (Waypoint point : waypoints) { for (Waypoint point : waypoints) {
GeoPath path = point.getPath(); GeoPath path = point.getParentGeoPath();
if (path instanceof Route) { if (path instanceof Route) {
Route route = (Route) path; Route route = (Route) path;
if (!routeList.contains(route)) { if (!routeList.contains(route)) {
@ -154,7 +164,7 @@ public final class WaypointBuilder {
public static List<Track> getTracks(List<Waypoint> waypoints) { public static List<Track> getTracks(List<Waypoint> waypoints) {
List<Track> trackList = new ArrayList<>(); List<Track> trackList = new ArrayList<>();
for (Waypoint point : waypoints) { for (Waypoint point : waypoints) {
GeoPath path = point.getPath(); GeoPath path = point.getParentGeoPath();
if (path instanceof Track) { if (path instanceof Track) {
Track route = (Track) path; Track route = (Track) path;
if (!trackList.contains(route)) { if (!trackList.contains(route)) {
@ -507,9 +517,7 @@ public final class WaypointBuilder {
// FROM blackboard_attributes // FROM blackboard_attributes
// WHERE attribute_type_id IN (%d, %d) // WHERE attribute_type_id IN (%d, %d)
return String.format(SELECT_WO_TIMESTAMP, return String.format(SELECT_WO_TIMESTAMP,
String.format(GEO_ARTIFACT_QUERY_ID_ONLY, String.format(GEO_ARTIFACT_QUERY_ID_ONLY,TIME_TYPE_IDS),
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID(),
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED.getTypeID()),
getWaypointListQuery(dataSources)); getWaypointListQuery(dataSources));
} }
@ -542,15 +550,13 @@ public final class WaypointBuilder {
// MOST_RECENT_TIME // MOST_RECENT_TIME
// SELECT MAX(value_int64) - (%d * 86400) // SELECT MAX(value_int64) - (%d * 86400)
// FROM blackboard_attributes // FROM blackboard_attributes
// WHERE attribute_type_id IN(%d, %d) // WHERE attribute_type_id IN(%s)
// AND artifact_id // AND artifact_id
// IN ( %s ) // IN ( %s )
// //
mostRecentQuery = String.format("AND value_int64 > (%s)", //NON-NLS mostRecentQuery = String.format("AND value_int64 > (%s)", //NON-NLS
String.format(MOST_RECENT_TIME, String.format(MOST_RECENT_TIME,
cntDaysFromRecent, cntDaysFromRecent, TIME_TYPE_IDS,
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID(),
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED.getTypeID(),
getWaypointListQuery(dataSources) getWaypointListQuery(dataSources)
)); ));
} }
@ -558,10 +564,8 @@ public final class WaypointBuilder {
// GEO_ARTIFACT_QUERY // GEO_ARTIFACT_QUERY
// SELECT artifact_id, artifact_type_id // SELECT artifact_id, artifact_type_id
// FROM blackboard_attributes // FROM blackboard_attributes
// WHERE attribute_type_id IN (%d, %d) // WHERE attribute_type_id IN (%s)
String query = String.format(GEO_ARTIFACT_QUERY, String query = String.format(GEO_ARTIFACT_QUERY, TIME_TYPE_IDS);
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID(),
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED.getTypeID());
// That are in the list of artifacts for the given data Sources // That are in the list of artifacts for the given data Sources
query += String.format("AND artifact_id IN(%s)", getWaypointListQuery(dataSources)); //NON-NLS query += String.format("AND artifact_id IN(%s)", getWaypointListQuery(dataSources)); //NON-NLS
@ -592,10 +596,8 @@ public final class WaypointBuilder {
// GEO_ARTIFACT_QUERY // GEO_ARTIFACT_QUERY
// SELECT artifact_id, artifact_type_id // SELECT artifact_id, artifact_type_id
// FROM blackboard_attributes // FROM blackboard_attributes
// WHERE attribute_type_id IN (%d, %d) // WHERE attribute_type_id IN (%s)
return String.format(GEO_ARTIFACT_QUERY, return String.format(GEO_ARTIFACT_QUERY, GEO_ATTRIBUTE_TYPE_IDS);
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE.getTypeID(),
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_START.getTypeID());
} }
String dataSourceList = ""; String dataSourceList = "";
@ -608,9 +610,7 @@ public final class WaypointBuilder {
dataSourceList = dataSourceList.substring(0, dataSourceList.length() - 1); dataSourceList = dataSourceList.substring(0, dataSourceList.length() - 1);
} }
return String.format(GEO_ARTIFACT_WITH_DATA_SOURCES_QUERY, return String.format(GEO_ARTIFACT_WITH_DATA_SOURCES_QUERY, GEO_ATTRIBUTE_TYPE_IDS,
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE.getTypeID(),
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_START.getTypeID(),
dataSourceList); dataSourceList);
} }

View File

@ -1,6 +1,6 @@
DATExtractor_process_message=Processing DJI DAT file: %s DATExtractor_process_message=Processing DJI DAT file: %s
DATFileExtractor_Extractor_Name=DAT File Extractor DATFileExtractor_Extractor_Name=DAT File Extractor
DroneIngestModule_Description=Description DroneIngestModule_Description=Analyzes files generated by drones.
DroneIngestModule_Name=Drone DroneIngestModule_Name=Drone Analyzer
# {0} - AbstractFileName # {0} - AbstractFileName
DroneIngestModule_process_start=Started {0} DroneIngestModule_process_start=Started {0}

View File

@ -41,10 +41,11 @@ import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress;
import org.sleuthkit.autopsy.ingest.IngestJobContext; import org.sleuthkit.autopsy.ingest.IngestJobContext;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.blackboardutils.attributes.GeoWaypoint.GeoTrackPoint; import org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoTrackpointsUtil.GeoTrackPointList.GeoTrackPoint;
import org.sleuthkit.datamodel.blackboardutils.GeoArtifactsHelper; import org.sleuthkit.datamodel.blackboardutils.GeoArtifactsHelper;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.Blackboard.BlackboardException; import org.sleuthkit.datamodel.Blackboard.BlackboardException;
import org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoTrackpointsUtil.GeoTrackPointList;
/** /**
* Extract drone position data from DJI Phantom drones. * Extract drone position data from DJI Phantom drones.
@ -110,10 +111,10 @@ final class DATExtractor extends DroneExtractor {
} }
// Process the csv file // Process the csv file
List<GeoTrackPoint> trackPoints = processCSVFile(context, DATFile, csvFilePath); GeoTrackPointList trackPoints = processCSVFile(context, DATFile, csvFilePath);
if (trackPoints != null && !trackPoints.isEmpty()) { if (trackPoints != null && !trackPoints.isEmpty()) {
(new GeoArtifactsHelper(getSleuthkitCase(), getName(), DATFile)).addTrack(DATFile.getName(), trackPoints); (new GeoArtifactsHelper(getSleuthkitCase(), getName(), "DatCon", DATFile)).addTrack(DATFile.getName(), trackPoints, null);
} else { } else {
logger.log(Level.INFO, String.format("No trackpoints with valid longitude or latitude found in %s", DATFile.getName())); //NON-NLS logger.log(Level.INFO, String.format("No trackpoints with valid longitude or latitude found in %s", DATFile.getName())); //NON-NLS
} }
@ -187,8 +188,8 @@ final class DATExtractor extends DroneExtractor {
* *
* @throws DroneIngestException * @throws DroneIngestException
*/ */
private List<GeoTrackPoint> processCSVFile(IngestJobContext context, AbstractFile DATFile, String csvFilePath) throws DroneIngestException { private GeoTrackPointList processCSVFile(IngestJobContext context, AbstractFile DATFile, String csvFilePath) throws DroneIngestException {
List<GeoTrackPoint> trackPoints = new ArrayList<>(); GeoTrackPointList trackPoints = new GeoTrackPointList();
try (BufferedReader reader = new BufferedReader(new FileReader(new File(csvFilePath)))) { try (BufferedReader reader = new BufferedReader(new FileReader(new File(csvFilePath)))) {
// First read in the header line and process // First read in the header line and process
String line = reader.readLine(); String line = reader.readLine();
@ -202,7 +203,7 @@ final class DATExtractor extends DroneExtractor {
String[] values = line.split(","); //NON-NLS String[] values = line.split(","); //NON-NLS
GeoTrackPoint point = createTrackPoint(headerMap, values); GeoTrackPoint point = createTrackPoint(headerMap, values);
if (point != null) { if (point != null) {
trackPoints.add(point); trackPoints.addPoint(point);
} }
} }
@ -258,6 +259,7 @@ final class DATExtractor extends DroneExtractor {
return new GeoTrackPoint(latitude, return new GeoTrackPoint(latitude,
longitude, longitude,
getDoubleValue(columnLookup.get(HEADER_ALTITUDE), values), getDoubleValue(columnLookup.get(HEADER_ALTITUDE), values),
null,
getDoubleValue(columnLookup.get(HEADER_VELOCITY), values), getDoubleValue(columnLookup.get(HEADER_VELOCITY), values),
getDoubleValue(columnLookup.get(HEADER_DISTANCE_FROM_HP), values), getDoubleValue(columnLookup.get(HEADER_DISTANCE_FROM_HP), values),
getDoubleValue(columnLookup.get(HEADER_DISTANCE_TRAVELED), values), getDoubleValue(columnLookup.get(HEADER_DISTANCE_TRAVELED), values),

View File

@ -1,5 +0,0 @@
iOSModuleFactory.moduleName=iOS Analyzer
iOSModuleFactory.moduleDescription=Extracts system and 3rd party app data
TextMessageAnalyzer.bbAttribute.incoming=Incoming
TextMessageAnalyzer.bbAttribute.outgoing=Outgoing
TextMessageAnalyzer.bbAttribute.smsMessage=SMS Message

View File

@ -1,8 +0,0 @@
CallLogAnalyzer.indexError.message=Failed to index call log artifact for keyword search.
ContactAnalyzer.indexError.message=Failed to index contact artifact for keyword search.
iOSModuleFactory.moduleName=iOS Analyzer
iOSModuleFactory.moduleDescription=Extracts system and 3rd party app data
TextMessageAnalyzer.bbAttribute.incoming=Incoming
TextMessageAnalyzer.bbAttribute.outgoing=Outgoing
TextMessageAnalyzer.bbAttribute.smsMessage=SMS Message
TextMessageAnalyzer.indexError.message=Failed to index text message artifact for keyword search.

View File

@ -1,8 +0,0 @@
CallLogAnalyzer.indexError.message=\u30ad\u30fc\u30ef\u30fc\u30c9\u3092\u691c\u7d22\u3059\u308b\u305f\u3081\u306e\u3001\u901a\u8a71\u30ed\u30b0\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8\u3092\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002
ContactAnalyzer.indexError.message=\u30ad\u30fc\u30ef\u30fc\u30c9\u3092\u691c\u7d22\u3059\u308b\u305f\u3081\u306e\u3001\u9023\u7d61\u5148\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8\u3092\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002
iOSModuleFactory.moduleName=iOS Analyzer
iOSModuleFactory.moduleDescription=\u30b7\u30b9\u30c6\u30e0\u3068\u30b5\u30fc\u30c9\u30d1\u30fc\u30c6\u30a3\u88fd\u30a2\u30d7\u30ea\u30c7\u30fc\u30bf\u3092\u62bd\u51fa
TextMessageAnalyzer.bbAttribute.incoming=\u53d7\u4fe1
TextMessageAnalyzer.bbAttribute.outgoing=\u9001\u4fe1
TextMessageAnalyzer.bbAttribute.smsMessage=SMS\u30e1\u30c3\u30bb\u30fc\u30b8
TextMessageAnalyzer.indexError.message=\u30ad\u30fc\u30ef\u30fc\u30c9\u3092\u691c\u7d22\u3059\u308b\u305f\u3081\u306e\u3001\u30c6\u30ad\u30b9\u30c8\u30e1\u30c3\u30bb\u30fc\u30b8\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8\u3092\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002

View File

@ -1,192 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014-2018 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.modules.iOS;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.logging.Level;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.autopsy.ingest.IngestJobContext;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Blackboard;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Look for call logs and allow resulting blackboard artifacts to be generated.
*/
final class CallLogAnalyzer {
private Connection connection = null;
private ResultSet resultSet = null;
private Statement statement = null;
private long fileId = 0;
private java.io.File jFile = null;
private final String moduleName = iOSModuleFactory.getModuleName();
private static final Logger logger = Logger.getLogger(CallLogAnalyzer.class.getName());
private Blackboard blackboard;
/**
* Find call logs given an ingest job context and index the results.
*
* @param context The ingest job context.
*/
public void findCallLogs(IngestJobContext context) {
Case openCase;
try {
openCase = Case.getCurrentCaseThrows();
} catch (NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS
return;
}
blackboard = openCase.getSleuthkitCase().getBlackboard();
List<AbstractFile> absFiles;
try {
SleuthkitCase skCase = openCase.getSleuthkitCase();
absFiles = skCase.findAllFilesWhere("name ='contacts2.db' OR name ='contacts.db'"); //NON-NLS //get exact file names
if (absFiles.isEmpty()) {
return;
}
for (AbstractFile file : absFiles) {
String dbPath = "";
try {
jFile = new java.io.File(Case.getCurrentCaseThrows().getTempDirectory(), file.getName().replaceAll("[<>%|\"/:*\\\\]", ""));
dbPath = jFile.toString(); //path of file as string
fileId = file.getId();
ContentUtils.writeToFile(file, jFile, context::dataSourceIngestIsCancelled);
findCallLogsInDB(dbPath, fileId);
} catch (ReadContentInputStreamException ex) {
logger.log(Level.WARNING, String.format("Error reading content from file '%s' (id=%d).", file.getName(), fileId), ex); //NON-NLS
} catch (Exception ex) {
logger.log(Level.SEVERE, String.format("Error writing content from file '%s' (id=%d) to '%s'.", file.getName(), fileId, dbPath), ex); //NON-NLS
}
}
} catch (TskCoreException e) {
logger.log(Level.SEVERE, "Error finding Call logs", e); //NON-NLS
}
}
/**
* Index results for call logs found in the database.
*
* @param DatabasePath The path to the database.
* @param fileId The ID of the file associated with artifacts.
*/
@Messages({"CallLogAnalyzer.indexError.message=Failed to index call log artifact for keyword search."})
private void findCallLogsInDB(String DatabasePath, long fileId) {
if (DatabasePath == null || DatabasePath.isEmpty()) {
return;
}
try {
Class.forName("org.sqlite.JDBC"); //NON-NLS //load JDBC driver
connection = DriverManager.getConnection("jdbc:sqlite:" + DatabasePath); //NON-NLS
statement = connection.createStatement();
} catch (ClassNotFoundException | SQLException e) {
logger.log(Level.SEVERE, "Error opening database", e); //NON-NLS
}
Case currentCase;
try {
currentCase = Case.getCurrentCaseThrows();
} catch (NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS
return;
}
SleuthkitCase skCase = currentCase.getSleuthkitCase();
try {
AbstractFile file = skCase.getAbstractFileById(fileId);
if (file == null) {
logger.log(Level.SEVERE, "Error getting abstract file {0}", fileId); //NON-NLS
return;
}
try {
resultSet = statement.executeQuery(
"SELECT number,date,duration,type, name FROM calls ORDER BY date DESC;"); //NON-NLS
BlackboardArtifact bba;
String name; // name of person dialed or called. null if unregistered
String number; //string phone number
String duration; //duration of call in seconds
String date; // Unix time
String type; // 1 incoming, 2 outgoing, 3 missed
while (resultSet.next()) {
name = resultSet.getString("name"); //NON-NLS
number = resultSet.getString("number"); //NON-NLS
duration = resultSet.getString("duration"); //NON-NLS
date = resultSet.getString("date"); //NON-NLS
type = resultSet.getString("type"); //NON-NLS
bba = file.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG); //create a call log and then add attributes from result set.
Collection<BlackboardAttribute> attributes = new ArrayList<>();
if (type.equalsIgnoreCase("outgoing")) { //NON-NLS
attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO, moduleName, number));
} else { /// Covers INCOMING and MISSED
attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM, moduleName, number));
}
attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START, moduleName, date)); // RC: Should be long!
attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_END, moduleName, duration + date)); // RC: Should be long!
attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION, moduleName, type));
attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME, moduleName, name));
bba.addAttributes(attributes);
try {
/*
* post the artifact which will index the artifact for
* keyword search, and fire an event to notify UI of
* this new artifact
*/
blackboard.postArtifact(bba, moduleName);
} catch (Blackboard.BlackboardException ex) {
logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bba.getArtifactID(), ex); //NON-NLS
MessageNotifyUtil.Notify.error(
Bundle.CallLogAnalyzer_indexError_message(), bba.getDisplayName());
}
}
} catch (Exception e) {
logger.log(Level.SEVERE, "Error parsing Call logs to the Blackboard", e); //NON-NLS
} finally {
try {
resultSet.close();
statement.close();
connection.close();
} catch (Exception e) {
logger.log(Level.SEVERE, "Error closing the database", e); //NON-NLS
}
}
} catch (Exception e) {
logger.log(Level.SEVERE, "Error parsing Call logs to the Blackboard", e); //NON-NLS
}
}
}

View File

@ -1,251 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014-2019 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.modules.iOS;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.logging.Level;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.autopsy.ingest.IngestJobContext;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Blackboard;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.ReadContentInputStream;
import org.sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Look for call logs and allow resulting blackboard artifacts to be generated.
*/
final class ContactAnalyzer {
private Connection connection = null;
private String dbPath = "";
private long fileId = 0;
private java.io.File jFile = null;
private final String moduleName = iOSModuleFactory.getModuleName();
private static final Logger logger = Logger.getLogger(ContactAnalyzer.class.getName());
private Blackboard blackboard;
/**
* Find contacts given an ingest job context and index the results.
*
* @param context The ingest job context.
*/
public void findContacts(IngestJobContext context) {
Case openCase;
try {
openCase = Case.getCurrentCaseThrows();
} catch (NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS
return;
}
blackboard = openCase.getSleuthkitCase().getBlackboard();
List<AbstractFile> absFiles;
try {
SleuthkitCase skCase = openCase.getSleuthkitCase();
absFiles = skCase.findAllFilesWhere("LOWER(name) LIKE LOWER('%call_history%') "); //NON-NLS //get exact file names
if (absFiles.isEmpty()) {
return;
}
for (AbstractFile file : absFiles) {
try {
jFile = new java.io.File(openCase.getTempDirectory(), file.getName().replaceAll("[<>%|\"/:*\\\\]", ""));
dbPath = jFile.toString(); //path of file as string
fileId = file.getId();
ContentUtils.writeToFile(file, jFile, context::dataSourceIngestIsCancelled);
} catch (ReadContentInputStreamException ex) {
logger.log(Level.WARNING, String.format("Error reading content from file '%s' (id=%d).", file.getName(), fileId), ex); //NON-NLS
} catch (Exception ex) {
logger.log(Level.SEVERE, String.format("Error writing content from file '%s' (id=%d) to '%s'.", file.getName(), fileId, dbPath), ex); //NON-NLS
}
}
} catch (TskCoreException e) {
logger.log(Level.SEVERE, "Error finding Contacts", e); //NON-NLS
}
}
/**
* Create blackboard artifacts and index results for call logs found in the
* database.
*
* @param DatabasePath The path to the database.
* @param fileId The ID of the file associated with artifacts.
*/
@Messages({"ContactAnalyzer.indexError.message=Failed to index contact artifact for keyword search."})
private void findContactsInDB(String DatabasePath, long fileId) {
if (DatabasePath == null || DatabasePath.isEmpty()) {
return;
}
Case currentCase;
try {
currentCase = Case.getCurrentCaseThrows();
} catch (NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS
return;
}
Statement statement = null;
try {
Class.forName("org.sqlite.JDBC"); //NON-NLS //load JDBC driver
connection = DriverManager.getConnection("jdbc:sqlite:" + DatabasePath); //NON-NLS
statement = connection.createStatement();
} catch (ClassNotFoundException | SQLException e) {
logger.log(Level.SEVERE, "Error opening database", e); //NON-NLS
}
SleuthkitCase skCase = currentCase.getSleuthkitCase();
try {
AbstractFile file = skCase.getAbstractFileById(fileId);
if (file == null) {
logger.log(Level.SEVERE, "Error getting abstract file {0}", fileId); //NON-NLS
return;
}
ResultSet resultSet = null;
try {
// get display_name, mimetype(email or phone number) and data1 (phonenumber or email address depending on mimetype)
//sorted by name, so phonenumber/email would be consecutive for a person if they exist.
resultSet = statement.executeQuery(
"SELECT mimetype,data1, name_raw_contact.display_name AS display_name \n" //NON-NLS
+ "FROM raw_contacts JOIN contacts ON (raw_contacts.contact_id=contacts._id) \n" //NON-NLS
+ "JOIN raw_contacts AS name_raw_contact ON(name_raw_contact_id=name_raw_contact._id) " //NON-NLS
+ "LEFT OUTER JOIN data ON (data.raw_contact_id=raw_contacts._id) \n" //NON-NLS
+ "LEFT OUTER JOIN mimetypes ON (data.mimetype_id=mimetypes._id) \n" //NON-NLS
+ "WHERE mimetype = 'vnd.android.cursor.item/phone_v2' OR mimetype = 'vnd.android.cursor.item/email_v2'\n" //NON-NLS
+ "ORDER BY name_raw_contact.display_name ASC;"); //NON-NLS
BlackboardArtifact bba;
bba = file.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT);
Collection<BlackboardAttribute> attributes = new ArrayList<>();
String name;
String oldName = "";
String mimetype; // either phone or email
String data1; // the phone number or email
while (resultSet.next()) {
name = resultSet.getString("display_name"); //NON-NLS
data1 = resultSet.getString("data1"); //NON-NLS
mimetype = resultSet.getString("mimetype"); //NON-NLS
if (name.equals(oldName) == false) {
bba = file.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT);
attributes = new ArrayList<>();
attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME, moduleName, name));
}
if (mimetype.equals("vnd.android.cursor.item/phone_v2")) { //NON-NLS
attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER, moduleName, data1));
} else {
attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL, moduleName, data1));
}
// TODO: If this code comes back to life, add code to create the account
// and relationship between the phone numbers & emails. Also
// investigate if the mimetype "vnd.android.cursor.item/phone_v2"
// makes sense in an ios word
oldName = name;
bba.addAttributes(attributes);
try {
// index the artifact for keyword search
blackboard.postArtifact(bba, moduleName);
} catch (Blackboard.BlackboardException ex) {
logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bba.getArtifactID(), ex); //NON-NLS
MessageNotifyUtil.Notify.error(
Bundle.ContactAnalyzer_indexError_message(), bba.getDisplayName());
}
}
} catch (Exception e) {
logger.log(Level.SEVERE, "Error parsing Contacts to Blackboard", e); //NON-NLS
} finally {
try {
resultSet.close();
statement.close();
connection.close();
} catch (Exception e) {
logger.log(Level.SEVERE, "Error closing database", e); //NON-NLS
}
}
} catch (Exception e) {
logger.log(Level.SEVERE, "Error parsing Contacts to Blackboard", e); //NON-NLS
}
}
public static void copyFileUsingStream(AbstractFile file, File jFile) throws IOException {
InputStream is = new ReadContentInputStream(file);
OutputStream os = new FileOutputStream(jFile);
byte[] buffer = new byte[8192];
int length;
try {
while ((length = is.read(buffer)) != -1) {
os.write(buffer, 0, length);
os.flush();
}
} finally {
is.close();
os.close();
}
}
public static void copyFileUsingStreams(AbstractFile file, File jFile) {
InputStream istream;
OutputStream ostream = null;
int c;
final int EOF = -1;
istream = new ReadContentInputStream(file);
try {
ostream = new FileOutputStream(jFile);
while ((c = istream.read()) != EOF) {
ostream.write(c);
}
} catch (IOException e) {
logger.log(Level.WARNING, "Error copying file", e);
} finally {
try {
istream.close();
ostream.close();
} catch (IOException e) {
logger.log(Level.WARNING, "File did not close", e);
}
}
}
}

View File

@ -1,198 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014-2018 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.modules.iOS;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.logging.Level;
import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.autopsy.ingest.IngestJobContext;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Blackboard;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.ReadContentInputStream;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Look for text messages and allow resulting blackboard artifacts to be
* generated.
*/
class TextMessageAnalyzer {
private Connection connection = null;
private ResultSet resultSet = null;
private Statement statement = null;
private String dbPath = "";
private long fileId = 0;
private java.io.File jFile = null;
List<AbstractFile> absFiles;
private final String moduleName = iOSModuleFactory.getModuleName();
private static final Logger logger = Logger.getLogger(TextMessageAnalyzer.class.getName());
private Blackboard blackboard;
/**
* Find text messages given an ingest job context and index the results.
*
* @param context The ingest job context.
*/
void findTexts(IngestJobContext context) {
Case openCase;
try {
openCase = Case.getCurrentCaseThrows();
} catch (NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS
return;
}
blackboard = openCase.getSleuthkitCase().getBlackboard();
try {
SleuthkitCase skCase = openCase.getSleuthkitCase();
absFiles = skCase.findAllFilesWhere("name ='mmssms.db'"); //NON-NLS //get exact file name
if (absFiles.isEmpty()) {
return;
}
for (AbstractFile file : absFiles) {
try {
jFile = new java.io.File(Case.getCurrentCaseThrows().getTempDirectory(), file.getName().replaceAll("[<>%|\"/:*\\\\]", ""));
dbPath = jFile.toString(); //path of file as string
fileId = file.getId();
ContentUtils.writeToFile(file, jFile, context::dataSourceIngestIsCancelled);
findTextsInDB(dbPath, fileId);
} catch (ReadContentInputStream.ReadContentInputStreamException ex) {
logger.log(Level.WARNING, String.format("Error reading content from file '%s' (id=%d).", file.getName(), fileId), ex); //NON-NLS
} catch (Exception ex) {
logger.log(Level.SEVERE, String.format("Error writing content from file '%s' (id=%d) to '%s'.", file.getName(), fileId, dbPath), ex); //NON-NLS
}
}
} catch (TskCoreException e) {
logger.log(Level.SEVERE, "Error finding text messages", e); //NON-NLS
}
}
/**
* Create blackboard artifacts and index results for text messages found in
* the database.
*
* @param DatabasePath The path to the database.
* @param fileId The ID of the file associated with artifacts.
*/
@Messages({"TextMessageAnalyzer.indexError.message=Failed to index text message artifact for keyword search."})
private void findTextsInDB(String DatabasePath, long fileId) {
if (DatabasePath == null || DatabasePath.isEmpty()) {
return;
}
Case currentCase;
try {
currentCase = Case.getCurrentCaseThrows();
} catch (NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS
return;
}
try {
Class.forName("org.sqlite.JDBC"); //NON-NLS //load JDBC driver
connection = DriverManager.getConnection("jdbc:sqlite:" + DatabasePath); //NON-NLS
statement = connection.createStatement();
} catch (ClassNotFoundException | SQLException e) {
logger.log(Level.SEVERE, "Error opening database", e); //NON-NLS
}
SleuthkitCase skCase = currentCase.getSleuthkitCase();
try {
AbstractFile file = skCase.getAbstractFileById(fileId);
if (file == null) {
logger.log(Level.SEVERE, "Error getting abstract file {0}", fileId); //NON-NLS
return;
}
try {
resultSet = statement.executeQuery(
"SELECT address,date,type,subject,body FROM sms;"); //NON-NLS
BlackboardArtifact bba;
String address; // may be phone number, or other addresses
String date;//unix time
String type; // message received in inbox = 1, message sent = 2
String subject;//message subject
String body; //message body
while (resultSet.next()) {
address = resultSet.getString("address"); //NON-NLS
date = resultSet.getString("date"); //NON-NLS
type = resultSet.getString("type"); //NON-NLS
subject = resultSet.getString("subject"); //NON-NLS
body = resultSet.getString("body"); //NON-NLS
bba = file.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE); //create Message artifact and then add attributes from result set.
Collection<BlackboardAttribute> attributes = new ArrayList<>();
// @@@ NEed to put into more specific TO or FROM
if (type.equals("1")) {
attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION, moduleName, NbBundle.getMessage(this.getClass(), "TextMessageAnalyzer.bbAttribute.incoming")));
attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM, moduleName, address));
} else {
attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION, moduleName, NbBundle.getMessage(this.getClass(), "TextMessageAnalyzer.bbAttribute.outgoing")));
attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO, moduleName, address));
}
attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, moduleName, date));
attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION, moduleName, type));
attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SUBJECT, moduleName, subject));
attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT, moduleName, body));
attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MESSAGE_TYPE, moduleName, NbBundle.getMessage(this.getClass(), "TextMessageAnalyzer.bbAttribute.smsMessage")));
bba.addAttributes(attributes);
try {
/*
* post the artifact which will index the artifact for
* keyword search, and fire an event to notify UI of
* this new artifact
*/ blackboard.postArtifact(bba, moduleName);
} catch (Blackboard.BlackboardException ex) {
logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bba.getArtifactID(), ex); //NON-NLS
MessageNotifyUtil.Notify.error(
Bundle.TextMessageAnalyzer_indexError_message(), bba.getDisplayName());
}
}
} catch (Exception e) {
logger.log(Level.SEVERE, "Error parsing text messages to Blackboard", e); //NON-NLS
} finally {
try {
resultSet.close();
statement.close();
connection.close();
} catch (Exception e) {
logger.log(Level.SEVERE, "Error closing database", e); //NON-NLS
}
}
} catch (Exception e) {
logger.log(Level.SEVERE, "Error parsing text messages to Blackboard", e); //NON-NLS
}
}
}

View File

@ -1,50 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014 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.modules.iOS;
import java.util.HashMap;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress;
import org.sleuthkit.autopsy.ingest.IngestModule;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.autopsy.ingest.DataSourceIngestModule;
import org.sleuthkit.autopsy.ingest.IngestJobContext;
import org.sleuthkit.autopsy.ingest.IngestModuleReferenceCounter;
import org.sleuthkit.autopsy.ingest.IngestServices;
class iOSIngestModule implements DataSourceIngestModule {
private static final HashMap<Long, Long> fileCountsForIngestJobs = new HashMap<>();
private IngestJobContext context = null;
private static final IngestModuleReferenceCounter refCounter = new IngestModuleReferenceCounter();
private static final Logger logger = Logger.getLogger(iOSModuleFactory.class.getName());
private IngestServices services = IngestServices.getInstance();
@Override
public void startUp(IngestJobContext context) throws IngestModule.IngestModuleException {
this.context = context;
}
@Override
public IngestModule.ProcessResult process(Content dataSource, DataSourceIngestModuleProgress progressBar) {
ContactAnalyzer FindContacts = new ContactAnalyzer();
FindContacts.findContacts(context);
return IngestModule.ProcessResult.OK;
}
}

View File

@ -1,59 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014 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.modules.iOS;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.Version;
import org.sleuthkit.autopsy.ingest.DataSourceIngestModule;
import org.sleuthkit.autopsy.ingest.IngestModuleFactoryAdapter;
import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings;
//@ServiceProvider(service = IngestModuleFactory.class) //
public class iOSModuleFactory extends IngestModuleFactoryAdapter {
static String getModuleName() {
return NbBundle.getMessage(iOSModuleFactory.class, "iOSModuleFactory.moduleName");
}
@Override
public String getModuleDisplayName() {
return getModuleName();
}
@Override
public String getModuleDescription() {
return NbBundle.getMessage(iOSModuleFactory.class, "iOSModuleFactory.moduleDescription");
}
@Override
public String getModuleVersionNumber() {
return Version.getVersion();
}
@Override
public boolean isDataSourceIngestModuleFactory() {
return true;
}
@Override
public DataSourceIngestModule createDataSourceIngestModule(IngestModuleIngestJobSettings settings) {
return new iOSIngestModule();
}
}

View File

@ -40,9 +40,9 @@ public interface TextSummarizer {
* @param file The AbstractFile to summarize. * @param file The AbstractFile to summarize.
* @param summarySize The size of the summary to create. * @param summarySize The size of the summary to create.
* *
* @return The summary as a string. * @return The summary as a TextSummary object.
* *
* @throws IOException * @throws IOException
*/ */
String summarize(AbstractFile file, int summarySize) throws IOException; TextSummary summarize(AbstractFile file, int summarySize) throws IOException;
} }

View File

@ -0,0 +1,67 @@
/*
* Autopsy
*
* Copyright 2020 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.textsummarizer;
import java.awt.Image;
/**
* Class to contain all information necessary to display a summary for a file.s
*/
public class TextSummary {
private final String summaryText;
private final Image sampleImage;
private final int numberOfImages;
/**
* Create a new TextSummary object.
*
* @param summary - The text portion of the summary.
* @param image - The Image portion of the summary
* @param countOfImages - The number of images including the one provided in
* the document.
*/
public TextSummary(String summary, Image image, int countOfImages) {
summaryText = summary;
sampleImage = image;
numberOfImages = countOfImages;
}
/**
* @return the summaryText
*/
public String getSummaryText() {
return summaryText;
}
/**
* @return the sampleImage
*/
public Image getSampleImage() {
return sampleImage;
}
/**
* @return the numberOfImages
*/
public int getNumberOfImages() {
return numberOfImages;
}
}

View File

@ -2,7 +2,7 @@
* *
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2018-2019 Basis Technology Corp. * Copyright 2018-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -62,6 +62,7 @@ import org.sleuthkit.autopsy.modules.photoreccarver.PhotoRecCarverIngestModuleFa
import org.sleuthkit.autopsy.modules.vmextractor.VMExtractorIngestModuleFactory; import org.sleuthkit.autopsy.modules.vmextractor.VMExtractorIngestModuleFactory;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.centralrepository.datamodel.RdbmsCentralRepoFactory;
/** /**
* Utilities for testing intercase correlation feature. * Utilities for testing intercase correlation feature.
@ -220,7 +221,7 @@ class InterCaseTestUtils {
this.kitchenShink = new IngestJobSettings(InterCaseTestUtils.class.getCanonicalName(), IngestType.ALL_MODULES, kitchenSink); this.kitchenShink = new IngestJobSettings(InterCaseTestUtils.class.getCanonicalName(), IngestType.ALL_MODULES, kitchenSink);
try { try {
Collection<CorrelationAttributeInstance.Type> types = CorrelationAttributeInstance.getDefaultCorrelationTypes(); Collection<CorrelationAttributeInstance.Type> types = CentralRepository.getInstance().getCorrelationTypes();
//TODO use ids instead of strings //TODO use ids instead of strings
FILE_TYPE = types.stream().filter(type -> type.getDisplayName().equals("Files")).findAny().get(); FILE_TYPE = types.stream().filter(type -> type.getDisplayName().equals("Files")).findAny().get();
@ -297,8 +298,10 @@ class InterCaseTestUtils {
crSettings.createDbDirectory(); crSettings.createDbDirectory();
} }
crSettings.initializeDatabaseSchema(); RdbmsCentralRepoFactory centralRepoSchemaFactory = new RdbmsCentralRepoFactory(CentralRepoPlatforms.SQLITE, crSettings);
crSettings.insertDefaultDatabaseContent(); centralRepoSchemaFactory.initializeDatabaseSchema();
centralRepoSchemaFactory.insertDefaultDatabaseContent();
crSettings.saveSettings(); crSettings.saveSettings();
CentralRepoPlatforms.setSelectedPlatform(CentralRepoPlatforms.SQLITE.name()); CentralRepoPlatforms.setSelectedPlatform(CentralRepoPlatforms.SQLITE.name());
CentralRepoPlatforms.saveSelectedPlatform(); CentralRepoPlatforms.saveSelectedPlatform();

View File

@ -37,8 +37,12 @@ from org.sleuthkit.datamodel import BlackboardArtifact
from org.sleuthkit.datamodel import BlackboardAttribute from org.sleuthkit.datamodel import BlackboardAttribute
from org.sleuthkit.datamodel import TskCoreException from org.sleuthkit.datamodel import TskCoreException
from org.sleuthkit.datamodel.blackboardutils import GeoArtifactsHelper from org.sleuthkit.datamodel.blackboardutils import GeoArtifactsHelper
from org.sleuthkit.datamodel.blackboardutils.attributes import GeoWaypoint from org.sleuthkit.datamodel.blackboardutils.attributes import TskGeoWaypointsUtil
from org.sleuthkit.datamodel.blackboardutils.attributes import GeoTrackPoints from org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoWaypointsUtil import GeoWaypointList
from org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoWaypointsUtil.GeoWaypointList import GeoWaypoint
from org.sleuthkit.datamodel.blackboardutils.attributes import TskGeoTrackpointsUtil
from org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoTrackpointsUtil import GeoTrackPointList
from org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoTrackpointsUtil.GeoTrackPointList import GeoTrackPoint
from org.sleuthkit.autopsy.datamodel import ContentUtils from org.sleuthkit.autopsy.datamodel import ContentUtils
from org.sleuthkit.autopsy.ingest import IngestModule from org.sleuthkit.autopsy.ingest import IngestModule
from org.sleuthkit.autopsy.ingest.IngestModule import IngestModuleException from org.sleuthkit.autopsy.ingest.IngestModule import IngestModuleException
@ -64,19 +68,14 @@ class GPXParserDataSourceIngestModuleFactory(IngestModuleFactoryAdapter):
moduleName = "GPX Parser" moduleName = "GPX Parser"
# True - Verbose debugging messages sent to log file.
# False - Verbose debugging turned off.
debuglevel = False
def getModuleDisplayName(self): def getModuleDisplayName(self):
return self.moduleName return self.moduleName
# TODO: Give it a description
def getModuleDescription(self): def getModuleDescription(self):
return "Module that extracts GEO data from GPX files." return "Module that extracts GEO data from GPX files."
def getModuleVersionNumber(self): def getModuleVersionNumber(self):
return "1.1" return "1.2"
def isDataSourceIngestModuleFactory(self): def isDataSourceIngestModuleFactory(self):
return True return True
@ -88,10 +87,11 @@ class GPXParserDataSourceIngestModuleFactory(IngestModuleFactoryAdapter):
# Data Source-level ingest module. One gets created per data source. # Data Source-level ingest module. One gets created per data source.
class GPXParserDataSourceIngestModule(DataSourceIngestModule): class GPXParserDataSourceIngestModule(DataSourceIngestModule):
_logger = Logger.getLogger(GPXParserDataSourceIngestModuleFactory.moduleName) logger = Logger.getLogger(GPXParserDataSourceIngestModuleFactory.moduleName)
writeDebugMsgs = False
def log(self, level, msg): def log(self, level, msg):
self._logger.logp(level, self.__class__.__name__, inspect.stack()[1][3], msg) self.logger.logp(level, self.__class__.__name__, inspect.stack()[1][3], msg)
def __init__(self): def __init__(self):
self.context = None self.context = None
@ -106,178 +106,129 @@ class GPXParserDataSourceIngestModule(DataSourceIngestModule):
# We don't know how much work there is yet. # We don't know how much work there is yet.
progressBar.switchToIndeterminate() progressBar.switchToIndeterminate()
# This will work in 4.0.1 and beyond. # Get the case database and its blackboard.
# Use blackboard class to index blackboard artifacts for keyword search.
blackboard = Case.getCurrentCase().getServices().getBlackboard()
# Get the sleuthkitcase
skCase = Case.getCurrentCase().getSleuthkitCase() skCase = Case.getCurrentCase().getSleuthkitCase()
blackboard = skCase.getBlackboard()
# In the name and then count and read them. # Get any files with a .gpx extension.
# It would perhaps be better to get these files by MIME type instead.
# RC: It would also be better if this were a file level ingest module so it could process files extracted from archives.
fileManager = Case.getCurrentCase().getServices().getFileManager() fileManager = Case.getCurrentCase().getServices().getFileManager()
files = fileManager.findFiles(dataSource, "%.gpx") files = fileManager.findFiles(dataSource, "%.gpx")
# TODO: Would like to change this to find files based on mimetype rather than extension.
#files = findFiles(dataSource, "text/xml")
#if (file.isMimeType('text/xml') == False):
# Update the progress bar now that we know how much work there is to do.
numFiles = len(files) numFiles = len(files)
if GPXParserDataSourceIngestModuleFactory.debuglevel: self.log(Level.INFO, "found " + str(numFiles) + " files") if self.writeDebugMsgs: self.log(Level.INFO, "Found " + str(numFiles) + " GPX files")
progressBar.switchToDeterminate(numFiles) progressBar.switchToDeterminate(numFiles)
fileCount = 0;
# Get module name for adding attributes # Get the module name, it will be needed for adding attributes
moduleName = GPXParserDataSourceIngestModuleFactory.moduleName moduleName = GPXParserDataSourceIngestModuleFactory.moduleName
for file in files: # Check if a folder for this module is present in the case Temp directory.
# If not, create it.
# Get the GeoArtifactsHelper
geoArtifactHelper = GeoArtifactsHelper(skCase, moduleName, file)
# Check if the user pressed cancel while we were busy.
if self.context.isJobCancelled():
return IngestModule.ProcessResult.OK
#self.log(Level.INFO, "GPX: Processing file: " + file.getName())
fileCount += 1
# Check if module folder is present. If not, create it.
dirName = os.path.join(Case.getCurrentCase().getTempDirectory(), "GPX_Parser_Module") dirName = os.path.join(Case.getCurrentCase().getTempDirectory(), "GPX_Parser_Module")
try: try:
os.stat(dirName) os.stat(dirName)
except: except:
os.mkdir(dirName) os.mkdir(dirName)
# Create a temp file name. It appears that we cannot close and delete
# this file, but we can overwrite it for each file we need to process.
fileName = os.path.join(dirName, "tmp.gpx") fileName = os.path.join(dirName, "tmp.gpx")
# Check to see if temporary file exists. If it does, remove it. fileCount = 0;
if os.path.exists(fileName): for file in files:
try:
os.remove(fileName)
if GPXParserDataSourceIngestModuleFactory.debuglevel: self.log(Level.INFO, "GPX:\t" + "FILE DELETED " + fileName )
except:
if GPXParserDataSourceIngestModuleFactory.debuglevel: self.log(Level.INFO, "GPX:\t" + "FILE NOT DELETED " + fileName)
# This writes the file to the local file system. # Create a GeoArtifactsHelper for this file.
geoArtifactHelper = GeoArtifactsHelper(skCase, moduleName, None, file)
# Check if the user pressed cancel while we were busy.
if self.context.isJobCancelled():
return IngestModule.ProcessResult.OK
if self.writeDebugMsgs: self.log(Level.INFO, "Processing " + file.getUniquePath() + " (objID = " + str(file.getId()) + ")")
fileCount += 1
# Write the file so that it can be parsed by gpxpy.
localFile = File(fileName) localFile = File(fileName)
ContentUtils.writeToFile(file, localFile) ContentUtils.writeToFile(file, localFile)
# Send to gpxpy for parsing. # Send the file to gpxpy for parsing.
gpxfile = open(fileName) gpxfile = open(fileName)
try: try:
gpx = gpxpy.parse(gpxfile) gpx = gpxpy.parse(gpxfile)
if GPXParserDataSourceIngestModuleFactory.debuglevel: self.log(Level.INFO, "GPX:\t" + "FILE PARSED") if self.writeDebugMsgs: self.log(Level.INFO, "Parsed " + file.getUniquePath() + " (objID = " + str(file.getId()) + ")")
except: except Exception as e:
if GPXParserDataSourceIngestModuleFactory.debuglevel: self.log(Level.SEVERE, "GPX:\t" + file.getName() + " - FILE NOT PARSED") self.log(Level.WARNING, "Error parsing file " + file.getUniquePath() + " (objID = " + str(file.getId()) + "):" + str(e))
continue continue
if gpx: if gpx:
if GPXParserDataSourceIngestModuleFactory.debuglevel: self.log(Level.INFO, "GPX: TRACKS") if self.writeDebugMsgs: self.log(Level.INFO, "Processing tracks from " + file.getUniquePath() + " (objID = " + str(file.getId()) + ")")
for track in gpx.tracks: for track in gpx.tracks:
for segment in track.segments: for segment in track.segments:
geoPointList = ArrayList() geoPointList = TskGeoTrackpointsUtil.GeoTrackPointList()
for point in segment.points: for point in segment.points:
elevation = 0 elevation = 0
if point.elevation != None: if point.elevation != None:
elevation = point.elevation elevation = point.elevation
dateTime = 0 timeStamp = 0
try: try:
if (point.time != None): if (point.time != None):
datetime = long(time.mktime(point.time.timetuple())) timeStamp = long(time.mktime(point.time.timetuple()))
except: except Exception as e:
pass self.log(Level.WARNING, "Error getting track timestamp from " + file.getUniquePath() + " (objID = " + str(file.getId()) + "):" + str(e))
geoPointList.add(GeoWaypoint.GeoTrackPoint(point.latitude, point.longitude, elevation, 0, 0, 0, dateTime)) geoPointList.addPoint(GeoTrackPoint(point.latitude, point.longitude, elevation, None, 0, 0, 0, timeStamp))
try: try:
# Add the trackpoint using the helper class geoArtifactHelper.addTrack("Track", geoPointList, None)
geoartifact = geoArtifactHelper.addTrack("Trackpoint", geoPointList)
except Blackboard.BlackboardException as e: except Blackboard.BlackboardException as e:
if GPXParserDataSourceIngestModuleFactory.debuglevel: self.log(Level.SEVERE, "GPX: Error using geo artifact helper with blackboard " ) self.log(Level.SEVERE, "Error posting GPS track artifact for " + file.getUniquePath() + " (objID = " + str(file.getId()) + "):" + e.getMessage())
except TskCoreException as e: except TskCoreException as e:
if GPXParserDataSourceIngestModuleFactory.debuglevel: self.log(Level.SEVERE, "GPX: Error using geo artifact helper tskcoreexception" ) self.log(Level.SEVERE, "Error creating GPS track artifact for " + file.getUniquePath() + " (objID = " + str(file.getId()) + "):" + e.getMessage())
if GPXParserDataSourceIngestModuleFactory.debuglevel: self.log(Level.INFO, "GPX: WAYPOINTS") if self.writeDebugMsgs: self.log(Level.INFO, "Processing waypoints from " + file.getUniquePath() + " (objID = " + str(file.getId()) + ")")
for waypoint in gpx.waypoints: for waypoint in gpx.waypoints:
attributes = ArrayList()
try:
art = file.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_BOOKMARK) art = file.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_BOOKMARK)
attributes = ArrayList()
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE.getTypeID(), moduleName, waypoint.latitude)) attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE.getTypeID(), moduleName, waypoint.latitude))
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE.getTypeID(), moduleName, waypoint.longitude)) attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE.getTypeID(), moduleName, waypoint.longitude))
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_FLAG.getTypeID(), moduleName, "Waypoint")) attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_FLAG.getTypeID(), moduleName, "Waypoint"))
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME.getTypeID(), moduleName, waypoint.name)) attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME.getTypeID(), moduleName, waypoint.name))
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME.getTypeID(), moduleName, "GPXParser")) attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME.getTypeID(), moduleName, "GPXParser"))
art.addAttributes(attributes) art.addAttributes(attributes)
try: blackboard.postArtifact(art, moduleName)
# Post the artifact to blackboard
skCase.getBlackboard().postArtifact(art, moduleName)
except Blackboard.BlackboardException as e:
if GPXParserDataSourceIngestModuleFactory.debuglevel: self.log(Level.SEVERE, "GPX: Error using geo artifact helper with blackboard for waypoints" )
if GPXParserDataSourceIngestModuleFactory.debuglevel: self.log(Level.INFO, "GPX: ROUTES") except Blackboard.BlackboardException as e:
self.log(Level.SEVERE, "Error posting GPS bookmark artifact for " + file.getUniquePath() + " (objID = " + str(file.getId()) + "):" + e.getMessage())
except TskCoreException as e:
self.log(Level.SEVERE, "Error creating GPS bookmark artifact for " + file.getUniquePath() + " (objID = " + str(file.getId()) + "):" + e.getMessage())
if self.writeDebugMsgs: self.log(Level.INFO, "Processing routes from " + file.getUniquePath() + " (objID = " + str(file.getId()) + ")")
for route in gpx.routes: for route in gpx.routes:
firstTimeThru = 0
startingPoint = list() geoWaypointList = TskGeoWaypointsUtil.GeoWaypointList()
endingPoint = list()
for point in route.points: for point in route.points:
# If first time in loop only populate starting point geoWaypointList.addPoint(point.latitude, point.longitude, elevation, point.name)
if (firstTimeThru == 0):
startingPoint.append((point.latitude, point.longitude))
firstTimeThru = 1
else:
startingPoint.append((point.latitude, point.longitude))
endingPoint.append((point.latitude, point.longitude))
if (len(endingPoint) > 0):
# get length of ending point as this ensures that we have equal points to process.
for i in range(0,len(endingPoint) -1):
attributes = ArrayList()
art = file.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_ROUTE)
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_START.getTypeID(), moduleName, startingPoint[i][0]))
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_START.getTypeID(), moduleName, startingPoint[i][1]))
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_END.getTypeID(), moduleName, endingPoint[i][0]))
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_END.getTypeID(), moduleName, endingPoint[i][1]))
art.addAttributes(attributes)
try: try:
# Post the artifact to blackboard geoArtifactHelper.addRoute(None, None, geoWaypointList, None)
skCase.getBlackboard().postArtifact(art, moduleName)
except Blackboard.BlackboardException as e: except Blackboard.BlackboardException as e:
if GPXParserDataSourceIngestModuleFactory.debuglevel: self.log(Level.SEVERE, "GPX: Error using geo artifact helper with blackboard for waypoints" ) self.log("Error posting GPS route artifact for " + file.getUniquePath() + " (objID = " + str(file.getId()) + "):" + e.getMessage())
else: except TskCoreException as e:
if (len(startingPoint) > 0): self.log(Level.SEVERE, "Error creating GPS route artifact for " + file.getUniquePath() + " (objID = " + str(file.getId()) + "):" + e.getMessage())
attributes = ArrayList()
art = file.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_ROUTE)
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_START.getTypeID(), moduleName, startingPoint[0][0]))
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_START.getTypeID(), moduleName, startingPoint[0][1]))
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_END.getTypeID(), moduleName, startingPoint[0][0]))
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_END.getTypeID(), moduleName, startingPoint[0][1]))
art.addAttributes(attributes)
try:
# Post the artifact to blackboard
skCase.getBlackboard().postArtifact(art, moduleName)
except Blackboard.BlackboardException as e:
if GPXParserDataSourceIngestModuleFactory.debuglevel: self.log(Level.SEVERE, "GPX: Error using geo artifact helper with blackboard for waypoints" )
# Update the progress bar. # Update the progress bar.
progressBar.progress(fileCount) progressBar.progress(fileCount)
if os.path.exists(fileName):
try:
os.remove(fileName)
if GPXParserDataSourceIngestModuleFactory.debuglevel: self.log(Level.INFO, "GPX:\t" + "FILE DELETED")
except:
self.log(Level.SEVERE, "GPX:\t" + "FILE NOT DELETED")
# Post a message to the ingest messages inbox. # Post a message to the ingest messages inbox.
message = IngestMessage.createMessage(IngestMessage.MessageType.DATA, "GPX Parser Data Source Ingest Module", "Found %d files" % fileCount) message = IngestMessage.createMessage(IngestMessage.MessageType.DATA, moduleName, "Processed %d files" % fileCount)
IngestServices.getInstance().postMessage(message) IngestServices.getInstance().postMessage(message)
return IngestModule.ProcessResult.OK; return IngestModule.ProcessResult.OK;

View File

@ -41,6 +41,8 @@ from org.sleuthkit.datamodel import BlackboardArtifact
from org.sleuthkit.datamodel import BlackboardAttribute from org.sleuthkit.datamodel import BlackboardAttribute
from org.sleuthkit.datamodel import Content from org.sleuthkit.datamodel import Content
from org.sleuthkit.datamodel import TskCoreException from org.sleuthkit.datamodel import TskCoreException
from org.sleuthkit.datamodel.blackboardutils import GeoArtifactsHelper
from org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoWaypointsUtil import GeoWaypointList
import traceback import traceback
import general import general
@ -52,15 +54,25 @@ class GoogleMapLocationAnalyzer(general.AndroidComponentAnalyzer):
def __init__(self): def __init__(self):
self._logger = Logger.getLogger(self.__class__.__name__) self._logger = Logger.getLogger(self.__class__.__name__)
self.current_case = None
self.PROGRAM_NAME = "Google Maps History"
self.CAT_DESTINATION = "Destination"
def analyze(self, dataSource, fileManager, context): def analyze(self, dataSource, fileManager, context):
try:
self.current_case = Case.getCurrentCaseThrows()
except NoCurrentCaseException as ex:
self._logger.log(Level.WARNING, "No case currently open.", ex)
self._logger.log(Level.WARNING, traceback.format_exc())
return
try: try:
absFiles = fileManager.findFiles(dataSource, "da_destination_history") absFiles = fileManager.findFiles(dataSource, "da_destination_history")
if absFiles.isEmpty(): if absFiles.isEmpty():
return return
for abstractFile in absFiles: for abstractFile in absFiles:
try: try:
jFile = File(Case.getCurrentCase().getTempDirectory(), str(abstractFile.getId()) + abstractFile.getName()) jFile = File(self.current_case.getTempDirectory(), str(abstractFile.getId()) + abstractFile.getName())
ContentUtils.writeToFile(abstractFile, jFile, context.dataSourceIngestIsCancelled) ContentUtils.writeToFile(abstractFile, jFile, context.dataSourceIngestIsCancelled)
self.__findGeoLocationsInDB(jFile.toString(), abstractFile) self.__findGeoLocationsInDB(jFile.toString(), abstractFile)
except Exception as ex: except Exception as ex:
@ -75,6 +87,8 @@ class GoogleMapLocationAnalyzer(general.AndroidComponentAnalyzer):
return return
try: try:
artifactHelper = GeoArtifactsHelper(self.current_case.getSleuthkitCase(),
general.MODULE_NAME, self.PROGRAM_NAME, abstractFile)
Class.forName("org.sqlite.JDBC") # load JDBC driver Class.forName("org.sqlite.JDBC") # load JDBC driver
connection = DriverManager.getConnection("jdbc:sqlite:" + databasePath) connection = DriverManager.getConnection("jdbc:sqlite:" + databasePath)
statement = connection.createStatement() statement = connection.createStatement()
@ -101,31 +115,21 @@ class GoogleMapLocationAnalyzer(general.AndroidComponentAnalyzer):
source_lat = GoogleMapLocationAnalyzer.convertGeo(resultSet.getString("source_lat")) source_lat = GoogleMapLocationAnalyzer.convertGeo(resultSet.getString("source_lat"))
source_lng = GoogleMapLocationAnalyzer.convertGeo(resultSet.getString("source_lng")) source_lng = GoogleMapLocationAnalyzer.convertGeo(resultSet.getString("source_lng"))
attributes = ArrayList() waypointlist = GeoWaypointList()
artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_ROUTE) waypointlist.addPoint(source_lat, source_lng, None, None)
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CATEGORY, general.MODULE_NAME, "Destination")) waypointlist.addPoint(dest_lat, dest_lng, None, dest_address)
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, general.MODULE_NAME, time))
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_END, general.MODULE_NAME, dest_lat))
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_END, general.MODULE_NAME, dest_lng))
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_START, general.MODULE_NAME, source_lat))
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_START, general.MODULE_NAME, source_lng))
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME, general.MODULE_NAME, dest_title))
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_LOCATION, general.MODULE_NAME, dest_address))
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, general.MODULE_NAME, "Google Maps History"))
artifact.addAttributes(attributes) artifactHelper.addRoute(dest_title, time, waypointlist, None)
try:
# index the artifact for keyword search
blackboard = Case.getCurrentCase().getSleuthkitCase().getBlackboard()
blackboard.postArtifact(artifact, general.MODULE_NAME)
except Blackboard.BlackboardException as ex:
self._logger.log(Level.SEVERE, "Unable to index blackboard artifact " + str(artifact.getArtifactID()), ex)
self._logger.log(Level.SEVERE, traceback.format_exc())
MessageNotifyUtil.Notify.error("Failed to index GPS route artifact for keyword search.", artifact.getDisplayName())
except SQLException as ex: except SQLException as ex:
# Unable to execute Google map locations SQL query against database. # Unable to execute Google map locations SQL query against database.
pass pass
except TskCoreException as ex:
self._logger.log(Level.SEVERE, "Failed to add route artifacts.", ex)
self._logger.log(Level.SEVERE, traceback.format_exc())
except BlackboardException as ex:
self._logger.log(Level.WARNING, "Failed to post artifacts.", ex)
self._logger.log(Level.WARNING, traceback.format_exc())
except Exception as ex: except Exception as ex:
self._logger.log(Level.SEVERE, "Error processing google maps history.", ex) self._logger.log(Level.SEVERE, "Error processing google maps history.", ex)
self._logger.log(Level.SEVERE, traceback.format_exc()) self._logger.log(Level.SEVERE, traceback.format_exc())

View File

@ -276,8 +276,8 @@
<binary-origin>release/modules/ext/icu4j-3.8.jar</binary-origin> <binary-origin>release/modules/ext/icu4j-3.8.jar</binary-origin>
</class-path-extension> </class-path-extension>
<class-path-extension> <class-path-extension>
<runtime-relative-path>ext/guava-17.0.jar</runtime-relative-path> <runtime-relative-path>ext/guava-19.0.jar</runtime-relative-path>
<binary-origin>release/modules/ext/guava-17.0.jar</binary-origin> <binary-origin>release/modules/ext/guava-19.0.jar</binary-origin>
</class-path-extension> </class-path-extension>
<class-path-extension> <class-path-extension>
<runtime-relative-path>ext/language-detector-0.6.jar</runtime-relative-path> <runtime-relative-path>ext/language-detector-0.6.jar</runtime-relative-path>

View File

@ -43,7 +43,7 @@ KeywordSearchEditListPanel.keywordsLabel.text=Keywords:
OpenIDE-Module-Short-Description=Keyword Search ingest module, extracted text viewer and keyword search tools OpenIDE-Module-Short-Description=Keyword Search ingest module, extracted text viewer and keyword search tools
KeywordSearchListsViewerPanel.manageListsButton.toolTipText=Manage keyword lists, their settings and associated keywords. The settings are shared among all cases. KeywordSearchListsViewerPanel.manageListsButton.toolTipText=Manage keyword lists, their settings and associated keywords. The settings are shared among all cases.
AbstractKeywordSearchPerformer.search.dialogErrorHeader=Keyword Search Error AbstractKeywordSearchPerformer.search.dialogErrorHeader=Keyword Search Error
AbstractKeywordSearchPerformer.search.invalidSyntaxHeader=Invalid query syntax. If this is a regular expression search note that Java predefined and POSIX character classes are not supported. AbstractKeywordSearchPerformer.search.invalidSyntaxHeader=Invalid query statement. If this is a regular expression, only Lucene regular expression patterns are supported. POSIX character classes (such as \\n or \\w) are not.
AbstractKeywordSearchPerformer.search.searchIngestInProgressTitle=Keyword Search Ingest in Progress AbstractKeywordSearchPerformer.search.searchIngestInProgressTitle=Keyword Search Ingest in Progress
AbstractKeywordSearchPerformer.search.ingestInProgressBody=<html>Keyword Search Ingest is currently running.<br />Not all files have been indexed and this search might yield incomplete results.<br />Do you want to proceed with this search anyway?</html> AbstractKeywordSearchPerformer.search.ingestInProgressBody=<html>Keyword Search Ingest is currently running.<br />Not all files have been indexed and this search might yield incomplete results.<br />Do you want to proceed with this search anyway?</html>
AbstractKeywordSearchPerformer.search.emptyKeywordErrorBody=Keyword list is empty, please add at least one keyword to the list AbstractKeywordSearchPerformer.search.emptyKeywordErrorBody=Keyword list is empty, please add at least one keyword to the list

View File

@ -79,7 +79,7 @@ KeywordSearchEditListPanel.keywordsLabel.text=Keywords:
OpenIDE-Module-Short-Description=Keyword Search ingest module, extracted text viewer and keyword search tools OpenIDE-Module-Short-Description=Keyword Search ingest module, extracted text viewer and keyword search tools
KeywordSearchListsViewerPanel.manageListsButton.toolTipText=Manage keyword lists, their settings and associated keywords. The settings are shared among all cases. KeywordSearchListsViewerPanel.manageListsButton.toolTipText=Manage keyword lists, their settings and associated keywords. The settings are shared among all cases.
AbstractKeywordSearchPerformer.search.dialogErrorHeader=Keyword Search Error AbstractKeywordSearchPerformer.search.dialogErrorHeader=Keyword Search Error
AbstractKeywordSearchPerformer.search.invalidSyntaxHeader=Invalid query syntax. If this is a regular expression search note that Java predefined and POSIX character classes are not supported. AbstractKeywordSearchPerformer.search.invalidSyntaxHeader=Invalid query statement. If this is a regular expression, only Lucene regular expression patterns are supported. POSIX character classes (such as \\n or \\w) are not.
AbstractKeywordSearchPerformer.search.searchIngestInProgressTitle=Keyword Search Ingest in Progress AbstractKeywordSearchPerformer.search.searchIngestInProgressTitle=Keyword Search Ingest in Progress
AbstractKeywordSearchPerformer.search.ingestInProgressBody=<html>Keyword Search Ingest is currently running.<br />Not all files have been indexed and this search might yield incomplete results.<br />Do you want to proceed with this search anyway?</html> AbstractKeywordSearchPerformer.search.ingestInProgressBody=<html>Keyword Search Ingest is currently running.<br />Not all files have been indexed and this search might yield incomplete results.<br />Do you want to proceed with this search anyway?</html>
AbstractKeywordSearchPerformer.search.emptyKeywordErrorBody=Keyword list is empty, please add at least one keyword to the list AbstractKeywordSearchPerformer.search.emptyKeywordErrorBody=Keyword list is empty, please add at least one keyword to the list

View File

@ -36,6 +36,7 @@ import java.util.logging.Level;
import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.ParserConfigurationException;
import org.apache.commons.io.FilenameUtils;
import org.openide.modules.InstalledFileLocator; import org.openide.modules.InstalledFileLocator;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.ExecUtil; import org.sleuthkit.autopsy.coreutils.ExecUtil;
@ -56,6 +57,7 @@ import org.xml.sax.SAXException;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.AbstractMap; import java.util.AbstractMap;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Collection; import java.util.Collection;
import java.util.Date; import java.util.Date;
@ -66,7 +68,6 @@ import java.util.Set;
import java.util.HashSet; import java.util.HashSet;
import static java.util.Locale.US; import static java.util.Locale.US;
import static java.util.TimeZone.getTimeZone; import static java.util.TimeZone.getTimeZone;
import org.apache.commons.io.FilenameUtils;
import org.openide.util.Lookup; import org.openide.util.Lookup;
import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress; import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress;
import org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException; import org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException;
@ -74,7 +75,9 @@ import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchService;
import org.sleuthkit.autopsy.recentactivity.ShellBagParser.ShellBag; import org.sleuthkit.autopsy.recentactivity.ShellBagParser.ShellBag;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_MODIFIED; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_MODIFIED;
@ -1201,8 +1204,6 @@ class ExtractRegistry extends Extract {
/** /**
* Create recently used artifacts from adobemru records * Create recently used artifacts from adobemru records
* *
* @param regFileName name of the regripper output file
*
* @param regFile registry file the artifact is associated with * @param regFile registry file the artifact is associated with
* *
* @param reader buffered reader to parse adobemru records * @param reader buffered reader to parse adobemru records
@ -1230,7 +1231,6 @@ class ExtractRegistry extends Extract {
if (fileName.charAt(0) == '/') { if (fileName.charAt(0) == '/') {
fileName = fileName.substring(1,fileName.length() - 1); fileName = fileName.substring(1,fileName.length() - 1);
fileName = fileName.replaceFirst("/", ":/"); fileName = fileName.replaceFirst("/", ":/");
fileName = FilenameUtils.normalize(fileName, true);
} }
// Check to see if more then 2 tokens, Date may not be populated, will default to 0 // Check to see if more then 2 tokens, Date may not be populated, will default to 0
if (tokens.length > 2) { if (tokens.length > 2) {
@ -1251,6 +1251,11 @@ class ExtractRegistry extends Extract {
BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes); BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes);
if(bba != null) { if(bba != null) {
bbartifacts.add(bba); bbartifacts.add(bba);
fileName = fileName.replace("\0", "");
bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba);
if (bba != null) {
bbartifacts.add(bba);
}
} }
line = reader.readLine(); line = reader.readLine();
} }
@ -1265,8 +1270,6 @@ class ExtractRegistry extends Extract {
/** /**
* Create recently used artifacts to parse the mpmru records * Create recently used artifacts to parse the mpmru records
* *
* @param regFileName name of the regripper output file
*
* @param regFile registry file the artifact is associated with * @param regFile registry file the artifact is associated with
* *
* @param reader buffered reader to parse adobemru records * @param reader buffered reader to parse adobemru records
@ -1287,12 +1290,19 @@ class ExtractRegistry extends Extract {
// Split line on "> " which is the record delimiter between position and file // Split line on "> " which is the record delimiter between position and file
String tokens[] = line.split("> "); String tokens[] = line.split("> ");
String fileName = tokens[1]; String fileName = tokens[1];
fileName = FilenameUtils.normalize(fileName, true);
Collection<BlackboardAttribute> attributes = new ArrayList<>(); Collection<BlackboardAttribute> attributes = new ArrayList<>();
attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName)); attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName));
BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes); BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes);
if(bba != null) { if(bba != null) {
bbartifacts.add(bba); bbartifacts.add(bba);
bba = createAssociatedArtifact(fileName, bba);
if (bba != null) {
bbartifacts.add(bba);
bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba);
if (bba != null) {
bbartifacts.add(bba);
}
}
} }
line = reader.readLine(); line = reader.readLine();
} }
@ -1307,8 +1317,6 @@ class ExtractRegistry extends Extract {
/** /**
* Create recently used artifacts to parse the regripper output * Create recently used artifacts to parse the regripper output
* *
* @param regFileName name of the regripper output file
*
* @param regFile registry file the artifact is associated with * @param regFile registry file the artifact is associated with
* *
* @param reader buffered reader to parse adobemru records * @param reader buffered reader to parse adobemru records
@ -1329,12 +1337,15 @@ class ExtractRegistry extends Extract {
// Split line on "> " which is the record delimiter between position and file // Split line on "> " which is the record delimiter between position and file
String tokens[] = line.split("> "); String tokens[] = line.split("> ");
String fileName = tokens[1]; String fileName = tokens[1];
fileName = FilenameUtils.normalize(fileName, true);
Collection<BlackboardAttribute> attributes = new ArrayList<>(); Collection<BlackboardAttribute> attributes = new ArrayList<>();
attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName)); attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName));
BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes); BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes);
if(bba != null) { if(bba != null) {
bbartifacts.add(bba); bbartifacts.add(bba);
bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba);
if (bba != null) {
bbartifacts.add(bba);
}
} }
line = reader.readLine(); line = reader.readLine();
} }
@ -1349,8 +1360,6 @@ class ExtractRegistry extends Extract {
/** /**
* Create recently used artifacts to parse the WinRAR output * Create recently used artifacts to parse the WinRAR output
* *
* @param regFileName name of the regripper output file
*
* @param regFile registry file the artifact is associated with * @param regFile registry file the artifact is associated with
* *
* @param reader buffered reader to parse adobemru records * @param reader buffered reader to parse adobemru records
@ -1372,12 +1381,15 @@ class ExtractRegistry extends Extract {
// Split line on "> " which is the record delimiter between position and file // Split line on "> " which is the record delimiter between position and file
String tokens[] = line.split("> "); String tokens[] = line.split("> ");
String fileName = tokens[1]; String fileName = tokens[1];
fileName = FilenameUtils.normalize(fileName, true);
Collection<BlackboardAttribute> attributes = new ArrayList<>(); Collection<BlackboardAttribute> attributes = new ArrayList<>();
attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName)); attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName));
BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes); BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes);
if(bba != null) { if(bba != null) {
bbartifacts.add(bba); bbartifacts.add(bba);
bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba);
if (bba != null) {
bbartifacts.add(bba);
}
} }
line = reader.readLine(); line = reader.readLine();
} }
@ -1393,8 +1405,6 @@ class ExtractRegistry extends Extract {
/** /**
* Create recently used artifacts to parse the runmru ArcHistory records * Create recently used artifacts to parse the runmru ArcHistory records
* *
* @param regFileName name of the regripper output file
*
* @param regFile registry file the artifact is associated with * @param regFile registry file the artifact is associated with
* *
* @param reader buffered reader to parse adobemru records * @param reader buffered reader to parse adobemru records
@ -1410,12 +1420,15 @@ class ExtractRegistry extends Extract {
// Columns are // Columns are
// <fileName> // <fileName>
String fileName = line; String fileName = line;
fileName = FilenameUtils.normalize(fileName, true);
Collection<BlackboardAttribute> attributes = new ArrayList<>(); Collection<BlackboardAttribute> attributes = new ArrayList<>();
attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName)); attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName));
BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes); BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes);
if (bba != null) { if (bba != null) {
bbartifacts.add(bba); bbartifacts.add(bba);
bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba);
if (bba != null) {
bbartifacts.add(bba);
}
} }
line = reader.readLine(); line = reader.readLine();
line = line.trim(); line = line.trim();
@ -1429,8 +1442,6 @@ class ExtractRegistry extends Extract {
/** /**
* Create recently used artifacts to parse the Office Documents 2010 records * Create recently used artifacts to parse the Office Documents 2010 records
* *
* @param regFileName name of the regripper output file
*
* @param regFile registry file the artifact is associated with * @param regFile registry file the artifact is associated with
* *
* @param reader buffered reader to parse adobemru records * @param reader buffered reader to parse adobemru records
@ -1454,13 +1465,16 @@ class ExtractRegistry extends Extract {
Long docDate = Long.valueOf(tokens[0]); Long docDate = Long.valueOf(tokens[0]);
String fileNameTokens[] = tokens[4].split(" - "); String fileNameTokens[] = tokens[4].split(" - ");
String fileName = fileNameTokens[1]; String fileName = fileNameTokens[1];
fileName = FilenameUtils.normalize(fileName, true);
Collection<BlackboardAttribute> attributes = new ArrayList<>(); Collection<BlackboardAttribute> attributes = new ArrayList<>();
attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName)); attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName));
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, getName(), docDate)); attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, getName(), docDate));
BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes); BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes);
if(bba != null) { if(bba != null) {
bbartifacts.add(bba); bbartifacts.add(bba);
bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba);
if (bba != null) {
bbartifacts.add(bba);
}
} }
line = reader.readLine(); line = reader.readLine();
line = line.trim(); line = line.trim();
@ -1473,8 +1487,6 @@ class ExtractRegistry extends Extract {
/** /**
* Create recently used artifacts to parse the trustrecords records * Create recently used artifacts to parse the trustrecords records
* *
* @param regFileName name of the regripper output file
*
* @param regFile registry file the artifact is associated with * @param regFile registry file the artifact is associated with
* *
* @param reader buffered reader to parse adobemru records * @param reader buffered reader to parse adobemru records
@ -1501,7 +1513,6 @@ class ExtractRegistry extends Extract {
String tokens[] = line.split(" : "); String tokens[] = line.split(" : ");
fileName = tokens[1]; fileName = tokens[1];
fileName = fileName.replace("%USERPROFILE%", userProfile); fileName = fileName.replace("%USERPROFILE%", userProfile);
fileName = FilenameUtils.normalize(fileName, true);
// Time in the format of Wed May 31 14:33:03 2017 Z // Time in the format of Wed May 31 14:33:03 2017 Z
try { try {
String fileUsedTime = tokens[0].replaceAll(" Z",""); String fileUsedTime = tokens[0].replaceAll(" Z","");
@ -1518,6 +1529,10 @@ class ExtractRegistry extends Extract {
BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes); BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes);
if(bba != null) { if(bba != null) {
bbartifacts.add(bba); bbartifacts.add(bba);
bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba);
if (bba != null) {
bbartifacts.add(bba);
}
} }
line = line.trim(); line = line.trim();
} }
@ -1527,6 +1542,46 @@ class ExtractRegistry extends Extract {
} }
} }
/**
* Create associated artifacts using file name and path and the artifact it associates with
*
* @param filePathName file and path of object being associated with
*
* @param bba blackboard artifact to associate with
*
* @returnv BlackboardArtifact or a null value
*/
private BlackboardArtifact createAssociatedArtifact(String filePathName, BlackboardArtifact bba) {
org.sleuthkit.autopsy.casemodule.services.FileManager fileManager = currentCase.getServices().getFileManager();
String fileName = FilenameUtils.getName(filePathName);
String filePath = FilenameUtils.getPath(filePathName);
List<AbstractFile> sourceFiles;
try {
sourceFiles = fileManager.findFiles(dataSource, fileName, filePath); //NON-NLS
if (!sourceFiles.isEmpty()) {
for (AbstractFile sourceFile : sourceFiles) {
if (sourceFile.getParentPath().endsWith(filePath)) {
Collection<BlackboardAttribute> bbattributes2 = new ArrayList<>();
bbattributes2.addAll(Arrays.asList(
new BlackboardAttribute(TSK_ASSOCIATED_ARTIFACT, this.getName(),
bba.getArtifactID())));
BlackboardArtifact associatedObjectBba = createArtifactWithAttributes(TSK_ASSOCIATED_OBJECT, sourceFile, bbattributes2);
if (associatedObjectBba != null) {
return associatedObjectBba;
}
}
}
}
} catch (TskCoreException ex) {
// only catching the error and displaying the message as the file may not exist on the
// system anymore
logger.log(Level.WARNING, String.format("Error finding actual file %s. file may not exist", filePathName)); //NON-NLS
}
return null;
}
/** /**
* Create the shellbag artifacts from the list of ShellBag objects. * Create the shellbag artifacts from the list of ShellBag objects.
* *

View File

@ -23,13 +23,13 @@
package org.sleuthkit.autopsy.recentactivity; package org.sleuthkit.autopsy.recentactivity;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
import org.apache.commons.io.FilenameUtils;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import java.util.Collection; import java.util.Collection;
import org.apache.commons.io.FilenameUtils;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.coreutils.JLNK; import org.sleuthkit.autopsy.coreutils.JLNK;
import org.sleuthkit.autopsy.coreutils.JLnkParser; import org.sleuthkit.autopsy.coreutils.JLnkParser;
@ -42,6 +42,8 @@ import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.*; import org.sleuthkit.datamodel.*;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT;
/** /**
* Recent documents class that will extract recent documents in the form of .lnk * Recent documents class that will extract recent documents in the form of .lnk
@ -108,7 +110,7 @@ class RecentDocumentsByLnk extends Extract {
} }
Collection<BlackboardAttribute> bbattributes = new ArrayList<>(); Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
String path = FilenameUtils.normalize(lnk.getBestPath(), true); String path = lnk.getBestPath();
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH, bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH,
NbBundle.getMessage(this.getClass(), NbBundle.getMessage(this.getClass(),
"RecentDocumentsByLnk.parentModuleName.noSpace"), "RecentDocumentsByLnk.parentModuleName.noSpace"),
@ -124,12 +126,53 @@ class RecentDocumentsByLnk extends Extract {
BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, recentFile, bbattributes); BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, recentFile, bbattributes);
if(bba != null) { if(bba != null) {
bbartifacts.add(bba); bbartifacts.add(bba);
bba = createAssociatedArtifact(path, bba);
if (bba != null) {
bbartifacts.add(bba);
}
} }
} }
postArtifacts(bbartifacts); postArtifacts(bbartifacts);
} }
/**
* Create associated artifacts using file name and path and the artifact it associates with
*
* @param filePathName file and path of object being associated with
*
* @param bba blackboard artifact to associate with
*
* @returnv BlackboardArtifact or a null value
*/
private BlackboardArtifact createAssociatedArtifact(String filePathName, BlackboardArtifact bba) {
org.sleuthkit.autopsy.casemodule.services.FileManager fileManager = currentCase.getServices().getFileManager();
String normalizePathName = FilenameUtils.normalize(filePathName, true);
String fileName = FilenameUtils.getName(normalizePathName);
String filePath = FilenameUtils.getPath(normalizePathName);
List<AbstractFile> sourceFiles;
try {
sourceFiles = fileManager.findFiles(dataSource, fileName, filePath); //NON-NLS
for (AbstractFile sourceFile : sourceFiles) {
if (sourceFile.getParentPath().endsWith(filePath)) {
Collection<BlackboardAttribute> bbattributes2 = new ArrayList<>();
bbattributes2.addAll(Arrays.asList(
new BlackboardAttribute(TSK_ASSOCIATED_ARTIFACT, this.getName(),
bba.getArtifactID())));
BlackboardArtifact associatedObjectBba = createArtifactWithAttributes(TSK_ASSOCIATED_OBJECT, sourceFile, bbattributes2);
if (associatedObjectBba != null) {
return associatedObjectBba;
}
}
}
} catch (TskCoreException ex) {
logger.log(Level.WARNING, String.format("Error finding actual file %s. file may not exist", filePathName)); //NON-NLS
}
return null;
}
@Override @Override
public void process(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar) { public void process(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar) {
this.dataSource = dataSource; this.dataSource = dataSource;

View File

@ -10,6 +10,8 @@ Autopsy ships with some built-in lists that define regular expressions and enabl
Once files are placed in the Solr index, they can be searched quickly for specific keywords, regular expressions, or keyword search lists that can contain a mixture of keywords and regular expressions. Search queries can be executed automatically during the ingest run or at the end of the ingest, depending on the current settings and the time it takes to ingest the image. Once files are placed in the Solr index, they can be searched quickly for specific keywords, regular expressions, or keyword search lists that can contain a mixture of keywords and regular expressions. Search queries can be executed automatically during the ingest run or at the end of the ingest, depending on the current settings and the time it takes to ingest the image.
Refer to \ref ad_hoc_keyword_search_page for more details on specifying regular expressions and other types of searches.
\section keyword_search_configuration_dialog Keyword Search Configuration Dialog \section keyword_search_configuration_dialog Keyword Search Configuration Dialog
The keyword search configuration dialog has three tabs, each with its own purpose: The keyword search configuration dialog has three tabs, each with its own purpose: