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