Merge remote-tracking branch 'upstream/develop' into 6050-Update-Android-OruxMaps-to-use-TSK_GPS_TRACK

This commit is contained in:
Mark McKinnon 2020-03-23 10:33:47 -04:00
commit ab7335758f
69 changed files with 3925 additions and 1871 deletions

View File

@ -45,7 +45,6 @@ file.reference.jericho-html-3.3.jar=release/modules/ext/jericho-html-3.3.jar
file.reference.jgraphx-v3.8.0.jar=release/modules/ext/jgraphx-v3.8.0.jar file.reference.jgraphx-v3.8.0.jar=release/modules/ext/jgraphx-v3.8.0.jar
file.reference.jhighlight-1.0.3.jar=release\\modules\\ext\\jhighlight-1.0.3.jar file.reference.jhighlight-1.0.3.jar=release\\modules\\ext\\jhighlight-1.0.3.jar
file.reference.jmatio-1.5.jar=release\\modules\\ext\\jmatio-1.5.jar file.reference.jmatio-1.5.jar=release\\modules\\ext\\jmatio-1.5.jar
file.reference.jna-5.1.0.jar=release\\modules\\ext\\jna-5.1.0.jar
file.reference.json-simple-1.1.1.jar=release\\modules\\ext\\json-simple-1.1.1.jar file.reference.json-simple-1.1.1.jar=release\\modules\\ext\\json-simple-1.1.1.jar
file.reference.jsoup-1.11.3.jar=release\\modules\\ext\\jsoup-1.11.3.jar file.reference.jsoup-1.11.3.jar=release\\modules\\ext\\jsoup-1.11.3.jar
file.reference.jul-to-slf4j-1.7.25.jar=release\\modules\\ext\\jul-to-slf4j-1.7.25.jar file.reference.jul-to-slf4j-1.7.25.jar=release\\modules\\ext\\jul-to-slf4j-1.7.25.jar
@ -97,7 +96,6 @@ file.reference.xz-1.8.jar=release\\modules\\ext\\xz-1.8.jar
file.reference.zookeeper-3.4.6.jar=release/modules/ext/zookeeper-3.4.6.jar file.reference.zookeeper-3.4.6.jar=release/modules/ext/zookeeper-3.4.6.jar
file.reference.SparseBitSet-1.1.jar=release/modules/ext/SparseBitSet-1.1.jar file.reference.SparseBitSet-1.1.jar=release/modules/ext/SparseBitSet-1.1.jar
file.reference.commons-validator-1.6.jar=release/modules/ext/commons-validator-1.6.jar file.reference.commons-validator-1.6.jar=release/modules/ext/commons-validator-1.6.jar
file.reference.jna-3.4.0.jar=release/modules/ext/jna-3.4.0.jar
file.reference.api-common-1.7.0.jar=release/modules/ext/api-common-1.7.0.jar file.reference.api-common-1.7.0.jar=release/modules/ext/api-common-1.7.0.jar
file.reference.gax-1.44.0.jar=release/modules/ext/gax-1.44.0.jar file.reference.gax-1.44.0.jar=release/modules/ext/gax-1.44.0.jar
file.reference.gax-grpc-1.44.0.jar=release/modules/ext/gax-grpc-1.44.0.jar file.reference.gax-grpc-1.44.0.jar=release/modules/ext/gax-grpc-1.44.0.jar

View File

@ -615,10 +615,6 @@
<runtime-relative-path>ext/commons-validator-1.6.jar</runtime-relative-path> <runtime-relative-path>ext/commons-validator-1.6.jar</runtime-relative-path>
<binary-origin>release/modules/ext/commons-validator-1.6.jar</binary-origin> <binary-origin>release/modules/ext/commons-validator-1.6.jar</binary-origin>
</class-path-extension> </class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/jna-5.1.0.jar</runtime-relative-path>
<binary-origin>release\modules\ext\jna-5.1.0.jar</binary-origin>
</class-path-extension>
<class-path-extension> <class-path-extension>
<runtime-relative-path>ext/jbig2-imageio-3.0.2.jar</runtime-relative-path> <runtime-relative-path>ext/jbig2-imageio-3.0.2.jar</runtime-relative-path>
<binary-origin>release\modules\ext\jbig2-imageio-3.0.2.jar</binary-origin> <binary-origin>release\modules\ext\jbig2-imageio-3.0.2.jar</binary-origin>

View File

@ -0,0 +1,76 @@
/*
* 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 org.openide.util.NbBundle.Messages;
/**
* This represents a database choices available for central repo.
*/
@Messages({
"CentralRepoDbChoice.Disabled.Text=Disabled",
"CentralRepoDbChoice.Sqlite.Text=SQLite",
"CentralRepoDbChoice.PostgreSQL_Multiuser.Text=PostgreSQL using multi-user settings",
"CentralRepoDbChoice.PostgreSQL.Text=Custom PostgreSQL",
})
public enum CentralRepoDbChoice {
DISABLED("Disabled", Bundle.CentralRepoDbChoice_Disabled_Text(), CentralRepoPlatforms.DISABLED),
SQLITE("Sqlite", Bundle.CentralRepoDbChoice_Sqlite_Text(), CentralRepoPlatforms.SQLITE),
POSTGRESQL_MULTIUSER("PostgreSQL_Multiuser", Bundle.CentralRepoDbChoice_PostgreSQL_Multiuser_Text(), CentralRepoPlatforms.POSTGRESQL),
POSTGRESQL_CUSTOM("PostgreSQL", Bundle.CentralRepoDbChoice_PostgreSQL_Text(), CentralRepoPlatforms.POSTGRESQL);
public static final CentralRepoDbChoice[] DB_CHOICES = new CentralRepoDbChoice[]{
SQLITE, POSTGRESQL_MULTIUSER, POSTGRESQL_CUSTOM
};
private final String settingKey;
private final String title;
private final CentralRepoPlatforms platform;
CentralRepoDbChoice(String key, String title, CentralRepoPlatforms platform) {
this.settingKey = key;
this.title = title;
this.platform = platform;
}
/**
* This is the value of this setting when saved to central repo properties.
* @return The value associated with this choice.
*/
public String getSettingKey() {
return settingKey;
}
/**
* This is the human-readable title for this choice.
* @return The human-readable title for this choice.
*/
public String getTitle() {
return title;
}
/**
* This represents the database type (i.e. Postgres, SQLite) associated with this choice.
* @return The database type associated with this choice.
*/
public CentralRepoPlatforms getDbPlatform() {
return platform;
}
}

View File

@ -0,0 +1,76 @@
/*
* 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;
/**
* This class is a common interface for settings pertaining to the database in central repository.
*/
public interface CentralRepoDbConnectivityManager {
/**
* This method loads the current settings for this connection.
*/
void loadSettings();
/**
* This method saves the altered settings to disk.
*/
void saveSettings();
/**
* This method will create a central repository database if necessary.
* @return Whether or not the operation was successful.
*/
boolean createDatabase();
/**
* This method deletes a central repository database (used for deleting a corrupted database).
* @return Whether or not the operation was successful.
*/
boolean deleteDatabase();
/**
* This method uses the current settings and the validation query to test the connection
* to the database.
*
* @return True if successfull connection, else false.
*/
boolean verifyConnection();
/**
* This method checks to see if the database exists.
*
* @return True if exists, else false.
*/
boolean verifyDatabaseExists();
/**
* This method is uses the current settings and the schema version query to test the
* database schema.
*
* @return True if successful connection, else false.
*/
boolean verifyDatabaseSchema();
/**
* This method tests the connectivity status of this connection and returns the testing result.
* @return The result of testing the database connectivity status.
*/
DatabaseTestResult testStatus();
}

View File

@ -18,26 +18,172 @@
*/ */
package org.sleuthkit.autopsy.centralrepository.datamodel; package org.sleuthkit.autopsy.centralrepository.datamodel;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.File; import java.io.File;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.logging.Level; import java.util.logging.Level;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coordinationservice.CoordinationService; import org.sleuthkit.autopsy.coordinationservice.CoordinationService;
import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
/** /**
* Contains business logic for saving and validating settings for central repo * This class contains business logic for saving and validating settings for central repository.
*/ */
public class CentralRepoDbManager { public class CentralRepoDbManager {
private static final Logger logger = Logger.getLogger(CentralRepoDbManager.class.getName()); private static final Logger logger = Logger.getLogger(CentralRepoDbManager.class.getName());
private static final String CENTRAL_REPO_DB_NAME = "central_repository"; private static final String CENTRAL_REPO_DB_NAME = "central_repository";
private static final String CENTRAL_REPOSITORY_SETTINGS_KEY = "CentralRepository";
private static final String DB_SELECTED_PLATFORM_KEY = "db.selectedPlatform";
private static final String DISABLED_DUE_TO_FAILURE_KEY = "disabledDueToFailure";
private static volatile CentralRepoDbChoice savedChoice = null;
private static final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(CentralRepoDbManager.class);
private static final Object dbChoiceLock = new Object();
private static final Object disabledDueToFailureLock = new Object();
/** /**
* obtains the database connectivity for central repository * This saves the currently selected database choice and clears any disabledDueToFailure flag.
* @param choice The choice to save.
* @return The newly saved choice.
*/
public static CentralRepoDbChoice saveDbChoice(CentralRepoDbChoice choice) {
return saveDbChoice(choice, true);
}
/**
* This saves the currently selected database choice.
* @param choice The choice to save.
* @param clearDisabledDueToError Whether or not to clear the 'disabledDueToFailure' settings key.
* @return The newly saved choice.
*/
public static CentralRepoDbChoice saveDbChoice(CentralRepoDbChoice choice, boolean clearDisabledDueToError) {
synchronized(dbChoiceLock) {
// clear disabling due to a failure
if (clearDisabledDueToError)
setDisabledDueToFailure(false);
// change the settings
CentralRepoDbChoice newChoice = (choice == null) ? CentralRepoDbChoice.DISABLED : choice;
CentralRepoDbChoice oldChoice = savedChoice;
savedChoice = newChoice;
ModuleSettings.setConfigSetting(CENTRAL_REPOSITORY_SETTINGS_KEY, DB_SELECTED_PLATFORM_KEY, newChoice.getSettingKey());
propertyChangeSupport.firePropertyChange("savedChoice", oldChoice, newChoice);
return newChoice;
}
}
/**
* This method indicates whether or not 'PostgreSQL using multi-user settings' is a valid option.
* @return True if 'PostgreSQL using multi-user settings' is valid.
*/
public static boolean isPostgresMultiuserAllowed() {
// if multi user mode is not enabled, then this cannot be used
if (!UserPreferences.getIsMultiUserModeEnabled())
return false;
// also validate the connection as well
PostgresCentralRepoSettings multiUserSettings =
new PostgresCentralRepoSettings(PostgresSettingsLoader.MULTIUSER_SETTINGS_LOADER);
return multiUserSettings.testStatus() == DatabaseTestResult.TESTED_OK;
}
/**
* This method loads the selectedPlatform boolean from the config file if it is set.
*/
public static CentralRepoDbChoice getSavedDbChoice() {
synchronized(dbChoiceLock) {
if (savedChoice == null) {
String selectedPlatformString = ModuleSettings.getConfigSetting(CENTRAL_REPOSITORY_SETTINGS_KEY, DB_SELECTED_PLATFORM_KEY); // NON-NLS
savedChoice = fromKey(selectedPlatformString);
}
return savedChoice;
}
}
/**
* This method disables the central repository and indicates through a flag that this was due to a failure during database setup.
* This is used when re-enabling multi-user as a flag to determine whether or not CR should be re-enabled.
*/
public static void disableDueToFailure() {
CentralRepoDbUtil.setUseCentralRepo(false);
setDisabledDueToFailure(true);
}
/**
* This method sets whether or not the repository has been disabled due to a database setup issue;
* This is used when re-enabling multi-user as a flag to determine whether or not CR should be re-enabled.
* *
* @return the CentralRepository object to connect to * @param disabledDueToFailure Whether or not the repository has been disabled due to a database setup issue.
*/
private static void setDisabledDueToFailure(boolean disabledDueToFailure) {
synchronized(disabledDueToFailureLock) {
boolean oldValue = isDisabledDueToFailure();
ModuleSettings.setConfigSetting(CENTRAL_REPOSITORY_SETTINGS_KEY, DISABLED_DUE_TO_FAILURE_KEY, Boolean.toString(disabledDueToFailure));
propertyChangeSupport.firePropertyChange("disabledDueToFailure", oldValue, disabledDueToFailure);
}
}
/**
* This method retrieves setting whether or not the repository has been disabled due to a database setup issue;
* this is used when re-enabling multi-user as a flag to determine whether or not CR should be re-enabled.
*
* @return Whether or not the repository has been disabled due to a database setup issue.
*/
public static boolean isDisabledDueToFailure() {
synchronized(disabledDueToFailureLock) {
return Boolean.toString(true).equals(ModuleSettings.getConfigSetting(CENTRAL_REPOSITORY_SETTINGS_KEY, DISABLED_DUE_TO_FAILURE_KEY));
}
}
/**
* This method adds a property change listener.
* NOTE: currently only listening for changes in currently saved db choice and disabling due to failure.
*
* @param listener The listener for the event.
*/
public static void addPropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.addPropertyChangeListener(listener);
}
/**
* This method removes a propert change listener.
* @param listener The listener to remove.
*/
public static void removePropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.removePropertyChangeListener(listener);
}
private static CentralRepoDbChoice fromKey(String keyName) {
for (CentralRepoDbChoice dbChoice : CentralRepoDbChoice.values()) {
if (dbChoice.getSettingKey().equalsIgnoreCase(keyName)) {
return dbChoice;
}
}
return CentralRepoDbChoice.DISABLED;
}
/**
* This method obtains the database connectivity for central repository.
*
* @return The CentralRepository object that will be used for connection.
* @throws CentralRepoException * @throws CentralRepoException
*/ */
private static CentralRepository obtainCentralRepository() throws CentralRepoException { private static CentralRepository obtainCentralRepository() throws CentralRepoException {
@ -55,10 +201,10 @@ public class CentralRepoDbManager {
} }
/** /**
* obtains central repository lock * This method obtains a central repository lock.
* *
* @param db the database connection * @param db The database connection.
* @return the lock if acquired * @return The lock if acquired.
* @throws CentralRepoException * @throws CentralRepoException
*/ */
private static CoordinationService.Lock obtainCentralRepoLock(CentralRepository db) throws CentralRepoException { private static CoordinationService.Lock obtainCentralRepoLock(CentralRepository db) throws CentralRepoException {
@ -79,10 +225,10 @@ public class CentralRepoDbManager {
} }
/** /**
* updates central repository schema if necessary * This method updates the central repository schema if necessary.
* *
* @param db the database connectivity * @param db The database connectivity object.
* @param lock the acquired lock * @param lock The acquired lock.
* @throws CentralRepoException * @throws CentralRepoException
*/ */
private static void updatedDbSchema(CentralRepository db, CoordinationService.Lock lock) throws CentralRepoException { private static void updatedDbSchema(CentralRepository db, CoordinationService.Lock lock) throws CentralRepoException {
@ -111,7 +257,7 @@ public class CentralRepoDbManager {
} }
/** /**
* Upgrade the current Central Reposity schema to the newest version. If the * This method upgrades the current Central Reposity schema to the newest version. If the
* upgrade fails, the Central Repository will be disabled and the current * upgrade fails, the Central Repository will be disabled and the current
* settings will be cleared. * settings will be cleared.
*/ */
@ -142,8 +288,7 @@ public class CentralRepoDbManager {
} catch (CentralRepoException ex2) { } catch (CentralRepoException ex2) {
logger.log(Level.SEVERE, "Error shutting down central repo connection pool", ex2); logger.log(Level.SEVERE, "Error shutting down central repo connection pool", ex2);
} }
CentralRepoPlatforms.setSelectedPlatform(CentralRepoPlatforms.DISABLED.name()); saveDbChoice(CentralRepoDbChoice.DISABLED, false);
CentralRepoPlatforms.saveSelectedPlatform();
if (innerException == null) { if (innerException == null) {
throw new CentralRepoException(message, desc); throw new CentralRepoException(message, desc);
} else { } else {
@ -151,41 +296,56 @@ public class CentralRepoDbManager {
} }
} }
private DatabaseTestResult testingStatus; private DatabaseTestResult testingStatus;
private CentralRepoPlatforms selectedPlatform; private CentralRepoDbChoice selectedDbChoice;
private final PostgresCentralRepoSettings dbSettingsPostgres; private final PostgresCentralRepoSettings dbSettingsPostgres;
private final PostgresCentralRepoSettings dbSettingsMultiUser;
private final SqliteCentralRepoSettings dbSettingsSqlite; private final SqliteCentralRepoSettings dbSettingsSqlite;
private boolean configurationChanged = false; private boolean configurationChanged = false;
public CentralRepoDbManager() { public CentralRepoDbManager() {
dbSettingsPostgres = new PostgresCentralRepoSettings(); selectedDbChoice = getSavedDbChoice();
dbSettingsPostgres = new PostgresCentralRepoSettings(PostgresSettingsLoader.CUSTOM_SETTINGS_LOADER);
dbSettingsMultiUser = new PostgresCentralRepoSettings(PostgresSettingsLoader.MULTIUSER_SETTINGS_LOADER);
dbSettingsSqlite = new SqliteCentralRepoSettings(); 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;
}
} }
/**
* This method retrieves the current multi-user database settings.
* @return The current multi-user database settings.
*/
public PostgresCentralRepoSettings getDbSettingsMultiUser() {
return dbSettingsMultiUser;
}
/**
* This method retrieves the current custom postgres database settings.
* @return The current custom postgres database settings.
*/
public PostgresCentralRepoSettings getDbSettingsPostgres() { public PostgresCentralRepoSettings getDbSettingsPostgres() {
return dbSettingsPostgres; return dbSettingsPostgres;
} }
/**
* This method returns the current SQLite database settings for central repository.
* @return The current SQLite database settings
*/
public SqliteCentralRepoSettings getDbSettingsSqlite() { public SqliteCentralRepoSettings getDbSettingsSqlite() {
return dbSettingsSqlite; return dbSettingsSqlite;
} }
/** /**
* setup sqlite db with default settings * This method sets up the sqlite database with default settings.
* @throws CentralRepoException if unable to successfully set up database * @throws CentralRepoException if unable to successfully set up database.
*/ */
public void setupDefaultSqliteDb() throws CentralRepoException { public void setupDefaultSqliteDb() throws CentralRepoException {
// change in-memory settings to default sqlite // change in-memory settings to default sqlite
selectedPlatform = CentralRepoPlatforms.SQLITE; selectedDbChoice = CentralRepoDbChoice.SQLITE;
dbSettingsSqlite.setupDefaultSettings(); dbSettingsSqlite.setupDefaultSettings();
// if db is not present, attempt to create it // if db is not present, attempt to create it
@ -196,7 +356,7 @@ public class CentralRepoDbManager {
} }
// the only successful setup status is tested ok // the only successful setup status is tested ok
if (curStatus != DatabaseTestResult.TESTEDOK) { if (curStatus != DatabaseTestResult.TESTED_OK) {
throw new CentralRepoException("Unable to successfully create sqlite database"); throw new CentralRepoException("Unable to successfully create sqlite database");
} }
@ -206,48 +366,55 @@ public class CentralRepoDbManager {
} }
/** /**
* Returns if changes to the central repository configuration were * This method returns if changes to the central repository configuration were
* successfully applied * successfully applied.
* *
* @return true if the database configuration was successfully changed false * @return Returns true if the database configuration was successfully changed false
* if it was not * if it was not.
*/ */
public boolean wasConfigurationChanged() { public boolean wasConfigurationChanged() {
return configurationChanged; return configurationChanged;
} }
private CentralRepoDbSettings getSelectedSettings() throws CentralRepoException { private CentralRepoDbConnectivityManager getSelectedSettings() throws CentralRepoException {
switch (selectedPlatform) { if (selectedDbChoice == CentralRepoDbChoice.POSTGRESQL_MULTIUSER)
case POSTGRESQL: return dbSettingsMultiUser;
if (selectedDbChoice == CentralRepoDbChoice.POSTGRESQL_CUSTOM)
return dbSettingsPostgres; return dbSettingsPostgres;
case SQLITE: if (selectedDbChoice == CentralRepoDbChoice.SQLITE)
return dbSettingsSqlite; return dbSettingsSqlite;
case DISABLED: if (selectedDbChoice == CentralRepoDbChoice.DISABLED)
return null; return null;
default:
throw new CentralRepoException("Unknown database type: " + selectedPlatform); throw new CentralRepoException("Unknown database type: " + selectedDbChoice);
}
} }
private RdbmsCentralRepoFactory getDbFactory() throws CentralRepoException { private RdbmsCentralRepoFactory getDbFactory() throws CentralRepoException {
switch (selectedPlatform) { if (selectedDbChoice == CentralRepoDbChoice.POSTGRESQL_MULTIUSER)
case POSTGRESQL: return new RdbmsCentralRepoFactory(CentralRepoPlatforms.POSTGRESQL, dbSettingsMultiUser);
return new RdbmsCentralRepoFactory(selectedPlatform, dbSettingsPostgres); if (selectedDbChoice == CentralRepoDbChoice.POSTGRESQL_CUSTOM)
case SQLITE: return new RdbmsCentralRepoFactory(CentralRepoPlatforms.POSTGRESQL, dbSettingsPostgres);
return new RdbmsCentralRepoFactory(selectedPlatform, dbSettingsSqlite); if (selectedDbChoice == CentralRepoDbChoice.SQLITE)
case DISABLED: return new RdbmsCentralRepoFactory(CentralRepoPlatforms.SQLITE, dbSettingsSqlite);
if (selectedDbChoice == CentralRepoDbChoice.DISABLED)
return null; return null;
default:
throw new CentralRepoException("Unknown database type: " + selectedPlatform); throw new CentralRepoException("Unknown database type: " + selectedDbChoice);
}
} }
/**
* This method creates a central repo database if it does not already exist.
* @return True if successful; false if unsuccessful.
* @throws CentralRepoException
*/
public boolean createDb() throws CentralRepoException { public boolean createDb() throws CentralRepoException {
CentralRepoDbConnectivityManager selectedDbSettings = getSelectedSettings();
if (selectedDbSettings == null)
throw new CentralRepoException("Unable to derive connectivity manager from settings: " + selectedDbChoice);
boolean result = false; boolean result = false;
boolean dbCreated = true; boolean dbCreated = true;
CentralRepoDbSettings selectedDbSettings = getSelectedSettings();
if (!selectedDbSettings.verifyDatabaseExists()) { if (!selectedDbSettings.verifyDatabaseExists()) {
dbCreated = selectedDbSettings.createDatabase(); dbCreated = selectedDbSettings.createDatabase();
} }
@ -265,7 +432,6 @@ public class CentralRepoDbManager {
if (!result) { if (!result) {
// Remove the incomplete database // Remove the incomplete database
if (dbCreated) { if (dbCreated) {
// RAMAN TBD: migrate deleteDatabase() to RdbmsCentralRepoFactory
selectedDbSettings.deleteDatabase(); selectedDbSettings.deleteDatabase();
} }
@ -274,12 +440,12 @@ public class CentralRepoDbManager {
throw new CentralRepoException(schemaError); throw new CentralRepoException(schemaError);
} }
testingStatus = DatabaseTestResult.TESTEDOK; testingStatus = DatabaseTestResult.TESTED_OK;
return true; return true;
} }
/** /**
* saves a new central repository based on current settings * This method saves a new central repository based on current settings.
*/ */
@NbBundle.Messages({"CentralRepoDbManager.connectionErrorMsg.text=Failed to connect to central repository database."}) @NbBundle.Messages({"CentralRepoDbManager.connectionErrorMsg.text=Failed to connect to central repository database."})
public void saveNewCentralRepo() throws CentralRepoException { public void saveNewCentralRepo() throws CentralRepoException {
@ -305,18 +471,18 @@ public class CentralRepoDbManager {
// Even if we fail to close the existing connections, make sure that we // Even if we fail to close the existing connections, make sure that we
// save the new connection settings, so an Autopsy restart will correctly // save the new connection settings, so an Autopsy restart will correctly
// start with the new settings. // start with the new settings.
CentralRepoPlatforms.setSelectedPlatform(selectedPlatform.name()); CentralRepoDbUtil.setUseCentralRepo(selectedDbChoice != CentralRepoDbChoice.DISABLED);
CentralRepoPlatforms.saveSelectedPlatform(); saveDbChoice(selectedDbChoice);
CentralRepoDbSettings selectedDbSettings = getSelectedSettings(); CentralRepoDbConnectivityManager selectedDbSettings = getSelectedSettings();
// save the new settings // save the new settings
selectedDbSettings.saveSettings(); selectedDbSettings.saveSettings();
// Load those newly saved settings into the postgres db manager instance // Load those newly saved settings into the postgres db manager instance
// in case we are still using the same instance. // in case we are still using the same instance.
if (selectedPlatform == CentralRepoPlatforms.POSTGRESQL || selectedPlatform == CentralRepoPlatforms.SQLITE) { if (selectedDbChoice != null && selectedDbChoice != CentralRepoDbChoice.DISABLED) {
try { try {
logger.info("Creating central repo db with settings: " + selectedDbSettings); logger.info("Saving central repo settings for db: " + selectedDbSettings);
CentralRepository.getInstance().updateSettings(); CentralRepository.getInstance().updateSettings();
configurationChanged = true; configurationChanged = true;
} catch (CentralRepoException ex) { } catch (CentralRepoException ex) {
@ -326,80 +492,80 @@ public class CentralRepoDbManager {
} }
} }
/**
* This method retrieves the current status.
* Note: this could be a dirty value if testing of the connection has not been performed.
* @return The current status of the database connection.
*/
public DatabaseTestResult getStatus() { public DatabaseTestResult getStatus() {
return testingStatus; return testingStatus;
} }
public CentralRepoPlatforms getSelectedPlatform() { /**
return selectedPlatform; * This method retrieves the currently selected database choice.
* NOTE: This choice may not align with the saved setting.
* @return The currently selected database choice.
*/
public CentralRepoDbChoice getSelectedDbChoice() {
return selectedDbChoice;
} }
/**
* This method clears the current database testing status.
*/
public void clearStatus() { public void clearStatus() {
testingStatus = DatabaseTestResult.UNTESTED; testingStatus = DatabaseTestResult.UNTESTED;
} }
public void setSelectedPlatform(CentralRepoPlatforms newSelected) { /**
selectedPlatform = newSelected; * This method sets the currently selected database choice and sets the testing status to untested.
* @param newSelected The new database choice.
*/
public void setSelctedDbChoice(CentralRepoDbChoice newSelected) {
selectedDbChoice = newSelected;
testingStatus = DatabaseTestResult.UNTESTED; testingStatus = DatabaseTestResult.UNTESTED;
} }
/** /**
* Tests whether or not the database settings are valid. * This method tests whether or not the settings have been filled in for the UI.
* NOTE: This does not check the connectivity status of these settings.
* *
* @return True or false. * @return True if database settings are valid.
*/ */
public boolean testDatabaseSettingsAreValid( public boolean testDatabaseSettingsAreValid(
String tbDbHostname, String tbDbPort, String tbDbUsername, String tfDatabasePath, String jpDbPassword) throws CentralRepoException, NumberFormatException { String tbDbHostname, String tbDbPort, String tbDbUsername, String tfDatabasePath, String jpDbPassword) throws CentralRepoException, NumberFormatException {
switch (selectedPlatform) { if (selectedDbChoice == CentralRepoDbChoice.POSTGRESQL_CUSTOM) {
case POSTGRESQL:
dbSettingsPostgres.setHost(tbDbHostname); dbSettingsPostgres.setHost(tbDbHostname);
dbSettingsPostgres.setPort(Integer.parseInt(tbDbPort)); dbSettingsPostgres.setPort(Integer.parseInt(tbDbPort));
dbSettingsPostgres.setDbName(CENTRAL_REPO_DB_NAME); dbSettingsPostgres.setDbName(CENTRAL_REPO_DB_NAME);
dbSettingsPostgres.setUserName(tbDbUsername); dbSettingsPostgres.setUserName(tbDbUsername);
dbSettingsPostgres.setPassword(jpDbPassword); dbSettingsPostgres.setPassword(jpDbPassword);
break; }
case SQLITE: else if (selectedDbChoice == CentralRepoDbChoice.SQLITE) {
File databasePath = new File(tfDatabasePath); File databasePath = new File(tfDatabasePath);
dbSettingsSqlite.setDbName(SqliteCentralRepoSettings.DEFAULT_DBNAME); dbSettingsSqlite.setDbName(SqliteCentralRepoSettings.DEFAULT_DBNAME);
dbSettingsSqlite.setDbDirectory(databasePath.getPath()); dbSettingsSqlite.setDbDirectory(databasePath.getPath());
break; }
default: else if (selectedDbChoice != CentralRepoDbChoice.POSTGRESQL_MULTIUSER) {
throw new IllegalStateException("Central Repo has an unknown selected platform: " + selectedPlatform); throw new IllegalStateException("Central Repo has an unknown selected platform: " + selectedDbChoice);
} }
return true; return true;
} }
/**
* This method tests the current database settings to see if a valid connection can be made.
* @return The result of testing the connection.
*/
public DatabaseTestResult testStatus() { public DatabaseTestResult testStatus() {
if (selectedPlatform == CentralRepoPlatforms.POSTGRESQL) { try {
if (dbSettingsPostgres.verifyConnection()) { CentralRepoDbConnectivityManager manager = getSelectedSettings();
if (dbSettingsPostgres.verifyDatabaseExists()) { if (manager != null)
if (dbSettingsPostgres.verifyDatabaseSchema()) { testingStatus = manager.testStatus();
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;
} }
catch (CentralRepoException e) {
logger.log(Level.WARNING, "unable to test status of db connection in central repo", e);
} }
return testingStatus; return testingStatus;

View File

@ -1,55 +0,0 @@
/*
* Central Repository
*
* Copyright 2015-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.centralrepository.datamodel;
/**
* common interface for settings pertaining to the database in central repository
*/
public interface CentralRepoDbSettings {
void saveSettings();
boolean createDatabase();
boolean deleteDatabase();
/**
* Use the current settings and the validation query to test the connection
* to the database.
*
* @return true if successfull connection, else false.
*/
boolean verifyConnection();
/**
* Check to see if the database exists.
*
* @return true if exists, else false
*/
boolean verifyDatabaseExists();
/**
* Use the current settings and the schema version query to test the
* database schema.
*
* @return true if successful connection, else false.
*/
boolean verifyDatabaseSchema();
}

View File

@ -0,0 +1,41 @@
/*
* 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 java.sql.Connection;
import java.sql.SQLException;
import org.sleuthkit.datamodel.CaseDbSchemaVersionNumber;
/**
* Common interface to upgrade central repository database schema.
*/
public interface CentralRepoDbUpgrader {
/**
* Updates the Central Repository schema using the given open connection.
*
* @param dbSchemaVersion Current schema version.
* @param connection Connection to use for upgrade.
*
* @throws CentralRepoException If there is an error in upgrade.
* @throws SQLException If there is any SQL errors.
*/
void upgradeSchema(CaseDbSchemaVersionNumber dbSchemaVersion, Connection connection) throws CentralRepoException, SQLException;
}

View File

@ -0,0 +1,114 @@
/*
* 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 java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import org.sleuthkit.datamodel.CaseDbSchemaVersionNumber;
/**
* This class updates CR schema to 1.4
*
* New correlation types for accounts are added, as well as some accounts related new tables are added in this version.
*
*/
public class CentralRepoDbUpgrader13To14 implements CentralRepoDbUpgrader {
@Override
public void upgradeSchema(CaseDbSchemaVersionNumber dbSchemaVersion, Connection connection) throws CentralRepoException, SQLException {
if (dbSchemaVersion.compareTo(new CaseDbSchemaVersionNumber(1, 4)) < 0) {
try (Statement statement = connection.createStatement();) {
CentralRepoPlatforms selectedPlatform = CentralRepoDbManager.getSavedDbChoice().getDbPlatform();
// Create account_types and accounts tables which are referred by X_instances tables
statement.execute(RdbmsCentralRepoFactory.getCreateAccountTypesTableStatement(selectedPlatform));
statement.execute(RdbmsCentralRepoFactory.getCreateAccountsTableStatement(selectedPlatform));
for (CorrelationAttributeInstance.Type type : CorrelationAttributeInstance.getDefaultCorrelationTypes()) {
String instance_type_dbname = CentralRepoDbUtil.correlationTypeToInstanceTableName(type);
if (type.getId() >= CorrelationAttributeInstance.ADDITIONAL_TYPES_BASE_ID) {
// these are new Correlation types - new tables need to be created
statement.execute(String.format(RdbmsCentralRepoFactory.getCreateAccountInstancesTableTemplate(selectedPlatform), instance_type_dbname, instance_type_dbname));
statement.execute(String.format(RdbmsCentralRepoFactory.getAddCaseIdIndexTemplate(), instance_type_dbname, instance_type_dbname));
statement.execute(String.format(RdbmsCentralRepoFactory.getAddDataSourceIdIndexTemplate(), instance_type_dbname, instance_type_dbname));
statement.execute(String.format(RdbmsCentralRepoFactory.getAddValueIndexTemplate(), instance_type_dbname, instance_type_dbname));
statement.execute(String.format(RdbmsCentralRepoFactory.getAddKnownStatusIndexTemplate(), instance_type_dbname, instance_type_dbname));
statement.execute(String.format(RdbmsCentralRepoFactory.getAddObjectIdIndexTemplate(), instance_type_dbname, instance_type_dbname));
// add new correlation type
CentralRepoDbUtil.insertCorrelationType(connection, type);
} else if (type.getId() == CorrelationAttributeInstance.EMAIL_TYPE_ID || type.getId() == CorrelationAttributeInstance.PHONE_TYPE_ID) {
// Alter the existing _instance tables for Phone and Email attributes to add account_id column
String sqlStr = String.format(getAlterArtifactInstancesAddAccountIdTemplate(selectedPlatform), instance_type_dbname);
statement.execute(sqlStr);
// SQLite does NOT allow adding a constraint with Alter Table statement.
// The alternative would be to create new tables, copy all data over, and delete old tables - potentially a time consuming process.
// We decided to not add this constraint for SQLite, since there likely aren't many users using SQLite based Central Repo.
if (selectedPlatform == CentralRepoPlatforms.POSTGRESQL) {
sqlStr = String.format(getAlterArtifactInstancesAddAccountIdConstraintTemplate(), instance_type_dbname);
statement.execute(sqlStr);
}
}
}
// insert default accounts data
RdbmsCentralRepoFactory.insertDefaultAccountsTablesContent(connection, selectedPlatform);
}
}
}
/**
* Returns ALTER TABLE SQL string template to add an account_id column to a
* TYPE_instances table.
*
* @param selectedPlatform
*
* @return SQL string template to alter the table.
*/
static String getAlterArtifactInstancesAddAccountIdTemplate(CentralRepoPlatforms selectedPlatform) {
// Each "%s" will be replaced with the relevant TYPE_instances table name.
return "ALTER TABLE %s"
+ " ADD account_id " + RdbmsCentralRepoFactory.getBigIntType(selectedPlatform) + " DEFAULT NULL";
}
/**
* Returns ALTER TABLE SQL string template to add a Foreign Key constraint
* to a TYPE_instances table.
*
* @return SQL string template to alter the table.
*/
static String getAlterArtifactInstancesAddAccountIdConstraintTemplate() {
// Each "%s" will be replaced with the relevant TYPE_instances table name.
return "ALTER TABLE %s"
+ " ADD CONSTRAINT account_id_fk foreign key (account_id) references accounts(id)";
}
}

View File

@ -123,6 +123,28 @@ public class CentralRepoDbUtil {
return true; return true;
} }
/**
* Inserts the specified correlation type into the database.
*
* @param conn Open connection to use.
* @param correlationType New correlation type to add.
*
*/
public static void insertCorrelationType(Connection conn, CorrelationAttributeInstance.Type correlationType) throws SQLException {
String sql = "INSERT INTO correlation_types(id, display_name, db_table_name, supported, enabled) VALUES (?, ?, ?, ?, ?)";
try (PreparedStatement preparedStatement = conn.prepareStatement(sql)) {
preparedStatement.setInt(1, correlationType.getId());
preparedStatement.setString(2, correlationType.getDisplayName());
preparedStatement.setString(3, correlationType.getDbTableName());
preparedStatement.setInt(4, correlationType.isSupported() ? 1 : 0);
preparedStatement.setInt(5, correlationType.isEnabled() ? 1 : 0);
preparedStatement.execute();
}
}
/** /**
* Writes the current schema version into the database. * Writes the current schema version into the database.
* *
@ -303,4 +325,17 @@ public class CentralRepoDbUtil {
closeStatement(preparedStatement); closeStatement(preparedStatement);
} }
/**
* Checks if the given correlation attribute type has an account behind it.
*
* @param type Correlation type to check.
*
* @return True If the specified correlation type has an account.
*/
static boolean correlationAttribHasAnAccount(CorrelationAttributeInstance.Type type) {
return (type.getId() >= CorrelationAttributeInstance.ADDITIONAL_TYPES_BASE_ID)
|| type.getId() == CorrelationAttributeInstance.PHONE_TYPE_ID
|| type.getId() == CorrelationAttributeInstance.EMAIL_TYPE_ID;
}
} }

View File

@ -18,102 +18,11 @@
*/ */
package org.sleuthkit.autopsy.centralrepository.datamodel; package org.sleuthkit.autopsy.centralrepository.datamodel;
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
/** /**
* * This enum describes the possible database types for central repo.
*/ */
public enum CentralRepoPlatforms { public enum CentralRepoPlatforms {
DISABLED("Disabled", true), DISABLED,
SQLITE("SQLite", false), SQLITE,
POSTGRESQL("PostgreSQL", false); POSTGRESQL
private final String platformName;
private Boolean selected;
CentralRepoPlatforms(String name, Boolean selected) {
this.platformName = name;
this.selected = selected;
loadSettings();
}
/**
* Load the selectedPlatform boolean from the config file, if it is set.
*/
private void loadSettings() {
String selectedPlatformString = ModuleSettings.getConfigSetting("CentralRepository", "db.selectedPlatform"); // NON-NLS
if (null != selectedPlatformString) {
selected = this.toString().equalsIgnoreCase(selectedPlatformString);
} else if (this == DISABLED) {
selected = true;
}
}
@Override
public String toString() {
return platformName;
}
private void setSelected(Boolean selected) {
this.selected = selected;
}
public Boolean isSelected() {
return selected;
}
public static CentralRepoPlatforms fromString(String pName) {
if (null == pName) {
return DISABLED;
}
for (CentralRepoPlatforms p : CentralRepoPlatforms.values()) {
if (p.toString().equalsIgnoreCase(pName)) {
return p;
}
}
return DISABLED;
}
/**
* Save the selected platform to the config file.
*/
public static void saveSelectedPlatform() {
CentralRepoPlatforms selectedPlatform = DISABLED;
for (CentralRepoPlatforms p : CentralRepoPlatforms.values()) {
if (p.isSelected()) {
selectedPlatform = p;
}
}
ModuleSettings.setConfigSetting("CentralRepository", "db.selectedPlatform", selectedPlatform.name()); // NON-NLS
}
/**
* Set the selected db platform. Other platforms will be set as not
* selected.
*
* @param platformString The name of the selected platform.
*/
public static void setSelectedPlatform(String platformString) {
CentralRepoPlatforms pSelected = CentralRepoPlatforms.fromString(platformString);
for (CentralRepoPlatforms p : CentralRepoPlatforms.values()) {
p.setSelected(p == pSelected);
}
}
/**
* Get the selected platform.
*
* @return The selected platform, or if not platform is selected, default to
* DISABLED.
*/
public static CentralRepoPlatforms getSelectedPlatform() {
for (CentralRepoPlatforms p : CentralRepoPlatforms.values()) {
if (p.isSelected()) {
return p;
}
}
return DISABLED;
}
} }

View File

@ -0,0 +1,185 @@
/*
*
* Autopsy Forensic Browser
*
* Copyright 2012-2020 Basis Technology Corp.
*
* Copyright 2012 42six Solutions.
* Contact: aebadirad <at> 42six <dot> com
* Project Contact/Architect: 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.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.core.UserPreferencesException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
import org.sleuthkit.autopsy.coreutils.TextConverter;
import org.sleuthkit.autopsy.coreutils.TextConverterException;
import org.sleuthkit.datamodel.CaseDbConnectionInfo;
/**
* This class handles saving and loading of postgres settings for central repository.
*/
public class CentralRepoPostgresSettingsUtil {
private final static Logger LOGGER = Logger.getLogger(CentralRepoPostgresSettingsUtil.class.getName());
private static final String PASSWORD_KEY = "db.postgresql.password";
private static final String BULK_THRESHOLD_KEY = "db.postgresql.bulkThreshold";
private static final String PORT_KEY = "db.postgresql.port";
private static final String USER_KEY = "db.postgresql.user";
private static final String DBNAME_KEY = "db.postgresql.dbName";
private static final String HOST_KEY = "db.postgresql.host";
private static final String MODULE_KEY = "CentralRepository";
private static CentralRepoPostgresSettingsUtil instance = null;
/**
* This method retrieves a singleton instance of this class.
* @return The singleton instance of this class.
*/
public static synchronized CentralRepoPostgresSettingsUtil getInstance() {
if (instance == null)
instance = new CentralRepoPostgresSettingsUtil();
return instance;
}
private CentralRepoPostgresSettingsUtil() {}
/**
* Uses setter object to set a value as specified by 'value'. In the event that 'value'
* is null, the setter will not be called. Exceptions that are raised from the setter will
* be logged.
*
* @param setter The setter to call.
* @param value The value to use with the setter.
*/
private void setValOrLog(ValueSetter setter, String value) {
// ignore null values as they indicate a setting that is not set yet
if (value == null || value.isEmpty())
return;
try {
setter.set(value);
}
catch (CentralRepoException | NumberFormatException e) {
LOGGER.log(Level.WARNING, "There was an error in converting central repo postgres settings", e);
}
}
/**
* This interface represents a setter that potentially throws an exception.
*/
private interface ValueSetter {
void set(String value) throws CentralRepoException, NumberFormatException;
}
/**
* This method loads multi-user settings to be used as a postgres connection to central repository. If
* settings could not be loaded, default values will be returned.
*
* @return The settings loaded from multi-user settings.
*/
public PostgresConnectionSettings loadMultiUserSettings() {
PostgresConnectionSettings settings = new PostgresConnectionSettings();
CaseDbConnectionInfo muConn;
try {
muConn = UserPreferences.getDatabaseConnectionInfo();
} catch (UserPreferencesException ex) {
LOGGER.log(Level.SEVERE, "Failed to import settings from multi-user settings.", ex);
return settings;
}
setValOrLog((v) -> settings.setHost(v), muConn.getHost());
setValOrLog((v) -> settings.setUserName(v), muConn.getUserName());
setValOrLog((v) -> settings.setPassword(v), muConn.getPassword());
setValOrLog((v) -> settings.setPort(Integer.parseInt(v)), muConn.getPort());
return settings;
}
/**
* This method loads the custom postgres settings for central repository. If
* settings could not be loaded, default values will be returned.
*
* @return The settings loaded from custom postgres settings.
*/
public PostgresConnectionSettings loadCustomSettings() {
PostgresConnectionSettings settings = new PostgresConnectionSettings();
Map<String, String> keyVals = ModuleSettings.getConfigSettings(MODULE_KEY);
setValOrLog((v) -> settings.setHost(v), keyVals.get(HOST_KEY));
setValOrLog((v) -> settings.setDbName(v), keyVals.get(DBNAME_KEY));
setValOrLog((v) -> settings.setUserName(v), keyVals.get(USER_KEY));
setValOrLog((v) -> settings.setPort(Integer.parseInt(v)), keyVals.get(PORT_KEY));
setValOrLog((v) -> settings.setBulkThreshold(Integer.parseInt(v)), keyVals.get((BULK_THRESHOLD_KEY)));
String passwordHex = keyVals.get(PASSWORD_KEY);
if (passwordHex != null) {
String password;
try {
password = TextConverter.convertHexTextToText(passwordHex);
} catch (TextConverterException ex) {
LOGGER.log(Level.WARNING, "Failed to convert password from hex text to text.", ex);
password = null;
}
final String finalPassword = password;
setValOrLog((v) -> settings.setPassword(v), finalPassword);
}
return settings;
}
/**
* This method saves the settings for a custom postgres central repository connection.
* @param settings The settings to save.
*/
public void saveCustomSettings(PostgresConnectionSettings settings) {
Map<String, String> map = new HashMap<String, String>();
map.put(HOST_KEY, settings.getHost());
map.put(PORT_KEY, Integer.toString(settings.getPort()));
map.put(DBNAME_KEY, settings.getDbName());
map.put(BULK_THRESHOLD_KEY, Integer.toString(settings.getBulkThreshold()));
map.put(USER_KEY, settings.getUserName());
try {
map.put(PASSWORD_KEY, TextConverter.convertTextToHexText(settings.getPassword())); // NON-NLS
} catch (TextConverterException ex) {
LOGGER.log(Level.SEVERE, "Failed to convert password from text to hex text.", ex);
}
ModuleSettings.setConfigSettings(MODULE_KEY, map);
}
/**
* This method checks if saved settings differ from the in-memory object provided in the 'settings' parameter.
* @param settings The in-memory object.
* @return Whether or not settings parameter differs from saved custom settings.
*/
public boolean areCustomSettingsChanged(PostgresConnectionSettings settings) {
PostgresConnectionSettings saved = loadCustomSettings();
return saved.equals(settings);
}
}

View File

@ -42,7 +42,7 @@ public interface CentralRepository {
CentralRepoPlatforms selectedPlatform = CentralRepoPlatforms.DISABLED; CentralRepoPlatforms selectedPlatform = CentralRepoPlatforms.DISABLED;
if (CentralRepoDbUtil.allowUseOfCentralRepository()) { if (CentralRepoDbUtil.allowUseOfCentralRepository()) {
selectedPlatform = CentralRepoPlatforms.getSelectedPlatform(); selectedPlatform = CentralRepoDbManager.getSavedDbChoice().getDbPlatform();
} }
switch (selectedPlatform) { switch (selectedPlatform) {
case POSTGRESQL: case POSTGRESQL:
@ -93,7 +93,7 @@ public interface CentralRepository {
*/ */
static boolean isEnabled() { static boolean isEnabled() {
return CentralRepoDbUtil.allowUseOfCentralRepository() return CentralRepoDbUtil.allowUseOfCentralRepository()
&& CentralRepoPlatforms.getSelectedPlatform() != CentralRepoPlatforms.DISABLED; && CentralRepoDbManager.getSavedDbChoice() != CentralRepoDbChoice.DISABLED;
} }
/** /**

View File

@ -294,8 +294,9 @@ public class CorrelationAttributeInstance implements Serializable {
// Create Correlation Types for Accounts. // Create Correlation Types for Accounts.
int correlationTypeId = ADDITIONAL_TYPES_BASE_ID; int correlationTypeId = ADDITIONAL_TYPES_BASE_ID;
for (Account.Type type : Account.Type.PREDEFINED_ACCOUNT_TYPES) { for (Account.Type type : Account.Type.PREDEFINED_ACCOUNT_TYPES) {
// Skip Device account type - we dont want to correlate on those.
// Skip Phone and Email accounts as there are already Correlation types defined for those. // Skip Phone and Email accounts as there are already Correlation types defined for those.
if (type != Account.Type.EMAIL && type != Account.Type.PHONE) { if (type != Account.Type.DEVICE && type != Account.Type.EMAIL && type != Account.Type.PHONE) {
defaultCorrelationTypes.add(new CorrelationAttributeInstance.Type(correlationTypeId, type.getDisplayName(), type.getTypeName().toLowerCase() + "_acct", true, true)); //NON-NLS defaultCorrelationTypes.add(new CorrelationAttributeInstance.Type(correlationTypeId, type.getDisplayName(), type.getTypeName().toLowerCase() + "_acct", true, true)); //NON-NLS
correlationTypeId++; correlationTypeId++;
} }

View File

@ -27,6 +27,7 @@ import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount.CentralRepoAccountType; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount.CentralRepoAccountType;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Account;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
@ -84,23 +85,12 @@ public class CorrelationAttributeUtil {
BlackboardArtifact sourceArtifact = getCorrAttrSourceArtifact(artifact); BlackboardArtifact sourceArtifact = getCorrAttrSourceArtifact(artifact);
if (sourceArtifact != null) { if (sourceArtifact != null) {
int artifactTypeID = sourceArtifact.getArtifactTypeID(); int artifactTypeID = sourceArtifact.getArtifactTypeID();
if (artifactTypeID == ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()) { if (artifactTypeID == ARTIFACT_TYPE.TSK_WEB_BOOKMARK.getTypeID()
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_COOKIE.getTypeID()
|| artifactTypeID == ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID() || artifactTypeID == ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID()
|| artifactTypeID == ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID()) { || artifactTypeID == ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID()) {
makeCorrAttrFromArtifactAttr(correlationAttrs, sourceArtifact, 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);
} else if (artifactTypeID == ARTIFACT_TYPE.TSK_DEVICE_ATTACHED.getTypeID()) { } 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_DEVICE_ID, CorrelationAttributeInstance.USBID_TYPE_ID);
makeCorrAttrFromArtifactAttr(correlationAttrs, sourceArtifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MAC_ADDRESS, CorrelationAttributeInstance.MAC_TYPE_ID); makeCorrAttrFromArtifactAttr(correlationAttrs, sourceArtifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MAC_ADDRESS, CorrelationAttributeInstance.MAC_TYPE_ID);
@ -169,58 +159,6 @@ public class CorrelationAttributeUtil {
return sourceArtifact; 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;
/*
* 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) {
corrAttr = makeCorrAttr(artifact, CentralRepository.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.PHONE_TYPE_ID), value);
}
}
return corrAttr;
}
/** /**
* Makes a correlation attribute instance for an account artifact. * Makes a correlation attribute instance for an account artifact.
* *
@ -248,6 +186,9 @@ public class CorrelationAttributeUtil {
BlackboardAttribute accountTypeAttribute = acctArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE)); BlackboardAttribute accountTypeAttribute = acctArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE));
String accountTypeStr = accountTypeAttribute.getValueString(); String accountTypeStr = accountTypeAttribute.getValueString();
// do not create any correlation attribute instance for a Device account
if (Account.Type.DEVICE.getTypeName().equalsIgnoreCase(accountTypeStr) == false) {
// Get the corresponding CentralRepoAccountType from the database. // Get the corresponding CentralRepoAccountType from the database.
CentralRepoAccountType crAccountType = CentralRepository.getInstance().getAccountTypeByName(accountTypeStr); CentralRepoAccountType crAccountType = CentralRepository.getInstance().getAccountTypeByName(accountTypeStr);
@ -268,6 +209,7 @@ public class CorrelationAttributeUtil {
corrAttrInstances.add(corrAttr); corrAttrInstances.add(corrAttr);
} }
} }
}
/** /**
* Makes a correlation attribute instance from a specified attribute of an * Makes a correlation attribute instance from a specified attribute of an

View File

@ -19,13 +19,13 @@
package org.sleuthkit.autopsy.centralrepository.datamodel; package org.sleuthkit.autopsy.centralrepository.datamodel;
/** /**
* provides the status of the database after attempting to validate central repo settings * This enum provides the status of the database after attempting to validate central repo settings.
*/ */
public enum DatabaseTestResult { public enum DatabaseTestResult {
UNTESTED, UNTESTED,
CONNECTION_FAILED, CONNECTION_FAILED,
SCHEMA_INVALID, SCHEMA_INVALID,
DB_DOES_NOT_EXIST, DB_DOES_NOT_EXIST,
TESTEDOK; TESTED_OK;
} }

View File

@ -24,15 +24,9 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.util.List;
import java.util.Properties; import java.util.Properties;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.regex.Pattern;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
import org.sleuthkit.autopsy.coreutils.TextConverter;
import org.sleuthkit.autopsy.coreutils.TextConverterException;
import static org.sleuthkit.autopsy.centralrepository.datamodel.RdbmsCentralRepo.SOFTWARE_CR_DB_SCHEMA_VERSION;
/** /**
* Settings for the Postgres implementation of the Central Repository database * Settings for the Postgres implementation of the Central Repository database
@ -40,105 +34,83 @@ import static org.sleuthkit.autopsy.centralrepository.datamodel.RdbmsCentralRepo
* NOTE: This is public scope because the options panel calls it directly to * NOTE: This is public scope because the options panel calls it directly to
* set/get * set/get
*/ */
public final class PostgresCentralRepoSettings implements CentralRepoDbSettings { public final class PostgresCentralRepoSettings implements CentralRepoDbConnectivityManager {
private final static Logger LOGGER = Logger.getLogger(PostgresCentralRepoSettings.class.getName()); private final static Logger LOGGER = Logger.getLogger(PostgresCentralRepoSettings.class.getName());
private final static String DEFAULT_HOST = ""; // NON-NLS
private final static int DEFAULT_PORT = 5432;
private final static String DEFAULT_DBNAME = "central_repository"; // NON-NLS
private final static String DEFAULT_USERNAME = "";
private final static String DEFAULT_PASSWORD = "";
private final static String VALIDATION_QUERY = "SELECT version()"; // NON-NLS private final static String VALIDATION_QUERY = "SELECT version()"; // NON-NLS
private final static String JDBC_BASE_URI = "jdbc:postgresql://"; // NON-NLS private final static String JDBC_BASE_URI = "jdbc:postgresql://"; // NON-NLS
private final static String JDBC_DRIVER = "org.postgresql.Driver"; // NON-NLS private final static String JDBC_DRIVER = "org.postgresql.Driver"; // NON-NLS
private final static String DB_NAMES_REGEX = "[a-z][a-z0-9_]*"; // only lower case
private final static String DB_USER_NAMES_REGEX = "[a-zA-Z]\\w*";
private String host;
private int port;
private String dbName;
private int bulkThreshold;
private String userName;
private String password;
public PostgresCentralRepoSettings() {
private final PostgresSettingsLoader loader;
private PostgresConnectionSettings connSettings;
private static PostgresSettingsLoader getLoaderFromSaved() throws CentralRepoException {
CentralRepoDbChoice choice = CentralRepoDbManager.getSavedDbChoice();
if (choice == CentralRepoDbChoice.POSTGRESQL_CUSTOM)
return PostgresSettingsLoader.CUSTOM_SETTINGS_LOADER;
else if (choice == CentralRepoDbChoice.POSTGRESQL_MULTIUSER)
return PostgresSettingsLoader.MULTIUSER_SETTINGS_LOADER;
else
throw new CentralRepoException("cannot load or save postgres settings for selection: " + choice);
}
/**
* This method loads the settings with a custom {@link PostgresSettingsLoader PostgresSettingsLoader} object.
* @param loader The loader to be used.
*/
public PostgresCentralRepoSettings(PostgresSettingsLoader loader) {
this.loader = loader;
loadSettings(); loadSettings();
} }
/**
* This is the default constructor that loads settings from selected db choice.
*/
public PostgresCentralRepoSettings() throws CentralRepoException {
this(getLoaderFromSaved());
}
@Override
public void loadSettings() {
this.connSettings = loader.loadSettings();
}
@Override
public void saveSettings() {
loader.saveSettings(connSettings);
}
@Override @Override
public String toString() { public String toString() {
return String.format("PostgresCentralRepoSettings: [db type: postgres, host: %s:%d, db name: %s, username: %s]", return String.format("PostgresCentralRepoSettings: [db type: postgres, host: %s:%d, db name: %s, username: %s]",
getHost(), getPort(), getDbName(), getUserName()); getHost(), getPort(), getDbName(), getUserName());
} }
public void loadSettings() {
host = ModuleSettings.getConfigSetting("CentralRepository", "db.postgresql.host"); // NON-NLS /**
if (host == null || host.isEmpty()) { * @return the VALIDATION_QUERY
host = DEFAULT_HOST; */
String getValidationQuery() {
return VALIDATION_QUERY;
} }
try { /**
String portString = ModuleSettings.getConfigSetting("CentralRepository", "db.postgresql.port"); // NON-NLS * @return the POSTGRES_DRIVER
if (portString == null || portString.isEmpty()) { */
port = DEFAULT_PORT; String getDriver() {
} else { return JDBC_DRIVER;
port = Integer.parseInt(portString);
if (port < 0 || port > 65535) {
port = DEFAULT_PORT;
}
}
} catch (NumberFormatException ex) {
port = DEFAULT_PORT;
} }
dbName = ModuleSettings.getConfigSetting("CentralRepository", "db.postgresql.dbName"); // NON-NLS /**
if (dbName == null || dbName.isEmpty()) { * @return the JDBC_BASE_URI
dbName = DEFAULT_DBNAME; */
String getJDBCBaseURI() {
return JDBC_BASE_URI;
} }
try {
String bulkThresholdString = ModuleSettings.getConfigSetting("CentralRepository", "db.postgresql.bulkThreshold"); // NON-NLS
if (bulkThresholdString == null || bulkThresholdString.isEmpty()) {
this.bulkThreshold = RdbmsCentralRepo.DEFAULT_BULK_THRESHHOLD;
} else {
this.bulkThreshold = Integer.parseInt(bulkThresholdString);
if (getBulkThreshold() <= 0) {
this.bulkThreshold = RdbmsCentralRepo.DEFAULT_BULK_THRESHHOLD;
}
}
} catch (NumberFormatException ex) {
this.bulkThreshold = RdbmsCentralRepo.DEFAULT_BULK_THRESHHOLD;
}
userName = ModuleSettings.getConfigSetting("CentralRepository", "db.postgresql.user"); // NON-NLS
if (userName == null || userName.isEmpty()) {
userName = DEFAULT_USERNAME;
}
password = ModuleSettings.getConfigSetting("CentralRepository", "db.postgresql.password"); // NON-NLS
if (password == null || password.isEmpty()) {
password = DEFAULT_PASSWORD;
} else {
try {
password = TextConverter.convertHexTextToText(password);
} catch (TextConverterException ex) {
LOGGER.log(Level.WARNING, "Failed to convert password from hex text to text.", ex);
password = DEFAULT_PASSWORD;
}
}
}
public void saveSettings() {
ModuleSettings.setConfigSetting("CentralRepository", "db.postgresql.host", getHost()); // NON-NLS
ModuleSettings.setConfigSetting("CentralRepository", "db.postgresql.port", Integer.toString(port)); // NON-NLS
ModuleSettings.setConfigSetting("CentralRepository", "db.postgresql.dbName", getDbName()); // NON-NLS
ModuleSettings.setConfigSetting("CentralRepository", "db.postgresql.bulkThreshold", Integer.toString(getBulkThreshold())); // NON-NLS
ModuleSettings.setConfigSetting("CentralRepository", "db.postgresql.user", getUserName()); // NON-NLS
try {
ModuleSettings.setConfigSetting("CentralRepository", "db.postgresql.password", TextConverter.convertTextToHexText(getPassword())); // NON-NLS
} catch (TextConverterException ex) {
LOGGER.log(Level.SEVERE, "Failed to convert password from text to hex text.", ex);
}
}
/** /**
* Get the full connection URL as a String * Get the full connection URL as a String
@ -302,72 +274,32 @@ public final class PostgresCentralRepoSettings implements CentralRepoDbSettings
} }
boolean isChanged() {
String hostString = ModuleSettings.getConfigSetting("CentralRepository", "db.postgresql.host"); // NON-NLS
String portString = ModuleSettings.getConfigSetting("CentralRepository", "db.postgresql.port"); // NON-NLS
String dbNameString = ModuleSettings.getConfigSetting("CentralRepository", "db.postgresql.dbName"); // NON-NLS
String bulkThresholdString = ModuleSettings.getConfigSetting("CentralRepository", "db.postgresql.bulkThreshold"); // NON-NLS
String userNameString = ModuleSettings.getConfigSetting("CentralRepository", "db.postgresql.user"); // NON-NLS
String userPasswordString = ModuleSettings.getConfigSetting("CentralRepository", "db.postgresql.password"); // NON-NLS
return !host.equals(hostString) || !Integer.toString(port).equals(portString)
|| !dbName.equals(dbNameString) || !Integer.toString(bulkThreshold).equals(bulkThresholdString)
|| !userName.equals(userNameString) || !password.equals(userPasswordString);
}
/** /**
* @return the host * @return the host
*/ */
public String getHost() { public String getHost() {
return host; return connSettings.getHost();
} }
/** /**
* @param host the host to set * @param host the host to set
*/ */
public void setHost(String host) throws CentralRepoException { public void setHost(String host) throws CentralRepoException {
if (null != host && !host.isEmpty()) { connSettings.setHost(host);
this.host = host;
} else {
throw new CentralRepoException("Invalid host name. Cannot be empty."); // NON-NLS
}
} }
/** /**
* @return the port * @return the port
*/ */
public int getPort() { public int getPort() {
return port; return connSettings.getPort();
} }
/** /**
* @param port the port to set * @param port the port to set
*/ */
public void setPort(int port) throws CentralRepoException { public void setPort(int port) throws CentralRepoException {
if (port > 0 && port < 65535) { connSettings.setPort(port);
this.port = port;
} else {
throw new CentralRepoException("Invalid port. Must be a number greater than 0."); // NON-NLS
}
} }
/** /**
@ -377,95 +309,72 @@ public final class PostgresCentralRepoSettings implements CentralRepoDbSettings
* @return the dbName * @return the dbName
*/ */
public String getDbName() { public String getDbName() {
return dbName.toLowerCase(); return connSettings.getDbName() == null ? null : connSettings.getDbName().toLowerCase();
} }
/** /**
* @param dbName the dbName to set * @param dbName the dbName to set
*/ */
public void setDbName(String dbName) throws CentralRepoException { public void setDbName(String dbName) throws CentralRepoException {
if (dbName == null || dbName.isEmpty()) { connSettings.setDbName(dbName);
throw new CentralRepoException("Invalid database name. Cannot be empty."); // NON-NLS
} else if (!Pattern.matches(DB_NAMES_REGEX, dbName)) {
throw new CentralRepoException("Invalid database name. Name must start with a lowercase letter and can only contain lowercase letters, numbers, and '_'."); // NON-NLS
}
this.dbName = dbName.toLowerCase();
} }
/** /**
* @return the bulkThreshold * @return the bulkThreshold
*/ */
int getBulkThreshold() { int getBulkThreshold() {
return bulkThreshold; return connSettings.getBulkThreshold();
} }
/** /**
* @param bulkThreshold the bulkThreshold to set * @param bulkThreshold the bulkThreshold to set
*/ */
public void setBulkThreshold(int bulkThreshold) throws CentralRepoException { public void setBulkThreshold(int bulkThreshold) throws CentralRepoException {
if (bulkThreshold > 0) { connSettings.setBulkThreshold(bulkThreshold);
this.bulkThreshold = bulkThreshold;
} else {
throw new CentralRepoException("Invalid bulk threshold."); // NON-NLS
}
} }
/** /**
* @return the userName * @return the userName
*/ */
public String getUserName() { public String getUserName() {
return userName; return connSettings.getUserName();
} }
/** /**
* @param userName the userName to set * @param userName the userName to set
*/ */
public void setUserName(String userName) throws CentralRepoException { public void setUserName(String userName) throws CentralRepoException {
if (userName == null || userName.isEmpty()) { connSettings.setUserName(userName);
throw new CentralRepoException("Invalid user name. Cannot be empty."); // NON-NLS
} else if (!Pattern.matches(DB_USER_NAMES_REGEX, userName)) {
throw new CentralRepoException("Invalid user name. Name must start with a letter and can only contain letters, numbers, and '_'."); // NON-NLS
}
this.userName = userName;
} }
/** /**
* @return the password * @return the password
*/ */
public String getPassword() { public String getPassword() {
return password; return connSettings.getPassword();
} }
/** /**
* @param password the password to set * @param password the password to set
*/ */
public void setPassword(String password) throws CentralRepoException { public void setPassword(String password) throws CentralRepoException {
if (password == null || password.isEmpty()) { connSettings.setPassword(password);
throw new CentralRepoException("Invalid user password. Cannot be empty."); // NON-NLS
}
this.password = password;
} }
/** @Override
* @return the VALIDATION_QUERY public DatabaseTestResult testStatus() {
*/ if (verifyConnection()) {
String getValidationQuery() { if (verifyDatabaseExists()) {
return VALIDATION_QUERY; if (verifyDatabaseSchema()) {
return DatabaseTestResult.TESTED_OK;
} else {
return DatabaseTestResult.SCHEMA_INVALID;
} }
} else {
/** return DatabaseTestResult.DB_DOES_NOT_EXIST;
* @return the POSTGRES_DRIVER }
*/ } else {
String getDriver() { return DatabaseTestResult.CONNECTION_FAILED;
return JDBC_DRIVER;
} }
/**
* @return the JDBC_BASE_URI
*/
String getJDBCBaseURI() {
return JDBC_BASE_URI;
} }
} }

View File

@ -0,0 +1,222 @@
/*
* 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.util.Objects;
import java.util.regex.Pattern;
/**
*
* This class is a POJO for postgres settings to be used with central repository.
*/
public class PostgresConnectionSettings {
private final static String DB_NAMES_REGEX = "[a-z][a-z0-9_]*"; // only lower case
private final static String DB_USER_NAMES_REGEX = "[a-zA-Z]\\w*";
public final static String DEFAULT_HOST = ""; // NON-NLS
public final static int DEFAULT_PORT = 5432;
public final static String DEFAULT_DBNAME = "central_repository"; // NON-NLS
public final static String DEFAULT_USERNAME = "";
public final static String DEFAULT_PASSWORD = "";
private static void validateStr(String s, String errMessage) throws CentralRepoException {
if (null == s || s.isEmpty())
throw new CentralRepoException(errMessage);
}
private static void validateRegex(String s, String pattern, String errMessage) throws CentralRepoException {
if (!Pattern.matches(pattern, s))
throw new CentralRepoException(errMessage);
}
private static void validateNum(int num, Integer min, Integer max, String errMessage) throws CentralRepoException {
if ((min != null && num < min) || (max != null && num > max))
throw new CentralRepoException(errMessage);
}
private String host = DEFAULT_HOST;
private int port = DEFAULT_PORT;
private String dbName = DEFAULT_DBNAME;
private int bulkThreshold = RdbmsCentralRepo.DEFAULT_BULK_THRESHHOLD;
private String userName = DEFAULT_USERNAME;
private String password = DEFAULT_PASSWORD;
/**
* This method retrieves the postgres host.
* @return The host for these settings.
*/
public String getHost() {
return host;
}
/**
* This method returns the port number for these settings.
* @return The port number for these settings.
*/
public int getPort() {
return port;
}
/**
* This method returns the database name for these settings.
* @return The database name for these settings.
*/
public String getDbName() {
return dbName;
}
/**
* This method returns the bulk threshold.
* @return The bulk threshold.
*/
public int getBulkThreshold() {
return bulkThreshold;
}
/**
* This method returns the username to use for this connection.
* @return The username to use.
*/
public String getUserName() {
return userName;
}
/**
* This method returns the password to use for this connection.
* @return The password to use for this connection.
*/
public String getPassword() {
return password;
}
/**
* This method sets the host for this connection.
* NOTE: must be non-empty string.
* @param host the host to set
*/
public void setHost(String host) throws CentralRepoException {
validateStr(host, "Invalid host name. Cannot be empty.");
this.host = host;
}
/**
* This method sets the port for this connection.
* @param port The port to set (must be [1,65535]).
*/
public void setPort(int port) throws CentralRepoException {
validateNum(port, 1, 65535, "Invalid port. Must be a number greater than 0.");
this.port = port;
}
/**
* This methods sets the name of the database.
* NOTE: this name needs to be a valid postgres database name.
* @param dbName The database name.
*/
public void setDbName(String dbName) throws CentralRepoException {
validateStr(dbName, "Invalid database name. Cannot be empty."); // NON-NLS
validateRegex(dbName, DB_NAMES_REGEX,
"Invalid database name. Name must start with a lowercase letter and can only contain lowercase letters, numbers, and '_'."); // NON-NLS
this.dbName = dbName.toLowerCase();
}
/**
* This method sets the bulk threshold of this connection.
* @param bulkThreshold The bulk threshold to set (must be greater than 0).
*/
public void setBulkThreshold(int bulkThreshold) throws CentralRepoException {
validateNum(bulkThreshold, 1, null, "Invalid bulk threshold.");
this.bulkThreshold = bulkThreshold;
}
/**
* This method sets the username for this connection.
* NOTE: must be a valid postgres username.
* @param userName The user name to set.
*/
public void setUserName(String userName) throws CentralRepoException {
validateStr(userName, "Invalid user name. Cannot be empty."); // NON-NLS
validateRegex(userName, DB_USER_NAMES_REGEX,
"Invalid user name. Name must start with a letter and can only contain letters, numbers, and '_'.");
this.userName = userName;
}
/**
* This method sets the password for this connection.
* @param password The password to set.
*/
public void setPassword(String password) throws CentralRepoException {
validateStr(password, "Invalid user password. Cannot be empty.");
this.password = password;
}
@Override
public int hashCode() {
int hash = 7;
hash = 43 * hash + Objects.hashCode(this.host);
hash = 43 * hash + this.port;
hash = 43 * hash + Objects.hashCode(this.dbName);
hash = 43 * hash + this.bulkThreshold;
hash = 43 * hash + Objects.hashCode(this.userName);
hash = 43 * hash + Objects.hashCode(this.password);
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 PostgresConnectionSettings other = (PostgresConnectionSettings) obj;
if (this.port != other.port) {
return false;
}
if (this.bulkThreshold != other.bulkThreshold) {
return false;
}
if (!Objects.equals(this.host, other.host)) {
return false;
}
if (!Objects.equals(this.dbName, other.dbName)) {
return false;
}
if (!Objects.equals(this.userName, other.userName)) {
return false;
}
if (!Objects.equals(this.password, other.password)) {
return false;
}
return true;
}
}

View File

@ -0,0 +1,77 @@
/*
* 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;
/**
* This is an interface to load or save postgres settings.
*/
public interface PostgresSettingsLoader {
/**
* This method loads the current settings.
* @return The settings that were loaded.
*/
PostgresConnectionSettings loadSettings();
/**
* This method saves the current settings.
* @param settings The settings to save.
*/
void saveSettings(PostgresConnectionSettings settings);
PostgresSettingsLoader CUSTOM_SETTINGS_LOADER = new Custom();
PostgresSettingsLoader MULTIUSER_SETTINGS_LOADER = new MultiUser();
CentralRepoPostgresSettingsUtil SETTINGS_UTIL = CentralRepoPostgresSettingsUtil.getInstance();
/**
* This class loads and saves custom postgres settings.
*/
class Custom implements PostgresSettingsLoader {
@Override
public PostgresConnectionSettings loadSettings() {
return SETTINGS_UTIL.loadCustomSettings();
}
@Override
public void saveSettings(PostgresConnectionSettings settings) {
SETTINGS_UTIL.saveCustomSettings(settings);
}
}
/**
* This class loads multi-user postgres settings to be used with central repo.
* NOTE: This class does not save settings on save operation as this is merely a proxy.
*/
class MultiUser implements PostgresSettingsLoader {
@Override
public PostgresConnectionSettings loadSettings() {
return SETTINGS_UTIL.loadMultiUserSettings();
}
/**
* NOTE: This action does not do anything. There is no need to save since
* this is just a proxy to multi user settings.
* @param settings The settings to save.
*/
@Override
public void saveSettings(PostgresConnectionSettings settings) {}
}
}

View File

@ -65,7 +65,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
static final String SCHEMA_MINOR_VERSION_KEY = "SCHEMA_MINOR_VERSION"; static final String SCHEMA_MINOR_VERSION_KEY = "SCHEMA_MINOR_VERSION";
static final String CREATION_SCHEMA_MAJOR_VERSION_KEY = "CREATION_SCHEMA_MAJOR_VERSION"; static final String CREATION_SCHEMA_MAJOR_VERSION_KEY = "CREATION_SCHEMA_MAJOR_VERSION";
static final String CREATION_SCHEMA_MINOR_VERSION_KEY = "CREATION_SCHEMA_MINOR_VERSION"; static final String CREATION_SCHEMA_MINOR_VERSION_KEY = "CREATION_SCHEMA_MINOR_VERSION";
static final CaseDbSchemaVersionNumber SOFTWARE_CR_DB_SCHEMA_VERSION = new CaseDbSchemaVersionNumber(1, 3); static final CaseDbSchemaVersionNumber SOFTWARE_CR_DB_SCHEMA_VERSION = new CaseDbSchemaVersionNumber(1, 4);
protected final List<CorrelationAttributeInstance.Type> defaultCorrelationTypes; protected final List<CorrelationAttributeInstance.Type> defaultCorrelationTypes;
@ -1002,18 +1002,26 @@ abstract class RdbmsCentralRepo implements CentralRepository {
public void addArtifactInstance(CorrelationAttributeInstance eamArtifact) throws CentralRepoException { public void addArtifactInstance(CorrelationAttributeInstance eamArtifact) throws CentralRepoException {
checkAddArtifactInstanceNulls(eamArtifact); checkAddArtifactInstanceNulls(eamArtifact);
// @@@ We should cache the case and data source IDs in memory // @@@ We should cache the case and data source IDs in memory
String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(eamArtifact.getCorrelationType()); String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(eamArtifact.getCorrelationType());
String sql boolean artifactHasAnAccount = CentralRepoDbUtil.correlationAttribHasAnAccount(eamArtifact.getCorrelationType());
= "INSERT INTO "
String sql;
// _instance table for accounts have an additional account_id column
if (artifactHasAnAccount) {
sql = "INSERT INTO "
+ tableName + tableName
+ "(case_id, data_source_id, value, file_path, known_status, comment, file_obj_id, account_id) " + "(case_id, data_source_id, value, file_path, known_status, comment, file_obj_id, account_id) "
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?) " + "VALUES (?, ?, ?, ?, ?, ?, ?, ?) "
+ getConflictClause(); + getConflictClause();
}
else {
sql = "INSERT INTO "
+ tableName
+ "(case_id, data_source_id, value, file_path, known_status, comment, file_obj_id) "
+ "VALUES (?, ?, ?, ?, ?, ?, ?) "
+ getConflictClause();
}
try (Connection conn = connect(); try (Connection conn = connect();
PreparedStatement preparedStatement = conn.prepareStatement(sql);) { PreparedStatement preparedStatement = conn.prepareStatement(sql);) {
@ -1032,11 +1040,14 @@ abstract class RdbmsCentralRepo implements CentralRepository {
} }
preparedStatement.setLong(7, eamArtifact.getFileObjectId()); preparedStatement.setLong(7, eamArtifact.getFileObjectId());
// set in the accountId only for artifacts that represent accounts
if (artifactHasAnAccount) {
if (eamArtifact.getAccountId() >= 0) { if (eamArtifact.getAccountId() >= 0) {
preparedStatement.setLong(8, eamArtifact.getAccountId()); preparedStatement.setLong(8, eamArtifact.getAccountId());
} else { } else {
preparedStatement.setNull(8, Types.INTEGER); preparedStatement.setNull(8, Types.INTEGER);
} }
}
preparedStatement.executeUpdate(); preparedStatement.executeUpdate();
} }
@ -3553,7 +3564,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
conn = connect(false); conn = connect(false);
conn.setAutoCommit(false); conn.setAutoCommit(false);
statement = conn.createStatement(); statement = conn.createStatement();
selectedPlatform = CentralRepoPlatforms.getSelectedPlatform(); selectedPlatform = CentralRepoDbManager.getSavedDbChoice().getDbPlatform();
int minorVersion = 0; int minorVersion = 0;
String minorVersionStr = null; String minorVersionStr = null;
resultSet = statement.executeQuery("SELECT value FROM db_info WHERE name='" + RdbmsCentralRepo.SCHEMA_MINOR_VERSION_KEY + "'"); resultSet = statement.executeQuery("SELECT value FROM db_info WHERE name='" + RdbmsCentralRepo.SCHEMA_MINOR_VERSION_KEY + "'");
@ -3808,6 +3819,10 @@ abstract class RdbmsCentralRepo implements CentralRepository {
throw new CentralRepoException("Currently selected database platform \"" + selectedPlatform.name() + "\" can not be upgraded.", Bundle.AbstractSqlEamDb_cannotUpgrage_message(selectedPlatform.name())); throw new CentralRepoException("Currently selected database platform \"" + selectedPlatform.name() + "\" can not be upgraded.", Bundle.AbstractSqlEamDb_cannotUpgrage_message(selectedPlatform.name()));
} }
} }
// Upgrade to 1.4
(new CentralRepoDbUpgrader13To14()).upgradeSchema(dbSchemaVersion, conn);
updateSchemaVersion(conn); updateSchemaVersion(conn);
conn.commit(); conn.commit();
logger.log(Level.INFO, String.format("Central Repository schema updated to version %s", SOFTWARE_CR_DB_SCHEMA_VERSION)); logger.log(Level.INFO, String.format("Central Repository schema updated to version %s", SOFTWARE_CR_DB_SCHEMA_VERSION));

View File

@ -83,6 +83,7 @@ public class RdbmsCentralRepoFactory {
public boolean initializeDatabaseSchema() { public boolean initializeDatabaseSchema() {
String createArtifactInstancesTableTemplate = getCreateArtifactInstancesTableTemplate(selectedPlatform); String createArtifactInstancesTableTemplate = getCreateArtifactInstancesTableTemplate(selectedPlatform);
String createAccountInstancesTableTemplate = getCreateAccountInstancesTableTemplate(selectedPlatform);
String instancesCaseIdIdx = getAddCaseIdIndexTemplate(); String instancesCaseIdIdx = getAddCaseIdIndexTemplate();
String instancesDatasourceIdIdx = getAddDataSourceIdIndexTemplate(); String instancesDatasourceIdIdx = getAddDataSourceIdIndexTemplate();
@ -147,7 +148,13 @@ public class RdbmsCentralRepoFactory {
reference_type_dbname = CentralRepoDbUtil.correlationTypeToReferenceTableName(type); reference_type_dbname = CentralRepoDbUtil.correlationTypeToReferenceTableName(type);
instance_type_dbname = CentralRepoDbUtil.correlationTypeToInstanceTableName(type); instance_type_dbname = CentralRepoDbUtil.correlationTypeToInstanceTableName(type);
stmt.execute(String.format(createArtifactInstancesTableTemplate, instance_type_dbname, instance_type_dbname)); // use the correct create table template, based on whether the attribute type represents an account or not.
String createTableTemplate = (CentralRepoDbUtil.correlationAttribHasAnAccount(type))
? createAccountInstancesTableTemplate
: createArtifactInstancesTableTemplate;
stmt.execute(String.format(createTableTemplate, instance_type_dbname, instance_type_dbname));
stmt.execute(String.format(instancesCaseIdIdx, instance_type_dbname, instance_type_dbname)); stmt.execute(String.format(instancesCaseIdIdx, instance_type_dbname, instance_type_dbname));
stmt.execute(String.format(instancesDatasourceIdIdx, instance_type_dbname, instance_type_dbname)); stmt.execute(String.format(instancesDatasourceIdIdx, instance_type_dbname, instance_type_dbname));
stmt.execute(String.format(instancesValueIdx, instance_type_dbname, instance_type_dbname)); stmt.execute(String.format(instancesValueIdx, instance_type_dbname, instance_type_dbname));
@ -193,7 +200,7 @@ public class RdbmsCentralRepoFactory {
result = CentralRepoDbUtil.insertDefaultCorrelationTypes(conn) result = CentralRepoDbUtil.insertDefaultCorrelationTypes(conn)
&& CentralRepoDbUtil.insertDefaultOrganization(conn) && && CentralRepoDbUtil.insertDefaultOrganization(conn) &&
insertDefaultAccountsTablesContent(conn); RdbmsCentralRepoFactory.insertDefaultAccountsTablesContent(conn, selectedPlatform );
// @TODO: uncomment when ready to create/populate persona tables // @TODO: uncomment when ready to create/populate persona tables
// && insertDefaultPersonaTablesContent(conn); // && insertDefaultPersonaTablesContent(conn);
@ -357,7 +364,7 @@ public class RdbmsCentralRepoFactory {
+ ")"; + ")";
} }
/** /**
* Get the template String for creating a new _instances table in a Sqlite * Get the template String for creating a new _instances table for non account artifacts in
* central repository. %s will exist in the template where the name of the * central repository. %s will exist in the template where the name of the
* new table will be added. * new table will be added.
* *
@ -365,6 +372,31 @@ public class RdbmsCentralRepoFactory {
*/ */
static String getCreateArtifactInstancesTableTemplate(CentralRepoPlatforms selectedPlatform) { static String getCreateArtifactInstancesTableTemplate(CentralRepoPlatforms selectedPlatform) {
// Each "%s" will be replaced with the relevant TYPE_instances table name. // Each "%s" will be replaced with the relevant TYPE_instances table name.
return "CREATE TABLE IF NOT EXISTS %s ("
+ getNumericPrimaryKeyClause("id", selectedPlatform)
+ "case_id integer NOT NULL,"
+ "data_source_id integer NOT 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 (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)";
}
/**
* Get the template String for creating a new _instances table for Accounts in
* central repository. %s will exist in the template where the name of the
* new table will be added.
*
* @return a String which is a template for creating a _instances table
*/
static String getCreateAccountInstancesTableTemplate(CentralRepoPlatforms selectedPlatform) {
// Each "%s" will be replaced with the relevant TYPE_instances table name.
return "CREATE TABLE IF NOT EXISTS %s (" return "CREATE TABLE IF NOT EXISTS %s ("
+ getNumericPrimaryKeyClause("id", selectedPlatform) + getNumericPrimaryKeyClause("id", selectedPlatform)
+ "case_id integer NOT NULL," + "case_id integer NOT NULL,"
@ -532,7 +564,7 @@ public class RdbmsCentralRepoFactory {
* *
* @return SQL clause. * @return SQL clause.
*/ */
private static String getBigIntType(CentralRepoPlatforms selectedPlatform) { static String getBigIntType(CentralRepoPlatforms selectedPlatform) {
switch (selectedPlatform) { switch (selectedPlatform) {
case POSTGRESQL: case POSTGRESQL:
return " BIGINT "; return " BIGINT ";
@ -766,33 +798,6 @@ public class RdbmsCentralRepoFactory {
} }
/**
* 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. * Inserts the default content in persona related tables.
* *
@ -800,7 +805,7 @@ public class RdbmsCentralRepoFactory {
* *
* @return True if success, false otherwise. * @return True if success, false otherwise.
*/ */
private boolean insertDefaultPersonaTablesContent(Connection conn) { private static boolean insertDefaultPersonaTablesContent(Connection conn, CentralRepoPlatforms selectedPlatform) {
try (Statement stmt = conn.createStatement()) { try (Statement stmt = conn.createStatement()) {
// populate the confidence table // populate the confidence table
@ -825,6 +830,37 @@ public class RdbmsCentralRepoFactory {
return true; return true;
} }
/**
* Inserts the default content in accounts related tables.
*
* @param conn Database connection to use.
*
* @return True if success, false otherwise.
*/
static boolean insertDefaultAccountsTablesContent(Connection conn, CentralRepoPlatforms selectedPlatform) {
try (Statement stmt = conn.createStatement();) {
// Populate the account_types table
for (Account.Type type : Account.Type.PREDEFINED_ACCOUNT_TYPES) {
if (type != Account.Type.DEVICE) {
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 account_types table."), ex);
return false;
}
return true;
}
/** /**
* Returns the correlation type id for the given account type, * Returns the correlation type id for the given account type,
* from the correlation_types table. * from the correlation_types table.
@ -834,7 +870,7 @@ public class RdbmsCentralRepoFactory {
* ' * '
* @return correlation type id. * @return correlation type id.
*/ */
private int getCorrelationTypeIdForAccountType(Connection conn, Account.Type accountType) { static int getCorrelationTypeIdForAccountType(Connection conn, Account.Type accountType) {
int typeId = -1; int typeId = -1;
if (accountType == Account.Type.EMAIL) { if (accountType == Account.Type.EMAIL) {

View File

@ -37,7 +37,7 @@ import org.sleuthkit.autopsy.coreutils.PlatformUtil;
* NOTE: This is public scope because the options panel calls it directly to * NOTE: This is public scope because the options panel calls it directly to
* set/get * set/get
*/ */
public final class SqliteCentralRepoSettings implements CentralRepoDbSettings { public final class SqliteCentralRepoSettings implements CentralRepoDbConnectivityManager {
public final static String DEFAULT_DBNAME = "central_repository.db"; // NON-NLS public final static String DEFAULT_DBNAME = "central_repository.db"; // NON-NLS
private final static Logger LOGGER = Logger.getLogger(SqliteCentralRepoSettings.class.getName()); private final static Logger LOGGER = Logger.getLogger(SqliteCentralRepoSettings.class.getName());
@ -115,13 +115,11 @@ public final class SqliteCentralRepoSettings implements CentralRepoDbSettings {
return (!dbFile.isDirectory()); return (!dbFile.isDirectory());
} }
@Override @Override
public boolean verifyDatabaseExists() { public boolean verifyDatabaseExists() {
return dbDirectoryExists(); return dbDirectoryExists();
} }
/** /**
* Verify that the db directory path exists. * Verify that the db directory path exists.
* *
@ -143,6 +141,7 @@ public final class SqliteCentralRepoSettings implements CentralRepoDbSettings {
/** /**
* creates database directory for sqlite database if it does not exist * creates database directory for sqlite database if it does not exist
*
* @return whether or not operation occurred successfully * @return whether or not operation occurred successfully
*/ */
@Override @Override
@ -150,7 +149,6 @@ public final class SqliteCentralRepoSettings implements CentralRepoDbSettings {
return createDbDirectory(); return createDbDirectory();
} }
/** /**
* Create the db directory if it does not exist. * Create the db directory if it does not exist.
* *
@ -354,4 +352,21 @@ public final class SqliteCentralRepoSettings implements CentralRepoDbSettings {
String getJDBCBaseURI() { String getJDBCBaseURI() {
return JDBC_BASE_URI; return JDBC_BASE_URI;
} }
@Override
public DatabaseTestResult testStatus() {
if (dbFileExists()) {
if (verifyConnection()) {
if (verifyDatabaseSchema()) {
return DatabaseTestResult.TESTED_OK;
} else {
return DatabaseTestResult.SCHEMA_INVALID;
}
} else {
return DatabaseTestResult.SCHEMA_INVALID;
}
} else {
return DatabaseTestResult.DB_DOES_NOT_EXIST;
}
}
} }

View File

@ -33,6 +33,7 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationDataSource;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeUtil; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeUtil;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoPlatforms; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoPlatforms;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoDbManager;
import org.sleuthkit.autopsy.centralrepository.eventlisteners.IngestEventsListener; import org.sleuthkit.autopsy.centralrepository.eventlisteners.IngestEventsListener;
import org.sleuthkit.autopsy.core.RuntimeProperties; import org.sleuthkit.autopsy.core.RuntimeProperties;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
@ -274,7 +275,7 @@ final class CentralRepoIngestModule implements FileIngestModule {
// Don't allow sqlite central repo databases to be used for multi user cases // Don't allow sqlite central repo databases to be used for multi user cases
if ((autopsyCase.getCaseType() == Case.CaseType.MULTI_USER_CASE) if ((autopsyCase.getCaseType() == Case.CaseType.MULTI_USER_CASE)
&& (CentralRepoPlatforms.getSelectedPlatform() == CentralRepoPlatforms.SQLITE)) { && (CentralRepoDbManager.getSavedDbChoice().getDbPlatform() == CentralRepoPlatforms.SQLITE)) {
logger.log(Level.SEVERE, "Cannot run correlation engine on a multi-user case with a SQLite central repository."); logger.log(Level.SEVERE, "Cannot run correlation engine on a multi-user case with a SQLite central repository.");
throw new IngestModuleException("Cannot run on a multi-user case with a SQLite central repository."); // NON-NLS throw new IngestModuleException("Cannot run on a multi-user case with a SQLite central repository."); // NON-NLS
} }

View File

@ -19,7 +19,7 @@ EamDbSettingsDialog.okButton.createDbDialog.title=Database Does Not Exist
EamDbSettingsDialog.okButton.createDbError.title=Unable to Create Database EamDbSettingsDialog.okButton.createDbError.title=Unable to Create Database
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.createPostgresDbError.message=Unable to create Postgres Database, please ensure address, port, and login credentials are correct for Postgres server 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.createSQLiteDbError.message=Unable to create SQLite Database, please ensure location exists and you have write permissions and try again.
EamDbSettingsDialog.okButton.databaseConnectionFailed.message=Unable to connect to database please check your settings and try again. EamDbSettingsDialog.okButton.databaseConnectionFailed.message=Unable to connect to database. Please check your settings and try again.
EamDbSettingsDialog.okButton.databaseConnectionFailed.title=Database Connection Failed EamDbSettingsDialog.okButton.databaseConnectionFailed.title=Database Connection Failed
EamDbSettingsDialog.okButton.errorMsg.text=Please restart Autopsy to begin using the new database platform. EamDbSettingsDialog.okButton.errorMsg.text=Please restart Autopsy to begin using the new database platform.
EamDbSettingsDialog.okButton.errorTitle.text=Restart Required. EamDbSettingsDialog.okButton.errorTitle.text=Restart Required.
@ -33,6 +33,12 @@ EamDbSettingsDialog.validation.finished=Click OK to save your database settings
EamDbSettingsDialog.validation.incompleteFields=Fill in all values for the selected database. EamDbSettingsDialog.validation.incompleteFields=Fill in all values for the selected database.
EamOptionsController.moduleErr=Error processing value changes. EamOptionsController.moduleErr=Error processing value changes.
EamOptionsController.moduleErr.msg=Value change processing failed. EamOptionsController.moduleErr.msg=Value change processing failed.
GlobalSettingsPanel.onMultiUserChange.disabledMu.description=The Central Repository will be reconfigured to use a local SQLite database.
GlobalSettingsPanel.onMultiUserChange.disabledMu.description2=Press Configure PostgreSQL to change to a PostgreSQL database.
GlobalSettingsPanel.onMultiUserChange.disabledMu.title=Central Repository Change Necessary
GlobalSettingsPanel.onMultiUserChange.enable.description=Do you want to update the Central Repository to use this PostgreSQL database?
GlobalSettingsPanel.onMultiUserChange.enable.description2=The Central Repository stores hash values and accounts from past cases.
GlobalSettingsPanel.onMultiUserChange.enable.title=Use with Central Repository?
GlobalSettingsPanel.updateFailed.title=Central repository disabled GlobalSettingsPanel.updateFailed.title=Central repository disabled
GlobalSettingsPanel.validationErrMsg.ingestRunning=You cannot change settings while ingest is running. GlobalSettingsPanel.validationErrMsg.ingestRunning=You cannot change settings while ingest is running.
GlobalSettingsPanel.validationerrMsg.mustConfigure=Configure the database to enable this module. GlobalSettingsPanel.validationerrMsg.mustConfigure=Configure the database to enable this module.

View File

@ -138,16 +138,16 @@
</Group> </Group>
</Group> </Group>
<EmptySpace min="-2" pref="10" max="-2" attributes="0"/> <EmptySpace min="-2" pref="10" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" max="-2" attributes="0">
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0">
<Component id="tfDatabasePath" min="-2" max="-2" attributes="0"/> <Component id="tfDatabasePath" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/> <EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="bnDatabasePathFileOpen" min="-2" max="-2" attributes="0"/> <Component id="bnDatabasePathFileOpen" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0">
<Component id="cbDatabaseType" min="-2" max="-2" attributes="0"/> <Component id="cbDatabaseType" min="-2" pref="210" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/> <EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="lbSingleUserSqLite" min="-2" max="-2" attributes="0"/> <Component id="lbSingleUserSqLite" pref="0" max="32767" attributes="0"/>
</Group> </Group>
<Component id="jpDbPassword" alignment="0" min="-2" max="-2" attributes="0"/> <Component id="jpDbPassword" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="tbDbUsername" alignment="0" min="-2" max="-2" attributes="0"/> <Component id="tbDbUsername" alignment="0" min="-2" max="-2" attributes="0"/>
@ -320,7 +320,7 @@
<Component class="javax.swing.JComboBox" name="cbDatabaseType"> <Component class="javax.swing.JComboBox" name="cbDatabaseType">
<Properties> <Properties>
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor"> <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new javax.swing.DefaultComboBoxModel&lt;&gt;(new org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoPlatforms[]{org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoPlatforms.POSTGRESQL, org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoPlatforms.SQLITE})" type="code"/> <Connection code="new javax.swing.DefaultComboBoxModel&lt;&gt;(org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoDbChoice.DB_CHOICES)" type="code"/>
</Property> </Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[120, 20]"/> <Dimension value="[120, 20]"/>
@ -330,7 +330,7 @@
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cbDatabaseTypeActionPerformed"/> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cbDatabaseTypeActionPerformed"/>
</Events> </Events>
<AuxValues> <AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoPlatforms&gt;"/> <AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoDbChoice&gt;"/>
</AuxValues> </AuxValues>
</Component> </Component>
<Component class="javax.swing.JLabel" name="lbSingleUserSqLite"> <Component class="javax.swing.JLabel" name="lbSingleUserSqLite">

View File

@ -19,17 +19,24 @@
package org.sleuthkit.autopsy.centralrepository.optionspanel; package org.sleuthkit.autopsy.centralrepository.optionspanel;
import java.awt.Color; import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor; import java.awt.Cursor;
import java.awt.HeadlessException;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.JDialog; import javax.swing.JDialog;
import javax.swing.JFileChooser; import javax.swing.JFileChooser;
import javax.swing.JFrame; import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import javax.swing.JTextField; import javax.swing.JTextField;
import javax.swing.ListCellRenderer;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener; import javax.swing.event.DocumentListener;
@ -38,6 +45,7 @@ import org.netbeans.spi.options.OptionsPanelController;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.openide.windows.WindowManager; import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoDbChoice;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoDbManager; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoDbManager;
import org.sleuthkit.autopsy.corecomponents.TextPrompt; import org.sleuthkit.autopsy.corecomponents.TextPrompt;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
@ -45,8 +53,6 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoPlatforms; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoPlatforms;
import org.sleuthkit.autopsy.centralrepository.datamodel.DatabaseTestResult; import org.sleuthkit.autopsy.centralrepository.datamodel.DatabaseTestResult;
import org.sleuthkit.autopsy.centralrepository.datamodel.SqliteCentralRepoSettings; import org.sleuthkit.autopsy.centralrepository.datamodel.SqliteCentralRepoSettings;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.centralrepository.datamodel.RdbmsCentralRepoFactory;
/** /**
* Configuration dialog for Central Repository database settings. * Configuration dialog for Central Repository database settings.
@ -57,9 +63,40 @@ public class EamDbSettingsDialog extends JDialog {
private static final Logger logger = Logger.getLogger(EamDbSettingsDialog.class.getName()); private static final Logger logger = Logger.getLogger(EamDbSettingsDialog.class.getName());
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/**
* This class handles displaying and rendering drop down menu for database choices in central repo.
*/
private class DbChoiceRenderer extends JLabel implements ListCellRenderer<CentralRepoDbChoice>, Serializable {
private static final long serialVersionUID = 1L;
@Override
public Component getListCellRendererComponent(
JList<? extends CentralRepoDbChoice> list, CentralRepoDbChoice value,
int index, boolean isSelected, boolean cellHasFocus) {
// disable cell if it is the db connection from multi user settings
// and that option is not enabled in multi user settings
setText(value.getTitle());
setEnabled(isDbChoiceSelectable(value));
return this;
}
}
private final Collection<JTextField> textBoxes; private final Collection<JTextField> textBoxes;
private final TextBoxChangedListener textBoxChangedListener; private final TextBoxChangedListener textBoxChangedListener;
private final CentralRepoDbManager manager = new CentralRepoDbManager(); private final CentralRepoDbManager manager = new CentralRepoDbManager();
private final boolean isMultiUserSelectable = CentralRepoDbManager.isPostgresMultiuserAllowed();
private final DbChoiceRenderer DB_CHOICE_RENDERER = new DbChoiceRenderer();
public EamDbSettingsDialog() {
this(null);
}
private boolean isDbChoiceSelectable(CentralRepoDbChoice item) {
return (item != CentralRepoDbChoice.POSTGRESQL_MULTIUSER || isMultiUserSelectable);
}
/** /**
@ -69,7 +106,7 @@ public class EamDbSettingsDialog extends JDialog {
"EamDbSettingsDialog.lbSingleUserSqLite.text=SQLite should only be used by one examiner at a time.", "EamDbSettingsDialog.lbSingleUserSqLite.text=SQLite should only be used by one examiner at a time.",
"EamDbSettingsDialog.lbDatabaseType.text=Database Type :", "EamDbSettingsDialog.lbDatabaseType.text=Database Type :",
"EamDbSettingsDialog.fcDatabasePath.title=Select location for central_repository.db"}) "EamDbSettingsDialog.fcDatabasePath.title=Select location for central_repository.db"})
public EamDbSettingsDialog() { public EamDbSettingsDialog(CentralRepoDbChoice initialMenuItem) {
super((JFrame) WindowManager.getDefault().getMainWindow(), super((JFrame) WindowManager.getDefault().getMainWindow(),
Bundle.EamDbSettingsDialog_title_text(), Bundle.EamDbSettingsDialog_title_text(),
true); true);
@ -95,29 +132,43 @@ public class EamDbSettingsDialog extends JDialog {
return "Directories and Central Repository databases"; return "Directories and Central Repository databases";
} }
}); });
cbDatabaseType.setSelectedItem(manager.getSelectedPlatform());
customizeComponents(); setupDbChoice(initialMenuItem);
valid(); valid();
display(); display();
}
private void setupDbChoice(CentralRepoDbChoice initialMenuItem) {
// setup initially selected item
CentralRepoDbChoice toSelect = (initialMenuItem == null) ?
(Arrays.asList(CentralRepoDbChoice.DB_CHOICES).contains(manager.getSelectedDbChoice())) ?
manager.getSelectedDbChoice() :
CentralRepoDbChoice.DB_CHOICES[0] :
initialMenuItem;
cbDatabaseType.setRenderer(DB_CHOICE_RENDERER);
changeDbSelection(toSelect);
} }
/** /**
* prompts user based on testing status (i.e. failure to connect, invalid schema, db does not exist, etc.) * This method 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 * @param manager The manager to use when setting up the database.
* @param dialog If non-null value, validates settings and updates 'okay' button enabled state.
* @return Whether or not the ultimate status after prompts is okay to continue.
*/ */
@NbBundle.Messages({"EamDbSettingsDialog.okButton.corruptDatabaseExists.title=Error Loading Database", @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.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.title=Database Does Not Exist",
"EamDbSettingsDialog.okButton.createDbDialog.message=Database does not exist, would you like to create it?", "EamDbSettingsDialog.okButton.createDbDialog.message=Database does not exist, would you like to create it?",
"EamDbSettingsDialog.okButton.databaseConnectionFailed.title=Database Connection Failed", "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.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.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.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"}) "EamDbSettingsDialog.okButton.createDbError.title=Unable to Create Database"})
private boolean promptTestStatusWarnings() { private static boolean promptTestStatusWarnings(CentralRepoDbManager manager, EamDbSettingsDialog dialog) {
if (manager.getStatus() == DatabaseTestResult.CONNECTION_FAILED) { if (manager.getStatus() == DatabaseTestResult.CONNECTION_FAILED) {
JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
Bundle.EamDbSettingsDialog_okButton_databaseConnectionFailed_message(), Bundle.EamDbSettingsDialog_okButton_databaseConnectionFailed_message(),
@ -135,37 +186,50 @@ public class EamDbSettingsDialog extends JDialog {
Bundle.EamDbSettingsDialog_okButton_createDbDialog_message(), Bundle.EamDbSettingsDialog_okButton_createDbDialog_message(),
Bundle.EamDbSettingsDialog_okButton_createDbDialog_title(), Bundle.EamDbSettingsDialog_okButton_createDbDialog_title(),
JOptionPane.YES_NO_OPTION)) { JOptionPane.YES_NO_OPTION)) {
try { onUserPromptCreateDb(manager, dialog);
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;
} }
return (manager.getStatus() == DatabaseTestResult.TESTED_OK);
}
/**
* When a new database needs to be created on user selecting cr, this code will be ran when user selects create cr.
* @param manager The manager handling the database creation.
* @param dialog The dialog that prompted database creation.
*/
private static void onUserPromptCreateDb(CentralRepoDbManager manager, EamDbSettingsDialog dialog) {
try {
manager.createDb();
} catch (CentralRepoException e) {
onPromptStatusError(manager);
}
if (dialog != null)
dialog.valid();
}
/**
* When an error occurs while going through promptTestStatusWarning, this method is called.
* @param manager1 The manager to use as service class.
* @throws HeadlessException
*/
private static void onPromptStatusError(CentralRepoDbManager manager1) {
// in the event that there is a failure to connect, notify user with corresponding message
String errorMessage = "";
if (manager1 == null || manager1.getSelectedDbChoice() == null) {
errorMessage = "";
} else if (manager1.getSelectedDbChoice().getDbPlatform() == CentralRepoPlatforms.POSTGRESQL) {
errorMessage = Bundle.EamDbSettingsDialog_okButton_createPostgresDbError_message();
} else if (manager1.getSelectedDbChoice().getDbPlatform() == CentralRepoPlatforms.SQLITE) {
errorMessage = Bundle.EamDbSettingsDialog_okButton_createSQLiteDbError_message();
}
JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
errorMessage, errorMessage,
Bundle.EamDbSettingsDialog_okButton_createDbError_title(), Bundle.EamDbSettingsDialog_okButton_createDbError_title(),
JOptionPane.WARNING_MESSAGE); JOptionPane.WARNING_MESSAGE);
} }
valid();
}
}
return (manager.getStatus() == DatabaseTestResult.TESTEDOK);
}
/** /**
* This method is called from within the constructor to initialize the form. * This method is called from within the constructor to initialize the form.
@ -281,7 +345,7 @@ public class EamDbSettingsDialog extends JDialog {
jpDbPassword.setPreferredSize(new java.awt.Dimension(509, 20)); jpDbPassword.setPreferredSize(new java.awt.Dimension(509, 20));
cbDatabaseType.setModel(new javax.swing.DefaultComboBoxModel<>(new CentralRepoPlatforms[]{CentralRepoPlatforms.POSTGRESQL, CentralRepoPlatforms.SQLITE})); cbDatabaseType.setModel(new javax.swing.DefaultComboBoxModel<>(org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoDbChoice.DB_CHOICES));
cbDatabaseType.setPreferredSize(new java.awt.Dimension(120, 20)); cbDatabaseType.setPreferredSize(new java.awt.Dimension(120, 20));
cbDatabaseType.addActionListener(new java.awt.event.ActionListener() { cbDatabaseType.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) { public void actionPerformed(java.awt.event.ActionEvent evt) {
@ -323,18 +387,18 @@ public class EamDbSettingsDialog extends JDialog {
.addComponent(lbUserName, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(lbUserName, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(lbPort, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(lbPort, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGroup(pnSQLiteSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) .addGroup(pnSQLiteSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
.addComponent(lbDatabaseDesc, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(lbDatabaseDesc, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 94, Short.MAX_VALUE)
.addComponent(lbUserPassword, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))) .addComponent(lbUserPassword, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
.addGap(10, 10, 10) .addGap(10, 10, 10)
.addGroup(pnSQLiteSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(pnSQLiteSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
.addGroup(pnSQLiteSettingsLayout.createSequentialGroup() .addGroup(pnSQLiteSettingsLayout.createSequentialGroup()
.addComponent(tfDatabasePath, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(tfDatabasePath, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(bnDatabasePathFileOpen)) .addComponent(bnDatabasePathFileOpen))
.addGroup(pnSQLiteSettingsLayout.createSequentialGroup() .addGroup(pnSQLiteSettingsLayout.createSequentialGroup()
.addComponent(cbDatabaseType, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(cbDatabaseType, javax.swing.GroupLayout.PREFERRED_SIZE, 210, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(lbSingleUserSqLite, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addComponent(lbSingleUserSqLite, javax.swing.GroupLayout.PREFERRED_SIZE, 1, Short.MAX_VALUE))
.addComponent(jpDbPassword, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(jpDbPassword, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(tbDbUsername, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(tbDbUsername, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(tbDbPort, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(tbDbPort, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
@ -414,15 +478,20 @@ public class EamDbSettingsDialog extends JDialog {
setTextPrompts(); setTextPrompts();
setTextBoxListeners(); setTextBoxListeners();
manager.clearStatus(); manager.clearStatus();
if (manager.getSelectedPlatform() == CentralRepoPlatforms.SQLITE) { if (manager.getSelectedDbChoice() == CentralRepoDbChoice.SQLITE) {
updatePostgresFields(false); updatePostgresFields(false);
updateSqliteFields(true); updateSqliteFields(true);
} }
else { else if (manager.getSelectedDbChoice() == CentralRepoDbChoice.POSTGRESQL_CUSTOM) {
updatePostgresFields(true); updatePostgresFields(true);
updateSqliteFields(false); updateSqliteFields(false);
} }
displayDatabaseSettings(CentralRepoPlatforms.POSTGRESQL.equals(manager.getSelectedPlatform())); else {
updatePostgresFields(false);
updateSqliteFields(false);
}
displayDatabaseSettings(manager.getSelectedDbChoice());
} }
private void display() { private void display() {
@ -452,14 +521,40 @@ public class EamDbSettingsDialog extends JDialog {
"EamDbSettingsDialog.okButton.errorMsg.text=Please restart Autopsy to begin using the new database platform.", "EamDbSettingsDialog.okButton.errorMsg.text=Please restart Autopsy to begin using the new database platform.",
"EamDbSettingsDialog.okButton.connectionErrorMsg.text=Failed to connect to central repository database."}) "EamDbSettingsDialog.okButton.connectionErrorMsg.text=Failed to connect to central repository database."})
private void bnOkActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnOkActionPerformed private void bnOkActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnOkActionPerformed
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); testStatusAndCreate(this, manager, this);
manager.testStatus(); dispose();
valid(); }//GEN-LAST:event_bnOkActionPerformed
boolean testedOk = promptTestStatusWarnings();
/**
* This method tests status for central repo db / creation and prompts user accordingly.
* @param parent The parent component (the anchor for displaying dialogs).
* @param manager The central repo db manager with settings to be tested and saved.
* @return Whether or not central repo db was successfully be created or found.
*/
public static boolean testStatusAndCreate(Component parent, CentralRepoDbManager manager) {
return testStatusAndCreate(parent, manager, null);
}
/**
* This method tests status for central repo db / creation and prompts user accordingly.
* @param parent The parent component (the anchor for displaying dialogs).
* @param manager The central repo db manager with settings to be tested and saved.
* @param dialog The db settings dialog; if non-null, will validate okay button state.
* @return Whether or not central repo db was successfully be created or found.
*/
private static boolean testStatusAndCreate(Component parent, CentralRepoDbManager manager, EamDbSettingsDialog dialog) {
parent.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
manager.testStatus();
if (dialog != null)
dialog.valid();
boolean testedOk = promptTestStatusWarnings(manager, dialog);
if (!testedOk) { if (!testedOk) {
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); parent.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
return; return false;
} }
try{ try{
@ -467,25 +562,26 @@ public class EamDbSettingsDialog extends JDialog {
} }
catch (CentralRepoException e) { catch (CentralRepoException e) {
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(() -> {
JOptionPane.showMessageDialog(this, JOptionPane.showMessageDialog(parent,
Bundle.EamDbSettingsDialog_okButton_errorMsg_text(), Bundle.EamDbSettingsDialog_okButton_errorMsg_text(),
Bundle.EamDbSettingsDialog_okButton_errorTitle_text(), Bundle.EamDbSettingsDialog_okButton_errorTitle_text(),
JOptionPane.WARNING_MESSAGE); JOptionPane.WARNING_MESSAGE);
}); });
parent.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
return false;
} }
parent.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); return true;
dispose(); }
}//GEN-LAST:event_bnOkActionPerformed
/** /**
* Returns if changes to the central repository configuration were * This method returns if changes to the central repository configuration were
* successfully applied * successfully applied.
* *
* @return true if the database configuration was successfully changed false * @return True if the database configuration was successfully changed; false
* if it was not * if it was not.
*/ */
public boolean wasConfigurationChanged() { public boolean wasConfigurationChanged() {
return manager.wasConfigurationChanged(); return manager.wasConfigurationChanged();
@ -497,22 +593,38 @@ public class EamDbSettingsDialog extends JDialog {
private void cbDatabaseTypeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbDatabaseTypeActionPerformed private void cbDatabaseTypeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbDatabaseTypeActionPerformed
manager.setSelectedPlatform((CentralRepoPlatforms) cbDatabaseType.getSelectedItem()); CentralRepoDbChoice selectedItem = (CentralRepoDbChoice) cbDatabaseType.getSelectedItem();
customizeComponents(); changeDbSelection(selectedItem);
}//GEN-LAST:event_cbDatabaseTypeActionPerformed }//GEN-LAST:event_cbDatabaseTypeActionPerformed
private void changeDbSelection(CentralRepoDbChoice selectedItem) {
if (isDbChoiceSelectable(selectedItem)) {
manager.setSelctedDbChoice(selectedItem);
cbDatabaseType.setSelectedItem(selectedItem);
}
else {
cbDatabaseType.setSelectedItem(manager.getSelectedDbChoice());
}
customizeComponents();
}
private void updateFullDbPath() { private void updateFullDbPath() {
dataBaseFileTextArea.setText(tfDatabasePath.getText() + File.separator + SqliteCentralRepoSettings.DEFAULT_DBNAME); dataBaseFileTextArea.setText(tfDatabasePath.getText() + File.separator + SqliteCentralRepoSettings.DEFAULT_DBNAME);
dataBaseFileTextArea.setCaretPosition(dataBaseFileTextArea.getText().length()); dataBaseFileTextArea.setCaretPosition(dataBaseFileTextArea.getText().length());
} }
private void displayDatabaseSettings(boolean isPostgres) { private void displayDatabaseSettings(CentralRepoDbChoice choice) {
lbDatabasePath.setVisible(!isPostgres); boolean isSqlite = choice == CentralRepoDbChoice.SQLITE;
tfDatabasePath.setVisible(!isPostgres); boolean isPostgres = choice == CentralRepoDbChoice.POSTGRESQL_CUSTOM;
lbDatabaseDesc.setVisible(!isPostgres);
dataBaseFileTextArea.setVisible(!isPostgres); lbDatabasePath.setVisible(isSqlite);
lbSingleUserSqLite.setVisible(!isPostgres); tfDatabasePath.setVisible(isSqlite);
bnDatabasePathFileOpen.setVisible(!isPostgres); lbDatabaseDesc.setVisible(isSqlite);
dataBaseFileTextArea.setVisible(isSqlite);
lbSingleUserSqLite.setVisible(isSqlite);
bnDatabasePathFileOpen.setVisible(isSqlite);
lbHostName.setVisible(isPostgres); lbHostName.setVisible(isPostgres);
tbDbHostname.setVisible(isPostgres); tbDbHostname.setVisible(isPostgres);
lbPort.setVisible(isPostgres); lbPort.setVisible(isPostgres);
@ -610,14 +722,14 @@ public class EamDbSettingsDialog extends JDialog {
@Messages({"EamDbSettingsDialog.validation.incompleteFields=Fill in all values for the selected database."}) @Messages({"EamDbSettingsDialog.validation.incompleteFields=Fill in all values for the selected database."})
private boolean databaseFieldsArePopulated() { private boolean databaseFieldsArePopulated() {
boolean result = true; boolean result = true;
if (manager.getSelectedPlatform() == CentralRepoPlatforms.POSTGRESQL) { if (manager.getSelectedDbChoice() == CentralRepoDbChoice.POSTGRESQL_CUSTOM) {
result = !tbDbHostname.getText().trim().isEmpty() result = !tbDbHostname.getText().trim().isEmpty()
&& !tbDbPort.getText().trim().isEmpty() && !tbDbPort.getText().trim().isEmpty()
// && !tbDbName.getText().trim().isEmpty() // && !tbDbName.getText().trim().isEmpty()
&& !tbDbUsername.getText().trim().isEmpty() && !tbDbUsername.getText().trim().isEmpty()
&& 0 < jpDbPassword.getPassword().length; && 0 < jpDbPassword.getPassword().length;
} }
else if (manager.getSelectedPlatform() == CentralRepoPlatforms.SQLITE) { else if (manager.getSelectedDbChoice() == CentralRepoDbChoice.SQLITE) {
result = !tfDatabasePath.getText().trim().isEmpty(); result = !tfDatabasePath.getText().trim().isEmpty();
} }
@ -722,7 +834,7 @@ public class EamDbSettingsDialog extends JDialog {
private javax.swing.JButton bnDatabasePathFileOpen; private javax.swing.JButton bnDatabasePathFileOpen;
private javax.swing.ButtonGroup bnGrpDatabasePlatforms; private javax.swing.ButtonGroup bnGrpDatabasePlatforms;
private javax.swing.JButton bnOk; private javax.swing.JButton bnOk;
private javax.swing.JComboBox<CentralRepoPlatforms> cbDatabaseType; private javax.swing.JComboBox<org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoDbChoice> cbDatabaseType;
private javax.swing.JScrollPane dataBaseFileScrollPane; private javax.swing.JScrollPane dataBaseFileScrollPane;
private javax.swing.JTextArea dataBaseFileTextArea; private javax.swing.JTextArea dataBaseFileTextArea;
private javax.swing.JFileChooser fcDatabasePath; private javax.swing.JFileChooser fcDatabasePath;

View File

@ -61,7 +61,7 @@
<Group type="102" attributes="0"> <Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Component id="pnDatabaseConfiguration" alignment="0" max="32767" attributes="0"/> <Component id="pnDatabaseConfiguration" alignment="0" max="32767" attributes="0"/>
<Component id="pnCorrelationProperties" alignment="0" pref="1012" max="32767" attributes="0"/> <Component id="pnCorrelationProperties" alignment="0" pref="1016" max="32767" attributes="0"/>
<Component id="organizationPanel" alignment="1" max="32767" attributes="0"/> <Component id="organizationPanel" alignment="1" max="32767" attributes="0"/>
<Component id="casesPanel" alignment="0" max="32767" attributes="0"/> <Component id="casesPanel" alignment="0" max="32767" attributes="0"/>
<Group type="102" attributes="0"> <Group type="102" attributes="0">
@ -258,7 +258,7 @@
<DimensionLayout dim="1"> <DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0"> <Group type="102" alignment="1" attributes="0">
<Component id="correlationPropertiesScrollPane" pref="32" max="32767" attributes="0"/> <Component id="correlationPropertiesScrollPane" pref="25" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="bnManageTypes" min="-2" max="-2" attributes="0"/> <Component id="bnManageTypes" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="8" max="-2" attributes="0"/> <EmptySpace min="-2" pref="8" max="-2" attributes="0"/>

View File

@ -33,15 +33,17 @@ import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoDbManager; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoDbManager;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoPlatforms;
import org.sleuthkit.autopsy.corecomponents.OptionsPanel; import org.sleuthkit.autopsy.corecomponents.OptionsPanel;
import org.sleuthkit.autopsy.events.AutopsyEvent; import org.sleuthkit.autopsy.events.AutopsyEvent;
import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.IngestModuleGlobalSettingsPanel; import org.sleuthkit.autopsy.ingest.IngestModuleGlobalSettingsPanel;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoPlatforms; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoDbChoice;
import static org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoPlatforms.DISABLED;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoDbUtil; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoDbUtil;
import org.sleuthkit.autopsy.centralrepository.datamodel.PostgresCentralRepoSettings; import org.sleuthkit.autopsy.centralrepository.datamodel.PostgresCentralRepoSettings;
import org.sleuthkit.autopsy.centralrepository.datamodel.SqliteCentralRepoSettings; import org.sleuthkit.autopsy.centralrepository.datamodel.SqliteCentralRepoSettings;
import java.awt.Component;
import java.util.logging.Level;
/** /**
* Main settings panel for the Central Repository * Main settings panel for the Central Repository
@ -54,12 +56,16 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.STARTED, IngestManager.IngestJobEvent.CANCELLED, IngestManager.IngestJobEvent.COMPLETED); private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.STARTED, IngestManager.IngestJobEvent.CANCELLED, IngestManager.IngestJobEvent.COMPLETED);
private final IngestJobEventPropertyChangeListener ingestJobEventListener; private final IngestJobEventPropertyChangeListener ingestJobEventListener;
/** /**
* Creates new form EamOptionsPanel * Creates new form EamOptionsPanel
*/ */
public GlobalSettingsPanel() { public GlobalSettingsPanel() {
ingestJobEventListener = new IngestJobEventPropertyChangeListener(); ingestJobEventListener = new IngestJobEventPropertyChangeListener();
// listen for change events in currently saved choice
CentralRepoDbManager.addPropertyChangeListener((PropertyChangeEvent evt) -> ingestStateUpdated(Case.isCaseOpen()));
initComponents(); initComponents();
customizeComponents(); customizeComponents();
addIngestJobEventsListener(); addIngestJobEventsListener();
@ -69,6 +75,7 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
}); });
} }
private void customizeComponents() { private void customizeComponents() {
setName(NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.pnCorrelationProperties.border.title")); setName(NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.pnCorrelationProperties.border.title"));
} }
@ -78,26 +85,163 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
ingestStateUpdated(Case.isCaseOpen()); ingestStateUpdated(Case.isCaseOpen());
} }
@Messages({"GlobalSettingsPanel.updateFailed.title=Central repository disabled"})
private void updateDatabase() { private void updateDatabase() {
updateDatabase(this);
}
if (CentralRepoPlatforms.getSelectedPlatform().equals(DISABLED)) { /**
* This method invokes central repository database choice selection as well as input for necessary configuration.
* @param parent The parent component for displaying dialogs.
* @param initialSelection If non-null, the menu item will be set to this choice; if null,
* the currently selected db choice will be selected.
* @return True if there was a change.
*/
private static boolean invokeCrChoice(Component parent, CentralRepoDbChoice initialSelection) {
EamDbSettingsDialog dialog = (initialSelection != null) ?
new EamDbSettingsDialog(initialSelection) :
new EamDbSettingsDialog();
if (dialog.wasConfigurationChanged()) {
updateDatabase(parent);
return true;
}
return false;
}
/**
* When multi user settings are updated, this function triggers pertinent updates for central repository.
* NOTE: If multi user settings were previously enabled and multi user settings are currently selected, this function assumes
* there is a change in the postgres connectivity.
*
* @param parent The swing component that serves as a parent for dialogs that may arise.
* @param muPreviouslySelected If multi user settings were previously enabled.
* @param muCurrentlySelected If multi user settings are currently enabled as of most recent change.
*/
@NbBundle.Messages({
"GlobalSettingsPanel.onMultiUserChange.enable.title=Use with Central Repository?",
"GlobalSettingsPanel.onMultiUserChange.enable.description=Do you want to update the Central Repository to use this PostgreSQL database?",
"GlobalSettingsPanel.onMultiUserChange.enable.description2=The Central Repository stores hash values and accounts from past cases."
})
public static void onMultiUserChange(Component parent, boolean muPreviouslySelected, boolean muCurrentlySelected) {
boolean crEnabled = CentralRepoDbUtil.allowUseOfCentralRepository();
boolean crMultiUser = CentralRepoDbManager.getSavedDbChoice() == CentralRepoDbChoice.POSTGRESQL_MULTIUSER;
boolean crDisabledDueToFailure = CentralRepoDbManager.isDisabledDueToFailure();
if (!muPreviouslySelected && muCurrentlySelected) {
SwingUtilities.invokeLater(() -> {
if (JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(parent,
"<html><body>" +
"<div style='width: 400px;'>" +
"<p>" + NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.onMultiUserChange.enable.description") + "</p>" +
"<p style='margin-top: 10px'>" + NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.onMultiUserChange.enable.description2") + "</p>" +
"</div>" +
"</body></html>",
NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.onMultiUserChange.enable.title"),
JOptionPane.YES_NO_OPTION)) {
// setup database for CR
CentralRepoDbUtil.setUseCentralRepo(true);
CentralRepoDbManager.saveDbChoice(CentralRepoDbChoice.POSTGRESQL_MULTIUSER);
handleDbChange(parent);
}
});
}
// moving from selected to not selected && 'PostgreSQL using multi-user settings' is selected
else if (muPreviouslySelected && !muCurrentlySelected && crEnabled && crMultiUser) {
SwingUtilities.invokeLater(() -> {
askForCentralRepoDbChoice(parent);
});
}
// changing multi-user settings connection && 'PostgreSQL using multi-user settings' is selected &&
// central repo either enabled or was disabled due to error
else if (muPreviouslySelected && muCurrentlySelected && crMultiUser && (crEnabled || crDisabledDueToFailure)) {
// test databse for CR change
CentralRepoDbUtil.setUseCentralRepo(true);
handleDbChange(parent);
}
}
/**
* This method is called when a user must select a new database other than using database from multi user settings.
* @param parent The parent component to use for displaying dialogs in reference.
*/
@NbBundle.Messages({
"GlobalSettingsPanel.onMultiUserChange.disabledMu.title=Central Repository Change Necessary",
"GlobalSettingsPanel.onMultiUserChange.disabledMu.description=The Central Repository will be reconfigured to use a local SQLite database.",
"GlobalSettingsPanel.onMultiUserChange.disabledMu.description2=Press Configure PostgreSQL to change to a PostgreSQL database."
})
private static void askForCentralRepoDbChoice(Component parent) {
// disable central repository until user makes choice
CentralRepoDbUtil.setUseCentralRepo(false);
CentralRepoDbManager.saveDbChoice(CentralRepoDbChoice.DISABLED, false);
Object[] options = {
"Use SQLite",
"Configure PostgreSQL",
"Disable Central Repository"
};
int result = JOptionPane.showOptionDialog(
parent,
"<html><body>" +
"<div style='width: 400px;'>" +
"<p>" + NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.onMultiUserChange.disabledMu.description") + "</p>" +
"<p style='margin-top: 10px'>" + NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.onMultiUserChange.disabledMu.description2") + "</p>" +
"</div>" +
"</body></html>",
NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.onMultiUserChange.disabledMu.title"),
JOptionPane.YES_NO_CANCEL_OPTION,
JOptionPane.PLAIN_MESSAGE,
null,
options,
options[0]
);
if (JOptionPane.YES_OPTION == result) {
invokeCrChoice(parent, CentralRepoDbChoice.SQLITE);
}
else if (JOptionPane.NO_OPTION == result) {
invokeCrChoice(parent, CentralRepoDbChoice.POSTGRESQL_CUSTOM);
}
}
private static void handleDbChange(Component parent) {
SwingUtilities.invokeLater(() -> {
boolean successful = EamDbSettingsDialog.testStatusAndCreate(parent, new CentralRepoDbManager());
if (successful) {
updateDatabase(parent);
}
else {
// disable central repository due to error
CentralRepoDbManager.disableDueToFailure();
}
});
}
@Messages({"GlobalSettingsPanel.updateFailed.title=Central repository disabled"})
private static void updateDatabase(Component parent) {
if (CentralRepoDbChoice.DISABLED.equals(CentralRepoDbManager.getSavedDbChoice())) {
return; return;
} }
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); parent.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
try { try {
CentralRepoDbManager.upgradeDatabase(); CentralRepoDbManager.upgradeDatabase();
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); parent.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
} catch (CentralRepoException ex) { } catch (CentralRepoException ex) {
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); parent.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
JOptionPane.showMessageDialog(this, JOptionPane.showMessageDialog(parent,
ex.getUserMessage(), ex.getUserMessage(),
NbBundle.getMessage(this.getClass(), NbBundle.getMessage(GlobalSettingsPanel.class,
"GlobalSettingsPanel.updateFailed.title"), "GlobalSettingsPanel.updateFailed.title"),
JOptionPane.WARNING_MESSAGE); JOptionPane.WARNING_MESSAGE);
} finally { } finally {
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); parent.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
} }
} }
@ -425,9 +569,8 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
private void bnDbConfigureActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnDbConfigureActionPerformed private void bnDbConfigureActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnDbConfigureActionPerformed
store(); store();
EamDbSettingsDialog dialog = new EamDbSettingsDialog(); boolean changed = invokeCrChoice(this, null);
if (dialog.wasConfigurationChanged()) { if (changed) {
updateDatabase();
load(); // reload db settings content and update buttons load(); // reload db settings content and update buttons
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
} }
@ -446,6 +589,16 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
private void cbUseCentralRepoActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbUseCentralRepoActionPerformed private void cbUseCentralRepoActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbUseCentralRepoActionPerformed
//if saved setting is disabled checkbox should be disabled already //if saved setting is disabled checkbox should be disabled already
store(); store();
// if moving to using CR, multi-user mode is disabled and selection is multiuser settings, set to disabled
if (cbUseCentralRepo.isSelected() &&
!CentralRepoDbManager.isPostgresMultiuserAllowed() &&
CentralRepoDbManager.getSavedDbChoice() == CentralRepoDbChoice.POSTGRESQL_MULTIUSER) {
CentralRepoDbManager.saveDbChoice(CentralRepoDbChoice.DISABLED);
}
updateDatabase(); updateDatabase();
load(); load();
this.ingestStateUpdated(Case.isCaseOpen()); this.ingestStateUpdated(Case.isCaseOpen());
@ -457,31 +610,35 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
public void load() { public void load() {
tbOops.setText(""); tbOops.setText("");
enableButtonSubComponents(false); enableButtonSubComponents(false);
CentralRepoPlatforms selectedPlatform = CentralRepoPlatforms.getSelectedPlatform(); CentralRepoDbChoice selectedChoice = CentralRepoDbManager.getSavedDbChoice();
cbUseCentralRepo.setSelected(CentralRepoDbUtil.allowUseOfCentralRepository()); // NON-NLS cbUseCentralRepo.setSelected(CentralRepoDbUtil.allowUseOfCentralRepository()); // NON-NLS
switch (selectedPlatform) {
case POSTGRESQL: lbDbPlatformValue.setText(selectedChoice.getTitle());
PostgresCentralRepoSettings dbSettingsPg = new PostgresCentralRepoSettings(); CentralRepoPlatforms selectedDb = selectedChoice.getDbPlatform();
lbDbPlatformValue.setText(CentralRepoPlatforms.POSTGRESQL.toString());
lbDbNameValue.setText(dbSettingsPg.getDbName()); if (selectedChoice == null || selectedDb == CentralRepoPlatforms.DISABLED) {
lbDbLocationValue.setText(dbSettingsPg.getHost());
enableButtonSubComponents(cbUseCentralRepo.isSelected());
break;
case SQLITE:
SqliteCentralRepoSettings dbSettingsSqlite = new SqliteCentralRepoSettings();
lbDbPlatformValue.setText(CentralRepoPlatforms.SQLITE.toString());
lbDbNameValue.setText(dbSettingsSqlite.getDbName());
lbDbLocationValue.setText(dbSettingsSqlite.getDbDirectory());
enableButtonSubComponents(cbUseCentralRepo.isSelected());
break;
default:
lbDbPlatformValue.setText(CentralRepoPlatforms.DISABLED.toString());
lbDbNameValue.setText(""); lbDbNameValue.setText("");
lbDbLocationValue.setText(""); lbDbLocationValue.setText("");
tbOops.setText(Bundle.GlobalSettingsPanel_validationerrMsg_mustConfigure()); tbOops.setText(Bundle.GlobalSettingsPanel_validationerrMsg_mustConfigure());
break;
} }
else {
enableButtonSubComponents(cbUseCentralRepo.isSelected());
if (selectedDb == CentralRepoPlatforms.POSTGRESQL) {
try {
PostgresCentralRepoSettings dbSettingsPg = new PostgresCentralRepoSettings();
lbDbNameValue.setText(dbSettingsPg.getDbName());
lbDbLocationValue.setText(dbSettingsPg.getHost());
}
catch (CentralRepoException e) {
logger.log(Level.WARNING, "Unable to load settings into global panel for postgres settings", e);
}
}
else if (selectedDb == CentralRepoPlatforms.SQLITE) {
SqliteCentralRepoSettings dbSettingsSqlite = new SqliteCentralRepoSettings();
lbDbNameValue.setText(dbSettingsSqlite.getDbName());
lbDbLocationValue.setText(dbSettingsSqlite.getDbDirectory());
}
}
} }
@Override @Override
@ -490,12 +647,12 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
} }
/** /**
* Validates that the dialog/panel is filled out correctly for our usage. * This method validates that the dialog/panel is filled out correctly for our usage.
* *
* @return true if it's okay, false otherwise. * @return True if it is okay, false otherwise.
*/ */
public boolean valid() { public boolean valid() {
return !cbUseCentralRepo.isSelected() || !lbDbPlatformValue.getText().equals(DISABLED.toString()); return !cbUseCentralRepo.isSelected() || !lbDbPlatformValue.getText().equals(CentralRepoDbChoice.DISABLED.toString());
} }
@Override @Override
@ -574,9 +731,9 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
enableButtonSubComponents(cbUseCentralRepo.isSelected()); enableButtonSubComponents(cbUseCentralRepo.isSelected());
} else { } else {
load(); load();
enableDatabaseConfigureButton(cbUseCentralRepo.isSelected() && !caseIsOpen);
} }
enableDatabaseConfigureButton(cbUseCentralRepo.isSelected() && !caseIsOpen);
} }
/** /**

View File

@ -343,6 +343,7 @@ public class CommandLineIngestManager {
* @param baseCaseName Case name * @param baseCaseName Case name
* @param rootOutputDirectory Full path to directory in which case * @param rootOutputDirectory Full path to directory in which case
* output folder will be created * output folder will be created
* @param caseType Type of case being created
* *
* @throws CaseActionException * @throws CaseActionException
*/ */

View File

@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.communications.relationships; package org.sleuthkit.autopsy.communications.relationships;
import java.util.logging.Level; import java.util.logging.Level;
import org.apache.commons.lang.StringUtils;
import org.openide.nodes.Sheet; import org.openide.nodes.Sheet;
import org.sleuthkit.autopsy.communications.Utils; import org.sleuthkit.autopsy.communications.Utils;
import static org.sleuthkit.autopsy.communications.relationships.RelationshipsNodeUtilities.getAttributeDisplayString; import static org.sleuthkit.autopsy.communications.relationships.RelationshipsNodeUtilities.getAttributeDisplayString;
@ -67,14 +68,6 @@ final class CallLogNode extends BlackboardArtifactNode {
return sheet; return sheet;
} }
String phoneNumber = getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_FROM);
if(phoneNumber == null || phoneNumber.isEmpty()) {
phoneNumber = getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_TO);
}
if(phoneNumber == null || phoneNumber.isEmpty()) {
phoneNumber = getAttributeDisplayString(artifact, TSK_PHONE_NUMBER);
}
long duration = -1; long duration = -1;
try{ try{
duration = getCallDuration(artifact); duration = getCallDuration(artifact);
@ -84,7 +77,7 @@ final class CallLogNode extends BlackboardArtifactNode {
sheetSet.put(createNode(TSK_DATETIME_START, artifact)); sheetSet.put(createNode(TSK_DATETIME_START, artifact));
sheetSet.put(createNode(TSK_DIRECTION, artifact)); sheetSet.put(createNode(TSK_DIRECTION, artifact));
sheetSet.put(new NodeProperty<>(TSK_PHONE_NUMBER.getLabel(), TSK_PHONE_NUMBER.getDisplayName(), "", phoneNumber)); sheetSet.put(new NodeProperty<>(TSK_PHONE_NUMBER.getLabel(), TSK_PHONE_NUMBER.getDisplayName(), "", getPhoneNumber(artifact)));
if(duration != -1) { if(duration != -1) {
sheetSet.put(new NodeProperty<>("duration", "Duration", "", Long.toString(duration))); sheetSet.put(new NodeProperty<>("duration", "Duration", "", Long.toString(duration)));
} }
@ -107,6 +100,59 @@ final class CallLogNode extends BlackboardArtifactNode {
return endAttribute.getValueLong() - startAttribute.getValueLong(); return endAttribute.getValueLong() - startAttribute.getValueLong();
} }
/**
* Returns the phone number to display in the To/From column. The number is
* picked from one of the 3 possible phone number attributes, based on the
* direction of the call.
*
* @param artifact Call log artifact.
*
* @return Phone number to display.
*/
private String getPhoneNumber(BlackboardArtifact artifact) {
String direction = getAttributeDisplayString(artifact, TSK_DIRECTION);
String phoneNumberToReturn;
String fromPhoneNumber = getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_FROM);
String toPhoneNumber = getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_TO);
String phoneNumber = getAttributeDisplayString(artifact, TSK_PHONE_NUMBER);
switch (direction.toLowerCase()) {
case "incoming": // NON-NLS
phoneNumberToReturn = getFirstNonBlank(fromPhoneNumber, phoneNumber, toPhoneNumber);
break;
case "outgoing": // NON-NLS
phoneNumberToReturn = getFirstNonBlank(toPhoneNumber, phoneNumber, fromPhoneNumber);
break;
default:
phoneNumberToReturn = getFirstNonBlank(toPhoneNumber, fromPhoneNumber, phoneNumber );
break;
}
return phoneNumberToReturn;
}
/**
* Checks the given string arguments in order and returns the first non blank string.
* Returns a blank string if all the input strings are blank.
*
* @param string1 First string to check
* @param string2 Second string to check
* @param string3 Third string to check
*
* @retunr first non blank string if there is one, blank string otherwise.
*
*/
private String getFirstNonBlank(String string1, String string2, String string3 ) {
if (!StringUtils.isBlank(string1)) {
return string1;
} else if (!StringUtils.isBlank(string2)) {
return string2;
} else if (!StringUtils.isBlank(string3)) {
return string3;
}
return "";
}
/** /**
* Circumvent DataResultFilterNode's slightly odd delegation to * Circumvent DataResultFilterNode's slightly odd delegation to
* BlackboardArtifactNode.getSourceName(). * BlackboardArtifactNode.getSourceName().

View File

@ -41,6 +41,7 @@ MediaFileViewer.AccessibleContext.accessibleDescription=
MediaFileViewer.title=Media MediaFileViewer.title=Media
MediaFileViewer.toolTip=Displays supported multimedia files (images, videos, audio) MediaFileViewer.toolTip=Displays supported multimedia files (images, videos, audio)
MediaPlayerPanel.noSupport=File not supported. MediaPlayerPanel.noSupport=File not supported.
MediaPlayerPanel.playbackDisabled=A problem was encountered with the video and audio playback service. Video and audio playback will be disabled for the remainder of the session.
MediaPlayerPanel.timeFormat=%02d:%02d:%02d MediaPlayerPanel.timeFormat=%02d:%02d:%02d
MediaPlayerPanel.unknownTime=Unknown MediaPlayerPanel.unknownTime=Unknown
MediaViewImagePanel.createTagOption=Create MediaViewImagePanel.createTagOption=Create
@ -168,7 +169,7 @@ MediaPlayerPanel.playBackSpeedLabel.text=Speed:
SQLiteViewer.readTable.errorText=Error getting rows for table: {0} SQLiteViewer.readTable.errorText=Error getting rows for table: {0}
# {0} - tableName # {0} - tableName
SQLiteViewer.selectTable.errorText=Error getting row count for table: {0} SQLiteViewer.selectTable.errorText=Error getting row count for table: {0}
TextTranslatableComponent.setPanelContent.onSetContentError=Unable to display text at this time.
TextTranslatableComponent.setTranslated.onTranslateError=Unable to translate text at this time.
TranslatablePanel.comboBoxOption.originalText=Original Text TranslatablePanel.comboBoxOption.originalText=Original Text
TranslatablePanel.comboBoxOption.translatedText=Translated Text TranslatablePanel.comboBoxOption.translatedText=Translated Text
# {0} - exception message
TranslatablePanel.onSetContentError.text=There was an error displaying the text: {0}

View File

@ -194,6 +194,6 @@ class MediaFileViewer extends javax.swing.JPanel implements FileTypeViewer {
@Override @Override
public boolean isSupported(AbstractFile file){ public boolean isSupported(AbstractFile file){
return true; return mediaPlayerPanel.isSupported(file) || imagePanel.isSupported(file);
} }
} }

View File

@ -63,7 +63,7 @@
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" max="-2" attributes="0"> <Group type="103" groupAlignment="0" max="-2" attributes="0">
<Component id="progressLabel" max="32767" attributes="0"/> <Component id="progressLabel" max="32767" attributes="0"/>
<Component id="playBackPanel" pref="0" max="32767" attributes="0"/> <Component id="playBackPanel" max="32767" attributes="0"/>
</Group> </Group>
<EmptySpace min="-2" pref="10" max="-2" attributes="0"/> <EmptySpace min="-2" pref="10" max="-2" attributes="0"/>
</Group> </Group>
@ -129,12 +129,6 @@
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MediaPlayerPanel.playButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MediaPlayerPanel.playButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[53, 29]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[53, 29]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[49, 29]"/> <Dimension value="[49, 29]"/>
</Property> </Property>

View File

@ -74,7 +74,9 @@ import org.freedesktop.gstreamer.Format;
import org.freedesktop.gstreamer.GstException; import org.freedesktop.gstreamer.GstException;
import org.freedesktop.gstreamer.event.SeekFlags; import org.freedesktop.gstreamer.event.SeekFlags;
import org.freedesktop.gstreamer.event.SeekType; import org.freedesktop.gstreamer.event.SeekType;
import org.sleuthkit.autopsy.coreutils.PlatformUtil; import org.sleuthkit.autopsy.contentviewers.utils.GstLoader;
import org.sleuthkit.autopsy.contentviewers.utils.GstLoader.GstStatus;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
/** /**
* This is a video player that is part of the Media View layered pane. It uses * This is a video player that is part of the Media View layered pane. It uses
@ -213,6 +215,8 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
//and the TrackListener on the slider itself. //and the TrackListener on the slider itself.
private final Semaphore sliderLock; private final Semaphore sliderLock;
private static volatile boolean IS_GST_ENABLED = true;
/** /**
* Creates a new MediaPlayerPanel * Creates a new MediaPlayerPanel
*/ */
@ -222,18 +226,10 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
//True for fairness. In other words, //True for fairness. In other words,
//acquire() calls are processed in order of invocation. //acquire() calls are processed in order of invocation.
sliderLock = new Semaphore(1, true); sliderLock = new Semaphore(1, true);
/**
* See JIRA-5888 for details. Initializing gstreamer here is more stable
* on Windows.
*/
if (PlatformUtil.isWindowsOS()) {
Gst.init();
}
} }
private void customizeComponents() { private void customizeComponents() {
progressSlider.setEnabled(false); // disable slider; enable after user plays vid enableComponents(false);
progressSlider.setMinimum(0); progressSlider.setMinimum(0);
progressSlider.setMaximum(PROGRESS_SLIDER_SIZE); progressSlider.setMaximum(PROGRESS_SLIDER_SIZE);
progressSlider.setValue(0); progressSlider.setValue(0);
@ -241,7 +237,7 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
progressSlider.addChangeListener(new ChangeListener() { progressSlider.addChangeListener(new ChangeListener() {
@Override @Override
public void stateChanged(ChangeEvent e) { public void stateChanged(ChangeEvent e) {
if (progressSlider.getValueIsAdjusting()) { if (progressSlider.getValueIsAdjusting() && gstPlayBin != null) {
long duration = gstPlayBin.queryDuration(TimeUnit.NANOSECONDS); long duration = gstPlayBin.queryDuration(TimeUnit.NANOSECONDS);
double relativePosition = progressSlider.getValue() * 1.0 / PROGRESS_SLIDER_SIZE; double relativePosition = progressSlider.getValue() * 1.0 / PROGRESS_SLIDER_SIZE;
long newStartTime = (long) (relativePosition * duration); long newStartTime = (long) (relativePosition * duration);
@ -267,17 +263,20 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
@Override @Override
public void mousePressed(MouseEvent e) { public void mousePressed(MouseEvent e) {
if (gstPlayBin != null) {
previousState = gstPlayBin.getState(); previousState = gstPlayBin.getState();
gstPlayBin.pause(); gstPlayBin.pause();
} }
}
@Override @Override
public void mouseReleased(MouseEvent e) { public void mouseReleased(MouseEvent e) {
if(previousState.equals(State.PLAYING)) { if (previousState.equals(State.PLAYING) && gstPlayBin != null) {
gstPlayBin.play(); gstPlayBin.play();
} }
previousState = State.NULL; previousState = State.NULL;
} }
@Override @Override
public void mouseClicked(MouseEvent e) { public void mouseClicked(MouseEvent e) {
} }
@ -293,7 +292,7 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
}); });
//Manage the audio level when the user is adjusting the volume slider //Manage the audio level when the user is adjusting the volume slider
audioSlider.addChangeListener((ChangeEvent event) -> { audioSlider.addChangeListener((ChangeEvent event) -> {
if (audioSlider.getValueIsAdjusting()) { if (audioSlider.getValueIsAdjusting() && gstPlayBin != null) {
double audioPercent = (audioSlider.getValue() * 2.0) / 100.0; double audioPercent = (audioSlider.getValue() * 2.0) / 100.0;
gstPlayBin.setVolume(audioPercent); gstPlayBin.setVolume(audioPercent);
} }
@ -327,12 +326,14 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
endOfStreamListener = new Bus.EOS() { endOfStreamListener = new Bus.EOS() {
@Override @Override
public void endOfStream(GstObject go) { public void endOfStream(GstObject go) {
if (gstPlayBin != null) {
gstPlayBin.seek(ClockTime.ZERO); gstPlayBin.seek(ClockTime.ZERO);
/** /**
* Keep the video from automatically playing * Keep the video from automatically playing
*/ */
Gst.getExecutor().submit(() -> gstPlayBin.pause()); Gst.getExecutor().submit(() -> gstPlayBin.pause());
} }
}
}; };
} }
@ -385,13 +386,16 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
} }
timer.stop(); timer.stop();
if (gstPlayBin != null) { if (gstPlayBin != null) {
Gst.getExecutor().submit(() -> {
gstPlayBin.stop(); gstPlayBin.stop();
gstPlayBin.getBus().disconnect(endOfStreamListener); gstPlayBin.getBus().disconnect(endOfStreamListener);
gstPlayBin.getBus().disconnect(stateChangeListener); gstPlayBin.getBus().disconnect(stateChangeListener);
gstPlayBin.getBus().disconnect(errorListener); gstPlayBin.getBus().disconnect(errorListener);
gstPlayBin.getBus().dispose();
gstPlayBin.dispose(); gstPlayBin.dispose();
fxAppSink.clear(); fxAppSink.clear();
gstPlayBin = null; gstPlayBin = null;
});
} }
videoPanel.removeAll(); videoPanel.removeAll();
resetComponents(); resetComponents();
@ -420,6 +424,10 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
@Override @Override
public boolean isSupported(AbstractFile file) { public boolean isSupported(AbstractFile file) {
if (!IS_GST_ENABLED) {
return false;
}
String extension = file.getNameExtension(); String extension = file.getNameExtension();
/** /**
* Although it seems too restrictive, requiring both a supported * Although it seems too restrictive, requiring both a supported
@ -537,6 +545,11 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
/* /*
* Initialize the playback components if the extraction was successful. * Initialize the playback components if the extraction was successful.
*/ */
@NbBundle.Messages({
"MediaPlayerPanel.playbackDisabled=A problem was encountered with"
+ " the video and audio playback service. Video and audio "
+ "playback will be disabled for the remainder of the session."
})
@Override @Override
protected void done() { protected void done() {
try { try {
@ -546,13 +559,16 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
return; return;
} }
// Initialize Gstreamer. It is safe to call this for every file. GstStatus loadStatus = GstLoader.tryLoad();
// It was moved here from the constructor because having it happen if (loadStatus == GstStatus.FAILURE) {
// earlier resulted in conflicts on Linux. See JIRA-5888. MessageNotifyUtil.Message.error(Bundle.MediaPlayerPanel_playbackDisabled());
if (!PlatformUtil.isWindowsOS()) {
Gst.init(); // This will disable the panel for future use.
IS_GST_ENABLED = false;
return;
} }
Gst.getExecutor().submit(() -> {
//Video is ready for playback. Create new components //Video is ready for playback. Create new components
gstPlayBin = new PlayBin("VideoPlayer", tempFile.toURI()); gstPlayBin = new PlayBin("VideoPlayer", tempFile.toURI());
//Configure event handling //Configure event handling
@ -570,17 +586,22 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
videoPanel.setLayout(new BoxLayout(videoPanel, BoxLayout.Y_AXIS)); videoPanel.setLayout(new BoxLayout(videoPanel, BoxLayout.Y_AXIS));
videoPanel.add(fxPanel); videoPanel.add(fxPanel);
fxAppSink = new JavaFxAppSink("JavaFxAppSink", fxPanel); fxAppSink = new JavaFxAppSink("JavaFxAppSink", fxPanel);
if (gstPlayBin != null) {
gstPlayBin.setVideoSink(fxAppSink); gstPlayBin.setVideoSink(fxAppSink);
}
if (this.isCancelled()) { if (this.isCancelled()) {
return; return;
} }
if (gstPlayBin != null) {
gstPlayBin.setVolume((audioSlider.getValue() * 2.0) / 100.0); gstPlayBin.setVolume((audioSlider.getValue() * 2.0) / 100.0);
gstPlayBin.pause(); gstPlayBin.pause();
}
timer.start(); timer.start();
SwingUtilities.invokeLater(() -> {
enableComponents(true); enableComponents(true);
});
});
} catch (CancellationException ex) { } catch (CancellationException ex) {
logger.log(Level.INFO, "Media buffering was canceled."); //NON-NLS logger.log(Level.INFO, "Media buffering was canceled."); //NON-NLS
} catch (InterruptedException ex) { } catch (InterruptedException ex) {
@ -598,14 +619,17 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
if (!progressSlider.getValueIsAdjusting()) { if (!progressSlider.getValueIsAdjusting() && gstPlayBin != null) {
Gst.getExecutor().submit(() -> {
try {
sliderLock.acquireUninterruptibly(); sliderLock.acquireUninterruptibly();
long position = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS); long position = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
long duration = gstPlayBin.queryDuration(TimeUnit.NANOSECONDS); long duration = gstPlayBin.queryDuration(TimeUnit.NANOSECONDS);
/** /**
* Duration may not be known until there is video data in the * Duration may not be known until there is video data
* pipeline. We start this updater when data-flow has just been * in the pipeline. We start this updater when data-flow
* initiated so buffering may still be in progress. * has just been initiated so buffering may still be in
* progress.
*/ */
if (duration >= 0 && position >= 0) { if (duration >= 0 && position >= 0) {
double relativePosition = (double) position / duration; double relativePosition = (double) position / duration;
@ -615,8 +639,11 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(() -> {
updateTimeLabel(position, duration); updateTimeLabel(position, duration);
}); });
} finally {
sliderLock.release(); sliderLock.release();
} }
});
}
} }
} }
@ -655,8 +682,8 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
} }
/** /**
* Modifies the View to be an oval rather than the underlying * Modifies the View to be an oval rather than the underlying rectangle
* rectangle Controller. * Controller.
*/ */
@Override @Override
public void paintThumb(Graphics graphic) { public void paintThumb(Graphics graphic) {
@ -705,11 +732,12 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
@Override @Override
protected TrackListener createTrackListener(JSlider slider) { protected TrackListener createTrackListener(JSlider slider) {
/** /**
* This track listener will force the thumb to be snapped to the mouse * This track listener will force the thumb to be snapped to the
* location. This makes grabbing and dragging the JSlider much easier. * mouse location. This makes grabbing and dragging the JSlider much
* Using the default track listener, the user would have to click * easier. Using the default track listener, the user would have to
* exactly on the slider thumb to drag it. Now the thumb positions * click exactly on the slider thumb to drag it. Now the thumb
* itself under the mouse so that it can always be dragged. * positions itself under the mouse so that it can always be
* dragged.
*/ */
return new TrackListener() { return new TrackListener() {
@Override @Override
@ -982,6 +1010,8 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
private void rewindButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_rewindButtonActionPerformed private void rewindButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_rewindButtonActionPerformed
Gst.getExecutor().submit(() -> {
if (gstPlayBin != null) {
long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS); long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
//Skip 30 seconds. //Skip 30 seconds.
long rewindDelta = TimeUnit.NANOSECONDS.convert(SKIP_IN_SECONDS, TimeUnit.SECONDS); long rewindDelta = TimeUnit.NANOSECONDS.convert(SKIP_IN_SECONDS, TimeUnit.SECONDS);
@ -997,9 +1027,13 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
SeekType.SET, newTime, SeekType.SET, newTime,
//Do nothing for the end position //Do nothing for the end position
SeekType.NONE, -1); SeekType.NONE, -1);
}
});
}//GEN-LAST:event_rewindButtonActionPerformed }//GEN-LAST:event_rewindButtonActionPerformed
private void fastForwardButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_fastForwardButtonActionPerformed private void fastForwardButtonActionPerformed(java.awt.event.ActionEvent evt) {
Gst.getExecutor().submit(() -> {
if (gstPlayBin != null) {
long duration = gstPlayBin.queryDuration(TimeUnit.NANOSECONDS); long duration = gstPlayBin.queryDuration(TimeUnit.NANOSECONDS);
long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS); long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
//Skip 30 seconds. //Skip 30 seconds.
@ -1029,9 +1063,13 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
SeekType.SET, newTime, SeekType.SET, newTime,
//Do nothing for the end position //Do nothing for the end position
SeekType.NONE, -1); SeekType.NONE, -1);
}//GEN-LAST:event_fastForwardButtonActionPerformed }
});
}
private void playButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_playButtonActionPerformed private void playButtonActionPerformed(java.awt.event.ActionEvent evt) {
Gst.getExecutor().submit(() -> {
if (gstPlayBin != null) {
if (gstPlayBin.isPlaying()) { if (gstPlayBin.isPlaying()) {
gstPlayBin.pause(); gstPlayBin.pause();
} else { } else {
@ -1049,9 +1087,13 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
SeekType.NONE, -1); SeekType.NONE, -1);
gstPlayBin.play(); gstPlayBin.play();
} }
}//GEN-LAST:event_playButtonActionPerformed }
});
}
private void playBackSpeedComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_playBackSpeedComboBoxActionPerformed private void playBackSpeedComboBoxActionPerformed(java.awt.event.ActionEvent evt) {
Gst.getExecutor().submit(() -> {
if (gstPlayBin != null) {
double playBackRate = getPlayBackRate(); double playBackRate = getPlayBackRate();
long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS); long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
gstPlayBin.seek(playBackRate, gstPlayBin.seek(playBackRate,
@ -1063,7 +1105,9 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
//playback rate. //playback rate.
SeekType.SET, currentTime, SeekType.SET, currentTime,
SeekType.NONE, 0); SeekType.NONE, 0);
}//GEN-LAST:event_playBackSpeedComboBoxActionPerformed }
});
}
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel VolumeIcon; private javax.swing.JLabel VolumeIcon;

View File

@ -1,8 +1,9 @@
ContextViewer.jSourceGoToResultButton.text=Go to Result ContextUsagePanel.jUsageGoToResultButton.text=Go to Result
ContextViewer.jSourceTextLabel.text=jLabel2 ContextSourcePanel.jSourceGoToResultButton.text=Go to Result
ContextViewer.jSourceNameLabel.text=jSourceNameLabel ContextUsagePanel.jUsageTextLabel.text=Label2
ContextViewer.jSourceLabel.text=Source ContextUsagePanel.jUsageNameLabel.text=jUsageLabel
ContextViewer.jUsageGoToResultButton.text=Go to Result ContextSourcePanel.jSourceTextLabel.text=Label2
ContextViewer.jUsageLabel.text=Usage ContextSourcePanel.jSourceNameLabel.text=jSourceNameLabel
ContextViewer.jUsageNameLabel.text=jSourceNameLabel ContextViewer.jSourceLabel.text=Usage
ContextViewer.jUsageTextLabel.text=jLabel2 ContextViewer.jUsageLabel.text=Source
ContextViewer.jUnknownLabel.text=Unknown

View File

@ -1,16 +1,17 @@
ContextUsagePanel.jUsageGoToResultButton.text=Go to Result
ContextSourcePanel.jSourceGoToResultButton.text=Go to Result
ContextUsagePanel.jUsageTextLabel.text=Label2
ContextUsagePanel.jUsageNameLabel.text=jUsageLabel
ContextSourcePanel.jSourceTextLabel.text=Label2
ContextSourcePanel.jSourceNameLabel.text=jSourceNameLabel
ContextViewer.attachmentSource=Attached to: ContextViewer.attachmentSource=Attached to:
ContextViewer.downloadedOn=On ContextViewer.downloadedOn=On
ContextViewer.downloadSource=Downloaded from: ContextViewer.downloadSource=Downloaded from:
ContextViewer.downloadURL=URL ContextViewer.downloadURL=URL
ContextViewer.email=Email ContextViewer.email=Email
ContextViewer.jSourceGoToResultButton.text=Go to Result ContextViewer.jSourceLabel.text=Usage
ContextViewer.jSourceTextLabel.text=jLabel2 ContextViewer.jUsageLabel.text=Source
ContextViewer.jSourceNameLabel.text=jSourceNameLabel ContextViewer.jUnknownLabel.text=Unknown
ContextViewer.jSourceLabel.text=Source
ContextViewer.jUsageGoToResultButton.text=Go to Result
ContextViewer.jUsageLabel.text=Usage
ContextViewer.jUsageNameLabel.text=jSourceNameLabel
ContextViewer.jUsageTextLabel.text=jLabel2
ContextViewer.message=Message ContextViewer.message=Message
ContextViewer.messageFrom=From ContextViewer.messageFrom=From
ContextViewer.messageOn=On ContextViewer.messageOn=On
@ -20,3 +21,4 @@ ContextViewer.recentDocs=Recent Documents:
ContextViewer.title=Context ContextViewer.title=Context
ContextViewer.toolTip=Displays context for selected file. ContextViewer.toolTip=Displays context for selected file.
ContextViewer.unknown=Opened at unknown time ContextViewer.unknown=Opened at unknown time
ContextViewer.unknownSource=Unknown

View File

@ -0,0 +1,82 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<Properties>
<Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="ff" green="ff" red="ff" type="rgb"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[495, 75]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace min="-2" pref="50" max="-2" attributes="0"/>
<Component id="jSourceNameLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="36" max="-2" attributes="0"/>
<Component id="jSourceTextLabel" max="32767" attributes="0"/>
<EmptySpace min="-2" pref="260" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="90" max="-2" attributes="0"/>
<Component id="jSourceGoToResultButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="jSourceNameLabel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="jSourceTextLabel" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="jSourceGoToResultButton" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JButton" name="jSourceGoToResultButton">
<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="ContextSourcePanel.jSourceGoToResultButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jSourceGoToResultButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="jSourceNameLabel">
<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="ContextSourcePanel.jSourceNameLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="jSourceTextLabel">
<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="ContextSourcePanel.jSourceTextLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Form>

View File

@ -0,0 +1,154 @@
/*
* Autopsy Forensic Browser
*
* 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.contentviewers.contextviewer;
import java.util.ArrayList;
import java.util.List;
import org.sleuthkit.autopsy.directorytree.DirectoryTreeTopComponent;
import org.sleuthkit.datamodel.BlackboardArtifact;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT;
/**
* Displays additional context for the selected file, such as its source, and
* usage, if known.
*
*/
public final class ContextSourcePanel extends javax.swing.JPanel {
private static final long serialVersionUID = 1L;
// defines a list of artifacts that provide context for a file
private static final List<BlackboardArtifact.ARTIFACT_TYPE> SOURCE_CONTEXT_ARTIFACTS = new ArrayList<>();
static {
SOURCE_CONTEXT_ARTIFACTS.add(TSK_ASSOCIATED_OBJECT);
}
private final BlackboardArtifact sourceContextArtifact;
/**
* Creates new form ContextViewer
*/
public ContextSourcePanel(String sourceName, String sourceText, BlackboardArtifact associatedArtifact) {
initComponents();
sourceContextArtifact = associatedArtifact;
setSourceName(sourceName);
setSourceText(sourceText);
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
jSourceGoToResultButton = new javax.swing.JButton();
jSourceNameLabel = new javax.swing.JLabel();
jSourceTextLabel = new javax.swing.JLabel();
setBackground(new java.awt.Color(255, 255, 255));
setPreferredSize(new java.awt.Dimension(495, 75));
org.openide.awt.Mnemonics.setLocalizedText(jSourceGoToResultButton, org.openide.util.NbBundle.getMessage(ContextSourcePanel.class, "ContextSourcePanel.jSourceGoToResultButton.text")); // NOI18N
jSourceGoToResultButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jSourceGoToResultButtonActionPerformed(evt);
}
});
org.openide.awt.Mnemonics.setLocalizedText(jSourceNameLabel, org.openide.util.NbBundle.getMessage(ContextSourcePanel.class, "ContextSourcePanel.jSourceNameLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(jSourceTextLabel, org.openide.util.NbBundle.getMessage(ContextSourcePanel.class, "ContextSourcePanel.jSourceTextLabel.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()
.addGap(50, 50, 50)
.addComponent(jSourceNameLabel)
.addGap(36, 36, 36)
.addComponent(jSourceTextLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGap(260, 260, 260))
.addGroup(layout.createSequentialGroup()
.addGap(90, 90, 90)
.addComponent(jSourceGoToResultButton)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(2, 2, 2)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jSourceNameLabel)
.addComponent(jSourceTextLabel))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jSourceGoToResultButton)
.addGap(0, 0, 0))
);
}// </editor-fold>//GEN-END:initComponents
private void jSourceGoToResultButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jSourceGoToResultButtonActionPerformed
final DirectoryTreeTopComponent dtc = DirectoryTreeTopComponent.findInstance();
// Navigate to the source context artifact.
if (sourceContextArtifact != null) {
dtc.viewArtifact(sourceContextArtifact);
}
}//GEN-LAST:event_jSourceGoToResultButtonActionPerformed
/**
* Sets the source label string.
*
* @param nameLabel String value for source label.
*/
private void setSourceName(String nameLabel) {
jSourceNameLabel.setText(nameLabel);
}
/**
* Sets the source text string.
*
* @param text String value for source text.
*/
private void setSourceText(String text) {
jSourceTextLabel.setText(text);
showSourceButton(!text.isEmpty());
showSourceText(true);
}
private void showSourceText(boolean show) {
jSourceTextLabel.setVisible(show);
}
private void showSourceButton(boolean show) {
jSourceGoToResultButton.setVisible(show);
jSourceGoToResultButton.setEnabled(show);
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton jSourceGoToResultButton;
private javax.swing.JLabel jSourceNameLabel;
private javax.swing.JLabel jSourceTextLabel;
// End of variables declaration//GEN-END:variables
}

View File

@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<Properties>
<Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="ff" green="ff" red="ff" type="rgb"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[495, 75]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace min="-2" pref="50" max="-2" attributes="0"/>
<Component id="jUsageNameLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="jUsageTextLabel" pref="240" max="32767" attributes="0"/>
<EmptySpace min="-2" pref="36" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="90" max="-2" attributes="0"/>
<Component id="jUsageGoToResultButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="jUsageTextLabel" min="-2" max="-2" attributes="0"/>
<Component id="jUsageNameLabel" alignment="1" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="jUsageGoToResultButton" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<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="ContextUsagePanel.jUsageGoToResultButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jUsageGoToResultButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="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="ContextUsagePanel.jUsageNameLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="jUsageTextLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/contextviewer/Bundle.properties" key="ContextUsagePanel.jUsageTextLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Form>

View File

@ -0,0 +1,152 @@
/*
* Autopsy Forensic Browser
*
* 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.contentviewers.contextviewer;
import java.util.ArrayList;
import java.util.List;
import org.sleuthkit.autopsy.directorytree.DirectoryTreeTopComponent;
import org.sleuthkit.datamodel.BlackboardArtifact;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT;
/**
* Displays additional context for the selected file, such as its source, and
* usage, if known.
*
*/
public final class ContextUsagePanel extends javax.swing.JPanel {
private static final long serialVersionUID = 1L;
// defines a list of artifacts that provide context for a file
private static final List<BlackboardArtifact.ARTIFACT_TYPE> SOURCE_CONTEXT_ARTIFACTS = new ArrayList<>();
static {
SOURCE_CONTEXT_ARTIFACTS.add(TSK_ASSOCIATED_OBJECT);
}
private final BlackboardArtifact sourceContextArtifact;
/**
* Creates new form ContextViewer
*/
public ContextUsagePanel(String sourceName, String sourceText, BlackboardArtifact associatedArtifact) {
initComponents();
sourceContextArtifact = associatedArtifact;
setUsageName(sourceName);
setUsageText(sourceText);
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
jUsageGoToResultButton = new javax.swing.JButton();
jUsageNameLabel = new javax.swing.JLabel();
jUsageTextLabel = new javax.swing.JLabel();
setBackground(new java.awt.Color(255, 255, 255));
setPreferredSize(new java.awt.Dimension(495, 75));
org.openide.awt.Mnemonics.setLocalizedText(jUsageGoToResultButton, org.openide.util.NbBundle.getMessage(ContextUsagePanel.class, "ContextUsagePanel.jUsageGoToResultButton.text")); // NOI18N
jUsageGoToResultButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jUsageGoToResultButtonActionPerformed(evt);
}
});
org.openide.awt.Mnemonics.setLocalizedText(jUsageNameLabel, org.openide.util.NbBundle.getMessage(ContextUsagePanel.class, "ContextUsagePanel.jUsageNameLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(jUsageTextLabel, org.openide.util.NbBundle.getMessage(ContextUsagePanel.class, "ContextUsagePanel.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()
.addGap(50, 50, 50)
.addComponent(jUsageNameLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(jUsageTextLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 240, Short.MAX_VALUE)
.addGap(36, 36, 36))
.addGroup(layout.createSequentialGroup()
.addGap(90, 90, 90)
.addComponent(jUsageGoToResultButton)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(2, 2, 2)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jUsageTextLabel)
.addComponent(jUsageNameLabel, javax.swing.GroupLayout.Alignment.TRAILING))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jUsageGoToResultButton))
);
}// </editor-fold>//GEN-END:initComponents
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
/**
* Sets the usage label string.
*
* @param nameLabel String value for usage label.
*/
private void setUsageName(String nameLabel) {
jUsageNameLabel.setText(nameLabel);
}
/**
* Sets the Usage text string.
*
* @param text String value for Usage text.
*/
private void setUsageText(String text) {
jUsageTextLabel.setText(text);
showUsageButton(!text.isEmpty());
showUsageText(true);
}
private void showUsageText(boolean show) {
jUsageTextLabel.setVisible(show);
}
private void showUsageButton(boolean show) {
jUsageGoToResultButton.setVisible(show);
jUsageGoToResultButton.setEnabled(show);
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton jUsageGoToResultButton;
private javax.swing.JLabel jUsageNameLabel;
private javax.swing.JLabel jUsageTextLabel;
// End of variables declaration//GEN-END:variables
}

View File

@ -1,11 +1,145 @@
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo"> <Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<NonVisualComponents>
<Container class="javax.swing.JPanel" name="jSourcePanel">
<Properties>
<Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="ff" green="ff" id="window" palette="3" red="ff" type="palette"/>
</Property>
</Properties>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="40" max="-2" attributes="0"/>
<Component id="jSourceLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="304" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="5" max="-2" attributes="0"/>
<Component id="jSourceLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="2" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="jSourceLabel">
<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.jSourceLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="jUsagePanel">
<Properties>
<Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="ff" green="ff" id="window" palette="3" red="ff" type="palette"/>
</Property>
</Properties>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="40" max="-2" attributes="0"/>
<Component id="jUsageLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="298" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="2" max="-2" attributes="0"/>
<Component id="jUsageLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="2" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="jUsageLabel">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Dialog" size="14" style="1"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/contextviewer/Bundle.properties" key="ContextViewer.jUsageLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="jUnknownPanel">
<Properties> <Properties>
<Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor"> <Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="ff" green="ff" red="ff" type="rgb"/> <Color blue="ff" green="ff" red="ff" type="rgb"/>
</Property> </Property>
</Properties> </Properties>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="50" max="-2" attributes="0"/>
<Component id="jUnknownLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="2" max="-2" attributes="0"/>
<Component id="jUnknownLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="2" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="jUnknownLabel">
<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.jUnknownLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
</Component>
</SubComponents>
</Container>
</NonVisualComponents>
<Properties>
<Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="ff" green="ff" red="ff" type="rgb"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[495, 358]"/>
</Property>
</Properties>
<AuxValues> <AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/> <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
@ -21,138 +155,24 @@
<Layout> <Layout>
<DimensionLayout dim="0"> <DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0"> <Component id="jScrollPane" alignment="0" pref="509" max="32767" attributes="0"/>
<EmptySpace max="-2" 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">
<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>
</DimensionLayout> </DimensionLayout>
<DimensionLayout dim="1"> <DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0"> <Component id="jScrollPane" alignment="0" pref="335" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="jSourceLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="jSourceNameLabel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="jSourceTextLabel" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="jSourceGoToResultButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="jUsageLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="jUsageNameLabel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="jUsageTextLabel" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="jUsageGoToResultButton" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="62" max="32767" attributes="0"/>
</Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
</Layout> </Layout>
<SubComponents> <SubComponents>
<Component class="javax.swing.JButton" name="jSourceGoToResultButton"> <Container class="javax.swing.JScrollPane" name="jScrollPane">
<Properties> <Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/contextviewer/Bundle.properties" key="ContextViewer.jSourceGoToResultButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <Color blue="ff" green="ff" red="ff" type="rgb"/>
</Property> </Property>
</Properties> </Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jSourceGoToResultButtonActionPerformed"/> <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
</Events> </Container>
</Component>
<Component class="javax.swing.JLabel" name="jSourceLabel">
<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.jSourceLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="jSourceNameLabel">
<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.jSourceNameLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="jSourceTextLabel">
<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.jSourceTextLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</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, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jUsageGoToResultButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="jUsageLabel">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Dialog" size="14" style="1"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/contextviewer/Bundle.properties" key="ContextViewer.jUsageLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="jUsageNameLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/contextviewer/Bundle.properties" key="ContextViewer.jUsageNameLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="jUsageTextLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/contextviewer/Bundle.properties" key="ContextViewer.jUsageTextLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents> </SubComponents>
</Form> </Form>

View File

@ -24,6 +24,8 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.BoxLayout;
import static javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.openide.nodes.Node; import org.openide.nodes.Node;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
@ -32,7 +34,6 @@ import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.directorytree.DirectoryTreeTopComponent;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT; import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT;
@ -55,19 +56,20 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte
// defines a list of artifacts that provide context for a file // defines a list of artifacts that provide context for a file
private static final List<BlackboardArtifact.ARTIFACT_TYPE> SOURCE_CONTEXT_ARTIFACTS = new ArrayList<>(); private static final List<BlackboardArtifact.ARTIFACT_TYPE> SOURCE_CONTEXT_ARTIFACTS = new ArrayList<>();
private final List<javax.swing.JPanel> contextSourcePanels = new ArrayList<>();
private final List<javax.swing.JPanel> contextUsagePanels = new ArrayList<>();
static { static {
SOURCE_CONTEXT_ARTIFACTS.add(TSK_ASSOCIATED_OBJECT); SOURCE_CONTEXT_ARTIFACTS.add(TSK_ASSOCIATED_OBJECT);
} }
private BlackboardArtifact sourceContextArtifact;
/** /**
* Creates new form ContextViewer * Creates new form ContextViewer
*/ */
public ContextViewer() { public ContextViewer() {
initComponents(); initComponents();
jScrollPane.setHorizontalScrollBarPolicy(HORIZONTAL_SCROLLBAR_AS_NEEDED);
} }
/** /**
@ -79,119 +81,96 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() { private void initComponents() {
jSourceGoToResultButton = new javax.swing.JButton(); jSourcePanel = new javax.swing.JPanel();
jSourceLabel = new javax.swing.JLabel(); javax.swing.JLabel jSourceLabel = new javax.swing.JLabel();
jSourceNameLabel = new javax.swing.JLabel(); jUsagePanel = new javax.swing.JPanel();
jSourceTextLabel = new javax.swing.JLabel(); javax.swing.JLabel jUsageLabel = new javax.swing.JLabel();
jUsageGoToResultButton = new javax.swing.JButton(); jUnknownPanel = new javax.swing.JPanel();
jUsageLabel = new javax.swing.JLabel(); javax.swing.JLabel jUnknownLabel = new javax.swing.JLabel();
jUsageNameLabel = new javax.swing.JLabel(); jScrollPane = new javax.swing.JScrollPane();
jUsageTextLabel = new javax.swing.JLabel();
setBackground(new java.awt.Color(255, 255, 255)); jSourcePanel.setBackground(javax.swing.UIManager.getDefaults().getColor("window"));
org.openide.awt.Mnemonics.setLocalizedText(jSourceGoToResultButton, org.openide.util.NbBundle.getMessage(ContextViewer.class, "ContextViewer.jSourceGoToResultButton.text")); // NOI18N
jSourceGoToResultButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jSourceGoToResultButtonActionPerformed(evt);
}
});
jSourceLabel.setFont(new java.awt.Font("Dialog", 1, 14)); // NOI18N jSourceLabel.setFont(new java.awt.Font("Dialog", 1, 14)); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(jSourceLabel, org.openide.util.NbBundle.getMessage(ContextViewer.class, "ContextViewer.jSourceLabel.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(jSourceLabel, org.openide.util.NbBundle.getMessage(ContextViewer.class, "ContextViewer.jSourceLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(jSourceNameLabel, org.openide.util.NbBundle.getMessage(ContextViewer.class, "ContextViewer.jSourceNameLabel.text")); // NOI18N javax.swing.GroupLayout jSourcePanelLayout = new javax.swing.GroupLayout(jSourcePanel);
jSourcePanel.setLayout(jSourcePanelLayout);
jSourcePanelLayout.setHorizontalGroup(
jSourcePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jSourcePanelLayout.createSequentialGroup()
.addGap(40, 40, 40)
.addComponent(jSourceLabel)
.addContainerGap(304, Short.MAX_VALUE))
);
jSourcePanelLayout.setVerticalGroup(
jSourcePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jSourcePanelLayout.createSequentialGroup()
.addGap(5, 5, 5)
.addComponent(jSourceLabel)
.addGap(2, 2, 2))
);
org.openide.awt.Mnemonics.setLocalizedText(jSourceTextLabel, org.openide.util.NbBundle.getMessage(ContextViewer.class, "ContextViewer.jSourceTextLabel.text")); // NOI18N jUsagePanel.setBackground(javax.swing.UIManager.getDefaults().getColor("window"));
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 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(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 javax.swing.GroupLayout jUsagePanelLayout = new javax.swing.GroupLayout(jUsagePanel);
jUsagePanel.setLayout(jUsagePanelLayout);
jUsagePanelLayout.setHorizontalGroup(
jUsagePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jUsagePanelLayout.createSequentialGroup()
.addGap(40, 40, 40)
.addComponent(jUsageLabel)
.addContainerGap(298, Short.MAX_VALUE))
);
jUsagePanelLayout.setVerticalGroup(
jUsagePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jUsagePanelLayout.createSequentialGroup()
.addGap(2, 2, 2)
.addComponent(jUsageLabel)
.addGap(2, 2, 2))
);
org.openide.awt.Mnemonics.setLocalizedText(jUsageTextLabel, org.openide.util.NbBundle.getMessage(ContextViewer.class, "ContextViewer.jUsageTextLabel.text")); // NOI18N jUnknownPanel.setBackground(new java.awt.Color(255, 255, 255));
org.openide.awt.Mnemonics.setLocalizedText(jUnknownLabel, org.openide.util.NbBundle.getMessage(ContextViewer.class, "ContextViewer.jUnknownLabel.text")); // NOI18N
javax.swing.GroupLayout jUnknownPanelLayout = new javax.swing.GroupLayout(jUnknownPanel);
jUnknownPanel.setLayout(jUnknownPanelLayout);
jUnknownPanelLayout.setHorizontalGroup(
jUnknownPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jUnknownPanelLayout.createSequentialGroup()
.addGap(50, 50, 50)
.addComponent(jUnknownLabel)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
jUnknownPanelLayout.setVerticalGroup(
jUnknownPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jUnknownPanelLayout.createSequentialGroup()
.addGap(2, 2, 2)
.addComponent(jUnknownLabel)
.addGap(2, 2, 2))
);
setBackground(new java.awt.Color(255, 255, 255));
setPreferredSize(new java.awt.Dimension(495, 358));
jScrollPane.setBackground(new java.awt.Color(255, 255, 255));
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout); this.setLayout(layout);
layout.setHorizontalGroup( layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup() .addComponent(jScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 509, Short.MAX_VALUE)
.addContainerGap()
.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))
.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()
.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.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup() .addComponent(jScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 335, Short.MAX_VALUE)
.addContainerGap()
.addComponent(jSourceLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jSourceNameLabel)
.addComponent(jSourceTextLabel))
.addGap(18, 18, 18)
.addComponent(jSourceGoToResultButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jUsageLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jUsageNameLabel)
.addComponent(jUsageTextLabel))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jUsageGoToResultButton)
.addGap(0, 62, Short.MAX_VALUE))
); );
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
private void jSourceGoToResultButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jSourceGoToResultButtonActionPerformed
final DirectoryTreeTopComponent dtc = DirectoryTreeTopComponent.findInstance();
// Navigate to the source context artifact.
if (sourceContextArtifact != null) {
dtc.viewArtifact(sourceContextArtifact);
}
}//GEN-LAST:event_jSourceGoToResultButtonActionPerformed
private void jUsageGoToResultButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jUsageGoToResultButtonActionPerformed
final DirectoryTreeTopComponent dtc = DirectoryTreeTopComponent.findInstance();
// Navigate to the source context artifact.
if (sourceContextArtifact != null) {
dtc.viewArtifact(sourceContextArtifact);
}
}//GEN-LAST:event_jUsageGoToResultButtonActionPerformed
@Override @Override
public void setNode(Node selectedNode) { public void setNode(Node selectedNode) {
if ((selectedNode == null) || (!isSupported(selectedNode))) { if ((selectedNode == null) || (!isSupported(selectedNode))) {
@ -234,9 +213,8 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte
@Override @Override
public void resetComponent() { public void resetComponent() {
jSourceGoToResultButton.setVisible(false); contextSourcePanels.clear();
setSourceName(""); contextUsagePanels.clear();
setSourceText("");
} }
@Override @Override
@ -268,6 +246,9 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte
return 1; return 1;
} }
@NbBundle.Messages({
"ContextViewer.unknownSource=Unknown ",
})
/** /**
* Looks for context providing artifacts for the given file and populates * Looks for context providing artifacts for the given file and populates
* the source context. * the source context.
@ -291,14 +272,32 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte
addSourceEntry(contextArtifact); addSourceEntry(contextArtifact);
} }
} }
jSourceGoToResultButton.setVisible(true); javax.swing.JPanel contextContainer = new javax.swing.JPanel();
jUsageGoToResultButton.setVisible(true); contextContainer.add(jSourcePanel);
if (foundASource == false) { contextContainer.setLayout(new BoxLayout(contextContainer, BoxLayout.Y_AXIS));
setSourceName("Unknown"); if (contextSourcePanels.isEmpty()) {
showSourceText(false); contextContainer.add(jUnknownPanel);
setUsageName("Unknown"); } else {
showUsageText(false); for (javax.swing.JPanel sourcePanel : contextSourcePanels) {
contextContainer.add(sourcePanel);
} }
}
contextContainer.add(jUsagePanel);
if (contextUsagePanels.isEmpty()) {
contextContainer.add(jUnknownPanel);
} else {
for (javax.swing.JPanel usagePanel : contextUsagePanels) {
contextContainer.add(usagePanel);
}
}
contextContainer.setEnabled(foundASource);
contextContainer.setVisible(foundASource);
jScrollPane.getViewport().setView(contextContainer);
jScrollPane.setEnabled(foundASource);
jScrollPane.setVisible(foundASource);
jScrollPane.repaint();
jScrollPane.revalidate();
} }
@ -312,10 +311,6 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte
* @throws TskCoreException * @throws TskCoreException
*/ */
private void addSourceEntry(BlackboardArtifact artifact) throws TskCoreException { private void addSourceEntry(BlackboardArtifact artifact) throws TskCoreException {
setSourceName("Unknown");
showSourceText(false);
setUsageName("Unknown");
showUsageText(false);
if (BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT.getTypeID() == artifact.getArtifactTypeID()) { if (BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT.getTypeID() == artifact.getArtifactTypeID()) {
BlackboardAttribute associatedArtifactAttribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT)); BlackboardAttribute associatedArtifactAttribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT));
@ -323,9 +318,6 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte
long artifactId = associatedArtifactAttribute.getValueLong(); long artifactId = associatedArtifactAttribute.getValueLong();
BlackboardArtifact associatedArtifact = artifact.getSleuthkitCase().getBlackboardArtifact(artifactId); BlackboardArtifact associatedArtifact = artifact.getSleuthkitCase().getBlackboardArtifact(artifactId);
//save the artifact for "Go to Result" button
sourceContextArtifact = associatedArtifact;
setSourceFields(associatedArtifact); setSourceFields(associatedArtifact);
} }
} }
@ -347,74 +339,27 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte
private void setSourceFields(BlackboardArtifact associatedArtifact) throws TskCoreException { private void setSourceFields(BlackboardArtifact associatedArtifact) throws TskCoreException {
if (BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE.getTypeID() == associatedArtifact.getArtifactTypeID() if (BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE.getTypeID() == associatedArtifact.getArtifactTypeID()
|| BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID() == associatedArtifact.getArtifactTypeID()) { || BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID() == associatedArtifact.getArtifactTypeID()) {
String sourceName = Bundle.ContextViewer_attachmentSource();
setSourceName(Bundle.ContextViewer_attachmentSource()); String sourceText = msgArtifactToAbbreviatedString(associatedArtifact);
setSourceText(msgArtifactToAbbreviatedString(associatedArtifact)); javax.swing.JPanel sourcePanel = new ContextSourcePanel(sourceName, sourceText, associatedArtifact);
contextSourcePanels.add(sourcePanel);
} else if (BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID() == associatedArtifact.getArtifactTypeID() } else if (BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID() == associatedArtifact.getArtifactTypeID()
|| BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE.getTypeID() == associatedArtifact.getArtifactTypeID()) { || BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE.getTypeID() == associatedArtifact.getArtifactTypeID()) {
String sourceName = Bundle.ContextViewer_downloadSource();
String sourceText = webDownloadArtifactToString(associatedArtifact);
javax.swing.JPanel sourcePanel = new ContextSourcePanel(sourceName, sourceText, associatedArtifact);
contextSourcePanels.add(sourcePanel);
setSourceName(Bundle.ContextViewer_downloadSource());
setSourceText(webDownloadArtifactToString(associatedArtifact));
} else if (BlackboardArtifact.ARTIFACT_TYPE.TSK_RECENT_OBJECT.getTypeID() == associatedArtifact.getArtifactTypeID()) { } else if (BlackboardArtifact.ARTIFACT_TYPE.TSK_RECENT_OBJECT.getTypeID() == associatedArtifact.getArtifactTypeID()) {
setUsageName(Bundle.ContextViewer_recentDocs()); String sourceName = Bundle.ContextViewer_recentDocs();
setUsageText(recentDocArtifactToString(associatedArtifact)); String sourceText = recentDocArtifactToString(associatedArtifact);
javax.swing.JPanel usagePanel = new ContextUsagePanel(sourceName, sourceText, associatedArtifact);
contextUsagePanels.add(usagePanel);
} }
} }
/**
* Sets the source label string.
*
* @param nameLabel String value for source label.
*/
private void setSourceName(String nameLabel) {
jSourceNameLabel.setText(nameLabel);
}
/**
* Sets the usage label string.
*
* @param nameLabel String value for usage label.
*/
private void setUsageName(String nameLabel) {
jUsageNameLabel.setText(nameLabel);
}
/**
* Sets the source text string.
*
* @param text String value for source text.
*/
private void setSourceText(String text) {
jSourceTextLabel.setText(text);
showSourceText(!text.isEmpty());
}
private void showSourceText(boolean show) {
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);
}
/** /**
* Returns a display string with download source URL from the given * Returns a display string with download source URL from the given
* artifact. * artifact.
@ -554,13 +499,9 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton jSourceGoToResultButton; private javax.swing.JScrollPane jScrollPane;
private javax.swing.JLabel jSourceLabel; private javax.swing.JPanel jSourcePanel;
private javax.swing.JLabel jSourceNameLabel; private javax.swing.JPanel jUnknownPanel;
private javax.swing.JLabel jSourceTextLabel; private javax.swing.JPanel jUsagePanel;
private javax.swing.JButton jUsageGoToResultButton;
private javax.swing.JLabel jUsageLabel;
private javax.swing.JLabel jUsageNameLabel;
private javax.swing.JLabel jUsageTextLabel;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
} }

View File

@ -0,0 +1,74 @@
/*
* Autopsy Forensic Browser
*
* 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.contentviewers.utils;
import java.util.logging.Level;
import org.freedesktop.gstreamer.Gst;
import org.sleuthkit.autopsy.coreutils.Logger;
/**
* A utility class that loads the gstreamer bindings.
*/
public final class GstLoader {
private static final Logger logger = Logger.getLogger(GstLoader.class.getName());
private static GstStatus status;
/**
* Attempts to load the gstreamer bindings. Only one attempt will be
* performed per Autopsy process. Clients should not attempt to interact
* with the gstreamer bindings unless the load was successful.
*
* @return Status - SUCCESS or FAILURE
*/
public synchronized static GstStatus tryLoad() {
// Null is our 'unknown' status. Prior to the first call, the status
// is unknown.
if (status != null) {
return status;
}
try {
// Setting the following property causes the GST
// Java bindings to call dispose() on the GST
// service thread instead of running it in the GST
// Native Object Reaper thread.
System.setProperty("glib.reapOnEDT", "true");
Gst.init();
status = GstStatus.SUCCESS;
} catch (Throwable ex) {
status = GstStatus.FAILURE;
logger.log(Level.WARNING, "Failed to load gsteamer bindings", ex);
}
return status;
}
/**
* The various init statuses that tryLoad can return.
*/
public enum GstStatus {
SUCCESS, FAILURE
}
private GstLoader() {
}
}

View File

@ -27,6 +27,7 @@ import javax.swing.event.DocumentListener;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.datamodel.CaseDbConnectionInfo; import org.sleuthkit.datamodel.CaseDbConnectionInfo;
import org.sleuthkit.datamodel.TskData.DbType; import org.sleuthkit.datamodel.TskData.DbType;
import org.sleuthkit.autopsy.centralrepository.optionspanel.GlobalSettingsPanel;
import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.events.MessageServiceConnectionInfo; import org.sleuthkit.autopsy.events.MessageServiceConnectionInfo;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
@ -684,20 +685,30 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel {
return (isUserSet == isPwSet); return (isUserSet == isPwSet);
} }
void store() { void store() {
boolean prevSelected = UserPreferences.getIsMultiUserModeEnabled();
CaseDbConnectionInfo prevConn = null;
try {
prevConn = UserPreferences.getDatabaseConnectionInfo();
} catch (UserPreferencesException ex) {
logger.log(Level.SEVERE, "There was an error while fetching previous connection settings while trying to save", ex); //NON-NLS
}
boolean multiUserCasesEnabled = cbEnableMultiUser.isSelected(); boolean multiUserCasesEnabled = cbEnableMultiUser.isSelected();
UserPreferences.setIsMultiUserModeEnabled(multiUserCasesEnabled); UserPreferences.setIsMultiUserModeEnabled(multiUserCasesEnabled);
if (multiUserCasesEnabled == false) {
return; CaseDbConnectionInfo info = null;
}
if (multiUserCasesEnabled == true) {
/* /*
* Currently only supporting multi-user cases with PostgreSQL case * Currently only supporting multi-user cases with PostgreSQL case
* databases. * databases.
*/ */
DbType dbType = DbType.POSTGRESQL; DbType dbType = DbType.POSTGRESQL;
CaseDbConnectionInfo info = new CaseDbConnectionInfo( info = new CaseDbConnectionInfo(
tbDbHostname.getText().trim(), tbDbHostname.getText().trim(),
tbDbPort.getText().trim(), tbDbPort.getText().trim(),
tbDbUsername.getText().trim(), tbDbUsername.getText().trim(),
@ -730,9 +741,36 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel {
UserPreferences.setIndexingServerHost(tbSolrHostname.getText().trim()); UserPreferences.setIndexingServerHost(tbSolrHostname.getText().trim());
UserPreferences.setIndexingServerPort(Integer.parseInt(tbSolrPort.getText().trim())); UserPreferences.setIndexingServerPort(Integer.parseInt(tbSolrPort.getText().trim()));
} }
// trigger changes to whether or not user can use multi user settings for central repository
if (prevSelected != multiUserCasesEnabled || !areCaseDbConnectionEqual(prevConn, info))
GlobalSettingsPanel.onMultiUserChange(this, prevSelected, multiUserCasesEnabled);
}
private static boolean arePropsEqual(Object a, Object b) {
if (a == null || b == null) {
return (a == null && b == null);
}
else {
return a.equals(b);
}
}
private static boolean areCaseDbConnectionEqual(CaseDbConnectionInfo a, CaseDbConnectionInfo b) {
if (a == null || b == null) {
return (a == null && b == null);
}
return
arePropsEqual(a.getDbType(), b.getDbType()) &&
arePropsEqual(a.getHost(), b.getHost()) &&
arePropsEqual(a.getPassword(), b.getPassword()) &&
arePropsEqual(a.getPort(), b.getPort()) &&
arePropsEqual(a.getUserName(), b.getUserName());
}
/** /**
* Validates that the form is filled out correctly for our usage. * Validates that the form is filled out correctly for our usage.
* *

View File

@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.datamodel;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.text.MessageFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.List; import java.util.List;
@ -28,7 +29,6 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import org.openide.nodes.Sheet; import org.openide.nodes.Sheet;
@ -66,6 +66,7 @@ import org.sleuthkit.datamodel.Tag;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.texttranslation.utils.FileNameTranslationUtil;
/** /**
* An abstract node that encapsulates AbstractFile data * An abstract node that encapsulates AbstractFile data
@ -490,40 +491,19 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
} }
/** /**
* Translates this nodes content name. Doesn't attempt translation if the * Translates the name of the file this node represents. An empty string
* name is in english or if there is now translation service available. * will be returned if the translation fails for any reason.
*
* @return The translated file name or the empty string.
*/ */
String getTranslatedFileName() { String getTranslatedFileName() {
//If already in complete English, don't translate.
if (content.getName().matches("^\\p{ASCII}+$")) {
return "";
}
TextTranslationService tts = TextTranslationService.getInstance();
if (tts.hasProvider()) {
//Seperate out the base and ext from the contents file name.
String base = FilenameUtils.getBaseName(content.getName());
try { try {
String translation = tts.translate(base); return FileNameTranslationUtil.translate(content.getName());
String ext = FilenameUtils.getExtension(content.getName()); } catch (NoServiceProviderException | TranslationException ex) {
logger.log(Level.WARNING, MessageFormat.format("Error translating file name (objID={0}))", content.getId()), ex);
//If we have no extension, then we shouldn't add the .
String extensionDelimiter = (ext.isEmpty()) ? "" : ".";
//Talk directly to this nodes pcl, fire an update when the translation
//is complete.
if (!translation.isEmpty()) {
return translation + extensionDelimiter + ext;
}
} catch (NoServiceProviderException noServiceEx) {
logger.log(Level.WARNING, "Translate unsuccessful because no TextTranslator "
+ "implementation was provided.", noServiceEx.getMessage());
} catch (TranslationException noTranslationEx) {
logger.log(Level.WARNING, "Could not successfully translate file name "
+ content.getName(), noTranslationEx.getMessage());
}
}
return ""; return "";
} }
}
/** /**
* Get all tags from the case database that are associated with the file * Get all tags from the case database that are associated with the file

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2013-2018 Basis Technology Corp. * Copyright 2013-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -24,13 +24,11 @@ import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.Action; import javax.swing.Action;
import org.openide.nodes.Children;
import org.openide.nodes.Sheet; import org.openide.nodes.Sheet;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.openide.util.lookup.Lookups; import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.timeline.actions.ViewArtifactInTimelineAction; import org.sleuthkit.autopsy.timeline.actions.ViewArtifactInTimelineAction;
import org.sleuthkit.autopsy.timeline.actions.ViewFileInTimelineAction; import org.sleuthkit.autopsy.timeline.actions.ViewFileInTimelineAction;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
@ -46,14 +44,14 @@ import static org.sleuthkit.autopsy.datamodel.Bundle.*;
* tag name nodes have tag type child nodes; tag type nodes are the parents of * tag name nodes have tag type child nodes; tag type nodes are the parents of
* either content or blackboard artifact tag nodes. * either content or blackboard artifact tag nodes.
*/ */
public class BlackboardArtifactTagNode extends DisplayableItemNode { public class BlackboardArtifactTagNode extends TagNode {
private static final Logger LOGGER = Logger.getLogger(BlackboardArtifactTagNode.class.getName()); private static final Logger LOGGER = Logger.getLogger(BlackboardArtifactTagNode.class.getName());
private static final String ICON_PATH = "org/sleuthkit/autopsy/images/green-tag-icon-16.png"; //NON-NLS private static final String ICON_PATH = "org/sleuthkit/autopsy/images/green-tag-icon-16.png"; //NON-NLS
private final BlackboardArtifactTag tag; private final BlackboardArtifactTag tag;
public BlackboardArtifactTagNode(BlackboardArtifactTag tag) { public BlackboardArtifactTagNode(BlackboardArtifactTag tag) {
super(Children.LEAF, Lookups.fixed(tag, tag.getArtifact(), tag.getContent())); super(Lookups.fixed(tag, tag.getArtifact(), tag.getContent()), tag.getContent());
super.setName(tag.getContent().getName()); super.setName(tag.getContent().getName());
super.setDisplayName(tag.getContent().getName()); super.setDisplayName(tag.getContent().getName());
this.setIconBaseWithExtension(ICON_PATH); this.setIconBaseWithExtension(ICON_PATH);
@ -75,6 +73,7 @@ public class BlackboardArtifactTagNode extends DisplayableItemNode {
NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.srcFile.text"), NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.srcFile.text"),
"", "",
tag.getContent().getName())); tag.getContent().getName()));
addOriginalNameProp(properties);
String contentPath; String contentPath;
try { try {
contentPath = tag.getContent().getUniquePath(); contentPath = tag.getContent().getUniquePath();
@ -82,7 +81,6 @@ public class BlackboardArtifactTagNode extends DisplayableItemNode {
Logger.getLogger(ContentTagNode.class.getName()).log(Level.SEVERE, "Failed to get path for content (id = " + tag.getContent().getId() + ")", ex); //NON-NLS Logger.getLogger(ContentTagNode.class.getName()).log(Level.SEVERE, "Failed to get path for content (id = " + tag.getContent().getId() + ")", ex); //NON-NLS
contentPath = NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.unavail.text"); contentPath = NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.unavail.text");
} }
properties.put(new NodeProperty<>( properties.put(new NodeProperty<>(
NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.srcFilePath.text"), NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.srcFilePath.text"),
NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.srcFilePath.text"), NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.srcFilePath.text"),
@ -119,7 +117,6 @@ public class BlackboardArtifactTagNode extends DisplayableItemNode {
} }
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, MessageFormat.format("Error getting arttribute(s) from blackboard artifact{0}.", artifact.getArtifactID()), ex); //NON-NLS LOGGER.log(Level.SEVERE, MessageFormat.format("Error getting arttribute(s) from blackboard artifact{0}.", artifact.getArtifactID()), ex); //NON-NLS
MessageNotifyUtil.Notify.error(Bundle.BlackboardArtifactNode_getAction_errorTitle(), Bundle.BlackboardArtifactNode_getAction_resultErrorMessage());
} }
// if the artifact links to another file, add an action to go to that file // if the artifact links to another file, add an action to go to that file
@ -130,7 +127,6 @@ public class BlackboardArtifactTagNode extends DisplayableItemNode {
} }
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, MessageFormat.format("Error getting linked file from blackboard artifact{0}.", artifact.getArtifactID()), ex); //NON-NLS LOGGER.log(Level.SEVERE, MessageFormat.format("Error getting linked file from blackboard artifact{0}.", artifact.getArtifactID()), ex); //NON-NLS
MessageNotifyUtil.Notify.error(Bundle.BlackboardArtifactNode_getAction_errorTitle(), Bundle.BlackboardArtifactNode_getAction_linkedFileMessage());
} }
//if this artifact has associated content, add the action to view the content in the timeline //if this artifact has associated content, add the action to view the content in the timeline
AbstractFile file = getLookup().lookup(AbstractFile.class); AbstractFile file = getLookup().lookup(AbstractFile.class);
@ -148,11 +144,6 @@ public class BlackboardArtifactTagNode extends DisplayableItemNode {
return visitor.visit(this); return visitor.visit(this);
} }
@Override
public boolean isLeafTypeNode() {
return true;
}
@Override @Override
public String getItemType() { public String getItemType() {
return getClass().getName(); return getClass().getName();

View File

@ -11,8 +11,6 @@ ArtifactTypeNode.createSheet.childCnt.name=Child Count
ArtifactTypeNode.createSheet.childCnt.displayName=Child Count ArtifactTypeNode.createSheet.childCnt.displayName=Child Count
ArtifactTypeNode.createSheet.childCnt.desc=no description ArtifactTypeNode.createSheet.childCnt.desc=no description
BlackboardArtifactNode.noDesc.text=no description BlackboardArtifactNode.noDesc.text=no description
BlackboardArtifactNode.createSheet.srcFile.name=Source File
BlackboardArtifactNode.createSheet.srcFile.displayName=Source File
BlackboardArtifactNode.createSheet.ext.name=Extension BlackboardArtifactNode.createSheet.ext.name=Extension
BlackboardArtifactNode.createSheet.ext.displayName=Extension BlackboardArtifactNode.createSheet.ext.displayName=Extension
BlackboardArtifactNode.createSheet.mimeType.name=MIME Type BlackboardArtifactNode.createSheet.mimeType.name=MIME Type

View File

@ -74,13 +74,12 @@ BlackboardArtifactNode.createSheet.path.displayName=Path
BlackboardArtifactNode.createSheet.path.name=Path BlackboardArtifactNode.createSheet.path.name=Path
BlackboardArtifactNode.createSheet.score.displayName=S BlackboardArtifactNode.createSheet.score.displayName=S
BlackboardArtifactNode.createSheet.score.name=S BlackboardArtifactNode.createSheet.score.name=S
BlackboardArtifactNode.createSheet.srcFile.displayName=Source File
BlackboardArtifactNode.createSheet.srcFile.name=Source File
BlackboardArtifactNode.createSheet.srcFile.origDisplayName=Original Name
BlackboardArtifactNode.createSheet.srcFile.origName=Original Name
BlackboardArtifactNode.createSheet.taggedItem.description=Result or associated file has been tagged. BlackboardArtifactNode.createSheet.taggedItem.description=Result or associated file has been tagged.
BlackboardArtifactNode.createSheet.tags.displayName=Tags BlackboardArtifactNode.createSheet.tags.displayName=Tags
# {0} - artifactDisplayName
BlackboardArtifactNode.displayName.artifact={0} Artifact
BlackboardArtifactNode.getAction.errorTitle=Error getting actions
BlackboardArtifactNode.getAction.linkedFileMessage=There was a problem getting actions for the selected result. The 'View File in Timeline' action will not be available.
BlackboardArtifactNode.getAction.resultErrorMessage=There was a problem getting actions for the selected result. The 'View Result in Timeline' action will not be available.
BlackboardArtifactTagNode.createSheet.userName.text=User Name BlackboardArtifactTagNode.createSheet.userName.text=User Name
BlackboardArtifactTagNode.viewSourceArtifact.text=View Source Result BlackboardArtifactTagNode.viewSourceArtifact.text=View Source Result
Category.five=CAT-5: Non-pertinent Category.five=CAT-5: Non-pertinent
@ -91,6 +90,7 @@ Category.two=CAT-2: Child Exploitation (Non-Illegal/Age Difficult)
Category.zero=CAT-0: Uncategorized Category.zero=CAT-0: Uncategorized
ContentTagNode.createSheet.artifactMD5.displayName=MD5 Hash ContentTagNode.createSheet.artifactMD5.displayName=MD5 Hash
ContentTagNode.createSheet.artifactMD5.name=MD5 Hash ContentTagNode.createSheet.artifactMD5.name=MD5 Hash
ContentTagNode.createSheet.origFileName=Original Name
ContentTagNode.createSheet.userName.text=User Name ContentTagNode.createSheet.userName.text=User Name
DeletedContent.allDelFilter.text=All DeletedContent.allDelFilter.text=All
DeletedContent.createSheet.filterType.desc=no description DeletedContent.createSheet.filterType.desc=no description
@ -178,8 +178,6 @@ ArtifactTypeNode.createSheet.childCnt.name=Child Count
ArtifactTypeNode.createSheet.childCnt.displayName=Child Count ArtifactTypeNode.createSheet.childCnt.displayName=Child Count
ArtifactTypeNode.createSheet.childCnt.desc=no description ArtifactTypeNode.createSheet.childCnt.desc=no description
BlackboardArtifactNode.noDesc.text=no description BlackboardArtifactNode.noDesc.text=no description
BlackboardArtifactNode.createSheet.srcFile.name=Source File
BlackboardArtifactNode.createSheet.srcFile.displayName=Source File
BlackboardArtifactNode.createSheet.ext.name=Extension BlackboardArtifactNode.createSheet.ext.name=Extension
BlackboardArtifactNode.createSheet.ext.displayName=Extension BlackboardArtifactNode.createSheet.ext.displayName=Extension
BlackboardArtifactNode.createSheet.mimeType.name=MIME Type BlackboardArtifactNode.createSheet.mimeType.name=MIME Type
@ -348,6 +346,8 @@ TagNameNode.bbArtTagTypeNodeKey.text=Result Tags
TagNameNode.bookmark.text=Bookmark TagNameNode.bookmark.text=Bookmark
TagNameNode.createSheet.name.name=Name TagNameNode.createSheet.name.name=Name
TagNameNode.createSheet.name.displayName=Name TagNameNode.createSheet.name.displayName=Name
TagNode.propertySheet.origName=Original Name
TagNode.propertySheet.origNameDisplayName=Original Name
TagsNode.displayName.text=Tags TagsNode.displayName.text=Tags
TagsNode.createSheet.name.name=Name TagsNode.createSheet.name.name=Name
TagsNode.createSheet.name.displayName=Name TagsNode.createSheet.name.displayName=Name

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2013-2016 Basis Technology Corp. * Copyright 2013-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -24,7 +24,6 @@ import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.Action; import javax.swing.Action;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.openide.nodes.Children;
import org.openide.nodes.Sheet; import org.openide.nodes.Sheet;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
@ -39,18 +38,16 @@ import org.sleuthkit.datamodel.TskCoreException;
/** /**
* Instances of this class wrap ContentTag objects. In the Autopsy presentation * Instances of this class wrap ContentTag objects. In the Autopsy presentation
* of the SleuthKit data model, they are leaf nodes of a tree consisting of * of the SleuthKit data model, they are leaf nodes of a tree consisting of
* content and blackboard artifact tags, grouped first by tag type, then by tag * content and artifact tags, grouped first by tag type, then by tag name.
* name.
*/ */
class ContentTagNode extends DisplayableItemNode { class ContentTagNode extends TagNode {
private static final Logger LOGGER = Logger.getLogger(ContentTagNode.class.getName()); private static final Logger LOGGER = Logger.getLogger(ContentTagNode.class.getName());
private static final String ICON_PATH = "org/sleuthkit/autopsy/images/blue-tag-icon-16.png"; //NON-NLS private static final String ICON_PATH = "org/sleuthkit/autopsy/images/blue-tag-icon-16.png"; //NON-NLS
private final ContentTag tag; private final ContentTag tag;
public ContentTagNode(ContentTag tag) { ContentTagNode(ContentTag tag) {
super(Children.LEAF, Lookups.fixed(tag, tag.getContent())); super(Lookups.fixed(tag, tag.getContent()), tag.getContent());
super.setName(tag.getContent().getName()); super.setName(tag.getContent().getName());
super.setDisplayName(tag.getContent().getName()); super.setDisplayName(tag.getContent().getName());
this.setIconBaseWithExtension(ICON_PATH); this.setIconBaseWithExtension(ICON_PATH);
@ -58,6 +55,7 @@ class ContentTagNode extends DisplayableItemNode {
} }
@Messages({ @Messages({
"ContentTagNode.createSheet.origFileName=Original Name",
"ContentTagNode.createSheet.artifactMD5.displayName=MD5 Hash", "ContentTagNode.createSheet.artifactMD5.displayName=MD5 Hash",
"ContentTagNode.createSheet.artifactMD5.name=MD5 Hash", "ContentTagNode.createSheet.artifactMD5.name=MD5 Hash",
"ContentTagNode.createSheet.userName.text=User Name"}) "ContentTagNode.createSheet.userName.text=User Name"})
@ -79,15 +77,19 @@ class ContentTagNode extends DisplayableItemNode {
properties = Sheet.createPropertiesSet(); properties = Sheet.createPropertiesSet();
propertySheet.put(properties); propertySheet.put(properties);
} }
properties.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.file.name"), properties.put(new NodeProperty<>(
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.file.name"),
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.file.displayName"), NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.file.displayName"),
"", "",
content.getName())); content.getName()));
properties.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.filePath.name"), addOriginalNameProp(properties);
properties.put(new NodeProperty<>(
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.filePath.name"),
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.filePath.displayName"), NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.filePath.displayName"),
"", "",
contentPath)); contentPath));
properties.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.comment.name"), properties.put(new NodeProperty<>(
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.comment.name"),
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.comment.displayName"), NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.comment.displayName"),
"", "",
tag.getComment())); tag.getComment()));
@ -95,23 +97,28 @@ class ContentTagNode extends DisplayableItemNode {
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileModifiedTime.displayName"), NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileModifiedTime.displayName"),
"", "",
file != null ? ContentUtils.getStringTime(file.getMtime(), file) : "")); file != null ? ContentUtils.getStringTime(file.getMtime(), file) : ""));
properties.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileChangedTime.name"), properties.put(new NodeProperty<>(
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileChangedTime.name"),
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileChangedTime.displayName"), NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileChangedTime.displayName"),
"", "",
file != null ? ContentUtils.getStringTime(file.getCtime(), file) : "")); file != null ? ContentUtils.getStringTime(file.getCtime(), file) : ""));
properties.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileAccessedTime.name"), properties.put(new NodeProperty<>(
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileAccessedTime.name"),
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileAccessedTime.displayName"), NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileAccessedTime.displayName"),
"", "",
file != null ? ContentUtils.getStringTime(file.getAtime(), file) : "")); file != null ? ContentUtils.getStringTime(file.getAtime(), file) : ""));
properties.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileCreatedTime.name"), properties.put(new NodeProperty<>(
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileCreatedTime.name"),
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileCreatedTime.displayName"), NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileCreatedTime.displayName"),
"", "",
file != null ? ContentUtils.getStringTime(file.getCrtime(), file) : "")); file != null ? ContentUtils.getStringTime(file.getCrtime(), file) : ""));
properties.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileSize.name"), properties.put(new NodeProperty<>(
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileSize.name"),
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileSize.displayName"), NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileSize.displayName"),
"", "",
content.getSize())); content.getSize()));
properties.put(new NodeProperty<>(Bundle.ContentTagNode_createSheet_artifactMD5_name(), properties.put(new NodeProperty<>(
Bundle.ContentTagNode_createSheet_artifactMD5_name(),
Bundle.ContentTagNode_createSheet_artifactMD5_displayName(), Bundle.ContentTagNode_createSheet_artifactMD5_displayName(),
"", "",
file != null ? StringUtils.defaultString(file.getMd5Hash()) : "")); file != null ? StringUtils.defaultString(file.getMd5Hash()) : ""));
@ -128,8 +135,7 @@ class ContentTagNode extends DisplayableItemNode {
List<Action> actions = new ArrayList<>(); List<Action> actions = new ArrayList<>();
actions.addAll(Arrays.asList(super.getActions(context))); actions.addAll(Arrays.asList(super.getActions(context)));
AbstractFile file = getLookup().lookup(AbstractFile.class AbstractFile file = getLookup().lookup(AbstractFile.class);
);
if (file != null) { if (file != null) {
actions.add(ViewFileInTimelineAction.createViewFileAction(file)); actions.add(ViewFileInTimelineAction.createViewFileAction(file));
} }
@ -144,13 +150,9 @@ class ContentTagNode extends DisplayableItemNode {
return visitor.visit(this); return visitor.visit(this);
} }
@Override
public boolean isLeafTypeNode() {
return true;
}
@Override @Override
public String getItemType() { public String getItemType() {
return getClass().getName(); return getClass().getName();
} }
} }

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2017 Basis Technology Corp. * Copyright 2012-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.datamodel;
import org.openide.nodes.AbstractNode; import org.openide.nodes.AbstractNode;
import org.openide.nodes.Children; import org.openide.nodes.Children;
import org.openide.nodes.Sheet;
import org.openide.util.Lookup; import org.openide.util.Lookup;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
@ -142,4 +143,26 @@ public abstract class DisplayableItemNode extends AbstractNode {
return selectedChildNodeInfo; return selectedChildNodeInfo;
} }
/**
* Updates the node property sheet by replacing existing properties with new
* properties with the same property name.
*
* @param newProps The replacement property objects.
*/
protected synchronized final void updatePropertySheet(NodeProperty<?>... newProps) {
Sheet currentSheet = this.getSheet();
Sheet.Set currentPropsSet = currentSheet.get(Sheet.PROPERTIES);
Property<?>[] currentProps = currentPropsSet.getProperties();
for (NodeProperty<?> newProp : newProps) {
for (int i = 0; i < currentProps.length; i++) {
if (currentProps[i].getName().equals(newProp.getName())) {
currentProps[i] = newProp;
}
}
}
currentPropsSet.put(currentProps);
currentSheet.put(currentPropsSet);
this.setSheet(currentSheet);
}
} }

View File

@ -0,0 +1,128 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020-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.datamodel;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import org.openide.nodes.Children;
import org.openide.nodes.Sheet;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.datamodel.utils.FileNameTransTask;
import org.sleuthkit.autopsy.texttranslation.TextTranslationService;
import org.sleuthkit.datamodel.Content;
/**
* An abstract superclass for a node that represents a tag, uses the name of a
* given Content object as its display name, and has a property sheet with an
* original name property when machine translation is enabled.
*
* The translation of the Content name is done in a background thread. The
* translated name is made the display name of the node and the untranslated
* name is put into both the original name property and into the node's tooltip.
*
* TODO (Jira-6174): Consider modifying this class to be able to use it more broadly
* within the Autopsy data model (i.e., AbstractNode suclasses). It's not really
* specific to a tag node.
*/
@NbBundle.Messages({
"TagNode.propertySheet.origName=Original Name",
"TagNode.propertySheet.origNameDisplayName=Original Name"
})
abstract class TagNode extends DisplayableItemNode {
private final static String ORIG_NAME_PROP_NAME = Bundle.TagNode_propertySheet_origName();
private final static String ORIG_NAME_PROP_DISPLAY_NAME = Bundle.TagNode_propertySheet_origNameDisplayName();
private final String originalName;
private volatile String translatedName;
/**
* An abstract superclass for a node that represents a tag, uses the name of
* a given Content object as its display name, and has a property sheet with
* an untranslated file name property when machine translation is enabled.
*
* @param lookup The Lookup of the node.
* @param content The Content to use for the node display name.
*/
TagNode(Lookup lookup, Content content) {
super(Children.LEAF, lookup);
originalName = content.getName();
}
@Override
public boolean isLeafTypeNode() {
return true;
}
@Override
abstract public String getItemType();
@Override
abstract public <T> T accept(DisplayableItemNodeVisitor<T> visitor);
/**
* Adds an original name property to the node's property sheet and submits
* an original name translation task.
*
* The translation of the original name is done in a background thread. The
* translated name is made the display name of the node and the untranslated
* name is put into both the original name property and into the node's
* tooltip.
*
* @param properties The node's property sheet.
*/
protected void addOriginalNameProp(Sheet.Set properties) {
if (TextTranslationService.getInstance().hasProvider() && UserPreferences.displayTranslatedFileNames()) {
properties.put(new NodeProperty<>(
ORIG_NAME_PROP_NAME,
ORIG_NAME_PROP_DISPLAY_NAME,
"",
translatedName != null ? originalName : ""));
if (translatedName == null) {
new FileNameTransTask(originalName, this, new NameTranslationListener()).submit();
}
}
}
/**
* A listener for PropertyChangeEvents from a background task used to
* translate the original display name associated with the node.
*/
private class NameTranslationListener implements PropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent evt) {
String eventType = evt.getPropertyName();
if (eventType.equals(FileNameTransTask.getPropertyName())) {
translatedName = evt.getNewValue().toString();
String originalName = evt.getOldValue().toString();
setDisplayName(translatedName);
setShortDescription(originalName);
updatePropertySheet(new NodeProperty<>(
ORIG_NAME_PROP_NAME,
ORIG_NAME_PROP_DISPLAY_NAME,
"",
originalName));
}
}
}
}

View File

@ -64,7 +64,7 @@ public abstract class AbstractNodePropertySheetTask<T extends AbstractNode> impl
* @return The Future of the task, may be used for task cancellation by * @return The Future of the task, may be used for task cancellation by
* calling Future.cancel(true). * calling Future.cancel(true).
*/ */
public static Future<?> submitTask(AbstractNodePropertySheetTask<?> task) { private static Future<?> submitTask(AbstractNodePropertySheetTask<?> task) {
return executor.submit(task); return executor.submit(task);
} }
@ -104,12 +104,22 @@ public abstract class AbstractNodePropertySheetTask<T extends AbstractNode> impl
* *
* @param node The AbstractNode. * @param node The AbstractNode.
* *
* @return The result of the computation as a PropertyChangeEvent. * @return The result of the computation as a PropertyChangeEvent, may be
* null.
*/ */
protected abstract PropertyChangeEvent computePropertyValue(T node) throws Exception; protected abstract PropertyChangeEvent computePropertyValue(T node) throws Exception;
/**
* Submits this task to the ExecutorService for the thread pool.
*
* @return The task's Future from the ExecutorService.
*/
public final Future<?> submit() {
return submitTask(this);
}
@Override @Override
final public void run() { public final void run() {
try { try {
T node = this.weakNodeRef.get(); T node = this.weakNodeRef.get();
PropertyChangeListener listener = this.weakListenerRef.get(); PropertyChangeListener listener = this.weakListenerRef.get();

View File

@ -0,0 +1,61 @@
/*
* Autopsy Forensic Browser
*
* 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.datamodel.utils;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import org.openide.nodes.AbstractNode;
import org.sleuthkit.autopsy.texttranslation.utils.FileNameTranslationUtil;
/**
* An AbstractNodePropertySheetTask that translates a file name for an
* AbstractNode's property sheet.
*/
public class FileNameTransTask extends AbstractNodePropertySheetTask<AbstractNode> {
private final static String EVENT_SOURCE = FileNameTransTask.class.getName();
private final static String PROPERTY_NAME = EVENT_SOURCE + ".TranslatedFileName";
private final String fileName;
public static String getPropertyName() {
return PROPERTY_NAME;
}
/**
* Constructs an AbstractNodePropertySheetTask that translates a file name
* for an AbstractNode's property sheet. When the translation is complete, a
* PropertyChangeEvent will be fired to the node's PropertyChangeListener.
* Call getPropertyName() to identify the property.
*
* @param node The node.
* @param listener The node's PropertyChangeListener.
* @param fileName THe file name.
*/
public FileNameTransTask(String fileName, AbstractNode node, PropertyChangeListener listener) {
super(node, listener);
this.fileName = fileName;
}
@Override
protected PropertyChangeEvent computePropertyValue(AbstractNode node) throws Exception {
String translatedFileName = FileNameTranslationUtil.translate(fileName);
return translatedFileName.isEmpty() ? null : new PropertyChangeEvent(EVENT_SOURCE, PROPERTY_NAME, fileName, translatedFileName);
}
}

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2019 Basis Technology Corp. * Copyright 2012-2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -187,27 +187,6 @@ public class DataResultFilterNode extends FilterNode {
return propertySets; return propertySets;
} }
/**
* Gets the display name for the wrapped node.
*
* OutlineView used in the DataResult table uses getDisplayName() to
* populate the first column, which is Source File.
*
* Hence this override to return the 'correct' displayName for the wrapped
* node.
*
* @return The display name for the node.
*/
@Override
public String getDisplayName() {
final Node orig = getOriginal();
String name = orig.getDisplayName();
if ((orig instanceof BlackboardArtifactNode)) {
name = ((BlackboardArtifactNode) orig).getSourceName();
}
return name;
}
/** /**
* Adds information about which child node of this node, if any, should be * Adds information about which child node of this node, if any, should be
* selected. Can be null. * selected. Can be null.

View File

@ -23,7 +23,6 @@ import com.google.common.cache.CacheBuilder;
import com.google.common.io.Files; import com.google.common.io.Files;
import java.awt.Image; import java.awt.Image;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.IOException; import java.io.IOException;
import java.io.Reader; import java.io.Reader;
import java.nio.file.Paths; import java.nio.file.Paths;
@ -95,6 +94,7 @@ class FileSearch {
.build(); .build();
private static final int PREVIEW_SIZE = 256; private static final int PREVIEW_SIZE = 256;
private static volatile TextSummarizer summarizerToUse = null; private static volatile TextSummarizer summarizerToUse = null;
private static final BufferedImage VIDEO_DEFAULT_IMAGE = getDefaultVideoThumbnail();
/** /**
* Run the file search and returns the SearchResults object for debugging. * Run the file search and returns the SearchResults object for debugging.
@ -456,6 +456,20 @@ class FileSearch {
+ "AND blackboard_artifacts.obj_id IN (" + objIdList + ") "; // NON-NLS + "AND blackboard_artifacts.obj_id IN (" + objIdList + ") "; // NON-NLS
} }
/**
* Get the default image to display when a thumbnail is not available.
*
* @return The default video thumbnail.
*/
private static BufferedImage getDefaultVideoThumbnail() {
try {
return ImageIO.read(ImageUtils.class.getResourceAsStream("/org/sleuthkit/autopsy/images/failedToCreateVideoThumb.png"));//NON-NLS
} catch (IOException ex) {
logger.log(Level.SEVERE, "Failed to load 'failed to create video' placeholder.", ex); //NON-NLS
}
return null;
}
/** /**
* Get the video thumbnails for a file which exists in a * Get the video thumbnails for a file which exists in a
* VideoThumbnailsWrapper and update the VideoThumbnailsWrapper to include * VideoThumbnailsWrapper and update the VideoThumbnailsWrapper to include
@ -476,7 +490,6 @@ class FileSearch {
cacheDirectory = null; cacheDirectory = null;
logger.log(Level.WARNING, "Unable to get cache directory, video thumbnails will not be saved", ex); logger.log(Level.WARNING, "Unable to get cache directory, video thumbnails will not be saved", ex);
} }
if (cacheDirectory == null || file.getMd5Hash() == null || !Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, file.getMd5Hash()).toFile().exists()) { if (cacheDirectory == null || file.getMd5Hash() == null || !Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, file.getMd5Hash()).toFile().exists()) {
java.io.File tempFile; java.io.File tempFile;
try { try {
@ -488,7 +501,7 @@ class FileSearch {
0, 0,
0, 0,
0}; 0};
thumbnailWrapper.setThumbnails(createDefaultThumbnailList(), framePositions); thumbnailWrapper.setThumbnails(createDefaultThumbnailList(VIDEO_DEFAULT_IMAGE), framePositions);
return; return;
} }
if (tempFile.exists() == false || tempFile.length() < file.getSize()) { if (tempFile.exists() == false || tempFile.length() < file.getSize()) {
@ -502,7 +515,7 @@ class FileSearch {
0, 0,
0, 0,
0}; 0};
thumbnailWrapper.setThumbnails(createDefaultThumbnailList(), framePositions); thumbnailWrapper.setThumbnails(createDefaultThumbnailList(VIDEO_DEFAULT_IMAGE), framePositions);
return; return;
} }
ContentUtils.writeToFile(file, tempFile, progress, null, true); ContentUtils.writeToFile(file, tempFile, progress, null, true);
@ -523,7 +536,7 @@ class FileSearch {
0, 0,
0, 0,
0}; 0};
thumbnailWrapper.setThumbnails(createDefaultThumbnailList(), framePositions); thumbnailWrapper.setThumbnails(createDefaultThumbnailList(VIDEO_DEFAULT_IMAGE), framePositions);
return; return;
} }
double fps = videoFile.get(5); // gets frame per second double fps = videoFile.get(5); // gets frame per second
@ -535,7 +548,7 @@ class FileSearch {
0, 0,
0, 0,
0}; 0};
thumbnailWrapper.setThumbnails(createDefaultThumbnailList(), framePositions); thumbnailWrapper.setThumbnails(createDefaultThumbnailList(VIDEO_DEFAULT_IMAGE), framePositions);
return; return;
} }
if (Thread.interrupted()) { if (Thread.interrupted()) {
@ -544,7 +557,7 @@ class FileSearch {
0, 0,
0, 0,
0}; 0};
thumbnailWrapper.setThumbnails(createDefaultThumbnailList(), framePositions); thumbnailWrapper.setThumbnails(createDefaultThumbnailList(VIDEO_DEFAULT_IMAGE), framePositions);
return; return;
} }
@ -573,10 +586,10 @@ class FileSearch {
logger.log(Level.WARNING, "Error seeking to " + framePositions[i] + "ms in {0}", file.getParentPath() + "/" + file.getName()); //NON-NLS logger.log(Level.WARNING, "Error seeking to " + framePositions[i] + "ms in {0}", file.getParentPath() + "/" + file.getName()); //NON-NLS
// If we can't set the time, continue to the next frame position and try again. // If we can't set the time, continue to the next frame position and try again.
videoThumbnails.add(ImageUtils.getDefaultThumbnail()); videoThumbnails.add(VIDEO_DEFAULT_IMAGE);
if (cacheDirectory != null) { if (cacheDirectory != null) {
try { try {
ImageIO.write((RenderedImage) ImageUtils.getDefaultThumbnail(), THUMBNAIL_FORMAT, ImageIO.write(VIDEO_DEFAULT_IMAGE, THUMBNAIL_FORMAT,
Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, file.getMd5Hash(), i + "-" + framePositions[i] + "." + THUMBNAIL_FORMAT).toFile()); //NON-NLS) Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, file.getMd5Hash(), i + "-" + framePositions[i] + "." + THUMBNAIL_FORMAT).toFile()); //NON-NLS)
} catch (IOException ex) { } catch (IOException ex) {
logger.log(Level.WARNING, "Unable to save default video thumbnail for " + file.getMd5Hash() + " at frame position " + framePositions[i], ex); logger.log(Level.WARNING, "Unable to save default video thumbnail for " + file.getMd5Hash() + " at frame position " + framePositions[i], ex);
@ -588,10 +601,10 @@ class FileSearch {
if (!videoFile.read(imageMatrix)) { if (!videoFile.read(imageMatrix)) {
logger.log(Level.WARNING, "Error reading frame at " + framePositions[i] + "ms from {0}", file.getParentPath() + "/" + file.getName()); //NON-NLS logger.log(Level.WARNING, "Error reading frame at " + framePositions[i] + "ms from {0}", file.getParentPath() + "/" + file.getName()); //NON-NLS
// If the image is bad for some reason, continue to the next frame position and try again. // If the image is bad for some reason, continue to the next frame position and try again.
videoThumbnails.add(ImageUtils.getDefaultThumbnail()); videoThumbnails.add(VIDEO_DEFAULT_IMAGE);
if (cacheDirectory != null) { if (cacheDirectory != null) {
try { try {
ImageIO.write((RenderedImage) ImageUtils.getDefaultThumbnail(), THUMBNAIL_FORMAT, ImageIO.write(VIDEO_DEFAULT_IMAGE, THUMBNAIL_FORMAT,
Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, file.getMd5Hash(), i + "-" + framePositions[i] + "." + THUMBNAIL_FORMAT).toFile()); //NON-NLS) Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, file.getMd5Hash(), i + "-" + framePositions[i] + "." + THUMBNAIL_FORMAT).toFile()); //NON-NLS)
} catch (IOException ex) { } catch (IOException ex) {
logger.log(Level.WARNING, "Unable to save default video thumbnail for " + file.getMd5Hash() + " at frame position " + framePositions[i], ex); logger.log(Level.WARNING, "Unable to save default video thumbnail for " + file.getMd5Hash() + " at frame position " + framePositions[i], ex);
@ -602,10 +615,10 @@ class FileSearch {
} }
// If the image is empty, return since no buffered image can be created. // If the image is empty, return since no buffered image can be created.
if (imageMatrix.empty()) { if (imageMatrix.empty()) {
videoThumbnails.add(ImageUtils.getDefaultThumbnail()); videoThumbnails.add(VIDEO_DEFAULT_IMAGE);
if (cacheDirectory != null) { if (cacheDirectory != null) {
try { try {
ImageIO.write((RenderedImage) ImageUtils.getDefaultThumbnail(), THUMBNAIL_FORMAT, ImageIO.write(VIDEO_DEFAULT_IMAGE, THUMBNAIL_FORMAT,
Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, file.getMd5Hash(), i + "-" + framePositions[i] + "." + THUMBNAIL_FORMAT).toFile()); //NON-NLS) Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, file.getMd5Hash(), i + "-" + framePositions[i] + "." + THUMBNAIL_FORMAT).toFile()); //NON-NLS)
} catch (IOException ex) { } catch (IOException ex) {
logger.log(Level.WARNING, "Unable to save default video thumbnail for " + file.getMd5Hash() + " at frame position " + framePositions[i], ex); logger.log(Level.WARNING, "Unable to save default video thumbnail for " + file.getMd5Hash() + " at frame position " + framePositions[i], ex);
@ -660,7 +673,7 @@ class FileSearch {
videoFile.release(); // close the file} videoFile.release(); // close the file}
} }
} else { } else {
loadSavedThumbnails(cacheDirectory, thumbnailWrapper); loadSavedThumbnails(cacheDirectory, thumbnailWrapper, VIDEO_DEFAULT_IMAGE);
} }
} }
@ -674,7 +687,7 @@ class FileSearch {
* information about the file and the thumbnails * information about the file and the thumbnails
* associated with it. * associated with it.
*/ */
private static void loadSavedThumbnails(String cacheDirectory, VideoThumbnailsWrapper thumbnailWrapper) { private static void loadSavedThumbnails(String cacheDirectory, VideoThumbnailsWrapper thumbnailWrapper, BufferedImage failedVideoThumbImage) {
int[] framePositions = new int[4]; int[] framePositions = new int[4];
List<Image> videoThumbnails = new ArrayList<>(); List<Image> videoThumbnails = new ArrayList<>();
int thumbnailNumber = 0; int thumbnailNumber = 0;
@ -683,7 +696,7 @@ class FileSearch {
try { try {
videoThumbnails.add(ImageIO.read(Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, md5, fileName).toFile())); videoThumbnails.add(ImageIO.read(Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, md5, fileName).toFile()));
} catch (IOException ex) { } catch (IOException ex) {
videoThumbnails.add(ImageUtils.getDefaultThumbnail()); videoThumbnails.add(failedVideoThumbImage);
logger.log(Level.WARNING, "Unable to read saved video thumbnail " + fileName + " for " + md5, ex); logger.log(Level.WARNING, "Unable to read saved video thumbnail " + fileName + " for " + md5, ex);
} }
int framePos = Integer.valueOf(FilenameUtils.getBaseName(fileName).substring(2)); int framePos = Integer.valueOf(FilenameUtils.getBaseName(fileName).substring(2));
@ -699,12 +712,12 @@ class FileSearch {
* *
* @return List containing the default thumbnail. * @return List containing the default thumbnail.
*/ */
private static List<Image> createDefaultThumbnailList() { private static List<Image> createDefaultThumbnailList(BufferedImage failedVideoThumbImage) {
List<Image> videoThumbnails = new ArrayList<>(); List<Image> videoThumbnails = new ArrayList<>();
videoThumbnails.add(ImageUtils.getDefaultThumbnail()); videoThumbnails.add(failedVideoThumbImage);
videoThumbnails.add(ImageUtils.getDefaultThumbnail()); videoThumbnails.add(failedVideoThumbImage);
videoThumbnails.add(ImageUtils.getDefaultThumbnail()); videoThumbnails.add(failedVideoThumbImage);
videoThumbnails.add(ImageUtils.getDefaultThumbnail()); videoThumbnails.add(failedVideoThumbImage);
return videoThumbnails; return videoThumbnails;
} }

View File

@ -273,7 +273,6 @@ final class FileSearchData {
= new ImmutableSet.Builder<String>() = new ImmutableSet.Builder<String>()
.add("text/html", //NON-NLS .add("text/html", //NON-NLS
"text/csv", //NON-NLS "text/csv", //NON-NLS
"text/x-log", //NON-NLS
"application/rtf", //NON-NLS "application/rtf", //NON-NLS
"application/pdf", //NON-NLS "application/pdf", //NON-NLS
"application/xhtml+xml", //NON-NLS "application/xhtml+xml", //NON-NLS

View File

@ -434,7 +434,7 @@ final public class MapPanel extends javax.swing.JPanel {
/** /**
* Find the waypoint that is closest to the given mouse click point. * Find the waypoint that is closest to the given mouse click point.
* *
* @param mouseClickPoint The mouse click point * @param clickPoint The mouse click point
* *
* @return A waypoint that is within 10 pixels of the given point, or null * @return A waypoint that is within 10 pixels of the given point, or null
* if none was found. * if none was found.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -81,9 +81,9 @@ public abstract class TranslateTextTask extends SwingWorker<TranslateTextTask.Tr
} }
/** /**
* This is the main contructor for the TranslateTextTask. * This is the main constructor for the TranslateTextTask.
* @param translateText whether or not to translate text * @param translateText whether or not to translate text
* @param contentDescriptor the content descriptor for the item being * @param fileDescriptor the content descriptor for the item being
* translated (used for logging errors) * translated (used for logging errors)
*/ */
public TranslateTextTask(boolean translateText, String fileDescriptor) { public TranslateTextTask(boolean translateText, String fileDescriptor) {

View File

@ -164,9 +164,7 @@ public final class TranslatedTextViewer implements TextViewer {
} }
/** /**
* Extracts text from the given node * Extracts text from the current node
*
* @param file Selected node in UI
* *
* @return Extracted text * @return Extracted text
* *

View File

@ -0,0 +1,74 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020-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.texttranslation.utils;
import org.apache.commons.io.FilenameUtils;
import org.sleuthkit.autopsy.texttranslation.NoServiceProviderException;
import org.sleuthkit.autopsy.texttranslation.TextTranslationService;
import org.sleuthkit.autopsy.texttranslation.TranslationException;
/**
* A utility to translate file names.
*/
public final class FileNameTranslationUtil {
/**
* Translates a file name using the configured machine translation service.
*
* @param fileName The file name.
*
* @return The translation of the file name.
*
* @throws NoServiceProviderException If machine translation is not
* configured.
* @throws TranslationException If there is an error doing the
* translation.
*/
public static String translate(String fileName) throws NoServiceProviderException, TranslationException {
/*
* Don't attempt translation if the characters of the file name are all
* ASCII chars.
*
* TODO (Jira-6175): This filter prevents translation of many
* non-English file names composed entirely of Latin chars.
*/
if (fileName.matches("^\\p{ASCII}+$")) {
return "";
}
TextTranslationService translator = TextTranslationService.getInstance();
String baseName = FilenameUtils.getBaseName(fileName);
String translation = translator.translate(baseName);
if (!translation.isEmpty()) {
String extension = FilenameUtils.getExtension(fileName);
if (!extension.isEmpty()) {
String extensionDelimiter = (extension.isEmpty()) ? "" : ".";
translation += extensionDelimiter + extension;
}
}
return translation;
}
/**
* Prevent instantiation of this utility class
*/
private FileNameTranslationUtil() {
}
}

View File

@ -130,8 +130,7 @@ public class CentralRepoDatamodelTest extends TestCase {
dbSettingsSqlite.saveSettings(); dbSettingsSqlite.saveSettings();
CentralRepoDbUtil.setUseCentralRepo(true); CentralRepoDbUtil.setUseCentralRepo(true);
CentralRepoPlatforms.setSelectedPlatform(CentralRepoPlatforms.SQLITE.name()); CentralRepoDbManager.saveDbChoice(CentralRepoDbChoice.SQLITE);
CentralRepoPlatforms.saveSelectedPlatform();
} catch (CentralRepoException ex) { } catch (CentralRepoException ex) {
Exceptions.printStackTrace(ex); Exceptions.printStackTrace(ex);
Assert.fail(ex.getMessage()); Assert.fail(ex.getMessage());

View File

@ -47,6 +47,8 @@ import org.sleuthkit.autopsy.testutils.IngestUtils;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import junit.framework.Assert; import junit.framework.Assert;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoDbChoice;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoDbManager;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase;
import org.sleuthkit.autopsy.coreutils.TimeStampUtils; import org.sleuthkit.autopsy.coreutils.TimeStampUtils;
@ -303,8 +305,7 @@ class InterCaseTestUtils {
centralRepoSchemaFactory.insertDefaultDatabaseContent(); centralRepoSchemaFactory.insertDefaultDatabaseContent();
crSettings.saveSettings(); crSettings.saveSettings();
CentralRepoPlatforms.setSelectedPlatform(CentralRepoPlatforms.SQLITE.name()); CentralRepoDbManager.saveDbChoice(CentralRepoDbChoice.SQLITE);
CentralRepoPlatforms.saveSelectedPlatform();
} }
/** /**

View File

@ -14,8 +14,7 @@
<!-- for viewers --> <!-- for viewers -->
<dependency conf="autopsy_core->*" org="org.freedesktop.gstreamer" name="gst1-java-core" rev="1.0.0"/> <dependency conf="autopsy_core->*" org="org.freedesktop.gstreamer" name="gst1-java-core" rev="1.0.0"/>
<dependency conf="autopsy_core->*" org="net.java.dev.jna" name="jna" rev="3.4.0"/> <dependency conf="autopsy_core->*" org="net.java.dev.jna" name="jna-platform" rev="5.5.0"/>
<dependency conf="autopsy_core->*" org="net.java.dev.jna" name="platform" rev="3.4.0"/>
<!-- for file search --> <!-- for file search -->
<dependency conf="autopsy_core->*" org="com.github.lgooddatepicker" name="LGoodDatePicker" rev="10.3.1"/> <dependency conf="autopsy_core->*" org="com.github.lgooddatepicker" name="LGoodDatePicker" rev="10.3.1"/>
@ -73,8 +72,5 @@
<dependency conf="autopsy_core->default" org="com.googlecode.plist" name="dd-plist" rev="1.20"/> <dependency conf="autopsy_core->default" org="com.googlecode.plist" name="dd-plist" rev="1.20"/>
<exclude org="*" ext="*" type="javadoc"/> <exclude org="*" ext="*" type="javadoc"/>
<!-- conflict resolutions for multiple JAR versions -->
<conflict org="net.java.dev.jna" module="jna" rev="3.4.0"/>
<conflict org="net.java.dev.jna" module="platform" rev="3.4.0"/>
</dependencies> </dependencies>
</ivy-module> </ivy-module>

View File

@ -21,7 +21,6 @@ file.reference.dom4j-1.6.1.jar=release/modules/ext/dom4j-1.6.1.jar
file.reference.geronimo-jms_1.1_spec-1.0.jar=release/modules/ext/geronimo-jms_1.1_spec-1.0.jar file.reference.geronimo-jms_1.1_spec-1.0.jar=release/modules/ext/geronimo-jms_1.1_spec-1.0.jar
file.reference.gson-2.8.5.jar=release/modules/ext/gson-2.8.5.jar file.reference.gson-2.8.5.jar=release/modules/ext/gson-2.8.5.jar
file.reference.gst1-java-core-1.0.0.jar=release\\modules\\ext\\gst1-java-core-1.0.0.jar file.reference.gst1-java-core-1.0.0.jar=release\\modules\\ext\\gst1-java-core-1.0.0.jar
file.reference.jna-3.4.0.jar=release/modules/ext/jna-3.4.0.jar
file.reference.guava-19.0.jar=release/modules/ext/guava-19.0.jar file.reference.guava-19.0.jar=release/modules/ext/guava-19.0.jar
file.reference.imageio-bmp-3.2.jar=release/modules/ext/imageio-bmp-3.2.jar file.reference.imageio-bmp-3.2.jar=release/modules/ext/imageio-bmp-3.2.jar
file.reference.imageio-core-3.2.jar=release/modules/ext/imageio-core-3.2.jar file.reference.imageio-core-3.2.jar=release/modules/ext/imageio-core-3.2.jar
@ -44,6 +43,8 @@ file.reference.jfxtras-common-8.0-r4.jar=release/modules/ext/jfxtras-common-8.0-
file.reference.jfxtras-controls-8.0-r4.jar=release/modules/ext/jfxtras-controls-8.0-r4.jar file.reference.jfxtras-controls-8.0-r4.jar=release/modules/ext/jfxtras-controls-8.0-r4.jar
file.reference.jfxtras-fxml-8.0-r4.jar=release/modules/ext/jfxtras-fxml-8.0-r4.jar file.reference.jfxtras-fxml-8.0-r4.jar=release/modules/ext/jfxtras-fxml-8.0-r4.jar
file.reference.jna-3.4.0.jar=release/modules/ext/jna-3.4.0.jar file.reference.jna-3.4.0.jar=release/modules/ext/jna-3.4.0.jar
file.reference.jna-5.5.0.jar=release\\modules\\ext\\jna-5.5.0.jar
file.reference.jna-platform-5.5.0.jar=release\\modules\\ext\\jna-platform-5.5.0.jar
file.reference.joda-time-2.4.jar=release/modules/ext/joda-time-2.4.jar file.reference.joda-time-2.4.jar=release/modules/ext/joda-time-2.4.jar
file.reference.jsr305-1.3.9.jar=release/modules/ext/jsr305-1.3.9.jar file.reference.jsr305-1.3.9.jar=release/modules/ext/jsr305-1.3.9.jar
file.reference.LGoodDatePicker-10.3.1.jar=release/modules/ext/LGoodDatePicker-10.3.1.jar file.reference.LGoodDatePicker-10.3.1.jar=release/modules/ext/LGoodDatePicker-10.3.1.jar
@ -52,7 +53,6 @@ file.reference.logkit-1.0.1.jar=release/modules/ext/logkit-1.0.1.jar
file.reference.mail-1.4.3.jar=release/modules/ext/mail-1.4.3.jar file.reference.mail-1.4.3.jar=release/modules/ext/mail-1.4.3.jar
file.reference.opencv-248.jar=release/modules/ext/opencv-248.jar file.reference.opencv-248.jar=release/modules/ext/opencv-248.jar
file.reference.openjfx-dialogs-1.0.2.jar=release/modules/ext/openjfx-dialogs-1.0.3.jar file.reference.openjfx-dialogs-1.0.2.jar=release/modules/ext/openjfx-dialogs-1.0.3.jar
file.reference.platform-3.4.0.jar=release/modules/ext/platform-3.4.0.jar
file.reference.poi-4.0.1.jar=release\\modules\\ext\\poi-4.0.1.jar file.reference.poi-4.0.1.jar=release\\modules\\ext\\poi-4.0.1.jar
file.reference.poi-excelant-4.0.1.jar=release\\modules\\ext\\poi-excelant-4.0.1.jar file.reference.poi-excelant-4.0.1.jar=release\\modules\\ext\\poi-excelant-4.0.1.jar
file.reference.poi-ooxml-4.0.1.jar=release\\modules\\ext\\poi-ooxml-4.0.1.jar file.reference.poi-ooxml-4.0.1.jar=release\\modules\\ext\\poi-ooxml-4.0.1.jar

View File

@ -806,10 +806,6 @@
<runtime-relative-path>ext/sigar-1.6.4.jar</runtime-relative-path> <runtime-relative-path>ext/sigar-1.6.4.jar</runtime-relative-path>
<binary-origin>release/modules/ext/sigar-1.6.4.jar</binary-origin> <binary-origin>release/modules/ext/sigar-1.6.4.jar</binary-origin>
</class-path-extension> </class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/jna-3.4.0.jar</runtime-relative-path>
<binary-origin>release/modules/ext/jna-3.4.0.jar</binary-origin>
</class-path-extension>
<class-path-extension> <class-path-extension>
<runtime-relative-path>ext/gson-2.8.5.jar</runtime-relative-path> <runtime-relative-path>ext/gson-2.8.5.jar</runtime-relative-path>
<binary-origin>release/modules/ext/gson-2.8.5.jar</binary-origin> <binary-origin>release/modules/ext/gson-2.8.5.jar</binary-origin>
@ -902,6 +898,10 @@
<runtime-relative-path>ext/commons-csv-1.4.jar</runtime-relative-path> <runtime-relative-path>ext/commons-csv-1.4.jar</runtime-relative-path>
<binary-origin>release/modules/ext/commons-csv-1.4.jar</binary-origin> <binary-origin>release/modules/ext/commons-csv-1.4.jar</binary-origin>
</class-path-extension> </class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/jna-5.5.0.jar</runtime-relative-path>
<binary-origin>release/modules/ext/jna-5.5.0.jar</binary-origin>
</class-path-extension>
<class-path-extension> <class-path-extension>
<runtime-relative-path>ext/imageio-sgi-3.2.jar</runtime-relative-path> <runtime-relative-path>ext/imageio-sgi-3.2.jar</runtime-relative-path>
<binary-origin>release/modules/ext/imageio-sgi-3.2.jar</binary-origin> <binary-origin>release/modules/ext/imageio-sgi-3.2.jar</binary-origin>
@ -946,10 +946,6 @@
<runtime-relative-path>ext/imageio-bmp-3.2.jar</runtime-relative-path> <runtime-relative-path>ext/imageio-bmp-3.2.jar</runtime-relative-path>
<binary-origin>release/modules/ext/imageio-bmp-3.2.jar</binary-origin> <binary-origin>release/modules/ext/imageio-bmp-3.2.jar</binary-origin>
</class-path-extension> </class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/platform-3.4.0.jar</runtime-relative-path>
<binary-origin>release/modules/ext/platform-3.4.0.jar</binary-origin>
</class-path-extension>
<class-path-extension> <class-path-extension>
<runtime-relative-path>ext/commons-lang-2.6.jar</runtime-relative-path> <runtime-relative-path>ext/commons-lang-2.6.jar</runtime-relative-path>
<binary-origin>release/modules/ext/commons-lang-2.6.jar</binary-origin> <binary-origin>release/modules/ext/commons-lang-2.6.jar</binary-origin>
@ -1018,6 +1014,10 @@
<runtime-relative-path>ext/dom4j-1.6.1.jar</runtime-relative-path> <runtime-relative-path>ext/dom4j-1.6.1.jar</runtime-relative-path>
<binary-origin>release/modules/ext/dom4j-1.6.1.jar</binary-origin> <binary-origin>release/modules/ext/dom4j-1.6.1.jar</binary-origin>
</class-path-extension> </class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/jna-platform-5.5.0.jar</runtime-relative-path>
<binary-origin>release/modules/ext/jna-platform-5.5.0.jar</binary-origin>
</class-path-extension>
<class-path-extension> <class-path-extension>
<runtime-relative-path>ext/imageio-metadata-3.2.jar</runtime-relative-path> <runtime-relative-path>ext/imageio-metadata-3.2.jar</runtime-relative-path>
<binary-origin>release/modules/ext/imageio-metadata-3.2.jar</binary-origin> <binary-origin>release/modules/ext/imageio-metadata-3.2.jar</binary-origin>

View File

@ -95,7 +95,7 @@ class BrowserLocationAnalyzer(general.AndroidComponentAnalyzer):
longitude = Double.valueOf(resultSet.getString("longitude")) longitude = Double.valueOf(resultSet.getString("longitude"))
attributes = ArrayList() attributes = ArrayList()
artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACKPOINT) artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_BOOKMARK)
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE, general.MODULE_NAME, latitude)) attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE, general.MODULE_NAME, latitude))
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE, general.MODULE_NAME, longitude)) attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE, general.MODULE_NAME, longitude))
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, general.MODULE_NAME, timestamp)) attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, general.MODULE_NAME, timestamp))

View File

@ -41,6 +41,7 @@ from org.sleuthkit.datamodel import TskCoreException
import traceback import traceback
import general import general
import struct
""" """
Parses cache files that Android maintains for Wifi and cell towers. Adds GPS points to blackboard. Parses cache files that Android maintains for Wifi and cell towers. Adds GPS points to blackboard.
@ -74,60 +75,24 @@ class CacheLocationAnalyzer(general.AndroidComponentAnalyzer):
def __findGeoLocationsInFile(self, file, abstractFile): def __findGeoLocationsInFile(self, file, abstractFile):
tempBytes = bytearray([0] * 2) # will temporarily hold bytes to be converted into the correct data types
try: try:
inputStream = FileInputStream(file) # code to parse the cache.wifi and cache.cell taken from https://forensics.spreitzenbarth.de/2011/10/28/decoding-cache-cell-and-cache-wifi-files/
cacheFile = open(str(file), 'rb')
inputStream.read(tempBytes) # version (version, entries) = struct.unpack('>hh', cacheFile.read(4))
i = 0
tempBytes = bytearray([0] * 2) while i < entries:
inputStream.read(tempBytes) # number of location entries key = cacheFile.read(struct.unpack('>h', cacheFile.read(2))[0])
(accuracy, confidence, latitude, longitude, readtime) = struct.unpack('>iiddQ', cacheFile.read(32))
iterations = BigInteger(tempBytes).intValue() timestamp = readtime/1000
i = i + 1
for i in range(iterations): # loop through every entry
tempBytes = bytearray([0] * 2)
inputStream.read(tempBytes)
tempBytes = bytearray([0])
inputStream.read(tempBytes)
while BigInteger(tempBytes).intValue() != 0: # pass through non important values until the start of accuracy(around 7-10 bytes)
if 0 > inputStream.read(tempBytes):
break # we've passed the end of the file, so stop
tempBytes = bytearray([0] * 3)
inputStream.read(tempBytes)
if BigInteger(tempBytes).intValue() <= 0: # This refers to a location that could not be calculated
tempBytes = bytearray([0] * 28) # read rest of the row's bytes
inputStream.read(tempBytes)
continue
accuracy = "" + BigInteger(tempBytes).intValue()
tempBytes = bytearray([0] * 4)
inputStream.read(tempBytes)
confidence = "" + BigInteger(tempBytes).intValue()
tempBytes = bytearray([0] * 8)
inputStream.read(tempBytes)
latitude = CacheLocationAnalyzer.toDouble(bytes)
tempBytes = bytearray([0] * 8)
inputStream.read(tempBytes)
longitude = CacheLocationAnalyzer.toDouble(bytes)
tempBytes = bytearray([0] * 8)
inputStream.read(tempBytes)
timestamp = BigInteger(tempBytes).longValue() / 1000
attributes = ArrayList() attributes = ArrayList()
artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACKPOINT) artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_BOOKMARK)
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE, AndroidAnalyzer.MODULE_NAME, latitude)) attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE, general.MODULE_NAME, latitude))
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE, AndroidAnalyzer.MODULE_NAME, longitude)) attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE, general.MODULE_NAME, longitude))
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, AndroidModuleFactorymodule.Name, timestamp)) attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, general.MODULE_NAME, timestamp))
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, AndroidAnalyzer.MODULE_NAME, attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, general.MODULE_NAME,
file.getName() + "Location History")) abstractFile.getName() + " Location History"))
artifact.addAttributes(attributes) artifact.addAttributes(attributes)
#Not storing these for now. #Not storing these for now.
@ -136,15 +101,13 @@ class CacheLocationAnalyzer(general.AndroidComponentAnalyzer):
try: try:
# index the artifact for keyword search # index the artifact for keyword search
blackboard = Case.getCurrentCase().getSleuthkitCase().getBlackboard() blackboard = Case.getCurrentCase().getSleuthkitCase().getBlackboard()
blackboard.postArtifact(artifact, MODULE_NAME) blackboard.postArtifact(artifact, general.MODULE_NAME)
except Blackboard.BlackboardException as ex: except Blackboard.BlackboardException as ex:
self._logger.log(Level.SEVERE, "Unable to index blackboard artifact " + str(artifact.getArtifactID()), ex) self._logger.log(Level.SEVERE, "Unable to index blackboard artifact " + str(artifact.getArtifactID()), ex)
self._logger.log(Level.SEVERE, traceback.format_exc()) self._logger.log(Level.SEVERE, traceback.format_exc())
MessageNotifyUtil.Notify.error("Failed to index GPS trackpoint artifact for keyword search.", artifact.getDisplayName()) MessageNotifyUtil.Notify.error("Failed to index GPS trackpoint artifact for keyword search.", artifact.getDisplayName())
cacheFile.close()
except SQLException as ex:
# Unable to execute Cached GPS locations SQL query against database.
pass
except Exception as ex: except Exception as ex:
self._logger.log(Level.SEVERE, "Error parsing Cached GPS locations to blackboard", ex) self._logger.log(Level.SEVERE, "Error parsing Cached GPS locations to blackboard", ex)
self._logger.log(Level.SEVERE, traceback.format_exc()) self._logger.log(Level.SEVERE, traceback.format_exc())

View File

@ -117,6 +117,7 @@ class ViberAnalyzer(general.AndroidComponentAnalyzer):
try: try:
contacts_parser = ViberContactsParser(contacts_db) contacts_parser = ViberContactsParser(contacts_db)
while contacts_parser.next(): while contacts_parser.next():
if (not(not contacts_parser.get_phone() or contacts_parser.get_phone().isspace())):
helper.addContact( helper.addContact(
contacts_parser.get_contact_name(), contacts_parser.get_contact_name(),
contacts_parser.get_phone(), contacts_parser.get_phone(),
@ -124,6 +125,17 @@ class ViberAnalyzer(general.AndroidComponentAnalyzer):
contacts_parser.get_mobile_phone(), contacts_parser.get_mobile_phone(),
contacts_parser.get_email() contacts_parser.get_email()
) )
# Check if contact_name is blank and if it is not create a TSK_CONTACT otherwise ignore as not Contact Info
elif (not(not contacts_parser.get_contact_name() or contacts_parser.get_contact_name().isspace())):
current_case = Case.getCurrentCase().getSleuthkitCase()
attributes = ArrayList()
artifact = contacts_db.getDBFile().newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT)
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME.getTypeID(), self._PARSER_NAME, contacts_parser.get_contact_name()))
artifact.addAttributes(attributes)
# Post the artifact to blackboard
current_case.getBlackboard().postArtifact(artifact, self._PARSER_NAME)
contacts_parser.close() contacts_parser.close()
except SQLException as ex: except SQLException as ex:
self._logger.log(Level.WARNING, "Error querying the viber database for contacts.", ex) self._logger.log(Level.WARNING, "Error querying the viber database for contacts.", ex)
@ -269,7 +281,7 @@ class ViberContactsParser(TskContactsParser):
super(ViberContactsParser, self).__init__(contact_db.runQuery( super(ViberContactsParser, self).__init__(contact_db.runQuery(
""" """
SELECT C.display_name AS name, SELECT C.display_name AS name,
D.data2 AS number coalesce(D.data2, D.data1, D.data3) AS number
FROM phonebookcontact AS C FROM phonebookcontact AS C
JOIN phonebookdata AS D JOIN phonebookdata AS D
ON C._id = D.contact_id ON C._id = D.contact_id