19 package org.sleuthkit.autopsy.datamodel;
 
   21 import java.beans.PropertyChangeEvent;
 
   22 import java.beans.PropertyChangeListener;
 
   23 import java.sql.ResultSet;
 
   24 import java.sql.SQLException;
 
   25 import java.util.ArrayList;
 
   26 import java.util.Collections;
 
   27 import java.util.Comparator;
 
   28 import java.util.EnumSet;
 
   29 import java.util.HashMap;
 
   30 import java.util.HashSet;
 
   31 import java.util.LinkedHashMap;
 
   32 import java.util.List;
 
   34 import java.util.Observable;
 
   35 import java.util.Observer;
 
   37 import java.util.logging.Level;
 
   38 import org.apache.commons.lang3.StringUtils;
 
   39 import org.openide.nodes.ChildFactory;
 
   40 import org.openide.nodes.Children;
 
   41 import org.openide.nodes.Node;
 
   42 import org.openide.nodes.Sheet;
 
   43 import org.openide.util.Lookup;
 
   44 import org.openide.util.NbBundle;
 
   45 import org.openide.util.lookup.Lookups;
 
   56 import org.
sleuthkit.datamodel.SleuthkitCase.CaseDbQuery;
 
   67     @NbBundle.Messages(
"KeywordHits.kwHits.text=Keyword Hits")
 
   68     private static final String 
KEYWORD_HITS = KeywordHits_kwHits_text();
 
   69     @NbBundle.Messages(
"KeywordHits.simpleLiteralSearch.text=Single Literal Keyword Search")
 
   71     @NbBundle.Messages(
"KeywordHits.singleRegexSearch.text=Single Regular Expression Search")
 
   74     public static final String 
NAME = BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getLabel();
 
   91             + 
"blackboard_attributes.value_int32, " 
   92             + 
"blackboard_attributes.artifact_id, "  
   93             + 
"blackboard_attributes.attribute_type_id " 
   94             + 
"FROM blackboard_attributes, blackboard_artifacts " 
   95             + 
"WHERE blackboard_attributes.artifact_id = blackboard_artifacts.artifact_id " 
   96             + 
" AND blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID() 
 
   97             + 
" AND (attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()
 
   98             + 
" OR attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID()
 
   99             + 
" OR attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE.getTypeID()
 
  100             + 
" OR attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID()
 
  104         return (instances.size() == 1) && (instances.get(0).equals(DEFAULT_INSTANCE_NAME));
 
  125         this.filteringDSObjId = objId;
 
  139         private final Map<String, Map<String, Map<String, Set<Long>>>> 
topLevelMap = 
new LinkedHashMap<>();
 
  150         List<String> getListNames() {
 
  152                 List<String> names = 
new ArrayList<>(topLevelMap.keySet());
 
  156                 Collections.sort(names, 
new Comparator<String>() {
 
  159                     public int compare(String o1, String o2) {
 
  162                         if (o1.startsWith(
"Single Literal Keyword Search")) {
 
  164                         } 
else if (o2.startsWith(
"Single Literal Keyword Search")) {
 
  166                         } 
else if (o1.startsWith(
"Single Regular Expression Search")) {
 
  168                         } 
else if (o2.startsWith(
"Single Regular Expression Search")) {
 
  171                         return o1.compareTo(o2);
 
  187         List<String> getKeywords(String listName) {
 
  188             List<String> keywords;
 
  190                 keywords = 
new ArrayList<>(topLevelMap.get(listName).keySet());
 
  192             Collections.sort(keywords);
 
  206         List<String> getKeywordInstances(String listName, String keyword) {
 
  207             List<String> instances;
 
  209                 instances = 
new ArrayList<>(topLevelMap.get(listName).get(keyword).keySet());
 
  211             Collections.sort(instances);
 
  226         Set<Long> getArtifactIds(String listName, String keyword, String keywordInstance) {
 
  228                 return topLevelMap.get(listName).get(keyword).get(keywordInstance);
 
  241         void addRegExpToList(Map<String, Map<String, Set<Long>>> listMap, String regExp, String keywordInstance, Long artifactId) {
 
  242             Map<String, Set<Long>> instanceMap = listMap.computeIfAbsent(regExp, r -> 
new LinkedHashMap<>());
 
  244             instanceMap.computeIfAbsent(keywordInstance, ki -> 
new HashSet<>()).add(artifactId);
 
  255         void addNonRegExpMatchToList(Map<String, Map<String, Set<Long>>> listMap, String keyWord, Long artifactId) {
 
  256             Map<String, Set<Long>> instanceMap = listMap.computeIfAbsent(keyWord, k -> 
new LinkedHashMap<>());
 
  259             instanceMap.computeIfAbsent(DEFAULT_INSTANCE_NAME, DIN -> 
new HashSet<>()).add(artifactId);
 
  269         void populateTreeMaps(Map<Long, Map<Long, String>> artifactIds) {
 
  274                 Map<String, Map<String, Map<String, Set<Long>>>> listsMap = 
new LinkedHashMap<>();
 
  277                 Map<String, Map<String, Set<Long>>> literalMap = 
new LinkedHashMap<>();
 
  280                 Map<String, Map<String, Set<Long>>> regexMap = 
new LinkedHashMap<>();
 
  283                 topLevelMap.put(SIMPLE_LITERAL_SEARCH, literalMap);
 
  284                 topLevelMap.put(SIMPLE_REGEX_SEARCH, regexMap);
 
  286                 for (Map.Entry<Long, Map<Long, String>> art : artifactIds.entrySet()) {
 
  287                     long id = art.getKey();
 
  288                     Map<Long, String> attributes = art.getValue();
 
  291                     String listName = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()));
 
  292                     String word = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID()));
 
  293                     String reg = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID()));
 
  294                     String kwType = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE.getTypeID()));
 
  296                     if (listName != null) {     
 
  298                         Map<String, Map<String, Set<Long>>> listMap = listsMap.computeIfAbsent(listName, ln -> 
new LinkedHashMap<>());
 
  300                         if (
"1".equals(kwType) || reg == null) {  
 
  307                             word = (reg != null) ? reg : word; 
 
  308                             addNonRegExpMatchToList(listMap, word, 
id);
 
  310                             addRegExpToList(listMap, reg, word, 
id);
 
  313                         if (
"1".equals(kwType) || reg == null) {  
 
  320                             word = (reg != null) ? reg : word; 
 
  321                             addNonRegExpMatchToList(literalMap, word, 
id);
 
  323                             addRegExpToList(regexMap, reg, word, 
id);
 
  327                 topLevelMap.putAll(listsMap);
 
  336             Map<Long, Map<Long, String>> artifactIds = 
new LinkedHashMap<>();
 
  338             if (skCase == null) {
 
  343             if (filteringDSObjId > 0) {
 
  344                 queryStr += 
"  AND blackboard_artifacts.data_source_obj_id = " + 
filteringDSObjId;
 
  347             try (CaseDbQuery dbQuery = skCase.executeQuery(queryStr)) {
 
  348                 ResultSet resultSet = dbQuery.getResultSet();
 
  349                 while (resultSet.next()) {
 
  350                     long artifactId = resultSet.getLong(
"artifact_id"); 
 
  351                     long typeId = resultSet.getLong(
"attribute_type_id"); 
 
  352                     String valueStr = resultSet.getString(
"value_text"); 
 
  355                     Map<Long, String> attributesByTypeMap = artifactIds.computeIfAbsent(artifactId, ai -> 
new LinkedHashMap<>());
 
  356                     if (StringUtils.isNotEmpty(valueStr)) {
 
  357                         attributesByTypeMap.put(typeId, valueStr);
 
  360                         Long valueLong = resultSet.getLong(
"value_int32");
 
  361                         attributesByTypeMap.put(typeId, valueLong.toString());
 
  364             } 
catch (TskCoreException | SQLException ex) {
 
  365                 logger.log(Level.WARNING, 
"SQL Exception occurred: ", ex); 
 
  368             populateTreeMaps(artifactIds);
 
  374         return visitor.
visit(
this);
 
  381             super(Children.create(
new ListFactory(), 
true), Lookups.singleton(KEYWORD_HITS));
 
  383             super.setDisplayName(KEYWORD_HITS);
 
  384             this.setIconBaseWithExtension(
"org/sleuthkit/autopsy/images/keyword_hits.png"); 
 
  394             return visitor.
visit(
this);
 
  398         @NbBundle.Messages({
"KeywordHits.createSheet.name.name=Name",
 
  399             "KeywordHits.createSheet.name.displayName=Name",
 
  400             "KeywordHits.createSheet.name.desc=no description"})
 
  402             Sheet sheet = super.createSheet();
 
  403             Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
 
  404             if (sheetSet == null) {
 
  405                 sheetSet = Sheet.createPropertiesSet();
 
  410                     KeywordHits_createSheet_name_name(),
 
  411                     KeywordHits_createSheet_name_displayName(),
 
  412                     KeywordHits_createSheet_name_desc(),
 
  420             return getClass().getName();
 
  428             keywordResults.addObserver(
this);
 
  433             keywordResults.deleteObserver(
this);
 
  437         public void update(Observable o, Object arg) {
 
  447         private final PropertyChangeListener 
pcl = 
new PropertyChangeListener() {
 
  449             public void propertyChange(PropertyChangeEvent evt) {
 
  450                 String eventType = evt.getPropertyName();
 
  467                         if (null != eventData && eventData.
getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()) {
 
  488                         && evt.getNewValue() == null) {
 
  514             super.removeNotify();
 
  519             list.addAll(keywordResults.getListNames());
 
  525             return new ListNode(key);
 
  534             super(children, lookup);
 
  544             return getClass().getName();
 
  548         public void update(Observable o, Object arg) {
 
  552         final void updateDisplayName() {
 
  553             super.setDisplayName(displayName + 
" (" + countTotalDescendants() + 
")");
 
  556         abstract int countTotalDescendants();
 
  563     class ListNode 
extends KWHitsNodeBase {
 
  565         private final String listName;
 
  567         private ListNode(String listName) {
 
  568             super(Children.create(
new TermFactory(listName), 
true), Lookups.singleton(listName), listName);
 
  569             super.setName(listName);
 
  570             this.setIconBaseWithExtension(
"org/sleuthkit/autopsy/images/keyword_hits.png"); 
 
  571             this.listName = listName;
 
  573             keywordResults.addObserver(
this);
 
  577         public int countTotalDescendants() {
 
  578             int totalDescendants = 0;
 
  580             for (String word : keywordResults.getKeywords(listName)) {
 
  581                 for (String instance : keywordResults.getKeywordInstances(listName, word)) {
 
  582                     Set<Long> ids = keywordResults.getArtifactIds(listName, word, instance);
 
  583                     totalDescendants += ids.size();
 
  586             return totalDescendants;
 
  590         @NbBundle.Messages({
"KeywordHits.createSheet.listName.name=List Name",
 
  591             "KeywordHits.createSheet.listName.displayName=List Name",
 
  592             "KeywordHits.createSheet.listName.desc=no description",
 
  593             "KeywordHits.createSheet.numChildren.name=Number of Children",
 
  594             "KeywordHits.createSheet.numChildren.displayName=Number of Children",
 
  595             "KeywordHits.createSheet.numChildren.desc=no description"})
 
  596         protected Sheet createSheet() {
 
  597             Sheet sheet = super.createSheet();
 
  598             Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
 
  599             if (sheetSet == null) {
 
  600                 sheetSet = Sheet.createPropertiesSet();
 
  604             sheetSet.put(
new NodeProperty<>(
 
  605                     KeywordHits_createSheet_listName_name(),
 
  606                     KeywordHits_createSheet_listName_displayName(),
 
  607                     KeywordHits_createSheet_listName_desc(),
 
  610             sheetSet.put(
new NodeProperty<>(
 
  611                     KeywordHits_createSheet_numChildren_name(),
 
  612                     KeywordHits_createSheet_numChildren_displayName(),
 
  613                     KeywordHits_createSheet_numChildren_desc(),
 
  614                     keywordResults.getKeywords(listName).size()));
 
  620         public boolean isLeafTypeNode() {
 
  625         public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
 
  626             return visitor.visit(
this);
 
  644             list.addAll(keywordResults.getKeywords(setName));
 
  650             return new TermNode(setName, key);
 
  664     ChildFactory<?> createChildFactory(String setName, String keyword) {
 
  666             return new HitsFactory(setName, keyword, DEFAULT_INSTANCE_NAME);
 
  668             return new RegExpInstancesFactory(setName, keyword);
 
  675     class TermNode 
extends KWHitsNodeBase {
 
  677         private final String setName;
 
  678         private final String keyword;
 
  680         private TermNode(String setName, String keyword) {
 
  681             super(Children.create(createChildFactory(setName, keyword), 
true), Lookups.singleton(keyword), keyword);
 
  690             super.setName(setName + 
"_" + keyword);
 
  691             this.setName = setName;
 
  692             this.keyword = keyword;
 
  693             this.setIconBaseWithExtension(
"org/sleuthkit/autopsy/images/keyword_hits.png"); 
 
  695             keywordResults.addObserver(
this);
 
  699         int countTotalDescendants() {
 
  700             return keywordResults.getKeywordInstances(setName, keyword).stream()
 
  701                     .mapToInt(instance -> keywordResults.getArtifactIds(setName, keyword, instance).size())
 
  706         public boolean isLeafTypeNode() {
 
  712         public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
 
  713             return visitor.visit(
this);
 
  717         @NbBundle.Messages({
"KeywordHits.createSheet.filesWithHits.name=Files with Hits",
 
  718             "KeywordHits.createSheet.filesWithHits.displayName=Files with Hits",
 
  719             "KeywordHits.createSheet.filesWithHits.desc=no description"})
 
  720         protected Sheet createSheet() {
 
  721             Sheet sheet = super.createSheet();
 
  722             Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
 
  723             if (sheetSet == null) {
 
  724                 sheetSet = Sheet.createPropertiesSet();
 
  727             sheetSet.put(
new NodeProperty<>(
 
  728                     KeywordHits_createSheet_listName_name(),
 
  729                     KeywordHits_createSheet_listName_displayName(),
 
  730                     KeywordHits_createSheet_listName_desc(),
 
  733             sheetSet.put(
new NodeProperty<>(
 
  734                     KeywordHits_createSheet_filesWithHits_name(),
 
  735                     KeywordHits_createSheet_filesWithHits_displayName(),
 
  736                     KeywordHits_createSheet_filesWithHits_desc(),
 
  737                     countTotalDescendants()));
 
  760             list.addAll(keywordResults.getKeywordInstances(setName, keyword));
 
  766             return new RegExpInstanceNode(setName, keyword, key);
 
  773     class RegExpInstanceNode 
extends KWHitsNodeBase {
 
  775         private final String setName;
 
  776         private final String keyword;
 
  777         private final String instance;
 
  779         private RegExpInstanceNode(String setName, String keyword, String instance) {
 
  780             super(Children.create(
new HitsFactory(setName, keyword, instance), 
true), Lookups.singleton(instance), instance);
 
  789             super.setName(setName + 
"_" + keyword + 
"_" + instance);
 
  790             this.setName = setName;
 
  791             this.keyword = keyword;
 
  792             this.instance = instance;
 
  793             this.setIconBaseWithExtension(
"org/sleuthkit/autopsy/images/keyword_hits.png"); 
 
  795             keywordResults.addObserver(
this);
 
  799         int countTotalDescendants() {
 
  800             return keywordResults.getArtifactIds(setName, keyword, instance).size();
 
  804         public boolean isLeafTypeNode() {
 
  809         public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
 
  810             return visitor.visit(
this);
 
  814         protected Sheet createSheet() {
 
  815             Sheet sheet = super.createSheet();
 
  816             Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
 
  817             if (sheetSet == null) {
 
  818                 sheetSet = Sheet.createPropertiesSet();
 
  822             sheetSet.put(
new NodeProperty<>(
 
  823                     KeywordHits_createSheet_listName_name(),
 
  824                     KeywordHits_createSheet_listName_displayName(),
 
  825                     KeywordHits_createSheet_listName_desc(),
 
  828             sheetSet.put(
new NodeProperty<>(
 
  829                     KeywordHits_createSheet_filesWithHits_name(),
 
  830                     KeywordHits_createSheet_filesWithHits_displayName(),
 
  831                     KeywordHits_createSheet_filesWithHits_desc(),
 
  832                     keywordResults.getArtifactIds(setName, keyword, instance).size()));
 
  846     @NbBundle.Messages({
"KeywordHits.createNodeForKey.modTime.name=ModifiedTime",
 
  847         "KeywordHits.createNodeForKey.modTime.displayName=Modified Time",
 
  848         "KeywordHits.createNodeForKey.modTime.desc=Modified Time",
 
  849         "KeywordHits.createNodeForKey.accessTime.name=AccessTime",
 
  850         "KeywordHits.createNodeForKey.accessTime.displayName=Access Time",
 
  851         "KeywordHits.createNodeForKey.accessTime.desc=Access Time",
 
  852         "KeywordHits.createNodeForKey.chgTime.name=ChangeTime",
 
  853         "KeywordHits.createNodeForKey.chgTime.displayName=Change Time",
 
  854         "KeywordHits.createNodeForKey.chgTime.desc=Change Time"})
 
  856         if (skCase == null) {
 
  864         AbstractFile file = n.getLookup().lookup(AbstractFile.class);
 
  867                 file = skCase.getAbstractFileById(art.getObjectID());
 
  868             } 
catch (TskCoreException ex) {
 
  869                 logger.log(Level.SEVERE, 
"TskCoreException while constructing BlackboardArtifact Node from KeywordHitsKeywordChildren", ex); 
 
  882                 KeywordHits_createNodeForKey_modTime_name(),
 
  883                 KeywordHits_createNodeForKey_modTime_displayName(),
 
  884                 KeywordHits_createNodeForKey_modTime_desc(),
 
  887                 KeywordHits_createNodeForKey_accessTime_name(),
 
  888                 KeywordHits_createNodeForKey_accessTime_displayName(),
 
  889                 KeywordHits_createNodeForKey_accessTime_desc(),
 
  892                 KeywordHits_createNodeForKey_chgTime_name(),
 
  893                 KeywordHits_createNodeForKey_chgTime_displayName(),
 
  894                 KeywordHits_createNodeForKey_chgTime_desc(),
 
  907         private final Map<Long, BlackboardArtifact> 
artifactHits = 
new HashMap<>();
 
  909         private HitsFactory(String setName, String keyword, String instance) {
 
  916             super(setName + 
"_" + keyword + (DEFAULT_INSTANCE_NAME.equals(instance) ? 
"" : 
"_" + 
instance));
 
  924             if (skCase != null) {
 
  925                 keywordResults.getArtifactIds(setName, keyword, instance).forEach((
id) -> {
 
  927                         if (!artifactHits.containsKey(
id)) {
 
  928                             BlackboardArtifact art = skCase.getBlackboardArtifact(
id);
 
  929                             artifactHits.put(
id, art);
 
  931                     } 
catch (TskCoreException ex) {
 
  932                         logger.log(Level.SEVERE, 
"TSK Exception occurred", ex); 
 
  936                 return new ArrayList<>(artifactHits.values());
 
  938             return Collections.emptyList();
 
  948             keywordResults.addObserver(
this);
 
  953             keywordResults.deleteObserver(
this);
 
  957         public void update(Observable o, Object arg) {
 
final PropertyChangeListener pcl
 
static boolean isOnlyDefaultInstance(List< String > instances)
 
static final String KEYWORD_HITS
 
BlackboardArtifact.Type getBlackboardArtifactType()
 
void removeIngestModuleEventListener(final PropertyChangeListener listener)
 
BlackboardArtifactNode createBlackboardArtifactNode(BlackboardArtifact art)
 
static String getStringTime(long epochSeconds, TimeZone tzone)
 
Node createNodeForKey(String key)
 
static synchronized IngestManager getInstance()
 
void update(Observable o, Object arg)
 
final KeywordResults keywordResults
 
static final String SIMPLE_REGEX_SEARCH
 
final Map< Long, BlackboardArtifact > artifactHits
 
TermFactory(String setName)
 
final Map< String, Map< String, Map< String, Set< Long > > > > topLevelMap
 
static final Set< IngestManager.IngestJobEvent > INGEST_JOB_EVENTS_OF_INTEREST
 
List< BlackboardArtifact > makeKeys()
 
T visit(DataSourcesNode in)
 
boolean createKeys(List< String > list)
 
void removeIngestJobEventListener(final PropertyChangeListener listener)
 
KWHitsNodeBase(Children children)
 
boolean createKeys(List< String > list)
 
boolean createKeys(List< String > list)
 
void update(Observable o, Object arg)
 
RegExpInstancesFactory(String setName, String keyword)
 
void addIngestJobEventListener(final PropertyChangeListener listener)
 
static final String DEFAULT_INSTANCE_NAME
 
Node createNodeForKey(String key)
 
Node createNodeForKey(BlackboardArtifact art)
 
static final Logger logger
 
Node createNodeForKey(String key)
 
static final String KEYWORD_HIT_ATTRIBUTES_QUERY
 
void addIngestModuleEventListener(final PropertyChangeListener listener)
 
KeywordHits(SleuthkitCase skCase, long objId)
 
synchronized static Logger getLogger(String name)
 
static final Set< IngestManager.IngestModuleEvent > INGEST_MODULE_EVENTS_OF_INTEREST
 
static Case getCurrentCaseThrows()
 
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
 
void update(Observable o, Object arg)
 
final long filteringDSObjId
 
HitsFactory(String setName, String keyword, String instance)
 
KWHitsNodeBase(Children children, Lookup lookup, String displayName)
 
void addNodeProperty(NodeProperty<?> np)
 
static void removeEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
 
static final String SIMPLE_LITERAL_SEARCH