19 package org.sleuthkit.autopsy.modules.hashdatabase;
 
   22 import java.io.FileInputStream;
 
   23 import java.io.FileOutputStream;
 
   24 import java.io.IOException;
 
   25 import java.io.Serializable;
 
   26 import java.util.ArrayList;
 
   27 import java.util.List;
 
   28 import java.util.logging.Level;
 
   29 import javax.swing.JOptionPane;
 
   30 import org.apache.commons.io.FileUtils;
 
   31 import org.openide.util.NbBundle;
 
   32 import org.openide.util.io.NbObjectInputStream;
 
   33 import org.openide.util.io.NbObjectOutputStream;
 
   39 import org.w3c.dom.Document;
 
   40 import org.w3c.dom.Element;
 
   41 import org.w3c.dom.NodeList;
 
   46 final class HashLookupSettings 
implements Serializable {
 
   48     private static final String SERIALIZATION_FILE_NAME = 
"hashLookup.settings"; 
 
   49     private static final String SERIALIZATION_FILE_PATH = PlatformUtil.getUserConfigDirectory() + File.separator + SERIALIZATION_FILE_NAME; 
 
   50     private static final String SET_ELEMENT = 
"hash_set"; 
 
   51     private static final String SET_NAME_ATTRIBUTE = 
"name"; 
 
   52     private static final String SET_TYPE_ATTRIBUTE = 
"type"; 
 
   53     private static final String SEARCH_DURING_INGEST_ATTRIBUTE = 
"use_for_ingest"; 
 
   54     private static final String SEND_INGEST_MESSAGES_ATTRIBUTE = 
"show_inbox_messages"; 
 
   55     private static final String PATH_ELEMENT = 
"hash_set_path"; 
 
   56     private static final String LEGACY_PATH_NUMBER_ATTRIBUTE = 
"number"; 
 
   57     private static final String CONFIG_FILE_NAME = 
"hashsets.xml"; 
 
   58     private static final String configFilePath = PlatformUtil.getUserConfigDirectory() + File.separator + CONFIG_FILE_NAME;
 
   59     private static final Logger logger = Logger.getLogger(HashDbManager.class.getName());
 
   61     private static final long serialVersionUID = 1L;
 
   62     private final List<HashDbInfo> hashDbInfoList;
 
   69     HashLookupSettings(List<HashDbInfo> hashDbInfoList) {
 
   70         this.hashDbInfoList = hashDbInfoList;
 
   79     HashLookupSettings(List<HashDbManager.HashDb> knownHashSets, List<HashDbManager.HashDb> knownBadHashSets) 
throws HashLookupSettingsException {
 
   80         hashDbInfoList = 
new ArrayList<>();
 
   81         this.addHashesToList(knownHashSets);
 
   82         this.addHashesToList(knownBadHashSets);
 
   92     private void addHashesToList(List<HashDbManager.HashDb> hashSetList) 
throws HashLookupSettingsException {
 
   93         for (HashDbManager.HashDb hashDb : hashSetList) {
 
   96                 if (hashDb.hasIndexOnly()) {
 
   97                     dbPath = hashDb.getIndexPath();
 
   99                     dbPath = hashDb.getDatabasePath();
 
  101                 hashDbInfoList.add(
new HashDbInfo(hashDb.getHashSetName(), hashDb.getKnownFilesType(), hashDb.getSearchDuringIngest(), hashDb.getSendIngestMessages(), dbPath));
 
  102             } 
catch (TskCoreException ex) {
 
  103                 throw new HashLookupSettingsException(
"Couldn't add hash database named: " + hashDb.getHashSetName(), ex);
 
  113     List<HashDbInfo> getHashDbInfo() {
 
  114         return hashDbInfoList;
 
  125     static HashLookupSettings readSettings() throws HashLookupSettingsException {
 
  126         File fileSetFile = 
new File(SERIALIZATION_FILE_PATH);
 
  127         if (fileSetFile.exists()) {
 
  128             return readSerializedSettings();
 
  130         return readXmlSettings();
 
  143     private static HashLookupSettings readSerializedSettings() throws HashLookupSettingsException {
 
  145             try (NbObjectInputStream in = 
new NbObjectInputStream(
new FileInputStream(SERIALIZATION_FILE_PATH))) {
 
  146                 HashLookupSettings filesSetsSettings = (HashLookupSettings) in.readObject();
 
  147                 return filesSetsSettings;
 
  149         } 
catch (IOException | ClassNotFoundException ex) {
 
  150             throw new HashLookupSettingsException(
"Could not read hash database settings.", ex);
 
  163     private static HashLookupSettings readXmlSettings() throws HashLookupSettingsException {
 
  164         File xmlFile = 
new File(configFilePath);
 
  165         if (xmlFile.exists()) {
 
  166             boolean updatedSchema = 
false;
 
  169             final Document doc = XMLUtil.loadDoc(HashDbManager.class, configFilePath);
 
  171                 throw new HashLookupSettingsException(
"Could not open xml document.");
 
  175             Element root = doc.getDocumentElement();
 
  177                 throw new HashLookupSettingsException(
"Error loading hash sets: invalid file format.");
 
  181             NodeList setsNList = root.getElementsByTagName(SET_ELEMENT);
 
  182             int numSets = setsNList.getLength();
 
  185             String attributeErrorMessage = 
"Missing %s attribute"; 
 
  186             String elementErrorMessage = 
"Empty %s element"; 
 
  187             List<String> hashSetNames = 
new ArrayList<>();
 
  188             List<HashDbInfo> hashDbInfoList = 
new ArrayList<>();
 
  189             for (
int i = 0; i < numSets; ++i) {
 
  190                 Element setEl = (Element) setsNList.item(i);
 
  192                 String hashSetName = setEl.getAttribute(SET_NAME_ATTRIBUTE);
 
  193                 if (hashSetName.isEmpty()) {
 
  194                     throw new HashLookupSettingsException(String.format(attributeErrorMessage, SET_NAME_ATTRIBUTE));
 
  198                 if (hashSetNames.contains(hashSetName)) {
 
  200                     String newHashSetName;
 
  203                         newHashSetName = hashSetName + suffix;
 
  204                     } 
while (hashSetNames.contains(newHashSetName));
 
  205                     logger.log(Level.INFO, 
"Duplicate hash set name " + hashSetName + 
" found. Replacing with " + newHashSetName + 
".");
 
  206                     if (RuntimeProperties.runningWithGUI()) {
 
  207                         JOptionPane.showMessageDialog(null,
 
  208                                 NbBundle.getMessage(HashLookupSettings.class,
 
  209                                         "HashDbManager.replacingDuplicateHashsetNameMsg",
 
  210                                         hashSetName, newHashSetName),
 
  211                                 NbBundle.getMessage(HashLookupSettings.class, 
"HashDbManager.openHashDbErr"),
 
  212                                 JOptionPane.ERROR_MESSAGE);
 
  213                         hashSetName = newHashSetName;
 
  217                 String knownFilesType = setEl.getAttribute(SET_TYPE_ATTRIBUTE);
 
  218                 if (knownFilesType.isEmpty()) {
 
  219                     throw new HashLookupSettingsException(String.format(attributeErrorMessage, SET_TYPE_ATTRIBUTE));
 
  223                 if (knownFilesType.equals(
"NSRL")) { 
 
  224                     knownFilesType = HashDbManager.HashDb.KnownFilesType.KNOWN.toString();
 
  225                     updatedSchema = 
true;
 
  228                 final String searchDuringIngest = setEl.getAttribute(SEARCH_DURING_INGEST_ATTRIBUTE);
 
  229                 if (searchDuringIngest.isEmpty()) {
 
  230                     throw new HashLookupSettingsException(String.format(attributeErrorMessage, SEND_INGEST_MESSAGES_ATTRIBUTE));
 
  232                 Boolean searchDuringIngestFlag = Boolean.parseBoolean(searchDuringIngest);
 
  234                 final String sendIngestMessages = setEl.getAttribute(SEND_INGEST_MESSAGES_ATTRIBUTE);
 
  235                 if (searchDuringIngest.isEmpty()) {
 
  236                     throw new HashLookupSettingsException(String.format(attributeErrorMessage, SEND_INGEST_MESSAGES_ATTRIBUTE));
 
  238                 Boolean sendIngestMessagesFlag = Boolean.parseBoolean(sendIngestMessages);
 
  241                 NodeList pathsNList = setEl.getElementsByTagName(PATH_ELEMENT);
 
  242                 if (pathsNList.getLength() > 0) {
 
  243                     Element pathEl = (Element) pathsNList.item(0); 
 
  246                     String legacyPathNumber = pathEl.getAttribute(LEGACY_PATH_NUMBER_ATTRIBUTE);
 
  247                     if (null != legacyPathNumber && !legacyPathNumber.isEmpty()) {
 
  248                         updatedSchema = 
true;
 
  251                     dbPath = pathEl.getTextContent();
 
  252                     if (dbPath.isEmpty()) {
 
  253                         throw new HashLookupSettingsException(String.format(elementErrorMessage, PATH_ELEMENT));
 
  256                     throw new HashLookupSettingsException(String.format(elementErrorMessage, PATH_ELEMENT));
 
  258                 hashDbInfoList.add(
new HashDbInfo(hashSetName, HashDbManager.HashDb.KnownFilesType.valueOf(knownFilesType),
 
  259                         searchDuringIngestFlag, sendIngestMessagesFlag, dbPath));
 
  260                 hashSetNames.add(hashSetName);
 
  264                 String backupFilePath = configFilePath + 
".v1_backup"; 
 
  265                 String messageBoxTitle = NbBundle.getMessage(HashLookupSettings.class,
 
  266                         "HashDbManager.msgBoxTitle.confFileFmtChanged");
 
  267                 String baseMessage = NbBundle.getMessage(HashLookupSettings.class,
 
  268                         "HashDbManager.baseMessage.updatedFormatHashDbConfig");
 
  270                     FileUtils.copyFile(
new File(configFilePath), 
new File(backupFilePath));
 
  271                     logger.log(Level.INFO, 
"Updated the schema, backup saved at: " + backupFilePath);
 
  272                     if (RuntimeProperties.runningWithGUI()) {
 
  273                         JOptionPane.showMessageDialog(null,
 
  274                                 NbBundle.getMessage(HashLookupSettings.class,
 
  275                                         "HashDbManager.savedBackupOfOldConfigMsg",
 
  276                                         baseMessage, backupFilePath),
 
  278                                 JOptionPane.INFORMATION_MESSAGE);
 
  280                 } 
catch (IOException ex) {
 
  281                     logger.log(Level.WARNING, 
"Failed to save backup of old format configuration file to " + backupFilePath, ex); 
 
  282                     JOptionPane.showMessageDialog(null, baseMessage, messageBoxTitle, JOptionPane.INFORMATION_MESSAGE);
 
  284                 HashLookupSettings settings;
 
  285                 settings = 
new HashLookupSettings(hashDbInfoList);
 
  286                 HashLookupSettings.writeSettings(settings);
 
  288             return new HashLookupSettings(hashDbInfoList);
 
  290             return new HashLookupSettings(
new ArrayList<>());
 
  301     static boolean writeSettings(HashLookupSettings settings) {
 
  302         try (NbObjectOutputStream out = 
new NbObjectOutputStream(
new FileOutputStream(SERIALIZATION_FILE_PATH))) {
 
  303             out.writeObject(settings);
 
  305         } 
catch (Exception ex) {
 
  306             logger.log(Level.SEVERE, 
"Could not wtite hash database settings.");
 
  316     static final class HashDbInfo 
implements Serializable {
 
  318         private static final long serialVersionUID = 1L;
 
  319         private final String hashSetName;
 
  320         private final HashDbManager.HashDb.KnownFilesType knownFilesType;
 
  321         private final boolean searchDuringIngest;
 
  322         private final boolean sendIngestMessages;
 
  323         private final String path;
 
  335         HashDbInfo(String hashSetName, HashDbManager.HashDb.KnownFilesType knownFilesType, 
boolean searchDuringIngest, 
boolean sendIngestMessages, String path) {
 
  336             this.hashSetName = hashSetName;
 
  337             this.knownFilesType = knownFilesType;
 
  338             this.searchDuringIngest = searchDuringIngest;
 
  339             this.sendIngestMessages = sendIngestMessages;
 
  348         String getHashSetName() {
 
  357         HashDbManager.HashDb.KnownFilesType getKnownFilesType() {
 
  358             return knownFilesType;
 
  366         boolean getSearchDuringIngest() {
 
  367             return searchDuringIngest;
 
  375         boolean getSendIngestMessages() {
 
  376             return sendIngestMessages;
 
  394     static class HashLookupSettingsException 
extends Exception {
 
  396         private static final long serialVersionUID = 1L;
 
  398         HashLookupSettingsException(String message) {
 
  402         HashLookupSettingsException(String message, Throwable throwable) {
 
  403             super(message, throwable);