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;
53 import static org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD;
54 import static org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW;
55 import static org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP;
67 class KeywordSearchResultFactory
extends ChildFactory<KeyValue> {
69 private static final Logger logger = Logger.getLogger(KeywordSearchResultFactory.class.getName());
72 static final List<String> COMMON_PROPERTIES
78 .map(BlackboardAttribute.ATTRIBUTE_TYPE::getDisplayName),
79 Arrays.stream(AbstractAbstractFileNode.AbstractFilePropertyType.values())
80 .map(Object::toString))
81 .collect(Collectors.toList());
83 private final Collection<QueryRequest> queryRequests;
85 KeywordSearchResultFactory(Collection<QueryRequest> queryRequests) {
86 this.queryRequests = queryRequests;
97 protected boolean createKeys(List<KeyValue> toPopulate) {
99 for (QueryRequest queryRequest : queryRequests) {
103 if (!queryRequest.getQuery().validate()) {
109 Map<String, Object> map = queryRequest.getProperties();
115 COMMON_PROPERTIES.stream()
116 .forEach((propertyType) -> map.put(propertyType,
""));
117 map.put(TSK_KEYWORD.getDisplayName(), queryRequest.getQueryString());
118 map.put(TSK_KEYWORD_REGEXP.getDisplayName(), !queryRequest.getQuery().isLiteral());
120 createFlatKeys(queryRequest.getQuery(), toPopulate);
133 @NbBundle.Messages({
"KeywordSearchResultFactory.query.exception.msg=Could not perform the query "})
134 private boolean createFlatKeys(KeywordSearchQuery queryRequest, List<KeyValue> toPopulate) {
139 QueryResults queryResults;
141 queryResults = queryRequest.performQuery();
142 }
catch (KeywordSearchModuleException | NoOpenCoreException ex) {
143 logger.log(Level.SEVERE,
"Could not perform the query " + queryRequest.getQueryString(), ex);
144 MessageNotifyUtil.Notify.error(Bundle.KeywordSearchResultFactory_query_exception_msg() + queryRequest.getQueryString(), ex.getCause().getMessage());
147 SleuthkitCase tskCase;
149 tskCase = Case.getCurrentCase().getSleuthkitCase();
150 }
catch (IllegalStateException ex) {
151 logger.log(Level.SEVERE,
"There was no case open.", ex);
156 List<KeyValueQueryContent> tempList =
new ArrayList<>();
157 for (KeywordHit hit : getOneHitPerObject(queryResults)) {
162 Map<String, Object> properties =
new LinkedHashMap<>();
166 content = tskCase.getContentById(hit.getContentID());
167 if (content == null) {
168 logger.log(Level.SEVERE,
"There was a error getting content by id.");
171 }
catch (TskCoreException ex) {
172 logger.log(Level.SEVERE,
"There was a error getting content by id.", ex);
176 contentName = content.getName();
177 if (content instanceof AbstractFile) {
178 AbstractFsContentNode.fillPropertyMap(properties, (AbstractFile) content);
180 properties.put(LOCATION.toString(), contentName);
186 if (hit.hasSnippet()) {
187 properties.put(TSK_KEYWORD_PREVIEW.getDisplayName(), hit.getSnippet());
191 BlackboardArtifact artifact = null;
192 if (hit.isArtifactHit()) {
194 artifact = tskCase.getBlackboardArtifact(hit.getArtifactID().get());
195 hitName = artifact.getDisplayName() +
" Artifact";
196 }
catch (TskCoreException ex) {
197 logger.log(Level.SEVERE,
"Error getting blckboard artifact by id", ex);
201 hitName = contentName;
204 tempList.add(
new KeyValueQueryContent(hitName, properties, hitNumber, hit.getSolrObjectId(), content, artifact, queryRequest, queryResults));
208 if (hitNumber == 0) {
209 toPopulate.add(
new KeyValue(
"This KeyValue Is Empty", 0));
213 toPopulate.addAll(tempList);
220 new BlackboardResultWriter(queryResults, queryRequest.getKeywordList().getName()).execute();
234 Collection<KeywordHit> getOneHitPerObject(QueryResults queryResults) {
235 HashMap<Long, KeywordHit> hits =
new HashMap<>();
236 for (Keyword keyWord : queryResults.getKeywords()) {
237 for (KeywordHit hit : queryResults.getResults(keyWord)) {
239 if (!hits.containsKey(hit.getSolrObjectId())) {
240 hits.put(hit.getSolrObjectId(), hit);
241 }
else if (hit.getChunkId() < hits.get(hit.getSolrObjectId()).getChunkId()) {
242 hits.put(hit.getSolrObjectId(), hit);
246 return hits.values();
249 @NbBundle.Messages({
"KeywordSearchResultFactory.createNodeForKey.noResultsFound.text=No results found."})
251 protected Node createNodeForKey(KeyValue key) {
254 if (key instanceof KeyValueQueryContent) {
255 AdHocQueryResult adHocQueryResult =
new AdHocQueryResult((KeyValueQueryContent) key);
257 Node kvNode =
new KeyValueNode(key, Children.LEAF);
260 resultNode =
new KeywordSearchFilterNode(adHocQueryResult, kvNode);
262 resultNode =
new EmptyNode(
"This Node Is Empty");
263 resultNode.setDisplayName(NbBundle.getMessage(
this.getClass(),
"KeywordSearchResultFactory.createNodeForKey.noResultsFound.text"));
274 final class AdHocQueryResult {
276 private final long solrObjectId;
277 private final Content content;
278 private final BlackboardArtifact artifact;
279 private final QueryResults results;
290 AdHocQueryResult(KeyValueQueryContent key) {
291 this.solrObjectId = key.getSolrObjectId();
292 this.content = key.getContent();
293 this.artifact = key.getArtifact();
294 this.results = key.getHits();
303 long getSolrObjectId() {
316 Content getContent() {
325 BlackboardArtifact getArtifact() {
334 QueryResults getResults() {
343 class KeyValueQueryContent
extends KeyValue {
345 private final long solrObjectId;
347 private final Content content;
348 private final BlackboardArtifact artifact;
349 private final QueryResults hits;
350 private final KeywordSearchQuery query;
366 KeyValueQueryContent(String name, Map<String, Object> map,
int id,
long solrObjectId, Content content, BlackboardArtifact artifact, KeywordSearchQuery query, QueryResults hits) {
367 super(name, map,
id);
368 this.solrObjectId = solrObjectId;
369 this.content = content;
370 this.artifact = artifact;
376 Content getContent() {
380 BlackboardArtifact getArtifact() {
384 long getSolrObjectId() {
388 QueryResults getHits() {
392 KeywordSearchQuery getQuery() {
401 static class BlackboardResultWriter
extends SwingWorker<Void, Void> {
403 private static final List<BlackboardResultWriter> WRITERS =
new ArrayList<>();
404 private ProgressHandle progress;
405 private final KeywordSearchQuery query;
406 private final QueryResults hits;
407 private static final int QUERY_DISPLAY_LEN = 40;
409 BlackboardResultWriter(QueryResults hits, String listName) {
411 this.query = hits.getQuery();
414 protected void finalizeWorker() {
415 deregisterWriter(
this);
416 EventQueue.invokeLater(progress::finish);
420 protected Void doInBackground() throws Exception {
421 registerWriter(
this);
422 final String queryStr = query.getQueryString();
423 final String queryDisp = queryStr.length() > QUERY_DISPLAY_LEN ? queryStr.substring(0, QUERY_DISPLAY_LEN - 1) +
" ..." : queryStr;
425 progress = ProgressHandle.createHandle(NbBundle.getMessage(
this.getClass(),
"KeywordSearchResultFactory.progress.saving", queryDisp), () -> BlackboardResultWriter.this.cancel(
true));
426 hits.process(progress, null,
this,
false);
434 protected void done() {
437 }
catch (InterruptedException | CancellationException ex) {
438 logger.log(Level.WARNING,
"User cancelled writing of ad hoc search query results for '{0}' to the blackboard", query.getQueryString());
439 }
catch (ExecutionException ex) {
440 logger.log(Level.SEVERE,
"Error writing of ad hoc search query results for " + query.getQueryString() +
" to the blackboard", ex);
444 private static synchronized void registerWriter(BlackboardResultWriter writer) {
448 private static synchronized void deregisterWriter(BlackboardResultWriter writer) {
449 WRITERS.remove(writer);
452 static synchronized void stopAllWriters() {
453 for (BlackboardResultWriter w : WRITERS) {