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<>();
182 content = tskCase.getContentById(hit.getContentID());
183 if (content == null) {
184 logger.log(Level.SEVERE,
"There was a error getting content by id.");
187 }
catch (TskCoreException ex) {
188 logger.log(Level.SEVERE,
"There was a error getting content by id.", ex);
192 contentName = content.getName();
193 if (content instanceof AbstractFile) {
194 AbstractFsContentNode.fillPropertyMap(properties, (AbstractFile) content);
196 properties.put(LOCATION.toString(), contentName);
202 if (hit.hasSnippet()) {
203 properties.put(TSK_KEYWORD_PREVIEW.getDisplayName(), hit.getSnippet());
207 BlackboardArtifact artifact = null;
208 if (hit.isArtifactHit()) {
210 artifact = tskCase.getBlackboardArtifact(hit.getArtifactID().get());
211 hitName = artifact.getDisplayName() +
" Artifact";
212 }
catch (TskCoreException ex) {
213 logger.log(Level.SEVERE,
"Error getting blckboard artifact by id", ex);
217 hitName = contentName;
220 tempList.add(
new KeywordHitKey(hitName, properties, hitNumber, hit.getSolrObjectId(), content, artifact, queryRequest, queryResults));
224 if (hitNumber != 0) {
227 toPopulate.addAll(tempList);
234 new BlackboardResultWriter(queryResults, queryRequest.getKeywordList().getName(), saveResults).execute();
248 Collection<KeywordHit> getOneHitPerObject(QueryResults queryResults) {
249 HashMap<Long, KeywordHit> hits =
new HashMap<>();
250 for (Keyword keyWord : queryResults.getKeywords()) {
251 for (KeywordHit hit : queryResults.getResults(keyWord)) {
253 if (!hits.containsKey(hit.getSolrObjectId())) {
254 hits.put(hit.getSolrObjectId(), hit);
255 }
else if (hit.getChunkId() < hits.get(hit.getSolrObjectId()).getChunkId()) {
256 hits.put(hit.getSolrObjectId(), hit);
260 return hits.values();
263 @NbBundle.Messages({
"KeywordSearchResultFactory.createNodeForKey.noResultsFound.text=No results found."})
265 protected Node createNodeForKey(KeyValue key) {
268 if (key instanceof KeywordHitKey) {
269 AdHocQueryResult adHocQueryResult =
new AdHocQueryResult((KeywordHitKey) key);
275 ArrayList<Object> lookups =
new ArrayList<>();
276 lookups.add(adHocQueryResult);
277 if (((KeywordHitKey) key).getContent() != null) {
278 lookups.add(((KeywordHitKey) key).getContent());
280 if (((KeywordHitKey) key).getArtifact() != null) {
281 lookups.add(((KeywordHitKey) key).getArtifact());
284 Node kvNode =
new KeyValueNode(key, Children.LEAF, Lookups.fixed(lookups.toArray()));
287 resultNode =
new AdHocSearchFilterNode(kvNode);
289 resultNode =
new EmptyNode(
"This Node Is Empty");
290 resultNode.setDisplayName(NbBundle.getMessage(
this.getClass(),
"KeywordSearchResultFactory.createNodeForKey.noResultsFound.text"));
301 final class AdHocQueryResult {
303 private final long solrObjectId;
304 private final QueryResults results;
313 AdHocQueryResult(KeywordHitKey key) {
314 this.solrObjectId = key.getSolrObjectId();
315 this.results = key.getHits();
324 long getSolrObjectId() {
333 QueryResults getResults() {
342 class KeywordHitKey
extends KeyValue {
344 private final long solrObjectId;
346 private final Content content;
347 private final BlackboardArtifact artifact;
348 private final QueryResults hits;
349 private final KeywordSearchQuery query;
365 KeywordHitKey(String name, Map<String, Object> map,
int id,
long solrObjectId, Content content, BlackboardArtifact artifact, KeywordSearchQuery query, QueryResults hits) {
366 super(name, map,
id);
367 this.solrObjectId = solrObjectId;
368 this.content = content;
369 this.artifact = artifact;
375 Content getContent() {
379 BlackboardArtifact getArtifact() {
383 long getSolrObjectId() {
387 QueryResults getHits() {
391 KeywordSearchQuery getQuery() {
400 static class BlackboardResultWriter
extends SwingWorker<Void, Void> {
402 private static final List<BlackboardResultWriter> WRITERS =
new ArrayList<>();
403 private ProgressHandle progress;
404 private final KeywordSearchQuery query;
405 private final QueryResults hits;
406 private static final int QUERY_DISPLAY_LEN = 40;
407 private final boolean saveResults;
409 BlackboardResultWriter(QueryResults hits, String listName,
boolean saveResults) {
411 this.query = hits.getQuery();
412 this.saveResults = saveResults;
415 protected void finalizeWorker() {
416 deregisterWriter(
this);
417 EventQueue.invokeLater(progress::finish);
421 protected Void doInBackground() throws Exception {
422 registerWriter(
this);
423 final String queryStr = query.getQueryString();
424 final String queryDisp = queryStr.length() > QUERY_DISPLAY_LEN ? queryStr.substring(0, QUERY_DISPLAY_LEN - 1) +
" ..." : queryStr;
426 progress = ProgressHandle.createHandle(NbBundle.getMessage(
this.getClass(),
"KeywordSearchResultFactory.progress.saving", queryDisp), () -> BlackboardResultWriter.this.cancel(
true));
427 hits.process(progress, null,
this,
false, saveResults);
435 protected void done() {
438 }
catch (InterruptedException | CancellationException ex) {
439 logger.log(Level.WARNING,
"User cancelled writing of ad hoc search query results for '{0}' to the blackboard", query.getQueryString());
440 }
catch (ExecutionException ex) {
441 logger.log(Level.SEVERE,
"Error writing of ad hoc search query results for " + query.getQueryString() +
" to the blackboard", ex);
445 private static synchronized void registerWriter(BlackboardResultWriter writer) {
449 private static synchronized void deregisterWriter(BlackboardResultWriter writer) {
450 WRITERS.remove(writer);
453 static synchronized void stopAllWriters() {
454 for (BlackboardResultWriter w : WRITERS) {