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.Objects;
 
   29 import java.util.logging.Level;
 
   30 import javax.swing.JOptionPane;
 
   31 import org.apache.commons.io.FileUtils;
 
   32 import org.openide.util.NbBundle;
 
   33 import org.openide.util.io.NbObjectInputStream;
 
   34 import org.openide.util.io.NbObjectOutputStream;
 
   35 import org.openide.windows.WindowManager;
 
   43 import org.w3c.dom.Document;
 
   44 import org.w3c.dom.Element;
 
   45 import org.w3c.dom.NodeList;
 
   51 final class HashLookupSettings 
implements Serializable {
 
   53     private static final String SERIALIZATION_FILE_NAME = 
"hashLookup.settings"; 
 
   54     private static final String SERIALIZATION_FILE_PATH = PlatformUtil.getUserConfigDirectory() + File.separator + SERIALIZATION_FILE_NAME; 
 
   55     private static final String SET_ELEMENT = 
"hash_set"; 
 
   56     private static final String SET_NAME_ATTRIBUTE = 
"name"; 
 
   57     private static final String SET_TYPE_ATTRIBUTE = 
"type"; 
 
   58     private static final String SEARCH_DURING_INGEST_ATTRIBUTE = 
"use_for_ingest"; 
 
   59     private static final String SEND_INGEST_MESSAGES_ATTRIBUTE = 
"show_inbox_messages"; 
 
   60     private static final String PATH_ELEMENT = 
"hash_set_path"; 
 
   61     private static final String LEGACY_PATH_NUMBER_ATTRIBUTE = 
"number"; 
 
   62     private static final String CONFIG_FILE_NAME = 
"hashsets.xml"; 
 
   63     private static final String configFilePath = PlatformUtil.getUserConfigDirectory() + File.separator + CONFIG_FILE_NAME;
 
   64     private static final Logger logger = Logger.getLogger(HashDbManager.class.getName());
 
   66     private static final String USER_DIR_PLACEHOLDER = 
"[UserConfigFolder]";
 
   67     private static final String CURRENT_USER_DIR = PlatformUtil.getUserConfigDirectory();
 
   69     private static final long serialVersionUID = 1L;
 
   70     private final List<HashDbInfo> hashDbInfoList;
 
   77     HashLookupSettings(List<HashDbInfo> hashDbInfoList) {
 
   78         this.hashDbInfoList = hashDbInfoList;
 
   81     static List<HashDbInfo> convertHashSetList(List<HashDbManager.HashDb> hashSets) 
throws HashLookupSettingsException{
 
   82         List<HashDbInfo> dbInfoList = 
new ArrayList<>();
 
   83         for(HashDbManager.HashDb db:hashSets){
 
   85                 dbInfoList.add(
new HashDbInfo(db));
 
   86             } 
catch (TskCoreException ex){
 
   87                 logger.log(Level.SEVERE, 
"Could not load hash set settings for {0}", db.getHashSetName());
 
   98     List<HashDbInfo> getHashDbInfo() {
 
   99         return hashDbInfoList;
 
  110     static HashLookupSettings readSettings() throws HashLookupSettingsException {
 
  111         File fileSetFile = 
new File(SERIALIZATION_FILE_PATH);
 
  112         if (fileSetFile.exists()) {
 
  113             return readSerializedSettings();
 
  115         return readXmlSettings();
 
  128     private static HashLookupSettings readSerializedSettings() throws HashLookupSettingsException {        
 
  130             try (NbObjectInputStream in = 
new NbObjectInputStream(
new FileInputStream(SERIALIZATION_FILE_PATH))) {
 
  131                 HashLookupSettings filesSetsSettings = (HashLookupSettings) in.readObject();
 
  138                 convertPlaceholderToPath(filesSetsSettings);
 
  139                 return filesSetsSettings;
 
  141         } 
catch (IOException | ClassNotFoundException ex) {
 
  142             throw new HashLookupSettingsException(
"Could not read hash set settings.", ex);
 
  155     private static HashLookupSettings readXmlSettings() throws HashLookupSettingsException {
 
  156         File xmlFile = 
new File(configFilePath);
 
  157         if (xmlFile.exists()) {
 
  158             boolean updatedSchema = 
false;
 
  161             final Document doc = XMLUtil.loadDoc(HashDbManager.class, configFilePath);
 
  163                 throw new HashLookupSettingsException(
"Could not open xml document.");
 
  167             Element root = doc.getDocumentElement();
 
  169                 throw new HashLookupSettingsException(
"Error loading hash sets: invalid file format.");
 
  173             NodeList setsNList = root.getElementsByTagName(SET_ELEMENT);
 
  174             int numSets = setsNList.getLength();
 
  177             String attributeErrorMessage = 
"Missing %s attribute"; 
 
  178             String elementErrorMessage = 
"Empty %s element"; 
 
  179             List<String> hashSetNames = 
new ArrayList<>();
 
  180             List<HashDbInfo> hashDbInfoList = 
new ArrayList<>();
 
  181             for (
int i = 0; i < numSets; ++i) {
 
  182                 Element setEl = (Element) setsNList.item(i);
 
  184                 String hashSetName = setEl.getAttribute(SET_NAME_ATTRIBUTE);
 
  185                 if (hashSetName.isEmpty()) {
 
  186                     throw new HashLookupSettingsException(String.format(attributeErrorMessage, SET_NAME_ATTRIBUTE));
 
  190                 if (hashSetNames.contains(hashSetName)) {
 
  192                     String newHashSetName;
 
  195                         newHashSetName = hashSetName + suffix;
 
  196                     } 
while (hashSetNames.contains(newHashSetName));
 
  197                     logger.log(Level.INFO, 
"Duplicate hash set name " + hashSetName + 
" found. Replacing with " + newHashSetName + 
".");
 
  198                     if (RuntimeProperties.runningWithGUI()) {
 
  199                         JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
 
  200                                 NbBundle.getMessage(HashLookupSettings.class,
 
  201                                         "HashDbManager.replacingDuplicateHashsetNameMsg",
 
  202                                         hashSetName, newHashSetName),
 
  203                                 NbBundle.getMessage(HashLookupSettings.class, 
"HashDbManager.openHashDbErr"),
 
  204                                 JOptionPane.ERROR_MESSAGE);
 
  205                         hashSetName = newHashSetName;
 
  209                 String knownFilesType = setEl.getAttribute(SET_TYPE_ATTRIBUTE);
 
  210                 if (knownFilesType.isEmpty()) {
 
  211                     throw new HashLookupSettingsException(String.format(attributeErrorMessage, SET_TYPE_ATTRIBUTE));
 
  215                 if (knownFilesType.equals(
"NSRL")) { 
 
  216                     knownFilesType = HashDbManager.HashDb.KnownFilesType.KNOWN.toString();
 
  217                     updatedSchema = 
true;
 
  220                 final String searchDuringIngest = setEl.getAttribute(SEARCH_DURING_INGEST_ATTRIBUTE);
 
  221                 if (searchDuringIngest.isEmpty()) {
 
  222                     throw new HashLookupSettingsException(String.format(attributeErrorMessage, SEND_INGEST_MESSAGES_ATTRIBUTE));
 
  224                 Boolean searchDuringIngestFlag = Boolean.parseBoolean(searchDuringIngest);
 
  226                 final String sendIngestMessages = setEl.getAttribute(SEND_INGEST_MESSAGES_ATTRIBUTE);
 
  227                 if (searchDuringIngest.isEmpty()) {
 
  228                     throw new HashLookupSettingsException(String.format(attributeErrorMessage, SEND_INGEST_MESSAGES_ATTRIBUTE));
 
  230                 Boolean sendIngestMessagesFlag = Boolean.parseBoolean(sendIngestMessages);
 
  233                 NodeList pathsNList = setEl.getElementsByTagName(PATH_ELEMENT);
 
  234                 if (pathsNList.getLength() > 0) {
 
  235                     Element pathEl = (Element) pathsNList.item(0); 
 
  238                     String legacyPathNumber = pathEl.getAttribute(LEGACY_PATH_NUMBER_ATTRIBUTE);
 
  239                     if (null != legacyPathNumber && !legacyPathNumber.isEmpty()) {
 
  240                         updatedSchema = 
true;
 
  243                     dbPath = pathEl.getTextContent();
 
  244                     if (dbPath.isEmpty()) {
 
  245                         throw new HashLookupSettingsException(String.format(elementErrorMessage, PATH_ELEMENT));
 
  248                     throw new HashLookupSettingsException(String.format(elementErrorMessage, PATH_ELEMENT));
 
  250                 hashDbInfoList.add(
new HashDbInfo(hashSetName, HashDbManager.HashDb.KnownFilesType.valueOf(knownFilesType),
 
  251                         searchDuringIngestFlag, sendIngestMessagesFlag, dbPath));
 
  252                 hashSetNames.add(hashSetName);
 
  256                 String backupFilePath = configFilePath + 
".v1_backup"; 
 
  257                 String messageBoxTitle = NbBundle.getMessage(HashLookupSettings.class,
 
  258                         "HashDbManager.msgBoxTitle.confFileFmtChanged");
 
  259                 String baseMessage = NbBundle.getMessage(HashLookupSettings.class,
 
  260                         "HashDbManager.baseMessage.updatedFormatHashDbConfig");
 
  262                     FileUtils.copyFile(
new File(configFilePath), 
new File(backupFilePath));
 
  263                     logger.log(Level.INFO, 
"Updated the schema, backup saved at: " + backupFilePath);
 
  264                     if (RuntimeProperties.runningWithGUI()) {
 
  265                         JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
 
  266                                 NbBundle.getMessage(HashLookupSettings.class,
 
  267                                         "HashDbManager.savedBackupOfOldConfigMsg",
 
  268                                         baseMessage, backupFilePath),
 
  270                                 JOptionPane.INFORMATION_MESSAGE);
 
  272                 } 
catch (IOException ex) {
 
  273                     logger.log(Level.WARNING, 
"Failed to save backup of old format configuration file to " + backupFilePath, ex); 
 
  274                     JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), baseMessage, messageBoxTitle, JOptionPane.INFORMATION_MESSAGE);
 
  276                 HashLookupSettings settings;
 
  277                 settings = 
new HashLookupSettings(hashDbInfoList);
 
  278                 HashLookupSettings.writeSettings(settings);
 
  280             return new HashLookupSettings(hashDbInfoList);
 
  282             return new HashLookupSettings(
new ArrayList<>());
 
  293     static boolean writeSettings(HashLookupSettings settings) {
 
  300         convertPathToPlaceholder(settings);
 
  301         try (NbObjectOutputStream out = 
new NbObjectOutputStream(
new FileOutputStream(SERIALIZATION_FILE_PATH))) {
 
  302             out.writeObject(settings);
 
  304             convertPlaceholderToPath(settings);
 
  306         } 
catch (Exception ex) {
 
  307             logger.log(Level.SEVERE, 
"Could not write hash set settings.");
 
  319     static void convertPathToPlaceholder(HashLookupSettings settings) {
 
  320         for (HashDbInfo hashDbInfo : settings.getHashDbInfo()) {
 
  321             if (hashDbInfo.isFileDatabaseType()) {
 
  322                 String dbPath = hashDbInfo.getPath();
 
  323                 if (dbPath.startsWith(CURRENT_USER_DIR)) {
 
  325                     String remainingPath = dbPath.substring(CURRENT_USER_DIR.length());
 
  326                     hashDbInfo.setPath(USER_DIR_PLACEHOLDER + remainingPath);
 
  339     static void convertPlaceholderToPath(HashLookupSettings settings) {
 
  340         for (HashDbInfo hashDbInfo : settings.getHashDbInfo()) {
 
  341             if (hashDbInfo.isFileDatabaseType()) {
 
  342                 String dbPath = hashDbInfo.getPath();
 
  343                 if (dbPath.startsWith(USER_DIR_PLACEHOLDER)) {
 
  345                     String remainingPath = dbPath.substring(USER_DIR_PLACEHOLDER.length());
 
  346                     hashDbInfo.setPath(CURRENT_USER_DIR + remainingPath);
 
  358     static final class HashDbInfo 
implements Serializable {
 
  365         private static final long serialVersionUID = 1L;
 
  366         private final String hashSetName;
 
  367         private final HashDbManager.HashDb.KnownFilesType knownFilesType;
 
  368         private boolean searchDuringIngest;
 
  369         private final boolean sendIngestMessages;
 
  371         private final String version;
 
  372         private final boolean readOnly;
 
  373         private final int referenceSetID;
 
  374         private DatabaseType dbType;
 
  386         HashDbInfo(String hashSetName, HashDbManager.HashDb.KnownFilesType knownFilesType, 
boolean searchDuringIngest, 
boolean sendIngestMessages, String path) {
 
  387             this.hashSetName = hashSetName;
 
  388             this.knownFilesType = knownFilesType;
 
  389             this.searchDuringIngest = searchDuringIngest;
 
  390             this.sendIngestMessages = sendIngestMessages;
 
  392             this.referenceSetID = -1;
 
  394             this.readOnly = 
false;
 
  395             this.dbType = DatabaseType.FILE;
 
  398         HashDbInfo(String hashSetName, String version, 
int referenceSetID, HashDbManager.HashDb.KnownFilesType knownFilesType, 
boolean readOnly, 
boolean searchDuringIngest, 
boolean sendIngestMessages){
 
  399             this.hashSetName = hashSetName;
 
  400             this.version = version;
 
  401             this.referenceSetID = referenceSetID;
 
  402             this.knownFilesType = knownFilesType;
 
  403             this.readOnly = readOnly;
 
  404             this.searchDuringIngest = searchDuringIngest;
 
  405             this.sendIngestMessages = sendIngestMessages;
 
  407             dbType = DatabaseType.CENTRAL_REPOSITORY;     
 
  410         HashDbInfo(HashDbManager.HashDb db) throws TskCoreException{
 
  411             if(db instanceof HashDbManager.SleuthkitHashSet){
 
  412                 HashDbManager.SleuthkitHashSet fileTypeDb = (HashDbManager.SleuthkitHashSet)db;
 
  413                 this.hashSetName = fileTypeDb.getHashSetName();
 
  414                 this.knownFilesType = fileTypeDb.getKnownFilesType();
 
  415                 this.searchDuringIngest = fileTypeDb.getSearchDuringIngest();
 
  416                 this.sendIngestMessages = fileTypeDb.getSendIngestMessages();
 
  417                 this.referenceSetID = -1;
 
  419                 this.readOnly = 
false;
 
  420                 this.dbType = DatabaseType.FILE;
 
  421                 if (fileTypeDb.hasIndexOnly()) {
 
  422                     this.path = fileTypeDb.getIndexPath();
 
  424                     this.path = fileTypeDb.getDatabasePath();
 
  427                 HashDbManager.CentralRepoHashSet centralRepoDb = (HashDbManager.CentralRepoHashSet)db;
 
  428                 this.hashSetName = centralRepoDb.getHashSetName();
 
  429                 this.version = centralRepoDb.getVersion();
 
  430                 this.knownFilesType = centralRepoDb.getKnownFilesType();
 
  431                 this.readOnly = ! centralRepoDb.isUpdateable();
 
  432                 this.searchDuringIngest = centralRepoDb.getSearchDuringIngest();
 
  433                 this.sendIngestMessages = centralRepoDb.getSendIngestMessages();
 
  435                 this.referenceSetID = centralRepoDb.getReferenceSetID();
 
  436                 this.dbType = DatabaseType.CENTRAL_REPOSITORY;
 
  445         String getHashSetName() {
 
  461         boolean isReadOnly(){
 
  470         HashDbManager.HashDb.KnownFilesType getKnownFilesType() {
 
  471             return knownFilesType;
 
  479         boolean getSearchDuringIngest() {
 
  480             return searchDuringIngest;
 
  487         void setSearchDuringIngest(
boolean searchDuringIngest) {
 
  488             this.searchDuringIngest = searchDuringIngest;
 
  496         boolean getSendIngestMessages() {
 
  497             return sendIngestMessages;
 
  513         public void setPath(String path) {
 
  517         int getReferenceSetID(){
 
  518             return referenceSetID;
 
  525         boolean isFileDatabaseType(){
 
  526             return dbType == DatabaseType.FILE;
 
  529         boolean isCentralRepoDatabaseType(){
 
  530             return dbType == DatabaseType.CENTRAL_REPOSITORY;
 
  533         boolean matches(HashDb hashDb){
 
  538             if( ! this.knownFilesType.equals(hashDb.getKnownFilesType())){
 
  542             if((this.dbType == DatabaseType.CENTRAL_REPOSITORY) && (! (hashDb instanceof CentralRepoHashSet))
 
  543                     || (this.dbType == DatabaseType.FILE) && (! (hashDb instanceof SleuthkitHashSet))){
 
  547             if( ! this.hashSetName.equals(hashDb.getHashSetName())){
 
  551             if(hashDb instanceof CentralRepoHashSet){
 
  552                 CentralRepoHashSet crDb = (CentralRepoHashSet) hashDb;
 
  553                 if(this.referenceSetID != crDb.getReferenceSetID()){
 
  557                 if(! version.equals(crDb.getVersion())){
 
  566         public boolean equals(Object obj) {
 
  571             if (getClass() != obj.getClass()) {
 
  575             final HashDbInfo other = (HashDbInfo) obj;
 
  577             if(! this.dbType.equals(other.dbType)){
 
  581             if(this.dbType.equals(DatabaseType.FILE)){
 
  583                 return (this.hashSetName.equals(other.hashSetName)
 
  584                         && this.knownFilesType.equals(other.knownFilesType));
 
  587                 return (this.hashSetName.equals(other.hashSetName)
 
  588                         && (this.referenceSetID == other.referenceSetID)
 
  589                         && this.knownFilesType.equals(other.knownFilesType));
 
  594         public int hashCode() {
 
  596             hash = 89 * hash + Objects.hashCode(this.hashSetName);
 
  597             hash = 89 * hash + Objects.hashCode(this.knownFilesType);
 
  598             hash = 89 * hash + Objects.hashCode(this.dbType);
 
  599             if(this.dbType.equals(DatabaseType.CENTRAL_REPOSITORY)){
 
  600                 hash = 89 * hash + this.referenceSetID;
 
  613         private void readObject(java.io.ObjectInputStream stream)
 
  614             throws IOException, ClassNotFoundException {
 
  615             stream.defaultReadObject();
 
  618                 dbType = DatabaseType.FILE;
 
  628     static class HashLookupSettingsException 
extends Exception {
 
  630         private static final long serialVersionUID = 1L;
 
  632         HashLookupSettingsException(String message) {
 
  636         HashLookupSettingsException(String message, Throwable throwable) {
 
  637             super(message, throwable);