Merge commit 'e978a4b759b68741ade89ca69656089c75844e51' of https://github.com/sleuthkit/autopsy into 6077-DisplayImageForSummary

This commit is contained in:
William Schaefer 2020-03-16 10:12:57 -04:00
commit c75adc72d2
59 changed files with 4025 additions and 1425 deletions

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2012-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");
@ -1114,7 +1114,7 @@ public class Case {
CallableSystemAction.get(CaseCloseAction.class).setEnabled(true); CallableSystemAction.get(CaseCloseAction.class).setEnabled(true);
CallableSystemAction.get(CaseDetailsAction.class).setEnabled(true); CallableSystemAction.get(CaseDetailsAction.class).setEnabled(true);
CallableSystemAction.get(DataSourceSummaryAction.class).setEnabled(true); CallableSystemAction.get(DataSourceSummaryAction.class).setEnabled(true);
CallableSystemAction.get(CaseDeleteAction.class).setEnabled(true); CallableSystemAction.get(CaseDeleteAction.class).setEnabled(FeatureAccessUtils.canDeleteCurrentCase());
CallableSystemAction.get(OpenTimelineAction.class).setEnabled(true); CallableSystemAction.get(OpenTimelineAction.class).setEnabled(true);
CallableSystemAction.get(OpenCommVisualizationToolAction.class).setEnabled(true); CallableSystemAction.get(OpenCommVisualizationToolAction.class).setEnabled(true);
CallableSystemAction.get(CommonAttributeSearchAction.class).setEnabled(true); CallableSystemAction.get(CommonAttributeSearchAction.class).setEnabled(true);

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2018 Basis Technology Corp. * Copyright 2011-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");
@ -35,6 +35,7 @@ import org.openide.util.NbBundle.Messages;
import org.openide.util.actions.CallableSystemAction; import org.openide.util.actions.CallableSystemAction;
import org.openide.windows.WindowManager; import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.featureaccess.FeatureAccessUtils;
/** /**
* The action associated with the Delete button of the Case Properties panel. It * The action associated with the Delete button of the Case Properties panel. It
@ -54,7 +55,7 @@ final class CaseDeleteAction extends CallableSystemAction {
/* /*
* A value of 'null' signifies that there is no case open. * A value of 'null' signifies that there is no case open.
*/ */
setEnabled(null != evt.getNewValue()); setEnabled(null != evt.getNewValue() && FeatureAccessUtils.canDeleteCurrentCase());
}); });
} }

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();
/**
* 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.
*
* @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);
}
}
/** /**
* obtains the database connectivity for central repository * 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 to connect to * @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;
return dbSettingsPostgres; if (selectedDbChoice == CentralRepoDbChoice.POSTGRESQL_CUSTOM)
case SQLITE: return dbSettingsPostgres;
return dbSettingsSqlite; if (selectedDbChoice == CentralRepoDbChoice.SQLITE)
case DISABLED: return dbSettingsSqlite;
return null; if (selectedDbChoice == CentralRepoDbChoice.DISABLED)
default: return null;
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);
return null; if (selectedDbChoice == CentralRepoDbChoice.DISABLED)
default: return null;
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,82 +492,82 @@ 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; else if (selectedDbChoice == CentralRepoDbChoice.SQLITE) {
case 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; else if (selectedDbChoice != CentralRepoDbChoice.POSTGRESQL_MULTIUSER) {
default: throw new IllegalStateException("Central Repo has an unknown selected platform: " + selectedDbChoice);
throw new IllegalStateException("Central Repo has an unknown selected platform: " + selectedPlatform);
} }
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,115 @@
/*
* 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.getCreateArtifactInstancesTableTemplate(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 {
// Alter the existing X_Instance tables 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.
* *

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,173 @@
/*
*
* 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() {}
private void logException(TryHandler handler) {
try {
handler.operation();
}
catch (CentralRepoException | NumberFormatException e) {
LOGGER.log(Level.WARNING, "There was an error in converting central repo postgres settings", e);
}
}
/**
* This interface represents an action that potentially throws an exception.
*/
private interface TryHandler {
void operation() 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;
}
logException(() -> settings.setHost(muConn.getHost()));
logException(() -> settings.setDbName(PostgresConnectionSettings.DEFAULT_DBNAME));
logException(() -> settings.setUserName(muConn.getUserName()));
logException(() -> settings.setPort(Integer.parseInt(muConn.getPort())));
logException(() -> settings.setBulkThreshold(RdbmsCentralRepo.DEFAULT_BULK_THRESHHOLD));
logException(() -> settings.setPassword(muConn.getPassword()));
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);
logException(() -> settings.setHost(keyVals.get(HOST_KEY)));
logException(() -> settings.setDbName(keyVals.get(DBNAME_KEY)));
logException(() -> settings.setUserName(keyVals.get(USER_KEY)));
logException(() -> settings.setPort(Integer.parseInt(keyVals.get(PORT_KEY))));
logException(() -> settings.setBulkThreshold(Integer.parseInt(keyVals.get((BULK_THRESHOLD_KEY)))));
String passwordHex = keyVals.get(PASSWORD_KEY);
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;
logException(() -> settings.setPassword(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

@ -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,106 +34,84 @@ 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 final PostgresSettingsLoader loader;
private int port; private PostgresConnectionSettings connSettings;
private String dbName;
private int bulkThreshold; private static PostgresSettingsLoader getLoaderFromSaved() throws CentralRepoException {
private String userName; CentralRepoDbChoice choice = CentralRepoDbManager.getSavedDbChoice();
private String password; if (choice == CentralRepoDbChoice.POSTGRESQL_CUSTOM)
return PostgresSettingsLoader.CUSTOM_SETTINGS_LOADER;
public PostgresCentralRepoSettings() { 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
if (portString == null || portString.isEmpty()) {
port = DEFAULT_PORT;
} else {
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()) {
dbName = DEFAULT_DBNAME;
}
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 * @return the POSTGRES_DRIVER
ModuleSettings.setConfigSetting("CentralRepository", "db.postgresql.port", Integer.toString(port)); // NON-NLS */
ModuleSettings.setConfigSetting("CentralRepository", "db.postgresql.dbName", getDbName()); // NON-NLS String getDriver() {
ModuleSettings.setConfigSetting("CentralRepository", "db.postgresql.bulkThreshold", Integer.toString(getBulkThreshold())); // NON-NLS return JDBC_DRIVER;
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);
}
} }
/**
* @return the JDBC_BASE_URI
*/
String getJDBCBaseURI() {
return JDBC_BASE_URI;
}
/** /**
* Get the full connection URL as a String * Get the full connection URL as a String
* *
@ -301,73 +273,33 @@ 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 }
@Override
public DatabaseTestResult testStatus() {
if (verifyConnection()) {
if (verifyDatabaseExists()) {
if (verifyDatabaseSchema()) {
return DatabaseTestResult.TESTED_OK;
} else {
return DatabaseTestResult.SCHEMA_INVALID;
}
} else {
return DatabaseTestResult.DB_DOES_NOT_EXIST;
}
} else {
return DatabaseTestResult.CONNECTION_FAILED;
} }
this.password = password;
} }
/**
* @return the VALIDATION_QUERY
*/
String getValidationQuery() {
return VALIDATION_QUERY;
}
/**
* @return the POSTGRES_DRIVER
*/
String getDriver() {
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;
@ -3553,7 +3553,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 +3808,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

@ -134,7 +134,7 @@ public class RdbmsCentralRepoFactory {
stmt.execute("INSERT INTO db_info (name, value) VALUES ('" + RdbmsCentralRepo.CREATION_SCHEMA_MAJOR_VERSION_KEY + "', '" + SOFTWARE_CR_DB_SCHEMA_VERSION.getMajor() + "')"); stmt.execute("INSERT INTO db_info (name, value) VALUES ('" + RdbmsCentralRepo.CREATION_SCHEMA_MAJOR_VERSION_KEY + "', '" + SOFTWARE_CR_DB_SCHEMA_VERSION.getMajor() + "')");
stmt.execute("INSERT INTO db_info (name, value) VALUES ('" + RdbmsCentralRepo.CREATION_SCHEMA_MINOR_VERSION_KEY + "', '" + SOFTWARE_CR_DB_SCHEMA_VERSION.getMinor() + "')"); stmt.execute("INSERT INTO db_info (name, value) VALUES ('" + RdbmsCentralRepo.CREATION_SCHEMA_MINOR_VERSION_KEY + "', '" + SOFTWARE_CR_DB_SCHEMA_VERSION.getMinor() + "')");
// Create account_types and accounts tab;es which are referred by X_instances tables // Create account_types and accounts tables which are referred by X_instances tables
stmt.execute(getCreateAccountTypesTableStatement(selectedPlatform)); stmt.execute(getCreateAccountTypesTableStatement(selectedPlatform));
stmt.execute(getCreateAccountsTableStatement(selectedPlatform)); stmt.execute(getCreateAccountsTableStatement(selectedPlatform));
@ -161,7 +161,8 @@ public class RdbmsCentralRepoFactory {
stmt.execute(String.format(getReferenceTypeValueKnownstatusIndexTemplate(), reference_type_dbname, reference_type_dbname)); stmt.execute(String.format(getReferenceTypeValueKnownstatusIndexTemplate(), reference_type_dbname, reference_type_dbname));
} }
} }
createPersonaTables(stmt); // @TODO: uncomment this when ready to create Persona tables.
//createPersonaTables(stmt);
} catch (SQLException ex) { } catch (SQLException ex) {
LOGGER.log(Level.SEVERE, "Error initializing db schema.", ex); // NON-NLS LOGGER.log(Level.SEVERE, "Error initializing db schema.", ex); // NON-NLS
return false; return false;
@ -191,8 +192,10 @@ public class RdbmsCentralRepoFactory {
} }
result = CentralRepoDbUtil.insertDefaultCorrelationTypes(conn) result = CentralRepoDbUtil.insertDefaultCorrelationTypes(conn)
&& CentralRepoDbUtil.insertDefaultOrganization(conn) && CentralRepoDbUtil.insertDefaultOrganization(conn) &&
&& insertDefaultPersonaTablesContent(conn); insertDefaultAccountsTablesContent(conn);
// @TODO: uncomment when ready to create/populate persona tables
// && insertDefaultPersonaTablesContent(conn);
} catch (SQLException ex) { } catch (SQLException ex) {
LOGGER.log(Level.SEVERE, String.format("Failed to populate default data in CR tables."), ex); LOGGER.log(Level.SEVERE, String.format("Failed to populate default data in CR tables."), ex);
@ -529,7 +532,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 ";
@ -576,7 +579,6 @@ public class RdbmsCentralRepoFactory {
stmt.execute(getCreateConfidenceTableStatement(selectedPlatform)); stmt.execute(getCreateConfidenceTableStatement(selectedPlatform));
stmt.execute(getCreateExaminersTableStatement(selectedPlatform)); stmt.execute(getCreateExaminersTableStatement(selectedPlatform));
stmt.execute(getCreatePersonaStatusTableStatement(selectedPlatform)); stmt.execute(getCreatePersonaStatusTableStatement(selectedPlatform));
stmt.execute(getCreateAliasesTableStatement(selectedPlatform));
stmt.execute(getCreatePersonasTableStatement(selectedPlatform)); stmt.execute(getCreatePersonasTableStatement(selectedPlatform));
stmt.execute(getCreatePersonaAliasTableStatement(selectedPlatform)); stmt.execute(getCreatePersonaAliasTableStatement(selectedPlatform));
@ -653,20 +655,6 @@ public class RdbmsCentralRepoFactory {
+ ")"; + ")";
} }
/**
* Get the SQL String for creating a new aliases table in a central
* repository.
*
* @return SQL string for creating aliases table
*/
static String getCreateAliasesTableStatement(CentralRepoPlatforms selectedPlatform) {
return "CREATE TABLE IF NOT EXISTS aliases ("
+ getNumericPrimaryKeyClause("id", selectedPlatform)
+ "alias TEXT NOT NULL,"
+ "CONSTRAINT alias_unique UNIQUE(alias)"
+ ")";
}
/** /**
* Get the SQL String for creating a new accounts table in a central * Get the SQL String for creating a new accounts table in a central
@ -719,13 +707,12 @@ public class RdbmsCentralRepoFactory {
return "CREATE TABLE IF NOT EXISTS persona_alias (" return "CREATE TABLE IF NOT EXISTS persona_alias ("
+ getNumericPrimaryKeyClause("id", selectedPlatform) + getNumericPrimaryKeyClause("id", selectedPlatform)
+ "persona_id " + getBigIntType(selectedPlatform) + " ," + "persona_id " + getBigIntType(selectedPlatform) + " ,"
+ "alias_id " + getBigIntType(selectedPlatform) + " ," + "alias TEXT NOT NULL, "
+ "justification TEXT NOT NULL," + "justification TEXT NOT NULL,"
+ "confidence_id integer NOT NULL," + "confidence_id integer NOT NULL,"
+ "date_added " + getBigIntType(selectedPlatform) + " ," + "date_added " + getBigIntType(selectedPlatform) + " ,"
+ "examiner_id integer NOT NULL," + "examiner_id integer NOT NULL,"
+ "FOREIGN KEY (persona_id) REFERENCES personas(id)," + "FOREIGN KEY (persona_id) REFERENCES personas(id),"
+ "FOREIGN KEY (alias_id) REFERENCES aliases(id),"
+ "FOREIGN KEY (confidence_id) REFERENCES confidence(confidence_id)," + "FOREIGN KEY (confidence_id) REFERENCES confidence(confidence_id),"
+ "FOREIGN KEY (examiner_id) REFERENCES examiners(id)" + "FOREIGN KEY (examiner_id) REFERENCES examiners(id)"
+ ")"; + ")";
@ -779,6 +766,33 @@ 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.
* *
@ -786,12 +800,9 @@ 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) {
Statement stmt = null; try (Statement stmt = conn.createStatement()) {
try {
stmt = conn.createStatement();
// populate the confidence table // populate the confidence table
for (Confidence confidence : Persona.Confidence.values()) { for (Confidence confidence : Persona.Confidence.values()) {
String sqlString = "INSERT INTO confidence (confidence_id, description) VALUES ( " + confidence.getLevel() + ", '" + confidence.toString() + "')" //NON-NLS String sqlString = "INSERT INTO confidence (confidence_id, description) VALUES ( " + confidence.getLevel() + ", '" + confidence.toString() + "')" //NON-NLS
@ -806,27 +817,38 @@ public class RdbmsCentralRepoFactory {
stmt.execute(sqlString); stmt.execute(sqlString);
} }
} catch (SQLException ex) {
LOGGER.log(Level.SEVERE, String.format("Failed to populate default data in Persona tables."), ex);
return false;
}
return true;
}
/**
* 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 // Populate the account_types table
for (Account.Type type : Account.Type.PREDEFINED_ACCOUNT_TYPES) { for (Account.Type type : Account.Type.PREDEFINED_ACCOUNT_TYPES) {
int correlationTypeId = getCorrelationTypeIdForAccountType(conn, type); int correlationTypeId = getCorrelationTypeIdForAccountType(conn, type);
if (correlationTypeId > 0) { if (correlationTypeId > 0) {
String sqlString = String.format("INSERT INTO account_types (type_name, display_name, correlation_type_id) VALUES ('%s', '%s', %d)" + getOnConflictDoNothingClause(selectedPlatform), 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); type.getTypeName(), type.getDisplayName(), correlationTypeId);
stmt.execute(sqlString); stmt.execute(sqlString);
} }
} }
} catch (SQLException ex) { } catch (SQLException ex) {
LOGGER.log(Level.SEVERE, String.format("Failed to populate default data in Persona tables."), ex); LOGGER.log(Level.SEVERE, String.format("Failed to populate default data in account_types table."), ex);
return false; return false;
} finally {
if (stmt != null) {
try {
stmt.close();
} catch (SQLException ex2) {
LOGGER.log(Level.SEVERE, "Error closing statement.", ex2);
}
}
} }
return true; return true;
@ -841,7 +863,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());
@ -45,7 +45,7 @@ public final class SqliteCentralRepoSettings implements CentralRepoDbSettings {
private final static String JDBC_DRIVER = "org.sqlite.JDBC"; // NON-NLS private final static String JDBC_DRIVER = "org.sqlite.JDBC"; // NON-NLS
private final static String JDBC_BASE_URI = "jdbc:sqlite:"; // NON-NLS private final static String JDBC_BASE_URI = "jdbc:sqlite:"; // NON-NLS
private final static String VALIDATION_QUERY = "SELECT count(*) from sqlite_master"; // NON-NLS private final static String VALIDATION_QUERY = "SELECT count(*) from sqlite_master"; // NON-NLS
private final static String DB_NAMES_REGEX = "[a-z][a-z0-9_]*(\\.db)?"; private final static String DB_NAMES_REGEX = "[a-z][a-z0-9_]*(\\.db)?";
private String dbName; private String dbName;
private String dbDirectory; private String dbDirectory;
@ -80,11 +80,11 @@ public final class SqliteCentralRepoSettings implements CentralRepoDbSettings {
this.bulkThreshold = RdbmsCentralRepo.DEFAULT_BULK_THRESHHOLD; this.bulkThreshold = RdbmsCentralRepo.DEFAULT_BULK_THRESHHOLD;
} }
} }
public String toString() { public String toString() {
return String.format("SqliteCentralRepoSettings: [db type: sqlite, directory: %s, name: %s]", getDbDirectory(), getDbName()); return String.format("SqliteCentralRepoSettings: [db type: sqlite, directory: %s, name: %s]", getDbDirectory(), getDbName());
} }
/** /**
* sets database directory and name to defaults * sets database directory and name to defaults
*/ */
@ -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

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

View File

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

View File

@ -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,25 +19,31 @@
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.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.JList;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import javax.swing.JTextField; import javax.swing.JTextField;
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;
import javax.swing.filechooser.FileFilter; import javax.swing.filechooser.FileFilter;
import javax.swing.plaf.basic.BasicComboBoxRenderer;
import org.netbeans.spi.options.OptionsPanelController; 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 +51,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,11 +61,42 @@ 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 BasicComboBoxRenderer {
private static final long serialVersionUID = 1L;
public Component getListCellRendererComponent(JList list, Object value,
int index, boolean isSelected, boolean cellHasFocus) {
CentralRepoDbChoice item = (CentralRepoDbChoice) value;
// disable cell if it is the db connection from multi user settings
// and that option is not enabled in multi user settings
setText(item.getTitle());
setEnabled(isDbChoiceSelectable(item));
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);
}
/** /**
* Creates new form EamDbSettingsDialog * Creates new form EamDbSettingsDialog
*/ */
@ -69,7 +104,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 +130,45 @@ 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;
// set the renderer so item is unselectable if inappropriate
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,36 +186,49 @@ 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;
}
JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
errorMessage,
Bundle.EamDbSettingsDialog_okButton_createDbError_title(),
JOptionPane.WARNING_MESSAGE);
}
valid();
} }
} }
return (manager.getStatus() == DatabaseTestResult.TESTEDOK); 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(),
errorMessage,
Bundle.EamDbSettingsDialog_okButton_createDbError_title(),
JOptionPane.WARNING_MESSAGE);
}
/** /**
@ -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);
dispose();
}//GEN-LAST:event_bnOkActionPerformed
/**
* 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(); manager.testStatus();
valid();
boolean testedOk = promptTestStatusWarnings(); 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;
} }
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
dispose();
}//GEN-LAST:event_bnOkActionPerformed
parent.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
return true;
}
/** /**
* 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,33 +33,39 @@ 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
*/ */
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel implements OptionsPanel { public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel implements OptionsPanel {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static final Logger logger = Logger.getLogger(GlobalSettingsPanel.class.getName()); private static final Logger logger = Logger.getLogger(GlobalSettingsPanel.class.getName());
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();
@ -68,7 +74,8 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
ingestStateUpdated(evt.getNewValue() != null); ingestStateUpdated(evt.getNewValue() != null);
}); });
} }
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);
}
/**
* 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);
}
}
if (CentralRepoPlatforms.getSelectedPlatform().equals(DISABLED)) {
/**
* 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()); lbDbNameValue.setText("");
enableButtonSubComponents(cbUseCentralRepo.isSelected()); lbDbLocationValue.setText("");
break; tbOops.setText(Bundle.GlobalSettingsPanel_validationerrMsg_mustConfigure());
case SQLITE: }
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(); SqliteCentralRepoSettings dbSettingsSqlite = new SqliteCentralRepoSettings();
lbDbPlatformValue.setText(CentralRepoPlatforms.SQLITE.toString());
lbDbNameValue.setText(dbSettingsSqlite.getDbName()); lbDbNameValue.setText(dbSettingsSqlite.getDbName());
lbDbLocationValue.setText(dbSettingsSqlite.getDbDirectory()); lbDbLocationValue.setText(dbSettingsSqlite.getDbDirectory());
enableButtonSubComponents(cbUseCentralRepo.isSelected()); }
break;
default:
lbDbPlatformValue.setText(CentralRepoPlatforms.DISABLED.toString());
lbDbNameValue.setText("");
lbDbLocationValue.setText("");
tbOops.setText(Bundle.GlobalSettingsPanel_validationerrMsg_mustConfigure());
break;
} }
} }
@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

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

@ -168,3 +168,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.translatedText=Translated Text

View File

@ -241,7 +241,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);
@ -264,20 +264,23 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
//Manage the video while the user is performing actions on the track. //Manage the video while the user is performing actions on the track.
progressSlider.addMouseListener(new MouseListener() { progressSlider.addMouseListener(new MouseListener() {
private State previousState = State.NULL; private State previousState = State.NULL;
@Override @Override
public void mousePressed(MouseEvent e) { public void mousePressed(MouseEvent e) {
previousState = gstPlayBin.getState(); if (gstPlayBin != null) {
gstPlayBin.pause(); previousState = gstPlayBin.getState();
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) {
} }
@ -289,11 +292,11 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
@Override @Override
public void mouseExited(MouseEvent e) { public void mouseExited(MouseEvent e) {
} }
}); });
//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,11 +330,13 @@ 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) {
gstPlayBin.seek(ClockTime.ZERO); if (gstPlayBin != null) {
/** 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());
}
} }
}; };
} }
@ -556,10 +561,12 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
//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
Bus playBinBus = gstPlayBin.getBus(); if (gstPlayBin != null) {
playBinBus.connect(endOfStreamListener); Bus playBinBus = gstPlayBin.getBus();
playBinBus.connect(stateChangeListener); playBinBus.connect(endOfStreamListener);
playBinBus.connect(errorListener); playBinBus.connect(stateChangeListener);
playBinBus.connect(errorListener);
}
if (this.isCancelled()) { if (this.isCancelled()) {
return; return;
@ -570,15 +577,16 @@ 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);
gstPlayBin.setVideoSink(fxAppSink); if (gstPlayBin != null) {
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();
enableComponents(true); enableComponents(true);
} catch (CancellationException ex) { } catch (CancellationException ex) {
@ -598,7 +606,7 @@ 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) {
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);
@ -635,13 +643,13 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
* thumb at the given width and height. It also paints the track blue as * thumb at the given width and height. It also paints the track blue as
* the thumb progresses. * the thumb progresses.
* *
* @param slider JSlider component * @param slider JSlider component
* @param thumbDimension * @param thumbDimension
*/ */
public CircularJSliderUI(JSlider slider, Dimension thumbDimension) { public CircularJSliderUI(JSlider slider, Dimension thumbDimension) {
super(slider); super(slider);
this.thumbDimension = thumbDimension; this.thumbDimension = thumbDimension;
//Configure track and thumb colors. //Configure track and thumb colors.
Color lightBlue = new Color(0, 130, 255); Color lightBlue = new Color(0, 130, 255);
thumbColor = lightBlue; thumbColor = lightBlue;
@ -655,8 +663,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,12 +713,13 @@ 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
public void mousePressed(MouseEvent e) { public void mousePressed(MouseEvent e) {
@ -982,87 +991,95 @@ 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
long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS); if (gstPlayBin != null) {
//Skip 30 seconds.
long rewindDelta = TimeUnit.NANOSECONDS.convert(SKIP_IN_SECONDS, TimeUnit.SECONDS);
//Ensure new video position is within bounds
long newTime = Math.max(currentTime - rewindDelta, 0);
double playBackRate = getPlayBackRate();
gstPlayBin.seek(playBackRate,
Format.TIME,
//FLUSH - flushes the pipeline
//ACCURATE - video will seek exactly to the position requested
EnumSet.of(SeekFlags.FLUSH, SeekFlags.ACCURATE),
//Set the start position to newTime
SeekType.SET, newTime,
//Do nothing for the end position
SeekType.NONE, -1);
}//GEN-LAST:event_rewindButtonActionPerformed
private void fastForwardButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_fastForwardButtonActionPerformed
long duration = gstPlayBin.queryDuration(TimeUnit.NANOSECONDS);
long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
//Skip 30 seconds.
long fastForwardDelta = TimeUnit.NANOSECONDS.convert(SKIP_IN_SECONDS, TimeUnit.SECONDS);
//Don't allow skipping within 2 seconds of video ending. Skipping right to
//the end causes undefined behavior for some gstreamer plugins.
long twoSecondsInNano = TimeUnit.NANOSECONDS.convert(2, TimeUnit.SECONDS);
if((duration - currentTime) <= twoSecondsInNano) {
return;
}
long newTime;
if (currentTime + fastForwardDelta >= duration) {
//If there are less than 30 seconds left, only fast forward to the midpoint.
newTime = currentTime + (duration - currentTime)/2;
} else {
newTime = currentTime + fastForwardDelta;
}
double playBackRate = getPlayBackRate();
gstPlayBin.seek(playBackRate,
Format.TIME,
//FLUSH - flushes the pipeline
//ACCURATE - video will seek exactly to the position requested
EnumSet.of(SeekFlags.FLUSH, SeekFlags.ACCURATE),
//Set the start position to newTime
SeekType.SET, newTime,
//Do nothing for the end position
SeekType.NONE, -1);
}//GEN-LAST:event_fastForwardButtonActionPerformed
private void playButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_playButtonActionPerformed
if (gstPlayBin.isPlaying()) {
gstPlayBin.pause();
} else {
double playBackRate = getPlayBackRate();
long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS); long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
//Set playback rate before play. //Skip 30 seconds.
long rewindDelta = TimeUnit.NANOSECONDS.convert(SKIP_IN_SECONDS, TimeUnit.SECONDS);
//Ensure new video position is within bounds
long newTime = Math.max(currentTime - rewindDelta, 0);
double playBackRate = getPlayBackRate();
gstPlayBin.seek(playBackRate, gstPlayBin.seek(playBackRate,
Format.TIME, Format.TIME,
//FLUSH - flushes the pipeline //FLUSH - flushes the pipeline
//ACCURATE - video will seek exactly to the position requested //ACCURATE - video will seek exactly to the position requested
EnumSet.of(SeekFlags.FLUSH, SeekFlags.ACCURATE), EnumSet.of(SeekFlags.FLUSH, SeekFlags.ACCURATE),
//Set the start position to newTime //Set the start position to newTime
SeekType.SET, currentTime, SeekType.SET, newTime,
//Do nothing for the end position //Do nothing for the end position
SeekType.NONE, -1); SeekType.NONE, -1);
gstPlayBin.play(); }
}//GEN-LAST:event_rewindButtonActionPerformed
private void fastForwardButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_fastForwardButtonActionPerformed
if (gstPlayBin != null) {
long duration = gstPlayBin.queryDuration(TimeUnit.NANOSECONDS);
long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
//Skip 30 seconds.
long fastForwardDelta = TimeUnit.NANOSECONDS.convert(SKIP_IN_SECONDS, TimeUnit.SECONDS);
//Don't allow skipping within 2 seconds of video ending. Skipping right to
//the end causes undefined behavior for some gstreamer plugins.
long twoSecondsInNano = TimeUnit.NANOSECONDS.convert(2, TimeUnit.SECONDS);
if ((duration - currentTime) <= twoSecondsInNano) {
return;
}
long newTime;
if (currentTime + fastForwardDelta >= duration) {
//If there are less than 30 seconds left, only fast forward to the midpoint.
newTime = currentTime + (duration - currentTime) / 2;
} else {
newTime = currentTime + fastForwardDelta;
}
double playBackRate = getPlayBackRate();
gstPlayBin.seek(playBackRate,
Format.TIME,
//FLUSH - flushes the pipeline
//ACCURATE - video will seek exactly to the position requested
EnumSet.of(SeekFlags.FLUSH, SeekFlags.ACCURATE),
//Set the start position to newTime
SeekType.SET, newTime,
//Do nothing for the end position
SeekType.NONE, -1);
}
}//GEN-LAST:event_fastForwardButtonActionPerformed
private void playButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_playButtonActionPerformed
if (gstPlayBin != null) {
if (gstPlayBin.isPlaying()) {
gstPlayBin.pause();
} else {
double playBackRate = getPlayBackRate();
long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
//Set playback rate before play.
gstPlayBin.seek(playBackRate,
Format.TIME,
//FLUSH - flushes the pipeline
//ACCURATE - video will seek exactly to the position requested
EnumSet.of(SeekFlags.FLUSH, SeekFlags.ACCURATE),
//Set the start position to newTime
SeekType.SET, currentTime,
//Do nothing for the end position
SeekType.NONE, -1);
gstPlayBin.play();
}
} }
}//GEN-LAST:event_playButtonActionPerformed }//GEN-LAST:event_playButtonActionPerformed
private void playBackSpeedComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_playBackSpeedComboBoxActionPerformed private void playBackSpeedComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_playBackSpeedComboBoxActionPerformed
double playBackRate = getPlayBackRate(); if (gstPlayBin != null) {
long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS); double playBackRate = getPlayBackRate();
gstPlayBin.seek(playBackRate, long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
Format.TIME, gstPlayBin.seek(playBackRate,
//FLUSH - flushes the pipeline Format.TIME,
//ACCURATE - video will seek exactly to the position requested //FLUSH - flushes the pipeline
EnumSet.of(SeekFlags.FLUSH, SeekFlags.ACCURATE), //ACCURATE - video will seek exactly to the position requested
//Set the position to the currentTime, we are only adjusting the EnumSet.of(SeekFlags.FLUSH, SeekFlags.ACCURATE),
//playback rate. //Set the position to the currentTime, we are only adjusting the
SeekType.SET, currentTime, //playback rate.
SeekType.NONE, 0); SeekType.SET, currentTime,
SeekType.NONE, 0);
}
}//GEN-LAST:event_playBackSpeedComboBoxActionPerformed }//GEN-LAST:event_playBackSpeedComboBoxActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables

View File

@ -233,33 +233,6 @@
</Component> </Component>
</SubComponents> </SubComponents>
</Container> </Container>
<Container class="javax.swing.JScrollPane" name="textbodyScrollPane">
<Properties>
<Property name="horizontalScrollBarPolicy" type="int" value="31"/>
<Property name="verticalScrollBarPolicy" type="int" value="22"/>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
<JTabbedPaneConstraints tabName="Text">
<Property name="tabTitle" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MessageContentViewer.textbodyScrollPane.TabConstraints.tabTitle" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</JTabbedPaneConstraints>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JTextArea" name="textbodyTextArea">
<Properties>
<Property name="editable" type="boolean" value="false"/>
<Property name="lineWrap" type="boolean" value="true"/>
<Property name="rows" type="int" value="5"/>
<Property name="wrapStyleWord" type="boolean" value="true"/>
</Properties>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="htmlPane"> <Container class="javax.swing.JPanel" name="htmlPane">
<Constraints> <Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription"> <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">

View File

@ -22,6 +22,7 @@ import org.sleuthkit.autopsy.datamodel.AttachmentNode;
import com.google.gson.Gson; import com.google.gson.Gson;
import java.awt.Color; import java.awt.Color;
import java.awt.Component; import java.awt.Component;
import java.awt.ComponentOrientation;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
@ -29,6 +30,7 @@ import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.JScrollPane;
import javax.swing.text.JTextComponent; import javax.swing.text.JTextComponent;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup; import org.jsoup.Jsoup;
@ -41,7 +43,9 @@ import org.openide.util.NbBundle;
import org.openide.util.lookup.ServiceProvider; import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.contentviewers.TranslatablePanel.TranslatablePanelException;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
import org.sleuthkit.autopsy.corecomponents.AutoWrappingJTextPane;
import org.sleuthkit.autopsy.corecomponents.DataResultPanel; import org.sleuthkit.autopsy.corecomponents.DataResultPanel;
import org.sleuthkit.autopsy.corecomponents.TableFilterNode; import org.sleuthkit.autopsy.corecomponents.TableFilterNode;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
@ -84,6 +88,38 @@ import org.sleuthkit.datamodel.blackboardutils.attributes.MessageAttachments.URL
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
public class MessageContentViewer extends javax.swing.JPanel implements DataContentViewer { public class MessageContentViewer extends javax.swing.JPanel implements DataContentViewer {
/**
* This is a text component viewer to be a child component to be placed in a {@link TranslatablePanel TranslatablePanel}.
*/
class TextComponent implements TranslatablePanel.ContentComponent {
private final Component rootComponent;
private final AutoWrappingJTextPane childTextComponent;
TextComponent() {
childTextComponent = new AutoWrappingJTextPane();
childTextComponent.setEditable(false);
JScrollPane parentComponent = new JScrollPane();
parentComponent.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
parentComponent.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
parentComponent.setViewportView(childTextComponent);
rootComponent = parentComponent;
}
@Override
public Component getRootComponent() {
return rootComponent;
}
@Override
public void setContent(String content, ComponentOrientation orientation) throws TranslatablePanelException {
childTextComponent.setText(content == null ? "" : content);
childTextComponent.setComponentOrientation(orientation);
childTextComponent.setCaretPosition(0);
}
}
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static final Logger LOGGER = Logger.getLogger(MessageContentViewer.class.getName()); private static final Logger LOGGER = Logger.getLogger(MessageContentViewer.class.getName());
private static final BlackboardAttribute.Type TSK_ASSOCIATED_TYPE = new BlackboardAttribute.Type(TSK_ASSOCIATED_ARTIFACT); private static final BlackboardAttribute.Type TSK_ASSOCIATED_TYPE = new BlackboardAttribute.Type(TSK_ASSOCIATED_ARTIFACT);
@ -96,6 +132,7 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont
private final List<JTextComponent> textAreas; private final List<JTextComponent> textAreas;
private final org.sleuthkit.autopsy.contentviewers.HtmlPanel htmlPanel = new org.sleuthkit.autopsy.contentviewers.HtmlPanel(); private final org.sleuthkit.autopsy.contentviewers.HtmlPanel htmlPanel = new org.sleuthkit.autopsy.contentviewers.HtmlPanel();
private final TranslatablePanel textPanel = new TranslatablePanel(new TextComponent());
/** /**
* Artifact currently being displayed * Artifact currently being displayed
*/ */
@ -113,13 +150,21 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont
envelopePanel.setBackground(new Color(0, 0, 0, 38)); envelopePanel.setBackground(new Color(0, 0, 0, 38));
drp = DataResultPanel.createInstanceUninitialized(Bundle.MessageContentViewer_AtrachmentsPanel_title(), "", new TableFilterNode(Node.EMPTY, false), 0, null); drp = DataResultPanel.createInstanceUninitialized(Bundle.MessageContentViewer_AtrachmentsPanel_title(), "", new TableFilterNode(Node.EMPTY, false), 0, null);
attachmentsScrollPane.setViewportView(drp); attachmentsScrollPane.setViewportView(drp);
msgbodyTabbedPane.insertTab(
NbBundle.getMessage(MessageContentViewer.class, "MessageContentViewer.textbodyScrollPane.TabConstraints.tabTitle"),
null,
textPanel,
null,
TEXT_TAB_INDEX);
msgbodyTabbedPane.setEnabledAt(ATTM_TAB_INDEX, true); msgbodyTabbedPane.setEnabledAt(ATTM_TAB_INDEX, true);
/* /*
* HTML tab uses the HtmlPanel instead of an internal text pane, so we * HTML tab uses the HtmlPanel instead of an internal text pane, so we
* use 'null' for that index. * use 'null' for that index.
*/ */
textAreas = Arrays.asList(headersTextArea, textbodyTextArea, null, rtfbodyTextPane); textAreas = Arrays.asList(headersTextArea, null, null, rtfbodyTextPane);
Utilities.configureTextPaneAsRtf(rtfbodyTextPane); Utilities.configureTextPaneAsRtf(rtfbodyTextPane);
resetComponent(); resetComponent();
@ -159,8 +204,6 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont
msgbodyTabbedPane = new javax.swing.JTabbedPane(); msgbodyTabbedPane = new javax.swing.JTabbedPane();
headersScrollPane = new javax.swing.JScrollPane(); headersScrollPane = new javax.swing.JScrollPane();
headersTextArea = new javax.swing.JTextArea(); headersTextArea = new javax.swing.JTextArea();
textbodyScrollPane = new javax.swing.JScrollPane();
textbodyTextArea = new javax.swing.JTextArea();
htmlPane = new javax.swing.JPanel(); htmlPane = new javax.swing.JPanel();
rtfbodyScrollPane = new javax.swing.JScrollPane(); rtfbodyScrollPane = new javax.swing.JScrollPane();
rtfbodyTextPane = new javax.swing.JTextPane(); rtfbodyTextPane = new javax.swing.JTextPane();
@ -263,17 +306,6 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont
msgbodyTabbedPane.addTab(org.openide.util.NbBundle.getMessage(MessageContentViewer.class, "MessageContentViewer.headersScrollPane.TabConstraints.tabTitle"), headersScrollPane); // NOI18N msgbodyTabbedPane.addTab(org.openide.util.NbBundle.getMessage(MessageContentViewer.class, "MessageContentViewer.headersScrollPane.TabConstraints.tabTitle"), headersScrollPane); // NOI18N
textbodyScrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
textbodyScrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
textbodyTextArea.setEditable(false);
textbodyTextArea.setLineWrap(true);
textbodyTextArea.setRows(5);
textbodyTextArea.setWrapStyleWord(true);
textbodyScrollPane.setViewportView(textbodyTextArea);
msgbodyTabbedPane.addTab(org.openide.util.NbBundle.getMessage(MessageContentViewer.class, "MessageContentViewer.textbodyScrollPane.TabConstraints.tabTitle"), textbodyScrollPane); // NOI18N
htmlPane.setLayout(new java.awt.BorderLayout()); htmlPane.setLayout(new java.awt.BorderLayout());
msgbodyTabbedPane.addTab(org.openide.util.NbBundle.getMessage(MessageContentViewer.class, "MessageContentViewer.htmlPane.TabConstraints.tabTitle"), htmlPane); // NOI18N msgbodyTabbedPane.addTab(org.openide.util.NbBundle.getMessage(MessageContentViewer.class, "MessageContentViewer.htmlPane.TabConstraints.tabTitle"), htmlPane); // NOI18N
@ -335,6 +367,8 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont
.addComponent(msgbodyTabbedPane) .addComponent(msgbodyTabbedPane)
.addGap(5, 5, 5)) .addGap(5, 5, 5))
); );
msgbodyTabbedPane.getAccessibleContext().setAccessibleParent(null);
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
private void viewInNewWindowButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_viewInNewWindowButtonActionPerformed private void viewInNewWindowButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_viewInNewWindowButtonActionPerformed
@ -360,8 +394,6 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont
private javax.swing.JTextPane rtfbodyTextPane; private javax.swing.JTextPane rtfbodyTextPane;
private javax.swing.JLabel subjectLabel; private javax.swing.JLabel subjectLabel;
private javax.swing.JLabel subjectText; private javax.swing.JLabel subjectText;
private javax.swing.JScrollPane textbodyScrollPane;
private javax.swing.JTextArea textbodyTextArea;
private javax.swing.JLabel toLabel; private javax.swing.JLabel toLabel;
private javax.swing.JLabel toText; private javax.swing.JLabel toText;
private javax.swing.JButton viewInNewWindowButton; private javax.swing.JButton viewInNewWindowButton;
@ -407,11 +439,11 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont
* Get the artifact associated with the given artifact, if there is one. * Get the artifact associated with the given artifact, if there is one.
* *
* @param artifact The artifact to get the associated artifact from. Must * @param artifact The artifact to get the associated artifact from. Must
* not be null * not be null
* *
* @throws TskCoreException If there is a critical error querying the DB. * @throws TskCoreException If there is a critical error querying the DB.
* @return An optional containing the artifact associated with the given * @return An optional containing the artifact associated with the given
* artifact, if there is one. * artifact, if there is one.
*/ */
private static Optional<BlackboardArtifact> getAssociatedArtifact(final BlackboardArtifact artifact) throws TskCoreException { private static Optional<BlackboardArtifact> getAssociatedArtifact(final BlackboardArtifact artifact) throws TskCoreException {
BlackboardAttribute attribute = artifact.getAttribute(TSK_ASSOCIATED_TYPE); BlackboardAttribute attribute = artifact.getAttribute(TSK_ASSOCIATED_TYPE);
@ -462,7 +494,7 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont
headersTextArea.setText(""); headersTextArea.setText("");
rtfbodyTextPane.setText(""); rtfbodyTextPane.setText("");
htmlPanel.reset(); htmlPanel.reset();
textbodyTextArea.setText(""); textPanel.reset();
msgbodyTabbedPane.setEnabled(false); msgbodyTabbedPane.setEnabled(false);
drp.setNode(null); drp.setNode(null);
} }
@ -491,10 +523,10 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont
* Is the given artifact one that can be shown in this viewer? * Is the given artifact one that can be shown in this viewer?
* *
* @param nodeArtifact An artifact that might be a message. Must not be * @param nodeArtifact An artifact that might be a message. Must not be
* null. * null.
* *
* @return True if the given artifact can be shown as a message in this * @return True if the given artifact can be shown as a message in this
* viewer. * viewer.
*/ */
private static boolean isMessageArtifact(BlackboardArtifact nodeArtifact) { private static boolean isMessageArtifact(BlackboardArtifact nodeArtifact) {
final int artifactTypeID = nodeArtifact.getArtifactTypeID(); final int artifactTypeID = nodeArtifact.getArtifactTypeID();
@ -540,14 +572,14 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont
return nodeArtifact; return nodeArtifact;
} }
@Override @Override
public int isPreferred(Node node) { public int isPreferred(Node node) {
// For message artifacts this is a high priority viewer, // For message artifacts this is a high priority viewer,
// but for attachment files, this a lower priority vewer. // but for attachment files, this a lower priority vewer.
if (isSupported(node)) { if (isSupported(node)) {
BlackboardArtifact nodeArtifact = node.getLookup().lookup(BlackboardArtifact.class); BlackboardArtifact nodeArtifact = node.getLookup().lookup(BlackboardArtifact.class);
if (nodeArtifact != null) { if (nodeArtifact != null) {
return 7; return 7;
} else { } else {
return 1; return 1;
@ -560,7 +592,7 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont
* Configure the text area at the given index to show the content of the * Configure the text area at the given index to show the content of the
* given type. * given type.
* *
* @param type The ATTRIBUT_TYPE to show in the indexed tab. * @param type The ATTRIBUT_TYPE to show in the indexed tab.
* @param index The index of the text area to configure. * @param index The index of the text area to configure.
* *
* @throws TskCoreException * @throws TskCoreException
@ -570,6 +602,8 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont
if (index == HTML_TAB_INDEX && StringUtils.isNotBlank(attributeText)) { if (index == HTML_TAB_INDEX && StringUtils.isNotBlank(attributeText)) {
htmlPanel.setHtmlText(attributeText); htmlPanel.setHtmlText(attributeText);
} else if (index == TEXT_TAB_INDEX && StringUtils.isNotBlank(attributeText)) {
textPanel.setContent(attributeText, artifact.toString());
} else { } else {
JTextComponent textComponent = textAreas.get(index); JTextComponent textComponent = textAreas.get(index);
if (textComponent != null) { if (textComponent != null) {
@ -595,34 +629,34 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont
} }
private void configureAttachments() throws TskCoreException { private void configureAttachments() throws TskCoreException {
final Set<Attachment> attachments; final Set<Attachment> attachments;
// Attachments are specified in an attribute TSK_ATTACHMENTS as JSON attribute // Attachments are specified in an attribute TSK_ATTACHMENTS as JSON attribute
BlackboardAttribute attachmentsAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ATTACHMENTS)); BlackboardAttribute attachmentsAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ATTACHMENTS));
if(attachmentsAttr != null) { if (attachmentsAttr != null) {
attachments = new HashSet<>(); attachments = new HashSet<>();
String jsonVal = attachmentsAttr.getValueString(); String jsonVal = attachmentsAttr.getValueString();
MessageAttachments msgAttachments = new Gson().fromJson(jsonVal, MessageAttachments.class); MessageAttachments msgAttachments = new Gson().fromJson(jsonVal, MessageAttachments.class);
Collection<FileAttachment> fileAttachments = msgAttachments.getFileAttachments(); Collection<FileAttachment> fileAttachments = msgAttachments.getFileAttachments();
for (FileAttachment fileAttachment: fileAttachments) { for (FileAttachment fileAttachment : fileAttachments) {
attachments.add(fileAttachment); attachments.add(fileAttachment);
} }
Collection<URLAttachment> urlAttachments = msgAttachments.getUrlAttachments(); Collection<URLAttachment> urlAttachments = msgAttachments.getUrlAttachments();
for (URLAttachment urlAttachment: urlAttachments) { for (URLAttachment urlAttachment : urlAttachments) {
attachments.add(urlAttachment); attachments.add(urlAttachment);
} }
} else { // For backward compatibility - email attachements are derived files and children of the email message artifact } else { // For backward compatibility - email attachements are derived files and children of the email message artifact
attachments = new HashSet<>(); attachments = new HashSet<>();
for (Content child: artifact.getChildren()) { for (Content child : artifact.getChildren()) {
if (child instanceof AbstractFile) { if (child instanceof AbstractFile) {
attachments.add(new FileAttachment((AbstractFile)child)); attachments.add(new FileAttachment((AbstractFile) child));
}
} }
}
} }
final int numberOfAttachments = attachments.size(); final int numberOfAttachments = attachments.size();
msgbodyTabbedPane.setEnabledAt(ATTM_TAB_INDEX, numberOfAttachments > 0); msgbodyTabbedPane.setEnabledAt(ATTM_TAB_INDEX, numberOfAttachments > 0);
@ -710,9 +744,8 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont
return doc.html(); return doc.html();
} }
/** /**
* Creates child nodes for message attachments. * Creates child nodes for message attachments.
*/ */
private static class AttachmentsChildren extends Children.Keys<Attachment> { private static class AttachmentsChildren extends Children.Keys<Attachment> {

View File

@ -0,0 +1,97 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<Properties>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[2000, 2000]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[2, 2]"/>
</Property>
<Property name="name" type="java.lang.String" value="" noResource="true"/>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[100, 58]"/>
</Property>
<Property name="verifyInputWhenFocusTarget" type="boolean" value="false"/>
</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"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,-116,0,0,2,-108"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
<SubComponents>
<Container class="javax.swing.JPanel" name="translationBar">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.EtchedBorderInfo">
<EtchetBorder/>
</Border>
</Property>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[182, 24]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[182, 24]"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
<BorderConstraints direction="North"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
<SubComponents>
<Component class="javax.swing.JComboBox" name="translateComboBox">
<Properties>
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
<StringArray count="0"/>
</Property>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[200, 20]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[200, 20]"/>
</Property>
<Property name="name" type="java.lang.String" value="" noResource="true"/>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[200, 20]"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="translateComboBoxActionPerformed"/>
</Events>
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;TranslateOption&gt;"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
<BorderConstraints direction="After"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JLabel" name="statusLabel">
<Properties>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[32767, 32767]"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
<BorderConstraints direction="Center"/>
</Constraint>
</Constraints>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Form>

View File

@ -0,0 +1,434 @@
/*
* 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;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.awt.Component;
import java.awt.ComponentOrientation;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.texttranslation.NoServiceProviderException;
import org.sleuthkit.autopsy.texttranslation.TextTranslationService;
import org.sleuthkit.autopsy.texttranslation.TranslationException;
import org.sleuthkit.autopsy.texttranslation.ui.TranslateTextTask;
/**
* This is a panel for translation with a subcomponent that allows for translation.
*/
class TranslatablePanel extends JPanel {
/**
* This is an exception that can occur during the normal operation of the translatable
* panel. For instance, this exception can be thrown if it is not possible to set the child
* content to the provided content string.
*/
class TranslatablePanelException extends Exception {
public static final long serialVersionUID = 1L;
TranslatablePanelException(String message) {
super(message);
}
TranslatablePanelException(String message, Throwable cause) {
super(message, cause);
}
}
/**
* This describes a child component to be placed as a child of this panel. The child received
* from {@link #getRootComponent() getRootComponent() } will listen for content updates from setContent().
*/
interface ContentComponent {
/**
* This method gets root component of the translation panel.
* @return the root component to insert into the translatable panel
*/
Component getRootComponent();
/**
* This method sets the content of the component to the provided content.
* @param content the content to be displayed
* @param orientation how it should be displayed
* @throws Exception if there is an error in rendering the content
*/
void setContent(String content, ComponentOrientation orientation) throws TranslatablePanelException;
}
/**
* This is an option in drop down of whether or not to translate.
*/
private static class TranslateOption {
private final String text;
private final boolean translate;
TranslateOption(String text, boolean translate) {
this.text = text;
this.translate = translate;
}
String getText() {
return text;
}
@Override
public String toString() {
return text;
}
boolean shouldTranslate() {
return translate;
}
}
/**
* This represents the cached result of translating the current content.
*/
private static class TranslatedText {
private final String text;
private final ComponentOrientation orientation;
TranslatedText(String text, ComponentOrientation orientation) {
this.text = text;
this.orientation = orientation;
}
String getText() {
return text;
}
ComponentOrientation getOrientation() {
return orientation;
}
}
/**
* This connects the swing worker specified by
* {@link org.sleuthkit.autopsy.texttranslation.ui.TranslateTextTask TranslateTextTask} to this component.
*/
private class OnTranslation extends TranslateTextTask {
OnTranslation() {
super(true, contentDescriptor == null ? "" : contentDescriptor);
}
@Override
protected String translate(String input) throws NoServiceProviderException, TranslationException {
// This defers to the outer class method so that it can be overridden for items like html, rtf, etc.
return retrieveTranslation(input);
}
@Override
protected void onProgressDisplay(String text, ComponentOrientation orientation, int font) {
setStatus(text, false);
}
@Override
protected void onErrorDisplay(String text, ComponentOrientation orientation, int font) {
setStatus(text, true);
}
@Override
protected String retrieveText() throws IOException, InterruptedException, IllegalStateException {
return content == null ? "" : content;
}
@Override
protected void onTextDisplay(String text, ComponentOrientation orientation, int font) {
// On successful acquire, this caches the result and set the text.
setCachedTranslated(new TranslatedText(text, orientation));
setChildComponentContent(text, orientation);
// This clears any status that may be present.
clearStatus();
}
}
private static final long serialVersionUID = 1L;
private static final ComponentOrientation DEFAULT_ORIENTATION = ComponentOrientation.LEFT_TO_RIGHT;
private final ImageIcon warningIcon = new ImageIcon(TranslatablePanel.class.getResource("/org/sleuthkit/autopsy/images/warning16.png"));
private final ContentComponent contentComponent;
private final TextTranslationService translationService;
private final ThreadFactory translationThreadFactory = new ThreadFactoryBuilder().setNameFormat("translatable-panel-%d").build();
private final ExecutorService executorService = Executors.newSingleThreadExecutor(translationThreadFactory);
private final Object cachedTranslatedLock = new Object();
private final Object backgroundTaskLock = new Object();
private String content;
private String contentDescriptor;
private boolean prevTranslateSelection;
private volatile TranslatedText cachedTranslated;
private volatile OnTranslation backgroundTask = null;
@Messages({"TranslatablePanel.comboBoxOption.originalText=Original Text",
"TranslatablePanel.comboBoxOption.translatedText=Translated Text"})
TranslatablePanel(ContentComponent contentComponent) {
this(
contentComponent,
Bundle.TranslatablePanel_comboBoxOption_originalText(),
Bundle.TranslatablePanel_comboBoxOption_translatedText(),
null,
TextTranslationService.getInstance());
}
/**
* This creates a new panel using @{link ContentPanel ContentPanel} as a child.
*/
TranslatablePanel(ContentComponent contentComponent, String origOptionText, String translatedOptionText, String origContent,
TextTranslationService translationService) {
this.contentComponent = contentComponent;
this.translationService = translationService;
initComponents();
additionalInit(contentComponent.getRootComponent(), origOptionText, translatedOptionText);
reset();
}
/**
* @return the cached translated text or returns null
*/
private TranslatedText getCachedTranslated() {
synchronized (cachedTranslatedLock) {
return cachedTranslated;
}
}
/**
* @param translated the translated text to be cached
*/
private void setCachedTranslated(TranslatedText translated) {
synchronized (cachedTranslatedLock) {
this.cachedTranslated = translated;
}
}
/**
* If a translation worker is running, this is called to cancel the worker.
*/
private void cancelPendingTranslation() {
synchronized (backgroundTaskLock) {
if (backgroundTask != null && !backgroundTask.isDone()) {
backgroundTask.cancel(true);
}
backgroundTask = null;
}
}
/**
* This runs a translation worker to translate the text.
*/
private void runTranslationTask() {
synchronized (backgroundTaskLock) {
cancelPendingTranslation();
backgroundTask = new OnTranslation();
//Pass the background task to a single threaded pool to keep
//the number of jobs running to one.
executorService.execute(backgroundTask);
}
}
/**
* This resets the component to an empty state and sets the translation bar visibility
* based on whether there is a provider.
*/
final void reset() {
setContent(null, null);
}
/**
* This method sets the content for the component; this also clears the status.
* @param content the content for the panel
* @param contentDescriptor the content descriptor to be used in error messages
*/
void setContent(String content, String contentDescriptor) {
cancelPendingTranslation();
setTranslationEnabled();
this.translateComboBox.setSelectedIndex(0);
this.prevTranslateSelection = false;
this.content = content;
this.contentDescriptor = contentDescriptor;
clearStatus();
setCachedTranslated(null);
setChildComponentContent(content);
}
/**
* This is where actual translation takes place allowed to be overridden for the
* sake of varying translatable content (i.e. html, rtf, etc).
*
* @param input the input content
* @return the result of translation
* @throws TranslationException
* @throws NoServiceProviderException
*/
protected String retrieveTranslation(String input) throws TranslationException, NoServiceProviderException {
return translationService.translate(input);
}
/**
* This method clears the status bar.
*/
private void clearStatus() {
setStatus(null, false);
}
/**
* This sets the status bar message.
* @param msg the status bar message to show
* @param showWarningIcon whether that status is a warning
*/
private synchronized void setStatus(String msg, boolean showWarningIcon) {
statusLabel.setText(msg);
statusLabel.setIcon(showWarningIcon ? warningIcon : null);
}
/**
* This method sets the translation bar visibility based on whether or not there is a provided.
*/
private void setTranslationEnabled() {
translateComboBox.setEnabled(this.translationService.hasProvider());
}
/**
* The child component provided in the constructor will have its content set to the string provided.
* @param content the content to display in the child component
*/
private void setChildComponentContent(String content) {
setChildComponentContent(content, DEFAULT_ORIENTATION);
}
/**
* The child component provided in the constructor will have its content set to the string provided.
* @param content the content to display in the child component
* @param orientation the orientation for the text
*/
@Messages({"# {0} - exception message", "TranslatablePanel.onSetContentError.text=There was an error displaying the text: {0}"})
private synchronized void setChildComponentContent(String content, ComponentOrientation orientation) {
SwingUtilities.invokeLater(() -> {
try {
contentComponent.setContent(content, orientation);
} catch (TranslatablePanelException ex) {
setStatus(Bundle.TranslatablePanel_onSetContentError_text(ex.getMessage()), true);
}
});
}
/**
* This method is for items that are programmatically initialized.
*/
private void additionalInit(Component rootComponent, String origOptionText, String translatedOptionText) {
add(rootComponent, java.awt.BorderLayout.CENTER);
translateComboBox.removeAllItems();
translateComboBox.addItem(new TranslateOption(origOptionText, false));
translateComboBox.addItem(new TranslateOption(translatedOptionText, true));
}
/**
* When the combo box choice is selected, this method is fired.
* @param translateOption the current translate option
*/
private void handleComboBoxChange(TranslateOption translateOption) {
boolean curTranslateSelection = translateOption.shouldTranslate();
if (curTranslateSelection != this.prevTranslateSelection) {
this.prevTranslateSelection = curTranslateSelection;
cancelPendingTranslation();
clearStatus();
if (curTranslateSelection) {
TranslatedText translated = getCachedTranslated();
if (translated != null) {
setChildComponentContent(translated.getText(), translated.getOrientation());
} else {
runTranslationTask();
}
} else {
setChildComponentContent(content);
}
}
}
/**
* 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() {
translationBar = new javax.swing.JPanel();
translateComboBox = new javax.swing.JComboBox<>();
statusLabel = new javax.swing.JLabel();
setMaximumSize(new java.awt.Dimension(2000, 2000));
setMinimumSize(new java.awt.Dimension(2, 2));
setName(""); // NOI18N
setPreferredSize(new java.awt.Dimension(100, 58));
setVerifyInputWhenFocusTarget(false);
setLayout(new java.awt.BorderLayout());
translationBar.setBorder(javax.swing.BorderFactory.createEtchedBorder());
translationBar.setMaximumSize(new java.awt.Dimension(182, 24));
translationBar.setPreferredSize(new java.awt.Dimension(182, 24));
translationBar.setLayout(new java.awt.BorderLayout());
translateComboBox.setMaximumSize(new java.awt.Dimension(200, 20));
translateComboBox.setMinimumSize(new java.awt.Dimension(200, 20));
translateComboBox.setName(""); // NOI18N
translateComboBox.setPreferredSize(new java.awt.Dimension(200, 20));
translateComboBox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
translateComboBoxActionPerformed(evt);
}
});
translationBar.add(translateComboBox, java.awt.BorderLayout.LINE_END);
statusLabel.setMaximumSize(new java.awt.Dimension(32767, 32767));
translationBar.add(statusLabel, java.awt.BorderLayout.CENTER);
add(translationBar, java.awt.BorderLayout.NORTH);
}// </editor-fold>//GEN-END:initComponents
private void translateComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_translateComboBoxActionPerformed
handleComboBoxChange((TranslateOption) translateComboBox.getSelectedItem());
}//GEN-LAST:event_translateComboBoxActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel statusLabel;
private javax.swing.JComboBox<TranslateOption> translateComboBox;
private javax.swing.JPanel translationBar;
// End of variables declaration//GEN-END:variables
}

View File

@ -1,4 +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
ContextSourcePanel.jSourceTextLabel.text=Label2
ContextSourcePanel.jSourceNameLabel.text=jSourceNameLabel
ContextViewer.jSourceLabel.text=Usage
ContextViewer.jUsageLabel.text=Source
ContextViewer.jUnknownLabel.text=Unknown

View File

@ -1,18 +1,24 @@
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.file=File ContextViewer.jSourceLabel.text=Usage
ContextViewer.jSourceGoToResultButton.text=Go to Result ContextViewer.jUsageLabel.text=Source
ContextViewer.jSourceTextLabel.text=jLabel2 ContextViewer.jUnknownLabel.text=Unknown
ContextViewer.jSourceNameLabel.text=jSourceNameLabel
ContextViewer.jSourceLabel.text=Source
ContextViewer.message=Message ContextViewer.message=Message
ContextViewer.messageFrom=From ContextViewer.messageFrom=From
ContextViewer.messageOn=On ContextViewer.messageOn=On
ContextViewer.messageTo=To ContextViewer.messageTo=To
ContextViewer.on=On ContextViewer.on=Opened at
ContextViewer.recentDocs=Recent Documents: 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.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,10 +1,144 @@
<?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>
<Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="ff" green="ff" red="ff" type="rgb"/>
</Property>
</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> <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>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[495, 358]"/>
</Property>
</Properties> </Properties>
<AuxValues> <AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/> <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
@ -21,80 +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">
<Group type="102" alignment="0" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="jSourceLabel" min="-2" max="-2" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="6" max="-2" attributes="0"/>
<Component id="jSourceNameLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="jSourceTextLabel" pref="192" max="32767" attributes="0"/>
</Group>
</Group>
<EmptySpace min="-2" pref="36" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<Component id="jSourceGoToResultButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
<DimensionLayout dim="1"> <DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<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 min="0" pref="203" 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>
</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,73 +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();
jUnknownPanel = new javax.swing.JPanel();
javax.swing.JLabel jUnknownLabel = new javax.swing.JLabel();
jScrollPane = new javax.swing.JScrollPane();
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"));
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
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))
);
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)
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jSourceLabel)
.addGroup(layout.createSequentialGroup()
.addGap(6, 6, 6)
.addComponent(jSourceNameLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jSourceTextLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 192, Short.MAX_VALUE)))
.addGap(36, 36, 36))
.addGroup(layout.createSequentialGroup()
.addComponent(jSourceGoToResultButton)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))))
); );
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)
.addGap(0, 203, 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
@Override @Override
public void setNode(Node selectedNode) { public void setNode(Node selectedNode) {
if ((selectedNode == null) || (!isSupported(selectedNode))) { if ((selectedNode == null) || (!isSupported(selectedNode))) {
@ -188,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
@ -222,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.
@ -234,7 +261,7 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte
private void populateSourceContextData(AbstractFile sourceFile) throws NoCurrentCaseException, TskCoreException { private void populateSourceContextData(AbstractFile sourceFile) throws NoCurrentCaseException, TskCoreException {
SleuthkitCase tskCase = Case.getCurrentCaseThrows().getSleuthkitCase(); SleuthkitCase tskCase = Case.getCurrentCaseThrows().getSleuthkitCase();
// Check for all context artifacts // Check for all context artifacts
boolean foundASource = false; boolean foundASource = false;
for (BlackboardArtifact.ARTIFACT_TYPE artifactType : SOURCE_CONTEXT_ARTIFACTS) { for (BlackboardArtifact.ARTIFACT_TYPE artifactType : SOURCE_CONTEXT_ARTIFACTS) {
@ -245,12 +272,33 @@ 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();
if (foundASource == false) { contextContainer.add(jSourcePanel);
setSourceName("Unknown"); contextContainer.setLayout(new BoxLayout(contextContainer, BoxLayout.Y_AXIS));
showSourceText(false); if (contextSourcePanels.isEmpty()) {
contextContainer.add(jUnknownPanel);
} else {
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();
} }
/** /**
@ -263,15 +311,13 @@ 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 {
if (BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT.getTypeID() == artifact.getArtifactTypeID()) { if (BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT.getTypeID() == artifact.getArtifactTypeID()) {
BlackboardAttribute associatedArtifactAttribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT)); BlackboardAttribute associatedArtifactAttribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT));
if (associatedArtifactAttribute != null) { if (associatedArtifactAttribute != null) {
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);
} }
} }
@ -293,47 +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()) {
setSourceName(Bundle.ContextViewer_recentDocs()); String sourceName = Bundle.ContextViewer_recentDocs();
setSourceText(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 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);
}
/** /**
* Returns a display string with download source URL from the given * Returns a display string with download source URL from the given
* artifact. * artifact.
@ -371,16 +397,21 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte
* @throws TskCoreException * @throws TskCoreException
*/ */
@NbBundle.Messages({ @NbBundle.Messages({
"ContextViewer.file=File", "ContextViewer.on=Opened at",
"ContextViewer.on=On" "ContextViewer.unknown=Opened at unknown time"
}) })
private String recentDocArtifactToString(BlackboardArtifact artifact) throws TskCoreException { private String recentDocArtifactToString(BlackboardArtifact artifact) throws TskCoreException {
StringBuilder sb = new StringBuilder(ARTIFACT_STR_MAX_LEN); StringBuilder sb = new StringBuilder(ARTIFACT_STR_MAX_LEN);
Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> attributesMap = getAttributesMap(artifact); Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> attributesMap = getAttributesMap(artifact);
BlackboardAttribute attribute = attributesMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME);
if (BlackboardArtifact.ARTIFACT_TYPE.TSK_RECENT_OBJECT.getTypeID() == artifact.getArtifactTypeID()) { if (BlackboardArtifact.ARTIFACT_TYPE.TSK_RECENT_OBJECT.getTypeID() == artifact.getArtifactTypeID()) {
appendAttributeString(sb, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH, attributesMap, Bundle.ContextViewer_file()); if (attribute.getValueLong() > 0) {
appendAttributeString(sb, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, attributesMap, Bundle.ContextViewer_on()); appendAttributeString(sb, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, attributesMap, Bundle.ContextViewer_on());
} else {
sb.append(Bundle.ContextViewer_unknown());
}
} }
return sb.toString(); return sb.toString();
} }
@ -468,9 +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;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
} }

View File

@ -75,6 +75,7 @@ public final class UserPreferences {
public static final String SHOW_ONLY_CURRENT_USER_TAGS = "ShowOnlyCurrentUserTags"; public static final String SHOW_ONLY_CURRENT_USER_TAGS = "ShowOnlyCurrentUserTags";
public static final String HIDE_SCO_COLUMNS = "HideCentralRepoCommentsAndOccurrences"; //The key for this setting pre-dates the settings current functionality //NON-NLS public static final String HIDE_SCO_COLUMNS = "HideCentralRepoCommentsAndOccurrences"; //The key for this setting pre-dates the settings current functionality //NON-NLS
public static final String DISPLAY_TRANSLATED_NAMES = "DisplayTranslatedNames"; public static final String DISPLAY_TRANSLATED_NAMES = "DisplayTranslatedNames";
private static final boolean DISPLAY_TRANSLATED_NAMES_DEFAULT = true;
public static final String EXTERNAL_HEX_EDITOR_PATH = "ExternalHexEditorPath"; public static final String EXTERNAL_HEX_EDITOR_PATH = "ExternalHexEditorPath";
public static final String SOLR_MAX_JVM_SIZE = "SolrMaxJVMSize"; public static final String SOLR_MAX_JVM_SIZE = "SolrMaxJVMSize";
public static final String RESULTS_TABLE_PAGE_SIZE = "ResultsTablePageSize"; public static final String RESULTS_TABLE_PAGE_SIZE = "ResultsTablePageSize";
@ -265,7 +266,7 @@ public final class UserPreferences {
} }
public static boolean displayTranslatedFileNames() { public static boolean displayTranslatedFileNames() {
return preferences.getBoolean(DISPLAY_TRANSLATED_NAMES, false); return preferences.getBoolean(DISPLAY_TRANSLATED_NAMES, DISPLAY_TRANSLATED_NAMES_DEFAULT);
} }
/** /**

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;
@ -683,56 +684,93 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel {
boolean isPwSet = (tbMsgPassword.getPassword().length != 0); boolean isPwSet = (tbMsgPassword.getPassword().length != 0);
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
* databases.
*/
DbType dbType = DbType.POSTGRESQL;
info = new CaseDbConnectionInfo(
tbDbHostname.getText().trim(),
tbDbPort.getText().trim(),
tbDbUsername.getText().trim(),
new String(tbDbPassword.getPassword()),
dbType);
try {
UserPreferences.setDatabaseConnectionInfo(info);
} catch (UserPreferencesException ex) {
logger.log(Level.SEVERE, "Error saving case database connection info", ex); //NON-NLS
}
int msgServicePort = 0;
try {
msgServicePort = Integer.parseInt(this.tbMsgPort.getText().trim());
} catch (NumberFormatException ex) {
logger.log(Level.SEVERE, "Could not parse messaging service port setting", ex);
}
MessageServiceConnectionInfo msgServiceInfo = new MessageServiceConnectionInfo(
tbMsgHostname.getText().trim(),
msgServicePort,
tbMsgUsername.getText().trim(),
new String(tbMsgPassword.getPassword()));
try {
UserPreferences.setMessageServiceConnectionInfo(msgServiceInfo);
} catch (UserPreferencesException ex) {
logger.log(Level.SEVERE, "Error saving messaging service connection info", ex); //NON-NLS
}
UserPreferences.setIndexingServerHost(tbSolrHostname.getText().trim());
UserPreferences.setIndexingServerPort(Integer.parseInt(tbSolrPort.getText().trim()));
} }
/* // trigger changes to whether or not user can use multi user settings for central repository
* Currently only supporting multi-user cases with PostgreSQL case if (prevSelected != multiUserCasesEnabled || !areCaseDbConnectionEqual(prevConn, info))
* databases. GlobalSettingsPanel.onMultiUserChange(this, prevSelected, multiUserCasesEnabled);
*/
DbType dbType = DbType.POSTGRESQL;
CaseDbConnectionInfo info = new CaseDbConnectionInfo(
tbDbHostname.getText().trim(),
tbDbPort.getText().trim(),
tbDbUsername.getText().trim(),
new String(tbDbPassword.getPassword()),
dbType);
try {
UserPreferences.setDatabaseConnectionInfo(info);
} catch (UserPreferencesException ex) {
logger.log(Level.SEVERE, "Error saving case database connection info", ex); //NON-NLS
}
int msgServicePort = 0;
try {
msgServicePort = Integer.parseInt(this.tbMsgPort.getText().trim());
} catch (NumberFormatException ex) {
logger.log(Level.SEVERE, "Could not parse messaging service port setting", ex);
}
MessageServiceConnectionInfo msgServiceInfo = new MessageServiceConnectionInfo(
tbMsgHostname.getText().trim(),
msgServicePort,
tbMsgUsername.getText().trim(),
new String(tbMsgPassword.getPassword()));
try {
UserPreferences.setMessageServiceConnectionInfo(msgServiceInfo);
} catch (UserPreferencesException ex) {
logger.log(Level.SEVERE, "Error saving messaging service connection info", ex); //NON-NLS
}
UserPreferences.setIndexingServerHost(tbSolrHostname.getText().trim());
UserPreferences.setIndexingServerPort(Integer.parseInt(tbSolrPort.getText().trim()));
} }
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

@ -105,7 +105,7 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
this.content.getName(), this.content.getId()), ex); this.content.getName(), this.content.getId()), ex);
} }
if (UserPreferences.displayTranslatedFileNames()) { if (TextTranslationService.getInstance().hasProvider() && UserPreferences.displayTranslatedFileNames()) {
backgroundTasksPool.submit(new TranslationTask( backgroundTasksPool.submit(new TranslationTask(
new WeakReference<>(this), weakPcl)); new WeakReference<>(this), weakPcl));
} }
@ -331,7 +331,7 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
* background task that promises to update these values. * background task that promises to update these values.
*/ */
if (UserPreferences.displayTranslatedFileNames()) { if (TextTranslationService.getInstance().hasProvider() && UserPreferences.displayTranslatedFileNames()) {
properties.add(new NodeProperty<>(ORIGINAL_NAME.toString(), ORIGINAL_NAME.toString(), NO_DESCR, "")); properties.add(new NodeProperty<>(ORIGINAL_NAME.toString(), ORIGINAL_NAME.toString(), NO_DESCR, ""));
} }

View File

@ -0,0 +1,140 @@
/*
* 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 com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.ref.WeakReference;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.logging.Level;
import org.openide.nodes.AbstractNode;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.AbstractContentNode;
/**
* An abstract base class for background tasks needed to compute values for the
* property sheet of an AbstractNode.
*
* The results of the computation are returned by firing a PropertyChangeEvent
* and the run method has an exception firewall with logging. These features
* relieve the AbstractNode from having to create a thread to block on the get()
* method of the task Future.
*
* Only weak references to the AbstractNode and its PropertyChangeListener are
* held prior to task execution so that a queued task does not interfere with
* garbage collection if the node has been destroyed by the NetBeans framework.
*
* A thread pool with descriptively named threads (node-background-task-N) is
* provided for executing instances of the tasks.
*/
public abstract class AbstractNodePropertySheetTask<T extends AbstractNode> implements Runnable {
private static final Logger LOGGER = Logger.getLogger(AbstractContentNode.class.getName());
private static final Integer THREAD_POOL_SIZE = 10;
private static final ExecutorService executor = Executors.newFixedThreadPool(THREAD_POOL_SIZE, new ThreadFactoryBuilder().setNameFormat("node-background-task-%d").build());
private final WeakReference<T> weakNodeRef;
private final WeakReference<PropertyChangeListener> weakListenerRef;
/**
* Submits a task to compute values for the property sheet of an
* AbstractNode to a thread pool dedicated to such tasks with descriptively
* named threads (node-background-task-N).
*
* @param task The task.
*
* @return The Future of the task, may be used for task cancellation by
* calling Future.cancel(true).
*/
public static Future<?> submitTask(AbstractNodePropertySheetTask<?> task) {
return executor.submit(task);
}
/**
* Constructs an abstract base class for background tasks needed to compute
* values for the property sheet of an AbstractNode.
*
* The results of the computation are returned by firing a
* PropertyChangeEvent and the run method has an exception firewall with
* logging. These features relieve the AbstractNode from having to create a
* thread to block on the get() method of the task Future.
*
* Only weak references to the AbstractNode and its PropertyChangeListener
* are held prior to task execution so that a queued task does not interfere
* with garbage collection if the node has been destroyed by the NetBeans
* framework.
*
* A thread pool with descriptively named threads (node-background-task-N)
* is provided for executing instances of the tasks.
*
* @param node The node.
* @param listener A property change listener for the node.
*/
protected AbstractNodePropertySheetTask(T node, PropertyChangeListener listener) {
this.weakNodeRef = new WeakReference<>(node);
this.weakListenerRef = new WeakReference<>(listener);
}
/**
* Computes the values for the property sheet of an AbstractNode. The
* results of the computation are returned as a PropertyChangeEvent which is
* fired to the PropertyChangeEventListener of the node.
*
* IMPORTANT: Implementations of this method should check for cancellation
* by calling Thread.currentThread().isInterrupted() at appropriate
* intervals.
*
* @param node The AbstractNode.
*
* @return The result of the computation as a PropertyChangeEvent.
*/
protected abstract PropertyChangeEvent computePropertyValue(T node) throws Exception;
@Override
final public void run() {
try {
T node = this.weakNodeRef.get();
PropertyChangeListener listener = this.weakListenerRef.get();
if (node == null || listener == null) {
return;
}
if (Thread.currentThread().isInterrupted()) {
return;
}
PropertyChangeEvent changeEvent = computePropertyValue(node);
if (Thread.currentThread().isInterrupted()) {
return;
}
if (changeEvent != null) {
listener.propertyChange(changeEvent);
}
} catch (Exception ex) {
LOGGER.log(Level.WARNING, "Error executing property sheet values computation background task", ex);
}
}
}

View File

@ -73,6 +73,25 @@ final public class FeatureAccessUtils {
return dataSourceDeletionAllowed; return dataSourceDeletionAllowed;
} }
/**
* Indicates whether or not a user can delete the current case.
*
* @return True or false.
*/
public static boolean canDeleteCurrentCase() {
return currentCaseIsSingleUserCase() || multiUserCaseRestrictionsFileAbsent();
}
/**
* Indicates whether or not a user can add hash sets to the central
* repository.
*
* @return True or false.
*/
public static boolean canAddHashSetsToCentralRepo() {
return multiUserCaseRestrictionsFileAbsent();
}
/** /**
* Indicates whether or not the current case is a single-user case. * Indicates whether or not the current case is a single-user case.
* *
@ -83,12 +102,12 @@ final public class FeatureAccessUtils {
} }
/** /**
* Indicates whether or not the current user is allowed to create or modify * Indicates whether or not the multi-user case privileges restriction file
* (add or delete data sources) multi-user cases. * is absent.
* *
* @return True or false. * @return True or false.
*/ */
public static boolean multiUserCaseRestrictionsFileAbsent() { private static boolean multiUserCaseRestrictionsFileAbsent() {
File accessLimitingFile = new File(MULTIUSER_CASE_RESTRICTED_FILE_PATH); File accessLimitingFile = new File(MULTIUSER_CASE_RESTRICTED_FILE_PATH);
return !accessLimitingFile.exists(); return !accessLimitingFile.exists();
} }

View File

@ -667,7 +667,7 @@ class FileSearch {
return; return;
} }
BufferedImage thumbnail = ScalrWrapper.resize(bufferedImage, Scalr.Method.SPEED, Scalr.Mode.FIT_TO_HEIGHT, ImageUtils.ICON_SIZE_LARGE, ImageUtils.ICON_SIZE_MEDIUM, Scalr.OP_ANTIALIAS); BufferedImage thumbnail = ScalrWrapper.resize(bufferedImage, Scalr.Method.SPEED, Scalr.Mode.FIT_TO_HEIGHT, ImageUtils.ICON_SIZE_LARGE, ImageUtils.ICON_SIZE_MEDIUM, Scalr.OP_ANTIALIAS);
//we are height limited here so it can be wider than it can be tall, scalr maintains aspect ratio //We are height limited here so it can be wider than it can be tall.Scalr maintains the aspect ratio.
videoThumbnails.add(thumbnail); videoThumbnails.add(thumbnail);
if (cacheDirectory != null) { if (cacheDirectory != null) {
try { try {

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.

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2014-2018 Basis Technology Corp. * Copyright 2014-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");
@ -51,6 +51,7 @@ final class IngestModuleFactoryLoader {
private static final Logger logger = Logger.getLogger(IngestModuleFactoryLoader.class.getName()); private static final Logger logger = Logger.getLogger(IngestModuleFactoryLoader.class.getName());
private static final String SAMPLE_MODULE_FACTORY_CLASS_NAME = SampleIngestModuleFactory.class.getCanonicalName(); private static final String SAMPLE_MODULE_FACTORY_CLASS_NAME = SampleIngestModuleFactory.class.getCanonicalName();
private static final ArrayList<String> coreModuleOrdering = new ArrayList<String>() { private static final ArrayList<String> coreModuleOrdering = new ArrayList<String>() {
private static final long serialVersionUID = 1L;
{ {
// The ordering of the core ingest module factories implemented // The ordering of the core ingest module factories implemented
// using Java is hard-coded. // using Java is hard-coded.
@ -79,7 +80,7 @@ final class IngestModuleFactoryLoader {
* removed between invocations. * removed between invocations.
* *
* @return A list of objects that implement the IngestModuleFactory * @return A list of objects that implement the IngestModuleFactory
* interface. * interface.
*/ */
static List<IngestModuleFactory> getIngestModuleFactories() { static List<IngestModuleFactory> getIngestModuleFactories() {
// A hash set of display names and a hash map of class names to // A hash set of display names and a hash map of class names to
@ -132,7 +133,7 @@ final class IngestModuleFactoryLoader {
factories.add(factory); factories.add(factory);
logger.log(Level.INFO, "Found ingest module factory: name = {0}, version = {1}", new Object[]{factory.getModuleDisplayName(), factory.getModuleVersionNumber()}); //NON-NLS logger.log(Level.INFO, "Found ingest module factory: name = {0}, version = {1}", new Object[]{factory.getModuleDisplayName(), factory.getModuleVersionNumber()}); //NON-NLS
} else { } else {
logger.log(Level.SEVERE, "Found duplicate ingest module display name (name = {0})", factory.getModuleDisplayName()); //NON-NLS logger.log(Level.WARNING, "Found duplicate ingest module display name (name = {0})", factory.getModuleDisplayName()); //NON-NLS
DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message( DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message(
NbBundle.getMessage(IngestModuleFactoryLoader.class, "IngestModuleFactoryLoader.errorMessages.duplicateDisplayName", factory.getModuleDisplayName()), NbBundle.getMessage(IngestModuleFactoryLoader.class, "IngestModuleFactoryLoader.errorMessages.duplicateDisplayName", factory.getModuleDisplayName()),
NotifyDescriptor.ERROR_MESSAGE)); NotifyDescriptor.ERROR_MESSAGE));
@ -154,11 +155,14 @@ final class IngestModuleFactoryLoader {
javaFactoriesByClass.put(factory.getClass().getCanonicalName(), factory); javaFactoriesByClass.put(factory.getClass().getCanonicalName(), factory);
logger.log(Level.INFO, "Found ingest module factory: name = {0}, version = {1}", new Object[]{factory.getModuleDisplayName(), factory.getModuleVersionNumber()}); //NON-NLS logger.log(Level.INFO, "Found ingest module factory: name = {0}, version = {1}", new Object[]{factory.getModuleDisplayName(), factory.getModuleVersionNumber()}); //NON-NLS
} else { } else {
logger.log(Level.SEVERE, "Found duplicate ingest module display name (name = {0})", factory.getModuleDisplayName()); //NON-NLS logger.log(Level.WARNING, "Found duplicate ingest module display name (name = {0})", factory.getModuleDisplayName()); //NON-NLS
DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message(
NbBundle.getMessage(IngestModuleFactoryLoader.class, "IngestModuleFactoryLoader.errorMessages.duplicateDisplayName", factory.getModuleDisplayName()),
NotifyDescriptor.ERROR_MESSAGE));
} }
} }
/**
* Private constructor to prevent instantiation of this utility class.
*/
private IngestModuleFactoryLoader() {
}
} }

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");
@ -44,6 +44,7 @@ import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDbManagerExc
import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.featureaccess.FeatureAccessUtils;
/** /**
* Instances of this class allow a user to create a new hash database and add it * Instances of this class allow a user to create a new hash database and add it
@ -124,29 +125,29 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog {
setLocationRelativeTo(getOwner()); setLocationRelativeTo(getOwner());
setVisible(true); setVisible(true);
} }
private void enableComponents(){ private void enableComponents() {
if(! CentralRepository.isEnabled()){ if (!CentralRepository.isEnabled() || !FeatureAccessUtils.canAddHashSetsToCentralRepo()) {
centralRepoRadioButton.setEnabled(false); centralRepoRadioButton.setEnabled(false);
fileTypeRadioButton.setSelected(true); fileTypeRadioButton.setSelected(true);
} else { } else {
populateCombobox(); populateCombobox();
} }
boolean isFileType = fileTypeRadioButton.isSelected(); boolean isFileType = fileTypeRadioButton.isSelected();
// Type type only // Type type only
databasePathLabel.setEnabled(isFileType); databasePathLabel.setEnabled(isFileType);
databasePathTextField.setEnabled(isFileType); databasePathTextField.setEnabled(isFileType);
saveAsButton.setEnabled(isFileType); saveAsButton.setEnabled(isFileType);
// Central repo only // Central repo only
lbOrg.setEnabled(! isFileType); lbOrg.setEnabled(!isFileType);
orgButton.setEnabled(! isFileType); orgButton.setEnabled(!isFileType);
orgComboBox.setEnabled(! isFileType); orgComboBox.setEnabled(!isFileType);
} }
@NbBundle.Messages({"HashDbCreateDatabaseDialog.populateOrgsError.message=Failure loading organizations."}) @NbBundle.Messages({"HashDbCreateDatabaseDialog.populateOrgsError.message=Failure loading organizations."})
private void populateCombobox() { private void populateCombobox() {
orgComboBox.removeAllItems(); orgComboBox.removeAllItems();
@ -155,7 +156,7 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog {
orgs = dbManager.getOrganizations(); orgs = dbManager.getOrganizations();
orgs.forEach((org) -> { orgs.forEach((org) -> {
orgComboBox.addItem(org.getName()); orgComboBox.addItem(org.getName());
if(CentralRepoDbUtil.isDefaultOrg(org)){ if (CentralRepoDbUtil.isDefaultOrg(org)) {
orgComboBox.setSelectedItem(org.getName()); orgComboBox.setSelectedItem(org.getName());
selectedOrg = org; selectedOrg = org;
} }
@ -167,7 +168,7 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog {
JOptionPane.showMessageDialog(this, Bundle.HashDbCreateDatabaseDialog_populateOrgsError_message()); JOptionPane.showMessageDialog(this, Bundle.HashDbCreateDatabaseDialog_populateOrgsError_message());
Logger.getLogger(ImportCentralRepoDbProgressDialog.class.getName()).log(Level.SEVERE, "Failure loading organizations", ex); Logger.getLogger(ImportCentralRepoDbProgressDialog.class.getName()).log(Level.SEVERE, "Failure loading organizations", ex);
} }
} }
/** /**
* This method is called from within the constructor to initialize the form. * This method is called from within the constructor to initialize the form.
@ -413,7 +414,7 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog {
path.append(lastBaseDirectory); path.append(lastBaseDirectory);
File hashDbFolder = new File(path.toString()); File hashDbFolder = new File(path.toString());
// create the folder if it doesn't exist // create the folder if it doesn't exist
if (!hashDbFolder.exists()){ if (!hashDbFolder.exists()) {
hashDbFolder.mkdir(); hashDbFolder.mkdir();
} }
if (!hashSetNameTextField.getText().isEmpty()) { if (!hashSetNameTextField.getText().isEmpty()) {
@ -452,7 +453,7 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog {
return; return;
} }
if(fileTypeRadioButton.isSelected()){ if (fileTypeRadioButton.isSelected()) {
if (databasePathTextField.getText().isEmpty()) { if (databasePathTextField.getText().isEmpty()) {
JOptionPane.showMessageDialog(this, JOptionPane.showMessageDialog(this,
NbBundle.getMessage(this.getClass(), NbBundle.getMessage(this.getClass(),
@ -463,13 +464,13 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog {
return; return;
} }
} else { } else {
if(selectedOrg == null){ if (selectedOrg == null) {
JOptionPane.showMessageDialog(this, JOptionPane.showMessageDialog(this,
NbBundle.getMessage(this.getClass(), NbBundle.getMessage(this.getClass(),
"HashDbCreateDatabaseDialog.missingOrg"), "HashDbCreateDatabaseDialog.missingOrg"),
NbBundle.getMessage(this.getClass(), NbBundle.getMessage(this.getClass(),
"HashDbCreateDatabaseDialog.createHashDbErr"), "HashDbCreateDatabaseDialog.createHashDbErr"),
JOptionPane.ERROR_MESSAGE); JOptionPane.ERROR_MESSAGE);
return; return;
} }
} }
@ -487,7 +488,7 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog {
String errorMessage = NbBundle String errorMessage = NbBundle
.getMessage(this.getClass(), "HashDbCreateDatabaseDialog.errMsg.hashDbCreationErr"); .getMessage(this.getClass(), "HashDbCreateDatabaseDialog.errMsg.hashDbCreationErr");
if(fileTypeRadioButton.isSelected()){ if (fileTypeRadioButton.isSelected()) {
try { try {
newHashDb = HashDbManager.getInstance().addNewHashDatabaseNoSave(hashSetNameTextField.getText(), fileChooser.getSelectedFile().getCanonicalPath(), true, sendIngestMessagesCheckbox.isSelected(), type); newHashDb = HashDbManager.getInstance().addNewHashDatabaseNoSave(hashSetNameTextField.getText(), fileChooser.getSelectedFile().getCanonicalPath(), true, sendIngestMessagesCheckbox.isSelected(), type);
} catch (IOException ex) { } catch (IOException ex) {
@ -510,17 +511,17 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog {
} }
} else { } else {
// Check if a hash set with the same name/version already exists // Check if a hash set with the same name/version already exists
try{ try {
if(CentralRepository.getInstance().referenceSetExists(hashSetNameTextField.getText(), "")){ if (CentralRepository.getInstance().referenceSetExists(hashSetNameTextField.getText(), "")) {
JOptionPane.showMessageDialog(this, JOptionPane.showMessageDialog(this,
NbBundle.getMessage(this.getClass(), NbBundle.getMessage(this.getClass(),
"HashDbCreateDatabaseDialog.duplicateName"), "HashDbCreateDatabaseDialog.duplicateName"),
NbBundle.getMessage(this.getClass(), NbBundle.getMessage(this.getClass(),
"HashDbCreateDatabaseDialog.createHashDbErr"), "HashDbCreateDatabaseDialog.createHashDbErr"),
JOptionPane.ERROR_MESSAGE); JOptionPane.ERROR_MESSAGE);
return; return;
} }
} catch (CentralRepoException ex){ } catch (CentralRepoException ex) {
Logger.getLogger(HashDbImportDatabaseDialog.class.getName()).log(Level.SEVERE, "Error looking up reference set", ex); Logger.getLogger(HashDbImportDatabaseDialog.class.getName()).log(Level.SEVERE, "Error looking up reference set", ex);
JOptionPane.showMessageDialog(this, JOptionPane.showMessageDialog(this,
NbBundle.getMessage(this.getClass(), NbBundle.getMessage(this.getClass(),
@ -528,16 +529,16 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog {
NbBundle.getMessage(this.getClass(), NbBundle.getMessage(this.getClass(),
"HashDbCreateDatabaseDialog.createHashDbErr"), "HashDbCreateDatabaseDialog.createHashDbErr"),
JOptionPane.ERROR_MESSAGE); JOptionPane.ERROR_MESSAGE);
return; return;
} }
try{ try {
int referenceSetID = CentralRepository.getInstance().newReferenceSet(new CentralRepoFileSet(selectedOrg.getOrgID(), hashSetNameTextField.getText(), int referenceSetID = CentralRepository.getInstance().newReferenceSet(new CentralRepoFileSet(selectedOrg.getOrgID(), hashSetNameTextField.getText(),
"", fileKnown, false, CentralRepository.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID))); "", fileKnown, false, CentralRepository.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID)));
newHashDb = HashDbManager.getInstance().addExistingCentralRepoHashSet(hashSetNameTextField.getText(), newHashDb = HashDbManager.getInstance().addExistingCentralRepoHashSet(hashSetNameTextField.getText(),
"", referenceSetID, "", referenceSetID,
true, sendIngestMessagesCheckbox.isSelected(), type, false); true, sendIngestMessagesCheckbox.isSelected(), type, false);
} catch (CentralRepoException | TskCoreException ex){ } catch (CentralRepoException | TskCoreException ex) {
Logger.getLogger(HashDbImportDatabaseDialog.class.getName()).log(Level.SEVERE, "Error creating new reference set", ex); Logger.getLogger(HashDbImportDatabaseDialog.class.getName()).log(Level.SEVERE, "Error creating new reference set", ex);
JOptionPane.showMessageDialog(this, JOptionPane.showMessageDialog(this,
NbBundle.getMessage(this.getClass(), NbBundle.getMessage(this.getClass(),
@ -545,8 +546,8 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog {
NbBundle.getMessage(this.getClass(), NbBundle.getMessage(this.getClass(),
"HashDbCreateDatabaseDialog.createHashDbErr"), "HashDbCreateDatabaseDialog.createHashDbErr"),
JOptionPane.ERROR_MESSAGE); JOptionPane.ERROR_MESSAGE);
return; return;
} }
} }
dispose(); dispose();
@ -561,11 +562,13 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog {
// update the combobox options // update the combobox options
if (dialog.isChanged()) { if (dialog.isChanged()) {
populateCombobox(); populateCombobox();
} }
}//GEN-LAST:event_orgButtonActionPerformed }//GEN-LAST:event_orgButtonActionPerformed
private void orgComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_orgComboBoxActionPerformed private void orgComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_orgComboBoxActionPerformed
if (null == orgComboBox.getSelectedItem()) return; if (null == orgComboBox.getSelectedItem()) {
return;
}
String orgName = this.orgComboBox.getSelectedItem().toString(); String orgName = this.orgComboBox.getSelectedItem().toString();
for (CentralRepoOrganization org : orgs) { for (CentralRepoOrganization org : orgs) {
if (org.getName().equals(orgName)) { if (org.getName().equals(orgName)) {

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-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");
@ -42,6 +42,7 @@ import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDb.KnownFile
import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDbManagerException; import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDbManagerException;
import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDb; import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDb;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.featureaccess.FeatureAccessUtils;
/** /**
* Instances of this class allow a user to select an existing hash database and * Instances of this class allow a user to select an existing hash database and
@ -89,7 +90,7 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog {
String[] EXTENSION = new String[]{"txt", "kdb", "idx", "hash", "Hash", "hsh"}; //NON-NLS String[] EXTENSION = new String[]{"txt", "kdb", "idx", "hash", "Hash", "hsh"}; //NON-NLS
FileNameExtensionFilter filter = new FileNameExtensionFilter( FileNameExtensionFilter filter = new FileNameExtensionFilter(
NbBundle.getMessage(this.getClass(), "HashDbImportDatabaseDialog.fileNameExtFilter.text"), EXTENSION); NbBundle.getMessage(this.getClass(), "HashDbImportDatabaseDialog.fileNameExtFilter.text"), EXTENSION);
fileChooser.setFileFilter(filter); fileChooser.setFileFilter(filter);
fileChooser.setMultiSelectionEnabled(false); fileChooser.setMultiSelectionEnabled(false);
} }
@ -105,29 +106,28 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog {
} }
return shortenedPath; return shortenedPath;
} }
private void enableComponents(){ private void enableComponents() {
if (!CentralRepository.isEnabled() || !FeatureAccessUtils.canAddHashSetsToCentralRepo()) {
if(! CentralRepository.isEnabled()){
centralRepoRadioButton.setEnabled(false); centralRepoRadioButton.setEnabled(false);
fileTypeRadioButton.setSelected(true); fileTypeRadioButton.setSelected(true);
} else { } else {
populateCombobox(); populateCombobox();
} }
boolean isFileType = fileTypeRadioButton.isSelected(); boolean isFileType = fileTypeRadioButton.isSelected();
// Central repo only // Central repo only
lbVersion.setEnabled((! isFileType) && (readOnlyCheckbox.isSelected())); lbVersion.setEnabled((!isFileType) && (readOnlyCheckbox.isSelected()));
versionTextField.setEnabled((! isFileType) && (readOnlyCheckbox.isSelected())); versionTextField.setEnabled((!isFileType) && (readOnlyCheckbox.isSelected()));
lbOrg.setEnabled(! isFileType); lbOrg.setEnabled(!isFileType);
orgButton.setEnabled(! isFileType); orgButton.setEnabled(!isFileType);
orgComboBox.setEnabled(! isFileType); orgComboBox.setEnabled(!isFileType);
readOnlyCheckbox.setEnabled(! isFileType); readOnlyCheckbox.setEnabled(!isFileType);
} }
@NbBundle.Messages({"HashDbImportDatabaseDialog.populateOrgsError.message=Failure loading organizations."}) @NbBundle.Messages({"HashDbImportDatabaseDialog.populateOrgsError.message=Failure loading organizations."})
private void populateCombobox() { private void populateCombobox() {
orgComboBox.removeAllItems(); orgComboBox.removeAllItems();
@ -136,7 +136,7 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog {
orgs = dbManager.getOrganizations(); orgs = dbManager.getOrganizations();
orgs.forEach((org) -> { orgs.forEach((org) -> {
orgComboBox.addItem(org.getName()); orgComboBox.addItem(org.getName());
if(CentralRepoDbUtil.isDefaultOrg(org)){ if (CentralRepoDbUtil.isDefaultOrg(org)) {
orgComboBox.setSelectedItem(org.getName()); orgComboBox.setSelectedItem(org.getName());
selectedOrg = org; selectedOrg = org;
} }
@ -468,28 +468,28 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog {
JOptionPane.ERROR_MESSAGE); JOptionPane.ERROR_MESSAGE);
return; return;
} }
if(centralRepoRadioButton.isSelected()){ if (centralRepoRadioButton.isSelected()) {
if(readOnlyCheckbox.isSelected() && versionTextField.getText().isEmpty()){ if (readOnlyCheckbox.isSelected() && versionTextField.getText().isEmpty()) {
JOptionPane.showMessageDialog(this, JOptionPane.showMessageDialog(this,
NbBundle.getMessage(this.getClass(), NbBundle.getMessage(this.getClass(),
"HashDbImportDatabaseDialog.missingVersion"), "HashDbImportDatabaseDialog.missingVersion"),
NbBundle.getMessage(this.getClass(), NbBundle.getMessage(this.getClass(),
"HashDbImportDatabaseDialog.importHashDbErr"), "HashDbImportDatabaseDialog.importHashDbErr"),
JOptionPane.ERROR_MESSAGE); JOptionPane.ERROR_MESSAGE);
return; return;
} }
if(selectedOrg == null){ if (selectedOrg == null) {
JOptionPane.showMessageDialog(this, JOptionPane.showMessageDialog(this,
NbBundle.getMessage(this.getClass(), NbBundle.getMessage(this.getClass(),
"HashDbImportDatabaseDialog.missingOrg"), "HashDbImportDatabaseDialog.missingOrg"),
NbBundle.getMessage(this.getClass(), NbBundle.getMessage(this.getClass(),
"HashDbImportDatabaseDialog.importHashDbErr"), "HashDbImportDatabaseDialog.importHashDbErr"),
JOptionPane.ERROR_MESSAGE); JOptionPane.ERROR_MESSAGE);
return; return;
} }
} }
if (selectedFilePath.isEmpty()) { if (selectedFilePath.isEmpty()) {
JOptionPane.showMessageDialog(this, JOptionPane.showMessageDialog(this,
@ -500,7 +500,7 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog {
JOptionPane.ERROR_MESSAGE); JOptionPane.ERROR_MESSAGE);
return; return;
} }
File file = new File(selectedFilePath); File file = new File(selectedFilePath);
if (!file.exists()) { if (!file.exists()) {
JOptionPane.showMessageDialog(this, JOptionPane.showMessageDialog(this,
@ -523,11 +523,11 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog {
String errorMessage = NbBundle.getMessage(this.getClass(), "HashDbImportDatabaseDialog.unableToCopyToUserDirMsg", locationInUserConfigDir); String errorMessage = NbBundle.getMessage(this.getClass(), "HashDbImportDatabaseDialog.unableToCopyToUserDirMsg", locationInUserConfigDir);
Logger.getLogger(HashDbImportDatabaseDialog.class.getName()).log(Level.SEVERE, errorMessage, ex); Logger.getLogger(HashDbImportDatabaseDialog.class.getName()).log(Level.SEVERE, errorMessage, ex);
JOptionPane.showMessageDialog(this, errorMessage, NbBundle.getMessage(this.getClass(), "HashDbImportDatabaseDialog.importHashDbErr"), JOptionPane.showMessageDialog(this, errorMessage, NbBundle.getMessage(this.getClass(), "HashDbImportDatabaseDialog.importHashDbErr"),
JOptionPane.ERROR_MESSAGE); JOptionPane.ERROR_MESSAGE);
return; return;
} }
} }
KnownFilesType type; KnownFilesType type;
if (knownRadioButton.isSelected()) { if (knownRadioButton.isSelected()) {
type = KnownFilesType.KNOWN; type = KnownFilesType.KNOWN;
@ -536,9 +536,9 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog {
} }
String errorMessage = NbBundle.getMessage(this.getClass(), String errorMessage = NbBundle.getMessage(this.getClass(),
"HashDbImportDatabaseDialog.errorMessage.failedToOpenHashDbMsg", "HashDbImportDatabaseDialog.errorMessage.failedToOpenHashDbMsg",
selectedFilePath); selectedFilePath);
if(fileTypeRadioButton.isSelected()){ if (fileTypeRadioButton.isSelected()) {
try { try {
selectedHashDb = HashDbManager.getInstance().addExistingHashDatabaseNoSave(hashSetNameTextField.getText(), selectedFilePath, true, sendIngestMessagesCheckbox.isSelected(), type); selectedHashDb = HashDbManager.getInstance().addExistingHashDatabaseNoSave(hashSetNameTextField.getText(), selectedFilePath, true, sendIngestMessagesCheckbox.isSelected(), type);
@ -552,19 +552,19 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog {
return; return;
} }
} else { } else {
// Check if a hash set with the same name/version already exists // Check if a hash set with the same name/version already exists
try{ try {
if(CentralRepository.getInstance().referenceSetExists(hashSetNameTextField.getText(), versionTextField.getText())){ if (CentralRepository.getInstance().referenceSetExists(hashSetNameTextField.getText(), versionTextField.getText())) {
JOptionPane.showMessageDialog(this, JOptionPane.showMessageDialog(this,
NbBundle.getMessage(this.getClass(), NbBundle.getMessage(this.getClass(),
"HashDbImportDatabaseDialog.duplicateName"), "HashDbImportDatabaseDialog.duplicateName"),
NbBundle.getMessage(this.getClass(), NbBundle.getMessage(this.getClass(),
"HashDbImportDatabaseDialog.importHashDbErr"), "HashDbImportDatabaseDialog.importHashDbErr"),
JOptionPane.ERROR_MESSAGE); JOptionPane.ERROR_MESSAGE);
return; return;
} }
} catch (CentralRepoException ex){ } catch (CentralRepoException ex) {
Logger.getLogger(HashDbImportDatabaseDialog.class.getName()).log(Level.SEVERE, "Error looking up reference set", ex); Logger.getLogger(HashDbImportDatabaseDialog.class.getName()).log(Level.SEVERE, "Error looking up reference set", ex);
JOptionPane.showMessageDialog(this, JOptionPane.showMessageDialog(this,
NbBundle.getMessage(this.getClass(), NbBundle.getMessage(this.getClass(),
@ -572,20 +572,20 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog {
NbBundle.getMessage(this.getClass(), NbBundle.getMessage(this.getClass(),
"HashDbImportDatabaseDialog.importHashDbErr"), "HashDbImportDatabaseDialog.importHashDbErr"),
JOptionPane.ERROR_MESSAGE); JOptionPane.ERROR_MESSAGE);
return; return;
} }
String version; String version;
if(readOnlyCheckbox.isSelected()){ if (readOnlyCheckbox.isSelected()) {
version = versionTextField.getText(); version = versionTextField.getText();
} else { } else {
// Editable databases don't have a version // Editable databases don't have a version
version = ""; version = "";
} }
ImportCentralRepoDbProgressDialog progressDialog = new ImportCentralRepoDbProgressDialog(); ImportCentralRepoDbProgressDialog progressDialog = new ImportCentralRepoDbProgressDialog();
progressDialog.importFile(hashSetNameTextField.getText(), version, progressDialog.importFile(hashSetNameTextField.getText(), version,
selectedOrg.getOrgID(), true, sendIngestMessagesCheckbox.isSelected(), type, selectedOrg.getOrgID(), true, sendIngestMessagesCheckbox.isSelected(), type,
readOnlyCheckbox.isSelected(), selectedFilePath); readOnlyCheckbox.isSelected(), selectedFilePath);
selectedHashDb = progressDialog.getDatabase(); selectedHashDb = progressDialog.getDatabase();
} }
@ -609,11 +609,13 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog {
// update the combobox options // update the combobox options
if (dialog.isChanged()) { if (dialog.isChanged()) {
populateCombobox(); populateCombobox();
} }
}//GEN-LAST:event_orgButtonActionPerformed }//GEN-LAST:event_orgButtonActionPerformed
private void orgComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_orgComboBoxActionPerformed private void orgComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_orgComboBoxActionPerformed
if (null == orgComboBox.getSelectedItem()) return; if (null == orgComboBox.getSelectedItem()) {
return;
}
String orgName = this.orgComboBox.getSelectedItem().toString(); String orgName = this.orgComboBox.getSelectedItem().toString();
for (CentralRepoOrganization org : orgs) { for (CentralRepoOrganization org : orgs) {
if (org.getName().equals(orgName)) { if (org.getName().equals(orgName)) {

View File

@ -0,0 +1,242 @@
/*
* 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.texttranslation.ui;
import java.awt.ComponentOrientation;
import java.awt.Font;
import java.io.IOException;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.TextUtil;
import org.sleuthkit.autopsy.texttranslation.NoServiceProviderException;
import org.sleuthkit.autopsy.texttranslation.TextTranslationService;
import org.sleuthkit.autopsy.texttranslation.TranslationException;
/**
* This is an abstract class for translating text and displaying to the user.
*/
public abstract class TranslateTextTask extends SwingWorker<TranslateTextTask.TranslateResult, Void> {
private static final Logger logger = Logger.getLogger(TranslatedTextViewer.class.getName());
private final boolean translateText;
private final String contentDescriptor;
/**
* This is a result of running and processing the translation.
*/
public static class TranslateResult {
private final String errorMessage;
private final String result;
private final boolean successful;
public static TranslateResult error(String message) {
return new TranslateResult(null, message, false);
}
public static TranslateResult success(String content) {
return new TranslateResult(content, null, true);
}
private TranslateResult(String result, String errorMessage, boolean successful) {
this.successful = successful;
this.errorMessage = errorMessage;
this.result = result;
}
public String getErrorMessage() {
return errorMessage;
}
public String getResult() {
return result;
}
public boolean isSuccessful() {
return successful;
}
}
/**
* This is the main constructor for the TranslateTextTask.
* @param translateText whether or not to translate text
* @param fileDescriptor the content descriptor for the item being
* translated (used for logging errors)
*/
public TranslateTextTask(boolean translateText, String fileDescriptor) {
this.translateText = translateText;
this.contentDescriptor = fileDescriptor;
}
/**
* This method retrieves the original text content to be translated.
* @return the original text content
* @throws IOException
* @throws InterruptedException
* @throws IllegalStateException
*/
protected abstract String retrieveText() throws IOException, InterruptedException, IllegalStateException;
/**
* This method should be overridden when a translated text result is received.
* @param text the text to display
* @param orientation the orientation of the text
* @param font the font style (returns plain)
*/
protected abstract void onTextDisplay(String text, ComponentOrientation orientation, int font);
/**
* When a progress result is received, this method is called.
* This method can be overridden depending on the scenario, but defaults to just displaying using onTextDisplay.
* @param text the text of the status update
* @param orientation the orientation for the status
* @param font the font style of the status
*/
protected void onProgressDisplay(String text, ComponentOrientation orientation, int font) {
// This defaults to normal display unless overridden.
onTextDisplay(text, orientation, font);
}
/**
* When an error result is received, this method is called. This method can be overridden depending on the
* scenario but defaults to just displaying using onTextDisplay.
* @param text the text of the error
* @param orientation the orientation for the error
* @param font the font style of the error
*/
protected void onErrorDisplay(String text, ComponentOrientation orientation, int font) {
// This defaults to normal display unless overridden.
onTextDisplay(text, orientation, font);
}
@NbBundle.Messages({
"TranslatedContentViewer.translatingText=Translating text, please wait...",
"TranslatedContentViewer.fileHasNoText=File has no text.",
"TranslatedContentViewer.noServiceProvider=The machine translation software was not found.",
"# {0} - exception message", "TranslatedContentViewer.translationException=An error occurred while translating the text ({0})."
})
@Override
public TranslateResult doInBackground() throws InterruptedException {
if (this.isCancelled()) {
throw new InterruptedException();
}
String fileText;
try {
fileText = retrieveText();
} catch (IOException | IllegalStateException ex) {
return TranslateResult.error(ex.getMessage());
}
if (this.isCancelled()) {
throw new InterruptedException();
}
if (fileText == null || fileText.isEmpty()) {
return TranslateResult.error(Bundle.TranslatedContentViewer_fileHasNoText());
}
if (!this.translateText) {
return TranslateResult.success(fileText);
}
return translateRetrievedText(fileText);
}
/**
* This is the final step in the translation swing worker prior to being {@link #done() done()}; translates the text if needed.
* @param fileText the text to translate
* @return the translated text
* @throws InterruptedException if operation is canclled, an interrupted exception is thrown
*/
private TranslateResult translateRetrievedText(String fileText) throws InterruptedException {
SwingUtilities.invokeLater(() -> {
onProgressDisplay(Bundle.TranslatedContentViewer_translatingText(), ComponentOrientation.LEFT_TO_RIGHT, Font.ITALIC);
});
try {
String translation = translate(fileText);
if (this.isCancelled()) {
throw new InterruptedException();
}
if (translation == null || translation.isEmpty()) {
return TranslateResult.error(Bundle.TranslatedContentViewer_emptyTranslation());
} else {
return TranslateResult.success(translation);
}
} catch (NoServiceProviderException ex) {
logger.log(Level.WARNING, "Error translating text for file " + this.contentDescriptor, ex);
return TranslateResult.error(Bundle.TranslatedContentViewer_noServiceProvider());
} catch (TranslationException ex) {
logger.log(Level.WARNING, "Error translating text for file " + this.contentDescriptor, ex);
return TranslateResult.error(Bundle.TranslatedContentViewer_translationException(ex.getMessage()));
}
}
@Override
public void done() {
try {
TranslateResult executionResult = get();
if (this.isCancelled()) {
throw new InterruptedException();
}
if (executionResult.isSuccessful()) {
String result = executionResult.getResult();
int len = result.length();
int maxOrientChars = Math.min(len, 1024);
String orientDetectSubstring = result.substring(0, maxOrientChars);
ComponentOrientation orientation = TextUtil.getTextDirection(orientDetectSubstring);
onTextDisplay(result, orientation, Font.PLAIN);
} else {
onErrorDisplay(executionResult.getErrorMessage(), ComponentOrientation.LEFT_TO_RIGHT, Font.ITALIC);
}
} catch (InterruptedException | CancellationException ignored) {
// Task cancelled, no error.
} catch (ExecutionException ex) {
logger.log(Level.WARNING, "Error occurred during background task execution for file " + this.contentDescriptor, ex);
onErrorDisplay(Bundle.TranslatedContentViewer_translationException(ex.getMessage()), ComponentOrientation.LEFT_TO_RIGHT, Font.ITALIC);
}
}
/**
* This method passes the translation off to the {@link org.sleuthkit.autopsy.texttranslation.TextTranslationService translation service provider}.
*
* @param input text to be translated
*
* @return translated text or error message
*/
@NbBundle.Messages({
"TranslatedContentViewer.emptyTranslation=The machine translation software did not return any text."
})
protected String translate(String input) throws NoServiceProviderException, TranslationException {
TextTranslationService translatorInstance = TextTranslationService.getInstance();
return translatorInstance.translate(input);
}
}

View File

@ -28,8 +28,6 @@ import java.awt.event.ActionListener;
import java.io.IOException; import java.io.IOException;
import java.io.Reader; import java.io.Reader;
import java.util.Arrays; import java.util.Arrays;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadFactory;
@ -37,19 +35,15 @@ import org.openide.nodes.Node;
import org.openide.util.lookup.ServiceProvider; import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.corecomponentinterfaces.TextViewer; import org.sleuthkit.autopsy.corecomponentinterfaces.TextViewer;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import javax.swing.SwingWorker;
import org.openide.util.Lookup; import org.openide.util.Lookup;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.lookup.Lookups; import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.corecomponents.DataContentViewerUtility; import org.sleuthkit.autopsy.corecomponents.DataContentViewerUtility;
import org.sleuthkit.autopsy.coreutils.ExecUtil.ProcessTerminator; import org.sleuthkit.autopsy.coreutils.ExecUtil.ProcessTerminator;
import org.sleuthkit.autopsy.coreutils.TextUtil;
import org.sleuthkit.autopsy.textextractors.TextExtractor; import org.sleuthkit.autopsy.textextractors.TextExtractor;
import org.sleuthkit.autopsy.textextractors.TextExtractorFactory; import org.sleuthkit.autopsy.textextractors.TextExtractorFactory;
import org.sleuthkit.autopsy.textextractors.configs.ImageConfig; import org.sleuthkit.autopsy.textextractors.configs.ImageConfig;
import org.sleuthkit.autopsy.texttranslation.TextTranslationService; import org.sleuthkit.autopsy.texttranslation.TextTranslationService;
import org.sleuthkit.autopsy.texttranslation.NoServiceProviderException;
import org.sleuthkit.autopsy.texttranslation.TranslationException;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import java.util.List; import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
@ -160,114 +154,41 @@ public final class TranslatedTextViewer implements TextViewer {
/** /**
* Extracts text from a file and optionally translates it. * Extracts text from a file and optionally translates it.
*/ */
private class ExtractAndTranslateTextTask extends SwingWorker<String, Void> { private class ExtractAndTranslateTextTask extends TranslateTextTask {
private final AbstractFile file; private final AbstractFile file;
private final boolean translateText;
private ExtractAndTranslateTextTask(AbstractFile file, boolean translateText) { private ExtractAndTranslateTextTask(AbstractFile file, boolean translateText) {
super(translateText, String.format("%s (objId=%d)", file.getName(), file.getId()));
this.file = file; this.file = file;
this.translateText = translateText;
}
@NbBundle.Messages({
"TranslatedContentViewer.extractingText=Extracting text, please wait...",
"TranslatedContentViewer.translatingText=Translating text, please wait...",
"# {0} - exception message", "TranslatedContentViewer.errorExtractingText=An error occurred while extracting the text ({0}).",
"TranslatedContentViewer.fileHasNoText=File has no text.",
"TranslatedContentViewer.noServiceProvider=The machine translation software was not found.",
"# {0} - exception message", "TranslatedContentViewer.translationException=An error occurred while translating the text ({0})."
})
@Override
public String doInBackground() throws InterruptedException {
if (this.isCancelled()) {
throw new InterruptedException();
}
SwingUtilities.invokeLater(() -> {
panel.display(Bundle.TranslatedContentViewer_extractingText(), ComponentOrientation.LEFT_TO_RIGHT, Font.ITALIC);
});
String fileText;
try {
fileText = getFileText(file);
} catch (IOException | TextExtractor.InitReaderException ex) {
logger.log(Level.WARNING, String.format("Error extracting text for file %s (objId=%d)", file.getName(), file.getId()), ex);
return Bundle.TranslatedContentViewer_errorExtractingText(ex.getMessage());
}
if (this.isCancelled()) {
throw new InterruptedException();
}
if (fileText == null || fileText.isEmpty()) {
return Bundle.TranslatedContentViewer_fileHasNoText();
}
if (!this.translateText) {
return fileText;
}
SwingUtilities.invokeLater(() -> {
panel.display(Bundle.TranslatedContentViewer_translatingText(), ComponentOrientation.LEFT_TO_RIGHT, Font.ITALIC);
});
String translation;
try {
translation = translate(fileText);
} catch (NoServiceProviderException ex) {
logger.log(Level.WARNING, String.format("Error translating text for file %s (objId=%d)", file.getName(), file.getId()), ex);
translation = Bundle.TranslatedContentViewer_noServiceProvider();
} catch (TranslationException ex) {
logger.log(Level.WARNING, String.format("Error translating text for file %s (objId=%d)", file.getName(), file.getId()), ex);
translation = Bundle.TranslatedContentViewer_translationException(ex.getMessage());
}
if (this.isCancelled()) {
throw new InterruptedException();
}
return translation;
}
@Override
public void done() {
try {
String result = get();
if (this.isCancelled()) {
throw new InterruptedException();
}
int len = result.length();
int maxOrientChars = Math.min(len, 1024);
String orientDetectSubstring = result.substring(0, maxOrientChars);
ComponentOrientation orientation = TextUtil.getTextDirection(orientDetectSubstring);
panel.display(result, orientation, Font.PLAIN);
} catch (InterruptedException | CancellationException ignored) {
// Task cancelled, no error.
} catch (ExecutionException ex) {
logger.log(Level.WARNING, String.format("Error occurred during background task execution for file %s (objId=%d)", file.getName(), file.getId()), ex);
panel.display(Bundle.TranslatedContentViewer_translationException(ex.getMessage()), ComponentOrientation.LEFT_TO_RIGHT, Font.ITALIC);
}
} }
/** /**
* Pass the translation off to the Translation service provider. * Extracts text from the current node
* *
* @param input Text to be translated * @return Extracted text
* *
* @return Translated text or error message * @throws Exception
*/ */
@NbBundle.Messages({ @NbBundle.Messages({
"TranslatedContentViewer.emptyTranslation=The machine translation software did not return any text." "TranslatedContentViewer.extractingText=Extracting text, please wait...",
"# {0} - exception message", "TranslatedContentViewer.errorExtractingText=An error occurred while extracting the text ({0}).",
}) })
private String translate(String input) throws NoServiceProviderException, TranslationException { protected String retrieveText() throws IOException, InterruptedException, IllegalStateException {
TextTranslationService translatorInstance = TextTranslationService.getInstance(); SwingUtilities.invokeLater(() -> {
String translatedResult = translatorInstance.translate(input); onProgressDisplay(Bundle.TranslatedContentViewer_extractingText(), ComponentOrientation.LEFT_TO_RIGHT, Font.ITALIC);
if (translatedResult.isEmpty()) { });
return Bundle.TranslatedContentViewer_emptyTranslation();
}
return translatedResult;
}
try {
return getFileText(file);
} catch (IOException | TextExtractor.InitReaderException ex) {
logger.log(Level.WARNING, String.format("Error extracting text for file %s (objId=%d)", file.getName(), file.getId()), ex);
// throw new exception with message to be displayed to user
throw new IllegalStateException(Bundle.TranslatedContentViewer_errorExtractingText(ex.getMessage()), ex);
}
}
/** /**
* Extracts text from the given node * Extracts text from the given node
* *
@ -280,9 +201,7 @@ public final class TranslatedTextViewer implements TextViewer {
* @throws * @throws
* org.sleuthkit.autopsy.textextractors.TextExtractor.InitReaderException * org.sleuthkit.autopsy.textextractors.TextExtractor.InitReaderException
*/ */
private String getFileText(AbstractFile file) throws IOException, private String getFileText(AbstractFile file) throws IOException, InterruptedException, TextExtractor.InitReaderException {
InterruptedException, TextExtractor.InitReaderException {
final boolean isImage = file.getMIMEType().toLowerCase().startsWith("image/"); // NON-NLS final boolean isImage = file.getMIMEType().toLowerCase().startsWith("image/"); // NON-NLS
String result; String result;
if (isImage) { if (isImage) {
@ -382,8 +301,15 @@ public final class TranslatedTextViewer implements TextViewer {
return TextExtractorFactory.getStringsExtractor(file, context).getReader(); return TextExtractorFactory.getStringsExtractor(file, context).getReader();
} }
} }
@Override
protected void onTextDisplay(String text, ComponentOrientation orientation, int font) {
panel.display(text, orientation, font);
}
} }
/** /**
* Listens for drop-down selection changes and pushes processing off of the * Listens for drop-down selection changes and pushes processing off of the
* EDT and into a SwingWorker. * EDT and into a SwingWorker.

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2014-2019 Basis Technology Corp. * Copyright 2014-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");
@ -72,7 +72,6 @@ import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent;
import org.sleuthkit.autopsy.coreutils.History; import org.sleuthkit.autopsy.coreutils.History;
import org.sleuthkit.autopsy.coreutils.LoggedTask; import org.sleuthkit.autopsy.coreutils.LoggedTask;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.events.AutopsyEvent; import org.sleuthkit.autopsy.events.AutopsyEvent;
import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.IngestManager;
@ -806,7 +805,6 @@ public class TimeLineController {
future.get(); future.get();
} catch (InterruptedException | ExecutionException ex) { } catch (InterruptedException | ExecutionException ex) {
logger.log(Level.SEVERE, errorLogMessage, ex); logger.log(Level.SEVERE, errorLogMessage, ex);
MessageNotifyUtil.Message.error(errorUserMessage);
} }
}, MoreExecutors.directExecutor()); }, MoreExecutors.directExecutor());
} }

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

@ -37,7 +37,7 @@ public class ScalrWrapper {
return Scalr.resize(input, size, Scalr.OP_ANTIALIAS); return Scalr.resize(input, size, Scalr.OP_ANTIALIAS);
} }
public static synchronized BufferedImage resize(BufferedImage bufferedImage, Method method, Scalr.Mode mode, int width, int height, BufferedImageOp... ops) { public static synchronized BufferedImage resize(BufferedImage bufferedImage, Method method, Scalr.Mode mode, int width, int height, BufferedImageOp ...ops) {
return Scalr.resize(bufferedImage, method, mode, width, height, ops); return Scalr.resize(bufferedImage, method, mode, width, height, ops);
} }

View File

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