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