19 package org.sleuthkit.autopsy.keywordsearch;
21 import java.awt.EventQueue;
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.Collection;
25 import java.util.HashMap;
26 import java.util.LinkedHashMap;
27 import java.util.List;
29 import java.util.concurrent.CancellationException;
30 import java.util.concurrent.ExecutionException;
31 import java.util.logging.Level;
32 import java.util.stream.Collectors;
33 import java.util.stream.Stream;
34 import javax.swing.SwingWorker;
35 import org.netbeans.api.progress.ProgressHandle;
36 import org.openide.nodes.ChildFactory;
37 import org.openide.nodes.Children;
38 import org.openide.nodes.Node;
39 import org.openide.util.NbBundle;
40 import org.openide.util.lookup.Lookups;
52 import static org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD;
53 import static org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW;
54 import static org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP;
66 class KeywordSearchResultFactory
extends ChildFactory<KeyValueQueryContent> {
68 private static final Logger logger = Logger.getLogger(KeywordSearchResultFactory.class.getName());
71 static final List<String> COMMON_PROPERTIES =
77 .map(BlackboardAttribute.ATTRIBUTE_TYPE::getDisplayName),
78 Arrays.stream(AbstractAbstractFileNode.AbstractFilePropertyType.values())
79 .map(Object::toString))
80 .collect(Collectors.toList());
82 private final Collection<QueryRequest> queryRequests;
84 KeywordSearchResultFactory(Collection<QueryRequest> queryRequests) {
85 this.queryRequests = queryRequests;
96 protected boolean createKeys(List<KeyValueQueryContent> toPopulate) {
98 for (QueryRequest queryRequest : queryRequests) {
102 if (!queryRequest.getQuery().validate()) {
108 Map<String, Object> map = queryRequest.getProperties();
114 COMMON_PROPERTIES.stream()
115 .forEach((propertyType) -> map.put(propertyType,
""));
116 map.put(TSK_KEYWORD.getDisplayName(), queryRequest.getQueryString());
117 map.put(TSK_KEYWORD_REGEXP.getDisplayName(), !queryRequest.getQuery().isLiteral());
119 createFlatKeys(queryRequest.getQuery(), toPopulate);
132 @NbBundle.Messages({
"KeywordSearchResultFactory.query.exception.msg=Could not perform the query "})
133 private boolean createFlatKeys(KeywordSearchQuery queryRequest, List<KeyValueQueryContent> toPopulate) {
138 QueryResults queryResults;
140 queryResults = queryRequest.performQuery();
141 }
catch (KeywordSearchModuleException | NoOpenCoreException ex) {
142 logger.log(Level.SEVERE,
"Could not perform the query " + queryRequest.getQueryString(), ex);
143 MessageNotifyUtil.Notify.error(Bundle.KeywordSearchResultFactory_query_exception_msg() + queryRequest.getQueryString(), ex.getCause().getMessage());
146 SleuthkitCase tskCase = null;
148 tskCase = Case.getCurrentCase().getSleuthkitCase();
149 }
catch (IllegalStateException ex) {
150 logger.log(Level.SEVERE,
"There was no case open.", ex);
155 List<KeyValueQueryContent> tempList =
new ArrayList<>();
156 for (KeywordHit hit : getOneHitPerObject(queryResults)) {
161 Map<String, Object> properties =
new LinkedHashMap<>();
162 Content content = null;
163 String contentName =
"";
165 content = tskCase.getContentById(hit.getContentID());
166 if (content == null) {
167 logger.log(Level.SEVERE,
"There was a error getting content by id.");
170 }
catch (TskCoreException ex) {
171 logger.log(Level.SEVERE,
"There was a error getting content by id.", ex);
175 contentName = content.getName();
176 if (content instanceof AbstractFile) {
177 AbstractFsContentNode.fillPropertyMap(properties, (AbstractFile) content);
179 properties.put(LOCATION.toString(), contentName);
185 if (hit.hasSnippet()) {
186 properties.put(TSK_KEYWORD_PREVIEW.getDisplayName(), hit.getSnippet());
190 if (hit.isArtifactHit()) {
192 hitName = tskCase.getBlackboardArtifact(hit.getArtifactID().get()).getDisplayName() +
" Artifact";
193 }
catch (TskCoreException ex) {
194 logger.log(Level.SEVERE,
"Error getting blckboard artifact by id", ex);
198 hitName = contentName;
201 tempList.add(
new KeyValueQueryContent(hitName, properties, hitNumber, hit.getSolrObjectId(), content, queryRequest, queryResults));
207 toPopulate.addAll(tempList);
213 new BlackboardResultWriter(queryResults, queryRequest.getKeywordList().getName()).execute();
227 Collection<KeywordHit> getOneHitPerObject(QueryResults queryResults) {
228 HashMap<Long, KeywordHit> hits =
new HashMap<>();
229 for (Keyword keyWord : queryResults.getKeywords()) {
230 for (KeywordHit hit : queryResults.getResults(keyWord)) {
232 if (!hits.containsKey(hit.getSolrObjectId())) {
233 hits.put(hit.getSolrObjectId(), hit);
234 }
else if (hit.getChunkId() < hits.get(hit.getSolrObjectId()).getChunkId()) {
235 hits.put(hit.getSolrObjectId(), hit);
239 return hits.values();
243 protected Node createNodeForKey(KeyValueQueryContent key) {
244 final Content content = key.getContent();
245 QueryResults hits = key.getHits();
247 Node kvNode =
new KeyValueNode(key, Children.LEAF, Lookups.singleton(content));
250 return new KeywordSearchFilterNode(hits, kvNode);
258 class KeyValueQueryContent
extends KeyValue {
260 private final long solrObjectId;
262 private final Content content;
263 private final QueryResults hits;
264 private final KeywordSearchQuery query;
279 KeyValueQueryContent(String name, Map<String, Object> map,
int id,
long solrObjectId, Content content, KeywordSearchQuery query, QueryResults hits) {
280 super(name, map,
id);
281 this.solrObjectId = solrObjectId;
282 this.content = content;
288 Content getContent() {
292 long getSolrObjectId() {
296 QueryResults getHits() {
300 KeywordSearchQuery getQuery() {
309 static class BlackboardResultWriter
extends SwingWorker<Void, Void> {
311 private static final List<BlackboardResultWriter> writers =
new ArrayList<>();
312 private ProgressHandle progress;
313 private final KeywordSearchQuery query;
314 private final QueryResults hits;
315 private static final int QUERY_DISPLAY_LEN = 40;
317 BlackboardResultWriter(QueryResults hits, String listName) {
319 this.query = hits.getQuery();
322 protected void finalizeWorker() {
323 deregisterWriter(
this);
324 EventQueue.invokeLater(progress::finish);
328 protected Void doInBackground() throws Exception {
329 registerWriter(
this);
330 final String queryStr = query.getQueryString();
331 final String queryDisp = queryStr.length() > QUERY_DISPLAY_LEN ? queryStr.substring(0, QUERY_DISPLAY_LEN - 1) +
" ..." : queryStr;
333 progress = ProgressHandle.createHandle(NbBundle.getMessage(
this.getClass(),
"KeywordSearchResultFactory.progress.saving", queryDisp), () -> BlackboardResultWriter.this.cancel(
true));
334 hits.writeAllHitsToBlackBoard(progress, null,
this,
false);
342 protected void done() {
345 }
catch (InterruptedException | CancellationException ex) {
346 logger.log(Level.WARNING,
"User cancelled writing of ad hoc search query results for '{0}' to the blackboard", query.getQueryString());
347 }
catch (ExecutionException ex) {
348 logger.log(Level.SEVERE,
"Error writing of ad hoc search query results for " + query.getQueryString() +
" to the blackboard", ex);
352 private static synchronized void registerWriter(BlackboardResultWriter writer) {
356 private static synchronized void deregisterWriter(BlackboardResultWriter writer) {
357 writers.remove(writer);
360 static synchronized void stopAllWriters() {
361 for (BlackboardResultWriter w : writers) {