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;
87 AdHocSearchChildFactory(Collection<AdHocQueryRequest> queryRequests) {
88 this.queryRequests = queryRequests;
99 protected boolean createKeys(List<KeyValue> toPopulate) {
101 for (AdHocQueryRequest queryRequest : queryRequests) {
105 if (!queryRequest.getQuery().validate()) {
111 Map<String, Object> map = queryRequest.getProperties();
117 COMMON_PROPERTIES.stream()
118 .forEach((propertyType) -> map.put(propertyType,
""));
119 map.put(TSK_KEYWORD.getDisplayName(), queryRequest.getQueryString());
120 map.put(TSK_KEYWORD_REGEXP.getDisplayName(), !queryRequest.getQuery().isLiteral());
122 createFlatKeys(queryRequest.getQuery(), toPopulate);
135 @NbBundle.Messages({
"KeywordSearchResultFactory.query.exception.msg=Could not perform the query "})
136 private boolean createFlatKeys(KeywordSearchQuery queryRequest, List<KeyValue> toPopulate) {
141 QueryResults queryResults;
143 queryResults = queryRequest.performQuery();
144 }
catch (KeywordSearchModuleException | NoOpenCoreException ex) {
145 logger.log(Level.SEVERE,
"Could not perform the query " + queryRequest.getQueryString(), ex);
146 MessageNotifyUtil.Notify.error(Bundle.KeywordSearchResultFactory_query_exception_msg() + queryRequest.getQueryString(), ex.getCause().getMessage());
149 SleuthkitCase tskCase;
151 tskCase = Case.getCurrentCaseThrows().getSleuthkitCase();
152 }
catch (NoCurrentCaseException ex) {
153 logger.log(Level.SEVERE,
"There was no case open.", ex);
158 List<KeywordHitKey> tempList =
new ArrayList<>();
159 for (KeywordHit hit : getOneHitPerObject(queryResults)) {
164 Map<String, Object> properties =
new LinkedHashMap<>();
168 content = tskCase.getContentById(hit.getContentID());
169 if (content == null) {
170 logger.log(Level.SEVERE,
"There was a error getting content by id.");
173 }
catch (TskCoreException ex) {
174 logger.log(Level.SEVERE,
"There was a error getting content by id.", ex);
178 contentName = content.getName();
179 if (content instanceof AbstractFile) {
180 AbstractFsContentNode.fillPropertyMap(properties, (AbstractFile) content);
182 properties.put(LOCATION.toString(), contentName);
188 if (hit.hasSnippet()) {
189 properties.put(TSK_KEYWORD_PREVIEW.getDisplayName(), hit.getSnippet());
193 BlackboardArtifact artifact = null;
194 if (hit.isArtifactHit()) {
196 artifact = tskCase.getBlackboardArtifact(hit.getArtifactID().get());
197 hitName = artifact.getDisplayName() +
" Artifact";
198 }
catch (TskCoreException ex) {
199 logger.log(Level.SEVERE,
"Error getting blckboard artifact by id", ex);
203 hitName = contentName;
206 tempList.add(
new KeywordHitKey(hitName, properties, hitNumber, hit.getSolrObjectId(), content, artifact, queryRequest, queryResults));
210 if (hitNumber == 0) {
211 toPopulate.add(
new KeyValue(
"This KeyValue Is Empty", 0));
215 toPopulate.addAll(tempList);
222 new BlackboardResultWriter(queryResults, queryRequest.getKeywordList().getName()).execute();
236 Collection<KeywordHit> getOneHitPerObject(QueryResults queryResults) {
237 HashMap<Long, KeywordHit> hits =
new HashMap<>();
238 for (Keyword keyWord : queryResults.getKeywords()) {
239 for (KeywordHit hit : queryResults.getResults(keyWord)) {
241 if (!hits.containsKey(hit.getSolrObjectId())) {
242 hits.put(hit.getSolrObjectId(), hit);
243 }
else if (hit.getChunkId() < hits.get(hit.getSolrObjectId()).getChunkId()) {
244 hits.put(hit.getSolrObjectId(), hit);
248 return hits.values();
251 @NbBundle.Messages({
"KeywordSearchResultFactory.createNodeForKey.noResultsFound.text=No results found."})
253 protected Node createNodeForKey(KeyValue key) {
256 if (key instanceof KeywordHitKey) {
257 AdHocQueryResult adHocQueryResult =
new AdHocQueryResult((KeywordHitKey) key);
263 ArrayList<Object> lookups =
new ArrayList<>();
264 lookups.add(adHocQueryResult);
265 if (((KeywordHitKey) key).getContent() != null) {
266 lookups.add(((KeywordHitKey) key).getContent());
268 if (((KeywordHitKey) key).getArtifact() != null) {
269 lookups.add(((KeywordHitKey) key).getArtifact());
272 Node kvNode =
new KeyValueNode(key, Children.LEAF, Lookups.fixed(lookups.toArray()));
275 resultNode =
new AdHocSearchFilterNode(kvNode);
277 resultNode =
new EmptyNode(
"This Node Is Empty");
278 resultNode.setDisplayName(NbBundle.getMessage(
this.getClass(),
"KeywordSearchResultFactory.createNodeForKey.noResultsFound.text"));
289 final class AdHocQueryResult {
291 private final long solrObjectId;
292 private final QueryResults results;
301 AdHocQueryResult(KeywordHitKey key) {
302 this.solrObjectId = key.getSolrObjectId();
303 this.results = key.getHits();
312 long getSolrObjectId() {
321 QueryResults getResults() {
330 class KeywordHitKey
extends KeyValue {
332 private final long solrObjectId;
334 private final Content content;
335 private final BlackboardArtifact artifact;
336 private final QueryResults hits;
337 private final KeywordSearchQuery query;
353 KeywordHitKey(String name, Map<String, Object> map,
int id,
long solrObjectId, Content content, BlackboardArtifact artifact, KeywordSearchQuery query, QueryResults hits) {
354 super(name, map,
id);
355 this.solrObjectId = solrObjectId;
356 this.content = content;
357 this.artifact = artifact;
363 Content getContent() {
367 BlackboardArtifact getArtifact() {
371 long getSolrObjectId() {
375 QueryResults getHits() {
379 KeywordSearchQuery getQuery() {
388 static class BlackboardResultWriter
extends SwingWorker<Void, Void> {
390 private static final List<BlackboardResultWriter> WRITERS =
new ArrayList<>();
391 private ProgressHandle progress;
392 private final KeywordSearchQuery query;
393 private final QueryResults hits;
394 private static final int QUERY_DISPLAY_LEN = 40;
396 BlackboardResultWriter(QueryResults hits, String listName) {
398 this.query = hits.getQuery();
401 protected void finalizeWorker() {
402 deregisterWriter(
this);
403 EventQueue.invokeLater(progress::finish);
407 protected Void doInBackground() throws Exception {
408 registerWriter(
this);
409 final String queryStr = query.getQueryString();
410 final String queryDisp = queryStr.length() > QUERY_DISPLAY_LEN ? queryStr.substring(0, QUERY_DISPLAY_LEN - 1) +
" ..." : queryStr;
412 progress = ProgressHandle.createHandle(NbBundle.getMessage(
this.getClass(),
"KeywordSearchResultFactory.progress.saving", queryDisp), () -> BlackboardResultWriter.this.cancel(
true));
413 hits.process(progress, null,
this,
false);
421 protected void done() {
424 }
catch (InterruptedException | CancellationException ex) {
425 logger.log(Level.WARNING,
"User cancelled writing of ad hoc search query results for '{0}' to the blackboard", query.getQueryString());
426 }
catch (ExecutionException ex) {
427 logger.log(Level.SEVERE,
"Error writing of ad hoc search query results for " + query.getQueryString() +
" to the blackboard", ex);
431 private static synchronized void registerWriter(BlackboardResultWriter writer) {
435 private static synchronized void deregisterWriter(BlackboardResultWriter writer) {
436 WRITERS.remove(writer);
439 static synchronized void stopAllWriters() {
440 for (BlackboardResultWriter w : WRITERS) {