/*
* Autopsy Forensic Browser
*
* Copyright 2014 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.core;
import java.util.Base64;
import java.util.prefs.BackingStoreException;
import org.sleuthkit.autopsy.events.MessageServiceConnectionInfo;
import java.util.prefs.PreferenceChangeListener;
import java.util.prefs.Preferences;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import org.openide.util.NbBundle;
import org.openide.util.NbPreferences;
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
import org.sleuthkit.datamodel.CaseDbConnectionInfo;
import org.sleuthkit.datamodel.TskData.DbType;
/**
* Provides convenient access to a Preferences node for user preferences with
* default values.
*/
public final class UserPreferences {
private static final boolean IS_WINDOWS_OS = PlatformUtil.isWindowsOS();
private static final Preferences preferences = NbPreferences.forModule(UserPreferences.class);
public static final String KEEP_PREFERRED_VIEWER = "KeepPreferredViewer"; // NON-NLS
public static final String HIDE_KNOWN_FILES_IN_DATA_SRCS_TREE = "HideKnownFilesInDataSourcesTree"; //NON-NLS
public static final String HIDE_KNOWN_FILES_IN_VIEWS_TREE = "HideKnownFilesInViewsTree"; //NON-NLS
public static final String HIDE_SLACK_FILES_IN_DATA_SRCS_TREE = "HideSlackFilesInDataSourcesTree"; //NON-NLS
public static final String HIDE_SLACK_FILES_IN_VIEWS_TREE = "HideSlackFilesInViewsTree"; //NON-NLS
public static final String DISPLAY_TIMES_IN_LOCAL_TIME = "DisplayTimesInLocalTime"; //NON-NLS
public static final String NUMBER_OF_FILE_INGEST_THREADS = "NumberOfFileIngestThreads"; //NON-NLS
public static final String IS_MULTI_USER_MODE_ENABLED = "IsMultiUserModeEnabled"; //NON-NLS
public static final String EXTERNAL_DATABASE_HOSTNAME_OR_IP = "ExternalDatabaseHostnameOrIp"; //NON-NLS
public static final String EXTERNAL_DATABASE_PORTNUMBER = "ExternalDatabasePortNumber"; //NON-NLS
public static final String EXTERNAL_DATABASE_NAME = "ExternalDatabaseName"; //NON-NLS
public static final String EXTERNAL_DATABASE_USER = "ExternalDatabaseUsername"; //NON-NLS
public static final String EXTERNAL_DATABASE_PASSWORD = "ExternalDatabasePassword"; //NON-NLS
public static final String EXTERNAL_DATABASE_TYPE = "ExternalDatabaseType"; //NON-NLS
public static final String INDEXING_SERVER_HOST = "IndexingServerHost"; //NON-NLS
public static final String INDEXING_SERVER_PORT = "IndexingServerPort"; //NON-NLS
private static final String MESSAGE_SERVICE_PASSWORD = "MessageServicePassword"; //NON-NLS
private static final String MESSAGE_SERVICE_USER = "MessageServiceUser"; //NON-NLS
private static final String MESSAGE_SERVICE_HOST = "MessageServiceHost"; //NON-NLS
private static final String MESSAGE_SERVICE_PORT = "MessageServicePort"; //NON-NLS
public static final String PROCESS_TIME_OUT_ENABLED = "ProcessTimeOutEnabled"; //NON-NLS
public static final String PROCESS_TIME_OUT_HOURS = "ProcessTimeOutHours"; //NON-NLS
private static final int DEFAULT_PROCESS_TIMEOUT_HR = 60;
private static final String DEFAULT_PORT_STRING = "61616";
private static final int DEFAULT_PORT_INT = 61616;
private static final String APP_NAME = "AppName";
// Prevent instantiation.
private UserPreferences() {
}
/**
* Reload all preferences from disk. This is only needed if the preferences
* file is being directly modified on disk while Autopsy is running.
*
* @throws BackingStoreException
*/
public static void reloadFromStorage() throws BackingStoreException {
preferences.sync();
}
/**
* Saves the current preferences to storage. This is only needed if the
* preferences files are going to be copied to another location while
* Autopsy is running.
*
* @throws BackingStoreException
*/
public static void saveToStorage() throws BackingStoreException {
preferences.flush();
}
public static void addChangeListener(PreferenceChangeListener listener) {
preferences.addPreferenceChangeListener(listener);
}
public static void removeChangeListener(PreferenceChangeListener listener) {
preferences.removePreferenceChangeListener(listener);
}
public static boolean keepPreferredContentViewer() {
return preferences.getBoolean(KEEP_PREFERRED_VIEWER, false);
}
public static void setKeepPreferredContentViewer(boolean value) {
preferences.putBoolean(KEEP_PREFERRED_VIEWER, value);
}
public static boolean hideKnownFilesInDataSourcesTree() {
return preferences.getBoolean(HIDE_KNOWN_FILES_IN_DATA_SRCS_TREE, false);
}
public static void setHideKnownFilesInDataSourcesTree(boolean value) {
preferences.putBoolean(HIDE_KNOWN_FILES_IN_DATA_SRCS_TREE, value);
}
public static boolean hideKnownFilesInViewsTree() {
return preferences.getBoolean(HIDE_KNOWN_FILES_IN_VIEWS_TREE, true);
}
public static void setHideKnownFilesInViewsTree(boolean value) {
preferences.putBoolean(HIDE_KNOWN_FILES_IN_VIEWS_TREE, value);
}
public static boolean hideSlackFilesInDataSourcesTree() {
return preferences.getBoolean(HIDE_SLACK_FILES_IN_DATA_SRCS_TREE, true);
}
public static void setHideSlackFilesInDataSourcesTree(boolean value) {
preferences.putBoolean(HIDE_SLACK_FILES_IN_DATA_SRCS_TREE, value);
}
public static boolean hideSlackFilesInViewsTree() {
return preferences.getBoolean(HIDE_SLACK_FILES_IN_VIEWS_TREE, true);
}
public static void setHideSlackFilesInViewsTree(boolean value) {
preferences.putBoolean(HIDE_SLACK_FILES_IN_VIEWS_TREE, value);
}
public static boolean displayTimesInLocalTime() {
return preferences.getBoolean(DISPLAY_TIMES_IN_LOCAL_TIME, true);
}
public static void setDisplayTimesInLocalTime(boolean value) {
preferences.putBoolean(DISPLAY_TIMES_IN_LOCAL_TIME, value);
}
public static int numberOfFileIngestThreads() {
return preferences.getInt(NUMBER_OF_FILE_INGEST_THREADS, 2);
}
public static void setNumberOfFileIngestThreads(int value) {
preferences.putInt(NUMBER_OF_FILE_INGEST_THREADS, value);
}
/**
* Reads persisted case database connection info.
* @return An object encapsulating the database connection info.
* @throws org.sleuthkit.autopsy.core.UserPreferencesException
*/
public static CaseDbConnectionInfo getDatabaseConnectionInfo() throws UserPreferencesException {
DbType dbType;
try {
dbType = DbType.valueOf(preferences.get(EXTERNAL_DATABASE_TYPE, "POSTGRESQL")); //NON-NLS
} catch (Exception ex) {
dbType = DbType.SQLITE;
}
return new CaseDbConnectionInfo(
preferences.get(EXTERNAL_DATABASE_HOSTNAME_OR_IP, ""),
preferences.get(EXTERNAL_DATABASE_PORTNUMBER, "5432"),
preferences.get(EXTERNAL_DATABASE_USER, ""),
TextConverter.convertHexTextToText(preferences.get(EXTERNAL_DATABASE_PASSWORD, "")),
dbType);
}
/**
* Persists case database connection info.
*
* @param connectionInfo An object encapsulating the database connection
* info.
* @throws org.sleuthkit.autopsy.core.UserPreferencesException
*/
public static void setDatabaseConnectionInfo(CaseDbConnectionInfo connectionInfo) throws UserPreferencesException {
preferences.put(EXTERNAL_DATABASE_HOSTNAME_OR_IP, connectionInfo.getHost());
preferences.put(EXTERNAL_DATABASE_PORTNUMBER, connectionInfo.getPort());
preferences.put(EXTERNAL_DATABASE_USER, connectionInfo.getUserName());
preferences.put(EXTERNAL_DATABASE_PASSWORD, TextConverter.convertTextToHexText(connectionInfo.getPassword()));
preferences.put(EXTERNAL_DATABASE_TYPE, connectionInfo.getDbType().toString());
}
public static void setIsMultiUserModeEnabled(boolean enabled) {
preferences.putBoolean(IS_MULTI_USER_MODE_ENABLED, enabled);
}
public static boolean getIsMultiUserModeEnabled() {
if (!IS_WINDOWS_OS) {
return false;
}
return preferences.getBoolean(IS_MULTI_USER_MODE_ENABLED, false);
}
public static String getIndexingServerHost() {
return preferences.get(INDEXING_SERVER_HOST, "");
}
public static void setIndexingServerHost(String hostName) {
preferences.put(INDEXING_SERVER_HOST, hostName);
}
public static String getIndexingServerPort() {
return preferences.get(INDEXING_SERVER_PORT, "8983");
}
public static void setIndexingServerPort(int port) {
preferences.putInt(INDEXING_SERVER_PORT, port);
}
/**
* Persists message service connection info.
*
* @param info An object encapsulating the message service info.
* @throws org.sleuthkit.autopsy.core.UserPreferencesException
*/
public static void setMessageServiceConnectionInfo(MessageServiceConnectionInfo info) throws UserPreferencesException {
preferences.put(MESSAGE_SERVICE_HOST, info.getHost());
preferences.put(MESSAGE_SERVICE_PORT, Integer.toString(info.getPort()));
preferences.put(MESSAGE_SERVICE_USER, info.getUserName());
preferences.put(MESSAGE_SERVICE_PASSWORD, TextConverter.convertTextToHexText(info.getPassword()));
}
/**
* Reads persisted message service connection info.
*
* @return An object encapsulating the message service info.
* @throws org.sleuthkit.autopsy.core.UserPreferencesException
*/
public static MessageServiceConnectionInfo getMessageServiceConnectionInfo() throws UserPreferencesException {
int port;
try {
port = Integer.parseInt(preferences.get(MESSAGE_SERVICE_PORT, DEFAULT_PORT_STRING));
} catch (NumberFormatException ex) {
// if there is an error parsing the port number, use the default port number
port = DEFAULT_PORT_INT;
}
return new MessageServiceConnectionInfo(
preferences.get(MESSAGE_SERVICE_HOST, ""),
port,
preferences.get(MESSAGE_SERVICE_USER, ""),
TextConverter.convertHexTextToText(preferences.get(MESSAGE_SERVICE_PASSWORD, "")));
}
/**
* Reads persisted process time out value.
*
* @return int Process time out value (hours).
*/
public static int getProcessTimeOutHrs() {
int timeOut = preferences.getInt(PROCESS_TIME_OUT_HOURS, DEFAULT_PROCESS_TIMEOUT_HR);
if (timeOut < 0) {
timeOut = 0;
}
return timeOut;
}
/**
* Stores persisted process time out value.
*
* @param value Persisted process time out value (hours).
*/
public static void setProcessTimeOutHrs(int value) {
if (value < 0) {
value = 0;
}
preferences.putInt(PROCESS_TIME_OUT_HOURS, value);
}
/**
* Reads persisted setting of whether process time out functionality is
* enabled.
*
* @return boolean True if process time out is functionality enabled, false
* otherwise.
*/
public static boolean getIsTimeOutEnabled() {
boolean enabled = preferences.getBoolean(PROCESS_TIME_OUT_ENABLED, false);
return enabled;
}
/**
* Stores persisted setting of whether process time out functionality is
* enabled.
*
* @param enabled Persisted setting of whether process time out
* functionality is enabled.
*/
public static void setIsTimeOutEnabled(boolean enabled) {
preferences.putBoolean(PROCESS_TIME_OUT_ENABLED, enabled);
}
/**
* Get the display name for this program
* @return Name of this program
*/
public static String getAppName(){
return preferences.get(APP_NAME, "Autopsy");
}
/**
* Set the display name for this program
*
* @param name Display name
*/
public static void setAppName(String name){
preferences.put(APP_NAME, name);
}
/**
* Provides ability to convert text to hex text.
*/
static final class TextConverter {
private static final char[] TMP = "hgleri21auty84fwe".toCharArray(); //NON-NLS
private static final byte[] SALT = {
(byte) 0xde, (byte) 0x33, (byte) 0x10, (byte) 0x12,
(byte) 0xde, (byte) 0x33, (byte) 0x10, (byte) 0x12,};
/**
* Convert text to hex text.
*
* @param property Input text string.
*
* @return Converted hex string.
*
* @throws org.sleuthkit.autopsy.core.UserPreferencesException
*/
static String convertTextToHexText(String property) throws UserPreferencesException {
try {
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES"); //NON-NLS
SecretKey key = keyFactory.generateSecret(new PBEKeySpec(TMP));
Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES"); //NON-NLS
pbeCipher.init(Cipher.ENCRYPT_MODE, key, new PBEParameterSpec(SALT, 20));
return base64Encode(pbeCipher.doFinal(property.getBytes("UTF-8")));
} catch (Exception ex) {
throw new UserPreferencesException(
NbBundle.getMessage(TextConverter.class, "TextConverter.convert.exception.txt"));
}
}
private static String base64Encode(byte[] bytes) {
return Base64.getEncoder().encodeToString(bytes);
}
/**
* Convert hex text back to text.
*
* @param property Input hex text string.
*
* @return Converted text string.
*
* @throws org.sleuthkit.autopsy.core.UserPreferencesException
*/
static String convertHexTextToText(String property) throws UserPreferencesException {
try {
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES"); //NON-NLS
SecretKey key = keyFactory.generateSecret(new PBEKeySpec(TMP));
Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES"); //NON-NLS
pbeCipher.init(Cipher.DECRYPT_MODE, key, new PBEParameterSpec(SALT, 20));
return new String(pbeCipher.doFinal(base64Decode(property)), "UTF-8");
} catch (Exception ex) {
throw new UserPreferencesException(
NbBundle.getMessage(TextConverter.class, "TextConverter.convertFromHex.exception.txt"));
}
}
private static byte[] base64Decode(String property) {
return Base64.getDecoder().decode(property);
}
}
}