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;
55 import static org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD;
56 import static org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW;
57 import static org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP;
69 class AdHocSearchChildFactory
extends ChildFactory<KeyValue> {
71 private static final Logger logger = Logger.getLogger(AdHocSearchChildFactory.class.getName());
74 static final List<String> COMMON_PROPERTIES
80 .map(BlackboardAttribute.ATTRIBUTE_TYPE::getDisplayName),
81 Arrays.stream(AbstractAbstractFileNode.AbstractFilePropertyType.values())
82 .map(Object::toString))
83 .collect(Collectors.toList());
85 private final Collection<AdHocQueryRequest> queryRequests;
86 private final boolean saveResults;
94 AdHocSearchChildFactory(Collection<AdHocQueryRequest> queryRequests,
boolean saveResults) {
95 this.queryRequests = queryRequests;
96 this.saveResults = saveResults;
107 protected boolean createKeys(List<KeyValue> toPopulate) {
109 for (AdHocQueryRequest queryRequest : queryRequests) {
113 if (!queryRequest.getQuery().validate()) {
119 Map<String, Object> map = queryRequest.getProperties();
125 COMMON_PROPERTIES.stream()
126 .forEach((propertyType) -> map.put(propertyType,
""));
127 map.put(TSK_KEYWORD.getDisplayName(), queryRequest.getQueryString());
128 map.put(TSK_KEYWORD_REGEXP.getDisplayName(), !queryRequest.getQuery().isLiteral());
130 createFlatKeys(queryRequest.getQuery(), toPopulate);
135 if (toPopulate.isEmpty()) {
136 toPopulate.add(
new KeyValue(
"This KeyValue Is Empty", 0));
149 @NbBundle.Messages({
"KeywordSearchResultFactory.query.exception.msg=Could not perform the query "})
150 private boolean createFlatKeys(KeywordSearchQuery queryRequest, List<KeyValue> toPopulate) {
155 QueryResults queryResults;
157 queryResults = queryRequest.performQuery();
158 }
catch (KeywordSearchModuleException | NoOpenCoreException ex) {
159 logger.log(Level.SEVERE,
"Could not perform the query " + queryRequest.getQueryString(), ex);
160 MessageNotifyUtil.Notify.error(Bundle.KeywordSearchResultFactory_query_exception_msg() + queryRequest.getQueryString(), ex.getCause().getMessage());
163 SleuthkitCase tskCase;
165 tskCase = Case.getCurrentCaseThrows().getSleuthkitCase();
166 }
catch (NoCurrentCaseException ex) {
167 logger.log(Level.SEVERE,
"There was no case open.", ex);
172 List<KeywordHitKey> tempList =
new ArrayList<>();
173 for (KeywordHit hit : getOneHitPerObject(queryResults)) {
178 Map<String, Object> properties =
new LinkedHashMap<>();
183 if (hit.hasSnippet()) {
184 properties.put(TSK_KEYWORD_PREVIEW.getDisplayName(), hit.getSnippet());
190 content = tskCase.getContentById(hit.getContentID());
191 if (content == null) {
192 logger.log(Level.SEVERE,
"There was a error getting content by id.");
195 }
catch (TskCoreException ex) {
196 logger.log(Level.SEVERE,
"There was a error getting content by id.", ex);
200 contentName = content.getName();
201 if (content instanceof AbstractFile) {
202 AbstractFsContentNode.fillPropertyMap(properties, (AbstractFile) content);
204 properties.put(LOCATION.toString(), contentName);
209 BlackboardArtifact artifact = null;
210 if (hit.isArtifactHit()) {
212 artifact = tskCase.getBlackboardArtifact(hit.getArtifactID().get());
213 hitName = artifact.getDisplayName() +
" Artifact";
214 }
catch (TskCoreException ex) {
215 logger.log(Level.SEVERE,
"Error getting blckboard artifact by id", ex);
219 hitName = contentName;
222 tempList.add(
new KeywordHitKey(hitName, properties, hitNumber, hit.getSolrObjectId(), content, artifact, queryRequest, queryResults));
226 if (hitNumber != 0) {
229 toPopulate.addAll(tempList);
236 new BlackboardResultWriter(queryResults, queryRequest.getKeywordList().getName(), saveResults).execute();
250 Collection<KeywordHit> getOneHitPerObject(QueryResults queryResults) {
251 HashMap<Long, KeywordHit> hits =
new HashMap<>();
252 for (Keyword keyWord : queryResults.getKeywords()) {
253 for (KeywordHit hit : queryResults.getResults(keyWord)) {
255 if (!hits.containsKey(hit.getSolrObjectId())) {
256 hits.put(hit.getSolrObjectId(), hit);
257 }
else if (hit.getChunkId() < hits.get(hit.getSolrObjectId()).getChunkId()) {
258 hits.put(hit.getSolrObjectId(), hit);
262 return hits.values();
265 @NbBundle.Messages({
"KeywordSearchResultFactory.createNodeForKey.noResultsFound.text=No results found."})
267 protected Node createNodeForKey(KeyValue key) {
270 if (key instanceof KeywordHitKey) {
271 AdHocQueryResult adHocQueryResult =
new AdHocQueryResult((KeywordHitKey) key);
277 ArrayList<Object> lookups =
new ArrayList<>();
278 lookups.add(adHocQueryResult);
279 if (((KeywordHitKey) key).getContent() != null) {
280 lookups.add(((KeywordHitKey) key).getContent());
282 if (((KeywordHitKey) key).getArtifact() != null) {
283 lookups.add(((KeywordHitKey) key).getArtifact());
286 Node kvNode =
new KeyValueNode(key, Children.LEAF, Lookups.fixed(lookups.toArray()));
289 resultNode =
new AdHocSearchFilterNode(kvNode);
291 resultNode =
new EmptyNode(
"This Node Is Empty");
292 resultNode.setDisplayName(NbBundle.getMessage(
this.getClass(),
"KeywordSearchResultFactory.createNodeForKey.noResultsFound.text"));
303 final class AdHocQueryResult {
305 private final long solrObjectId;
306 private final QueryResults results;
315 AdHocQueryResult(KeywordHitKey key) {
316 this.solrObjectId = key.getSolrObjectId();
317 this.results = key.getHits();
326 long getSolrObjectId() {
335 QueryResults getResults() {
344 class KeywordHitKey
extends KeyValue {
346 private final long solrObjectId;
348 private final Content content;
349 private final BlackboardArtifact artifact;
350 private final QueryResults hits;
351 private final KeywordSearchQuery query;
367 KeywordHitKey(String name, Map<String, Object> map,
int id,
long solrObjectId, Content content, BlackboardArtifact artifact, KeywordSearchQuery query, QueryResults hits) {
368 super(name, map,
id);
369 this.solrObjectId = solrObjectId;
370 this.content = content;
371 this.artifact = artifact;
377 Content getContent() {
381 BlackboardArtifact getArtifact() {
385 long getSolrObjectId() {
389 QueryResults getHits() {
393 KeywordSearchQuery getQuery() {
402 static class BlackboardResultWriter
extends SwingWorker<Void, Void> {
404 private static final List<BlackboardResultWriter> WRITERS =
new ArrayList<>();
405 private ProgressHandle progress;
406 private final KeywordSearchQuery query;
407 private final QueryResults hits;
408 private static final int QUERY_DISPLAY_LEN = 40;
409 private final boolean saveResults;
411 BlackboardResultWriter(QueryResults hits, String listName,
boolean saveResults) {
413 this.query = hits.getQuery();
414 this.saveResults = saveResults;
417 protected void finalizeWorker() {
418 deregisterWriter(
this);
419 EventQueue.invokeLater(progress::finish);
423 protected Void doInBackground() throws Exception {
424 registerWriter(
this);
425 final String queryStr = query.getQueryString();
426 final String queryDisp = queryStr.length() > QUERY_DISPLAY_LEN ? queryStr.substring(0, QUERY_DISPLAY_LEN - 1) +
" ..." : queryStr;
428 progress = ProgressHandle.createHandle(NbBundle.getMessage(
this.getClass(),
"KeywordSearchResultFactory.progress.saving", queryDisp), () -> BlackboardResultWriter.this.cancel(
true));
429 hits.process(progress, null,
this,
false, saveResults);
437 protected void done() {
440 }
catch (InterruptedException | CancellationException ex) {
441 logger.log(Level.WARNING,
"User cancelled writing of ad hoc search query results for '{0}' to the blackboard", query.getQueryString());
442 }
catch (ExecutionException ex) {
443 logger.log(Level.SEVERE,
"Error writing of ad hoc search query results for " + query.getQueryString() +
" to the blackboard", ex);
447 private static synchronized void registerWriter(BlackboardResultWriter writer) {
451 private static synchronized void deregisterWriter(BlackboardResultWriter writer) {
452 WRITERS.remove(writer);
455 static synchronized void stopAllWriters() {
456 for (BlackboardResultWriter w : WRITERS) {