diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Artifacts.java b/Core/src/org/sleuthkit/autopsy/datamodel/Artifacts.java index 373c5b5fed7d34abf8a7d1a76f383c272097caf5..57db059411edb14c5a45ba2eac3d05c3c24949a4 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Artifacts.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Artifacts.java @@ -246,7 +246,7 @@ static class TypeFactory extends ChildFactory.Detachable<TypeNodeKey> implements @SuppressWarnings("deprecation") private static TypeNodeKey getTypeKey(BlackboardArtifact.Type type, SleuthkitCase skCase, long dsObjId) { - // ELTODO + // Get the custom TSK_MALWARE artifact type from case database if (MALWARE_ARTIFACT_TYPE == null) { try { MALWARE_ARTIFACT_TYPE = skCase.getArtifactType(MALWARE_HITS); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/MalwareHits.java b/Core/src/org/sleuthkit/autopsy/datamodel/MalwareHits.java index 6c3a9f74d63005ce0aa0ba440f6eb8aa89cf56a0..784da11ba5c2a7db824012c00136ef29f4a9d32f 100755 --- a/Core/src/org/sleuthkit/autopsy/datamodel/MalwareHits.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/MalwareHits.java @@ -27,14 +27,12 @@ import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Observable; import java.util.Observer; import java.util.Set; import java.util.logging.Level; -import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.nodes.Sheet; @@ -57,13 +55,11 @@ /** * Malware hits node support. Inner classes have all of the nodes in the tree. */ -@NbBundle.Messages({ - "MalwareHits_malwareTypeDisplayName=Malware",}) public class MalwareHits implements AutopsyVisitableItem { private static final String MALWARE_HITS = "TSK_MALWARE"; private static BlackboardArtifact.Type MALWARE_ARTIFACT_TYPE = null; - private static final String DISPLAY_NAME = Bundle.MalwareHits_malwareTypeDisplayName(); // ELTODO get from database + private static String DISPLAY_NAME; private static final Logger logger = Logger.getLogger(MalwareHits.class.getName()); private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED); private static final Set<IngestManager.IngestModuleEvent> INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestModuleEvent.DATA_ADDED); @@ -103,13 +99,14 @@ public <T> T accept(AutopsyItemVisitor<T> visitor) { * Stores all of the malware results in a single class that is observable * for the child nodes */ - private class MalwareResults extends Observable { + private class MalwareResults extends Observable implements Observer { // list of artifacts // NOTE: the list can be accessed by multiple worker threads and needs to be synchronized private final Set<Long> malwareHits = new HashSet<>(); MalwareResults() { + addNotify(); update(); } @@ -128,16 +125,18 @@ final void update() { if (skCase == null) { return; } - + + // Get the custom TSK_MALWARE artifact type from case database if (MALWARE_ARTIFACT_TYPE == null) { try { MALWARE_ARTIFACT_TYPE = skCase.getArtifactType(MALWARE_HITS); + DISPLAY_NAME = MALWARE_ARTIFACT_TYPE.getDisplayName(); } catch (TskCoreException ex) { logger.log(Level.WARNING, "Unable to get TSK_MALWARE artifact type from database : ", ex); //NON-NLS return; } } - + String query = "SELECT blackboard_artifacts.artifact_obj_id " //NON-NLS + "FROM blackboard_artifacts,tsk_analysis_results WHERE " //NON-NLS + "blackboard_artifacts.artifact_type_id=" + MALWARE_ARTIFACT_TYPE.getTypeID() @@ -163,6 +162,84 @@ final void update() { setChanged(); notifyObservers(); } + + private final PropertyChangeListener pcl = new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + String eventType = evt.getPropertyName(); + if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) { + /** + * Checking for a current case is a stop gap measure until a + * different way of handling the closing of cases is worked + * out. Currently, remote events may be received for a case + * that is already closed. + */ + try { + Case.getCurrentCaseThrows(); + /** + * Due to some unresolved issues with how cases are + * closed, it is possible for the event to have a null + * oldValue if the event is a remote event. + */ + ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue(); + if (null != eventData && eventData.getBlackboardArtifactType().getTypeID() == MALWARE_ARTIFACT_TYPE.getTypeID()) { + malwareResults.update(); + } + } catch (NoCurrentCaseException notUsed) { + /** + * Case is closed, do nothing. + */ + } + } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString()) + || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) { + /** + * Checking for a current case is a stop gap measure until a + * different way of handling the closing of cases is worked + * out. Currently, remote events may be received for a case + * that is already closed. + */ + try { + Case.getCurrentCaseThrows(); + malwareResults.update(); + } catch (NoCurrentCaseException notUsed) { + /** + * Case is closed, do nothing. + */ + } + } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) { + // case was closed. Remove listeners so that we don't get called with a stale case handle + if (evt.getNewValue() == null) { + removeNotify(); + skCase = null; + } + } + } + }; + + private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(pcl, null); + + protected void addNotify() { + IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, weakPcl); + IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, weakPcl); + Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), weakPcl); + } + + protected void removeNotify() { + IngestManager.getInstance().removeIngestJobEventListener(weakPcl); + IngestManager.getInstance().removeIngestModuleEventListener(weakPcl); + Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), weakPcl); + } + + @Override + protected void finalize() throws Throwable { + super.finalize(); + removeNotify(); + } + + @Override + public void update(Observable o, Object arg) { + update(); + } } /** @@ -212,68 +289,16 @@ protected Sheet createSheet() { public String getItemType() { return getClass().getName(); } - } - - /** - * Node for a hash set name - - public class HashsetNameNode extends DisplayableItemNode implements Observer { - - private final String hashSetName; - - public HashsetNameNode(String hashSetName) { - super(Children.create(new HitFactory(hashSetName), true), Lookups.singleton(hashSetName)); - super.setName(hashSetName); - this.hashSetName = hashSetName; - updateDisplayName(); - this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/hashset_hits.png"); // ELTODO - malwareResults.addObserver(this); - } - - // Update the count in the display name - private void updateDisplayName() { - super.setDisplayName(hashSetName + " (" + malwareResults.getArtifactIds(hashSetName).size() + ")"); - } - - @Override - public boolean isLeafTypeNode() { - return true; - } - - @Override - protected Sheet createSheet() { - Sheet sheet = super.createSheet(); - Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES); - if (sheetSet == null) { - sheetSet = Sheet.createPropertiesSet(); - sheet.put(sheetSet); - } - - sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "MalwareHits.createSheet.name.name"), - NbBundle.getMessage(this.getClass(), "MalwareHits.createSheet.name.displayName"), - NbBundle.getMessage(this.getClass(), "MalwareHits.createSheet.name.desc"), - getName())); - - return sheet; - } - - @Override - public <T> T accept(DisplayableItemNodeVisitor<T> visitor) { - return visitor.visit(this); - } - - @Override - public void update(Observable o, Object arg) { - updateDisplayName(); - } + /** + * When this method is called, the count to be displayed will be + * updated. + */ @Override - public String getItemType() { - // For custom settings for each hash set, return - *getClass().getName() + hashSetName instead. - return getClass().getName(); + void updateDisplayName() { + super.setDisplayName(DISPLAY_NAME + " (" + malwareResults.getArtifactIds().size() + ")"); } - }*/ + } /** * Creates the nodes for the malware hits.