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.EnumSet;
 
   28 import java.util.HashSet;
 
   29 import java.util.LinkedHashMap;
 
   30 import java.util.List;
 
   32 import java.util.Objects;
 
   33 import java.util.Observable;
 
   34 import java.util.Observer;
 
   36 import java.util.logging.Level;
 
   37 import java.util.stream.Collectors;
 
   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;
 
   58 import org.
sleuthkit.datamodel.SleuthkitCase.CaseDbQuery;
 
   68     @NbBundle.Messages(
"KeywordHits.kwHits.text=Keyword Hits")
 
   69     private static final String 
KEYWORD_HITS = KeywordHits_kwHits_text();
 
   70     @NbBundle.Messages(
"KeywordHits.simpleLiteralSearch.text=Single Literal Keyword Search")
 
   72     @NbBundle.Messages(
"KeywordHits.singleRegexSearch.text=Single Regular Expression Search")
 
   75     public static final String 
NAME = BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getLabel();
 
   93             + 
"blackboard_attributes.value_int32, " 
   94             + 
"blackboard_attributes.artifact_id, "  
   95             + 
"blackboard_attributes.attribute_type_id " 
   96             + 
"FROM blackboard_attributes, blackboard_artifacts " 
   97             + 
"WHERE blackboard_attributes.artifact_id = blackboard_artifacts.artifact_id " 
   98             + 
" AND blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID() 
 
   99             + 
" AND (attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()
 
  100             + 
" OR attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID()
 
  101             + 
" OR attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE.getTypeID()
 
  102             + 
" OR attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID()
 
  106         return (instances.size() == 1) && (instances.get(0).equals(DEFAULT_INSTANCE_NAME));
 
  127         this.datasourceObjId = objId;
 
  141         private final Map<String, Map<String, Map<String, Set<Long>>>> 
topLevelMap = 
new LinkedHashMap<>();
 
  152         List<String> getListNames() {
 
  154                 List<String> names = 
new ArrayList<>(topLevelMap.keySet());
 
  170         List<String> getKeywords(String listName) {
 
  171             List<String> keywords;
 
  173                 keywords = 
new ArrayList<>(topLevelMap.get(listName).keySet());
 
  175             Collections.sort(keywords);
 
  189         List<String> getKeywordInstances(String listName, String keyword) {
 
  190             List<String> instances;
 
  192                 instances = 
new ArrayList<>(topLevelMap.get(listName).get(keyword).keySet());
 
  194             Collections.sort(instances);
 
  209         Set<Long> getArtifactIds(String listName, String keyword, String keywordInstance) {
 
  211                 return topLevelMap.get(listName).get(keyword).get(keywordInstance);
 
  224         void addRegExpToList(Map<String, Map<String, Set<Long>>> listMap, String regExp, String keywordInstance, Long artifactId) {
 
  225             Map<String, Set<Long>> instanceMap = listMap.computeIfAbsent(regExp, r -> 
new LinkedHashMap<>());
 
  227             instanceMap.computeIfAbsent(keywordInstance, ki -> 
new HashSet<>()).add(artifactId);
 
  238         void addNonRegExpMatchToList(Map<String, Map<String, Set<Long>>> listMap, String keyWord, Long artifactId) {
 
  239             Map<String, Set<Long>> instanceMap = listMap.computeIfAbsent(keyWord, k -> 
new LinkedHashMap<>());
 
  242             instanceMap.computeIfAbsent(DEFAULT_INSTANCE_NAME, DIN -> 
new HashSet<>()).add(artifactId);
 
  252         void populateTreeMaps(Map<Long, Map<Long, String>> artifactIds) {
 
  257                 Map<String, Map<String, Map<String, Set<Long>>>> listsMap = 
new LinkedHashMap<>();
 
  260                 Map<String, Map<String, Set<Long>>> literalMap = 
new LinkedHashMap<>();
 
  263                 Map<String, Map<String, Set<Long>>> regexMap = 
new LinkedHashMap<>();
 
  266                 topLevelMap.put(SIMPLE_LITERAL_SEARCH, literalMap);
 
  267                 topLevelMap.put(SIMPLE_REGEX_SEARCH, regexMap);
 
  269                 for (Map.Entry<Long, Map<Long, String>> art : artifactIds.entrySet()) {
 
  270                     long id = art.getKey();
 
  271                     Map<Long, String> attributes = art.getValue();
 
  274                     String listName = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()));
 
  275                     String word = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID()));
 
  276                     String reg = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID()));
 
  277                     String kwType = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE.getTypeID()));
 
  279                     if (listName != null) {     
 
  281                         Map<String, Map<String, Set<Long>>> listMap = listsMap.computeIfAbsent(listName, ln -> 
new LinkedHashMap<>());
 
  283                         if (
"1".equals(kwType) || reg == null) {  
 
  290                             word = (reg != null) ? reg : word; 
 
  291                             addNonRegExpMatchToList(listMap, word, 
id);
 
  293                             addRegExpToList(listMap, reg, word, 
id);
 
  296                         if (
"1".equals(kwType) || reg == null) {  
 
  303                             word = (reg != null) ? reg : word; 
 
  304                             addNonRegExpMatchToList(literalMap, word, 
id);
 
  306                             addRegExpToList(regexMap, reg, word, 
id);
 
  310                 topLevelMap.putAll(listsMap);
 
  319             Map<Long, Map<Long, String>> artifactIds = 
new LinkedHashMap<>();
 
  321             if (skCase == null) {
 
  327                 queryStr +=  
"  AND blackboard_artifacts.data_source_obj_id = " + 
datasourceObjId;
 
  330             try (CaseDbQuery dbQuery = skCase.executeQuery(queryStr)) {
 
  331                 ResultSet resultSet = dbQuery.getResultSet();
 
  332                 while (resultSet.next()) {
 
  333                     long artifactId = resultSet.getLong(
"artifact_id"); 
 
  334                     long typeId = resultSet.getLong(
"attribute_type_id"); 
 
  335                     String valueStr = resultSet.getString(
"value_text"); 
 
  338                     Map<Long, String> attributesByTypeMap = artifactIds.computeIfAbsent(artifactId, ai -> 
new LinkedHashMap<>());
 
  339                     if (StringUtils.isNotEmpty(valueStr)) {
 
  340                         attributesByTypeMap.put(typeId, valueStr);
 
  343                         Long valueLong = resultSet.getLong(
"value_int32");
 
  344                         attributesByTypeMap.put(typeId, valueLong.toString());
 
  347             } 
catch (TskCoreException | SQLException ex) {
 
  348                 logger.log(Level.WARNING, 
"SQL Exception occurred: ", ex); 
 
  351             populateTreeMaps(artifactIds);
 
  357         return visitor.
visit(
this);
 
  364             super(Children.create(
new ListFactory(), 
true), Lookups.singleton(KEYWORD_HITS));
 
  366             super.setDisplayName(KEYWORD_HITS);
 
  367             this.setIconBaseWithExtension(
"org/sleuthkit/autopsy/images/keyword_hits.png"); 
 
  377             return visitor.
visit(
this);
 
  381         @NbBundle.Messages({
"KeywordHits.createSheet.name.name=Name",
 
  382             "KeywordHits.createSheet.name.displayName=Name",
 
  383             "KeywordHits.createSheet.name.desc=no description"})
 
  385             Sheet sheet = super.createSheet();
 
  386             Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
 
  387             if (sheetSet == null) {
 
  388                 sheetSet = Sheet.createPropertiesSet();
 
  393                     KeywordHits_createSheet_name_name(),
 
  394                     KeywordHits_createSheet_name_displayName(),
 
  395                     KeywordHits_createSheet_name_desc(),
 
  403             return getClass().getName();
 
  411             keywordResults.addObserver(
this);
 
  416             keywordResults.deleteObserver(
this);
 
  420         public void update(Observable o, Object arg) {
 
  430         private final PropertyChangeListener 
pcl = 
new PropertyChangeListener() {
 
  432             public void propertyChange(PropertyChangeEvent evt) {
 
  433                 String eventType = evt.getPropertyName();
 
  450                         if (null != eventData && eventData.
getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()) {
 
  471                         && evt.getNewValue() == null) {
 
  497             super.removeNotify();
 
  502             list.addAll(keywordResults.getListNames());
 
  508             return new ListNode(key);
 
  515             super(children, lookup);
 
  524             return getClass().getName();
 
  528         public void update(Observable o, Object arg) {
 
  532         final void updateDisplayName() {
 
  533             super.setDisplayName(getName() + 
" (" + countTotalDescendants() + 
")");
 
  536         abstract int countTotalDescendants();
 
  543     class ListNode 
extends KWHitsNodeBase {
 
  545         private final String listName;
 
  547         private ListNode(String listName) {
 
  548             super(Children.create(
new TermFactory(listName), 
true), Lookups.singleton(listName));
 
  549             super.setName(listName);
 
  550             this.setIconBaseWithExtension(
"org/sleuthkit/autopsy/images/keyword_hits.png"); 
 
  551             this.listName = listName;
 
  553             keywordResults.addObserver(
this);
 
  557         public int countTotalDescendants() {
 
  558             int totalDescendants = 0;
 
  560             for (String word : keywordResults.getKeywords(listName)) {
 
  561                 for (String instance : keywordResults.getKeywordInstances(listName, word)) {
 
  562                     Set<Long> ids = keywordResults.getArtifactIds(listName, word, instance);
 
  563                     totalDescendants += ids.size();
 
  566             return totalDescendants;
 
  570         @NbBundle.Messages({
"KeywordHits.createSheet.listName.name=List Name",
 
  571             "KeywordHits.createSheet.listName.displayName=List Name",
 
  572             "KeywordHits.createSheet.listName.desc=no description",
 
  573             "KeywordHits.createSheet.numChildren.name=Number of Children",
 
  574             "KeywordHits.createSheet.numChildren.displayName=Number of Children",
 
  575             "KeywordHits.createSheet.numChildren.desc=no description"})
 
  576         protected Sheet createSheet() {
 
  577             Sheet sheet = super.createSheet();
 
  578             Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
 
  579             if (sheetSet == null) {
 
  580                 sheetSet = Sheet.createPropertiesSet();
 
  584             sheetSet.put(
new NodeProperty<>(
 
  585                     KeywordHits_createSheet_listName_name(),
 
  586                     KeywordHits_createSheet_listName_displayName(),
 
  587                     KeywordHits_createSheet_listName_desc(),
 
  590             sheetSet.put(
new NodeProperty<>(
 
  591                     KeywordHits_createSheet_numChildren_name(),
 
  592                     KeywordHits_createSheet_numChildren_displayName(),
 
  593                     KeywordHits_createSheet_numChildren_desc(),
 
  594                     keywordResults.getKeywords(listName).size()));
 
  600         public boolean isLeafTypeNode() {
 
  605         public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
 
  606             return visitor.visit(
this);
 
  624             list.addAll(keywordResults.getKeywords(setName));
 
  630             return new TermNode(setName, key);
 
  637     class TermNode 
extends KWHitsNodeBase {
 
  639         private final String setName;
 
  640         private final String keyword;
 
  642         private TermNode(String setName, String keyword) {
 
  643             super(Children.create(
new RegExpInstancesFactory(setName, keyword), 
true), Lookups.singleton(keyword));
 
  644             super.setName(keyword);
 
  645             this.setName = setName;
 
  646             this.keyword = keyword;
 
  647             this.setIconBaseWithExtension(
"org/sleuthkit/autopsy/images/keyword_hits.png"); 
 
  649             keywordResults.addObserver(
this);
 
  653         int countTotalDescendants() {
 
  654             return keywordResults.getKeywordInstances(setName, keyword).stream()
 
  655                     .mapToInt(instance -> keywordResults.getArtifactIds(setName, keyword, instance).size())
 
  660         public boolean isLeafTypeNode() {
 
  666         public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
 
  667             return visitor.visit(
this);
 
  671         @NbBundle.Messages({
"KeywordHits.createSheet.filesWithHits.name=Files with Hits",
 
  672             "KeywordHits.createSheet.filesWithHits.displayName=Files with Hits",
 
  673             "KeywordHits.createSheet.filesWithHits.desc=no description"})
 
  674         protected Sheet createSheet() {
 
  675             Sheet sheet = super.createSheet();
 
  676             Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
 
  677             if (sheetSet == null) {
 
  678                 sheetSet = Sheet.createPropertiesSet();
 
  681             sheetSet.put(
new NodeProperty<>(
 
  682                     KeywordHits_createSheet_listName_name(),
 
  683                     KeywordHits_createSheet_listName_displayName(),
 
  684                     KeywordHits_createSheet_listName_desc(),
 
  687             sheetSet.put(
new NodeProperty<>(
 
  688                     KeywordHits_createSheet_filesWithHits_name(),
 
  689                     KeywordHits_createSheet_filesWithHits_displayName(),
 
  690                     KeywordHits_createSheet_filesWithHits_desc(),
 
  691                     countTotalDescendants()));
 
  726         String getRegExpKey() {
 
  748             List<String> instances = keywordResults.getKeywordInstances(setName, keyword);
 
  753                 list.addAll(keywordResults.getArtifactIds(setName, keyword, DEFAULT_INSTANCE_NAME).stream()
 
  754                         .map(RegExpInstanceKey::new)
 
  755                         .collect(Collectors.toList()));
 
  757                 list.addAll(instances.stream()
 
  758                         .map(RegExpInstanceKey::new)
 
  759                         .collect(Collectors.toList()));
 
  767                 return new RegExpInstanceNode(setName, keyword, key.getRegExpKey());
 
  779     class RegExpInstanceNode 
extends KWHitsNodeBase {
 
  781         private final String setName;
 
  782         private final String keyword;
 
  783         private final String instance;
 
  785         private RegExpInstanceNode(String setName, String keyword, String instance) {
 
  786             super(Children.create(
new HitsFactory(setName, keyword, instance), 
true), Lookups.singleton(instance));
 
  788             this.setName = setName;
 
  789             this.keyword = keyword;
 
  790             this.instance = instance;
 
  791             this.setIconBaseWithExtension(
"org/sleuthkit/autopsy/images/keyword_hits.png"); 
 
  793             keywordResults.addObserver(
this);
 
  797         int countTotalDescendants() {
 
  798             return keywordResults.getArtifactIds(setName, keyword, instance).size();
 
  802         public boolean isLeafTypeNode() {
 
  807         public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
 
  808             return visitor.visit(
this);
 
  812         protected Sheet createSheet() {
 
  813             Sheet sheet = super.createSheet();
 
  814             Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
 
  815             if (sheetSet == null) {
 
  816                 sheetSet = Sheet.createPropertiesSet();
 
  820             sheetSet.put(
new NodeProperty<>(
 
  821                     KeywordHits_createSheet_listName_name(),
 
  822                     KeywordHits_createSheet_listName_displayName(),
 
  823                     KeywordHits_createSheet_listName_desc(),
 
  826             sheetSet.put(
new NodeProperty<>(
 
  827                     KeywordHits_createSheet_filesWithHits_name(),
 
  828                     KeywordHits_createSheet_filesWithHits_displayName(),
 
  829                     KeywordHits_createSheet_filesWithHits_desc(),
 
  830                     keywordResults.getArtifactIds(setName, keyword, instance).size()));
 
  844     @NbBundle.Messages({
"KeywordHits.createNodeForKey.modTime.name=ModifiedTime",
 
  845         "KeywordHits.createNodeForKey.modTime.displayName=Modified Time",
 
  846         "KeywordHits.createNodeForKey.modTime.desc=Modified Time",
 
  847         "KeywordHits.createNodeForKey.accessTime.name=AccessTime",
 
  848         "KeywordHits.createNodeForKey.accessTime.displayName=Access Time",
 
  849         "KeywordHits.createNodeForKey.accessTime.desc=Access Time",
 
  850         "KeywordHits.createNodeForKey.chgTime.name=ChangeTime",
 
  851         "KeywordHits.createNodeForKey.chgTime.displayName=Change Time",
 
  852         "KeywordHits.createNodeForKey.chgTime.desc=Change Time"})
 
  854         if (skCase == null) {
 
  859             BlackboardArtifact art = skCase.getBlackboardArtifact(artifactId);
 
  863             AbstractFile file = n.getLookup().lookup(AbstractFile.class);
 
  866                     file = skCase.getAbstractFileById(art.getObjectID());
 
  867                 } 
catch (TskCoreException ex) {
 
  868                     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(),
 
  897         } 
catch (TskCoreException ex) {
 
  898             logger.log(Level.WARNING, 
"TSK Exception occurred", ex); 
 
  912         private HitsFactory(String setName, String keyword, String instance) {
 
  921             list.addAll(keywordResults.getArtifactIds(setName, keyword, instance));
 
final PropertyChangeListener pcl
static boolean isOnlyDefaultInstance(List< String > instances)
static final String KEYWORD_HITS
BlackboardArtifact.Type getBlackboardArtifactType()
void removeIngestModuleEventListener(final PropertyChangeListener listener)
KWHitsNodeBase(Children children, Lookup lookup)
Node createNodeForKey(RegExpInstanceKey key)
static String getStringTime(long epochSeconds, TimeZone tzone)
Node createNodeForKey(String key)
static synchronized IngestManager getInstance()
final KeywordResults keywordResults
void setName(String name)
static final String SIMPLE_REGEX_SEARCH
TermFactory(String setName)
BlackboardArtifactNode createBlackboardArtifactNode(Long artifactId)
final Map< String, Map< String, Map< String, Set< Long > > > > topLevelMap
T visit(DataSourcesNode in)
boolean createKeys(List< String > list)
void removeIngestJobEventListener(final PropertyChangeListener listener)
KWHitsNodeBase(Children children)
boolean createKeys(List< String > list)
void update(Observable o, Object arg)
boolean createKeys(List< RegExpInstanceKey > list)
RegExpInstancesFactory(String setName, String keyword)
void addIngestJobEventListener(final PropertyChangeListener listener)
static final String DEFAULT_INSTANCE_NAME
Node createNodeForKey(Long artifactId)
Node createNodeForKey(String key)
boolean createKeys(List< Long > list)
static Boolean getGroupItemsInTreeByDataSource()
static final Logger logger
static final String KEYWORD_HIT_ATTRIBUTES_QUERY
void addIngestModuleEventListener(final PropertyChangeListener listener)
KeywordHits(SleuthkitCase skCase, long objId)
synchronized static Logger getLogger(String name)
static Case getCurrentCaseThrows()
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
void update(Observable o, Object arg)
final long datasourceObjId
HitsFactory(String setName, String keyword, String instance)
void addNodeProperty(NodeProperty<?> np)
static void removeEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
static final String SIMPLE_LITERAL_SEARCH