Line endings fix

This commit is contained in:
Richard Cordovano 2013-11-04 10:05:46 -05:00
parent d43343ebd7
commit 64bc2a2faa
3 changed files with 792 additions and 792 deletions

View File

@ -1,64 +1,64 @@
OpenIDE-Module-Display-Category=Ingest Module OpenIDE-Module-Display-Category=Ingest Module
OpenIDE-Module-Long-Description=\ OpenIDE-Module-Long-Description=\
Hash Database ingest module. \n\n\ 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 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. 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 OpenIDE-Module-Name=HashDatabase
HashDbSimplePanel.knownLabel.text=NSRL Database: HashDbSimplePanel.knownLabel.text=NSRL Database:
HashDbSimplePanel.notableLabel.text=Known Bad Database(s): HashDbSimplePanel.notableLabel.text=Known Bad Database(s):
HashDbSimplePanel.knownValLabel.text=- HashDbSimplePanel.knownValLabel.text=-
HashDbSimplePanel.notableValLabel.text=- HashDbSimplePanel.notableValLabel.text=-
HashDbSimplePanel.jLabel1.text=Enable known bad databases for ingest: HashDbSimplePanel.jLabel1.text=Enable known bad databases for ingest:
HashDbAddDatabaseDialog.cancelButton.text=Cancel HashDbAddDatabaseDialog.cancelButton.text=Cancel
HashDbAddDatabaseDialog.okButton.text=OK HashDbAddDatabaseDialog.okButton.text=OK
HashDbAddDatabaseDialog.nsrlRadioButton.text=NSRL HashDbAddDatabaseDialog.nsrlRadioButton.text=NSRL
HashDbAddDatabaseDialog.knownBadRadioButton.text=Known Bad HashDbAddDatabaseDialog.knownBadRadioButton.text=Known Bad
HashDbAddDatabaseDialog.databasePathTextField.text= HashDbAddDatabaseDialog.databasePathTextField.text=
HashDbAddDatabaseDialog.browseButton.text=Browse HashDbAddDatabaseDialog.browseButton.text=Browse
HashDbAddDatabaseDialog.jLabel1.text=Enter the name of the database: HashDbAddDatabaseDialog.jLabel1.text=Enter the name of the database:
HashDbAddDatabaseDialog.databaseNameTextField.text= HashDbAddDatabaseDialog.databaseNameTextField.text=
HashDbAddDatabaseDialog.jLabel2.text=Select the type of database: HashDbAddDatabaseDialog.jLabel2.text=Select the type of database:
HashDbAddDatabaseDialog.useForIngestCheckbox.text=Enable for ingest HashDbAddDatabaseDialog.useForIngestCheckbox.text=Enable for ingest
HashDbAddDatabaseDialog.sendInboxMessagesCheckbox.text=Enable sending messages to inbox during ingest HashDbAddDatabaseDialog.sendInboxMessagesCheckbox.text=Enable sending messages to inbox during ingest
HashDbSearchPanel.hashTable.columnModel.title0=MD5 Hashes HashDbSearchPanel.hashTable.columnModel.title0=MD5 Hashes
HashDbSearchPanel.hashTable.columnModel.title3=Title 4 HashDbSearchPanel.hashTable.columnModel.title3=Title 4
HashDbSearchPanel.hashTable.columnModel.title2=Title 3 HashDbSearchPanel.hashTable.columnModel.title2=Title 3
HashDbSearchPanel.hashTable.columnModel.title1=Title 2 HashDbSearchPanel.hashTable.columnModel.title1=Title 2
HashDbSearchPanel.addButton.text=Add Hash HashDbSearchPanel.addButton.text=Add Hash
HashDbSearchPanel.hashField.text= HashDbSearchPanel.hashField.text=
HashDbSearchPanel.hashLabel.text=MD5 hash: HashDbSearchPanel.hashLabel.text=MD5 hash:
HashDbSearchPanel.searchButton.text=Search HashDbSearchPanel.searchButton.text=Search
HashDbSearchPanel.removeButton.text=Remove Selected HashDbSearchPanel.removeButton.text=Remove Selected
HashDbSearchPanel.titleLabel.text=Search for files with the following MD5 hash(es): HashDbSearchPanel.titleLabel.text=Search for files with the following MD5 hash(es):
HashDbSearchPanel.errorField.text=Error: Not all files have been hashed. HashDbSearchPanel.errorField.text=Error: Not all files have been hashed.
HashDbSearchPanel.saveBox.text=Remember Hashes HashDbSearchPanel.saveBox.text=Remember Hashes
HashDbSearchPanel.cancelButton.text=Cancel HashDbSearchPanel.cancelButton.text=Cancel
HashDbSimplePanel.calcHashesButton.text=Calculate hashes even if no hash database is selected HashDbSimplePanel.calcHashesButton.text=Calculate hashes even if no hash database is selected
HashDbSimplePanel.nsrlDbLabel.text=NSRL Database: HashDbSimplePanel.nsrlDbLabel.text=NSRL Database:
HashDbSimplePanel.nsrlDbLabelVal.text=- HashDbSimplePanel.nsrlDbLabelVal.text=-
HashDbManagementPanel.hashDbIndexStatusLabel.text=No database selected HashDbManagementPanel.hashDbIndexStatusLabel.text=No database selected
HashDbManagementPanel.jLabel2.text=Name: HashDbManagementPanel.jLabel2.text=Name:
HashDbManagementPanel.showInboxMessagesCheckBox.text=Enable sending messages to inbox during ingest HashDbManagementPanel.showInboxMessagesCheckBox.text=Enable sending messages to inbox during ingest
HashDbManagementPanel.useForIngestCheckbox.text=Enable for ingest HashDbManagementPanel.useForIngestCheckbox.text=Enable for ingest
HashDbManagementPanel.indexButton.text=Index HashDbManagementPanel.indexButton.text=Index
HashDbManagementPanel.indexLabel.text=Index Status: HashDbManagementPanel.indexLabel.text=Index Status:
HashDbManagementPanel.optionsLabel.text=Options HashDbManagementPanel.optionsLabel.text=Options
HashDbManagementPanel.jLabel4.text=Location: HashDbManagementPanel.jLabel4.text=Location:
HashDbManagementPanel.jLabel6.text=Type: HashDbManagementPanel.jLabel6.text=Type:
HashDbManagementPanel.ingestWarningLabel.text=Ingest is ongoing, some settings will be unavailable until it finishes. HashDbManagementPanel.ingestWarningLabel.text=Ingest is ongoing, some settings will be unavailable until it finishes.
HashDbManagementPanel.hashDbTypeLabel.text=No database selected HashDbManagementPanel.hashDbTypeLabel.text=No database selected
HashDbManagementPanel.typeLabel.text=Type: HashDbManagementPanel.typeLabel.text=Type:
HashDbManagementPanel.deleteButton.text=Delete Database HashDbManagementPanel.deleteButton.text=Delete Database
HashDbManagementPanel.importButton.text=Import Database HashDbManagementPanel.importButton.text=Import Database
HashDbManagementPanel.hashDbNameLabel.text=No database selected HashDbManagementPanel.hashDbNameLabel.text=No database selected
HashDbManagementPanel.nameLabel.text=Name: HashDbManagementPanel.nameLabel.text=Name:
HashDbManagementPanel.jButton3.text=Import Database HashDbManagementPanel.jButton3.text=Import Database
HashDbManagementPanel.locationLabel.text=Location: HashDbManagementPanel.locationLabel.text=Location:
HashDbManagementPanel.hashDbLocationLabel.text=No database selected HashDbManagementPanel.hashDbLocationLabel.text=No database selected
HashDbManagementPanel.informationLabel.text=Information HashDbManagementPanel.informationLabel.text=Information
HashDbManagementPanel.hashDatabasesLabel.text=Hash Databases: HashDbManagementPanel.hashDatabasesLabel.text=Hash Databases:
OpenIDE-Module-Short-Description=Hash Database Ingest Module and hash db tools OpenIDE-Module-Short-Description=Hash Database Ingest Module and hash db tools
ModalNoButtons.CURRENTLYON_LABEL.text=Currently Indexing x of y 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.GO_GET_COFFEE_LABEL.text=Hash databases are currently being indexed, this may take some time.
ModalNoButtons.CURRENTDB_LABEL.text=(CurrentDb) ModalNoButtons.CURRENTDB_LABEL.text=(CurrentDb)
ModalNoButtons.CANCEL_BUTTON.text=Cancel ModalNoButtons.CANCEL_BUTTON.text=Cancel

View File

@ -1,304 +1,304 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011 Basis Technology Corp. * Copyright 2011 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.sleuthkit.autopsy.hashdatabase; package org.sleuthkit.autopsy.hashdatabase;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport; import java.beans.PropertyChangeSupport;
import java.io.File; import java.io.File;
import java.util.List; import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.SwingWorker; import javax.swing.SwingWorker;
import org.netbeans.api.progress.ProgressHandle; import org.netbeans.api.progress.ProgressHandle;
import org.netbeans.api.progress.ProgressHandleFactory; import org.netbeans.api.progress.ProgressHandleFactory;
import org.openide.util.Cancellable; import org.openide.util.Cancellable;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.SleuthkitJNI; import org.sleuthkit.datamodel.SleuthkitJNI;
import org.sleuthkit.datamodel.TskException; import org.sleuthkit.datamodel.TskException;
/** /**
* Hash database representation of NSRL and Known Bad hash databases * Hash database representation of NSRL and Known Bad hash databases
* with indexing capability * with indexing capability
* *
*/ */
public class HashDb implements Comparable<HashDb> { public class HashDb implements Comparable<HashDb> {
enum EVENT {INDEXING_DONE }; enum EVENT {INDEXING_DONE };
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
public enum DBType{ public enum DBType{
NSRL("NSRL"), KNOWN_BAD("Known Bad"); NSRL("NSRL"), KNOWN_BAD("Known Bad");
private String displayName; private String displayName;
private DBType(String displayName) { private DBType(String displayName) {
this.displayName = displayName; this.displayName = displayName;
} }
public String getDisplayName() { public String getDisplayName() {
return this.displayName; return this.displayName;
} }
} }
// Suffix added to the end of a database name to get its index file // Suffix added to the end of a database name to get its index file
private static final String INDEX_SUFFIX = "-md5.idx"; private static final String INDEX_SUFFIX = "-md5.idx";
private String name; private String name;
private List<String> databasePaths; // TODO: Length limited to one for now... private List<String> databasePaths; // TODO: Length limited to one for now...
private boolean useForIngest; private boolean useForIngest;
private boolean showInboxMessages; private boolean showInboxMessages;
private boolean indexing; private boolean indexing;
private DBType type; private DBType type;
public HashDb(String name, List<String> databasePaths, boolean useForIngest, boolean showInboxMessages, DBType type) { public HashDb(String name, List<String> databasePaths, boolean useForIngest, boolean showInboxMessages, DBType type) {
this.name = name; this.name = name;
this.databasePaths = databasePaths; this.databasePaths = databasePaths;
this.useForIngest = useForIngest; this.useForIngest = useForIngest;
this.showInboxMessages = showInboxMessages; this.showInboxMessages = showInboxMessages;
this.type = type; this.type = type;
this.indexing = false; this.indexing = false;
} }
void addPropertyChangeListener(PropertyChangeListener pcl) { void addPropertyChangeListener(PropertyChangeListener pcl) {
pcs.addPropertyChangeListener(pcl); pcs.addPropertyChangeListener(pcl);
} }
void removePropertyChangeListener(PropertyChangeListener pcl) { void removePropertyChangeListener(PropertyChangeListener pcl) {
pcs.removePropertyChangeListener(pcl); pcs.removePropertyChangeListener(pcl);
} }
boolean getUseForIngest() { boolean getUseForIngest() {
return useForIngest; return useForIngest;
} }
boolean getShowInboxMessages() { boolean getShowInboxMessages() {
return showInboxMessages; return showInboxMessages;
} }
DBType getDbType() { DBType getDbType() {
return type; return type;
} }
String getName() { String getName() {
return name; return name;
} }
List<String> getDatabasePaths() { List<String> getDatabasePaths() {
return databasePaths; return databasePaths;
} }
void setUseForIngest(boolean useForIngest) { void setUseForIngest(boolean useForIngest) {
this.useForIngest = useForIngest; this.useForIngest = useForIngest;
} }
void setShowInboxMessages(boolean showInboxMessages) { void setShowInboxMessages(boolean showInboxMessages) {
this.showInboxMessages = showInboxMessages; this.showInboxMessages = showInboxMessages;
} }
void setName(String name) { void setName(String name) {
this.name = name; this.name = name;
} }
void setDatabasePaths(List<String> databasePaths) { void setDatabasePaths(List<String> databasePaths) {
this.databasePaths = databasePaths; this.databasePaths = databasePaths;
} }
void setDbType(DBType type) { void setDbType(DBType type) {
this.type = type; this.type = type;
} }
/** /**
* Checks if the database exists. * Checks if the database exists.
* @return true if a file exists at the database path, else false * @return true if a file exists at the database path, else false
*/ */
boolean databaseExists() { boolean databaseExists() {
return databaseFile().exists(); return databaseFile().exists();
} }
/** /**
* Checks if Sleuth Kit can open the index for the database path. * Checks if Sleuth Kit can open the index for the database path.
* @return true if the index was found and opened successfully, else false * @return true if the index was found and opened successfully, else false
*/ */
boolean indexExists() { boolean indexExists() {
try { try {
return hasIndex(databasePaths.get(0)); // TODO: support multiple paths return hasIndex(databasePaths.get(0)); // TODO: support multiple paths
} catch (TskException ex) { } catch (TskException ex) {
Logger.getLogger(this.getClass().getName()).log(Level.WARNING, "Error checking if index exists.", ex); Logger.getLogger(this.getClass().getName()).log(Level.WARNING, "Error checking if index exists.", ex);
return false; return false;
} }
} }
/** /**
* Gets the database file. * Gets the database file.
* @return a File initialized with the database path * @return a File initialized with the database path
*/ */
File databaseFile() { File databaseFile() {
return new File(databasePaths.get(0)); // TODO: support multiple paths return new File(databasePaths.get(0)); // TODO: support multiple paths
} }
/** /**
* Gets the index file * Gets the index file
* @return a File initialized with an index path derived from the database * @return a File initialized with an index path derived from the database
* path * path
*/ */
File indexFile() { File indexFile() {
return new File(toIndexPath(databasePaths.get(0))); // TODO: support multiple paths return new File(toIndexPath(databasePaths.get(0))); // TODO: support multiple paths
} }
/** /**
* Checks if the index file is older than the database file * 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 * @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 * path, and the index file has an older modified-time than the database
* file, else false * file, else false
*/ */
boolean isOutdated() { boolean isOutdated() {
File i = indexFile(); File i = indexFile();
File db = databaseFile(); File db = databaseFile();
return i.exists() && db.exists() && isOlderThan(i, db); return i.exists() && db.exists() && isOlderThan(i, db);
} }
/** /**
* Checks if the database is being indexed * Checks if the database is being indexed
*/ */
boolean isIndexing() { boolean isIndexing() {
return indexing; return indexing;
} }
/** /**
* Returns the status of the HashDb as determined from indexExists(), * Returns the status of the HashDb as determined from indexExists(),
* databaseExists(), and isOutdated() * databaseExists(), and isOutdated()
* @return IndexStatus enum according to their definitions * @return IndexStatus enum according to their definitions
*/ */
IndexStatus status() { IndexStatus status() {
boolean i = this.indexExists(); boolean i = this.indexExists();
boolean db = this.databaseExists(); boolean db = this.databaseExists();
if(indexing) if(indexing)
return IndexStatus.INDEXING; return IndexStatus.INDEXING;
if (i) { if (i) {
if (db) { if (db) {
return this.isOutdated() ? IndexStatus.INDEX_OUTDATED : IndexStatus.INDEX_CURRENT; return this.isOutdated() ? IndexStatus.INDEX_OUTDATED : IndexStatus.INDEX_CURRENT;
} else { } else {
return IndexStatus.NO_DB; return IndexStatus.NO_DB;
} }
} else { } else {
return db ? IndexStatus.NO_INDEX : IndexStatus.NONE; return db ? IndexStatus.NO_INDEX : IndexStatus.NONE;
} }
} }
/** /**
* Tries to index the database (overwrites any existing index) * Tries to index the database (overwrites any existing index)
* @throws TskException if an error occurs in the SleuthKit bindings * @throws TskException if an error occurs in the SleuthKit bindings
*/ */
void createIndex() throws TskException { void createIndex() throws TskException {
indexing = true; indexing = true;
CreateIndex creator = new CreateIndex(); CreateIndex creator = new CreateIndex();
creator.execute(); creator.execute();
} }
/** /**
* Checks if one file is older than an other * Checks if one file is older than an other
* @param a first file * @param a first file
* @param b second file * @param b second file
* @return true if the first file's last modified data is before the second * @return true if the first file's last modified data is before the second
* file's last modified date * file's last modified date
*/ */
private static boolean isOlderThan(File a, File b) { private static boolean isOlderThan(File a, File b) {
return a.lastModified() < b.lastModified(); return a.lastModified() < b.lastModified();
} }
/** /**
* Determines if a path points to an index by checking the suffix * Determines if a path points to an index by checking the suffix
* @param path * @param path
* @return true if index * @return true if index
*/ */
static boolean isIndexPath(String path) { static boolean isIndexPath(String path) {
return path.endsWith(INDEX_SUFFIX); return path.endsWith(INDEX_SUFFIX);
} }
/** /**
* Derives database path from an image path by removing the suffix. * Derives database path from an image path by removing the suffix.
* @param indexPath * @param indexPath
* @return * @return
*/ */
static String toDatabasePath(String indexPath) { static String toDatabasePath(String indexPath) {
return indexPath.substring(0, indexPath.lastIndexOf(INDEX_SUFFIX)); return indexPath.substring(0, indexPath.lastIndexOf(INDEX_SUFFIX));
} }
/** /**
* Derives image path from an database path by appending the suffix. * Derives image path from an database path by appending the suffix.
* @param databasePath * @param databasePath
* @return * @return
*/ */
static String toIndexPath(String databasePath) { static String toIndexPath(String databasePath) {
return databasePath.concat(INDEX_SUFFIX); return databasePath.concat(INDEX_SUFFIX);
} }
/** /**
* Calls Sleuth Kit method via JNI to determine whether there is an * Calls Sleuth Kit method via JNI to determine whether there is an
* index for the given path * index for the given path
* @param databasePath path Path for the database the index is of * @param databasePath path Path for the database the index is of
* (database doesn't have to actually exist)' * (database doesn't have to actually exist)'
* @return true if index exists * @return true if index exists
* @throws TskException if there is an error in the JNI call * @throws TskException if there is an error in the JNI call
*/ */
static boolean hasIndex(String databasePath) throws TskException { static boolean hasIndex(String databasePath) throws TskException {
return SleuthkitJNI.lookupIndexExists(databasePath); return SleuthkitJNI.lookupIndexExists(databasePath);
} }
@Override @Override
public int compareTo(HashDb o) { public int compareTo(HashDb o) {
return this.name.compareTo(o.name); return this.name.compareTo(o.name);
} }
/* Thread that creates a database's index */ /* Thread that creates a database's index */
private class CreateIndex extends SwingWorker<Object,Void> { private class CreateIndex extends SwingWorker<Object,Void> {
private ProgressHandle progress; private ProgressHandle progress;
CreateIndex(){}; CreateIndex(){};
@Override @Override
protected Object doInBackground() throws Exception { protected Object doInBackground() throws Exception {
progress = ProgressHandleFactory.createHandle("Indexing " + name); progress = ProgressHandleFactory.createHandle("Indexing " + name);
/** We need proper cancel support in TSK to make the task cancellable /** We need proper cancel support in TSK to make the task cancellable
new Cancellable() { new Cancellable() {
Override Override
public boolean cancel() { public boolean cancel() {
return CreateIndex.this.cancel(true); return CreateIndex.this.cancel(true);
} }
}); });
*/ */
progress.start(); progress.start();
progress.switchToIndeterminate(); progress.switchToIndeterminate();
SleuthkitJNI.createLookupIndex(databasePaths.get(0)); SleuthkitJNI.createLookupIndex(databasePaths.get(0));
return null; return null;
} }
/* clean up or start the worker threads */ /* clean up or start the worker threads */
@Override @Override
protected void done() { protected void done() {
indexing = false; indexing = false;
progress.finish(); progress.finish();
pcs.firePropertyChange(EVENT.INDEXING_DONE.toString(), null, name); pcs.firePropertyChange(EVENT.INDEXING_DONE.toString(), null, name);
} }
} }
} }

View File

@ -1,425 +1,425 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011 Basis Technology Corp. * Copyright 2011 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.sleuthkit.autopsy.hashdatabase; package org.sleuthkit.autopsy.hashdatabase;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.JFileChooser; import javax.swing.JFileChooser;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.filechooser.FileNameExtensionFilter;
import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.ParserConfigurationException;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.PlatformUtil; import org.sleuthkit.autopsy.coreutils.PlatformUtil;
import org.sleuthkit.autopsy.coreutils.XMLUtil; import org.sleuthkit.autopsy.coreutils.XMLUtil;
import org.sleuthkit.autopsy.hashdatabase.HashDb.DBType; import org.sleuthkit.autopsy.hashdatabase.HashDb.DBType;
import org.sleuthkit.datamodel.SleuthkitJNI; import org.sleuthkit.datamodel.SleuthkitJNI;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import org.w3c.dom.Element; import org.w3c.dom.Element;
import org.w3c.dom.NodeList; import org.w3c.dom.NodeList;
public class HashDbXML { public class HashDbXML {
private static final String ROOT_EL = "hash_sets"; private static final String ROOT_EL = "hash_sets";
private static final String SET_EL = "hash_set"; private static final String SET_EL = "hash_set";
private static final String SET_NAME_ATTR = "name"; private static final String SET_NAME_ATTR = "name";
private static final String SET_TYPE_ATTR = "type"; 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_USE_FOR_INGEST_ATTR = "use_for_ingest";
private static final String SET_SHOW_INBOX_MESSAGES = "show_inbox_messages"; 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_EL = "hash_set_path";
private static final String PATH_NUMBER_ATTR = "number"; private static final String PATH_NUMBER_ATTR = "number";
private static final String CUR_HASHSETS_FILE_NAME = "hashsets.xml"; private static final String CUR_HASHSETS_FILE_NAME = "hashsets.xml";
private static final String XSDFILE = "HashsetsSchema.xsd"; private static final String XSDFILE = "HashsetsSchema.xsd";
private static final String ENCODING = "UTF-8"; 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 CUR_HASHSET_FILE = PlatformUtil.getUserConfigDirectory() + File.separator + CUR_HASHSETS_FILE_NAME;
private static final String SET_CALC = "hash_calculate"; private static final String SET_CALC = "hash_calculate";
private static final String SET_VALUE = "value"; private static final String SET_VALUE = "value";
private static final Logger logger = Logger.getLogger(HashDbXML.class.getName()); private static final Logger logger = Logger.getLogger(HashDbXML.class.getName());
private static HashDbXML currentInstance; private static HashDbXML currentInstance;
private List<HashDb> knownBadSets; private List<HashDb> knownBadSets;
private HashDb nsrlSet; private HashDb nsrlSet;
private String xmlFile; private String xmlFile;
private boolean calculate; private boolean calculate;
private HashDbXML(String xmlFile) { private HashDbXML(String xmlFile) {
knownBadSets = new ArrayList<HashDb>(); knownBadSets = new ArrayList<HashDb>();
this.xmlFile = xmlFile; this.xmlFile = xmlFile;
} }
/** /**
* get instance for managing the current keyword list of the application * get instance for managing the current keyword list of the application
*/ */
static synchronized HashDbXML getCurrent() { static synchronized HashDbXML getCurrent() {
if (currentInstance == null) { if (currentInstance == null) {
currentInstance = new HashDbXML(CUR_HASHSET_FILE); currentInstance = new HashDbXML(CUR_HASHSET_FILE);
currentInstance.reload(); currentInstance.reload();
} }
return currentInstance; return currentInstance;
} }
/** /**
* Get the hash sets * Get the hash sets
*/ */
public List<HashDb> getAllSets() { public List<HashDb> getAllSets() {
List<HashDb> ret = new ArrayList<HashDb>(); List<HashDb> ret = new ArrayList<HashDb>();
if(nsrlSet != null) { if(nsrlSet != null) {
ret.add(nsrlSet); ret.add(nsrlSet);
} }
ret.addAll(knownBadSets); ret.addAll(knownBadSets);
return ret; return ret;
} }
/** /**
* Get the Known Bad sets * Get the Known Bad sets
*/ */
public List<HashDb> getKnownBadSets() { public List<HashDb> getKnownBadSets() {
return knownBadSets; return knownBadSets;
} }
/** /**
* Get the NSRL set * Get the NSRL set
*/ */
public HashDb getNSRLSet() { public HashDb getNSRLSet() {
return nsrlSet; return nsrlSet;
} }
/** /**
* Add a known bad hash set * Add a known bad hash set
*/ */
public void addKnownBadSet(HashDb set) { public void addKnownBadSet(HashDb set) {
knownBadSets.add(set); knownBadSets.add(set);
//save(); //save();
} }
/** /**
* Add a known bad hash set * Add a known bad hash set
*/ */
public void addKnownBadSet(int index, HashDb set) { public void addKnownBadSet(int index, HashDb set) {
knownBadSets.add(index, set); knownBadSets.add(index, set);
//save(); //save();
} }
/** /**
* Set the NSRL hash set (override old set) * Set the NSRL hash set (override old set)
*/ */
public void setNSRLSet(HashDb set) { public void setNSRLSet(HashDb set) {
this.nsrlSet = set; this.nsrlSet = set;
//save(); //save();
} }
/** /**
* Remove a hash known bad set * Remove a hash known bad set
*/ */
public void removeKnownBadSetAt(int index) { public void removeKnownBadSetAt(int index) {
knownBadSets.remove(index); knownBadSets.remove(index);
//save(); //save();
} }
/** /**
* Remove the NSRL database * Remove the NSRL database
*/ */
public void removeNSRLSet() { public void removeNSRLSet() {
this.nsrlSet = null; this.nsrlSet = null;
//save(); //save();
} }
/** /**
* load the file or create new * load the file or create new
*/ */
public void reload() { public void reload() {
boolean created = false; boolean created = false;
//TODO clearing the list causes a bug: we lose track of the state //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 //whether db is being indexed, we should somehow preserve the state when loading new HashDb objects
knownBadSets.clear(); knownBadSets.clear();
nsrlSet = null; nsrlSet = null;
if (!this.setsFileExists()) { if (!this.setsFileExists()) {
//create new if it doesn't exist //create new if it doesn't exist
save(); save();
created = true; created = true;
} }
//load, if fails to load create new; save regardless //load, if fails to load create new; save regardless
load(); load();
if (!created) { if (!created) {
//create new if failed to load //create new if failed to load
save(); save();
} }
} }
/** /**
* Sets the local variable calculate to the given boolean. * Sets the local variable calculate to the given boolean.
* @param set the state to make calculate * @param set the state to make calculate
*/ */
public void setCalculate(boolean set) { public void setCalculate(boolean set) {
this.calculate = set; this.calculate = set;
//save(); //save();
} }
/** /**
* Returns the value of the local boolean calculate. * Returns the value of the local boolean calculate.
* @return true if calculate is true, false otherwise * @return true if calculate is true, false otherwise
*/ */
public boolean getCalculate() { public boolean getCalculate() {
return this.calculate; return this.calculate;
} }
/** /**
* writes out current sets file replacing the last one * writes out current sets file replacing the last one
*/ */
public boolean save() { public boolean save() {
boolean success = false; boolean success = false;
DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance(); DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance();
try { try {
DocumentBuilder docBuilder = dbfac.newDocumentBuilder(); DocumentBuilder docBuilder = dbfac.newDocumentBuilder();
Document doc = docBuilder.newDocument(); Document doc = docBuilder.newDocument();
Element rootEl = doc.createElement(ROOT_EL); Element rootEl = doc.createElement(ROOT_EL);
doc.appendChild(rootEl); doc.appendChild(rootEl);
for (HashDb set : knownBadSets) { for (HashDb set : knownBadSets) {
String useForIngest = Boolean.toString(set.getUseForIngest()); String useForIngest = Boolean.toString(set.getUseForIngest());
String showInboxMessages = Boolean.toString(set.getShowInboxMessages()); String showInboxMessages = Boolean.toString(set.getShowInboxMessages());
List<String> paths = set.getDatabasePaths(); List<String> paths = set.getDatabasePaths();
String type = DBType.KNOWN_BAD.toString(); String type = DBType.KNOWN_BAD.toString();
Element setEl = doc.createElement(SET_EL); Element setEl = doc.createElement(SET_EL);
setEl.setAttribute(SET_NAME_ATTR, set.getName()); setEl.setAttribute(SET_NAME_ATTR, set.getName());
setEl.setAttribute(SET_TYPE_ATTR, type); setEl.setAttribute(SET_TYPE_ATTR, type);
setEl.setAttribute(SET_USE_FOR_INGEST_ATTR, useForIngest); setEl.setAttribute(SET_USE_FOR_INGEST_ATTR, useForIngest);
setEl.setAttribute(SET_SHOW_INBOX_MESSAGES, showInboxMessages); setEl.setAttribute(SET_SHOW_INBOX_MESSAGES, showInboxMessages);
for (int i = 0; i < paths.size(); i++) { for (int i = 0; i < paths.size(); i++) {
String path = paths.get(i); String path = paths.get(i);
Element pathEl = doc.createElement(PATH_EL); Element pathEl = doc.createElement(PATH_EL);
pathEl.setAttribute(PATH_NUMBER_ATTR, Integer.toString(i)); pathEl.setAttribute(PATH_NUMBER_ATTR, Integer.toString(i));
pathEl.setTextContent(path); pathEl.setTextContent(path);
setEl.appendChild(pathEl); setEl.appendChild(pathEl);
} }
rootEl.appendChild(setEl); rootEl.appendChild(setEl);
} }
if(nsrlSet != null) { if(nsrlSet != null) {
String useForIngest = Boolean.toString(nsrlSet.getUseForIngest()); String useForIngest = Boolean.toString(nsrlSet.getUseForIngest());
String showInboxMessages = Boolean.toString(nsrlSet.getShowInboxMessages()); String showInboxMessages = Boolean.toString(nsrlSet.getShowInboxMessages());
List<String> paths = nsrlSet.getDatabasePaths(); List<String> paths = nsrlSet.getDatabasePaths();
String type = DBType.NSRL.toString(); String type = DBType.NSRL.toString();
Element setEl = doc.createElement(SET_EL); Element setEl = doc.createElement(SET_EL);
setEl.setAttribute(SET_NAME_ATTR, nsrlSet.getName()); setEl.setAttribute(SET_NAME_ATTR, nsrlSet.getName());
setEl.setAttribute(SET_TYPE_ATTR, type); setEl.setAttribute(SET_TYPE_ATTR, type);
setEl.setAttribute(SET_USE_FOR_INGEST_ATTR, useForIngest); setEl.setAttribute(SET_USE_FOR_INGEST_ATTR, useForIngest);
setEl.setAttribute(SET_SHOW_INBOX_MESSAGES, showInboxMessages); setEl.setAttribute(SET_SHOW_INBOX_MESSAGES, showInboxMessages);
for (int i = 0; i < paths.size(); i++) { for (int i = 0; i < paths.size(); i++) {
String path = paths.get(i); String path = paths.get(i);
Element pathEl = doc.createElement(PATH_EL); Element pathEl = doc.createElement(PATH_EL);
pathEl.setAttribute(PATH_NUMBER_ATTR, Integer.toString(i)); pathEl.setAttribute(PATH_NUMBER_ATTR, Integer.toString(i));
pathEl.setTextContent(path); pathEl.setTextContent(path);
setEl.appendChild(pathEl); setEl.appendChild(pathEl);
} }
rootEl.appendChild(setEl); rootEl.appendChild(setEl);
} }
String calcValue = Boolean.toString(calculate); String calcValue = Boolean.toString(calculate);
Element setCalc = doc.createElement(SET_CALC); Element setCalc = doc.createElement(SET_CALC);
setCalc.setAttribute(SET_VALUE, calcValue); setCalc.setAttribute(SET_VALUE, calcValue);
rootEl.appendChild(setCalc); rootEl.appendChild(setCalc);
success = XMLUtil.saveDoc(HashDbXML.class, xmlFile, ENCODING, doc); success = XMLUtil.saveDoc(HashDbXML.class, xmlFile, ENCODING, doc);
} catch (ParserConfigurationException e) { } catch (ParserConfigurationException e) {
logger.log(Level.SEVERE, "Error saving hash sets: can't initialize parser.", e); logger.log(Level.SEVERE, "Error saving hash sets: can't initialize parser.", e);
} }
return success; return success;
} }
/** /**
* load and parse XML, then dispose * load and parse XML, then dispose
*/ */
public boolean load() { public boolean load() {
final Document doc = XMLUtil.loadDoc(HashDbXML.class, xmlFile, XSDFILE); final Document doc = XMLUtil.loadDoc(HashDbXML.class, xmlFile, XSDFILE);
if (doc == null) { if (doc == null) {
return false; return false;
} }
Element root = doc.getDocumentElement(); Element root = doc.getDocumentElement();
if (root == null) { if (root == null) {
logger.log(Level.SEVERE, "Error loading hash sets: invalid file format."); logger.log(Level.SEVERE, "Error loading hash sets: invalid file format.");
return false; return false;
} }
NodeList setsNList = root.getElementsByTagName(SET_EL); NodeList setsNList = root.getElementsByTagName(SET_EL);
int numSets = setsNList.getLength(); int numSets = setsNList.getLength();
if(numSets==0) { if(numSets==0) {
logger.log(Level.WARNING, "No element hash_set exists."); logger.log(Level.WARNING, "No element hash_set exists.");
} }
for (int i = 0; i < numSets; ++i) { for (int i = 0; i < numSets; ++i) {
Element setEl = (Element) setsNList.item(i); Element setEl = (Element) setsNList.item(i);
final String name = setEl.getAttribute(SET_NAME_ATTR); final String name = setEl.getAttribute(SET_NAME_ATTR);
final String type = setEl.getAttribute(SET_TYPE_ATTR); final String type = setEl.getAttribute(SET_TYPE_ATTR);
final String useForIngest = setEl.getAttribute(SET_USE_FOR_INGEST_ATTR); final String useForIngest = setEl.getAttribute(SET_USE_FOR_INGEST_ATTR);
final String showInboxMessages = setEl.getAttribute(SET_SHOW_INBOX_MESSAGES); final String showInboxMessages = setEl.getAttribute(SET_SHOW_INBOX_MESSAGES);
Boolean useForIngestBool = Boolean.parseBoolean(useForIngest); Boolean useForIngestBool = Boolean.parseBoolean(useForIngest);
Boolean showInboxMessagesBool = Boolean.parseBoolean(showInboxMessages); Boolean showInboxMessagesBool = Boolean.parseBoolean(showInboxMessages);
List<String> paths = new ArrayList<String>(); List<String> paths = new ArrayList<String>();
// Parse all paths // Parse all paths
NodeList pathsNList = setEl.getElementsByTagName(PATH_EL); NodeList pathsNList = setEl.getElementsByTagName(PATH_EL);
final int numPaths = pathsNList.getLength(); final int numPaths = pathsNList.getLength();
for (int j = 0; j < numPaths; ++j) { for (int j = 0; j < numPaths; ++j) {
Element pathEl = (Element) pathsNList.item(j); Element pathEl = (Element) pathsNList.item(j);
String number = pathEl.getAttribute(PATH_NUMBER_ATTR); String number = pathEl.getAttribute(PATH_NUMBER_ATTR);
String path = pathEl.getTextContent(); String path = pathEl.getTextContent();
// If either the database or it's index exist // If either the database or it's index exist
File database = new File(path); File database = new File(path);
File index = new File(HashDb.toIndexPath(path)); File index = new File(HashDb.toIndexPath(path));
if(database.exists() || index.exists()) { if(database.exists() || index.exists()) {
paths.add(path); paths.add(path);
} else { } else {
// Ask for new path // Ask for new path
int ret = JOptionPane.showConfirmDialog(null, "Database " + name + " could not be found at location\n" int ret = JOptionPane.showConfirmDialog(null, "Database " + name + " could not be found at location\n"
+ path + "\n" + path + "\n"
+ " Would you like to search for the file?", "Missing Database", JOptionPane.YES_NO_OPTION); + " Would you like to search for the file?", "Missing Database", JOptionPane.YES_NO_OPTION);
if (ret == JOptionPane.YES_OPTION) { if (ret == JOptionPane.YES_OPTION) {
String filePath = searchForFile(name); String filePath = searchForFile(name);
if(filePath!=null) { if(filePath!=null) {
paths.add(filePath); paths.add(filePath);
} }
} }
} }
} }
// Check everything was properly set // Check everything was properly set
if(name.isEmpty()) { if(name.isEmpty()) {
logger.log(Level.WARNING, "Name was not set for hash_set at index {0}.", i); logger.log(Level.WARNING, "Name was not set for hash_set at index {0}.", i);
} }
if(type.isEmpty()) { if(type.isEmpty()) {
logger.log(Level.SEVERE, "Type was not set for hash_set at index {0}, cannot make instance of HashDb class.", i); 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 return false; // exit because this causes a fatal error
} }
if(useForIngest.isEmpty()) { if(useForIngest.isEmpty()) {
logger.log(Level.WARNING, "UseForIngest was not set for hash_set at index {0}.", i); logger.log(Level.WARNING, "UseForIngest was not set for hash_set at index {0}.", i);
} }
if(showInboxMessages.isEmpty()) { if(showInboxMessages.isEmpty()) {
logger.log(Level.WARNING, "ShowInboxMessages was not set for hash_set at index {0}.", i); logger.log(Level.WARNING, "ShowInboxMessages was not set for hash_set at index {0}.", i);
} }
if(paths.isEmpty()) { if(paths.isEmpty()) {
logger.log(Level.WARNING, "No paths were set for hash_set at index {0}. Removing the database.", i); logger.log(Level.WARNING, "No paths were set for hash_set at index {0}. Removing the database.", i);
} else { } else {
// No paths for this entry, the user most likely declined to search for them // No paths for this entry, the user most likely declined to search for them
DBType typeDBType = DBType.valueOf(type); DBType typeDBType = DBType.valueOf(type);
HashDb set = new HashDb(name, paths, useForIngestBool, showInboxMessagesBool, typeDBType); HashDb set = new HashDb(name, paths, useForIngestBool, showInboxMessagesBool, typeDBType);
if(typeDBType == DBType.KNOWN_BAD) { if(typeDBType == DBType.KNOWN_BAD) {
knownBadSets.add(set); knownBadSets.add(set);
} else if(typeDBType == DBType.NSRL) { } else if(typeDBType == DBType.NSRL) {
this.nsrlSet = set; this.nsrlSet = set;
} }
} }
} }
NodeList calcList = root.getElementsByTagName(SET_CALC); NodeList calcList = root.getElementsByTagName(SET_CALC);
int numCalc = calcList.getLength(); // Shouldn't be more than 1 int numCalc = calcList.getLength(); // Shouldn't be more than 1
if(numCalc==0) { if(numCalc==0) {
logger.log(Level.WARNING, "No element hash_calculate exists."); logger.log(Level.WARNING, "No element hash_calculate exists.");
} }
for(int i=0; i<numCalc; i++) { for(int i=0; i<numCalc; i++) {
Element calcEl = (Element) calcList.item(i); Element calcEl = (Element) calcList.item(i);
final String value = calcEl.getAttribute(SET_VALUE); final String value = calcEl.getAttribute(SET_VALUE);
calculate = Boolean.parseBoolean(value); calculate = Boolean.parseBoolean(value);
} }
return true; return true;
} }
/** /**
* Ask the user to browse to a new Hash Database file with the same database * 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 * 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 * be added. If the user cancels the search, return null, meaning the user
* would like to remove the entry for the missing database. * would like to remove the entry for the missing database.
* *
* @param name the name of the database to add * @param name the name of the database to add
* @return the file path to the new database, or null if the user wants to * @return the file path to the new database, or null if the user wants to
* delete the old database * delete the old database
*/ */
private String searchForFile(String name) { private String searchForFile(String name) {
// Initialize the file chooser and only allow hash databases to be opened // Initialize the file chooser and only allow hash databases to be opened
JFileChooser fc = new JFileChooser(); JFileChooser fc = new JFileChooser();
fc.setDragEnabled(false); fc.setDragEnabled(false);
fc.setFileSelectionMode(JFileChooser.FILES_ONLY); fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
String[] EXTENSION = new String[] { "txt", "idx", "hash", "Hash" }; String[] EXTENSION = new String[] { "txt", "idx", "hash", "Hash" };
FileNameExtensionFilter filter = new FileNameExtensionFilter( FileNameExtensionFilter filter = new FileNameExtensionFilter(
"Hash Database File", EXTENSION); "Hash Database File", EXTENSION);
fc.setFileFilter(filter); fc.setFileFilter(filter);
fc.setMultiSelectionEnabled(false); fc.setMultiSelectionEnabled(false);
int retval = fc.showOpenDialog(null); int retval = fc.showOpenDialog(null);
// If the user selects an appropriate file // If the user selects an appropriate file
if (retval == JFileChooser.APPROVE_OPTION) { if (retval == JFileChooser.APPROVE_OPTION) {
File f = fc.getSelectedFile(); File f = fc.getSelectedFile();
try { try {
String filePath = f.getCanonicalPath(); String filePath = f.getCanonicalPath();
if (HashDb.isIndexPath(filePath)) { if (HashDb.isIndexPath(filePath)) {
filePath = HashDb.toDatabasePath(filePath); filePath = HashDb.toDatabasePath(filePath);
} }
String derivedName = SleuthkitJNI.getDatabaseName(filePath); String derivedName = SleuthkitJNI.getDatabaseName(filePath);
// If the database has the same name as before, return it // If the database has the same name as before, return it
if(derivedName.equals(name)) { if(derivedName.equals(name)) {
return filePath; return filePath;
} else { } else {
int tryAgain = JOptionPane.showConfirmDialog(null, "Database file cannot be added because it does not have the same name as the original.\n" + 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); "Would you like to try a different database?", "Invalid File", JOptionPane.YES_NO_OPTION);
if (tryAgain == JOptionPane.YES_OPTION) { if (tryAgain == JOptionPane.YES_OPTION) {
return searchForFile(name); return searchForFile(name);
} else { } else {
return null; return null;
} }
} }
} catch (IOException ex) { } catch (IOException ex) {
logger.log(Level.WARNING, "Couldn't get selected file path.", ex); logger.log(Level.WARNING, "Couldn't get selected file path.", ex);
} catch (TskCoreException 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" + 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); "Would you like to choose another database?", "Invalid File", JOptionPane.YES_NO_OPTION);
if (tryAgain == JOptionPane.YES_OPTION) { if (tryAgain == JOptionPane.YES_OPTION) {
return searchForFile(name); return searchForFile(name);
} else { } else {
return null; return null;
} }
} }
} }
// Otherwise the user cancelled, so delete the missing entry // Otherwise the user cancelled, so delete the missing entry
return null; return null;
} }
private boolean setsFileExists() { private boolean setsFileExists() {
File f = new File(xmlFile); File f = new File(xmlFile);
return f.exists() && f.canRead() && f.canWrite(); return f.exists() && f.canRead() && f.canWrite();
} }
} }