19 package org.sleuthkit.autopsy.keywordsearch;
21 import java.awt.EventQueue;
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.HashMap;
25 import java.util.LinkedHashMap;
26 import java.util.List;
28 import java.util.concurrent.CancellationException;
29 import java.util.concurrent.ExecutionException;
30 import java.util.logging.Level;
31 import javax.swing.SwingWorker;
32 import org.netbeans.api.progress.ProgressHandle;
33 import org.openide.nodes.ChildFactory;
34 import org.openide.nodes.Children;
35 import org.openide.nodes.Node;
36 import org.openide.util.NbBundle;
37 import org.openide.util.lookup.Lookups;
58 class KeywordSearchResultFactory
extends ChildFactory<KeyValueQueryContent> {
66 public String toString() {
67 return BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getDisplayName();
72 public String toString() {
73 return BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getDisplayName();
78 public String toString() {
79 return BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW.getDisplayName();
83 private final Collection<QueryRequest> queryRequests;
87 this.queryRequests = queryRequests;
97 public static void initCommonProperties(Map<String, Object> toSet) {
98 CommonPropertyTypes[] commonTypes = CommonPropertyTypes.values();
99 final int COMMON_PROPS_LEN = commonTypes.length;
100 for (
int i = 0; i < COMMON_PROPS_LEN; ++i) {
101 toSet.put(commonTypes[i].toString(),
"");
104 AbstractAbstractFileNode.AbstractFilePropertyType[] fsTypes = AbstractAbstractFileNode.AbstractFilePropertyType.values();
105 final int FS_PROPS_LEN = fsTypes.length;
106 for (
int i = 0; i < FS_PROPS_LEN; ++i) {
107 toSet.put(fsTypes[i].toString(),
"");
111 public static void setCommonProperty(Map<String, Object> toSet, CommonPropertyTypes type, String value) {
112 final String typeStr = type.toString();
113 toSet.put(typeStr, value);
116 public static void setCommonProperty(Map<String, Object> toSet, CommonPropertyTypes type, Boolean value) {
117 final String typeStr = type.toString();
118 toSet.put(typeStr, value);
122 protected boolean createKeys(List<KeyValueQueryContent> toPopulate) {
124 for (QueryRequest queryRequest : queryRequests) {
125 Map<String, Object> map = queryRequest.getProperties();
126 initCommonProperties(map);
127 final String query = queryRequest.getQueryString();
128 setCommonProperty(map, CommonPropertyTypes.KEYWORD, query);
129 setCommonProperty(map, CommonPropertyTypes.REGEX, !queryRequest.getQuery().isLiteral());
130 createFlatKeys(queryRequest, toPopulate);
143 @NbBundle.Messages({
"KeywordSearchResultFactory.query.exception.msg=Could not perform the query "})
144 private boolean createFlatKeys(QueryRequest queryRequest, List<KeyValueQueryContent> toPopulate) {
148 final KeywordSearchQuery keywordSearchQuery = queryRequest.getQuery();
149 if (!keywordSearchQuery.validate()) {
157 QueryResults queryResults;
159 queryResults = keywordSearchQuery.performQuery();
160 }
catch (KeywordSearchModuleException | NoOpenCoreException ex) {
161 logger.log(Level.SEVERE,
"Could not perform the query " + keywordSearchQuery.getQueryString(), ex);
162 MessageNotifyUtil.Notify.error(Bundle.KeywordSearchResultFactory_query_exception_msg() + keywordSearchQuery.getQueryString(), ex.getCause().getMessage());
167 List<KeyValueQueryContent> tempList =
new ArrayList<>();
168 for (KeywordHit hit : getOneHitPerObject(queryResults)) {
172 Map<String, Object> properties =
new LinkedHashMap<>();
173 Content content = hit.getContent();
174 if (content instanceof AbstractFile) {
175 AbstractFsContentNode.fillPropertyMap(properties, (AbstractFile) content);
177 properties.put(AbstractAbstractFileNode.AbstractFilePropertyType.LOCATION.toString(), content.getName());
183 if (hit.hasSnippet()) {
184 setCommonProperty(properties, CommonPropertyTypes.CONTEXT, hit.getSnippet());
191 final String highlightQueryEscaped = getHighlightQuery(keywordSearchQuery, keywordSearchQuery.isLiteral(), queryResults, content);
193 String name = content.getName();
194 if (hit.isArtifactHit()) {
195 name = hit.getArtifact().getDisplayName() +
" Artifact";
198 tempList.add(
new KeyValueQueryContent(name, properties,
id, hit.getSolrObjectId(), content, highlightQueryEscaped, keywordSearchQuery, queryResults));
203 toPopulate.addAll(tempList);
209 new BlackboardResultWriter(queryResults, queryRequest.getQuery().getKeywordList().getName()).execute();
223 Collection<KeywordHit> getOneHitPerObject(QueryResults queryResults) {
224 HashMap<Long, KeywordHit> hits =
new HashMap<>();
225 for (Keyword keyWord : queryResults.getKeywords()) {
226 for (KeywordHit hit : queryResults.getResults(keyWord)) {
228 if (!hits.containsKey(hit.getSolrObjectId())) {
229 hits.put(hit.getSolrObjectId(), hit);
231 if (hit.getChunkId() < hits.get(hit.getSolrObjectId()).getChunkId()) {
232 hits.put(hit.getSolrObjectId(), hit);
237 return hits.values();
250 private String getHighlightQuery(KeywordSearchQuery query,
boolean literal_query, QueryResults queryResults, Content content) {
253 return constructEscapedSolrQuery(query.getQueryString(), literal_query);
257 if (queryResults.getKeywords().size() == 1) {
259 Keyword keyword = queryResults.getKeywords().iterator().next();
260 return constructEscapedSolrQuery(keyword.getSearchTerm(), literal_query);
263 List<Keyword> hitTerms =
new ArrayList<>();
264 for (Keyword keyword : queryResults.getKeywords()) {
265 for (KeywordHit hit : queryResults.getResults(keyword)) {
266 if (hit.getContent().equals(content)) {
267 hitTerms.add(keyword);
273 StringBuilder highlightQuery =
new StringBuilder();
274 final int lastTerm = hitTerms.size() - 1;
276 for (Keyword term : hitTerms) {
278 highlightQuery.append(constructEscapedSolrQuery(term.getSearchTerm(), literal_query));
279 if (lastTerm != curTerm) {
280 highlightQuery.append(
" ");
285 return highlightQuery.toString();
297 private String constructEscapedSolrQuery(String query,
boolean literal_query) {
298 StringBuilder highlightQuery =
new StringBuilder();
299 String highLightField;
301 highLightField = LuceneQuery.HIGHLIGHT_FIELD_LITERAL;
303 highLightField = LuceneQuery.HIGHLIGHT_FIELD_REGEX;
305 highlightQuery.append(highLightField).append(
":").append(
"\"").append(KeywordSearchUtil.escapeLuceneQuery(query)).append(
"\"");
306 return highlightQuery.toString();
310 protected Node createNodeForKey(KeyValueQueryContent key) {
311 final Content content = key.getContent();
312 final String queryStr = key.getQueryStr();
313 QueryResults hits = key.getHits();
315 Node kvNode =
new KeyValueNode(key, Children.LEAF, Lookups.singleton(content));
319 HighlightedText highlights =
new HighlightedText(key.solrObjectId, queryStr, !key.getQuery().isLiteral(),
false, hits);
320 return new KeywordSearchFilterNode(highlights, kvNode);
327 class KeyValueQueryContent
extends KeyValue {
329 private long solrObjectId;
330 private final Content content;
331 private final String queryStr;
332 private final QueryResults hits;
333 private final KeywordSearchQuery query;
350 public KeyValueQueryContent(String name, Map<String, Object> map,
int id,
long solrObjectId, Content content, String queryStr, KeywordSearchQuery query, QueryResults hits) {
351 super(name, map,
id);
352 this.solrObjectId = solrObjectId;
353 this.content = content;
354 this.queryStr = queryStr;
359 Content getContent() {
363 String getQueryStr() {
367 QueryResults getHits() {
371 KeywordSearchQuery getQuery() {
380 static class BlackboardResultWriter
extends SwingWorker<Object, Void> {
382 private static final List<BlackboardResultWriter> writers =
new ArrayList<>();
383 private ProgressHandle progress;
384 private final KeywordSearchQuery query;
385 private final QueryResults hits;
386 private Collection<BlackboardArtifact> newArtifacts =
new ArrayList<>();
387 private static final int QUERY_DISPLAY_LEN = 40;
389 BlackboardResultWriter(QueryResults hits, String listName) {
391 this.query = hits.getQuery();
394 protected void finalizeWorker() {
395 deregisterWriter(
this);
396 EventQueue.invokeLater(progress::finish);
400 protected Object doInBackground() throws Exception {
401 registerWriter(
this);
402 final String queryStr = query.getQueryString();
403 final String queryDisp = queryStr.length() > QUERY_DISPLAY_LEN ? queryStr.substring(0, QUERY_DISPLAY_LEN - 1) +
" ..." : queryStr;
405 progress = ProgressHandle.createHandle(NbBundle.getMessage(
this.getClass(),
"KeywordSearchResultFactory.progress.saving", queryDisp), () -> BlackboardResultWriter.this.cancel(
true));
406 newArtifacts = hits.writeAllHitsToBlackBoard(progress, null,
this,
false);
414 protected void done() {
417 }
catch (InterruptedException | CancellationException ex) {
418 logger.log(Level.WARNING,
"User cancelled writing of ad hoc search query results for '{0}' to the blackboard", query.getQueryString());
419 }
catch (ExecutionException ex) {
420 logger.log(Level.SEVERE,
"Error writing of ad hoc search query results for " + query.getQueryString() +
" to the blackboard", ex);
424 private static synchronized void registerWriter(BlackboardResultWriter writer) {
428 private static synchronized void deregisterWriter(BlackboardResultWriter writer) {
429 writers.remove(writer);
432 static synchronized void stopAllWriters() {
433 for (BlackboardResultWriter w : writers) {
synchronized static Logger getLogger(String name)