/* * Autopsy Forensic Browser * * Copyright 2011 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.sleuthkit.autopsy.datamodel; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import org.openide.nodes.AbstractNode; import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.nodes.Sheet; import org.openide.util.lookup.Lookups; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskException; /** * Keyword hits node support */ public class KeywordHits implements AutopsyVisitableItem { private SleuthkitCase skCase; private static final Logger logger = Logger.getLogger(KeywordHits.class.getName()); private static final String KEYWORD_HITS = "Keyword Hits"; public static final String NAME = BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getLabel(); public static final String SIMPLE_LITERAL_SEARCH = "Single Literal Keyword Search"; public static final String SIMPLE_REGEX_SEARCH = "Single Regular Expression Search"; // Map from String (list name) to Map from string (keyword) to set (artifact ids) private Map>> topLevelMap; private Map>> listsMap; // Map from String (literal keyword) to set (artifact ids) private Map> literalMap; // Map from String (regex keyword) to set (artifact ids); private Map> regexMap; Map> artifacts; public KeywordHits(SleuthkitCase skCase) { this.skCase = skCase; artifacts = new LinkedHashMap>(); listsMap = new LinkedHashMap>>(); literalMap = new LinkedHashMap>(); regexMap = new LinkedHashMap>(); topLevelMap = new LinkedHashMap>>(); } private void initMaps() { topLevelMap.clear(); topLevelMap.put(SIMPLE_LITERAL_SEARCH, literalMap); topLevelMap.put(SIMPLE_REGEX_SEARCH, regexMap); listsMap.clear(); regexMap.clear(); literalMap.clear(); for (Map.Entry> art : artifacts.entrySet()) { long id = art.getKey(); Map attributes = art.getValue(); // I think we can use attributes.remove(...) here? String listName = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID())); String word = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID())); String reg = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID())); if (listName != null) { if (!listsMap.containsKey(listName)) { listsMap.put(listName, new LinkedHashMap>()); } if (!listsMap.get(listName).containsKey(word)) { listsMap.get(listName).put(word, new HashSet()); } listsMap.get(listName).get(word).add(id); } else if (reg != null) { if (!regexMap.containsKey(reg)) { regexMap.put(reg, new HashSet()); } regexMap.get(reg).add(id); } else { if (!literalMap.containsKey(word)) { literalMap.put(word, new HashSet()); } literalMap.get(word).add(id); } topLevelMap.putAll(listsMap); } } private void initArtifacts() { artifacts.clear(); try { int setId = BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID(); int wordId = BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID(); int regexId = BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID(); int artId = BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID(); String query = "SELECT blackboard_attributes.value_text,blackboard_attributes.artifact_id," + "blackboard_attributes.attribute_type_id FROM blackboard_attributes,blackboard_artifacts WHERE " + "(blackboard_attributes.artifact_id=blackboard_artifacts.artifact_id AND " + "blackboard_artifacts.artifact_type_id=" + artId + ") AND (attribute_type_id=" + setId + " OR " + "attribute_type_id=" + wordId + " OR " + "attribute_type_id=" + regexId + ")"; ResultSet rs = skCase.runQuery(query); while (rs.next()) { String value = rs.getString("value_text"); long artifactId = rs.getLong("artifact_id"); long typeId = rs.getLong("attribute_type_id"); if (!artifacts.containsKey(artifactId)) { artifacts.put(artifactId, new LinkedHashMap()); } if (!value.equals("")) { artifacts.get(artifactId).put(typeId, value); } } Statement s = rs.getStatement(); rs.close(); if (s != null) { s.close(); } } catch (SQLException ex) { logger.log(Level.WARNING, "SQL Exception occurred: ", ex); } } @Override public T accept(AutopsyItemVisitor v) { return v.visit(this); } public class KeywordHitsRootNode extends DisplayableItemNode { public KeywordHitsRootNode() { super(Children.create(new KeywordHitsRootChildren(), true), Lookups.singleton(KEYWORD_HITS)); super.setName(NAME); super.setDisplayName(KEYWORD_HITS); this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/keyword_hits.png"); //long start = System.currentTimeMillis(); initArtifacts(); initMaps(); //long finish = System.currentTimeMillis(); //logger.info("Process took " + (finish-start) + " ms" ); } @Override public T accept(DisplayableItemNodeVisitor v) { return v.visit(this); } @Override public TYPE getDisplayableItemNodeType() { return TYPE.ARTIFACT; } @Override protected Sheet createSheet() { Sheet s = super.createSheet(); Sheet.Set ss = s.get(Sheet.PROPERTIES); if (ss == null) { ss = Sheet.createPropertiesSet(); s.put(ss); } ss.put(new NodeProperty("Name", "Name", "no description", getName())); return s; } } private class KeywordHitsRootChildren extends ChildFactory { @Override protected boolean createKeys(List list) { list.addAll(topLevelMap.keySet()); return true; } @Override protected Node createNodeForKey(String key) { return new KeywordHitsListNode(key, topLevelMap.get(key)); } } public class KeywordHitsListNode extends DisplayableItemNode { private String name; private Map> children; public KeywordHitsListNode(String name, Map> children) { super(Children.create(new KeywordHitsListChildren(children), true), Lookups.singleton(name)); super.setName(name); int totalDescendants = 0; for (Set grandChildren : children.values()) { totalDescendants += grandChildren.size(); } super.setDisplayName(name + " (" + totalDescendants + ")"); this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/keyword_hits.png"); this.name = name; this.children = children; } @Override public TYPE getDisplayableItemNodeType() { return TYPE.ARTIFACT; } @Override protected Sheet createSheet() { Sheet s = super.createSheet(); Sheet.Set ss = s.get(Sheet.PROPERTIES); if (ss == null) { ss = Sheet.createPropertiesSet(); s.put(ss); } ss.put(new NodeProperty("List Name", "List Name", "no description", name)); ss.put(new NodeProperty("Number of Children", "Number of Children", "no description", children.size())); return s; } @Override public T accept(DisplayableItemNodeVisitor v) { return v.visit(this); } } private class KeywordHitsListChildren extends ChildFactory { private Map> children; private KeywordHitsListChildren(Map> children) { super(); this.children = children; } @Override protected boolean createKeys(List list) { list.addAll(children.keySet()); return true; } @Override protected Node createNodeForKey(String key) { return new KeywordHitsKeywordNode(key, children.get(key)); } } public class KeywordHitsKeywordNode extends DisplayableItemNode { private String name; private Set children; public KeywordHitsKeywordNode(String name, Set children) { super(Children.create(new KeywordHitsKeywordChildren(children), true), Lookups.singleton(name)); super.setName(name); super.setDisplayName(name + " (" + children.size() + ")"); this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/keyword_hits.png"); this.name = name; this.children = children; } @Override public boolean isLeafTypeNode() { return true; } @Override public T accept(DisplayableItemNodeVisitor v) { return v.visit(this); } @Override public TYPE getDisplayableItemNodeType() { return TYPE.ARTIFACT; } @Override protected Sheet createSheet() { Sheet s = super.createSheet(); Sheet.Set ss = s.get(Sheet.PROPERTIES); if (ss == null) { ss = Sheet.createPropertiesSet(); s.put(ss); } ss.put(new NodeProperty("List Name", "List Name", "no description", name)); ss.put(new NodeProperty("Number of Hits", "Number of Hits", "no description", children.size())); return s; } } private class KeywordHitsKeywordChildren extends ChildFactory { private Set children; private KeywordHitsKeywordChildren(Set children) { super(); this.children = children; } @Override protected boolean createKeys(List list) { for (long l : children) { try { //TODO: bulk artifact gettings list.add(skCase.getBlackboardArtifact(l)); } catch (TskException ex) { logger.log(Level.WARNING, "TSK Exception occurred", ex); } } return true; } @Override protected Node createNodeForKey(BlackboardArtifact artifact) { return new BlackboardArtifactNode(artifact); } } }