From 64bc2a2faa0d27d437a552e01a6a09e76ff61fca Mon Sep 17 00:00:00 2001
From: Richard Cordovano <rcordovano@basistech.com>
Date: Mon, 4 Nov 2013 10:05:46 -0500
Subject: [PATCH] Line endings fix

---
 .../autopsy/hashdatabase/Bundle.properties    | 128 +--
 .../autopsy/hashdatabase/HashDb.java          | 606 ++++++-------
 .../autopsy/hashdatabase/HashDbXML.java       | 850 +++++++++---------
 3 files changed, 792 insertions(+), 792 deletions(-)

diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/Bundle.properties b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/Bundle.properties
index a9aead73cb..bf9c210f1a 100644
--- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/Bundle.properties
+++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/Bundle.properties
@@ -1,64 +1,64 @@
-OpenIDE-Module-Display-Category=Ingest Module
-OpenIDE-Module-Long-Description=\
-    Hash Database ingest module.  \n\n\
-    The ingest module analyzes files in the disk image and marks them as "known" (based on NSRL database lookup for "known" files) and "bad / interesting" (based on one or more databases supplied by the user).\n\n\
-    The module also contains additional non-ingest tools that are integrated in the GUI, such as file lookup by hash and hash database configuration.
-OpenIDE-Module-Name=HashDatabase
-HashDbSimplePanel.knownLabel.text=NSRL Database: 
-HashDbSimplePanel.notableLabel.text=Known Bad Database(s):
-HashDbSimplePanel.knownValLabel.text=-
-HashDbSimplePanel.notableValLabel.text=-
-HashDbSimplePanel.jLabel1.text=Enable known bad databases for ingest:
-HashDbAddDatabaseDialog.cancelButton.text=Cancel
-HashDbAddDatabaseDialog.okButton.text=OK
-HashDbAddDatabaseDialog.nsrlRadioButton.text=NSRL
-HashDbAddDatabaseDialog.knownBadRadioButton.text=Known Bad
-HashDbAddDatabaseDialog.databasePathTextField.text=
-HashDbAddDatabaseDialog.browseButton.text=Browse
-HashDbAddDatabaseDialog.jLabel1.text=Enter the name of the database:
-HashDbAddDatabaseDialog.databaseNameTextField.text=
-HashDbAddDatabaseDialog.jLabel2.text=Select the type of database:
-HashDbAddDatabaseDialog.useForIngestCheckbox.text=Enable for ingest
-HashDbAddDatabaseDialog.sendInboxMessagesCheckbox.text=Enable sending messages to inbox during ingest
-HashDbSearchPanel.hashTable.columnModel.title0=MD5 Hashes
-HashDbSearchPanel.hashTable.columnModel.title3=Title 4
-HashDbSearchPanel.hashTable.columnModel.title2=Title 3
-HashDbSearchPanel.hashTable.columnModel.title1=Title 2
-HashDbSearchPanel.addButton.text=Add Hash
-HashDbSearchPanel.hashField.text=
-HashDbSearchPanel.hashLabel.text=MD5 hash:
-HashDbSearchPanel.searchButton.text=Search
-HashDbSearchPanel.removeButton.text=Remove Selected
-HashDbSearchPanel.titleLabel.text=Search for files with the following MD5 hash(es):
-HashDbSearchPanel.errorField.text=Error: Not all files have been hashed.
-HashDbSearchPanel.saveBox.text=Remember Hashes
-HashDbSearchPanel.cancelButton.text=Cancel
-HashDbSimplePanel.calcHashesButton.text=Calculate hashes even if no hash database is selected
-HashDbSimplePanel.nsrlDbLabel.text=NSRL Database:
-HashDbSimplePanel.nsrlDbLabelVal.text=-
-HashDbManagementPanel.hashDbIndexStatusLabel.text=No database selected
-HashDbManagementPanel.jLabel2.text=Name:
-HashDbManagementPanel.showInboxMessagesCheckBox.text=Enable sending messages to inbox during ingest
-HashDbManagementPanel.useForIngestCheckbox.text=Enable for ingest
-HashDbManagementPanel.indexButton.text=Index
-HashDbManagementPanel.indexLabel.text=Index Status:
-HashDbManagementPanel.optionsLabel.text=Options
-HashDbManagementPanel.jLabel4.text=Location:
-HashDbManagementPanel.jLabel6.text=Type:
-HashDbManagementPanel.ingestWarningLabel.text=Ingest is ongoing, some settings will be unavailable until it finishes.
-HashDbManagementPanel.hashDbTypeLabel.text=No database selected
-HashDbManagementPanel.typeLabel.text=Type:
-HashDbManagementPanel.deleteButton.text=Delete Database
-HashDbManagementPanel.importButton.text=Import Database
-HashDbManagementPanel.hashDbNameLabel.text=No database selected
-HashDbManagementPanel.nameLabel.text=Name:
-HashDbManagementPanel.jButton3.text=Import Database
-HashDbManagementPanel.locationLabel.text=Location:
-HashDbManagementPanel.hashDbLocationLabel.text=No database selected
-HashDbManagementPanel.informationLabel.text=Information
-HashDbManagementPanel.hashDatabasesLabel.text=Hash Databases:
-OpenIDE-Module-Short-Description=Hash Database Ingest Module and hash db tools
-ModalNoButtons.CURRENTLYON_LABEL.text=Currently Indexing x of y
-ModalNoButtons.GO_GET_COFFEE_LABEL.text=Hash databases are currently being indexed, this may take some time.
-ModalNoButtons.CURRENTDB_LABEL.text=(CurrentDb)
-ModalNoButtons.CANCEL_BUTTON.text=Cancel
+OpenIDE-Module-Display-Category=Ingest Module
+OpenIDE-Module-Long-Description=\
+    Hash Database ingest module.  \n\n\
+    The ingest module analyzes files in the disk image and marks them as "known" (based on NSRL database lookup for "known" files) and "bad / interesting" (based on one or more databases supplied by the user).\n\n\
+    The module also contains additional non-ingest tools that are integrated in the GUI, such as file lookup by hash and hash database configuration.
+OpenIDE-Module-Name=HashDatabase
+HashDbSimplePanel.knownLabel.text=NSRL Database: 
+HashDbSimplePanel.notableLabel.text=Known Bad Database(s):
+HashDbSimplePanel.knownValLabel.text=-
+HashDbSimplePanel.notableValLabel.text=-
+HashDbSimplePanel.jLabel1.text=Enable known bad databases for ingest:
+HashDbAddDatabaseDialog.cancelButton.text=Cancel
+HashDbAddDatabaseDialog.okButton.text=OK
+HashDbAddDatabaseDialog.nsrlRadioButton.text=NSRL
+HashDbAddDatabaseDialog.knownBadRadioButton.text=Known Bad
+HashDbAddDatabaseDialog.databasePathTextField.text=
+HashDbAddDatabaseDialog.browseButton.text=Browse
+HashDbAddDatabaseDialog.jLabel1.text=Enter the name of the database:
+HashDbAddDatabaseDialog.databaseNameTextField.text=
+HashDbAddDatabaseDialog.jLabel2.text=Select the type of database:
+HashDbAddDatabaseDialog.useForIngestCheckbox.text=Enable for ingest
+HashDbAddDatabaseDialog.sendInboxMessagesCheckbox.text=Enable sending messages to inbox during ingest
+HashDbSearchPanel.hashTable.columnModel.title0=MD5 Hashes
+HashDbSearchPanel.hashTable.columnModel.title3=Title 4
+HashDbSearchPanel.hashTable.columnModel.title2=Title 3
+HashDbSearchPanel.hashTable.columnModel.title1=Title 2
+HashDbSearchPanel.addButton.text=Add Hash
+HashDbSearchPanel.hashField.text=
+HashDbSearchPanel.hashLabel.text=MD5 hash:
+HashDbSearchPanel.searchButton.text=Search
+HashDbSearchPanel.removeButton.text=Remove Selected
+HashDbSearchPanel.titleLabel.text=Search for files with the following MD5 hash(es):
+HashDbSearchPanel.errorField.text=Error: Not all files have been hashed.
+HashDbSearchPanel.saveBox.text=Remember Hashes
+HashDbSearchPanel.cancelButton.text=Cancel
+HashDbSimplePanel.calcHashesButton.text=Calculate hashes even if no hash database is selected
+HashDbSimplePanel.nsrlDbLabel.text=NSRL Database:
+HashDbSimplePanel.nsrlDbLabelVal.text=-
+HashDbManagementPanel.hashDbIndexStatusLabel.text=No database selected
+HashDbManagementPanel.jLabel2.text=Name:
+HashDbManagementPanel.showInboxMessagesCheckBox.text=Enable sending messages to inbox during ingest
+HashDbManagementPanel.useForIngestCheckbox.text=Enable for ingest
+HashDbManagementPanel.indexButton.text=Index
+HashDbManagementPanel.indexLabel.text=Index Status:
+HashDbManagementPanel.optionsLabel.text=Options
+HashDbManagementPanel.jLabel4.text=Location:
+HashDbManagementPanel.jLabel6.text=Type:
+HashDbManagementPanel.ingestWarningLabel.text=Ingest is ongoing, some settings will be unavailable until it finishes.
+HashDbManagementPanel.hashDbTypeLabel.text=No database selected
+HashDbManagementPanel.typeLabel.text=Type:
+HashDbManagementPanel.deleteButton.text=Delete Database
+HashDbManagementPanel.importButton.text=Import Database
+HashDbManagementPanel.hashDbNameLabel.text=No database selected
+HashDbManagementPanel.nameLabel.text=Name:
+HashDbManagementPanel.jButton3.text=Import Database
+HashDbManagementPanel.locationLabel.text=Location:
+HashDbManagementPanel.hashDbLocationLabel.text=No database selected
+HashDbManagementPanel.informationLabel.text=Information
+HashDbManagementPanel.hashDatabasesLabel.text=Hash Databases:
+OpenIDE-Module-Short-Description=Hash Database Ingest Module and hash db tools
+ModalNoButtons.CURRENTLYON_LABEL.text=Currently Indexing x of y
+ModalNoButtons.GO_GET_COFFEE_LABEL.text=Hash databases are currently being indexed, this may take some time.
+ModalNoButtons.CURRENTDB_LABEL.text=(CurrentDb)
+ModalNoButtons.CANCEL_BUTTON.text=Cancel
diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDb.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDb.java
index 9a08234eab..47d3e58c52 100644
--- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDb.java
+++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDb.java
@@ -1,304 +1,304 @@
-/*
- * Autopsy Forensic Browser
- * 
- * Copyright 2011 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.hashdatabase;
-
-import java.beans.PropertyChangeListener;
-import java.beans.PropertyChangeSupport;
-import java.io.File;
-import java.util.List;
-import java.util.logging.Level;
-import javax.swing.SwingWorker;
-import org.netbeans.api.progress.ProgressHandle;
-import org.netbeans.api.progress.ProgressHandleFactory;
-import org.openide.util.Cancellable;
-import org.sleuthkit.autopsy.coreutils.Logger;
-import org.sleuthkit.datamodel.SleuthkitJNI;
-import org.sleuthkit.datamodel.TskException;
-
-/**
- * Hash database representation of NSRL and Known Bad hash databases
- * with indexing capability
- * 
- */
-public class HashDb implements Comparable<HashDb> {
-
-    enum EVENT {INDEXING_DONE };
-    private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
-    
-    
-    public enum DBType{
-        NSRL("NSRL"), KNOWN_BAD("Known Bad");
-        
-        private String displayName;
-        
-        private DBType(String displayName) {
-            this.displayName = displayName;
-        }
-        
-        public String getDisplayName() {
-            return this.displayName;
-        }
-    }
-    
-    // Suffix added to the end of a database name to get its index file
-    private static final String INDEX_SUFFIX = "-md5.idx";
-    
-    private String name;
-    private List<String> databasePaths; // TODO: Length limited to one for now...
-    private boolean useForIngest;
-    private boolean showInboxMessages;
-    private boolean indexing;
-    private DBType type;
-    
-    public HashDb(String name, List<String> databasePaths, boolean useForIngest, boolean showInboxMessages, DBType type) {
-        this.name = name;
-        this.databasePaths = databasePaths;
-        this.useForIngest = useForIngest;
-        this.showInboxMessages = showInboxMessages;
-        this.type = type;
-        this.indexing = false;
-    }
-    
-    void addPropertyChangeListener(PropertyChangeListener pcl) {
-        pcs.addPropertyChangeListener(pcl);
-    }
-    
-    void removePropertyChangeListener(PropertyChangeListener pcl) {
-        pcs.removePropertyChangeListener(pcl);
-    }
-    
-    boolean getUseForIngest() {
-        return useForIngest;
-    }
-    
-    boolean getShowInboxMessages() {
-        return showInboxMessages;
-    }
-    
-    DBType getDbType() {
-        return type;
-    }
-    
-    String getName() {
-        return name;
-    }
-    
-    List<String> getDatabasePaths() {
-        return databasePaths;
-    }
-    
-    void setUseForIngest(boolean useForIngest) {
-        this.useForIngest = useForIngest;
-    }
-    
-    void setShowInboxMessages(boolean showInboxMessages) {
-        this.showInboxMessages = showInboxMessages;
-    }
-    
-    void setName(String name) {
-        this.name = name;
-    }
-    
-    void setDatabasePaths(List<String> databasePaths) {
-        this.databasePaths = databasePaths;
-    }
-    
-    void setDbType(DBType type) {
-        this.type = type;
-    }
-    
-    /**
-     * Checks if the database exists.
-     * @return true if a file exists at the database path, else false
-     */
-    boolean databaseExists() {
-        return databaseFile().exists();
-    }
-    
-    /**
-     * Checks if Sleuth Kit can open the index for the database path.
-     * @return true if the index was found and opened successfully, else false
-     */
-    boolean indexExists() {
-        try {
-            return hasIndex(databasePaths.get(0)); // TODO: support multiple paths
-        } catch (TskException ex) {
-            Logger.getLogger(this.getClass().getName()).log(Level.WARNING, "Error checking if index exists.", ex);
-            return false;
-        }
-    }
-
-    /**
-     * Gets the database file.
-     * @return a File initialized with the database path
-     */
-    File databaseFile() {
-        return new File(databasePaths.get(0)); // TODO: support multiple paths
-    }
-    
-    /**
-     * Gets the index file
-     * @return a File initialized with an index path derived from the database
-     * path
-     */
-    File indexFile() {
-        return new File(toIndexPath(databasePaths.get(0))); // TODO: support multiple paths
-    }
-
-    /**
-     * Checks if the index file is older than the database file
-     * @return true if there is are files at the index path and the database
-     * path, and the index file has an older modified-time than the database
-     * file, else false
-     */
-    boolean isOutdated() {
-        File i = indexFile();
-        File db = databaseFile();
-
-        return i.exists() && db.exists() && isOlderThan(i, db);
-    }
-    
-    /**
-     * Checks if the database is being indexed
-     */
-    boolean isIndexing() {
-        return indexing;
-    }
-
-    /**
-     * Returns the status of the HashDb as determined from indexExists(),
-     * databaseExists(), and isOutdated()
-     * @return IndexStatus enum according to their definitions
-     */
-    IndexStatus status() {
-        boolean i = this.indexExists();
-        boolean db = this.databaseExists();
-
-        if(indexing)
-            return IndexStatus.INDEXING;
-        if (i) {
-            if (db) {
-                return this.isOutdated() ? IndexStatus.INDEX_OUTDATED : IndexStatus.INDEX_CURRENT;
-            } else {
-                return IndexStatus.NO_DB;
-            }
-        } else {
-            return db ? IndexStatus.NO_INDEX : IndexStatus.NONE;
-        }
-    }
-
-    /**
-     * Tries to index the database (overwrites any existing index)
-     * @throws TskException if an error occurs in the SleuthKit bindings 
-     */
-    void createIndex() throws TskException {
-        indexing = true;
-        CreateIndex creator = new CreateIndex();
-        creator.execute();
-    }
-
-    /**
-     * Checks if one file is older than an other
-     * @param a first file
-     * @param b second file
-     * @return true if the first file's last modified data is before the second
-     * file's last modified date
-     */
-    private static boolean isOlderThan(File a, File b) {
-        return a.lastModified() < b.lastModified();
-    }
-
-    /**
-     * Determines if a path points to an index by checking the suffix
-     * @param path
-     * @return true if index
-     */
-    static boolean isIndexPath(String path) {
-        return path.endsWith(INDEX_SUFFIX);
-    }
-
-    /**
-     * Derives database path from an image path by removing the suffix.
-     * @param indexPath
-     * @return 
-     */
-    static String toDatabasePath(String indexPath) {
-        return indexPath.substring(0, indexPath.lastIndexOf(INDEX_SUFFIX));
-    }
-
-    /**
-     * Derives image path from an database path by appending the suffix.
-     * @param databasePath
-     * @return 
-     */
-    static String toIndexPath(String databasePath) {
-        return databasePath.concat(INDEX_SUFFIX);
-    }
-
-    /**
-     * Calls Sleuth Kit method via JNI to determine whether there is an
-     * index for the given path
-     * @param databasePath path Path for the database the index is of
-     * (database doesn't have to actually exist)'
-     * @return true if index exists
-     * @throws TskException if  there is an error in the JNI call 
-     */
-    static boolean hasIndex(String databasePath) throws TskException {
-        return SleuthkitJNI.lookupIndexExists(databasePath);
-    }
-    
-    @Override
-    public int compareTo(HashDb o) {
-        return this.name.compareTo(o.name);
-    }
-    
-    /* Thread that creates a database's index */
-    private class CreateIndex extends SwingWorker<Object,Void> {
-
-        private ProgressHandle progress;
-        
-        CreateIndex(){};
-
-        @Override
-        protected Object doInBackground() throws Exception {
-            progress = ProgressHandleFactory.createHandle("Indexing " + name);
-
-        /** We need proper cancel support in TSK to make the task cancellable
-         new Cancellable() {
-                Override
-                public boolean cancel() {
-                    return CreateIndex.this.cancel(true);
-                }
-            });
-            */
-            progress.start();
-            progress.switchToIndeterminate();
-            SleuthkitJNI.createLookupIndex(databasePaths.get(0));
-            return null;
-        }
-
-        /* clean up or start the worker threads */
-        @Override
-        protected void done() {
-            indexing = false;
-            progress.finish();
-            pcs.firePropertyChange(EVENT.INDEXING_DONE.toString(), null, name);
-        }
-    }
+/*
+ * Autopsy Forensic Browser
+ * 
+ * Copyright 2011 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.hashdatabase;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.io.File;
+import java.util.List;
+import java.util.logging.Level;
+import javax.swing.SwingWorker;
+import org.netbeans.api.progress.ProgressHandle;
+import org.netbeans.api.progress.ProgressHandleFactory;
+import org.openide.util.Cancellable;
+import org.sleuthkit.autopsy.coreutils.Logger;
+import org.sleuthkit.datamodel.SleuthkitJNI;
+import org.sleuthkit.datamodel.TskException;
+
+/**
+ * Hash database representation of NSRL and Known Bad hash databases
+ * with indexing capability
+ * 
+ */
+public class HashDb implements Comparable<HashDb> {
+
+    enum EVENT {INDEXING_DONE };
+    private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
+    
+    
+    public enum DBType{
+        NSRL("NSRL"), KNOWN_BAD("Known Bad");
+        
+        private String displayName;
+        
+        private DBType(String displayName) {
+            this.displayName = displayName;
+        }
+        
+        public String getDisplayName() {
+            return this.displayName;
+        }
+    }
+    
+    // Suffix added to the end of a database name to get its index file
+    private static final String INDEX_SUFFIX = "-md5.idx";
+    
+    private String name;
+    private List<String> databasePaths; // TODO: Length limited to one for now...
+    private boolean useForIngest;
+    private boolean showInboxMessages;
+    private boolean indexing;
+    private DBType type;
+    
+    public HashDb(String name, List<String> databasePaths, boolean useForIngest, boolean showInboxMessages, DBType type) {
+        this.name = name;
+        this.databasePaths = databasePaths;
+        this.useForIngest = useForIngest;
+        this.showInboxMessages = showInboxMessages;
+        this.type = type;
+        this.indexing = false;
+    }
+    
+    void addPropertyChangeListener(PropertyChangeListener pcl) {
+        pcs.addPropertyChangeListener(pcl);
+    }
+    
+    void removePropertyChangeListener(PropertyChangeListener pcl) {
+        pcs.removePropertyChangeListener(pcl);
+    }
+    
+    boolean getUseForIngest() {
+        return useForIngest;
+    }
+    
+    boolean getShowInboxMessages() {
+        return showInboxMessages;
+    }
+    
+    DBType getDbType() {
+        return type;
+    }
+    
+    String getName() {
+        return name;
+    }
+    
+    List<String> getDatabasePaths() {
+        return databasePaths;
+    }
+    
+    void setUseForIngest(boolean useForIngest) {
+        this.useForIngest = useForIngest;
+    }
+    
+    void setShowInboxMessages(boolean showInboxMessages) {
+        this.showInboxMessages = showInboxMessages;
+    }
+    
+    void setName(String name) {
+        this.name = name;
+    }
+    
+    void setDatabasePaths(List<String> databasePaths) {
+        this.databasePaths = databasePaths;
+    }
+    
+    void setDbType(DBType type) {
+        this.type = type;
+    }
+    
+    /**
+     * Checks if the database exists.
+     * @return true if a file exists at the database path, else false
+     */
+    boolean databaseExists() {
+        return databaseFile().exists();
+    }
+    
+    /**
+     * Checks if Sleuth Kit can open the index for the database path.
+     * @return true if the index was found and opened successfully, else false
+     */
+    boolean indexExists() {
+        try {
+            return hasIndex(databasePaths.get(0)); // TODO: support multiple paths
+        } catch (TskException ex) {
+            Logger.getLogger(this.getClass().getName()).log(Level.WARNING, "Error checking if index exists.", ex);
+            return false;
+        }
+    }
+
+    /**
+     * Gets the database file.
+     * @return a File initialized with the database path
+     */
+    File databaseFile() {
+        return new File(databasePaths.get(0)); // TODO: support multiple paths
+    }
+    
+    /**
+     * Gets the index file
+     * @return a File initialized with an index path derived from the database
+     * path
+     */
+    File indexFile() {
+        return new File(toIndexPath(databasePaths.get(0))); // TODO: support multiple paths
+    }
+
+    /**
+     * Checks if the index file is older than the database file
+     * @return true if there is are files at the index path and the database
+     * path, and the index file has an older modified-time than the database
+     * file, else false
+     */
+    boolean isOutdated() {
+        File i = indexFile();
+        File db = databaseFile();
+
+        return i.exists() && db.exists() && isOlderThan(i, db);
+    }
+    
+    /**
+     * Checks if the database is being indexed
+     */
+    boolean isIndexing() {
+        return indexing;
+    }
+
+    /**
+     * Returns the status of the HashDb as determined from indexExists(),
+     * databaseExists(), and isOutdated()
+     * @return IndexStatus enum according to their definitions
+     */
+    IndexStatus status() {
+        boolean i = this.indexExists();
+        boolean db = this.databaseExists();
+
+        if(indexing)
+            return IndexStatus.INDEXING;
+        if (i) {
+            if (db) {
+                return this.isOutdated() ? IndexStatus.INDEX_OUTDATED : IndexStatus.INDEX_CURRENT;
+            } else {
+                return IndexStatus.NO_DB;
+            }
+        } else {
+            return db ? IndexStatus.NO_INDEX : IndexStatus.NONE;
+        }
+    }
+
+    /**
+     * Tries to index the database (overwrites any existing index)
+     * @throws TskException if an error occurs in the SleuthKit bindings 
+     */
+    void createIndex() throws TskException {
+        indexing = true;
+        CreateIndex creator = new CreateIndex();
+        creator.execute();
+    }
+
+    /**
+     * Checks if one file is older than an other
+     * @param a first file
+     * @param b second file
+     * @return true if the first file's last modified data is before the second
+     * file's last modified date
+     */
+    private static boolean isOlderThan(File a, File b) {
+        return a.lastModified() < b.lastModified();
+    }
+
+    /**
+     * Determines if a path points to an index by checking the suffix
+     * @param path
+     * @return true if index
+     */
+    static boolean isIndexPath(String path) {
+        return path.endsWith(INDEX_SUFFIX);
+    }
+
+    /**
+     * Derives database path from an image path by removing the suffix.
+     * @param indexPath
+     * @return 
+     */
+    static String toDatabasePath(String indexPath) {
+        return indexPath.substring(0, indexPath.lastIndexOf(INDEX_SUFFIX));
+    }
+
+    /**
+     * Derives image path from an database path by appending the suffix.
+     * @param databasePath
+     * @return 
+     */
+    static String toIndexPath(String databasePath) {
+        return databasePath.concat(INDEX_SUFFIX);
+    }
+
+    /**
+     * Calls Sleuth Kit method via JNI to determine whether there is an
+     * index for the given path
+     * @param databasePath path Path for the database the index is of
+     * (database doesn't have to actually exist)'
+     * @return true if index exists
+     * @throws TskException if  there is an error in the JNI call 
+     */
+    static boolean hasIndex(String databasePath) throws TskException {
+        return SleuthkitJNI.lookupIndexExists(databasePath);
+    }
+    
+    @Override
+    public int compareTo(HashDb o) {
+        return this.name.compareTo(o.name);
+    }
+    
+    /* Thread that creates a database's index */
+    private class CreateIndex extends SwingWorker<Object,Void> {
+
+        private ProgressHandle progress;
+        
+        CreateIndex(){};
+
+        @Override
+        protected Object doInBackground() throws Exception {
+            progress = ProgressHandleFactory.createHandle("Indexing " + name);
+
+        /** We need proper cancel support in TSK to make the task cancellable
+         new Cancellable() {
+                Override
+                public boolean cancel() {
+                    return CreateIndex.this.cancel(true);
+                }
+            });
+            */
+            progress.start();
+            progress.switchToIndeterminate();
+            SleuthkitJNI.createLookupIndex(databasePaths.get(0));
+            return null;
+        }
+
+        /* clean up or start the worker threads */
+        @Override
+        protected void done() {
+            indexing = false;
+            progress.finish();
+            pcs.firePropertyChange(EVENT.INDEXING_DONE.toString(), null, name);
+        }
+    }
 }
\ No newline at end of file
diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbXML.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbXML.java
index 9ddc638b9e..b0040ff080 100644
--- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbXML.java
+++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbXML.java
@@ -1,425 +1,425 @@
-/*
- * Autopsy Forensic Browser
- * 
- * Copyright 2011 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.hashdatabase;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.logging.Level;
-import javax.swing.JFileChooser;
-import javax.swing.JOptionPane;
-import javax.swing.filechooser.FileNameExtensionFilter;
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-import org.sleuthkit.autopsy.coreutils.Logger;
-import org.sleuthkit.autopsy.coreutils.PlatformUtil;
-import org.sleuthkit.autopsy.coreutils.XMLUtil;
-import org.sleuthkit.autopsy.hashdatabase.HashDb.DBType;
-import org.sleuthkit.datamodel.SleuthkitJNI;
-import org.sleuthkit.datamodel.TskCoreException;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.NodeList;
-
-public class HashDbXML {
-    private static final String ROOT_EL = "hash_sets";
-    private static final String SET_EL = "hash_set";
-    private static final String SET_NAME_ATTR = "name";
-    private static final String SET_TYPE_ATTR = "type"; 
-    private static final String SET_USE_FOR_INGEST_ATTR = "use_for_ingest";
-    private static final String SET_SHOW_INBOX_MESSAGES = "show_inbox_messages";
-    private static final String PATH_EL = "hash_set_path";
-    private static final String PATH_NUMBER_ATTR = "number";
-    private static final String CUR_HASHSETS_FILE_NAME = "hashsets.xml";
-    private static final String XSDFILE = "HashsetsSchema.xsd";
-    private static final String ENCODING = "UTF-8";
-    private static final String CUR_HASHSET_FILE = PlatformUtil.getUserConfigDirectory() + File.separator + CUR_HASHSETS_FILE_NAME;
-    private static final String SET_CALC = "hash_calculate";
-    private static final String SET_VALUE = "value";
-    private static final Logger logger = Logger.getLogger(HashDbXML.class.getName());
-    private static HashDbXML currentInstance;
-    
-    private List<HashDb> knownBadSets;
-    private HashDb nsrlSet;
-    private String xmlFile;
-    private boolean calculate;
-    
-    private HashDbXML(String xmlFile) {
-        knownBadSets = new ArrayList<HashDb>();
-        this.xmlFile = xmlFile;
-    }
-    
-    /**
-     * get instance for managing the current keyword list of the application
-     */
-    static synchronized HashDbXML getCurrent() {
-        if (currentInstance == null) {
-            currentInstance = new HashDbXML(CUR_HASHSET_FILE);
-            currentInstance.reload();
-        }
-        return currentInstance;
-    }
-    
-    /**
-     * Get the hash sets
-     */
-    public List<HashDb> getAllSets() {
-        List<HashDb> ret = new ArrayList<HashDb>();
-        if(nsrlSet != null) {
-            ret.add(nsrlSet);
-        }
-        ret.addAll(knownBadSets);
-        return ret;
-    }
-    
-    /** 
-     * Get the Known Bad sets
-     */
-    public List<HashDb> getKnownBadSets() {
-        return knownBadSets;
-    }
-    
-    /** 
-     * Get the NSRL set
-     */
-    public HashDb getNSRLSet() {
-        return nsrlSet;
-    }
-    
-    /**
-     * Add a known bad hash set
-     */
-    public void addKnownBadSet(HashDb set) {
-        knownBadSets.add(set);
-        //save();
-    }
-    
-    /**
-     * Add a known bad hash set
-     */
-    public void addKnownBadSet(int index, HashDb set) {
-        knownBadSets.add(index, set);
-        //save();
-    }
-    
-    /**
-     * Set the NSRL hash set (override old set)
-     */
-    public void setNSRLSet(HashDb set) {
-        this.nsrlSet = set;
-        //save();
-    }
-    
-    /**
-     * Remove a hash known bad set
-     */
-    public void removeKnownBadSetAt(int index) {
-        knownBadSets.remove(index);
-        //save();
-    }
-    
-    /** 
-     * Remove the NSRL database
-     */
-    public void removeNSRLSet() {
-        this.nsrlSet = null;
-        //save();
-    }
-    
-    /**
-     * load the file or create new
-     */
-    public void reload() {
-        boolean created = false;
-
-        //TODO clearing the list causes a bug: we lose track of the state
-        //whether db is being indexed, we should somehow preserve the state when loading new HashDb objects
-        
-        knownBadSets.clear();
-        nsrlSet = null;
-        
-        if (!this.setsFileExists()) {
-            //create new if it doesn't exist
-            save();
-            created = true;
-        }
-
-        //load, if fails to load create new; save regardless
-        load();
-        if (!created) {
-            //create new if failed to load
-            save();
-        }
-    }
-    
-    /**
-     * Sets the local variable calculate to the given boolean.
-     * @param set the state to make calculate
-     */
-    public void setCalculate(boolean set) {
-        this.calculate = set;
-        //save();
-    }
-    
-    /**
-     * Returns the value of the local boolean calculate.
-     * @return true if calculate is true, false otherwise
-     */
-    public boolean getCalculate() {
-        return this.calculate;
-    }
-    
-    /**
-     * writes out current sets file replacing the last one
-     */
-    public boolean save() {
-        boolean success = false;
-
-        DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance();
-
-        try {
-            DocumentBuilder docBuilder = dbfac.newDocumentBuilder();
-            Document doc = docBuilder.newDocument();
-
-            Element rootEl = doc.createElement(ROOT_EL);
-            doc.appendChild(rootEl);
-
-            for (HashDb set : knownBadSets) {
-                String useForIngest = Boolean.toString(set.getUseForIngest());
-                String showInboxMessages = Boolean.toString(set.getShowInboxMessages());
-                List<String> paths = set.getDatabasePaths();
-                String type = DBType.KNOWN_BAD.toString();
-
-                Element setEl = doc.createElement(SET_EL);
-                setEl.setAttribute(SET_NAME_ATTR, set.getName());
-                setEl.setAttribute(SET_TYPE_ATTR, type);
-                setEl.setAttribute(SET_USE_FOR_INGEST_ATTR, useForIngest);
-                setEl.setAttribute(SET_SHOW_INBOX_MESSAGES, showInboxMessages);
-
-                for (int i = 0; i < paths.size(); i++) {
-                    String path = paths.get(i);
-                    Element pathEl = doc.createElement(PATH_EL);
-                    pathEl.setAttribute(PATH_NUMBER_ATTR, Integer.toString(i));
-                    pathEl.setTextContent(path);
-                    setEl.appendChild(pathEl);
-                }
-                rootEl.appendChild(setEl);
-            }
-            
-            if(nsrlSet != null) {
-                String useForIngest = Boolean.toString(nsrlSet.getUseForIngest());
-                String showInboxMessages = Boolean.toString(nsrlSet.getShowInboxMessages());
-                List<String> paths = nsrlSet.getDatabasePaths();
-                String type = DBType.NSRL.toString();
-
-                Element setEl = doc.createElement(SET_EL);
-                setEl.setAttribute(SET_NAME_ATTR, nsrlSet.getName());
-                setEl.setAttribute(SET_TYPE_ATTR, type);
-                setEl.setAttribute(SET_USE_FOR_INGEST_ATTR, useForIngest);
-                setEl.setAttribute(SET_SHOW_INBOX_MESSAGES, showInboxMessages);
-
-                for (int i = 0; i < paths.size(); i++) {
-                    String path = paths.get(i);
-                    Element pathEl = doc.createElement(PATH_EL);
-                    pathEl.setAttribute(PATH_NUMBER_ATTR, Integer.toString(i));
-                    pathEl.setTextContent(path);
-                    setEl.appendChild(pathEl);
-                }
-                rootEl.appendChild(setEl);
-            }
-            
-            String calcValue = Boolean.toString(calculate);
-            Element setCalc = doc.createElement(SET_CALC);
-            setCalc.setAttribute(SET_VALUE, calcValue);
-            rootEl.appendChild(setCalc);
-
-            success = XMLUtil.saveDoc(HashDbXML.class, xmlFile, ENCODING, doc);
-        } catch (ParserConfigurationException e) {
-            logger.log(Level.SEVERE, "Error saving hash sets: can't initialize parser.", e);
-        }
-        return success;
-    }
-
-    /**
-     * load and parse XML, then dispose
-     */
-    public boolean load() {
-        final Document doc = XMLUtil.loadDoc(HashDbXML.class, xmlFile, XSDFILE);
-        if (doc == null) {
-            return false;
-        }
-
-        Element root = doc.getDocumentElement();
-        if (root == null) {
-            logger.log(Level.SEVERE, "Error loading hash sets: invalid file format.");
-            return false;
-        }
-        NodeList setsNList = root.getElementsByTagName(SET_EL);
-        int numSets = setsNList.getLength();
-        if(numSets==0) {
-            logger.log(Level.WARNING, "No element hash_set exists.");
-        }
-        for (int i = 0; i < numSets; ++i) {
-            Element setEl = (Element) setsNList.item(i);
-            final String name = setEl.getAttribute(SET_NAME_ATTR);
-            final String type = setEl.getAttribute(SET_TYPE_ATTR);
-            final String useForIngest = setEl.getAttribute(SET_USE_FOR_INGEST_ATTR);
-            final String showInboxMessages = setEl.getAttribute(SET_SHOW_INBOX_MESSAGES);
-            Boolean useForIngestBool = Boolean.parseBoolean(useForIngest);
-            Boolean showInboxMessagesBool = Boolean.parseBoolean(showInboxMessages);
-            List<String> paths = new ArrayList<String>();
-
-            // Parse all paths
-            NodeList pathsNList = setEl.getElementsByTagName(PATH_EL);
-            final int numPaths = pathsNList.getLength();
-            for (int j = 0; j < numPaths; ++j) {
-                Element pathEl = (Element) pathsNList.item(j);
-                String number = pathEl.getAttribute(PATH_NUMBER_ATTR);
-                String path = pathEl.getTextContent();
-                
-                // If either the database or it's index exist
-                File database = new File(path);
-                File index = new File(HashDb.toIndexPath(path));
-                if(database.exists() || index.exists()) {
-                    paths.add(path);
-                } else {
-                    // Ask for new path
-                    int ret = JOptionPane.showConfirmDialog(null, "Database " + name + " could not be found at location\n"
-                            + path + "\n"
-                            + " Would you like to search for the file?", "Missing Database", JOptionPane.YES_NO_OPTION);
-                    if (ret == JOptionPane.YES_OPTION) {
-                        String filePath = searchForFile(name);
-                        if(filePath!=null) {
-                            paths.add(filePath);
-                        }
-                    }
-                }
-            }
-            
-            // Check everything was properly set
-            if(name.isEmpty()) {
-                logger.log(Level.WARNING, "Name was not set for hash_set at index {0}.", i);
-            }
-            if(type.isEmpty()) {
-                logger.log(Level.SEVERE, "Type was not set for hash_set at index {0}, cannot make instance of HashDb class.", i);
-                return false; // exit because this causes a fatal error
-            }
-            if(useForIngest.isEmpty()) {
-                logger.log(Level.WARNING, "UseForIngest was not set for hash_set at index {0}.", i);
-            }
-            if(showInboxMessages.isEmpty()) {
-                logger.log(Level.WARNING, "ShowInboxMessages was not set for hash_set at index {0}.", i);
-            }
-            
-            if(paths.isEmpty()) {
-                logger.log(Level.WARNING, "No paths were set for hash_set at index {0}. Removing the database.", i);
-            } else {
-                // No paths for this entry, the user most likely declined to search for them
-                DBType typeDBType = DBType.valueOf(type);
-                HashDb set = new HashDb(name, paths, useForIngestBool, showInboxMessagesBool, typeDBType);
-                
-                if(typeDBType == DBType.KNOWN_BAD) {
-                    knownBadSets.add(set);
-                } else if(typeDBType == DBType.NSRL) {
-                    this.nsrlSet = set;
-                }
-            }
-        }
-        
-        NodeList calcList = root.getElementsByTagName(SET_CALC);
-        int numCalc = calcList.getLength(); // Shouldn't be more than 1
-        if(numCalc==0) {
-            logger.log(Level.WARNING, "No element hash_calculate exists.");
-        }
-        for(int i=0; i<numCalc; i++) {
-            Element calcEl = (Element) calcList.item(i);
-            final String value = calcEl.getAttribute(SET_VALUE);
-            calculate = Boolean.parseBoolean(value);
-        }
-        return true;
-    }
-    
-    /**
-     * Ask the user to browse to a new Hash Database file with the same database
-     * name as the one provided. If the names do not match, the database cannot
-     * be added. If the user cancels the search, return null, meaning the user
-     * would like to remove the entry for the missing database.
-     * 
-     * @param name the name of the database to add
-     * @return the file path to the new database, or null if the user wants to
-     *         delete the old database
-     */
-    private String searchForFile(String name) {
-        // Initialize the file chooser and only allow hash databases to be opened
-        JFileChooser fc = new JFileChooser();
-        fc.setDragEnabled(false);
-        fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
-        String[] EXTENSION = new String[] { "txt", "idx", "hash", "Hash" };
-        FileNameExtensionFilter filter = new FileNameExtensionFilter(
-                "Hash Database File", EXTENSION);
-        fc.setFileFilter(filter);
-        fc.setMultiSelectionEnabled(false);
-
-        int retval = fc.showOpenDialog(null);
-        // If the user selects an appropriate file
-        if (retval == JFileChooser.APPROVE_OPTION) {
-            File f = fc.getSelectedFile();
-            try {
-                String filePath = f.getCanonicalPath();
-                if (HashDb.isIndexPath(filePath)) {
-                    filePath = HashDb.toDatabasePath(filePath);
-                }
-                String derivedName = SleuthkitJNI.getDatabaseName(filePath);
-                // If the database has the same name as before, return it
-                if(derivedName.equals(name)) {
-                    return filePath;
-                } else {
-                    int tryAgain = JOptionPane.showConfirmDialog(null, "Database file cannot be added because it does not have the same name as the original.\n" +
-                            "Would you like to try a different database?", "Invalid File", JOptionPane.YES_NO_OPTION);
-                    if (tryAgain == JOptionPane.YES_OPTION) {
-                        return searchForFile(name);
-                    } else {
-                        return null;
-                    }
-                }
-            } catch (IOException ex) {
-                logger.log(Level.WARNING, "Couldn't get selected file path.", ex);
-            } catch (TskCoreException ex) {
-                int tryAgain = JOptionPane.showConfirmDialog(null, "Database file you chose cannot be opened.\n" + "If it was just an index, please try to recreate it from the database.\n" +
-                        "Would you like to choose another database?", "Invalid File", JOptionPane.YES_NO_OPTION);
-                if (tryAgain == JOptionPane.YES_OPTION) {
-                    return searchForFile(name);
-                } else {
-                    return null;
-                }
-            }
-        }
-        // Otherwise the user cancelled, so delete the missing entry
-        return null;
-    }
-
-    private boolean setsFileExists() {
-        File f = new File(xmlFile);
-        return f.exists() && f.canRead() && f.canWrite();
-    }
-    
-    
-}
+/*
+ * Autopsy Forensic Browser
+ * 
+ * Copyright 2011 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.hashdatabase;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import javax.swing.JFileChooser;
+import javax.swing.JOptionPane;
+import javax.swing.filechooser.FileNameExtensionFilter;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import org.sleuthkit.autopsy.coreutils.Logger;
+import org.sleuthkit.autopsy.coreutils.PlatformUtil;
+import org.sleuthkit.autopsy.coreutils.XMLUtil;
+import org.sleuthkit.autopsy.hashdatabase.HashDb.DBType;
+import org.sleuthkit.datamodel.SleuthkitJNI;
+import org.sleuthkit.datamodel.TskCoreException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+public class HashDbXML {
+    private static final String ROOT_EL = "hash_sets";
+    private static final String SET_EL = "hash_set";
+    private static final String SET_NAME_ATTR = "name";
+    private static final String SET_TYPE_ATTR = "type"; 
+    private static final String SET_USE_FOR_INGEST_ATTR = "use_for_ingest";
+    private static final String SET_SHOW_INBOX_MESSAGES = "show_inbox_messages";
+    private static final String PATH_EL = "hash_set_path";
+    private static final String PATH_NUMBER_ATTR = "number";
+    private static final String CUR_HASHSETS_FILE_NAME = "hashsets.xml";
+    private static final String XSDFILE = "HashsetsSchema.xsd";
+    private static final String ENCODING = "UTF-8";
+    private static final String CUR_HASHSET_FILE = PlatformUtil.getUserConfigDirectory() + File.separator + CUR_HASHSETS_FILE_NAME;
+    private static final String SET_CALC = "hash_calculate";
+    private static final String SET_VALUE = "value";
+    private static final Logger logger = Logger.getLogger(HashDbXML.class.getName());
+    private static HashDbXML currentInstance;
+    
+    private List<HashDb> knownBadSets;
+    private HashDb nsrlSet;
+    private String xmlFile;
+    private boolean calculate;
+    
+    private HashDbXML(String xmlFile) {
+        knownBadSets = new ArrayList<HashDb>();
+        this.xmlFile = xmlFile;
+    }
+    
+    /**
+     * get instance for managing the current keyword list of the application
+     */
+    static synchronized HashDbXML getCurrent() {
+        if (currentInstance == null) {
+            currentInstance = new HashDbXML(CUR_HASHSET_FILE);
+            currentInstance.reload();
+        }
+        return currentInstance;
+    }
+    
+    /**
+     * Get the hash sets
+     */
+    public List<HashDb> getAllSets() {
+        List<HashDb> ret = new ArrayList<HashDb>();
+        if(nsrlSet != null) {
+            ret.add(nsrlSet);
+        }
+        ret.addAll(knownBadSets);
+        return ret;
+    }
+    
+    /** 
+     * Get the Known Bad sets
+     */
+    public List<HashDb> getKnownBadSets() {
+        return knownBadSets;
+    }
+    
+    /** 
+     * Get the NSRL set
+     */
+    public HashDb getNSRLSet() {
+        return nsrlSet;
+    }
+    
+    /**
+     * Add a known bad hash set
+     */
+    public void addKnownBadSet(HashDb set) {
+        knownBadSets.add(set);
+        //save();
+    }
+    
+    /**
+     * Add a known bad hash set
+     */
+    public void addKnownBadSet(int index, HashDb set) {
+        knownBadSets.add(index, set);
+        //save();
+    }
+    
+    /**
+     * Set the NSRL hash set (override old set)
+     */
+    public void setNSRLSet(HashDb set) {
+        this.nsrlSet = set;
+        //save();
+    }
+    
+    /**
+     * Remove a hash known bad set
+     */
+    public void removeKnownBadSetAt(int index) {
+        knownBadSets.remove(index);
+        //save();
+    }
+    
+    /** 
+     * Remove the NSRL database
+     */
+    public void removeNSRLSet() {
+        this.nsrlSet = null;
+        //save();
+    }
+    
+    /**
+     * load the file or create new
+     */
+    public void reload() {
+        boolean created = false;
+
+        //TODO clearing the list causes a bug: we lose track of the state
+        //whether db is being indexed, we should somehow preserve the state when loading new HashDb objects
+        
+        knownBadSets.clear();
+        nsrlSet = null;
+        
+        if (!this.setsFileExists()) {
+            //create new if it doesn't exist
+            save();
+            created = true;
+        }
+
+        //load, if fails to load create new; save regardless
+        load();
+        if (!created) {
+            //create new if failed to load
+            save();
+        }
+    }
+    
+    /**
+     * Sets the local variable calculate to the given boolean.
+     * @param set the state to make calculate
+     */
+    public void setCalculate(boolean set) {
+        this.calculate = set;
+        //save();
+    }
+    
+    /**
+     * Returns the value of the local boolean calculate.
+     * @return true if calculate is true, false otherwise
+     */
+    public boolean getCalculate() {
+        return this.calculate;
+    }
+    
+    /**
+     * writes out current sets file replacing the last one
+     */
+    public boolean save() {
+        boolean success = false;
+
+        DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance();
+
+        try {
+            DocumentBuilder docBuilder = dbfac.newDocumentBuilder();
+            Document doc = docBuilder.newDocument();
+
+            Element rootEl = doc.createElement(ROOT_EL);
+            doc.appendChild(rootEl);
+
+            for (HashDb set : knownBadSets) {
+                String useForIngest = Boolean.toString(set.getUseForIngest());
+                String showInboxMessages = Boolean.toString(set.getShowInboxMessages());
+                List<String> paths = set.getDatabasePaths();
+                String type = DBType.KNOWN_BAD.toString();
+
+                Element setEl = doc.createElement(SET_EL);
+                setEl.setAttribute(SET_NAME_ATTR, set.getName());
+                setEl.setAttribute(SET_TYPE_ATTR, type);
+                setEl.setAttribute(SET_USE_FOR_INGEST_ATTR, useForIngest);
+                setEl.setAttribute(SET_SHOW_INBOX_MESSAGES, showInboxMessages);
+
+                for (int i = 0; i < paths.size(); i++) {
+                    String path = paths.get(i);
+                    Element pathEl = doc.createElement(PATH_EL);
+                    pathEl.setAttribute(PATH_NUMBER_ATTR, Integer.toString(i));
+                    pathEl.setTextContent(path);
+                    setEl.appendChild(pathEl);
+                }
+                rootEl.appendChild(setEl);
+            }
+            
+            if(nsrlSet != null) {
+                String useForIngest = Boolean.toString(nsrlSet.getUseForIngest());
+                String showInboxMessages = Boolean.toString(nsrlSet.getShowInboxMessages());
+                List<String> paths = nsrlSet.getDatabasePaths();
+                String type = DBType.NSRL.toString();
+
+                Element setEl = doc.createElement(SET_EL);
+                setEl.setAttribute(SET_NAME_ATTR, nsrlSet.getName());
+                setEl.setAttribute(SET_TYPE_ATTR, type);
+                setEl.setAttribute(SET_USE_FOR_INGEST_ATTR, useForIngest);
+                setEl.setAttribute(SET_SHOW_INBOX_MESSAGES, showInboxMessages);
+
+                for (int i = 0; i < paths.size(); i++) {
+                    String path = paths.get(i);
+                    Element pathEl = doc.createElement(PATH_EL);
+                    pathEl.setAttribute(PATH_NUMBER_ATTR, Integer.toString(i));
+                    pathEl.setTextContent(path);
+                    setEl.appendChild(pathEl);
+                }
+                rootEl.appendChild(setEl);
+            }
+            
+            String calcValue = Boolean.toString(calculate);
+            Element setCalc = doc.createElement(SET_CALC);
+            setCalc.setAttribute(SET_VALUE, calcValue);
+            rootEl.appendChild(setCalc);
+
+            success = XMLUtil.saveDoc(HashDbXML.class, xmlFile, ENCODING, doc);
+        } catch (ParserConfigurationException e) {
+            logger.log(Level.SEVERE, "Error saving hash sets: can't initialize parser.", e);
+        }
+        return success;
+    }
+
+    /**
+     * load and parse XML, then dispose
+     */
+    public boolean load() {
+        final Document doc = XMLUtil.loadDoc(HashDbXML.class, xmlFile, XSDFILE);
+        if (doc == null) {
+            return false;
+        }
+
+        Element root = doc.getDocumentElement();
+        if (root == null) {
+            logger.log(Level.SEVERE, "Error loading hash sets: invalid file format.");
+            return false;
+        }
+        NodeList setsNList = root.getElementsByTagName(SET_EL);
+        int numSets = setsNList.getLength();
+        if(numSets==0) {
+            logger.log(Level.WARNING, "No element hash_set exists.");
+        }
+        for (int i = 0; i < numSets; ++i) {
+            Element setEl = (Element) setsNList.item(i);
+            final String name = setEl.getAttribute(SET_NAME_ATTR);
+            final String type = setEl.getAttribute(SET_TYPE_ATTR);
+            final String useForIngest = setEl.getAttribute(SET_USE_FOR_INGEST_ATTR);
+            final String showInboxMessages = setEl.getAttribute(SET_SHOW_INBOX_MESSAGES);
+            Boolean useForIngestBool = Boolean.parseBoolean(useForIngest);
+            Boolean showInboxMessagesBool = Boolean.parseBoolean(showInboxMessages);
+            List<String> paths = new ArrayList<String>();
+
+            // Parse all paths
+            NodeList pathsNList = setEl.getElementsByTagName(PATH_EL);
+            final int numPaths = pathsNList.getLength();
+            for (int j = 0; j < numPaths; ++j) {
+                Element pathEl = (Element) pathsNList.item(j);
+                String number = pathEl.getAttribute(PATH_NUMBER_ATTR);
+                String path = pathEl.getTextContent();
+                
+                // If either the database or it's index exist
+                File database = new File(path);
+                File index = new File(HashDb.toIndexPath(path));
+                if(database.exists() || index.exists()) {
+                    paths.add(path);
+                } else {
+                    // Ask for new path
+                    int ret = JOptionPane.showConfirmDialog(null, "Database " + name + " could not be found at location\n"
+                            + path + "\n"
+                            + " Would you like to search for the file?", "Missing Database", JOptionPane.YES_NO_OPTION);
+                    if (ret == JOptionPane.YES_OPTION) {
+                        String filePath = searchForFile(name);
+                        if(filePath!=null) {
+                            paths.add(filePath);
+                        }
+                    }
+                }
+            }
+            
+            // Check everything was properly set
+            if(name.isEmpty()) {
+                logger.log(Level.WARNING, "Name was not set for hash_set at index {0}.", i);
+            }
+            if(type.isEmpty()) {
+                logger.log(Level.SEVERE, "Type was not set for hash_set at index {0}, cannot make instance of HashDb class.", i);
+                return false; // exit because this causes a fatal error
+            }
+            if(useForIngest.isEmpty()) {
+                logger.log(Level.WARNING, "UseForIngest was not set for hash_set at index {0}.", i);
+            }
+            if(showInboxMessages.isEmpty()) {
+                logger.log(Level.WARNING, "ShowInboxMessages was not set for hash_set at index {0}.", i);
+            }
+            
+            if(paths.isEmpty()) {
+                logger.log(Level.WARNING, "No paths were set for hash_set at index {0}. Removing the database.", i);
+            } else {
+                // No paths for this entry, the user most likely declined to search for them
+                DBType typeDBType = DBType.valueOf(type);
+                HashDb set = new HashDb(name, paths, useForIngestBool, showInboxMessagesBool, typeDBType);
+                
+                if(typeDBType == DBType.KNOWN_BAD) {
+                    knownBadSets.add(set);
+                } else if(typeDBType == DBType.NSRL) {
+                    this.nsrlSet = set;
+                }
+            }
+        }
+        
+        NodeList calcList = root.getElementsByTagName(SET_CALC);
+        int numCalc = calcList.getLength(); // Shouldn't be more than 1
+        if(numCalc==0) {
+            logger.log(Level.WARNING, "No element hash_calculate exists.");
+        }
+        for(int i=0; i<numCalc; i++) {
+            Element calcEl = (Element) calcList.item(i);
+            final String value = calcEl.getAttribute(SET_VALUE);
+            calculate = Boolean.parseBoolean(value);
+        }
+        return true;
+    }
+    
+    /**
+     * Ask the user to browse to a new Hash Database file with the same database
+     * name as the one provided. If the names do not match, the database cannot
+     * be added. If the user cancels the search, return null, meaning the user
+     * would like to remove the entry for the missing database.
+     * 
+     * @param name the name of the database to add
+     * @return the file path to the new database, or null if the user wants to
+     *         delete the old database
+     */
+    private String searchForFile(String name) {
+        // Initialize the file chooser and only allow hash databases to be opened
+        JFileChooser fc = new JFileChooser();
+        fc.setDragEnabled(false);
+        fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
+        String[] EXTENSION = new String[] { "txt", "idx", "hash", "Hash" };
+        FileNameExtensionFilter filter = new FileNameExtensionFilter(
+                "Hash Database File", EXTENSION);
+        fc.setFileFilter(filter);
+        fc.setMultiSelectionEnabled(false);
+
+        int retval = fc.showOpenDialog(null);
+        // If the user selects an appropriate file
+        if (retval == JFileChooser.APPROVE_OPTION) {
+            File f = fc.getSelectedFile();
+            try {
+                String filePath = f.getCanonicalPath();
+                if (HashDb.isIndexPath(filePath)) {
+                    filePath = HashDb.toDatabasePath(filePath);
+                }
+                String derivedName = SleuthkitJNI.getDatabaseName(filePath);
+                // If the database has the same name as before, return it
+                if(derivedName.equals(name)) {
+                    return filePath;
+                } else {
+                    int tryAgain = JOptionPane.showConfirmDialog(null, "Database file cannot be added because it does not have the same name as the original.\n" +
+                            "Would you like to try a different database?", "Invalid File", JOptionPane.YES_NO_OPTION);
+                    if (tryAgain == JOptionPane.YES_OPTION) {
+                        return searchForFile(name);
+                    } else {
+                        return null;
+                    }
+                }
+            } catch (IOException ex) {
+                logger.log(Level.WARNING, "Couldn't get selected file path.", ex);
+            } catch (TskCoreException ex) {
+                int tryAgain = JOptionPane.showConfirmDialog(null, "Database file you chose cannot be opened.\n" + "If it was just an index, please try to recreate it from the database.\n" +
+                        "Would you like to choose another database?", "Invalid File", JOptionPane.YES_NO_OPTION);
+                if (tryAgain == JOptionPane.YES_OPTION) {
+                    return searchForFile(name);
+                } else {
+                    return null;
+                }
+            }
+        }
+        // Otherwise the user cancelled, so delete the missing entry
+        return null;
+    }
+
+    private boolean setsFileExists() {
+        File f = new File(xmlFile);
+        return f.exists() && f.canRead() && f.canWrite();
+    }
+    
+    
+}
-- 
GitLab