Autopsy  4.7.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
HashDbManager.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2011-2018 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.modules.hashdatabase;
20 
21 import java.beans.PropertyChangeEvent;
22 import java.beans.PropertyChangeListener;
23 import java.beans.PropertyChangeSupport;
24 import java.io.File;
25 import java.io.IOException;
26 import java.util.ArrayList;
27 import java.util.HashSet;
28 import java.util.List;
29 import java.util.Objects;
30 import java.util.Set;
31 import java.util.concurrent.ExecutionException;
32 import java.util.logging.Level;
33 import javax.swing.JFileChooser;
34 import javax.swing.JOptionPane;
35 import javax.swing.SwingWorker;
36 import javax.swing.filechooser.FileNameExtensionFilter;
37 import org.apache.commons.io.FilenameUtils;
38 import org.netbeans.api.progress.ProgressHandle;
39 import org.openide.util.NbBundle;
40 import org.openide.util.NbBundle.Messages;
41 import org.openide.windows.WindowManager;
52 import org.sleuthkit.datamodel.AbstractFile;
53 import org.sleuthkit.datamodel.Content;
54 import org.sleuthkit.datamodel.HashEntry;
55 import org.sleuthkit.datamodel.HashHitInfo;
56 import org.sleuthkit.datamodel.SleuthkitJNI;
57 import org.sleuthkit.datamodel.TskCoreException;
58 import org.sleuthkit.datamodel.TskData;
59 
64 public class HashDbManager implements PropertyChangeListener {
65 
66  private static final String HASH_DATABASE_FILE_EXTENSON = "kdb"; //NON-NLS
67  private static HashDbManager instance = null;
68  private List<HashDb> hashSets = new ArrayList<>();
69  private Set<String> hashSetNames = new HashSet<>();
70  private Set<String> hashSetPaths = new HashSet<>();
71  PropertyChangeSupport changeSupport = new PropertyChangeSupport(HashDbManager.class);
72  private static final Logger logger = Logger.getLogger(HashDbManager.class.getName());
73  private boolean allDatabasesLoadedCorrectly = false;
74 
80  public enum SetEvt {
81 
82  DB_ADDED, DB_DELETED, DB_INDEXED
83  };
84 
90  public static synchronized HashDbManager getInstance() {
91  if (instance == null) {
92  instance = new HashDbManager();
93  }
94  return instance;
95  }
96 
97  public synchronized void addPropertyChangeListener(PropertyChangeListener listener) {
98  changeSupport.addPropertyChangeListener(listener);
99  }
100 
101  public synchronized void removePropertyChangeListener(PropertyChangeListener listener) {
102  changeSupport.removePropertyChangeListener(listener);
103  }
104 
105  synchronized boolean verifyAllDatabasesLoadedCorrectly(){
107  }
108 
109  private HashDbManager() {
111  }
112 
118  static String getHashDatabaseFileExtension() {
120  }
121 
122  public class HashDbManagerException extends Exception {
123 
124  private static final long serialVersionUID = 1L;
125 
126  private HashDbManagerException(String message) {
127  super(message);
128  }
129 
130  private HashDbManagerException(String message, Throwable exception) {
131  super(message, exception);
132  }
133  }
134 
154  public synchronized HashDb addExistingHashDatabase(String hashSetName, String path, boolean searchDuringIngest, boolean sendIngestMessages, HashDb.KnownFilesType knownFilesType) throws HashDbManagerException {
155  HashDb hashDb = null;
156  hashDb = this.addExistingHashDatabaseNoSave(hashSetName, path, searchDuringIngest, sendIngestMessages, knownFilesType);
157  this.save();
158  return hashDb;
159  }
160 
161  synchronized HashDb addExistingHashDatabaseNoSave(String hashSetName, String path, boolean searchDuringIngest, boolean sendIngestMessages, HashDb.KnownFilesType knownFilesType) throws HashDbManagerException {
162  HashDb hashDb = null;
163  try {
164  if (!new File(path).exists()) {
165  throw new HashDbManagerException(NbBundle.getMessage(HashDbManager.class, "HashDbManager.hashDbDoesNotExistExceptionMsg", path));
166  }
167 
168  if (hashSetPaths.contains(path)) {
169  throw new HashDbManagerException(NbBundle.getMessage(HashDbManager.class, "HashDbManager.hashDbAlreadyAddedExceptionMsg", path));
170  }
171 
172  if (hashSetNames.contains(hashSetName)) {
173  throw new HashDbManagerException(NbBundle.getMessage(HashDbManager.class, "HashDbManager.duplicateHashSetNameExceptionMsg", hashSetName));
174  }
175 
176  hashDb = addHashDatabase(SleuthkitJNI.openHashDatabase(path), hashSetName, searchDuringIngest, sendIngestMessages, knownFilesType);
177  } catch (TskCoreException ex) {
178  throw new HashDbManagerException(ex.getMessage());
179  }
180  return hashDb;
181  }
182 
201  public synchronized HashDb addNewHashDatabase(String hashSetName, String path, boolean searchDuringIngest, boolean sendIngestMessages,
202  HashDb.KnownFilesType knownFilesType) throws HashDbManagerException {
203 
204  HashDb hashDb = null;
205  hashDb = this.addNewHashDatabaseNoSave(hashSetName, path, searchDuringIngest, sendIngestMessages, knownFilesType);
206 
207  this.save();
208 
209  return hashDb;
210  }
211 
212  public synchronized HashDb addNewHashDatabaseNoSave(String hashSetName, String path, boolean searchDuringIngest, boolean sendIngestMessages,
213  HashDb.KnownFilesType knownFilesType) throws HashDbManagerException {
214  HashDb hashDb = null;
215  try {
216  File file = new File(path);
217  if (file.exists()) {
218  throw new HashDbManagerException(NbBundle.getMessage(HashDbManager.class, "HashDbManager.hashDbFileExistsExceptionMsg", path));
219  }
220  if (!FilenameUtils.getExtension(file.getName()).equalsIgnoreCase(HASH_DATABASE_FILE_EXTENSON)) {
221  throw new HashDbManagerException(NbBundle.getMessage(HashDbManager.class, "HashDbManager.illegalHashDbFileNameExtensionMsg",
222  getHashDatabaseFileExtension()));
223  }
224 
225  if (hashSetPaths.contains(path)) {
226  throw new HashDbManagerException(NbBundle.getMessage(HashDbManager.class, "HashDbManager.hashDbAlreadyAddedExceptionMsg", path));
227  }
228 
229  if (hashSetNames.contains(hashSetName)) {
230  throw new HashDbManagerException(NbBundle.getMessage(HashDbManager.class, "HashDbManager.duplicateHashSetNameExceptionMsg", hashSetName));
231  }
232 
233  hashDb = addHashDatabase(SleuthkitJNI.createHashDatabase(path), hashSetName, searchDuringIngest, sendIngestMessages, knownFilesType);
234  } catch (TskCoreException ex) {
235  throw new HashDbManagerException(ex.getMessage());
236  }
237  return hashDb;
238  }
239 
240  private SleuthkitHashSet addHashDatabase(int handle, String hashSetName, boolean searchDuringIngest, boolean sendIngestMessages, HashDb.KnownFilesType knownFilesType) throws TskCoreException {
241  // Wrap an object around the handle.
242  SleuthkitHashSet hashDb = new SleuthkitHashSet(handle, hashSetName, searchDuringIngest, sendIngestMessages, knownFilesType);
243 
244  // Get the indentity data before updating the collections since the
245  // accessor methods may throw.
246  String databasePath = hashDb.getDatabasePath();
247  String indexPath = hashDb.getIndexPath();
248 
249  // Update the collections used to ensure that hash set names are unique
250  // and the same database is not added to the configuration more than once.
251  hashSetNames.add(hashDb.getHashSetName());
252  if (!databasePath.equals("None")) { //NON-NLS
253  hashSetPaths.add(databasePath);
254  }
255  if (!indexPath.equals("None")) { //NON-NLS
256  hashSetPaths.add(indexPath);
257  }
258 
259  // Add the hash database to the collection
260  hashSets.add(hashDb);
261 
262  // Let any external listeners know that there's a new set
263  try {
264  changeSupport.firePropertyChange(SetEvt.DB_ADDED.toString(), null, hashSetName);
265  } catch (Exception e) {
266  logger.log(Level.SEVERE, "HashDbManager listener threw exception", e); //NON-NLS
268  NbBundle.getMessage(this.getClass(), "HashDbManager.moduleErr"),
269  NbBundle.getMessage(this.getClass(), "HashDbManager.moduleErrorListeningToUpdatesMsg"),
271  }
272  return hashDb;
273  }
274 
275  CentralRepoHashSet addExistingCentralRepoHashSet(String hashSetName, String version, int referenceSetID,
276  boolean searchDuringIngest, boolean sendIngestMessages, HashDb.KnownFilesType knownFilesType,
277  boolean readOnly) throws TskCoreException{
278 
279  if(! EamDb.isEnabled()){
280  throw new TskCoreException("Could not load central repository hash set " + hashSetName + " - central repository is not enabled");
281  }
282 
283  CentralRepoHashSet db = new CentralRepoHashSet(hashSetName, version, referenceSetID, searchDuringIngest,
284  sendIngestMessages, knownFilesType, readOnly);
285 
286  if(! db.isValid()){
287  throw new TskCoreException("Error finding hash set " + hashSetName + " in central repository");
288  }
289 
290  // Add the hash database to the collection
291  hashSets.add(db);
292 
293  // Let any external listeners know that there's a new set
294  try {
295  changeSupport.firePropertyChange(SetEvt.DB_ADDED.toString(), null, hashSetName);
296  } catch (Exception e) {
297  logger.log(Level.SEVERE, "HashDbManager listener threw exception", e); //NON-NLS
298  MessageNotifyUtil.Notify.show(
299  NbBundle.getMessage(this.getClass(), "HashDbManager.moduleErr"),
300  NbBundle.getMessage(this.getClass(), "HashDbManager.moduleErrorListeningToUpdatesMsg"),
301  MessageNotifyUtil.MessageType.ERROR);
302  }
303  return db;
304 
305  }
306 
307  synchronized void indexHashDatabase(SleuthkitHashSet hashDb) {
308  hashDb.addPropertyChangeListener(this);
309  HashDbIndexer creator = new HashDbIndexer(hashDb);
310  creator.execute();
311  }
312 
313  @Override
314  public void propertyChange(PropertyChangeEvent event) {
315  if (event.getPropertyName().equals(SleuthkitHashSet.Event.INDEXING_DONE.name())) {
316  SleuthkitHashSet hashDb = (SleuthkitHashSet) event.getNewValue();
317  if (null != hashDb) {
318  try {
319  String indexPath = hashDb.getIndexPath();
320  if (!indexPath.equals("None")) { //NON-NLS
321  hashSetPaths.add(indexPath);
322  }
323  } catch (TskCoreException ex) {
324  Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error getting index path of " + hashDb.getHashSetName() + " hash set after indexing", ex); //NON-NLS
325  }
326  }
327  }
328  }
329 
338  public synchronized void removeHashDatabase(HashDb hashDb) throws HashDbManagerException {
339  this.removeHashDatabaseNoSave(hashDb);
340  this.save();
341  }
342 
343  public synchronized void removeHashDatabaseNoSave(HashDb hashDb) throws HashDbManagerException {
344  // Don't remove a database if ingest is running
345  boolean ingestIsRunning = IngestManager.getInstance().isIngestRunning();
346  if (ingestIsRunning) {
347  throw new HashDbManagerException(NbBundle.getMessage(this.getClass(), "HashDbManager.ingestRunningExceptionMsg"));
348  }
349  // Remove the database from whichever hash set list it occupies,
350  // and remove its hash set name from the hash set used to ensure unique
351  // hash set names are used, before undertaking These operations will succeed and constitute
352  // a mostly effective removal, even if the subsequent operations fail.
353  String hashSetName = hashDb.getHashSetName();
354  hashSetNames.remove(hashSetName);
355  hashSets.remove(hashDb);
356 
357  // Now undertake the operations that could throw.
358 
359  // Indexing is only relevanet for sleuthkit hashsets
360  if(hashDb instanceof SleuthkitHashSet){
361  SleuthkitHashSet hashDatabase = (SleuthkitHashSet)hashDb;
362  try {
363  if(hashDatabase.hasIndex()){
364  hashSetPaths.remove(hashDatabase.getIndexPath());
365  }
366  } catch (TskCoreException ex) {
367  Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error getting index path of " + hashDatabase.getHashSetName() + " hash set when removing the hash set", ex); //NON-NLS
368  }
369 
370  try {
371  if (!hashDatabase.hasIndexOnly()) {
372  hashSetPaths.remove(hashDatabase.getDatabasePath());
373  }
374  } catch (TskCoreException ex) {
375  Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error getting hash set path of " + hashDatabase.getHashSetName() + " hash set when removing the hash set", ex); //NON-NLS
376  }
377 
378  try {
379  hashDatabase.close();
380  } catch (TskCoreException ex) {
381  Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error closing " + hashDb.getHashSetName() + " hash set when removing the hash set", ex); //NON-NLS
382  }
383  }
384 
385  // Let any external listeners know that a set has been deleted
386  try {
387  changeSupport.firePropertyChange(SetEvt.DB_DELETED.toString(), null, hashSetName);
388  } catch (Exception e) {
389  logger.log(Level.SEVERE, "HashDbManager listener threw exception", e); //NON-NLS
391  NbBundle.getMessage(this.getClass(), "HashDbManager.moduleErr"),
392  NbBundle.getMessage(this.getClass(), "HashDbManager.moduleErrorListeningToUpdatesMsg"),
394  }
395  }
396 
397  void save() throws HashDbManagerException {
398  try {
399  if (!HashLookupSettings.writeSettings(new HashLookupSettings(HashLookupSettings.convertHashSetList(this.hashSets)))) {
400  throw new HashDbManagerException(NbBundle.getMessage(this.getClass(), "HashDbManager.saveErrorExceptionMsg"));
401  }
402  } catch (HashLookupSettings.HashLookupSettingsException ex) {
403  throw new HashDbManagerException(NbBundle.getMessage(this.getClass(), "HashDbManager.saveErrorExceptionMsg"));
404  }
405  }
406 
414  public synchronized List<HashDb> getAllHashSets() {
415  try{
417  } catch (TskCoreException ex){
418  Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error loading central repository hash sets", ex); //NON-NLS
419  }
420 
421  List<HashDb> hashDbs = new ArrayList<>();
422  hashDbs.addAll(this.hashSets);
423  return hashDbs;
424  }
425 
431  public synchronized List<HashDb> getKnownFileHashSets() {
432  List<HashDb> hashDbs = new ArrayList<>();
433  try{
435  } catch (TskCoreException ex){
436  Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error loading central repository hash sets", ex); //NON-NLS
437  }
438  this.hashSets.stream().filter((db) -> (db.getKnownFilesType() == HashDb.KnownFilesType.KNOWN)).forEach((db) -> {
439  hashDbs.add(db);
440  });
441  return hashDbs;
442  }
443 
449  public synchronized List<HashDb> getKnownBadFileHashSets() {
450  List<HashDb> hashDbs = new ArrayList<>();
451  try{
453  } catch (TskCoreException ex){
454  Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error loading central repository hash sets", ex); //NON-NLS
455  }
456  this.hashSets.stream().filter((db) -> (db.getKnownFilesType() == HashDb.KnownFilesType.KNOWN_BAD)).forEach((db) -> {
457  hashDbs.add(db);
458  });
459  return hashDbs;
460  }
461 
467  public synchronized List<HashDb> getUpdateableHashSets() {
468  return getUpdateableHashSets(this.hashSets);
469  }
470 
471  private List<HashDb> getUpdateableHashSets(List<HashDb> hashDbs) {
472  ArrayList<HashDb> updateableDbs = new ArrayList<>();
473  try{
475  } catch (TskCoreException ex){
476  Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error loading central repository hash sets", ex); //NON-NLS
477  }
478  for (HashDb db : hashDbs) {
479  try {
480  if (db.isUpdateable()) {
481  updateableDbs.add(db);
482  }
483  } catch (TskCoreException ex) {
484  Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error checking updateable status of " + db.getHashSetName() + " hash set", ex); //NON-NLS
485  }
486  }
487  return updateableDbs;
488  }
489 
490  private List<HashDbInfo> getCentralRepoHashSetsFromDatabase(){
491  List<HashDbInfo> crHashSets = new ArrayList<>();
492  if(EamDb.isEnabled()){
493  try{
495  for(EamGlobalSet globalSet:crSets){
496 
497  // Defaults for fields not stored in the central repository:
498  // searchDuringIngest: false
499  // sendIngestMessages: true if the hash set is notable
500  boolean sendIngestMessages = convertFileKnown(globalSet.getFileKnownStatus()).equals(HashDb.KnownFilesType.KNOWN_BAD);
501  crHashSets.add(new HashDbInfo(globalSet.getSetName(), globalSet.getVersion(),
502  globalSet.getGlobalSetID(), convertFileKnown(globalSet.getFileKnownStatus()), globalSet.isReadOnly(), false, sendIngestMessages));
503  }
504  } catch (EamDbException ex){
505  Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error loading central repository hash sets", ex); //NON-NLS
506  }
507  }
508  return crHashSets;
509  }
510 
511  private static HashDb.KnownFilesType convertFileKnown(TskData.FileKnown fileKnown){
512  if(fileKnown.equals(TskData.FileKnown.BAD)){
514  }
515  return HashDb.KnownFilesType.KNOWN;
516  }
517 
522  public synchronized void loadLastSavedConfiguration() {
523  closeHashDatabases(this.hashSets);
524  hashSetNames.clear();
525  hashSetPaths.clear();
526 
528  }
529 
530  private void closeHashDatabases(List<HashDb> hashDatabases) {
531  for (HashDb database : hashDatabases) {
532  if(database instanceof SleuthkitHashSet){
533  try {
534  ((SleuthkitHashSet)database).close();
535  } catch (TskCoreException ex) {
536  Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error closing " + database.getHashSetName() + " hash set", ex); //NON-NLS
537  }
538  }
539  }
540  hashDatabases.clear();
541  }
542 
543  private void loadHashsetsConfiguration() {
544  try {
545  HashLookupSettings settings = HashLookupSettings.readSettings();
546  this.configureSettings(settings);
547  } catch (HashLookupSettings.HashLookupSettingsException ex) {
548  Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Could not read Hash lookup settings from disk.", ex);
549  }
550  }
551 
558  @Messages({"# {0} - hash set name", "HashDbManager.noDbPath.message=Couldn't get valid hash set path for: {0}",
559  "HashDbManager.centralRepoLoadError.message=Error loading central repository hash sets"})
560  private void configureSettings(HashLookupSettings settings) {
561  allDatabasesLoadedCorrectly = true;
562  List<HashDbInfo> hashDbInfoList = settings.getHashDbInfo();
563  for (HashDbInfo hashDbInfo : hashDbInfoList) {
564  try {
565  if(hashDbInfo.isFileDatabaseType()){
566  String dbPath = this.getValidFilePath(hashDbInfo.getHashSetName(), hashDbInfo.getPath());
567  if (dbPath != null) {
568  addHashDatabase(SleuthkitJNI.openHashDatabase(dbPath), hashDbInfo.getHashSetName(), hashDbInfo.getSearchDuringIngest(), hashDbInfo.getSendIngestMessages(), hashDbInfo.getKnownFilesType());
569  } else {
570  logger.log(Level.WARNING, Bundle.HashDbManager_noDbPath_message(hashDbInfo.getHashSetName()));
571  allDatabasesLoadedCorrectly = false;
572  }
573  } else {
574  if(EamDb.isEnabled()){
575  addExistingCentralRepoHashSet(hashDbInfo.getHashSetName(), hashDbInfo.getVersion(),
576  hashDbInfo.getReferenceSetID(),
577  hashDbInfo.getSearchDuringIngest(), hashDbInfo.getSendIngestMessages(),
578  hashDbInfo.getKnownFilesType(), hashDbInfo.isReadOnly());
579  }
580  }
581  } catch (TskCoreException ex) {
582  Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error opening hash set", ex); //NON-NLS
583  JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
584  NbBundle.getMessage(this.getClass(),
585  "HashDbManager.unableToOpenHashDbMsg", hashDbInfo.getHashSetName()),
586  NbBundle.getMessage(this.getClass(), "HashDbManager.openHashDbErr"),
587  JOptionPane.ERROR_MESSAGE);
588  allDatabasesLoadedCorrectly = false;
589  }
590  }
591 
592  if(EamDb.isEnabled()){
593  try{
595  } catch (TskCoreException ex){
596  Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error opening hash set", ex); //NON-NLS
597 
598  JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
599  Bundle.HashDbManager_centralRepoLoadError_message(),
600  NbBundle.getMessage(this.getClass(), "HashDbManager.openHashDbErr"),
601  JOptionPane.ERROR_MESSAGE);
602  allDatabasesLoadedCorrectly = false;
603  }
604  }
605 
606  /* NOTE: When RuntimeProperties.coreComponentsAreActive() is "false",
607  I don't think we should overwrite hash db settings file because we
608  were unable to load a database. The user should have to fix the issue or
609  remove the database from settings. Overwiting the settings effectively removes
610  the database from HashLookupSettings and the user may not know about this
611  because the dialogs are not being displayed. The next time user starts Autopsy, HashDB
612  will load without errors and the user may think that the problem was solved.*/
613  if (!allDatabasesLoadedCorrectly && RuntimeProperties.runningWithGUI()) {
614  try {
615  HashLookupSettings.writeSettings(new HashLookupSettings(HashLookupSettings.convertHashSetList(this.hashSets)));
616  allDatabasesLoadedCorrectly = true;
617  } catch (HashLookupSettings.HashLookupSettingsException ex) {
618  allDatabasesLoadedCorrectly = false;
619  logger.log(Level.SEVERE, "Could not overwrite hash set settings.", ex);
620  }
621  }
622  }
623 
624  private void updateHashSetsFromCentralRepository() throws TskCoreException {
625  if(EamDb.isEnabled()){
626  List<HashDbInfo> crHashDbInfoList = getCentralRepoHashSetsFromDatabase();
627  for(HashDbInfo hashDbInfo : crHashDbInfoList) {
628  if(hashDbInfoIsNew(hashDbInfo)){
629  addExistingCentralRepoHashSet(hashDbInfo.getHashSetName(), hashDbInfo.getVersion(),
630  hashDbInfo.getReferenceSetID(),
631  hashDbInfo.getSearchDuringIngest(), hashDbInfo.getSendIngestMessages(), hashDbInfo.getKnownFilesType(),
632  hashDbInfo.isReadOnly());
633  }
634  }
635  }
636  }
637 
638  private boolean hashDbInfoIsNew(HashDbInfo dbInfo){
639  for(HashDb db:this.hashSets){
640  if(dbInfo.matches(db)){
641  return false;
642  }
643  }
644  return true;
645  }
646 
647  private String getValidFilePath(String hashSetName, String configuredPath) {
648  // Check the configured path.
649  File database = new File(configuredPath);
650  if (database.exists()) {
651  return configuredPath;
652  }
653 
654  // Give the user an opportunity to find the desired file.
655  String newPath = null;
657  JOptionPane.showConfirmDialog(WindowManager.getDefault().getMainWindow(),
658  NbBundle.getMessage(this.getClass(), "HashDbManager.dlgMsg.dbNotFoundAtLoc",
659  hashSetName, configuredPath),
660  NbBundle.getMessage(this.getClass(), "HashDbManager.dlgTitle.MissingDb"),
661  JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
662  newPath = searchForFile();
663  if (null != newPath && !newPath.isEmpty()) {
664  database = new File(newPath);
665  if (!database.exists()) {
666  newPath = null;
667  }
668  }
669  }
670  return newPath;
671  }
672 
673  private String searchForFile() {
674  String filePath = null;
675  JFileChooser fc = new JFileChooser();
676  fc.setDragEnabled(false);
677  fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
678  String[] EXTENSION = new String[]{"txt", "idx", "hash", "Hash", "kdb"}; //NON-NLS
679  FileNameExtensionFilter filter = new FileNameExtensionFilter(
680  NbBundle.getMessage(this.getClass(), "HashDbManager.fileNameExtensionFilter.title"), EXTENSION);
681  fc.setFileFilter(filter);
682  fc.setMultiSelectionEnabled(false);
683  if (fc.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
684  File f = fc.getSelectedFile();
685  try {
686  filePath = f.getCanonicalPath();
687  } catch (IOException ex) {
688  Logger.getLogger(HashDbManager.class.getName()).log(Level.WARNING, "Couldn't get selected file path", ex); //NON-NLS
689  }
690  }
691  return filePath;
692  }
693 
694  public static abstract class HashDb {
695 
700  public enum KnownFilesType {
701 
702  KNOWN(NbBundle.getMessage(HashDbManager.class, "HashDbManager.known.text")),
703  KNOWN_BAD(NbBundle.getMessage(HashDbManager.class, "HashDbManager.knownBad.text"));
704  private final String displayName;
705 
706  private KnownFilesType(String displayName) {
707  this.displayName = displayName;
708  }
709 
710  public String getDisplayName() {
711  return this.displayName;
712  }
713  }
714 
718  public enum Event {
719 
720  INDEXING_DONE
721  }
722 
723  public abstract String getHashSetName();
724 
725  abstract String getDisplayName();
726 
727  public abstract String getDatabasePath() throws TskCoreException;
728 
729  public abstract HashDb.KnownFilesType getKnownFilesType();
730 
731  public abstract boolean getSearchDuringIngest();
732 
733  abstract void setSearchDuringIngest(boolean useForIngest);
734 
735  public abstract boolean getSendIngestMessages();
736 
737  abstract void setSendIngestMessages(boolean showInboxMessages);
738 
746  public abstract boolean isUpdateable() throws TskCoreException;
747 
756  public abstract void addHashes(Content content) throws TskCoreException;
757 
758  public abstract void addHashes(Content content, String comment) throws TskCoreException;
759 
760  public abstract void addHashes(List<HashEntry> hashes) throws TskCoreException;
761 
762  public abstract boolean lookupMD5Quick(Content content) throws TskCoreException;
763 
764  public abstract HashHitInfo lookupMD5(Content content) throws TskCoreException;
765 
772  abstract boolean isValid() throws TskCoreException;
773 
774  public abstract String getIndexPath() throws TskCoreException;
775 
776  public abstract boolean hasIndexOnly() throws TskCoreException;
777 
778  public abstract void firePropertyChange(String propertyName, Object oldValue, Object newValue);
779 
780  public abstract void addPropertyChangeListener(PropertyChangeListener pcl);
781 
782  public abstract void removePropertyChangeListener(PropertyChangeListener pcl);
783 
784  @Override
785  public abstract String toString();
786 
787  }
788 
793  class SleuthkitHashSet extends HashDb{
794 
795  private static final long serialVersionUID = 1L;
796  private final int handle;
797  private final String hashSetName;
798  private boolean searchDuringIngest;
799  private boolean sendIngestMessages;
800  private final HashDb.KnownFilesType knownFilesType;
801  private boolean indexing;
802  private final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
803 
804  private SleuthkitHashSet(int handle, String hashSetName, boolean useForIngest, boolean sendHitMessages, KnownFilesType knownFilesType) {
805  this.handle = handle;
806  this.hashSetName = hashSetName;
807  this.searchDuringIngest = useForIngest;
808  this.sendIngestMessages = sendHitMessages;
809  this.knownFilesType = knownFilesType;
810  this.indexing = false;
811  }
812 
819  @Override
820  public void addPropertyChangeListener(PropertyChangeListener pcl) {
821  propertyChangeSupport.addPropertyChangeListener(pcl);
822  }
823 
829  @Override
830  public void removePropertyChangeListener(PropertyChangeListener pcl) {
831  propertyChangeSupport.removePropertyChangeListener(pcl);
832  }
833 
834  int getHandle(){
835  return handle;
836  }
837 
838  @Override
839  public String getHashSetName() {
840  return hashSetName;
841  }
842 
843  @Override
844  String getDisplayName(){
845  return getHashSetName();
846  }
847 
848  @Override
849  public String getDatabasePath() throws TskCoreException {
850  return SleuthkitJNI.getHashDatabasePath(handle);
851  }
852 
853  public void setIndexing(boolean indexing){
854  this.indexing = indexing;
855  }
856 
857  @Override
858  public String getIndexPath() throws TskCoreException {
859  return SleuthkitJNI.getHashDatabaseIndexPath(handle);
860  }
861 
862  @Override
863  public KnownFilesType getKnownFilesType() {
864  return knownFilesType;
865  }
866 
867  @Override
868  public boolean getSearchDuringIngest() {
869  return searchDuringIngest;
870  }
871 
872  @Override
873  void setSearchDuringIngest(boolean useForIngest) {
874  this.searchDuringIngest = useForIngest;
875  }
876 
877  @Override
878  public boolean getSendIngestMessages() {
879  return sendIngestMessages;
880  }
881 
882  @Override
883  void setSendIngestMessages(boolean showInboxMessages) {
884  this.sendIngestMessages = showInboxMessages;
885  }
886 
894  @Override
895  public boolean isUpdateable() throws TskCoreException {
896  return SleuthkitJNI.isUpdateableHashDatabase(this.handle);
897  }
898 
907  @Override
908  public void addHashes(Content content) throws TskCoreException {
909  addHashes(content, null);
910  }
911 
922  @Override
923  public void addHashes(Content content, String comment) throws TskCoreException {
924  // This only works for AbstractFiles and MD5 hashes at present.
925  assert content instanceof AbstractFile;
926  if (content instanceof AbstractFile) {
927  AbstractFile file = (AbstractFile) content;
928  if (null != file.getMd5Hash()) {
929  SleuthkitJNI.addToHashDatabase(null, file.getMd5Hash(), null, null, comment, handle);
930  }
931  }
932  }
933 
941  @Override
942  public void addHashes(List<HashEntry> hashes) throws TskCoreException {
943  SleuthkitJNI.addToHashDatabase(hashes, handle);
944  }
945 
955  @Override
956  public boolean lookupMD5Quick(Content content) throws TskCoreException {
957  boolean result = false;
958  assert content instanceof AbstractFile;
959  if (content instanceof AbstractFile) {
960  AbstractFile file = (AbstractFile) content;
961  if (null != file.getMd5Hash()) {
962  result = SleuthkitJNI.lookupInHashDatabase(file.getMd5Hash(), handle);
963  }
964  }
965  return result;
966  }
967 
977  @Override
978  public HashHitInfo lookupMD5(Content content) throws TskCoreException {
979  HashHitInfo result = null;
980  // This only works for AbstractFiles and MD5 hashes at present.
981  assert content instanceof AbstractFile;
982  if (content instanceof AbstractFile) {
983  AbstractFile file = (AbstractFile) content;
984  if (null != file.getMd5Hash()) {
985  result = SleuthkitJNI.lookupInHashDatabaseVerbose(file.getMd5Hash(), handle);
986  }
987  }
988  return result;
989  }
990 
997  @Override
998  boolean isValid() throws TskCoreException {
999  return hasIndex();
1000  }
1001 
1002  boolean hasIndex() throws TskCoreException {
1003  return SleuthkitJNI.hashDatabaseHasLookupIndex(handle);
1004  }
1005 
1006  @Override
1007  public boolean hasIndexOnly() throws TskCoreException {
1008  return SleuthkitJNI.hashDatabaseIsIndexOnly(handle);
1009  }
1010 
1011  boolean canBeReIndexed() throws TskCoreException {
1012  return SleuthkitJNI.hashDatabaseCanBeReindexed(handle);
1013  }
1014 
1015  boolean isIndexing() {
1016  return indexing;
1017  }
1018 
1019  @Override
1020  public void firePropertyChange(String propertyName, Object oldValue, Object newValue){
1021  this.propertyChangeSupport.firePropertyChange(propertyName, oldValue, newValue);
1022  }
1023 
1024  private void close() throws TskCoreException {
1025  SleuthkitJNI.closeHashDatabase(handle);
1026  }
1027 
1028  @Override
1029  public String toString(){
1030  return getHashSetName();
1031  }
1032 
1033 
1034  @Override
1035  public int hashCode() {
1036  int code = 23;
1037  code = 47 * code + Integer.hashCode(handle);
1038  code = 47 * code + Objects.hashCode(this.hashSetName);
1039  code = 47 * code + Objects.hashCode(this.propertyChangeSupport);
1040  code = 47 * code + Objects.hashCode(this.knownFilesType);
1041  return code;
1042  }
1043 
1044  @Override
1045  public boolean equals(Object obj) {
1046  if (obj == null) {
1047  return false;
1048  }
1049  if (getClass() != obj.getClass()) {
1050  return false;
1051  }
1052  final SleuthkitHashSet other = (SleuthkitHashSet) obj;
1053  if (!Objects.equals(this.hashSetName, other.hashSetName)) {
1054  return false;
1055  }
1056  if (this.knownFilesType != other.knownFilesType) {
1057  return false;
1058  }
1059  return true;
1060  }
1061  }
1062 
1067  class CentralRepoHashSet extends HashDb{
1068 
1069  private static final long serialVersionUID = 1L;
1070  private final String hashSetName;
1071  private boolean searchDuringIngest;
1072  private boolean sendIngestMessages;
1073  private final HashDb.KnownFilesType knownFilesType;
1074  private final int referenceSetID;
1075  private final String version;
1076  private String orgName;
1077  private final boolean readOnly;
1078  private final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
1079 
1080  @Messages({"HashDbManager.CentralRepoHashDb.orgError=Error loading organization"})
1081  private CentralRepoHashSet(String hashSetName, String version, int referenceSetID,
1082  boolean useForIngest, boolean sendHitMessages, HashDb.KnownFilesType knownFilesType,
1083  boolean readOnly)
1084  throws TskCoreException{
1085  this.hashSetName = hashSetName;
1086  this.version = version;
1087  this.referenceSetID = referenceSetID;
1088  this.searchDuringIngest = useForIngest;
1089  this.sendIngestMessages = sendHitMessages;
1090  this.knownFilesType = knownFilesType;
1091  this.readOnly = readOnly;
1092 
1093  try{
1094  orgName = EamDb.getInstance().getReferenceSetOrganization(referenceSetID).getName();
1095  } catch (EamDbException ex){
1096  Logger.getLogger(SleuthkitHashSet.class.getName()).log(Level.SEVERE, "Error looking up central repository organization for reference set " + referenceSetID, ex); //NON-NLS
1097  orgName = Bundle.HashDbManager_CentralRepoHashDb_orgError();
1098  }
1099  }
1100 
1107  @Override
1108  public void addPropertyChangeListener(PropertyChangeListener pcl) {
1109  propertyChangeSupport.addPropertyChangeListener(pcl);
1110  }
1111 
1117  @Override
1118  public void removePropertyChangeListener(PropertyChangeListener pcl) {
1119  propertyChangeSupport.removePropertyChangeListener(pcl);
1120  }
1121 
1122  @Override
1123  public boolean hasIndexOnly() throws TskCoreException{
1124  return true;
1125  }
1126 
1127  @Override
1128  public String getHashSetName() {
1129  return hashSetName;
1130  }
1131 
1132  @Override
1133  public String getDisplayName(){
1134  if(! getVersion().isEmpty()){
1135  return getHashSetName() + " " + getVersion() + " (remote)";
1136  } else {
1137  return getHashSetName() + " (remote)";
1138  }
1139  }
1140 
1141  String getVersion(){
1142  return version;
1143  }
1144 
1145  String getOrgName(){
1146  return orgName;
1147  }
1148 
1149  int getReferenceSetID(){
1150  return referenceSetID;
1151  }
1152 
1153  @Override
1154  public String getDatabasePath() throws TskCoreException {
1155  return "";
1156  }
1157 
1158  @Override
1159  public String getIndexPath() throws TskCoreException {
1160  return "";
1161  }
1162 
1163  @Override
1164  public HashDb.KnownFilesType getKnownFilesType() {
1165  return knownFilesType;
1166  }
1167 
1168  @Override
1169  public boolean getSearchDuringIngest() {
1170  return searchDuringIngest;
1171  }
1172 
1173  @Override
1174  void setSearchDuringIngest(boolean useForIngest) {
1175  this.searchDuringIngest = useForIngest;
1176  }
1177 
1178  @Override
1179  public boolean getSendIngestMessages() {
1180  return sendIngestMessages;
1181  }
1182 
1183  @Override
1184  void setSendIngestMessages(boolean showInboxMessages) {
1185  this.sendIngestMessages = showInboxMessages;
1186  }
1187 
1195  @Override
1196  public boolean isUpdateable() throws TskCoreException {
1197  return (! readOnly);
1198  }
1199 
1208  @Override
1209  public void addHashes(Content content) throws TskCoreException {
1210  addHashes(content, null);
1211  }
1212 
1223  @Override
1224  public void addHashes(Content content, String comment) throws TskCoreException {
1225  // This only works for AbstractFiles and MD5 hashes at present.
1226  assert content instanceof AbstractFile;
1227  if (content instanceof AbstractFile) {
1228  AbstractFile file = (AbstractFile) content;
1229  if (null != file.getMd5Hash()) {
1230  TskData.FileKnown type;
1231  if(knownFilesType.equals(HashDb.KnownFilesType.KNOWN_BAD)){
1232  type = TskData.FileKnown.BAD;
1233  } else {
1234  type = TskData.FileKnown.KNOWN;
1235  }
1236 
1237  try{
1238  EamGlobalFileInstance fileInstance = new EamGlobalFileInstance(referenceSetID, file.getMd5Hash(),
1239  type, comment);
1240  EamDb.getInstance().addReferenceInstance(fileInstance,EamDb.getInstance().getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID));
1241  } catch (EamDbException ex){
1242  throw new TskCoreException("Error adding hashes to " + getDisplayName(), ex);
1243  }
1244  }
1245  }
1246  }
1247 
1255  @Override
1256  public void addHashes(List<HashEntry> hashes) throws TskCoreException {
1257  Set<EamGlobalFileInstance> globalFileInstances = new HashSet<>();
1258  for(HashEntry hashEntry:hashes){
1259  TskData.FileKnown type;
1260  if(knownFilesType.equals(HashDb.KnownFilesType.KNOWN_BAD)){
1261  type = TskData.FileKnown.BAD;
1262  } else {
1263  type = TskData.FileKnown.KNOWN;
1264  }
1265  try {
1266  globalFileInstances.add(new EamGlobalFileInstance(referenceSetID, hashEntry.getMd5Hash(), type, hashEntry.getComment()));
1267  } catch (EamDbException ex){
1268  throw new TskCoreException("Error adding hashes to " + getDisplayName(), ex);
1269  }
1270  }
1271 
1272  try{
1273  EamDb.getInstance().bulkInsertReferenceTypeEntries(globalFileInstances,
1274  EamDb.getInstance().getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID));
1275  } catch (EamDbException ex){
1276  throw new TskCoreException("Error adding hashes to " + getDisplayName(), ex);
1277  }
1278  }
1279 
1289  @Override
1290  public boolean lookupMD5Quick(Content content) throws TskCoreException {
1291  // This only works for AbstractFiles and MD5 hashes
1292  assert content instanceof AbstractFile;
1293  if (content instanceof AbstractFile) {
1294  AbstractFile file = (AbstractFile) content;
1295  if (null != file.getMd5Hash()) {
1296  try{
1297  return EamDb.getInstance().isFileHashInReferenceSet(file.getMd5Hash(), this.referenceSetID);
1298  } catch (EamDbException ex){
1299  Logger.getLogger(SleuthkitHashSet.class.getName()).log(Level.SEVERE, "Error performing central reposiotry hash lookup for hash "
1300  + file.getMd5Hash() + " in reference set " + referenceSetID, ex); //NON-NLS
1301  throw new TskCoreException("Error performing central reposiotry hash lookup", ex);
1302  }
1303  }
1304  }
1305  return false;
1306  }
1307 
1317  @Override
1318  public HashHitInfo lookupMD5(Content content) throws TskCoreException {
1319  HashHitInfo result = null;
1320  // This only works for AbstractFiles and MD5 hashes
1321  assert content instanceof AbstractFile;
1322  if (content instanceof AbstractFile) {
1323  AbstractFile file = (AbstractFile) content;
1324  if (null != file.getMd5Hash()) {
1325  try{
1326  if(EamDb.getInstance().isFileHashInReferenceSet(file.getMd5Hash(), this.referenceSetID)){
1327  // Make a bare-bones HashHitInfo for now
1328  result = new HashHitInfo(file.getMd5Hash(), "", "");
1329  }
1330  } catch (EamDbException ex){
1331  Logger.getLogger(SleuthkitHashSet.class.getName()).log(Level.SEVERE, "Error performing central reposiotry hash lookup for hash "
1332  + file.getMd5Hash() + " in reference set " + referenceSetID, ex); //NON-NLS
1333  throw new TskCoreException("Error performing central reposiotry hash lookup", ex);
1334  }
1335  }
1336  }
1337  return result;
1338  }
1339 
1345  @Override
1346  boolean isValid() {
1347  if(! EamDb.isEnabled()) {
1348  return false;
1349  }
1350  try{
1351  return EamDb.getInstance().referenceSetIsValid(this.referenceSetID, this.hashSetName, this.version);
1352  } catch (EamDbException ex){
1353  Logger.getLogger(CentralRepoHashSet.class.getName()).log(Level.SEVERE, "Error validating hash set " + hashSetName, ex); //NON-NLS
1354  return false;
1355  }
1356  }
1357 
1358  @Override
1359  public void firePropertyChange(String propertyName, Object oldValue, Object newValue){
1360  this.propertyChangeSupport.firePropertyChange(propertyName, oldValue, newValue);
1361  }
1362 
1363  @Override
1364  public String toString(){
1365  return getDisplayName();
1366  }
1367 
1368 
1369  @Override
1370  public int hashCode() {
1371  int code = 23;
1372  code = 47 * code + Objects.hashCode(this.hashSetName);
1373  code = 47 * code + Objects.hashCode(this.version);
1374  code = 47 * code + Integer.hashCode(this.referenceSetID);
1375  code = 47 * code + Objects.hashCode(this.knownFilesType);
1376  return code;
1377  }
1378 
1379  @Override
1380  public boolean equals(Object obj) {
1381  if (obj == null) {
1382  return false;
1383  }
1384  if (getClass() != obj.getClass()) {
1385  return false;
1386  }
1387  final CentralRepoHashSet other = (CentralRepoHashSet) obj;
1388  if (!Objects.equals(this.hashSetName, other.hashSetName)) {
1389  return false;
1390  }
1391  if (!Objects.equals(this.version, other.version)) {
1392  return false;
1393  }
1394  if (this.knownFilesType != other.knownFilesType) {
1395  return false;
1396  }
1397  return true;
1398  }
1399  }
1400 
1404  private class HashDbIndexer extends SwingWorker<Object, Void> {
1405 
1406  private ProgressHandle progress = null;
1407  private SleuthkitHashSet hashDb = null;
1408 
1409  HashDbIndexer(SleuthkitHashSet hashDb) {
1410  this.hashDb = hashDb;
1411  }
1412 
1413  @Override
1414  protected Object doInBackground() {
1415  hashDb.setIndexing(true);
1416  progress = ProgressHandle.createHandle(
1417  NbBundle.getMessage(this.getClass(), "HashDbManager.progress.indexingHashSet", hashDb.getHashSetName()));
1418  progress.start();
1419  progress.switchToIndeterminate();
1420  try {
1421  SleuthkitJNI.createLookupIndexForHashDatabase(hashDb.getHandle());
1422  } catch (TskCoreException ex) {
1423  Logger.getLogger(HashDbIndexer.class.getName()).log(Level.SEVERE, "Error indexing hash set " + hashDb.getHashSetName(), ex); //NON-NLS
1424  JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
1425  NbBundle.getMessage(this.getClass(),
1426  "HashDbManager.dlgMsg.errorIndexingHashSet",
1427  hashDb.getHashSetName()),
1428  NbBundle.getMessage(this.getClass(), "HashDbManager.hashDbIndexingErr"),
1429  JOptionPane.ERROR_MESSAGE);
1430  }
1431  return null;
1432  }
1433 
1434  @Override
1435  protected void done() {
1436  hashDb.setIndexing(false);
1437  progress.finish();
1438 
1439  // see if we got any errors
1440  try {
1441  get();
1442  } catch (InterruptedException | ExecutionException ex) {
1443  logger.log(Level.SEVERE, "Error creating index", ex); //NON-NLS
1445  NbBundle.getMessage(this.getClass(), "HashDbManager.errCreatingIndex.title"),
1446  NbBundle.getMessage(this.getClass(), "HashDbManager.errCreatingIndex.msg", ex.getMessage()),
1448  } // catch and ignore if we were cancelled
1449  catch (java.util.concurrent.CancellationException ex) {
1450  }
1451 
1452  try {
1453  hashDb.firePropertyChange(SleuthkitHashSet.Event.INDEXING_DONE.toString(), null, hashDb);
1454  hashDb.firePropertyChange(HashDbManager.SetEvt.DB_INDEXED.toString(), null, hashDb.getHashSetName());
1455  } catch (Exception e) {
1456  logger.log(Level.SEVERE, "HashDbManager listener threw exception", e); //NON-NLS
1458  NbBundle.getMessage(this.getClass(), "HashDbManager.moduleErr"),
1459  NbBundle.getMessage(this.getClass(), "HashDbManager.moduleErrorListeningToUpdatesMsg"),
1461  }
1462  }
1463  }
1464 }
CorrelationAttribute.Type getCorrelationTypeById(int typeId)
static synchronized IngestManager getInstance()
synchronized void addPropertyChangeListener(PropertyChangeListener listener)
List< HashDb > getUpdateableHashSets(List< HashDb > hashDbs)
synchronized HashDb addNewHashDatabaseNoSave(String hashSetName, String path, boolean searchDuringIngest, boolean sendIngestMessages, HashDb.KnownFilesType knownFilesType)
abstract void firePropertyChange(String propertyName, Object oldValue, Object newValue)
synchronized void removeHashDatabaseNoSave(HashDb hashDb)
String getValidFilePath(String hashSetName, String configuredPath)
static HashDb.KnownFilesType convertFileKnown(TskData.FileKnown fileKnown)
List< EamGlobalSet > getAllReferenceSets(CorrelationAttribute.Type correlationType)
synchronized HashDb addNewHashDatabase(String hashSetName, String path, boolean searchDuringIngest, boolean sendIngestMessages, HashDb.KnownFilesType knownFilesType)
synchronized void removePropertyChangeListener(PropertyChangeListener listener)
void closeHashDatabases(List< HashDb > hashDatabases)
abstract void addPropertyChangeListener(PropertyChangeListener pcl)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
static void show(String title, String message, MessageType type, ActionListener actionListener)
SleuthkitHashSet addHashDatabase(int handle, String hashSetName, boolean searchDuringIngest, boolean sendIngestMessages, HashDb.KnownFilesType knownFilesType)
synchronized HashDb addExistingHashDatabase(String hashSetName, String path, boolean searchDuringIngest, boolean sendIngestMessages, HashDb.KnownFilesType knownFilesType)
abstract void removePropertyChangeListener(PropertyChangeListener pcl)

Copyright © 2012-2016 Basis Technology. Generated on: Mon Jun 18 2018
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.