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;
 
   42 import org.w3c.dom.Document;
 
   43 import org.w3c.dom.Element;
 
   44 import org.w3c.dom.NodeList;
 
   51 final class HashLookupSettings 
implements Serializable {
 
   52     private static final String SET_ELEMENT = 
"hash_set"; 
 
   53     private static final String SET_NAME_ATTRIBUTE = 
"name"; 
 
   54     private static final String SET_TYPE_ATTRIBUTE = 
"type"; 
 
   55     private static final String SEARCH_DURING_INGEST_ATTRIBUTE = 
"use_for_ingest"; 
 
   56     private static final String SEND_INGEST_MESSAGES_ATTRIBUTE = 
"show_inbox_messages"; 
 
   57     private static final String PATH_ELEMENT = 
"hash_set_path"; 
 
   58     private static final String LEGACY_PATH_NUMBER_ATTRIBUTE = 
"number"; 
 
   60     private static final Logger logger = Logger.getLogger(HashDbManager.class.getName());
 
   62     private static final String USER_DIR_PLACEHOLDER = 
"[UserConfigFolder]";
 
   64     private static final long serialVersionUID = 1L;
 
   65     private final List<HashDbInfo> hashDbInfoList;
 
   70     static String getSettingsPath() {
 
   77     static String getDefaultDbPath() {
 
   78         return HashConfigPaths.getInstance().getDefaultDbPath();
 
   85     static String getBaseHashsetConfigPath() {
 
   86         return HashConfigPaths.getInstance().getBasePath();
 
   94     HashLookupSettings(List<HashDbInfo> hashDbInfoList) {
 
   95         this.hashDbInfoList = hashDbInfoList;
 
   98     static List<HashDbInfo> convertHashSetList(List<HashDbManager.HashDb> hashSets) 
throws HashLookupSettingsException{
 
   99         List<HashDbInfo> dbInfoList = 
new ArrayList<>();
 
  100         for(HashDbManager.HashDb db:hashSets){
 
  102                 dbInfoList.add(
new HashDbInfo(db));
 
  103             } 
catch (TskCoreException ex){
 
  104                 logger.log(Level.SEVERE, 
"Could not load hash set settings for {0}", db.getHashSetName());
 
  115     List<HashDbInfo> getHashDbInfo() {
 
  116         return hashDbInfoList;
 
  127     static HashLookupSettings readSettings() throws HashLookupSettingsException {
 
  128         File fileSetFile = 
new File(HashConfigPaths.getInstance().getSettingsPath());
 
  129         if (fileSetFile.exists()) {
 
  130             return readSerializedSettings();
 
  132         return readXmlSettings();
 
  145     private static HashLookupSettings readSerializedSettings() throws HashLookupSettingsException {        
 
  147             try (NbObjectInputStream in = 
new NbObjectInputStream(
new FileInputStream(HashConfigPaths.getInstance().getSettingsPath()))) {
 
  148                 HashLookupSettings filesSetsSettings = (HashLookupSettings) in.readObject();
 
  155                 convertPlaceholderToPath(filesSetsSettings);
 
  156                 return filesSetsSettings;
 
  158         } 
catch (IOException | ClassNotFoundException ex) {
 
  159             throw new HashLookupSettingsException(
"Could not read hash set settings.", ex);
 
  172     private static HashLookupSettings readXmlSettings() throws HashLookupSettingsException {
 
  173         File xmlFile = 
new File(HashConfigPaths.getInstance().getXmlSettingsPath());
 
  174         if (xmlFile.exists()) {
 
  175             boolean updatedSchema = 
false;
 
  178             final Document doc = XMLUtil.loadDoc(HashDbManager.class, HashConfigPaths.getInstance().getXmlSettingsPath());
 
  180                 throw new HashLookupSettingsException(
"Could not open xml document.");
 
  184             Element root = doc.getDocumentElement();
 
  186                 throw new HashLookupSettingsException(
"Error loading hash sets: invalid file format.");
 
  190             NodeList setsNList = root.getElementsByTagName(SET_ELEMENT);
 
  191             int numSets = setsNList.getLength();
 
  194             String attributeErrorMessage = 
"Missing %s attribute"; 
 
  195             String elementErrorMessage = 
"Empty %s element"; 
 
  196             List<String> hashSetNames = 
new ArrayList<>();
 
  197             List<HashDbInfo> hashDbInfoList = 
new ArrayList<>();
 
  198             for (
int i = 0; i < numSets; ++i) {
 
  199                 Element setEl = (Element) setsNList.item(i);
 
  201                 String hashSetName = setEl.getAttribute(SET_NAME_ATTRIBUTE);
 
  202                 if (hashSetName.isEmpty()) {
 
  203                     throw new HashLookupSettingsException(String.format(attributeErrorMessage, SET_NAME_ATTRIBUTE));
 
  207                 if (hashSetNames.contains(hashSetName)) {
 
  209                     String newHashSetName;
 
  212                         newHashSetName = hashSetName + suffix;
 
  213                     } 
while (hashSetNames.contains(newHashSetName));
 
  214                     logger.log(Level.INFO, 
"Duplicate hash set name " + hashSetName + 
" found. Replacing with " + newHashSetName + 
".");
 
  215                     if (RuntimeProperties.runningWithGUI()) {
 
  216                         JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
 
  217                                 NbBundle.getMessage(HashLookupSettings.class,
 
  218                                         "HashDbManager.replacingDuplicateHashsetNameMsg",
 
  219                                         hashSetName, newHashSetName),
 
  220                                 NbBundle.getMessage(HashLookupSettings.class, 
"HashDbManager.openHashDbErr"),
 
  221                                 JOptionPane.ERROR_MESSAGE);
 
  222                         hashSetName = newHashSetName;
 
  226                 String knownFilesType = setEl.getAttribute(SET_TYPE_ATTRIBUTE);
 
  227                 if (knownFilesType.isEmpty()) {
 
  228                     throw new HashLookupSettingsException(String.format(attributeErrorMessage, SET_TYPE_ATTRIBUTE));
 
  232                 if (knownFilesType.equals(
"NSRL")) { 
 
  233                     knownFilesType = HashDbManager.HashDb.KnownFilesType.KNOWN.toString();
 
  234                     updatedSchema = 
true;
 
  237                 final String searchDuringIngest = setEl.getAttribute(SEARCH_DURING_INGEST_ATTRIBUTE);
 
  238                 if (searchDuringIngest.isEmpty()) {
 
  239                     throw new HashLookupSettingsException(String.format(attributeErrorMessage, SEND_INGEST_MESSAGES_ATTRIBUTE));
 
  241                 Boolean searchDuringIngestFlag = Boolean.parseBoolean(searchDuringIngest);
 
  243                 final String sendIngestMessages = setEl.getAttribute(SEND_INGEST_MESSAGES_ATTRIBUTE);
 
  244                 if (searchDuringIngest.isEmpty()) {
 
  245                     throw new HashLookupSettingsException(String.format(attributeErrorMessage, SEND_INGEST_MESSAGES_ATTRIBUTE));
 
  247                 Boolean sendIngestMessagesFlag = Boolean.parseBoolean(sendIngestMessages);
 
  250                 NodeList pathsNList = setEl.getElementsByTagName(PATH_ELEMENT);
 
  251                 if (pathsNList.getLength() > 0) {
 
  252                     Element pathEl = (Element) pathsNList.item(0); 
 
  255                     String legacyPathNumber = pathEl.getAttribute(LEGACY_PATH_NUMBER_ATTRIBUTE);
 
  256                     if (null != legacyPathNumber && !legacyPathNumber.isEmpty()) {
 
  257                         updatedSchema = 
true;
 
  260                     dbPath = pathEl.getTextContent();
 
  261                     if (dbPath.isEmpty()) {
 
  262                         throw new HashLookupSettingsException(String.format(elementErrorMessage, PATH_ELEMENT));
 
  265                     throw new HashLookupSettingsException(String.format(elementErrorMessage, PATH_ELEMENT));
 
  267                 hashDbInfoList.add(
new HashDbInfo(hashSetName, HashDbManager.HashDb.KnownFilesType.valueOf(knownFilesType),
 
  268                         searchDuringIngestFlag, sendIngestMessagesFlag, dbPath));
 
  269                 hashSetNames.add(hashSetName);
 
  273                 String backupFilePath = HashConfigPaths.getInstance().getXmlSettingsPath() + 
".v1_backup"; 
 
  274                 String messageBoxTitle = NbBundle.getMessage(HashLookupSettings.class,
 
  275                         "HashDbManager.msgBoxTitle.confFileFmtChanged");
 
  276                 String baseMessage = NbBundle.getMessage(HashLookupSettings.class,
 
  277                         "HashDbManager.baseMessage.updatedFormatHashDbConfig");
 
  279                     FileUtils.copyFile(
new File(HashConfigPaths.getInstance().getXmlSettingsPath()), 
new File(backupFilePath));
 
  280                     logger.log(Level.INFO, 
"Updated the schema, backup saved at: " + backupFilePath);
 
  281                     if (RuntimeProperties.runningWithGUI()) {
 
  282                         JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
 
  283                                 NbBundle.getMessage(HashLookupSettings.class,
 
  284                                         "HashDbManager.savedBackupOfOldConfigMsg",
 
  285                                         baseMessage, backupFilePath),
 
  287                                 JOptionPane.INFORMATION_MESSAGE);
 
  289                 } 
catch (IOException ex) {
 
  290                     logger.log(Level.WARNING, 
"Failed to save backup of old format configuration file to " + backupFilePath, ex); 
 
  291                     JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), baseMessage, messageBoxTitle, JOptionPane.INFORMATION_MESSAGE);
 
  293                 HashLookupSettings settings;
 
  294                 settings = 
new HashLookupSettings(hashDbInfoList);
 
  295                 HashLookupSettings.writeSettings(settings);
 
  297             return new HashLookupSettings(hashDbInfoList);
 
  299             return new HashLookupSettings(
new ArrayList<>());
 
  310     static boolean writeSettings(HashLookupSettings settings) {
 
  317         convertPathToPlaceholder(settings);
 
  318         try (NbObjectOutputStream out = 
new NbObjectOutputStream(
new FileOutputStream(HashConfigPaths.getInstance().getSettingsPath()))) {
 
  319             out.writeObject(settings);
 
  321             convertPlaceholderToPath(settings);
 
  323         } 
catch (Exception ex) {
 
  324             logger.log(Level.SEVERE, 
"Could not write hash set settings.");
 
  336     static void convertPathToPlaceholder(HashLookupSettings settings) {
 
  337         for (HashDbInfo hashDbInfo : settings.getHashDbInfo()) {
 
  338             if (hashDbInfo.isFileDatabaseType()) {
 
  339                 String dbPath = hashDbInfo.getPath();
 
  340                 if (dbPath.startsWith(HashConfigPaths.getInstance().getBasePath())) {
 
  342                     String remainingPath = dbPath.substring(HashConfigPaths.getInstance().getBasePath().length());
 
  343                     hashDbInfo.setPath(USER_DIR_PLACEHOLDER + remainingPath);
 
  356     static void convertPlaceholderToPath(HashLookupSettings settings) {
 
  357         for (HashDbInfo hashDbInfo : settings.getHashDbInfo()) {
 
  358             if (hashDbInfo.isFileDatabaseType()) {
 
  359                 String dbPath = hashDbInfo.getPath();
 
  360                 if (dbPath.startsWith(USER_DIR_PLACEHOLDER)) {
 
  362                     String remainingPath = dbPath.substring(USER_DIR_PLACEHOLDER.length());
 
  363                     hashDbInfo.setPath(HashConfigPaths.getInstance().getBasePath() + remainingPath);
 
  375     static final class HashDbInfo 
implements Serializable {
 
  382         private static final long serialVersionUID = 1L;
 
  383         private final String hashSetName;
 
  384         private final HashDbManager.HashDb.KnownFilesType knownFilesType;
 
  385         private boolean searchDuringIngest;
 
  386         private final boolean sendIngestMessages;
 
  388         private final String version;
 
  389         private final boolean readOnly;
 
  390         private final int referenceSetID;
 
  391         private DatabaseType dbType;
 
  409         HashDbInfo(String hashSetName, HashDbManager.HashDb.KnownFilesType knownFilesType, 
boolean searchDuringIngest, 
boolean sendIngestMessages, 
 
  410                 String path, 
int referenceSetID, String version, 
boolean readOnly, 
boolean isCRType) {
 
  411             this.hashSetName = hashSetName;
 
  412             this.knownFilesType = knownFilesType;
 
  413             this.searchDuringIngest = searchDuringIngest;
 
  414             this.sendIngestMessages = sendIngestMessages;
 
  416             this.referenceSetID = referenceSetID;
 
  417             this.version = version;
 
  418             this.readOnly = readOnly;
 
  419             this.dbType = isCRType ? DatabaseType.CENTRAL_REPOSITORY : DatabaseType.FILE;
 
  432         HashDbInfo(String hashSetName, HashDbManager.HashDb.KnownFilesType knownFilesType, 
boolean searchDuringIngest, 
boolean sendIngestMessages, String path) {
 
  433             this.hashSetName = hashSetName;
 
  434             this.knownFilesType = knownFilesType;
 
  435             this.searchDuringIngest = searchDuringIngest;
 
  436             this.sendIngestMessages = sendIngestMessages;
 
  438             this.referenceSetID = -1;
 
  440             this.readOnly = 
false;
 
  441             this.dbType = DatabaseType.FILE;
 
  444         HashDbInfo(String hashSetName, String version, 
int referenceSetID, HashDbManager.HashDb.KnownFilesType knownFilesType, 
boolean readOnly, 
boolean searchDuringIngest, 
boolean sendIngestMessages){
 
  445             this.hashSetName = hashSetName;
 
  446             this.version = version;
 
  447             this.referenceSetID = referenceSetID;
 
  448             this.knownFilesType = knownFilesType;
 
  449             this.readOnly = readOnly;
 
  450             this.searchDuringIngest = searchDuringIngest;
 
  451             this.sendIngestMessages = sendIngestMessages;
 
  453             dbType = DatabaseType.CENTRAL_REPOSITORY;     
 
  456         HashDbInfo(HashDbManager.HashDb db) throws TskCoreException{
 
  457             if(db instanceof HashDbManager.SleuthkitHashSet){
 
  458                 HashDbManager.SleuthkitHashSet fileTypeDb = (HashDbManager.SleuthkitHashSet)db;
 
  459                 this.hashSetName = fileTypeDb.getHashSetName();
 
  460                 this.knownFilesType = fileTypeDb.getKnownFilesType();
 
  461                 this.searchDuringIngest = fileTypeDb.getSearchDuringIngest();
 
  462                 this.sendIngestMessages = fileTypeDb.getSendIngestMessages();
 
  463                 this.referenceSetID = -1;
 
  465                 this.readOnly = 
false;
 
  466                 this.dbType = DatabaseType.FILE;
 
  467                 if (fileTypeDb.hasIndexOnly()) {
 
  468                     this.path = fileTypeDb.getIndexPath();
 
  470                     this.path = fileTypeDb.getDatabasePath();
 
  473                 HashDbManager.CentralRepoHashSet centralRepoDb = (HashDbManager.CentralRepoHashSet)db;
 
  474                 this.hashSetName = centralRepoDb.getHashSetName();
 
  475                 this.version = centralRepoDb.getVersion();
 
  476                 this.knownFilesType = centralRepoDb.getKnownFilesType();
 
  477                 this.readOnly = ! centralRepoDb.isUpdateable();
 
  478                 this.searchDuringIngest = centralRepoDb.getSearchDuringIngest();
 
  479                 this.sendIngestMessages = centralRepoDb.getSendIngestMessages();
 
  481                 this.referenceSetID = centralRepoDb.getReferenceSetID();
 
  482                 this.dbType = DatabaseType.CENTRAL_REPOSITORY;
 
  491         String getHashSetName() {
 
  507         boolean isReadOnly(){
 
  516         HashDbManager.HashDb.KnownFilesType getKnownFilesType() {
 
  517             return knownFilesType;
 
  525         boolean getSearchDuringIngest() {
 
  526             return searchDuringIngest;
 
  533         void setSearchDuringIngest(
boolean searchDuringIngest) {
 
  534             this.searchDuringIngest = searchDuringIngest;
 
  542         boolean getSendIngestMessages() {
 
  543             return sendIngestMessages;
 
  559         public void setPath(String path) {
 
  563         int getReferenceSetID(){
 
  564             return referenceSetID;
 
  571         boolean isFileDatabaseType(){
 
  572             return dbType == DatabaseType.FILE;
 
  575         boolean isCentralRepoDatabaseType(){
 
  576             return dbType == DatabaseType.CENTRAL_REPOSITORY;
 
  579         boolean matches(HashDb hashDb){
 
  584             if( ! this.knownFilesType.equals(hashDb.getKnownFilesType())){
 
  588             if((this.dbType == DatabaseType.CENTRAL_REPOSITORY) && (! (hashDb instanceof CentralRepoHashSet))
 
  589                     || (this.dbType == DatabaseType.FILE) && (! (hashDb instanceof SleuthkitHashSet))){
 
  593             if( ! this.hashSetName.equals(hashDb.getHashSetName())){
 
  597             if(hashDb instanceof CentralRepoHashSet){
 
  598                 CentralRepoHashSet crDb = (CentralRepoHashSet) hashDb;
 
  599                 if(this.referenceSetID != crDb.getReferenceSetID()){
 
  603                 if(! version.equals(crDb.getVersion())){
 
  612         public boolean equals(Object obj) {
 
  617             if (getClass() != obj.getClass()) {
 
  621             final HashDbInfo other = (HashDbInfo) obj;
 
  623             if(! this.dbType.equals(other.dbType)){
 
  627             if(this.dbType.equals(DatabaseType.FILE)){
 
  629                 return (this.hashSetName.equals(other.hashSetName)
 
  630                         && this.knownFilesType.equals(other.knownFilesType));
 
  633                 return (this.hashSetName.equals(other.hashSetName)
 
  634                         && (this.referenceSetID == other.referenceSetID)
 
  635                         && this.knownFilesType.equals(other.knownFilesType));
 
  640         public int hashCode() {
 
  642             hash = 89 * hash + Objects.hashCode(this.hashSetName);
 
  643             hash = 89 * hash + Objects.hashCode(this.knownFilesType);
 
  644             hash = 89 * hash + Objects.hashCode(this.dbType);
 
  645             if(this.dbType.equals(DatabaseType.CENTRAL_REPOSITORY)){
 
  646                 hash = 89 * hash + this.referenceSetID;
 
  659         private void readObject(java.io.ObjectInputStream stream)
 
  660             throws IOException, ClassNotFoundException {
 
  661             stream.defaultReadObject();
 
  664                 dbType = DatabaseType.FILE;
 
  674     static class HashLookupSettingsException 
extends Exception {
 
  676         private static final long serialVersionUID = 1L;
 
  678         HashLookupSettingsException(String message) {
 
  682         HashLookupSettingsException(String message, Throwable throwable) {
 
  683             super(message, throwable);
 
static HashConfigPaths getInstance()