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.SwingUtilities;
35 import javax.swing.SwingWorker;
36 import org.netbeans.api.progress.ProgressHandle;
37 import org.openide.nodes.ChildFactory;
38 import org.openide.nodes.Children;
39 import org.openide.nodes.Node;
40 import org.openide.util.Cancellable;
41 import org.openide.util.NbBundle;
42 import org.openide.util.lookup.Lookups;
58 import static org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD;
59 import static org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW;
60 import static org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP;
72 class AdHocSearchChildFactory
extends ChildFactory<KeyValue> {
74 private static final Logger logger = Logger.getLogger(AdHocSearchChildFactory.class.getName());
77 static final List<String> COMMON_PROPERTIES
83 .map(BlackboardAttribute.ATTRIBUTE_TYPE::getDisplayName),
84 Arrays.stream(AbstractAbstractFileNode.AbstractFilePropertyType.values())
85 .map(Object::toString))
86 .collect(Collectors.toList());
88 private final Collection<AdHocQueryRequest> queryRequests;
89 private final boolean saveResults;
98 AdHocSearchChildFactory(Collection<AdHocQueryRequest> queryRequests,
boolean saveResults) {
99 this.queryRequests = queryRequests;
100 this.saveResults = saveResults;
111 protected boolean createKeys(List<KeyValue> toPopulate) {
113 for (AdHocQueryRequest queryRequest : queryRequests) {
117 if (!queryRequest.getQuery().validate()) {
123 Map<String, Object> map = queryRequest.getProperties();
129 COMMON_PROPERTIES.stream()
130 .forEach((propertyType) -> map.put(propertyType,
""));
131 map.put(TSK_KEYWORD.getDisplayName(), queryRequest.getQueryString());
132 map.put(TSK_KEYWORD_REGEXP.getDisplayName(), !queryRequest.getQuery().isLiteral());
134 createFlatKeys(queryRequest.getQuery(), toPopulate);
139 if (toPopulate.isEmpty()) {
140 toPopulate.add(
new KeyValue(
"This KeyValue Is Empty", 0));
153 @NbBundle.Messages({
"KeywordSearchResultFactory.query.exception.msg=Could not perform the query "})
154 private boolean createFlatKeys(KeywordSearchQuery queryRequest, List<KeyValue> toPopulate) {
159 QueryResults queryResults;
161 queryResults = queryRequest.performQuery();
162 }
catch (KeywordSearchModuleException | NoOpenCoreException ex) {
163 logger.log(Level.SEVERE,
"Could not perform the query " + queryRequest.getQueryString(), ex);
164 MessageNotifyUtil.Notify.error(Bundle.KeywordSearchResultFactory_query_exception_msg() + queryRequest.getQueryString(), ex.getCause().getMessage());
167 SleuthkitCase tskCase;
169 tskCase = Case.getCurrentCaseThrows().getSleuthkitCase();
170 }
catch (NoCurrentCaseException ex) {
171 logger.log(Level.SEVERE,
"There was no case open.", ex);
176 List<KeywordHitKey> tempList =
new ArrayList<>();
177 for (KeywordHit hit : getOneHitPerObject(queryResults)) {
182 Map<String, Object> properties =
new LinkedHashMap<>();
187 if (hit.hasSnippet()) {
188 properties.put(TSK_KEYWORD_PREVIEW.getDisplayName(), hit.getSnippet());
194 content = tskCase.getContentById(hit.getContentID());
195 if (content == null) {
196 logger.log(Level.SEVERE,
"There was a error getting content by id.");
199 }
catch (TskCoreException ex) {
200 logger.log(Level.SEVERE,
"There was a error getting content by id.", ex);
204 contentName = content.getName();
205 if (content instanceof AbstractFile) {
206 AbstractFsContentNode.fillPropertyMap(properties, (AbstractFile) content);
208 properties.put(LOCATION.toString(), contentName);
212 BlackboardArtifact artifact = null;
213 if (hit.isArtifactHit()) {
215 artifact = tskCase.getBlackboardArtifact(hit.getArtifactID().get());
216 hitName = artifact.getDisplayName() +
" Artifact";
217 }
catch (TskCoreException ex) {
218 logger.log(Level.SEVERE,
"Error getting blckboard artifact by id", ex);
222 hitName = contentName;
225 tempList.add(
new KeywordHitKey(hitName, properties, hitNumber, hit.getSolrObjectId(), content, artifact, queryRequest, queryResults));
229 if (hitNumber != 0) {
232 toPopulate.addAll(tempList);
239 new BlackboardResultWriter(queryResults, queryRequest.getKeywordList().getName(), saveResults).execute();
253 Collection<KeywordHit> getOneHitPerObject(QueryResults queryResults) {
254 HashMap<Long, KeywordHit> hits =
new HashMap<>();
255 for (Keyword keyWord : queryResults.getKeywords()) {
256 for (KeywordHit hit : queryResults.getResults(keyWord)) {
258 if (!hits.containsKey(hit.getSolrObjectId())) {
259 hits.put(hit.getSolrObjectId(), hit);
260 }
else if (hit.getChunkId() < hits.get(hit.getSolrObjectId()).getChunkId()) {
261 hits.put(hit.getSolrObjectId(), hit);
265 return hits.values();
268 @NbBundle.Messages({
"KeywordSearchResultFactory.createNodeForKey.noResultsFound.text=No results found."})
270 protected Node createNodeForKey(KeyValue key) {
273 if (key instanceof KeywordHitKey) {
274 AdHocQueryResult adHocQueryResult =
new AdHocQueryResult((KeywordHitKey) key);
280 ArrayList<Object> lookups =
new ArrayList<>();
281 lookups.add(adHocQueryResult);
282 if (((KeywordHitKey) key).getContent() != null) {
283 lookups.add(((KeywordHitKey) key).getContent());
285 if (((KeywordHitKey) key).getArtifact() != null) {
286 lookups.add(((KeywordHitKey) key).getArtifact());
289 Node kvNode =
new KeyValueNode(key, Children.LEAF, Lookups.fixed(lookups.toArray()));
292 resultNode =
new AdHocSearchFilterNode(kvNode);
294 resultNode =
new EmptyNode(
"This Node Is Empty");
295 resultNode.setDisplayName(NbBundle.getMessage(
this.getClass(),
"KeywordSearchResultFactory.createNodeForKey.noResultsFound.text"));
306 final class AdHocQueryResult {
308 private final long solrObjectId;
309 private final QueryResults results;
318 AdHocQueryResult(KeywordHitKey key) {
319 this.solrObjectId = key.getSolrObjectId();
320 this.results = key.getHits();
329 long getSolrObjectId() {
338 QueryResults getResults() {
347 class KeywordHitKey
extends KeyValue {
349 private final long solrObjectId;
351 private final Content content;
352 private final BlackboardArtifact artifact;
353 private final QueryResults hits;
354 private final KeywordSearchQuery query;
370 KeywordHitKey(String name, Map<String, Object> map,
int id,
long solrObjectId, Content content, BlackboardArtifact artifact, KeywordSearchQuery query, QueryResults hits) {
371 super(name, map,
id);
372 this.solrObjectId = solrObjectId;
373 this.content = content;
374 this.artifact = artifact;
380 Content getContent() {
384 BlackboardArtifact getArtifact() {
388 long getSolrObjectId() {
392 QueryResults getHits() {
396 KeywordSearchQuery getQuery() {
405 static class BlackboardResultWriter
extends SwingWorker<Void, Void> {
407 private static final List<BlackboardResultWriter> WRITERS =
new ArrayList<>();
408 private ProgressHandle progress;
409 private final KeywordSearchQuery query;
410 private final QueryResults hits;
411 private static final int QUERY_DISPLAY_LEN = 40;
412 private final boolean saveResults;
414 BlackboardResultWriter(QueryResults hits, String listName,
boolean saveResults) {
416 this.query = hits.getQuery();
417 this.saveResults = saveResults;
421 protected Void doInBackground() throws Exception {
423 if (RuntimeProperties.runningWithGUI()) {
424 final String queryStr = query.getQueryString();
425 final String queryDisp = queryStr.length() > QUERY_DISPLAY_LEN ? queryStr.substring(0, QUERY_DISPLAY_LEN - 1) +
" ..." : queryStr;
426 SwingUtilities.invokeLater(() -> {
427 progress = ProgressHandle.createHandle(
428 NbBundle.getMessage(
this.getClass(),
"KeywordSearchResultFactory.progress.saving", queryDisp),
431 public boolean cancel() {
433 logger.log(Level.INFO,
"Ad hoc search cancelled by user");
435 BlackboardResultWriter.this.cancel(
true);
442 registerWriter(
this);
443 hits.process(
this,
false, saveResults, null);
445 deregisterWriter(
this);
446 if (RuntimeProperties.runningWithGUI() && progress != null) {
447 EventQueue.invokeLater(progress::finish);
454 protected void done() {
457 }
catch (InterruptedException | CancellationException ex) {
458 logger.log(Level.WARNING,
"User cancelled writing of ad hoc search query results for '{0}' to the blackboard", query.getQueryString());
459 }
catch (ExecutionException ex) {
460 logger.log(Level.SEVERE,
"Error writing of ad hoc search query results for " + query.getQueryString() +
" to the blackboard", ex);
464 private static synchronized void registerWriter(BlackboardResultWriter writer) {
468 private static synchronized void deregisterWriter(BlackboardResultWriter writer) {
469 WRITERS.remove(writer);
472 static synchronized void stopAllWriters() {
473 for (BlackboardResultWriter w : WRITERS) {