Autopsy 4.22.1
Graphical digital forensics platform for The Sleuth Kit and other tools.
KeywordHits.java
Go to the documentation of this file.
1/*
2 * Autopsy Forensic Browser
3 *
4 * Copyright 2011-2021 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 */
19package org.sleuthkit.autopsy.datamodel;
20
21import java.beans.PropertyChangeEvent;
22import java.beans.PropertyChangeListener;
23import java.sql.ResultSet;
24import java.sql.SQLException;
25import java.util.ArrayList;
26import java.util.Collections;
27import java.util.Comparator;
28import java.util.EnumSet;
29import java.util.HashMap;
30import java.util.HashSet;
31import java.util.LinkedHashMap;
32import java.util.List;
33import java.util.Map;
34import java.util.Observable;
35import java.util.Observer;
36import java.util.Set;
37import java.util.logging.Level;
38import org.apache.commons.lang3.StringUtils;
39import org.openide.nodes.ChildFactory;
40import org.openide.nodes.Children;
41import org.openide.nodes.Node;
42import org.openide.nodes.Sheet;
43import org.openide.util.Lookup;
44import org.openide.util.NbBundle;
45import org.openide.util.WeakListeners;
46import org.openide.util.lookup.Lookups;
47import org.sleuthkit.autopsy.casemodule.Case;
48import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
49import org.sleuthkit.autopsy.coreutils.Logger;
50import org.sleuthkit.autopsy.coreutils.TimeZoneUtils;
51import static org.sleuthkit.autopsy.datamodel.Bundle.*;
52import org.sleuthkit.autopsy.ingest.IngestManager;
53import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
54import org.sleuthkit.datamodel.AbstractFile;
55import org.sleuthkit.datamodel.BlackboardArtifact;
56import org.sleuthkit.datamodel.BlackboardAttribute;
57import org.sleuthkit.datamodel.SleuthkitCase;
58import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery;
59import org.sleuthkit.datamodel.TskCoreException;
60import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_KEYWORD_HIT;
61import org.sleuthkit.autopsy.datamodel.Artifacts.UpdatableCountTypeNode;
62import org.sleuthkit.datamodel.AnalysisResult;
63
67public class KeywordHits implements AutopsyVisitableItem {
68
69 private static final Logger logger = Logger.getLogger(KeywordHits.class.getName());
72 @NbBundle.Messages("KeywordHits.kwHits.text=Keyword Hits")
73 private static final String KEYWORD_HITS = KeywordHits_kwHits_text();
74 @NbBundle.Messages("KeywordHits.simpleLiteralSearch.text=Single Literal Keyword Search")
75 private static final String SIMPLE_LITERAL_SEARCH = KeywordHits_simpleLiteralSearch_text();
76 @NbBundle.Messages("KeywordHits.singleRegexSearch.text=Single Regular Expression Search")
77 private static final String SIMPLE_REGEX_SEARCH = KeywordHits_singleRegexSearch_text();
78
79 public static final String NAME = BlackboardArtifact.Type.TSK_KEYWORD_HIT.getTypeName();
80
81 private SleuthkitCase skCase;
83 private final long filteringDSObjId; // 0 if not filtering/grouping by data source
84
90 private static final String DEFAULT_INSTANCE_NAME = "DEFAULT_INSTANCE_NAME";
91
95 private static final String KEYWORD_HIT_ATTRIBUTES_QUERY = "SELECT blackboard_attributes.value_text, "//NON-NLS
96 + "blackboard_attributes.value_int32, "//NON-NLS
97 + "blackboard_artifacts.artifact_obj_id, " //NON-NLS
98 + "blackboard_attributes.attribute_type_id "//NON-NLS
99 + "FROM blackboard_attributes, blackboard_artifacts "//NON-NLS
100 + "WHERE blackboard_attributes.artifact_id = blackboard_artifacts.artifact_id "//NON-NLS
101 + " AND blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.Type.TSK_KEYWORD_HIT.getTypeID() //NON-NLS
102 + " AND (attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()//NON-NLS
103 + " OR attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID()//NON-NLS
104 + " OR attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE.getTypeID()//NON-NLS
105 + " OR attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID()//NON-NLS
106 + ")"; //NON-NLS
107
108 static private boolean isOnlyDefaultInstance(List<String> instances) {
109 return (instances.size() == 1) && (instances.get(0).equals(DEFAULT_INSTANCE_NAME));
110 }
111
117 KeywordHits(SleuthkitCase skCase) {
118 this(skCase, 0);
119 }
120
128 public KeywordHits(SleuthkitCase skCase, long objId) {
129 this.skCase = skCase;
130 this.filteringDSObjId = objId;
132 }
133
134 /*
135 * All of these maps and code assume the following: Regexps will have an
136 * 'instance' layer that shows the specific words that matched the regexp
137 * Exact match and substring will not have the instance layer and instead
138 * will have the specific hits below their term.
139 */
140 private final class KeywordResults extends Observable {
141
142 // Map from listName/Type to Map of keywords/regexp to Map of instance terms to Set of artifact Ids
143 // NOTE: the map can be accessed by multiple worker threads and needs to be synchronized
144 private final Map<String, Map<String, Map<String, Set<Long>>>> topLevelMap = new LinkedHashMap<>();
145
146 KeywordResults() {
147 update();
148 }
149
155 List<String> getListNames() {
156 synchronized (topLevelMap) {
157 List<String> names = new ArrayList<>(topLevelMap.keySet());
158
159 // sort the list names, but ensure that the special lists
160 // stay at the top.
161 Collections.sort(names, new Comparator<String>() {
162
163 @Override
164 public int compare(String o1, String o2) {
165 // ideally, they would not be hard coded, but this module
166 // doesn't know about Keyword Search NBM
167 if (o1.startsWith("Single Literal Keyword Search")) {
168 return -1;
169 } else if (o2.startsWith("Single Literal Keyword Search")) {
170 return 1;
171 } else if (o1.startsWith("Single Regular Expression Search")) {
172 return -1;
173 } else if (o2.startsWith("Single Regular Expression Search")) {
174 return 1;
175 }
176 return o1.compareTo(o2);
177 }
178 });
179
180 return names;
181 }
182 }
183
192 List<String> getKeywords(String listName) {
193 List<String> keywords;
194 synchronized (topLevelMap) {
195 keywords = new ArrayList<>(topLevelMap.get(listName).keySet());
196 }
197 Collections.sort(keywords);
198 return keywords;
199 }
200
211 List<String> getKeywordInstances(String listName, String keyword) {
212 List<String> instances;
213 synchronized (topLevelMap) {
214 instances = new ArrayList<>(topLevelMap.get(listName).get(keyword).keySet());
215 }
216 Collections.sort(instances);
217 return instances;
218 }
219
231 Set<Long> getArtifactIds(String listName, String keyword, String keywordInstance) {
232 synchronized (topLevelMap) {
233 return topLevelMap.get(listName).get(keyword).get(keywordInstance);
234 }
235 }
236
246 void addRegExpToList(Map<String, Map<String, Set<Long>>> listMap, String regExp, String keywordInstance, Long artifactId) {
247 Map<String, Set<Long>> instanceMap = listMap.computeIfAbsent(regExp, r -> new LinkedHashMap<>());
248 // add this ID to the instances entry, creating one if needed
249 instanceMap.computeIfAbsent(keywordInstance, ki -> new HashSet<>()).add(artifactId);
250 }
251
260 void addNonRegExpMatchToList(Map<String, Map<String, Set<Long>>> listMap, String keyWord, Long artifactId) {
261 Map<String, Set<Long>> instanceMap = listMap.computeIfAbsent(keyWord, k -> new LinkedHashMap<>());
262
263 // Use the default instance name, since we don't need that level in the tree
264 instanceMap.computeIfAbsent(DEFAULT_INSTANCE_NAME, DIN -> new HashSet<>()).add(artifactId);
265 }
266
274 void populateTreeMaps(Map<Long, Map<Long, String>> artifactIds) {
275 synchronized (topLevelMap) {
276 topLevelMap.clear();
277
278 // map of list name to keword to artifact IDs
279 Map<String, Map<String, Map<String, Set<Long>>>> listsMap = new LinkedHashMap<>();
280
281 // Map from from literal keyword to instances (which will be empty) to artifact IDs
282 Map<String, Map<String, Set<Long>>> literalMap = new LinkedHashMap<>();
283
284 // Map from regex keyword artifact to instances to artifact IDs
285 Map<String, Map<String, Set<Long>>> regexMap = new LinkedHashMap<>();
286
287 // top-level nodes
288 topLevelMap.put(SIMPLE_LITERAL_SEARCH, literalMap);
289 topLevelMap.put(SIMPLE_REGEX_SEARCH, regexMap);
290
291 for (Map.Entry<Long, Map<Long, String>> art : artifactIds.entrySet()) {
292 long id = art.getKey();
293 Map<Long, String> attributes = art.getValue();
294
295 // I think we can use attributes.remove(...) here? - why should bwe use remove?
296 String listName = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()));
297 String word = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID()));
298 String reg = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID()));
299 String kwType = attributes.get(Long.valueOf(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE.getTypeID()));
300
301 if (listName != null) { // part of a list
302 // get or create list entry
303 Map<String, Map<String, Set<Long>>> listMap = listsMap.computeIfAbsent(listName, ln -> new LinkedHashMap<>());
304
305 if ("1".equals(kwType) || reg == null) { //literal, substring or exact
306 /*
307 * Substring, treated same as exact match. "1" is
308 * the ordinal value for substring as defined in
309 * KeywordSearch.java. The original term should be
310 * stored in reg
311 */
312 word = (reg != null) ? reg : word; //use original term if it there.
313 addNonRegExpMatchToList(listMap, word, id);
314 } else {
315 addRegExpToList(listMap, reg, word, id);
316 }
317 } else {//single term
318 if ("1".equals(kwType) || reg == null) { //literal, substring or exact
319 /*
320 * Substring, treated same as exact match. "1" is
321 * the ordinal value for substring as defined in
322 * KeywordSearch.java. The original term should be
323 * stored in reg
324 */
325 word = (reg != null) ? reg : word; //use original term if it there.
326 addNonRegExpMatchToList(literalMap, word, id);
327 } else {
328 addRegExpToList(regexMap, reg, word, id);
329 }
330 }
331 }
332 topLevelMap.putAll(listsMap);
333 }
334
335 setChanged();
336 notifyObservers();
337 }
338
339 public void update() {
340 // maps Artifact ID to map of attribute types to attribute values
341 Map<Long, Map<Long, String>> artifactIds = new LinkedHashMap<>();
342
343 if (skCase == null) {
344 return;
345 }
346
347 String queryStr = KEYWORD_HIT_ATTRIBUTES_QUERY;
348 if (filteringDSObjId > 0) {
349 queryStr += " AND blackboard_artifacts.data_source_obj_id = " + filteringDSObjId;
350 }
351
352 try (CaseDbQuery dbQuery = skCase.executeQuery(queryStr)) {
353 ResultSet resultSet = dbQuery.getResultSet();
354 while (resultSet.next()) {
355 long artifactObjId = resultSet.getLong("artifact_obj_id"); //NON-NLS
356 long typeId = resultSet.getLong("attribute_type_id"); //NON-NLS
357 String valueStr = resultSet.getString("value_text"); //NON-NLS
358
359 //get the map of attributes for this artifact
360 Map<Long, String> attributesByTypeMap = artifactIds.computeIfAbsent(artifactObjId, ai -> new LinkedHashMap<>());
361 if (StringUtils.isNotEmpty(valueStr)) {
362 attributesByTypeMap.put(typeId, valueStr);
363 } else {
364 // Keyword Search Type is an int
365 Long valueLong = resultSet.getLong("value_int32");
366 attributesByTypeMap.put(typeId, valueLong.toString());
367 }
368 }
369 } catch (TskCoreException | SQLException ex) {
370 logger.log(Level.WARNING, "SQL Exception occurred: ", ex); //NON-NLS
371 }
372
373 populateTreeMaps(artifactIds);
374 }
375 }
376
377 @Override
378 public <T> T accept(AutopsyItemVisitor<T> visitor) {
379 return visitor.visit(this);
380 }
381
382 // Created by CreateAutopsyNodeVisitor
383 public class RootNode extends UpdatableCountTypeNode {
384
385 public RootNode() {
386 super(Children.create(new ListFactory(), true),
387 Lookups.singleton(KEYWORD_HITS),
390 TSK_KEYWORD_HIT);
391
392 super.setName(NAME);
393 this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/keyword_hits.png"); //NON-NLS
394 }
395
396 @Override
397 public boolean isLeafTypeNode() {
398 return false;
399 }
400
401 @Override
402 public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
403 return visitor.visit(this);
404 }
405
406 @Override
407 @NbBundle.Messages({"KeywordHits.createSheet.name.name=Name",
408 "KeywordHits.createSheet.name.displayName=Name",
409 "KeywordHits.createSheet.name.desc=no description"})
410 protected Sheet createSheet() {
411 Sheet sheet = super.createSheet();
412 Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
413 if (sheetSet == null) {
414 sheetSet = Sheet.createPropertiesSet();
415 sheet.put(sheetSet);
416 }
417
418 sheetSet.put(new NodeProperty<>(
419 KeywordHits_createSheet_name_name(),
420 KeywordHits_createSheet_name_displayName(),
421 KeywordHits_createSheet_name_desc(),
422 getName()));
423
424 return sheet;
425 }
426
427 @Override
428 public String getItemType() {
429 return getClass().getName();
430 }
431 }
432
433 private abstract class DetachableObserverChildFactory<X> extends ChildFactory.Detachable<X> implements Observer {
434
435 @Override
436 protected void addNotify() {
437 keywordResults.addObserver(this);
438 }
439
440 @Override
441 protected void finalize() throws Throwable {
442 super.finalize();
443 keywordResults.deleteObserver(this);
444 }
445
446 @Override
447 public void update(Observable o, Object arg) {
448 refresh(true);
449 }
450 }
451
455 private class ListFactory extends DetachableObserverChildFactory<String> {
456
457 private final PropertyChangeListener pcl = new PropertyChangeListener() {
458 @Override
459 public void propertyChange(PropertyChangeEvent evt) {
460 String eventType = evt.getPropertyName();
461 if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
468 try {
476 ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
477 if (null != eventData && eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.Type.TSK_KEYWORD_HIT.getTypeID()) {
478 keywordResults.update();
479 }
480 } catch (NoCurrentCaseException notUsed) {
481 // Case is closed, do nothing.
482 }
483 } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
484 || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
491 try {
493 keywordResults.update();
494 } catch (NoCurrentCaseException notUsed) {
495 // Case is closed, do nothing.
496 }
497 } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())
498 && evt.getNewValue() == null) {
499 /*
500 * Case was closed. Remove listeners so that we don't get
501 * called with a stale case handle
502 */
503 removeNotify();
504 skCase = null;
505 }
506
507 }
508 };
509
510 private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(pcl, null);
511
512 @Override
520
521 @Override
528
529 @Override
530 protected boolean createKeys(List<String> list) {
531 list.addAll(keywordResults.getListNames());
532 return true;
533 }
534
535 @Override
536 protected Node createNodeForKey(String key) {
537 return new ListNode(key);
538 }
539 }
540
541 private abstract class KWHitsNodeBase extends DisplayableItemNode implements Observer {
542
543 private String displayName;
544
545 private KWHitsNodeBase(Children children, Lookup lookup, String displayName) {
546 super(children, lookup);
547 this.displayName = displayName;
548 }
549
550 private KWHitsNodeBase(Children children) {
551 super(children);
552 }
553
554 @Override
555 public String getItemType() {
556 return getClass().getName();
557 }
558
559 @Override
560 public void update(Observable o, Object arg) {
561 updateDisplayName();
562 }
563
564 final void updateDisplayName() {
565 super.setDisplayName(displayName + " (" + countTotalDescendants() + ")");
566 }
567
568 abstract int countTotalDescendants();
569 }
570
575 class ListNode extends KWHitsNodeBase {
576
577 private final String listName;
578
579 private ListNode(String listName) {
580 super(Children.create(new TermFactory(listName), true), Lookups.singleton(listName), listName);
581 super.setName(listName);
582 this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/keyword_hits.png"); //NON-NLS
583 this.listName = listName;
584 updateDisplayName();
585 keywordResults.addObserver(this);
586 }
587
588 @Override
589 public int countTotalDescendants() {
590 int totalDescendants = 0;
591
592 for (String word : keywordResults.getKeywords(listName)) {
593 for (String instance : keywordResults.getKeywordInstances(listName, word)) {
594 Set<Long> ids = keywordResults.getArtifactIds(listName, word, instance);
595 totalDescendants += ids.size();
596 }
597 }
598 return totalDescendants;
599 }
600
601 @Override
602 @NbBundle.Messages({"KeywordHits.createSheet.listName.name=List Name",
603 "KeywordHits.createSheet.listName.displayName=List Name",
604 "KeywordHits.createSheet.listName.desc=no description",
605 "KeywordHits.createSheet.numChildren.name=Number of Children",
606 "KeywordHits.createSheet.numChildren.displayName=Number of Children",
607 "KeywordHits.createSheet.numChildren.desc=no description"})
608 protected Sheet createSheet() {
609 Sheet sheet = super.createSheet();
610 Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
611 if (sheetSet == null) {
612 sheetSet = Sheet.createPropertiesSet();
613 sheet.put(sheetSet);
614 }
615
616 sheetSet.put(new NodeProperty<>(
617 KeywordHits_createSheet_listName_name(),
618 KeywordHits_createSheet_listName_displayName(),
619 KeywordHits_createSheet_listName_desc(),
620 listName));
621
622 sheetSet.put(new NodeProperty<>(
623 KeywordHits_createSheet_numChildren_name(),
624 KeywordHits_createSheet_numChildren_displayName(),
625 KeywordHits_createSheet_numChildren_desc(),
626 keywordResults.getKeywords(listName).size()));
627
628 return sheet;
629 }
630
631 @Override
632 public boolean isLeafTypeNode() {
633 return false;
634 }
635
636 @Override
637 public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
638 return visitor.visit(this);
639 }
640 }
641
645 private class TermFactory extends DetachableObserverChildFactory<String> {
646
647 private final String setName;
648
649 private TermFactory(String setName) {
650 super();
651 this.setName = setName;
652 }
653
654 @Override
655 protected boolean createKeys(List<String> list) {
656 list.addAll(keywordResults.getKeywords(setName));
657 return true;
658 }
659
660 @Override
661 protected Node createNodeForKey(String key) {
662 return new TermNode(setName, key);
663 }
664 }
665
676 ChildFactory<?> createChildFactory(String setName, String keyword) {
677 if (isOnlyDefaultInstance(keywordResults.getKeywordInstances(setName, keyword))) {
678 return new HitsFactory(setName, keyword, DEFAULT_INSTANCE_NAME);
679 } else {
680 return new RegExpInstancesFactory(setName, keyword);
681 }
682 }
683
687 class TermNode extends KWHitsNodeBase {
688
689 private final String setName;
690 private final String keyword;
691
692 private TermNode(String setName, String keyword) {
693 super(Children.create(createChildFactory(setName, keyword), true), Lookups.singleton(keyword), keyword);
694
702 super.setName(setName + "_" + keyword);
703 this.setName = setName;
704 this.keyword = keyword;
705 this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/keyword_hits.png"); //NON-NLS
706 updateDisplayName();
707 keywordResults.addObserver(this);
708 }
709
710 @Override
711 int countTotalDescendants() {
712 return keywordResults.getKeywordInstances(setName, keyword).stream()
713 .mapToInt(instance -> keywordResults.getArtifactIds(setName, keyword, instance).size())
714 .sum();
715 }
716
717 @Override
718 public boolean isLeafTypeNode() {
719 // is this an exact/substring match (i.e. did we use the DEFAULT name)?
720 return isOnlyDefaultInstance(keywordResults.getKeywordInstances(setName, keyword));
721 }
722
723 @Override
724 public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
725 return visitor.visit(this);
726 }
727
728 @Override
729 @NbBundle.Messages({"KeywordHits.createSheet.filesWithHits.name=Files with Hits",
730 "KeywordHits.createSheet.filesWithHits.displayName=Files with Hits",
731 "KeywordHits.createSheet.filesWithHits.desc=no description"})
732 protected Sheet createSheet() {
733 Sheet sheet = super.createSheet();
734 Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
735 if (sheetSet == null) {
736 sheetSet = Sheet.createPropertiesSet();
737 sheet.put(sheetSet);
738 }
739 sheetSet.put(new NodeProperty<>(
740 KeywordHits_createSheet_listName_name(),
741 KeywordHits_createSheet_listName_displayName(),
742 KeywordHits_createSheet_listName_desc(),
743 getDisplayName()));
744
745 sheetSet.put(new NodeProperty<>(
746 KeywordHits_createSheet_filesWithHits_name(),
747 KeywordHits_createSheet_filesWithHits_displayName(),
748 KeywordHits_createSheet_filesWithHits_desc(),
749 countTotalDescendants()));
750
751 return sheet;
752 }
753 }
754
760
761 private final String keyword;
762 private final String setName;
763
764 private RegExpInstancesFactory(String setName, String keyword) {
765 super();
766 this.setName = setName;
767 this.keyword = keyword;
768 }
769
770 @Override
771 protected boolean createKeys(List<String> list) {
772 list.addAll(keywordResults.getKeywordInstances(setName, keyword));
773 return true;
774 }
775
776 @Override
777 protected Node createNodeForKey(String key) {
778 return new RegExpInstanceNode(setName, keyword, key);
779 }
780 }
781
785 class RegExpInstanceNode extends KWHitsNodeBase {
786
787 private final String setName;
788 private final String keyword;
789 private final String instance;
790
791 private RegExpInstanceNode(String setName, String keyword, String instance) {
792 super(Children.create(new HitsFactory(setName, keyword, instance), true), Lookups.singleton(instance), instance);
793
801 super.setName(setName + "_" + keyword + "_" + instance);
802 this.setName = setName;
803 this.keyword = keyword;
804 this.instance = instance;
805 this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/keyword_hits.png"); //NON-NLS
806 updateDisplayName();
807 keywordResults.addObserver(this);
808 }
809
810 @Override
811 int countTotalDescendants() {
812 return keywordResults.getArtifactIds(setName, keyword, instance).size();
813 }
814
815 @Override
816 public boolean isLeafTypeNode() {
817 return true;
818 }
819
820 @Override
821 public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
822 return visitor.visit(this);
823 }
824
825 @Override
826 protected Sheet createSheet() {
827 Sheet sheet = super.createSheet();
828 Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
829 if (sheetSet == null) {
830 sheetSet = Sheet.createPropertiesSet();
831 sheet.put(sheetSet);
832 }
833
834 sheetSet.put(new NodeProperty<>(
835 KeywordHits_createSheet_listName_name(),
836 KeywordHits_createSheet_listName_displayName(),
837 KeywordHits_createSheet_listName_desc(),
838 getDisplayName()));
839
840 sheetSet.put(new NodeProperty<>(
841 KeywordHits_createSheet_filesWithHits_name(),
842 KeywordHits_createSheet_filesWithHits_displayName(),
843 KeywordHits_createSheet_filesWithHits_desc(),
844 keywordResults.getArtifactIds(setName, keyword, instance).size()));
845
846 return sheet;
847 }
848
849 }
850
858 @NbBundle.Messages({"KeywordHits.createNodeForKey.modTime.name=ModifiedTime",
859 "KeywordHits.createNodeForKey.modTime.displayName=Modified Time",
860 "KeywordHits.createNodeForKey.modTime.desc=Modified Time",
861 "KeywordHits.createNodeForKey.accessTime.name=AccessTime",
862 "KeywordHits.createNodeForKey.accessTime.displayName=Access Time",
863 "KeywordHits.createNodeForKey.accessTime.desc=Access Time",
864 "KeywordHits.createNodeForKey.chgTime.name=ChangeTime",
865 "KeywordHits.createNodeForKey.chgTime.displayName=Change Time",
866 "KeywordHits.createNodeForKey.chgTime.desc=Change Time"})
868 if (skCase == null) {
869 return null;
870 }
871
872 BlackboardArtifactNode n = new BlackboardArtifactNode(art); //NON-NLS
873
874 // The associated file should be available through the Lookup that
875 // gets created when the BlackboardArtifactNode is constructed.
876 AbstractFile file = n.getLookup().lookup(AbstractFile.class);
877 if (file == null) {
878 try {
879 file = skCase.getAbstractFileById(art.getObjectID());
880 } catch (TskCoreException ex) {
881 logger.log(Level.SEVERE, "TskCoreException while constructing BlackboardArtifact Node from KeywordHitsKeywordChildren", ex); //NON-NLS
882 return n;
883 }
884 }
885 /*
886 * It is possible to get a keyword hit on artifacts generated for the
887 * underlying image in which case MAC times are not
888 * available/applicable/useful.
889 */
890 if (file == null) {
891 return n;
892 }
894 KeywordHits_createNodeForKey_modTime_name(),
895 KeywordHits_createNodeForKey_modTime_displayName(),
896 KeywordHits_createNodeForKey_modTime_desc(),
897 TimeZoneUtils.getFormattedTime(file.getMtime())));
899 KeywordHits_createNodeForKey_accessTime_name(),
900 KeywordHits_createNodeForKey_accessTime_displayName(),
901 KeywordHits_createNodeForKey_accessTime_desc(),
902 TimeZoneUtils.getFormattedTime(file.getAtime())));
904 KeywordHits_createNodeForKey_chgTime_name(),
905 KeywordHits_createNodeForKey_chgTime_displayName(),
906 KeywordHits_createNodeForKey_chgTime_desc(),
907 TimeZoneUtils.getFormattedTime(file.getCtime())));
908 return n;
909 }
910
914 private class HitsFactory extends BaseChildFactory<AnalysisResult> implements Observer {
915
916 private final String keyword;
917 private final String setName;
918 private final String instance;
919 private final Map<Long, AnalysisResult> artifactHits = new HashMap<>();
920
921 private HitsFactory(String setName, String keyword, String instance) {
928 super(setName + "_" + keyword + (DEFAULT_INSTANCE_NAME.equals(instance) ? "" : "_" + instance));
929 this.setName = setName;
930 this.keyword = keyword;
931 this.instance = instance;
932 }
933
934 @Override
935 protected List<AnalysisResult> makeKeys() {
936 if (skCase != null) {
937 keywordResults.getArtifactIds(setName, keyword, instance).forEach((id) -> {
938 try {
939 if (!artifactHits.containsKey(id)) {
940 AnalysisResult art = skCase.getBlackboard().getAnalysisResultById(id);
941 //Cache attributes while we are off the EDT.
942 //See JIRA-5969
943 art.getAttributes();
944 artifactHits.put(id, art);
945 }
946 } catch (TskCoreException ex) {
947 logger.log(Level.SEVERE, "TSK Exception occurred", ex); //NON-NLS
948 }
949 });
950
951 return new ArrayList<>(artifactHits.values());
952 }
953 return Collections.emptyList();
954 }
955
956 @Override
957 protected Node createNodeForKey(AnalysisResult art) {
959 }
960
961 @Override
962 protected void onAdd() {
963 keywordResults.addObserver(this);
964 }
965
966 @Override
967 protected void onRemove() {
968 keywordResults.deleteObserver(this);
969 }
970
971 @Override
972 public void update(Observable o, Object arg) {
973 refresh(true);
974 }
975 }
976}
static void removeEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition Case.java:757
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition Case.java:712
synchronized static Logger getLogger(String name)
Definition Logger.java:124
static String getFormattedTime(long epochTime)
UpdatableCountTypeNode(Children children, Lookup lookup, String baseName, long filteringDSObjId, BlackboardArtifact.Type... types)
HitsFactory(String setName, String keyword, String instance)
KWHitsNodeBase(Children children, Lookup lookup, String displayName)
final Map< String, Map< String, Map< String, Set< Long > > > > topLevelMap
BlackboardArtifactNode createBlackboardArtifactNode(AnalysisResult art)
static final Set< IngestManager.IngestJobEvent > INGEST_JOB_EVENTS_OF_INTEREST
static boolean isOnlyDefaultInstance(List< String > instances)
static final Set< IngestManager.IngestModuleEvent > INGEST_MODULE_EVENTS_OF_INTEREST
KeywordHits(SleuthkitCase skCase, long objId)
static synchronized IngestManager getInstance()
void removeIngestModuleEventListener(final PropertyChangeListener listener)
void removeIngestJobEventListener(final PropertyChangeListener listener)
void addIngestModuleEventListener(final PropertyChangeListener listener)
void addIngestJobEventListener(final PropertyChangeListener listener)

Copyright © 2012-2024 Sleuth Kit Labs. Generated on:
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.