mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-06 21:00:22 +00:00
Merge branch 'develop' into 6020-Viber-Parser-produces-an-IllegalArgumentException-from-CommHelper
This commit is contained in:
commit
d6bc88d0d4
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2018 Basis Technology Corp.
|
||||
* Copyright 2018-2020 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -61,10 +61,10 @@ public final class AddEditCentralRepoCommentAction extends AbstractAction {
|
||||
*/
|
||||
public AddEditCentralRepoCommentAction(AbstractFile file) {
|
||||
fileId = file.getId();
|
||||
correlationAttributeInstance = CorrelationAttributeUtil.getInstanceFromContent(file);
|
||||
correlationAttributeInstance = CorrelationAttributeUtil.getCorrAttrForFile(file);
|
||||
if (correlationAttributeInstance == null) {
|
||||
addToDatabase = true;
|
||||
correlationAttributeInstance = CorrelationAttributeUtil.makeInstanceFromContent(file);
|
||||
correlationAttributeInstance = CorrelationAttributeUtil.makeCorrAttrFromFile(file);
|
||||
}
|
||||
if (file.getSize() == 0) {
|
||||
putValue(Action.NAME, Bundle.AddEditCentralRepoCommentAction_menuItemText_addEditCentralRepoCommentEmptyFile());
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Central Repository
|
||||
*
|
||||
* Copyright 2017-2019 Basis Technology Corp.
|
||||
* Copyright 2017-2020 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* 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
|
||||
BlackboardArtifact bbArtifact = getBlackboardArtifactFromNode(node);
|
||||
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
|
||||
|
@ -7,8 +7,10 @@ AbstractSqlEamDb.cannotUpgrage.message=Currently selected database platform "{0}
|
||||
AbstractSqlEamDb.failedToReadMajorVersion.message=Failed to read schema 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.
|
||||
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.nullName.message=Database name is null.
|
||||
CorrelationAttributeUtil.emailaddresses.text=Email Addresses
|
||||
CorrelationType.DOMAIN.displayName=Domains
|
||||
CorrelationType.EMAIL.displayName=Email Addresses
|
||||
CorrelationType.FILES.displayName=Files
|
||||
@ -23,7 +25,6 @@ DataSourceUpdateService.serviceName.text=Update Central Repository Data Sources
|
||||
EamArtifactInstances.knownStatus.bad=Bad
|
||||
EamArtifactInstances.knownStatus.known=Known
|
||||
EamArtifactInstances.knownStatus.unknown=Unknown
|
||||
EamArtifactUtil.emailaddresses.text=Email Addresses
|
||||
EamCase.title.caseDisplayName=Case Name:
|
||||
EamCase.title.caseNumber=Case Number:
|
||||
EamCase.title.caseUUID=Case UUID:
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
||||
}
|
@ -25,9 +25,6 @@ import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.List;
|
||||
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.ModuleSettings;
|
||||
import static org.sleuthkit.autopsy.centralrepository.datamodel.RdbmsCentralRepo.SOFTWARE_CR_DB_SCHEMA_VERSION;
|
||||
@ -166,80 +163,6 @@ public class CentralRepoDbUtil {
|
||||
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
|
||||
|
@ -23,6 +23,7 @@ import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount.CentralRepoAccountType;
|
||||
import org.sleuthkit.autopsy.coordinationservice.CoordinationService;
|
||||
|
||||
/**
|
||||
@ -803,4 +804,37 @@ public interface CentralRepository {
|
||||
* @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;
|
||||
|
||||
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Central Repository
|
||||
*
|
||||
* Copyright 2015-2018 Basis Technology Corp.
|
||||
* Copyright 2015-2020 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* 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.regex.Pattern;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.datamodel.Account;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
|
||||
/**
|
||||
@ -49,6 +50,7 @@ public class CorrelationAttributeInstance implements Serializable {
|
||||
private String comment;
|
||||
private TskData.FileKnown knownStatus;
|
||||
private Long objectId;
|
||||
private Long accountId;
|
||||
|
||||
public CorrelationAttributeInstance(
|
||||
CorrelationAttributeInstance.Type correlationType,
|
||||
@ -72,6 +74,20 @@ public class CorrelationAttributeInstance implements Serializable {
|
||||
String comment,
|
||||
TskData.FileKnown knownStatus,
|
||||
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 {
|
||||
if (filePath == null) {
|
||||
throw new CentralRepoException("file path is null");
|
||||
@ -87,6 +103,7 @@ public class CorrelationAttributeInstance implements Serializable {
|
||||
this.comment = comment;
|
||||
this.knownStatus = knownStatus;
|
||||
this.objectId = fileObjectId;
|
||||
this.accountId = accountId;
|
||||
}
|
||||
|
||||
public Boolean equals(CorrelationAttributeInstance otherInstance) {
|
||||
@ -97,7 +114,8 @@ public class CorrelationAttributeInstance implements Serializable {
|
||||
&& (this.getCorrelationDataSource().equals(otherInstance.getCorrelationDataSource()))
|
||||
&& (this.getFilePath().equals(otherInstance.getFilePath()))
|
||||
&& (this.getKnownStatus().equals(otherInstance.getKnownStatus()))
|
||||
&& (this.getComment().equals(otherInstance.getComment())));
|
||||
&& (this.getComment().equals(otherInstance.getComment()))
|
||||
&& (this.getAccountId().equals(otherInstance.getAccountId())));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -105,6 +123,7 @@ public class CorrelationAttributeInstance implements Serializable {
|
||||
return this.getID()
|
||||
+ this.getCorrelationCase().getCaseUUID()
|
||||
+ this.getCorrelationDataSource().getDeviceID()
|
||||
+ this.getAccountId()
|
||||
+ this.getFilePath()
|
||||
+ this.getCorrelationType().toString()
|
||||
+ this.getCorrelationValue()
|
||||
@ -209,6 +228,24 @@ public class CorrelationAttributeInstance implements Serializable {
|
||||
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
|
||||
public static final int FILES_TYPE_ID = 0;
|
||||
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 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
|
||||
*
|
||||
@ -238,18 +278,30 @@ public class CorrelationAttributeInstance implements Serializable {
|
||||
"CorrelationType.IMSI.displayName=IMSI Number",
|
||||
"CorrelationType.ICCID.displayName=ICCID Number"})
|
||||
public static List<CorrelationAttributeInstance.Type> getDefaultCorrelationTypes() throws CentralRepoException {
|
||||
List<CorrelationAttributeInstance.Type> DEFAULT_CORRELATION_TYPES = 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
|
||||
DEFAULT_CORRELATION_TYPES.add(new CorrelationAttributeInstance.Type(EMAIL_TYPE_ID, Bundle.CorrelationType_EMAIL_displayName(), "email_address", true, true)); // NON-NLS
|
||||
DEFAULT_CORRELATION_TYPES.add(new CorrelationAttributeInstance.Type(PHONE_TYPE_ID, Bundle.CorrelationType_PHONE_displayName(), "phone_number", true, true)); // NON-NLS
|
||||
DEFAULT_CORRELATION_TYPES.add(new CorrelationAttributeInstance.Type(USBID_TYPE_ID, Bundle.CorrelationType_USBID_displayName(), "usb_devices", true, true)); // NON-NLS
|
||||
DEFAULT_CORRELATION_TYPES.add(new CorrelationAttributeInstance.Type(SSID_TYPE_ID, Bundle.CorrelationType_SSID_displayName(), "wireless_networks", true, true)); // NON-NLS
|
||||
DEFAULT_CORRELATION_TYPES.add(new CorrelationAttributeInstance.Type(MAC_TYPE_ID, Bundle.CorrelationType_MAC_displayName(), "mac_address", true, true)); //NON-NLS
|
||||
DEFAULT_CORRELATION_TYPES.add(new CorrelationAttributeInstance.Type(IMEI_TYPE_ID, Bundle.CorrelationType_IMEI_displayName(), "imei_number", true, true)); //NON-NLS
|
||||
DEFAULT_CORRELATION_TYPES.add(new CorrelationAttributeInstance.Type(IMSI_TYPE_ID, Bundle.CorrelationType_IMSI_displayName(), "imsi_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
|
||||
return DEFAULT_CORRELATION_TYPES;
|
||||
List<CorrelationAttributeInstance.Type> defaultCorrelationTypes = new ArrayList<>();
|
||||
|
||||
defaultCorrelationTypes.add(new CorrelationAttributeInstance.Type(FILES_TYPE_ID, Bundle.CorrelationType_FILES_displayName(), "file", true, true)); // NON-NLS
|
||||
defaultCorrelationTypes.add(new CorrelationAttributeInstance.Type(DOMAIN_TYPE_ID, Bundle.CorrelationType_DOMAIN_displayName(), "domain", true, true)); // NON-NLS
|
||||
defaultCorrelationTypes.add(new CorrelationAttributeInstance.Type(EMAIL_TYPE_ID, Bundle.CorrelationType_EMAIL_displayName(), "email_address", true, true)); // NON-NLS
|
||||
defaultCorrelationTypes.add(new CorrelationAttributeInstance.Type(PHONE_TYPE_ID, Bundle.CorrelationType_PHONE_displayName(), "phone_number", true, true)); // NON-NLS
|
||||
defaultCorrelationTypes.add(new CorrelationAttributeInstance.Type(USBID_TYPE_ID, Bundle.CorrelationType_USBID_displayName(), "usb_devices", true, true)); // NON-NLS
|
||||
defaultCorrelationTypes.add(new CorrelationAttributeInstance.Type(SSID_TYPE_ID, Bundle.CorrelationType_SSID_displayName(), "wireless_networks", true, true)); // NON-NLS
|
||||
defaultCorrelationTypes.add(new CorrelationAttributeInstance.Type(MAC_TYPE_ID, Bundle.CorrelationType_MAC_displayName(), "mac_address", true, true)); //NON-NLS
|
||||
defaultCorrelationTypes.add(new CorrelationAttributeInstance.Type(IMEI_TYPE_ID, Bundle.CorrelationType_IMEI_displayName(), "imei_number", true, true)); //NON-NLS
|
||||
defaultCorrelationTypes.add(new CorrelationAttributeInstance.Type(IMSI_TYPE_ID, Bundle.CorrelationType_IMSI_displayName(), "imsi_number", true, true)); //NON-NLS
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -76,10 +76,22 @@ final public class CorrelationAttributeNormalizer {
|
||||
return normalizeIccid(trimmedData);
|
||||
|
||||
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(
|
||||
"Validator function not found for attribute type: %s",
|
||||
attributeType.getDisplayName());
|
||||
throw new CorrelationAttributeNormalizationException(errorMessage);
|
||||
} catch (CentralRepoException ex) {
|
||||
throw new CorrelationAttributeNormalizationException("Failed to get default correlation types.", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Central Repository
|
||||
*
|
||||
* Copyright 2015-2020 Basis Technology Corp.
|
||||
* Copyright 2017-2020 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* 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.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount.CentralRepoAccountType;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.HashUtility;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
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 {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(CorrelationAttributeUtil.class.getName());
|
||||
|
||||
@Messages({"EamArtifactUtil.emailaddresses.text=Email Addresses"})
|
||||
public static String getEmailAddressAttrString() {
|
||||
return Bundle.EamArtifactUtil_emailaddresses_text();
|
||||
/**
|
||||
* Gets a string that is expected to be the same string that is stored in
|
||||
* 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
|
||||
* has contents that can be used for Correlation. If so, return a
|
||||
* EamArtifact with a single EamArtifactInstance within. If not, return
|
||||
* null.
|
||||
* Makes zero to many correlation attribute instances from the attributes of
|
||||
* an artifact.
|
||||
*
|
||||
* @param artifact BlackboardArtifact to examine
|
||||
* @param checkEnabled If true, only create a CorrelationAttribute if it is
|
||||
* enabled
|
||||
* IMPORTANT: The correlation attribute instances are NOT added to the
|
||||
* central repository by this method.
|
||||
*
|
||||
* @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,
|
||||
boolean checkEnabled) {
|
||||
List<CorrelationAttributeInstance> eamArtifacts = new ArrayList<>();
|
||||
public static List<CorrelationAttributeInstance> makeCorrAttrsFromArtifact(BlackboardArtifact artifact) {
|
||||
List<CorrelationAttributeInstance> correlationAttrs = new ArrayList<>();
|
||||
try {
|
||||
BlackboardArtifact artifactForInstance = null;
|
||||
if (BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID() == artifact.getArtifactTypeID()) {
|
||||
// Get the associated artifactForInstance
|
||||
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();
|
||||
BlackboardArtifact sourceArtifact = getCorrAttrSourceArtifact(artifact);
|
||||
if (sourceArtifact != null) {
|
||||
int artifactTypeID = sourceArtifact.getArtifactTypeID();
|
||||
if (artifactTypeID == ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()) {
|
||||
BlackboardAttribute setNameAttr = artifactForInstance.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME));
|
||||
if (setNameAttr != null
|
||||
&& CorrelationAttributeUtil.getEmailAddressAttrString().equals(setNameAttr.getValueString())) {
|
||||
addCorrelationAttributeToList(eamArtifacts, artifactForInstance, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD, CorrelationAttributeInstance.EMAIL_TYPE_ID);
|
||||
BlackboardAttribute setNameAttr = sourceArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME));
|
||||
if (setNameAttr != null && CorrelationAttributeUtil.getEmailAddressAttrDisplayName().equals(setNameAttr.getValueString())) {
|
||||
makeCorrAttrFromArtifactAttr(correlationAttrs, sourceArtifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD, CorrelationAttributeInstance.EMAIL_TYPE_ID);
|
||||
}
|
||||
|
||||
} else if (artifactTypeID == ARTIFACT_TYPE.TSK_WEB_BOOKMARK.getTypeID()
|
||||
|| artifactTypeID == ARTIFACT_TYPE.TSK_WEB_COOKIE.getTypeID()
|
||||
|| artifactTypeID == ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.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()
|
||||
|| artifactTypeID == ARTIFACT_TYPE.TSK_CALLLOG.getTypeID()
|
||||
|| artifactTypeID == ARTIFACT_TYPE.TSK_MESSAGE.getTypeID()) {
|
||||
makeCorrAttrFromArtifactPhoneAttr(sourceArtifact);
|
||||
|
||||
String value = null;
|
||||
if (null != artifactForInstance.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER))) {
|
||||
value = artifactForInstance.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER)).getValueString();
|
||||
} 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 (null != artifactForInstance.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO))) {
|
||||
value = artifactForInstance.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO)).getValueString();
|
||||
} else if (artifactTypeID == ARTIFACT_TYPE.TSK_DEVICE_ATTACHED.getTypeID()) {
|
||||
makeCorrAttrFromArtifactAttr(correlationAttrs, sourceArtifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_ID, CorrelationAttributeInstance.USBID_TYPE_ID);
|
||||
makeCorrAttrFromArtifactAttr(correlationAttrs, sourceArtifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MAC_ADDRESS, CorrelationAttributeInstance.MAC_TYPE_ID);
|
||||
|
||||
} else if (artifactTypeID == ARTIFACT_TYPE.TSK_WIFI_NETWORK.getTypeID()) {
|
||||
makeCorrAttrFromArtifactAttr(correlationAttrs, sourceArtifact, 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()) {
|
||||
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) {
|
||||
String newValue = value.replaceAll("\\D", "");
|
||||
if (value.startsWith("+")) {
|
||||
newValue = "+" + 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) {
|
||||
CorrelationAttributeInstance inst = makeCorrelationAttributeInstanceUsingTypeValue(artifactForInstance, CentralRepository.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.PHONE_TYPE_ID), value);
|
||||
if (inst != null) {
|
||||
eamArtifacts.add(inst);
|
||||
corrAttr = makeCorrAttr(artifact, CentralRepository.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.PHONE_TYPE_ID), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (artifactTypeID == ARTIFACT_TYPE.TSK_DEVICE_ATTACHED.getTypeID()) {
|
||||
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;
|
||||
|
||||
return corrAttr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a CorrelationAttributeInstance of the specified type to the provided
|
||||
* list if the artifactForInstance has an Attribute of the given type with a
|
||||
* non empty value.
|
||||
* Makes a correlation attribute instance for an account artifact.
|
||||
*
|
||||
* @param eamArtifacts the list of CorrelationAttributeInstance objects
|
||||
* 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
|
||||
* Also creates an account in the CR DB if it doesn't exist.
|
||||
*
|
||||
* @throws CentralRepoException
|
||||
* @throws TskCoreException
|
||||
* IMPORTANT: The correlation attribute instance is NOT added to the central
|
||||
* 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 {
|
||||
BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(bbAttributeType));
|
||||
private static void makeCorrAttrFromAcctArtifact(List<CorrelationAttributeInstance> corrAttrInstances, BlackboardArtifact acctArtifact) throws TskCoreException, CentralRepoException {
|
||||
|
||||
// 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) {
|
||||
String value = attribute.getValueString();
|
||||
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) {
|
||||
eamArtifacts.add(inst);
|
||||
corrAttrInstances.add(inst);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the determined type and vallue, then looks up instance details to
|
||||
* create proper CorrelationAttributeInstance.
|
||||
* Makes a correlation attribute instance of a given type from an artifact.
|
||||
*
|
||||
* @param bbArtifact the blackboard artifactForInstance
|
||||
* @param correlationType the given type
|
||||
* @param value the artifactForInstance value
|
||||
* @param artifact The artifact.
|
||||
* @param correlationType the correlation attribute type.
|
||||
* @param value The correlation attribute value.
|
||||
*
|
||||
* @return CorrelationAttributeInstance from details, or null if validation
|
||||
* failed or another error occurred
|
||||
* 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 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 {
|
||||
Case currentCase = Case.getCurrentCaseThrows();
|
||||
AbstractFile bbSourceFile = currentCase.getSleuthkitCase().getAbstractFileById(bbArtifact.getObjectID());
|
||||
AbstractFile bbSourceFile = currentCase.getSleuthkitCase().getAbstractFileById(artifact.getObjectID());
|
||||
if (null == bbSourceFile) {
|
||||
logger.log(Level.SEVERE, "Error creating artifact instance. Abstract File was null."); // NON-NLS
|
||||
return null;
|
||||
}
|
||||
|
||||
// make an instance for the BB source file
|
||||
CorrelationCase correlationCase = CentralRepository.getInstance().getCase(Case.getCurrentCaseThrows());
|
||||
return new CorrelationAttributeInstance(
|
||||
correlationType,
|
||||
@ -212,31 +337,34 @@ public class CorrelationAttributeUtil {
|
||||
bbSourceFile.getId());
|
||||
|
||||
} 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;
|
||||
} 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;
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
|
||||
if (!(content instanceof AbstractFile)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final AbstractFile file = (AbstractFile) content;
|
||||
public static CorrelationAttributeInstance getCorrAttrForFile(AbstractFile file) {
|
||||
|
||||
if (!isSupportedAbstractFileType(file)) {
|
||||
return null;
|
||||
@ -254,11 +382,14 @@ public class CorrelationAttributeUtil {
|
||||
return null;
|
||||
}
|
||||
correlationDataSource = CorrelationDataSource.fromTSKDataSource(correlationCase, file.getDataSource());
|
||||
} catch (TskCoreException | CentralRepoException ex) {
|
||||
logger.log(Level.SEVERE, "Error retrieving correlation attribute.", ex);
|
||||
} catch (TskCoreException 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;
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.SEVERE, "Case is closed.", ex);
|
||||
logger.log(Level.SEVERE, "Error getting current case", ex); // NON-NLS
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -266,20 +397,22 @@ public class CorrelationAttributeUtil {
|
||||
try {
|
||||
correlationAttributeInstance = CentralRepository.getInstance().getCorrelationAttributeInstance(type, correlationCase, correlationDataSource, file.getId());
|
||||
} catch (CentralRepoException | CorrelationAttributeNormalizationException ex) {
|
||||
logger.log(Level.WARNING, String.format(
|
||||
"Correlation attribute could not be retrieved for '%s' (id=%d): ",
|
||||
content.getName(), content.getId()), ex);
|
||||
logger.log(Level.SEVERE, String.format("Error querying central repository (%s)", file), ex); // NON-NLS
|
||||
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) {
|
||||
String filePath = (file.getParentPath() + file.getName()).toLowerCase();
|
||||
try {
|
||||
correlationAttributeInstance = CentralRepository.getInstance().getCorrelationAttributeInstance(type, correlationCase, correlationDataSource, file.getMd5Hash(), filePath);
|
||||
} catch (CentralRepoException | CorrelationAttributeNormalizationException ex) {
|
||||
logger.log(Level.WARNING, String.format(
|
||||
"Correlation attribute could not be retrieved for '%s' (id=%d): ",
|
||||
content.getName(), content.getId()), ex);
|
||||
logger.log(Level.SEVERE, String.format("Error querying central repository (%s)", file), ex); // NON-NLS
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -288,32 +421,31 @@ public class CorrelationAttributeUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an EamArtifact from the given Content. Will return null if an
|
||||
* 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.
|
||||
* Makes a correlation attribute instance for a file.
|
||||
*
|
||||
* 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;
|
||||
}
|
||||
|
||||
final AbstractFile af = (AbstractFile) content;
|
||||
|
||||
if (!isSupportedAbstractFileType(af)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// We need a hash to make the artifactForInstance
|
||||
String md5 = af.getMd5Hash();
|
||||
// We need a hash to make the correlation artifact instance.
|
||||
String md5 = file.getMd5Hash();
|
||||
if (md5 == null || md5.isEmpty() || HashUtility.isNoDataMd5(md5)) {
|
||||
return null;
|
||||
}
|
||||
@ -324,31 +456,33 @@ public class CorrelationAttributeUtil {
|
||||
CorrelationCase correlationCase = CentralRepository.getInstance().getCase(Case.getCurrentCaseThrows());
|
||||
return new CorrelationAttributeInstance(
|
||||
filesType,
|
||||
af.getMd5Hash(),
|
||||
file.getMd5Hash(),
|
||||
correlationCase,
|
||||
CorrelationDataSource.fromTSKDataSource(correlationCase, af.getDataSource()),
|
||||
af.getParentPath() + af.getName(),
|
||||
CorrelationDataSource.fromTSKDataSource(correlationCase, file.getDataSource()),
|
||||
file.getParentPath() + file.getName(),
|
||||
"",
|
||||
TskData.FileKnown.UNKNOWN,
|
||||
af.getId());
|
||||
file.getId());
|
||||
|
||||
} catch (TskCoreException | CentralRepoException | CorrelationAttributeNormalizationException ex) {
|
||||
logger.log(Level.SEVERE, "Error making correlation attribute.", ex);
|
||||
} catch (TskCoreException 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;
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.SEVERE, "Case is closed.", ex);
|
||||
logger.log(Level.SEVERE, "Error getting current case", ex); // NON-NLS
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given abstract file should be processed for the central
|
||||
* repository.
|
||||
* Checks whether or not a file is of a type that can be added to the
|
||||
* 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
|
||||
* otherwise
|
||||
* @return True or false.
|
||||
*/
|
||||
public static boolean isSupportedAbstractFileType(AbstractFile file) {
|
||||
if (file == null) {
|
||||
@ -375,9 +509,9 @@ public class CorrelationAttributeUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new EamArtifactUtil
|
||||
* Prevent instantiation of this utility class.
|
||||
*/
|
||||
private CorrelationAttributeUtil() {
|
||||
//empty constructor
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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
|
||||
* 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 String DEFAULT_HOST = ""; // NON-NLS
|
||||
@ -64,6 +64,12 @@ public final class PostgresCentralRepoSettings {
|
||||
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() {
|
||||
host = ModuleSettings.getConfigSetting("CentralRepository", "db.postgresql.host"); // NON-NLS
|
||||
if (host == null || host.isEmpty()) {
|
||||
@ -187,6 +193,7 @@ public final class PostgresCentralRepoSettings {
|
||||
*
|
||||
* @return true if successfull connection, else false.
|
||||
*/
|
||||
@Override
|
||||
public boolean verifyConnection() {
|
||||
Connection conn = getEphemeralConnection(true);
|
||||
if (null == conn) {
|
||||
@ -203,6 +210,7 @@ public final class PostgresCentralRepoSettings {
|
||||
*
|
||||
* @return true if exists, else false
|
||||
*/
|
||||
@Override
|
||||
public boolean verifyDatabaseExists() {
|
||||
Connection conn = getEphemeralConnection(true);
|
||||
if (null == conn) {
|
||||
@ -236,6 +244,7 @@ public final class PostgresCentralRepoSettings {
|
||||
*
|
||||
* @return true if successful connection, else false.
|
||||
*/
|
||||
@Override
|
||||
public boolean verifyDatabaseSchema() {
|
||||
Connection conn = getEphemeralConnection(false);
|
||||
if (null == conn) {
|
||||
@ -248,6 +257,7 @@ public final class PostgresCentralRepoSettings {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean createDatabase() {
|
||||
Connection conn = getEphemeralConnection(true);
|
||||
if (null == conn) {
|
||||
@ -269,6 +279,7 @@ public final class PostgresCentralRepoSettings {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteDatabase() {
|
||||
Connection conn = getEphemeralConnection(true);
|
||||
if (null == conn) {
|
||||
|
@ -41,12 +41,15 @@ import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount.CentralRepoAccountType;
|
||||
import static org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoDbUtil.updateSchemaVersion;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.healthmonitor.HealthMonitor;
|
||||
import org.sleuthkit.autopsy.healthmonitor.TimingMetric;
|
||||
import org.sleuthkit.datamodel.Account;
|
||||
import org.sleuthkit.datamodel.CaseDbSchemaVersionNumber;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
|
||||
@ -71,6 +74,12 @@ abstract class RdbmsCentralRepo implements CentralRepository {
|
||||
private final Map<String, Collection<CorrelationAttributeInstance>> bulkArtifacts;
|
||||
private static final int CASE_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<String, CorrelationCase> caseCacheByUUID = CacheBuilder.newBuilder()
|
||||
.expireAfterWrite(CASE_CACHE_TIMEOUT, TimeUnit.MINUTES).
|
||||
@ -993,21 +1002,21 @@ abstract class RdbmsCentralRepo implements CentralRepository {
|
||||
public void addArtifactInstance(CorrelationAttributeInstance eamArtifact) throws CentralRepoException {
|
||||
checkAddArtifactInstanceNulls(eamArtifact);
|
||||
|
||||
Connection conn = connect();
|
||||
|
||||
PreparedStatement preparedStatement = null;
|
||||
|
||||
|
||||
|
||||
// @@@ We should cache the case and data source IDs in memory
|
||||
String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(eamArtifact.getCorrelationType());
|
||||
String sql
|
||||
= "INSERT INTO "
|
||||
+ tableName
|
||||
+ "(case_id, data_source_id, value, file_path, known_status, comment, file_obj_id) "
|
||||
+ "VALUES (?, ?, ?, ?, ?, ?, ?) "
|
||||
+ "(case_id, data_source_id, value, file_path, known_status, comment, file_obj_id, account_id) "
|
||||
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?) "
|
||||
+ getConflictClause();
|
||||
|
||||
try {
|
||||
preparedStatement = conn.prepareStatement(sql);
|
||||
try (Connection conn = connect();
|
||||
PreparedStatement preparedStatement = conn.prepareStatement(sql);) {
|
||||
|
||||
if (!eamArtifact.getCorrelationValue().isEmpty()) {
|
||||
preparedStatement.setInt(1, eamArtifact.getCorrelationCase().getID());
|
||||
@ -1023,17 +1032,162 @@ abstract class RdbmsCentralRepo implements CentralRepository {
|
||||
}
|
||||
preparedStatement.setLong(7, eamArtifact.getFileObjectId());
|
||||
|
||||
if (eamArtifact.getAccountId() >= 0) {
|
||||
preparedStatement.setLong(8, eamArtifact.getAccountId());
|
||||
} else {
|
||||
preparedStatement.setNull(8, Types.INTEGER);
|
||||
}
|
||||
|
||||
preparedStatement.executeUpdate();
|
||||
}
|
||||
|
||||
} catch (SQLException ex) {
|
||||
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 {
|
||||
if (eamArtifact == null) {
|
||||
throw new CentralRepoException("CorrelationAttribute is null");
|
||||
@ -1373,6 +1527,9 @@ abstract class RdbmsCentralRepo implements CentralRepository {
|
||||
}
|
||||
|
||||
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);
|
||||
bulkArtifactsCount++;
|
||||
|
||||
@ -2845,6 +3002,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
|
||||
typeId = newCorrelationTypeKnownId(newType);
|
||||
}
|
||||
|
||||
typeCache.put(newType.getId(), newType);
|
||||
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
|
||||
*
|
||||
@ -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
|
||||
*
|
||||
|
@ -19,12 +19,17 @@
|
||||
package org.sleuthkit.autopsy.centralrepository.datamodel;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.List;
|
||||
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 org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.datamodel.Account;
|
||||
|
||||
/**
|
||||
* 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
|
||||
// provides no benefit.
|
||||
Connection conn = null;
|
||||
Statement stmt = null;
|
||||
try {
|
||||
conn = this.getEphemeralConnection();
|
||||
try (Connection conn = this.getEphemeralConnection();) {
|
||||
|
||||
if (null == conn) {
|
||||
LOGGER.log(Level.SEVERE, "Cannot initialize CR database, don't have a valid connection."); // NON-NLS
|
||||
return false;
|
||||
}
|
||||
|
||||
stmt = conn.createStatement();
|
||||
try (Statement stmt = conn.createStatement();) {
|
||||
|
||||
// these setting PRAGMAs are SQLIte spcific
|
||||
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_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
|
||||
List<CorrelationAttributeInstance.Type> DEFAULT_CORRELATION_TYPES = CorrelationAttributeInstance.getDefaultCorrelationTypes();
|
||||
List<CorrelationAttributeInstance.Type> defaultCorrelationTypes = CorrelationAttributeInstance.getDefaultCorrelationTypes();
|
||||
|
||||
String reference_type_dbname;
|
||||
String instance_type_dbname;
|
||||
for (CorrelationAttributeInstance.Type type : DEFAULT_CORRELATION_TYPES) {
|
||||
for (CorrelationAttributeInstance.Type type : defaultCorrelationTypes) {
|
||||
reference_type_dbname = CentralRepoDbUtil.correlationTypeToReferenceTableName(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));
|
||||
}
|
||||
}
|
||||
// @TODO: uncomment this when ready to create Persona tables.
|
||||
//createPersonaTables(stmt);
|
||||
} catch (SQLException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Error initializing db schema.", ex); // NON-NLS
|
||||
return false;
|
||||
} 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
|
||||
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;
|
||||
}
|
||||
|
||||
@ -179,13 +184,24 @@ public class RdbmsCentralRepoFactory {
|
||||
* @return True if success, False otherwise.
|
||||
*/
|
||||
public boolean insertDefaultDatabaseContent() {
|
||||
Connection conn = this.getEphemeralConnection();
|
||||
|
||||
boolean result;
|
||||
try (Connection conn = this.getEphemeralConnection();) {
|
||||
if (null == conn) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean result = CentralRepoDbUtil.insertDefaultCorrelationTypes(conn) && CentralRepoDbUtil.insertDefaultOrganization(conn);
|
||||
CentralRepoDbUtil.closeConnection(conn);
|
||||
result = CentralRepoDbUtil.insertDefaultCorrelationTypes(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;
|
||||
}
|
||||
|
||||
@ -353,12 +369,14 @@ public class RdbmsCentralRepoFactory {
|
||||
+ getNumericPrimaryKeyClause("id", selectedPlatform)
|
||||
+ "case_id integer NOT NULL,"
|
||||
+ "data_source_id integer NOT NULL,"
|
||||
+ "account_id " + getBigIntType(selectedPlatform) + " DEFAULT NULL,"
|
||||
+ "value text NOT NULL,"
|
||||
+ "file_path text NOT NULL,"
|
||||
+ "known_status integer NOT NULL,"
|
||||
+ "comment text,"
|
||||
+ "file_obj_id " + getBigIntType(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 (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
|
||||
* 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
|
||||
* 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
|
||||
* 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
|
||||
* 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
|
||||
* 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
|
||||
* 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.
|
||||
*
|
||||
@ -541,4 +568,293 @@ public class RdbmsCentralRepoFactory {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -37,10 +37,10 @@ import org.sleuthkit.autopsy.coreutils.PlatformUtil;
|
||||
* NOTE: This is public scope because the options panel calls it directly to
|
||||
* 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 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 JDBC_DRIVER = "org.sqlite.JDBC"; // 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() {
|
||||
createDbDirectory();
|
||||
|
||||
@ -103,6 +115,13 @@ public final class SqliteCentralRepoSettings {
|
||||
return (!dbFile.isDirectory());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean verifyDatabaseExists() {
|
||||
return dbDirectoryExists();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
@ -325,5 +354,4 @@ public final class SqliteCentralRepoSettings {
|
||||
String getJDBCBaseURI() {
|
||||
return JDBC_BASE_URI;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -7,3 +7,6 @@ IngestEventsListener.prevCount.text=Number of previous {0}: {1}
|
||||
IngestEventsListener.prevExists.text=Previously Seen Devices (Central Repository)
|
||||
IngestEventsListener.prevTaggedSet.text=Previously Tagged As Notable (Central Repository)
|
||||
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?
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Central Repository
|
||||
*
|
||||
* Copyright 2015-2018 Basis Technology Corp.
|
||||
* Copyright 2017-2020 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* 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.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
import org.sleuthkit.datamodel.TskDataException;
|
||||
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) {
|
||||
// send update to Central Repository db
|
||||
@ -297,7 +296,7 @@ final class CaseEventListener implements PropertyChangeListener {
|
||||
return;
|
||||
}
|
||||
|
||||
List<CorrelationAttributeInstance> convertedArtifacts = CorrelationAttributeUtil.makeInstancesFromBlackboardArtifact(bbArtifact, true);
|
||||
List<CorrelationAttributeInstance> convertedArtifacts = CorrelationAttributeUtil.makeCorrAttrsFromArtifact(bbArtifact);
|
||||
for (CorrelationAttributeInstance eamArtifact : convertedArtifacts) {
|
||||
eamArtifact.setComment(comment);
|
||||
try {
|
||||
@ -370,7 +369,7 @@ final class CaseEventListener implements PropertyChangeListener {
|
||||
if (!hasTagWithConflictingKnownStatus) {
|
||||
//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
|
||||
List<CorrelationAttributeInstance> convertedArtifacts = CorrelationAttributeUtil.makeInstancesFromBlackboardArtifact(bbTag.getArtifact(), true);
|
||||
List<CorrelationAttributeInstance> convertedArtifacts = CorrelationAttributeUtil.makeCorrAttrsFromArtifact(bbTag.getArtifact());
|
||||
for (CorrelationAttributeInstance eamArtifact : convertedArtifacts) {
|
||||
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 (!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) {
|
||||
CentralRepository.getInstance().setAttributeInstanceKnownStatus(eamArtifact, tagName.getKnownStatus());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Cannot update known status in central repository for tag: " + modifiedTagName, ex); //NON-NLS
|
||||
} catch (CentralRepoException ex) {
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Central Repository
|
||||
*
|
||||
* Copyright 2015-2019 Basis Technology Corp.
|
||||
* Copyright 2017-2020 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -456,7 +456,7 @@ public class IngestEventsListener {
|
||||
|
||||
for (BlackboardArtifact bbArtifact : bbArtifacts) {
|
||||
// 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) {
|
||||
try {
|
||||
// Only do something with this artifact if it's unique within the job
|
||||
|
@ -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
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -18,28 +18,54 @@
|
||||
*/
|
||||
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.SwingUtilities;
|
||||
import org.openide.modules.ModuleInstall;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.windows.WindowManager;
|
||||
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.CentralRepoDbUtil;
|
||||
import org.sleuthkit.autopsy.core.RuntimeProperties;
|
||||
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 {
|
||||
|
||||
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 final CaseEventListener pcl = new CaseEventListener();
|
||||
private final IngestEventsListener ieListener = new IngestEventsListener();
|
||||
|
||||
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() {
|
||||
if (instance == null) {
|
||||
instance = new Installer();
|
||||
@ -47,48 +73,189 @@ public class Installer extends ModuleInstall {
|
||||
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() {
|
||||
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
|
||||
public void restored() {
|
||||
Case.addPropertyChangeListener(pcl);
|
||||
ieListener.installListeners();
|
||||
addApplicationEventListeners();
|
||||
|
||||
// Perform the database upgrade and inform the user if it fails
|
||||
try {
|
||||
CentralRepoDbUtil.upgradeDatabase();
|
||||
} catch (CentralRepoException ex) {
|
||||
if (Version.getBuildType() == Version.Type.RELEASE) {
|
||||
setupDefaultCentralRepository();
|
||||
}
|
||||
|
||||
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()) {
|
||||
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,
|
||||
ex.getUserMessage(),
|
||||
NbBundle.getMessage(this.getClass(),
|
||||
"Installer.centralRepoUpgradeFailed.title"),
|
||||
NbBundle.getMessage(this.getClass(), "Installer.centralRepoUpgradeFailed.title"),
|
||||
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
|
||||
public void uninstalled() {
|
||||
//module is being unloaded
|
||||
|
||||
Case.removePropertyChangeListener(pcl);
|
||||
pcl.shutdown();
|
||||
ieListener.shutdown();
|
||||
ieListener.uninstallListeners();
|
||||
|
||||
// TODO: remove thread pool
|
||||
/*
|
||||
* TODO (Jira-6108): This code is erronoeous. As documented at
|
||||
* http://bits.netbeans.org/dev/javadoc/org-openide-modules/org/openide/modules/ModuleInstall.html#uninstalled--
|
||||
*
|
||||
* "Called when the module is disabled while the application is still
|
||||
* running. Should remove whatever functionality that it had registered
|
||||
* in ModuleInstall.restored().
|
||||
*
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
|
@ -67,7 +67,7 @@ final class CentralRepoIngestModule implements FileIngestModule {
|
||||
private static final String MODULE_NAME = CentralRepoIngestModuleFactory.getModuleName();
|
||||
|
||||
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;
|
||||
|
||||
private final static Logger logger = Logger.getLogger(CentralRepoIngestModule.class.getName());
|
||||
|
@ -35,14 +35,15 @@ import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import javax.swing.filechooser.FileFilter;
|
||||
import org.netbeans.spi.options.OptionsPanelController;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.openide.windows.WindowManager;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoDbManager;
|
||||
import org.sleuthkit.autopsy.corecomponents.TextPrompt;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoPlatforms;
|
||||
import static org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoPlatforms.SQLITE;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.PostgresCentralRepoSettings;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.DatabaseTestResult;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.SqliteCentralRepoSettings;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.RdbmsCentralRepoFactory;
|
||||
@ -54,17 +55,12 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.RdbmsCentralRepoFactory
|
||||
public class EamDbSettingsDialog extends JDialog {
|
||||
|
||||
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 final Collection<JTextField> textBoxes;
|
||||
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
|
||||
@ -73,7 +69,6 @@ public class EamDbSettingsDialog extends JDialog {
|
||||
"EamDbSettingsDialog.lbSingleUserSqLite.text=SQLite should only be used by one examiner at a time.",
|
||||
"EamDbSettingsDialog.lbDatabaseType.text=Database Type :",
|
||||
"EamDbSettingsDialog.fcDatabasePath.title=Select location for central_repository.db"})
|
||||
|
||||
public EamDbSettingsDialog() {
|
||||
super((JFrame) WindowManager.getDefault().getMainWindow(),
|
||||
Bundle.EamDbSettingsDialog_title_text(),
|
||||
@ -81,12 +76,6 @@ public class EamDbSettingsDialog extends JDialog {
|
||||
|
||||
textBoxes = new ArrayList<>();
|
||||
textBoxChangedListener = new TextBoxChangedListener();
|
||||
dbSettingsPostgres = new PostgresCentralRepoSettings();
|
||||
dbSettingsSqlite = new SqliteCentralRepoSettings();
|
||||
selectedPlatform = CentralRepoPlatforms.getSelectedPlatform();
|
||||
if (selectedPlatform == null || selectedPlatform.equals(CentralRepoPlatforms.DISABLED)) {
|
||||
selectedPlatform = CentralRepoPlatforms.POSTGRESQL;
|
||||
}
|
||||
|
||||
initComponents();
|
||||
fcDatabasePath.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
|
||||
@ -98,7 +87,7 @@ public class EamDbSettingsDialog extends JDialog {
|
||||
if (pathname.isDirectory()) {
|
||||
return true;
|
||||
}
|
||||
return pathname.getName().toLowerCase().equals((CENTRAL_REPO_DB_NAME + CENTRAL_REPO_SQLITE_EXT).toLowerCase());
|
||||
return pathname.getName().equalsIgnoreCase(SqliteCentralRepoSettings.DEFAULT_DBNAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -106,13 +95,78 @@ public class EamDbSettingsDialog extends JDialog {
|
||||
return "Directories and Central Repository databases";
|
||||
}
|
||||
});
|
||||
cbDatabaseType.setSelectedItem(selectedPlatform);
|
||||
cbDatabaseType.setSelectedItem(manager.getSelectedPlatform());
|
||||
customizeComponents();
|
||||
valid();
|
||||
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.
|
||||
* 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() {
|
||||
setTextPrompts();
|
||||
setTextBoxListeners();
|
||||
switch (selectedPlatform) {
|
||||
case SQLITE:
|
||||
testingStatus = DatabaseTestResult.UNTESTED;
|
||||
manager.clearStatus();
|
||||
if (manager.getSelectedPlatform() == CentralRepoPlatforms.SQLITE) {
|
||||
updatePostgresFields(false);
|
||||
updateSqliteFields(true);
|
||||
break;
|
||||
default:
|
||||
POSTGRESQL:
|
||||
testingStatus = DatabaseTestResult.UNTESTED;
|
||||
}
|
||||
else {
|
||||
updatePostgresFields(true);
|
||||
updateSqliteFields(false);
|
||||
break;
|
||||
|
||||
}
|
||||
displayDatabaseSettings(selectedPlatform.equals(CentralRepoPlatforms.POSTGRESQL));
|
||||
displayDatabaseSettings(CentralRepoPlatforms.POSTGRESQL.equals(manager.getSelectedPlatform()));
|
||||
}
|
||||
|
||||
private void display() {
|
||||
@ -383,7 +432,7 @@ public class EamDbSettingsDialog extends JDialog {
|
||||
|
||||
@Messages({"EamDbSettingsDialog.chooserPath.failedToGetDbPathMsg=Selected database path is invalid. Try again."})
|
||||
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) {
|
||||
File databaseFile = fcDatabasePath.getSelectedFile();
|
||||
if (databaseFile.isFile()) {
|
||||
@ -399,176 +448,24 @@ public class EamDbSettingsDialog extends JDialog {
|
||||
}
|
||||
}//GEN-LAST:event_bnDatabasePathFileOpenActionPerformed
|
||||
|
||||
private void testDbSettings() {
|
||||
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.",
|
||||
@NbBundle.Messages({"EamDbSettingsDialog.okButton.errorTitle.text=Restart Required.",
|
||||
"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.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.connectionErrorMsg.text=Failed to connect to central repository database."})
|
||||
private void bnOkActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnOkActionPerformed
|
||||
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
||||
testDbSettings();
|
||||
if (testingStatus == DatabaseTestResult.CONNECTION_FAILED) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
manager.testStatus();
|
||||
valid();
|
||||
|
||||
if (testingStatus != DatabaseTestResult.TESTEDOK) {
|
||||
boolean testedOk = promptTestStatusWarnings();
|
||||
if (!testedOk) {
|
||||
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
|
||||
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{
|
||||
CentralRepository previousDbManager = CentralRepository.getInstance();
|
||||
if (null != previousDbManager) {
|
||||
// NOTE: do not set/save the seleted platform before calling this.
|
||||
CentralRepository.getInstance().shutdownConnections();
|
||||
manager.saveNewCentralRepo();
|
||||
}
|
||||
} catch (CentralRepoException ex) {
|
||||
logger.log(Level.SEVERE, "Failed to close database connections in previously selected platform.", ex); // NON-NLS
|
||||
catch (CentralRepoException e) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
JOptionPane.showMessageDialog(this,
|
||||
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));
|
||||
dispose();
|
||||
}//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
|
||||
dispose();
|
||||
}//GEN-LAST:event_bnCancelActionPerformed
|
||||
|
||||
|
||||
private void cbDatabaseTypeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbDatabaseTypeActionPerformed
|
||||
selectedPlatform = (CentralRepoPlatforms) cbDatabaseType.getSelectedItem();
|
||||
manager.setSelectedPlatform((CentralRepoPlatforms) cbDatabaseType.getSelectedItem());
|
||||
customizeComponents();
|
||||
}//GEN-LAST:event_cbDatabaseTypeActionPerformed
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
@ -670,13 +540,13 @@ public class EamDbSettingsDialog extends JDialog {
|
||||
}
|
||||
|
||||
private void updatePostgresFields(boolean enabled) {
|
||||
tbDbHostname.setText(enabled ? dbSettingsPostgres.getHost() : "");
|
||||
tbDbHostname.setText(enabled ? manager.getDbSettingsPostgres().getHost() : "");
|
||||
tbDbHostname.setEnabled(enabled);
|
||||
tbDbPort.setText(enabled ? Integer.toString(dbSettingsPostgres.getPort()) : "");
|
||||
tbDbPort.setText(enabled ? Integer.toString(manager.getDbSettingsPostgres().getPort()) : "");
|
||||
tbDbPort.setEnabled(enabled);
|
||||
tbDbUsername.setText(enabled ? dbSettingsPostgres.getUserName() : "");
|
||||
tbDbUsername.setText(enabled ? manager.getDbSettingsPostgres().getUserName() : "");
|
||||
tbDbUsername.setEnabled(enabled);
|
||||
jpDbPassword.setText(enabled ? dbSettingsPostgres.getPassword() : "");
|
||||
jpDbPassword.setText(enabled ? manager.getDbSettingsPostgres().getPassword() : "");
|
||||
jpDbPassword.setEnabled(enabled);
|
||||
}
|
||||
|
||||
@ -687,7 +557,7 @@ public class EamDbSettingsDialog extends JDialog {
|
||||
* @param enabled
|
||||
*/
|
||||
private void updateSqliteFields(boolean enabled) {
|
||||
tfDatabasePath.setText(enabled ? dbSettingsSqlite.getDbDirectory() : "");
|
||||
tfDatabasePath.setText(enabled ? manager.getDbSettingsSqlite().getDbDirectory() : "");
|
||||
tfDatabasePath.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."})
|
||||
private boolean databaseFieldsArePopulated() {
|
||||
boolean result = true;
|
||||
switch (selectedPlatform) {
|
||||
case POSTGRESQL:
|
||||
if (manager.getSelectedPlatform() == CentralRepoPlatforms.POSTGRESQL) {
|
||||
result = !tbDbHostname.getText().trim().isEmpty()
|
||||
&& !tbDbPort.getText().trim().isEmpty()
|
||||
// && !tbDbName.getText().trim().isEmpty()
|
||||
&& !tbDbUsername.getText().trim().isEmpty()
|
||||
&& 0 < jpDbPassword.getPassword().length;
|
||||
|
||||
break;
|
||||
|
||||
case SQLITE:
|
||||
result = !tfDatabasePath.getText().trim().isEmpty();
|
||||
break;
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
else if (manager.getSelectedPlatform() == CentralRepoPlatforms.SQLITE) {
|
||||
result = !tfDatabasePath.getText().trim().isEmpty();
|
||||
}
|
||||
|
||||
return result;
|
||||
@ -771,66 +634,6 @@ public class EamDbSettingsDialog extends JDialog {
|
||||
&& 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.
|
||||
@ -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
|
||||
* have been updated and that validation needs to happen.
|
||||
@ -867,7 +693,7 @@ public class EamDbSettingsDialog extends JDialog {
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e) {
|
||||
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
|
||||
testingStatus = DatabaseTestResult.UNTESTED;
|
||||
manager.clearStatus();
|
||||
updateFullDbPath();
|
||||
valid();
|
||||
}
|
||||
@ -875,7 +701,7 @@ public class EamDbSettingsDialog extends JDialog {
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
|
||||
testingStatus = DatabaseTestResult.UNTESTED;
|
||||
manager.clearStatus();
|
||||
updateFullDbPath();
|
||||
valid();
|
||||
}
|
||||
@ -883,20 +709,13 @@ public class EamDbSettingsDialog extends JDialog {
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
|
||||
testingStatus = DatabaseTestResult.UNTESTED;
|
||||
manager.clearStatus();
|
||||
updateFullDbPath();
|
||||
valid();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private enum DatabaseTestResult {
|
||||
UNTESTED,
|
||||
CONNECTION_FAILED,
|
||||
SCHEMA_INVALID,
|
||||
DB_DOES_NOT_EXIST,
|
||||
TESTEDOK;
|
||||
}
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JButton bnCancel;
|
||||
|
@ -31,6 +31,7 @@ import org.netbeans.spi.options.OptionsPanelController;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoDbManager;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
|
||||
import org.sleuthkit.autopsy.corecomponents.OptionsPanel;
|
||||
import org.sleuthkit.autopsy.events.AutopsyEvent;
|
||||
@ -86,7 +87,7 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
|
||||
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
||||
|
||||
try {
|
||||
CentralRepoDbUtil.upgradeDatabase();
|
||||
CentralRepoDbManager.upgradeDatabase();
|
||||
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
|
||||
} catch (CentralRepoException ex) {
|
||||
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
|
||||
|
@ -2,7 +2,7 @@
|
||||
*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2018-2019 Basis Technology Corp.
|
||||
* Copyright 2018-2020 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* 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
|
||||
return filteredCaseNameToDataSourcesTree;
|
||||
}
|
||||
CorrelationAttributeInstance.Type attributeType = CorrelationAttributeInstance
|
||||
.getDefaultCorrelationTypes()
|
||||
CorrelationAttributeInstance.Type attributeType = CentralRepository.getInstance().getCorrelationTypes()
|
||||
.stream()
|
||||
.filter(filterType -> filterType.getId() == resultTypeId)
|
||||
.findFirst().get();
|
||||
|
@ -128,13 +128,12 @@ final public class CommonAttributeCountSearchResults {
|
||||
return;
|
||||
}
|
||||
|
||||
CorrelationAttributeInstance.Type attributeType = CorrelationAttributeInstance
|
||||
.getDefaultCorrelationTypes()
|
||||
CentralRepository eamDb = CentralRepository.getInstance();
|
||||
CorrelationAttributeInstance.Type attributeType = eamDb.getCorrelationTypes()
|
||||
.stream()
|
||||
.filter(filterType -> filterType.getId() == this.resultTypeId)
|
||||
.findFirst().get();
|
||||
|
||||
CentralRepository eamDb = CentralRepository.getInstance();
|
||||
|
||||
Map<Integer, List<CommonAttributeValue>> itemsToRemove = new HashMap<>();
|
||||
//Call countUniqueDataSources once to reduce the number of DB queries needed to get
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2018 Basis Technology Corp.
|
||||
* Copyright 2018-2020 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* 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();
|
||||
}
|
||||
if (corType == null) {
|
||||
corType = CorrelationAttributeInstance.getDefaultCorrelationTypes().get(0);
|
||||
corType = CentralRepository.getInstance().getCorrelationTypes().get(0);
|
||||
}
|
||||
if (caseId == InterCasePanel.NO_CASE_SELECTED) {
|
||||
builder = new AllInterCaseCommonAttributeSearcher(filterByMedia, filterByDocuments, corType, percentageThreshold);
|
||||
@ -366,7 +366,7 @@ final class CommonAttributePanel extends javax.swing.JDialog implements Observer
|
||||
filterByDocuments = interCasePanel.documentsCheckboxIsSelected();
|
||||
}
|
||||
if (corType == null) {
|
||||
corType = CorrelationAttributeInstance.getDefaultCorrelationTypes().get(0);
|
||||
corType = CentralRepository.getInstance().getCorrelationTypes().get(0);
|
||||
}
|
||||
if (caseId == InterCasePanel.NO_CASE_SELECTED) {
|
||||
builder = new AllInterCaseCommonAttributeSearcher(filterByMedia, filterByDocuments, corType, percentageThreshold);
|
||||
|
@ -2,7 +2,7 @@
|
||||
*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2018-2019 Basis Technology Corp.
|
||||
* Copyright 2018-2020 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -31,6 +31,7 @@ import java.util.logging.Level;
|
||||
import javax.swing.ComboBoxModel;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
|
||||
/**
|
||||
@ -117,7 +118,7 @@ public final class InterCasePanel extends javax.swing.JPanel {
|
||||
void setupCorrelationTypeFilter() {
|
||||
this.correlationTypeFilters = new HashMap<>();
|
||||
try {
|
||||
List<CorrelationAttributeInstance.Type> types = CorrelationAttributeInstance.getDefaultCorrelationTypes();
|
||||
List<CorrelationAttributeInstance.Type> types = CentralRepository.getInstance().getCorrelationTypes();
|
||||
for (CorrelationAttributeInstance.Type type : types) {
|
||||
correlationTypeFilters.put(type.getDisplayName(), type);
|
||||
this.correlationTypeComboBox.addItem(type.getDisplayName());
|
||||
|
@ -16,9 +16,9 @@
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JTabbedPane" name="filterTabPane">
|
||||
<Container class="javax.swing.JTabbedPane" name="filterTabbedPane">
|
||||
<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>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
|
||||
@ -40,10 +40,6 @@
|
||||
</Constraints>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="org.sleuthkit.autopsy.communications.FiltersPanel" name="filtersPane">
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
@ -78,42 +74,6 @@
|
||||
</Constraints>
|
||||
|
||||
<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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
|
@ -55,7 +55,10 @@ public final class CVTTopComponent extends TopComponent {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private boolean filtersVisible = true;
|
||||
private final RelationshipBrowser relationshipBrowser = new RelationshipBrowser();
|
||||
private final AccountsBrowser accountsBrowser = new AccountsBrowser(relationshipBrowser);
|
||||
private CommunicationsFilter currentFilter;
|
||||
private final VisualizationPanel vizPanel = new VisualizationPanel(relationshipBrowser);
|
||||
private final FiltersPanel filtersPane = new FiltersPanel();
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
public CVTTopComponent() {
|
||||
@ -84,7 +87,10 @@ public final class CVTTopComponent extends TopComponent {
|
||||
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
|
||||
* 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.setTitleAt(0, "");
|
||||
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
@ -118,11 +125,8 @@ public final class CVTTopComponent extends TopComponent {
|
||||
|
||||
filterTabbedPane = new JTabbedPane();
|
||||
filterTabPanel = new JPanel();
|
||||
filtersPane = new FiltersPanel();
|
||||
splitPane = new JSplitPane();
|
||||
browseVisualizeTabPane = new JTabbedPane();
|
||||
accountsBrowser = new AccountsBrowser(relationshipBrowser);
|
||||
vizPanel = new VisualizationPanel(relationshipBrowser);
|
||||
|
||||
setLayout(new BorderLayout());
|
||||
|
||||
@ -131,10 +135,6 @@ public final class CVTTopComponent extends TopComponent {
|
||||
filterTabbedPaneMouseClicked(evt);
|
||||
}
|
||||
});
|
||||
|
||||
filterTabPanel.setLayout(new BorderLayout());
|
||||
filterTabPanel.add(filtersPane, BorderLayout.CENTER);
|
||||
|
||||
filterTabbedPane.addTab(NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.filterTabPanel.TabConstraints.tabTitle"), filterTabPanel); // NOI18N
|
||||
|
||||
add(filterTabbedPane, BorderLayout.WEST);
|
||||
@ -143,9 +143,6 @@ public final class CVTTopComponent extends TopComponent {
|
||||
splitPane.setResizeWeight(0.25);
|
||||
|
||||
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);
|
||||
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
|
||||
private AccountsBrowser accountsBrowser;
|
||||
private JTabbedPane browseVisualizeTabPane;
|
||||
private JTabbedPane filterTabbedPane;
|
||||
private JPanel filterTabPanel;
|
||||
private FiltersPanel filtersPane;
|
||||
private JTabbedPane filterTabbedPane;
|
||||
private JSplitPane splitPane;
|
||||
private VisualizationPanel vizPanel;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
@Override
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2019 Basis Technology Corp.
|
||||
* Copyright 2019-2020 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* 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 {
|
||||
if (correlationTypeMap == null) {
|
||||
correlationTypeMap = new HashMap<>();
|
||||
List<CorrelationAttributeInstance.Type> correcationTypeList = CorrelationAttributeInstance.getDefaultCorrelationTypes();
|
||||
List<CorrelationAttributeInstance.Type> correcationTypeList = CentralRepository.getInstance().getCorrelationTypes();
|
||||
correcationTypeList.forEach((type) -> {
|
||||
correlationTypeMap.put(type.getId(), type);
|
||||
});
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2018 Basis Technology Corp.
|
||||
* Copyright 2018-2020 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* 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");
|
||||
List<CorrelationAttributeInstance> instancesList = new ArrayList<>();
|
||||
if (artifact != null) {
|
||||
instancesList.addAll(CorrelationAttributeUtil.makeInstancesFromBlackboardArtifact(artifact, false));
|
||||
instancesList.addAll(CorrelationAttributeUtil.makeCorrAttrsFromArtifact(artifact));
|
||||
}
|
||||
try {
|
||||
List<CorrelationAttributeInstance.Type> artifactTypes = CentralRepository.getInstance().getDefinedCorrelationTypes();
|
||||
|
@ -2,3 +2,7 @@ ContextViewer.jSourceGoToResultButton.text=Go to Result
|
||||
ContextViewer.jSourceTextLabel.text=jLabel2
|
||||
ContextViewer.jSourceNameLabel.text=jSourceNameLabel
|
||||
ContextViewer.jSourceLabel.text=Source
|
||||
ContextViewer.jUsageGoToResultButton.text=Go to Result
|
||||
ContextViewer.jUsageLabel.text=Usage
|
||||
ContextViewer.jUsageNameLabel.text=jSourceNameLabel
|
||||
ContextViewer.jUsageTextLabel.text=jLabel2
|
||||
|
@ -7,9 +7,16 @@ ContextViewer.jSourceGoToResultButton.text=Go to Result
|
||||
ContextViewer.jSourceTextLabel.text=jLabel2
|
||||
ContextViewer.jSourceNameLabel.text=jSourceNameLabel
|
||||
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.messageFrom=From
|
||||
ContextViewer.messageOn=On
|
||||
ContextViewer.messageTo=To
|
||||
ContextViewer.on=Opened at
|
||||
ContextViewer.recentDocs=Recent Documents:
|
||||
ContextViewer.title=Context
|
||||
ContextViewer.toolTip=Displays context for selected file.
|
||||
ContextViewer.unknown=Opened at unknown time
|
||||
|
@ -23,26 +23,41 @@
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" 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">
|
||||
<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">
|
||||
<EmptySpace min="-2" pref="6" max="-2" attributes="0"/>
|
||||
<Component id="jSourceNameLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="jSourceTextLabel" pref="192" max="32767" attributes="0"/>
|
||||
</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>
|
||||
<EmptySpace min="-2" pref="36" max="-2" attributes="0"/>
|
||||
</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">
|
||||
<EmptySpace min="-2" pref="46" max="-2" attributes="0"/>
|
||||
<Component id="jSourceGoToResultButton" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
@ -56,7 +71,16 @@
|
||||
</Group>
|
||||
<EmptySpace type="separate" 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>
|
||||
</DimensionLayout>
|
||||
@ -96,5 +120,39 @@
|
||||
</Property>
|
||||
</Properties>
|
||||
</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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
|
@ -83,6 +83,10 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte
|
||||
jSourceLabel = new javax.swing.JLabel();
|
||||
jSourceNameLabel = 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));
|
||||
|
||||
@ -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(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);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(jSourceLabel)
|
||||
.addComponent(jUsageLabel)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGap(6, 6, 6)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(jSourceNameLabel)
|
||||
.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))
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(jSourceGoToResultButton)
|
||||
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))))
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.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.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
@ -131,7 +160,15 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte
|
||||
.addComponent(jSourceTextLabel))
|
||||
.addGap(18, 18, 18)
|
||||
.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
|
||||
|
||||
@ -146,6 +183,15 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte
|
||||
|
||||
}//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
|
||||
public void setNode(Node selectedNode) {
|
||||
if ((selectedNode == null) || (!isSupported(selectedNode))) {
|
||||
@ -246,9 +292,12 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte
|
||||
}
|
||||
}
|
||||
jSourceGoToResultButton.setVisible(true);
|
||||
jUsageGoToResultButton.setVisible(true);
|
||||
if (foundASource == false) {
|
||||
setSourceName("Unknown");
|
||||
showSourceText(false);
|
||||
setUsageName("Unknown");
|
||||
showUsageText(false);
|
||||
}
|
||||
|
||||
}
|
||||
@ -263,6 +312,11 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte
|
||||
* @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()) {
|
||||
BlackboardAttribute associatedArtifactAttribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT));
|
||||
if (associatedArtifactAttribute != null) {
|
||||
@ -287,7 +341,8 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"ContextViewer.attachmentSource=Attached to: ",
|
||||
"ContextViewer.downloadSource=Downloaded from: "
|
||||
"ContextViewer.downloadSource=Downloaded from: ",
|
||||
"ContextViewer.recentDocs=Recent Documents: "
|
||||
})
|
||||
private void setSourceFields(BlackboardArtifact associatedArtifact) throws TskCoreException {
|
||||
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());
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
@ -327,6 +395,24 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte
|
||||
jSourceTextLabel.setVisible(show);
|
||||
jSourceGoToResultButton.setEnabled(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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
@ -442,5 +558,9 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte
|
||||
private javax.swing.JLabel jSourceLabel;
|
||||
private javax.swing.JLabel jSourceNameLabel;
|
||||
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
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-2020 Basis Technology Corp.
|
||||
* Copyright 2012-2020 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* 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() {
|
||||
CorrelationAttributeInstance attribute = null;
|
||||
if (CentralRepository.isEnabled() && !UserPreferences.getHideSCOColumns()) {
|
||||
attribute = CorrelationAttributeUtil.getInstanceFromContent(content);
|
||||
attribute = CorrelationAttributeUtil.getCorrAttrForFile(content);
|
||||
}
|
||||
return attribute;
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-2020 Basis Technology Corp.
|
||||
* Copyright 2012-2020 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* 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) {
|
||||
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) {
|
||||
displayName = artifact.getName();
|
||||
}
|
||||
@ -605,8 +606,8 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
|
||||
@Override
|
||||
protected final CorrelationAttributeInstance getCorrelationAttributeInstance() {
|
||||
CorrelationAttributeInstance correlationAttribute = null;
|
||||
if (CentralRepository.isEnabled()) {
|
||||
correlationAttribute = CorrelationAttributeUtil.getInstanceFromContent(associated);
|
||||
if (CentralRepository.isEnabled() && associated instanceof AbstractFile) {
|
||||
correlationAttribute = CorrelationAttributeUtil.getCorrAttrForFile((AbstractFile)associated);
|
||||
}
|
||||
return correlationAttribute;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
} else {
|
||||
List<CorrelationAttributeInstance> listOfPossibleAttributes = CorrelationAttributeUtil.makeInstancesFromBlackboardArtifact(bbArtifact, false);
|
||||
List<CorrelationAttributeInstance> listOfPossibleAttributes = CorrelationAttributeUtil.makeCorrAttrsFromArtifact(bbArtifact);
|
||||
if (listOfPossibleAttributes.size() > 1) {
|
||||
//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();
|
||||
|
@ -40,6 +40,7 @@ import org.sleuthkit.autopsy.directorytree.NewWindowViewAction;
|
||||
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||
import org.sleuthkit.autopsy.ingest.ModuleContentEvent;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.Pool;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.VirtualDirectory;
|
||||
import org.sleuthkit.datamodel.Volume;
|
||||
@ -76,9 +77,19 @@ public class VolumeNode extends AbstractContentNode<Volume> {
|
||||
|
||||
// set name, display name, and icon
|
||||
String volName = nameForVolume(vol);
|
||||
|
||||
long end = vol.getStart() + (vol.getLength() - 1);
|
||||
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.setIconBaseWithExtension("org/sleuthkit/autopsy/images/vol-icon.png"); //NON-NLS
|
||||
|
@ -78,6 +78,7 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
|
||||
import org.sleuthkit.autopsy.textextractors.TextExtractor;
|
||||
import org.sleuthkit.autopsy.textextractors.TextExtractorFactory;
|
||||
import org.sleuthkit.autopsy.textsummarizer.TextSummarizer;
|
||||
import org.sleuthkit.autopsy.textsummarizer.TextSummary;
|
||||
|
||||
/**
|
||||
* Main class to perform the file search.
|
||||
@ -258,8 +259,8 @@ class FileSearch {
|
||||
*/
|
||||
@NbBundle.Messages({"FileSearch.documentSummary.noPreview=No preview available.",
|
||||
"FileSearch.documentSummary.noBytes=No bytes read for document, unable to display preview."})
|
||||
static String summarize(AbstractFile file) {
|
||||
String summary = null;
|
||||
static TextSummary summarize(AbstractFile file) {
|
||||
TextSummary summary = null;
|
||||
TextSummarizer localSummarizer = summarizerToUse;
|
||||
if (localSummarizer == null) {
|
||||
synchronized (searchCache) {
|
||||
@ -273,12 +274,12 @@ class FileSearch {
|
||||
//a summary of length 40 seems to fit without vertical scroll bars
|
||||
summary = localSummarizer.summarize(file, 40);
|
||||
} catch (IOException ex) {
|
||||
return Bundle.FileSearch_documentSummary_noPreview();
|
||||
return new TextSummary(Bundle.FileSearch_documentSummary_noPreview(), null, 0);
|
||||
}
|
||||
}
|
||||
if (StringUtils.isBlank(summary)) {
|
||||
//no summarizer was found or summary was empty just grab the beginning of the file
|
||||
summary = getFirstLines(file);
|
||||
if (summary == null || StringUtils.isBlank(summary.getSummaryText())) {
|
||||
//summary text was empty grab the beginning of the file
|
||||
summary = new TextSummary(getFirstLines(file), null, 0);
|
||||
}
|
||||
return summary;
|
||||
}
|
||||
@ -291,13 +292,21 @@ class FileSearch {
|
||||
* @return The beginning of text from the specified AbstractFile.
|
||||
*/
|
||||
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];
|
||||
reader.read(cbuf, 0, PREVIEW_SIZE);
|
||||
return new String(cbuf);
|
||||
} catch (IOException ex) {
|
||||
return Bundle.FileSearch_documentSummary_noBytes();
|
||||
} catch (TextExtractorFactory.NoTextExtractorFound | TextExtractor.InitReaderException ex) {
|
||||
} catch (TextExtractor.InitReaderException ex) {
|
||||
return Bundle.FileSearch_documentSummary_noPreview();
|
||||
}
|
||||
}
|
||||
|
@ -55,6 +55,7 @@ import org.sleuthkit.autopsy.modules.hashdatabase.AddContentToHashDbAction;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
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
|
||||
@ -777,11 +778,11 @@ public class ResultsPanel extends javax.swing.JPanel {
|
||||
@Messages({"ResultsPanel.unableToCreate.text=Unable to create summary."})
|
||||
@Override
|
||||
protected Void doInBackground() throws Exception {
|
||||
String preview = FileSearch.summarize(documentWrapper.getResultFile().getFirstInstance());
|
||||
TextSummary preview = FileSearch.summarize(documentWrapper.getResultFile().getFirstInstance());
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -30,6 +30,8 @@ GeoTopComponent_no_waypoints_returned_mgs=Applied filter failed to find waypoint
|
||||
GeoTopComponent_no_waypoints_returned_Title=No Waypoints Found
|
||||
GLTopComponent_initilzation_error=An error occurred during waypoint initilization. Geolocation data maybe incomplete.
|
||||
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
|
||||
MapPanel_connection_failure_message=Failed to connect to new geolocation map tile source.
|
||||
MapPanel_connection_failure_message_title=Connection Failure
|
||||
|
@ -52,6 +52,7 @@ class GeoFilterPanel extends javax.swing.JPanel {
|
||||
private final CheckBoxListPanel<DataSource> checkboxPanel;
|
||||
|
||||
// Make sure to update if
|
||||
@SuppressWarnings("deprecation")
|
||||
private static final BlackboardArtifact.ARTIFACT_TYPE[] GPS_ARTIFACT_TYPES = {
|
||||
BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_BOOKMARK,
|
||||
BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_LAST_KNOWN_LOCATION,
|
||||
|
@ -3,6 +3,9 @@ GEOTrack_point_label_header=Trackpoint for track: {0}
|
||||
LastKnownWaypoint_Label=Last Known Location
|
||||
Route_End_Label=End
|
||||
Route_Label=As-the-crow-flies Route
|
||||
Route_point_label=Waypoints for route
|
||||
Route_Start_Label=Start
|
||||
SearchWaypoint_DisplayLabel=GPS Search
|
||||
Track_distanceFromHome_displayName=Distance from home point
|
||||
Track_distanceTraveled_displayName=Distance traveled
|
||||
TrackpointWaypoint_DisplayLabel=GPS Trackpoint
|
||||
|
@ -25,20 +25,26 @@ import java.util.Map;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
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
|
||||
* however the class was written with the assumption that routes may have
|
||||
* more that two points.
|
||||
* however the class was written with the assumption that routes may have more
|
||||
* than two points.
|
||||
*
|
||||
*/
|
||||
public class Route extends GeoPath {
|
||||
|
||||
private final Long timestamp;
|
||||
|
||||
// This list is not expected to change after construction so the
|
||||
// constructor will take care of creating an unmodifiable List
|
||||
private final List<Waypoint.Property> propertiesList;
|
||||
|
||||
private static final TskGeoWaypointsUtil attributeUtil = new TskGeoWaypointsUtil();
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
addToPath(getRouteStartPoint(artifact, attributeMap));
|
||||
addToPath(getRouteEndPoint(artifact, attributeMap));
|
||||
createRoute(artifact, attributeMap);
|
||||
|
||||
BlackboardAttribute attribute = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME);
|
||||
timestamp = attribute != null ? attribute.getValueLong() : null;
|
||||
@ -82,10 +87,52 @@ public class Route extends GeoPath{
|
||||
return Collections.unmodifiableList(propertiesList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the route timestamp.
|
||||
*
|
||||
* @return Route timestamp
|
||||
*/
|
||||
public Long getTimestamp() {
|
||||
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.
|
||||
*
|
||||
@ -106,16 +153,14 @@ public class Route extends GeoPath{
|
||||
BlackboardAttribute latitude = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_START);
|
||||
BlackboardAttribute longitude = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_START);
|
||||
BlackboardAttribute altitude = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE);
|
||||
BlackboardAttribute pointTimestamp = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME);
|
||||
|
||||
if (latitude != null && longitude != null) {
|
||||
return new Waypoint(artifact,
|
||||
return new RoutePoint(artifact,
|
||||
Bundle.Route_Start_Label(),
|
||||
pointTimestamp != null ? pointTimestamp.getValueLong() : null,
|
||||
latitude.getValueDouble(),
|
||||
longitude.getValueDouble(),
|
||||
altitude != null ? altitude.getValueDouble() : null,
|
||||
null, attributeMap, this);
|
||||
attributeMap);
|
||||
} else {
|
||||
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 longitude = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_END);
|
||||
BlackboardAttribute altitude = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE);
|
||||
BlackboardAttribute pointTimestamp = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME);
|
||||
|
||||
if (latitude != null && longitude != null) {
|
||||
|
||||
return new Waypoint(artifact,
|
||||
return new RoutePoint(artifact,
|
||||
Bundle.Route_End_Label(),
|
||||
pointTimestamp != null ? pointTimestamp.getValueLong() : null,
|
||||
latitude.getValueDouble(),
|
||||
longitude.getValueDouble(),
|
||||
altitude != null ? altitude.getValueDouble() : null,
|
||||
null, attributeMap, this);
|
||||
attributeMap);
|
||||
} else {
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,8 +26,9 @@ import java.util.Map;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.blackboardutils.attributes.GeoTrackPoints;
|
||||
import org.sleuthkit.datamodel.blackboardutils.attributes.GeoWaypoint.GeoTrackPoint;
|
||||
import org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoTrackpointsUtil;
|
||||
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.
|
||||
@ -37,6 +38,8 @@ public final class Track extends GeoPath{
|
||||
private final Long startTimestamp;
|
||||
private final Long endTimeStamp;
|
||||
|
||||
private static final TskGeoTrackpointsUtil attributeUtil = new TskGeoTrackpointsUtil();
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
super(artifact, getTrackName(attributeMap));
|
||||
|
||||
List<GeoTrackPoint> points = getPointsList(attributeMap);
|
||||
GeoTrackPointList points = getPointsList(attributeMap);
|
||||
buildPath(points);
|
||||
|
||||
startTimestamp = findStartTime(points);
|
||||
endTimeStamp = findEndTime(points);
|
||||
startTimestamp = points.getStartTime();
|
||||
endTimeStamp = points.getEndTime();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -111,7 +114,7 @@ public final class Track extends GeoPath{
|
||||
"# {0} - track name",
|
||||
"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) {
|
||||
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.
|
||||
*/
|
||||
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);
|
||||
if (attribute != null) {
|
||||
String value = attribute.getValueString();
|
||||
return GeoTrackPoints.deserializePoints(value);
|
||||
return attributeUtil.fromAttribute(attribute);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -219,6 +182,10 @@ public final class Track extends GeoPath{
|
||||
*
|
||||
* @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) {
|
||||
List<Waypoint.Property> list = new ArrayList<>();
|
||||
|
||||
@ -234,12 +201,12 @@ public final class Track extends GeoPath{
|
||||
|
||||
value = point.getDistanceTraveled();
|
||||
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) {
|
||||
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;
|
||||
|
@ -43,7 +43,7 @@ public class Waypoint {
|
||||
final private String label;
|
||||
final private AbstractFile image;
|
||||
final private BlackboardArtifact artifact;
|
||||
final private GeoPath path;
|
||||
final private GeoPath parentGeoPath;
|
||||
|
||||
final private List<Waypoint.Property> propertiesList;
|
||||
|
||||
@ -78,7 +78,7 @@ public class Waypoint {
|
||||
* @throws GeoLocationDataException Exception will be thrown if artifact did
|
||||
* 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) {
|
||||
throw new GeoLocationDataException("Invalid waypoint, null value passed for longitude or latitude");
|
||||
}
|
||||
@ -90,7 +90,7 @@ public class Waypoint {
|
||||
this.longitude = longitude;
|
||||
this.latitude = latitude;
|
||||
this.altitude = altitude;
|
||||
this.path = path;
|
||||
this.parentGeoPath = parentGeoPath;
|
||||
|
||||
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
|
||||
* route.
|
||||
*/
|
||||
public GeoPath getPath() {
|
||||
return path;
|
||||
public GeoPath getParentGeoPath() {
|
||||
return parentGeoPath;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -231,6 +231,10 @@ public class Waypoint {
|
||||
}
|
||||
|
||||
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 value = attributeMap.get(type).getDisplayString();
|
||||
|
||||
|
@ -40,38 +40,48 @@ public final class WaypointBuilder {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(WaypointBuilder.class.getName());
|
||||
|
||||
// SELECT statement for getting a list of waypoints.
|
||||
final static String GEO_ARTIFACT_QUERY
|
||||
private final static String TIME_TYPE_IDS = String.format("%d, %d",
|
||||
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 "
|
||||
+ "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
|
||||
final static String GEO_ARTIFACT_QUERY_ID_ONLY
|
||||
private final static String GEO_ARTIFACT_QUERY_ID_ONLY
|
||||
= "SELECT artifact_id "
|
||||
+ "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
|
||||
final static String GEO_ARTIFACT_WITH_DATA_SOURCES_QUERY
|
||||
private final static String GEO_ARTIFACT_WITH_DATA_SOURCES_QUERY
|
||||
= "SELECT blackboard_attributes.artifact_id "
|
||||
+ "FROM blackboard_attributes, blackboard_artifacts "
|
||||
+ "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
|
||||
|
||||
// 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.
|
||||
+ "FROM blackboard_attributes "
|
||||
+ "WHERE attribute_type_id IN(%d, %d) "
|
||||
+ "WHERE attribute_type_id IN(%s) "
|
||||
+ "AND artifact_id "
|
||||
+ "IN ( "
|
||||
+ "%s" //GEO_ARTIFACT with or without data source
|
||||
+ " )";
|
||||
|
||||
// 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 "
|
||||
+ "FROM blackboard_attributes "
|
||||
+ "WHERE artifact_id NOT IN (%s) "
|
||||
@ -132,7 +142,7 @@ public final class WaypointBuilder {
|
||||
public static List<Route> getRoutes(List<Waypoint> waypoints) {
|
||||
List<Route> routeList = new ArrayList<>();
|
||||
for (Waypoint point : waypoints) {
|
||||
GeoPath path = point.getPath();
|
||||
GeoPath path = point.getParentGeoPath();
|
||||
if (path instanceof Route) {
|
||||
Route route = (Route) path;
|
||||
if (!routeList.contains(route)) {
|
||||
@ -154,7 +164,7 @@ public final class WaypointBuilder {
|
||||
public static List<Track> getTracks(List<Waypoint> waypoints) {
|
||||
List<Track> trackList = new ArrayList<>();
|
||||
for (Waypoint point : waypoints) {
|
||||
GeoPath path = point.getPath();
|
||||
GeoPath path = point.getParentGeoPath();
|
||||
if (path instanceof Track) {
|
||||
Track route = (Track) path;
|
||||
if (!trackList.contains(route)) {
|
||||
@ -507,9 +517,7 @@ public final class WaypointBuilder {
|
||||
// FROM blackboard_attributes
|
||||
// WHERE attribute_type_id IN (%d, %d)
|
||||
return String.format(SELECT_WO_TIMESTAMP,
|
||||
String.format(GEO_ARTIFACT_QUERY_ID_ONLY,
|
||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID(),
|
||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED.getTypeID()),
|
||||
String.format(GEO_ARTIFACT_QUERY_ID_ONLY,TIME_TYPE_IDS),
|
||||
getWaypointListQuery(dataSources));
|
||||
}
|
||||
|
||||
@ -542,15 +550,13 @@ public final class WaypointBuilder {
|
||||
// MOST_RECENT_TIME
|
||||
// SELECT MAX(value_int64) - (%d * 86400)
|
||||
// FROM blackboard_attributes
|
||||
// WHERE attribute_type_id IN(%d, %d)
|
||||
// WHERE attribute_type_id IN(%s)
|
||||
// AND artifact_id
|
||||
// IN ( %s )
|
||||
//
|
||||
mostRecentQuery = String.format("AND value_int64 > (%s)", //NON-NLS
|
||||
String.format(MOST_RECENT_TIME,
|
||||
cntDaysFromRecent,
|
||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID(),
|
||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED.getTypeID(),
|
||||
cntDaysFromRecent, TIME_TYPE_IDS,
|
||||
getWaypointListQuery(dataSources)
|
||||
));
|
||||
}
|
||||
@ -558,10 +564,8 @@ public final class WaypointBuilder {
|
||||
// GEO_ARTIFACT_QUERY
|
||||
// SELECT artifact_id, artifact_type_id
|
||||
// FROM blackboard_attributes
|
||||
// WHERE attribute_type_id IN (%d, %d)
|
||||
String query = String.format(GEO_ARTIFACT_QUERY,
|
||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID(),
|
||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED.getTypeID());
|
||||
// WHERE attribute_type_id IN (%s)
|
||||
String query = String.format(GEO_ARTIFACT_QUERY, TIME_TYPE_IDS);
|
||||
|
||||
// That are in the list of artifacts for the given data Sources
|
||||
query += String.format("AND artifact_id IN(%s)", getWaypointListQuery(dataSources)); //NON-NLS
|
||||
@ -592,10 +596,8 @@ public final class WaypointBuilder {
|
||||
// GEO_ARTIFACT_QUERY
|
||||
// SELECT artifact_id, artifact_type_id
|
||||
// FROM blackboard_attributes
|
||||
// WHERE attribute_type_id IN (%d, %d)
|
||||
return String.format(GEO_ARTIFACT_QUERY,
|
||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE.getTypeID(),
|
||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_START.getTypeID());
|
||||
// WHERE attribute_type_id IN (%s)
|
||||
return String.format(GEO_ARTIFACT_QUERY, GEO_ATTRIBUTE_TYPE_IDS);
|
||||
}
|
||||
|
||||
String dataSourceList = "";
|
||||
@ -608,9 +610,7 @@ public final class WaypointBuilder {
|
||||
dataSourceList = dataSourceList.substring(0, dataSourceList.length() - 1);
|
||||
}
|
||||
|
||||
return String.format(GEO_ARTIFACT_WITH_DATA_SOURCES_QUERY,
|
||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE.getTypeID(),
|
||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_START.getTypeID(),
|
||||
return String.format(GEO_ARTIFACT_WITH_DATA_SOURCES_QUERY, GEO_ATTRIBUTE_TYPE_IDS,
|
||||
dataSourceList);
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
DATExtractor_process_message=Processing DJI DAT file: %s
|
||||
DATFileExtractor_Extractor_Name=DAT File Extractor
|
||||
DroneIngestModule_Description=Description
|
||||
DroneIngestModule_Name=Drone
|
||||
DroneIngestModule_Description=Analyzes files generated by drones.
|
||||
DroneIngestModule_Name=Drone Analyzer
|
||||
# {0} - AbstractFileName
|
||||
DroneIngestModule_process_start=Started {0}
|
||||
|
@ -41,10 +41,11 @@ import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress;
|
||||
import org.sleuthkit.autopsy.ingest.IngestJobContext;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
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.TskCoreException;
|
||||
import org.sleuthkit.datamodel.Blackboard.BlackboardException;
|
||||
import org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoTrackpointsUtil.GeoTrackPointList;
|
||||
|
||||
/**
|
||||
* Extract drone position data from DJI Phantom drones.
|
||||
@ -110,10 +111,10 @@ final class DATExtractor extends DroneExtractor {
|
||||
}
|
||||
|
||||
// Process the csv file
|
||||
List<GeoTrackPoint> trackPoints = processCSVFile(context, DATFile, csvFilePath);
|
||||
GeoTrackPointList trackPoints = processCSVFile(context, DATFile, csvFilePath);
|
||||
|
||||
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 {
|
||||
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
|
||||
*/
|
||||
private List<GeoTrackPoint> processCSVFile(IngestJobContext context, AbstractFile DATFile, String csvFilePath) throws DroneIngestException {
|
||||
List<GeoTrackPoint> trackPoints = new ArrayList<>();
|
||||
private GeoTrackPointList processCSVFile(IngestJobContext context, AbstractFile DATFile, String csvFilePath) throws DroneIngestException {
|
||||
GeoTrackPointList trackPoints = new GeoTrackPointList();
|
||||
try (BufferedReader reader = new BufferedReader(new FileReader(new File(csvFilePath)))) {
|
||||
// First read in the header line and process
|
||||
String line = reader.readLine();
|
||||
@ -202,7 +203,7 @@ final class DATExtractor extends DroneExtractor {
|
||||
String[] values = line.split(","); //NON-NLS
|
||||
GeoTrackPoint point = createTrackPoint(headerMap, values);
|
||||
if (point != null) {
|
||||
trackPoints.add(point);
|
||||
trackPoints.addPoint(point);
|
||||
}
|
||||
}
|
||||
|
||||
@ -258,6 +259,7 @@ final class DATExtractor extends DroneExtractor {
|
||||
return new GeoTrackPoint(latitude,
|
||||
longitude,
|
||||
getDoubleValue(columnLookup.get(HEADER_ALTITUDE), values),
|
||||
null,
|
||||
getDoubleValue(columnLookup.get(HEADER_VELOCITY), values),
|
||||
getDoubleValue(columnLookup.get(HEADER_DISTANCE_FROM_HP), values),
|
||||
getDoubleValue(columnLookup.get(HEADER_DISTANCE_TRAVELED), values),
|
||||
|
@ -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
|
@ -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.
|
@ -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
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -40,9 +40,9 @@ public interface TextSummarizer {
|
||||
* @param file The AbstractFile to summarize.
|
||||
* @param summarySize The size of the summary to create.
|
||||
*
|
||||
* @return The summary as a string.
|
||||
* @return The summary as a TextSummary object.
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
String summarize(AbstractFile file, int summarySize) throws IOException;
|
||||
TextSummary summarize(AbstractFile file, int summarySize) throws IOException;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2018-2019 Basis Technology Corp.
|
||||
* Copyright 2018-2020 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* 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.datamodel.AbstractFile;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.RdbmsCentralRepoFactory;
|
||||
|
||||
/**
|
||||
* Utilities for testing intercase correlation feature.
|
||||
@ -220,7 +221,7 @@ class InterCaseTestUtils {
|
||||
this.kitchenShink = new IngestJobSettings(InterCaseTestUtils.class.getCanonicalName(), IngestType.ALL_MODULES, kitchenSink);
|
||||
|
||||
try {
|
||||
Collection<CorrelationAttributeInstance.Type> types = CorrelationAttributeInstance.getDefaultCorrelationTypes();
|
||||
Collection<CorrelationAttributeInstance.Type> types = CentralRepository.getInstance().getCorrelationTypes();
|
||||
|
||||
//TODO use ids instead of strings
|
||||
FILE_TYPE = types.stream().filter(type -> type.getDisplayName().equals("Files")).findAny().get();
|
||||
@ -297,8 +298,10 @@ class InterCaseTestUtils {
|
||||
crSettings.createDbDirectory();
|
||||
}
|
||||
|
||||
crSettings.initializeDatabaseSchema();
|
||||
crSettings.insertDefaultDatabaseContent();
|
||||
RdbmsCentralRepoFactory centralRepoSchemaFactory = new RdbmsCentralRepoFactory(CentralRepoPlatforms.SQLITE, crSettings);
|
||||
centralRepoSchemaFactory.initializeDatabaseSchema();
|
||||
centralRepoSchemaFactory.insertDefaultDatabaseContent();
|
||||
|
||||
crSettings.saveSettings();
|
||||
CentralRepoPlatforms.setSelectedPlatform(CentralRepoPlatforms.SQLITE.name());
|
||||
CentralRepoPlatforms.saveSelectedPlatform();
|
||||
|
@ -37,8 +37,12 @@ from org.sleuthkit.datamodel import BlackboardArtifact
|
||||
from org.sleuthkit.datamodel import BlackboardAttribute
|
||||
from org.sleuthkit.datamodel import TskCoreException
|
||||
from org.sleuthkit.datamodel.blackboardutils import GeoArtifactsHelper
|
||||
from org.sleuthkit.datamodel.blackboardutils.attributes import GeoWaypoint
|
||||
from org.sleuthkit.datamodel.blackboardutils.attributes import GeoTrackPoints
|
||||
from org.sleuthkit.datamodel.blackboardutils.attributes import TskGeoWaypointsUtil
|
||||
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.ingest import IngestModule
|
||||
from org.sleuthkit.autopsy.ingest.IngestModule import IngestModuleException
|
||||
@ -64,19 +68,14 @@ class GPXParserDataSourceIngestModuleFactory(IngestModuleFactoryAdapter):
|
||||
|
||||
moduleName = "GPX Parser"
|
||||
|
||||
# True - Verbose debugging messages sent to log file.
|
||||
# False - Verbose debugging turned off.
|
||||
debuglevel = False
|
||||
|
||||
def getModuleDisplayName(self):
|
||||
return self.moduleName
|
||||
|
||||
# TODO: Give it a description
|
||||
def getModuleDescription(self):
|
||||
return "Module that extracts GEO data from GPX files."
|
||||
|
||||
def getModuleVersionNumber(self):
|
||||
return "1.1"
|
||||
return "1.2"
|
||||
|
||||
def isDataSourceIngestModuleFactory(self):
|
||||
return True
|
||||
@ -88,10 +87,11 @@ class GPXParserDataSourceIngestModuleFactory(IngestModuleFactoryAdapter):
|
||||
# Data Source-level ingest module. One gets created per data source.
|
||||
class GPXParserDataSourceIngestModule(DataSourceIngestModule):
|
||||
|
||||
_logger = Logger.getLogger(GPXParserDataSourceIngestModuleFactory.moduleName)
|
||||
logger = Logger.getLogger(GPXParserDataSourceIngestModuleFactory.moduleName)
|
||||
writeDebugMsgs = False
|
||||
|
||||
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):
|
||||
self.context = None
|
||||
@ -106,178 +106,129 @@ class GPXParserDataSourceIngestModule(DataSourceIngestModule):
|
||||
# We don't know how much work there is yet.
|
||||
progressBar.switchToIndeterminate()
|
||||
|
||||
# This will work in 4.0.1 and beyond.
|
||||
# Use blackboard class to index blackboard artifacts for keyword search.
|
||||
blackboard = Case.getCurrentCase().getServices().getBlackboard()
|
||||
|
||||
# Get the sleuthkitcase
|
||||
# Get the case database and its blackboard.
|
||||
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()
|
||||
|
||||
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)
|
||||
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)
|
||||
fileCount = 0;
|
||||
|
||||
# Get module name for adding attributes
|
||||
# Get the module name, it will be needed for adding attributes
|
||||
moduleName = GPXParserDataSourceIngestModuleFactory.moduleName
|
||||
|
||||
for file in files:
|
||||
|
||||
# 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.
|
||||
# Check if a folder for this module is present in the case Temp directory.
|
||||
# If not, create it.
|
||||
dirName = os.path.join(Case.getCurrentCase().getTempDirectory(), "GPX_Parser_Module")
|
||||
try:
|
||||
os.stat(dirName)
|
||||
except:
|
||||
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")
|
||||
|
||||
# Check to see if temporary file exists. If it does, remove it.
|
||||
if os.path.exists(fileName):
|
||||
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)
|
||||
fileCount = 0;
|
||||
for file in files:
|
||||
|
||||
# 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)
|
||||
ContentUtils.writeToFile(file, localFile)
|
||||
|
||||
# Send to gpxpy for parsing.
|
||||
# Send the file to gpxpy for parsing.
|
||||
gpxfile = open(fileName)
|
||||
try:
|
||||
gpx = gpxpy.parse(gpxfile)
|
||||
if GPXParserDataSourceIngestModuleFactory.debuglevel: self.log(Level.INFO, "GPX:\t" + "FILE PARSED")
|
||||
except:
|
||||
if GPXParserDataSourceIngestModuleFactory.debuglevel: self.log(Level.SEVERE, "GPX:\t" + file.getName() + " - FILE NOT PARSED")
|
||||
if self.writeDebugMsgs: self.log(Level.INFO, "Parsed " + file.getUniquePath() + " (objID = " + str(file.getId()) + ")")
|
||||
except Exception as e:
|
||||
self.log(Level.WARNING, "Error parsing file " + file.getUniquePath() + " (objID = " + str(file.getId()) + "):" + str(e))
|
||||
continue
|
||||
|
||||
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 segment in track.segments:
|
||||
geoPointList = ArrayList()
|
||||
geoPointList = TskGeoTrackpointsUtil.GeoTrackPointList()
|
||||
for point in segment.points:
|
||||
|
||||
elevation = 0
|
||||
if point.elevation != None:
|
||||
elevation = point.elevation
|
||||
|
||||
dateTime = 0
|
||||
timeStamp = 0
|
||||
try:
|
||||
if (point.time != None):
|
||||
datetime = long(time.mktime(point.time.timetuple()))
|
||||
except:
|
||||
pass
|
||||
timeStamp = long(time.mktime(point.time.timetuple()))
|
||||
except Exception as e:
|
||||
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:
|
||||
# Add the trackpoint using the helper class
|
||||
geoartifact = geoArtifactHelper.addTrack("Trackpoint", geoPointList)
|
||||
geoArtifactHelper.addTrack("Track", geoPointList, None)
|
||||
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:
|
||||
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:
|
||||
attributes = ArrayList()
|
||||
|
||||
try:
|
||||
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_LONGITUDE.getTypeID(), moduleName, waypoint.longitude))
|
||||
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_PROG_NAME.getTypeID(), moduleName, "GPXParser"))
|
||||
|
||||
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" )
|
||||
blackboard.postArtifact(art, moduleName)
|
||||
|
||||
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:
|
||||
firstTimeThru = 0
|
||||
startingPoint = list()
|
||||
endingPoint = list()
|
||||
|
||||
geoWaypointList = TskGeoWaypointsUtil.GeoWaypointList()
|
||||
|
||||
for point in route.points:
|
||||
# If first time in loop only populate starting point
|
||||
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)
|
||||
geoWaypointList.addPoint(point.latitude, point.longitude, elevation, point.name)
|
||||
|
||||
try:
|
||||
# Post the artifact to blackboard
|
||||
skCase.getBlackboard().postArtifact(art, moduleName)
|
||||
geoArtifactHelper.addRoute(None, None, geoWaypointList, None)
|
||||
except Blackboard.BlackboardException as e:
|
||||
if GPXParserDataSourceIngestModuleFactory.debuglevel: self.log(Level.SEVERE, "GPX: Error using geo artifact helper with blackboard for waypoints" )
|
||||
else:
|
||||
if (len(startingPoint) > 0):
|
||||
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" )
|
||||
|
||||
self.log("Error posting GPS route artifact for " + file.getUniquePath() + " (objID = " + str(file.getId()) + "):" + e.getMessage())
|
||||
except TskCoreException as e:
|
||||
self.log(Level.SEVERE, "Error creating GPS route artifact for " + file.getUniquePath() + " (objID = " + str(file.getId()) + "):" + e.getMessage())
|
||||
|
||||
# Update the progress bar.
|
||||
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.
|
||||
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)
|
||||
return IngestModule.ProcessResult.OK;
|
||||
|
@ -41,6 +41,8 @@ from org.sleuthkit.datamodel import BlackboardArtifact
|
||||
from org.sleuthkit.datamodel import BlackboardAttribute
|
||||
from org.sleuthkit.datamodel import Content
|
||||
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 general
|
||||
@ -52,15 +54,25 @@ class GoogleMapLocationAnalyzer(general.AndroidComponentAnalyzer):
|
||||
|
||||
def __init__(self):
|
||||
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):
|
||||
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:
|
||||
absFiles = fileManager.findFiles(dataSource, "da_destination_history")
|
||||
if absFiles.isEmpty():
|
||||
return
|
||||
for abstractFile in absFiles:
|
||||
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)
|
||||
self.__findGeoLocationsInDB(jFile.toString(), abstractFile)
|
||||
except Exception as ex:
|
||||
@ -75,6 +87,8 @@ class GoogleMapLocationAnalyzer(general.AndroidComponentAnalyzer):
|
||||
return
|
||||
|
||||
try:
|
||||
artifactHelper = GeoArtifactsHelper(self.current_case.getSleuthkitCase(),
|
||||
general.MODULE_NAME, self.PROGRAM_NAME, abstractFile)
|
||||
Class.forName("org.sqlite.JDBC") # load JDBC driver
|
||||
connection = DriverManager.getConnection("jdbc:sqlite:" + databasePath)
|
||||
statement = connection.createStatement()
|
||||
@ -101,31 +115,21 @@ class GoogleMapLocationAnalyzer(general.AndroidComponentAnalyzer):
|
||||
source_lat = GoogleMapLocationAnalyzer.convertGeo(resultSet.getString("source_lat"))
|
||||
source_lng = GoogleMapLocationAnalyzer.convertGeo(resultSet.getString("source_lng"))
|
||||
|
||||
attributes = ArrayList()
|
||||
artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_ROUTE)
|
||||
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CATEGORY, general.MODULE_NAME, "Destination"))
|
||||
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"))
|
||||
waypointlist = GeoWaypointList()
|
||||
waypointlist.addPoint(source_lat, source_lng, None, None)
|
||||
waypointlist.addPoint(dest_lat, dest_lng, None, dest_address)
|
||||
|
||||
artifact.addAttributes(attributes)
|
||||
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())
|
||||
artifactHelper.addRoute(dest_title, time, waypointlist, None)
|
||||
|
||||
except SQLException as ex:
|
||||
# Unable to execute Google map locations SQL query against database.
|
||||
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:
|
||||
self._logger.log(Level.SEVERE, "Error processing google maps history.", ex)
|
||||
self._logger.log(Level.SEVERE, traceback.format_exc())
|
||||
|
@ -276,8 +276,8 @@
|
||||
<binary-origin>release/modules/ext/icu4j-3.8.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/guava-17.0.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/guava-17.0.jar</binary-origin>
|
||||
<runtime-relative-path>ext/guava-19.0.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/guava-19.0.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/language-detector-0.6.jar</runtime-relative-path>
|
||||
|
@ -43,7 +43,7 @@ KeywordSearchEditListPanel.keywordsLabel.text=Keywords:
|
||||
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.
|
||||
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.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
|
||||
|
@ -79,7 +79,7 @@ KeywordSearchEditListPanel.keywordsLabel.text=Keywords:
|
||||
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.
|
||||
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.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
|
||||
|
@ -36,6 +36,7 @@ import java.util.logging.Level;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.openide.modules.InstalledFileLocator;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.coreutils.ExecUtil;
|
||||
@ -56,6 +57,7 @@ import org.xml.sax.SAXException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
@ -66,7 +68,6 @@ import java.util.Set;
|
||||
import java.util.HashSet;
|
||||
import static java.util.Locale.US;
|
||||
import static java.util.TimeZone.getTimeZone;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.openide.util.Lookup;
|
||||
import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress;
|
||||
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.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT;
|
||||
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_CREATED;
|
||||
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
|
||||
*
|
||||
* @param regFileName name of the regripper output file
|
||||
*
|
||||
* @param regFile registry file the artifact is associated with
|
||||
*
|
||||
* @param reader buffered reader to parse adobemru records
|
||||
@ -1230,7 +1231,6 @@ class ExtractRegistry extends Extract {
|
||||
if (fileName.charAt(0) == '/') {
|
||||
fileName = fileName.substring(1,fileName.length() - 1);
|
||||
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
|
||||
if (tokens.length > 2) {
|
||||
@ -1251,6 +1251,11 @@ class ExtractRegistry extends Extract {
|
||||
BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes);
|
||||
if(bba != null) {
|
||||
bbartifacts.add(bba);
|
||||
fileName = fileName.replace("\0", "");
|
||||
bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba);
|
||||
if (bba != null) {
|
||||
bbartifacts.add(bba);
|
||||
}
|
||||
}
|
||||
line = reader.readLine();
|
||||
}
|
||||
@ -1265,8 +1270,6 @@ class ExtractRegistry extends Extract {
|
||||
/**
|
||||
* 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 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
|
||||
String tokens[] = line.split("> ");
|
||||
String fileName = tokens[1];
|
||||
fileName = FilenameUtils.normalize(fileName, true);
|
||||
Collection<BlackboardAttribute> attributes = new ArrayList<>();
|
||||
attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName));
|
||||
BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes);
|
||||
if(bba != null) {
|
||||
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();
|
||||
}
|
||||
@ -1307,8 +1317,6 @@ class ExtractRegistry extends Extract {
|
||||
/**
|
||||
* 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 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
|
||||
String tokens[] = line.split("> ");
|
||||
String fileName = tokens[1];
|
||||
fileName = FilenameUtils.normalize(fileName, true);
|
||||
Collection<BlackboardAttribute> attributes = new ArrayList<>();
|
||||
attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName));
|
||||
BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes);
|
||||
if(bba != null) {
|
||||
bbartifacts.add(bba);
|
||||
bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba);
|
||||
if (bba != null) {
|
||||
bbartifacts.add(bba);
|
||||
}
|
||||
}
|
||||
line = reader.readLine();
|
||||
}
|
||||
@ -1349,8 +1360,6 @@ class ExtractRegistry extends Extract {
|
||||
/**
|
||||
* 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 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
|
||||
String tokens[] = line.split("> ");
|
||||
String fileName = tokens[1];
|
||||
fileName = FilenameUtils.normalize(fileName, true);
|
||||
Collection<BlackboardAttribute> attributes = new ArrayList<>();
|
||||
attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName));
|
||||
BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes);
|
||||
if(bba != null) {
|
||||
bbartifacts.add(bba);
|
||||
bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba);
|
||||
if (bba != null) {
|
||||
bbartifacts.add(bba);
|
||||
}
|
||||
}
|
||||
line = reader.readLine();
|
||||
}
|
||||
@ -1393,8 +1405,6 @@ class ExtractRegistry extends Extract {
|
||||
/**
|
||||
* 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 reader buffered reader to parse adobemru records
|
||||
@ -1410,12 +1420,15 @@ class ExtractRegistry extends Extract {
|
||||
// Columns are
|
||||
// <fileName>
|
||||
String fileName = line;
|
||||
fileName = FilenameUtils.normalize(fileName, true);
|
||||
Collection<BlackboardAttribute> attributes = new ArrayList<>();
|
||||
attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName));
|
||||
BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes);
|
||||
if (bba != null) {
|
||||
bbartifacts.add(bba);
|
||||
bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba);
|
||||
if (bba != null) {
|
||||
bbartifacts.add(bba);
|
||||
}
|
||||
}
|
||||
line = reader.readLine();
|
||||
line = line.trim();
|
||||
@ -1429,8 +1442,6 @@ class ExtractRegistry extends Extract {
|
||||
/**
|
||||
* 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 reader buffered reader to parse adobemru records
|
||||
@ -1454,13 +1465,16 @@ class ExtractRegistry extends Extract {
|
||||
Long docDate = Long.valueOf(tokens[0]);
|
||||
String fileNameTokens[] = tokens[4].split(" - ");
|
||||
String fileName = fileNameTokens[1];
|
||||
fileName = FilenameUtils.normalize(fileName, true);
|
||||
Collection<BlackboardAttribute> attributes = new ArrayList<>();
|
||||
attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName));
|
||||
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, getName(), docDate));
|
||||
BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes);
|
||||
if(bba != null) {
|
||||
bbartifacts.add(bba);
|
||||
bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba);
|
||||
if (bba != null) {
|
||||
bbartifacts.add(bba);
|
||||
}
|
||||
}
|
||||
line = reader.readLine();
|
||||
line = line.trim();
|
||||
@ -1473,8 +1487,6 @@ class ExtractRegistry extends Extract {
|
||||
/**
|
||||
* 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 reader buffered reader to parse adobemru records
|
||||
@ -1501,7 +1513,6 @@ class ExtractRegistry extends Extract {
|
||||
String tokens[] = line.split(" : ");
|
||||
fileName = tokens[1];
|
||||
fileName = fileName.replace("%USERPROFILE%", userProfile);
|
||||
fileName = FilenameUtils.normalize(fileName, true);
|
||||
// Time in the format of Wed May 31 14:33:03 2017 Z
|
||||
try {
|
||||
String fileUsedTime = tokens[0].replaceAll(" Z","");
|
||||
@ -1518,6 +1529,10 @@ class ExtractRegistry extends Extract {
|
||||
BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes);
|
||||
if(bba != null) {
|
||||
bbartifacts.add(bba);
|
||||
bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba);
|
||||
if (bba != null) {
|
||||
bbartifacts.add(bba);
|
||||
}
|
||||
}
|
||||
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.
|
||||
*
|
||||
|
@ -23,13 +23,13 @@
|
||||
package org.sleuthkit.autopsy.recentactivity;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import java.util.Collection;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.coreutils.JLNK;
|
||||
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.Content;
|
||||
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
|
||||
@ -108,7 +110,7 @@ class RecentDocumentsByLnk extends Extract {
|
||||
}
|
||||
|
||||
Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
|
||||
String path = FilenameUtils.normalize(lnk.getBestPath(), true);
|
||||
String path = lnk.getBestPath();
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH,
|
||||
NbBundle.getMessage(this.getClass(),
|
||||
"RecentDocumentsByLnk.parentModuleName.noSpace"),
|
||||
@ -124,12 +126,53 @@ class RecentDocumentsByLnk extends Extract {
|
||||
BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, recentFile, bbattributes);
|
||||
if(bba != null) {
|
||||
bbartifacts.add(bba);
|
||||
bba = createAssociatedArtifact(path, bba);
|
||||
if (bba != null) {
|
||||
bbartifacts.add(bba);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
public void process(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar) {
|
||||
this.dataSource = dataSource;
|
||||
|
@ -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.
|
||||
|
||||
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
|
||||
|
||||
The keyword search configuration dialog has three tabs, each with its own purpose:
|
||||
|
Loading…
x
Reference in New Issue
Block a user