mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-06 21:00:22 +00:00
- Added query manager to handle various query types, which delegates to proper query engines and child factories.
- more refactoring to handle word lists.
This commit is contained in:
parent
529afce917
commit
c8ffe6005f
@ -31,7 +31,7 @@ class KeywordSearch {
|
||||
private static final String BASE_URL = "http://localhost:8983/solr/";
|
||||
private static final Server SERVER = new Server(BASE_URL);
|
||||
|
||||
public enum QueryType {LUCENE, REGEX};
|
||||
public enum QueryType {WORD, REGEX};
|
||||
|
||||
public static final String NUM_FILES_CHANGE_EVT = "NUM_FILES_CHANGE_EVT";
|
||||
|
||||
|
@ -28,6 +28,7 @@ import javax.swing.JOptionPane;
|
||||
import org.openide.util.lookup.ServiceProvider;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataExplorer;
|
||||
import org.sleuthkit.autopsy.keywordsearch.KeywordSearch.QueryType;
|
||||
import org.sleuthkit.autopsy.keywordsearch.KeywordSearchQueryManager.Presentation;
|
||||
|
||||
/**
|
||||
* Provides a data explorer to perform Solr searches with
|
||||
@ -48,7 +49,7 @@ public class KeywordSearchDataExplorer implements DataExplorer {
|
||||
tc.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
||||
QueryType queryType = null;
|
||||
if (tc.isLuceneQuerySelected()) {
|
||||
queryType = QueryType.LUCENE;
|
||||
queryType = QueryType.WORD;
|
||||
} else {
|
||||
queryType = QueryType.REGEX;
|
||||
}
|
||||
@ -78,39 +79,16 @@ public class KeywordSearchDataExplorer implements DataExplorer {
|
||||
* @param solrQuery
|
||||
*/
|
||||
private void search(String query, QueryType queryType) {
|
||||
KeywordSearchQueryManager man = new KeywordSearchQueryManager(query, queryType, Presentation.DETAIL);
|
||||
|
||||
switch (queryType) {
|
||||
case LUCENE:
|
||||
searchLuceneQuery(query);
|
||||
break;
|
||||
case REGEX:
|
||||
searchRegexQuery(query);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a Lucene query and populates a DataResult tab with the results
|
||||
* @param solrQuery
|
||||
*/
|
||||
private void searchRegexQuery(String regexQuery) {
|
||||
TermComponentQuery rq = new TermComponentQuery(regexQuery);
|
||||
if (rq.validate()) {
|
||||
rq.execute();
|
||||
if (man.validate()) {
|
||||
man.execute();
|
||||
} else {
|
||||
displayErrorDialog("Invalid RegEx query syntax: " + regexQuery);
|
||||
displayErrorDialog("Invalid query syntax: " + query);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a Lucene query and populates a DataResult tab with the results
|
||||
* @param solrQuery
|
||||
*/
|
||||
private void searchLuceneQuery(String luceneQuery) {
|
||||
new LuceneQuery(luceneQuery).execute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.openide.windows.TopComponent getTopComponent() {
|
||||
|
@ -47,5 +47,17 @@ public interface KeywordSearchQuery {
|
||||
*/
|
||||
public void escape();
|
||||
|
||||
/**
|
||||
* return original query string
|
||||
* @return the query String supplied originally
|
||||
*/
|
||||
public String getQueryString();
|
||||
|
||||
/**
|
||||
* return escaped query string if escaping was done
|
||||
* @return the escaped query string, or original string if no escaping done
|
||||
*/
|
||||
public String getEscapedQueryString();
|
||||
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.keywordsearch;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.sleuthkit.autopsy.keywordsearch.KeywordSearch.QueryType;
|
||||
import org.sleuthkit.datamodel.FsContent;
|
||||
|
||||
/**
|
||||
* Query manager responsible for running appropriate queries and displaying results
|
||||
* for single, multi keyword queries, with detailed or collapsed results
|
||||
*/
|
||||
public class KeywordSearchQueryManager implements KeywordSearchQuery {
|
||||
|
||||
public enum Presentation {
|
||||
|
||||
COLLAPSE, DETAIL
|
||||
};
|
||||
//map query->boolean (true if literal, false otherwise)
|
||||
private Map<String, Boolean> queries;
|
||||
private Presentation presentation;
|
||||
private List<KeywordSearchQuery> queryDelegates;
|
||||
private QueryType queryType;
|
||||
private static Logger logger = Logger.getLogger(KeywordSearchQueryManager.class.getName());
|
||||
|
||||
public KeywordSearchQueryManager(Map<String, Boolean> queries, Presentation presentation) {
|
||||
this.queries = queries;
|
||||
this.presentation = presentation;
|
||||
queryType = QueryType.REGEX;
|
||||
init();
|
||||
}
|
||||
|
||||
public KeywordSearchQueryManager(String query, QueryType qt, Presentation presentation) {
|
||||
queries = new LinkedHashMap<String, Boolean>();
|
||||
queries.put(query, false);
|
||||
this.presentation = presentation;
|
||||
queryType = qt;
|
||||
init();
|
||||
}
|
||||
|
||||
public KeywordSearchQueryManager(String query, boolean isLiteral, Presentation presentation) {
|
||||
queries = new LinkedHashMap<String, Boolean>();
|
||||
queries.put(query, isLiteral);
|
||||
this.presentation = presentation;
|
||||
queryType = QueryType.REGEX;
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
queryDelegates = new ArrayList<KeywordSearchQuery>();
|
||||
for (String query : queries.keySet()) {
|
||||
KeywordSearchQuery del = null;
|
||||
switch (queryType) {
|
||||
case WORD:
|
||||
del = new LuceneQuery(query);
|
||||
break;
|
||||
case REGEX:
|
||||
del = new TermComponentQuery(query);
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
|
||||
escape();
|
||||
queryDelegates.add(del);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
|
||||
if (queryType == QueryType.WORD || presentation == Presentation.DETAIL) {
|
||||
for (KeywordSearchQuery q : queryDelegates) {
|
||||
q.execute();
|
||||
}
|
||||
} else {
|
||||
for (KeywordSearchQuery q : queryDelegates) {
|
||||
List<FsContent> fsContents = q.performQuery();
|
||||
//TODO create view, send to proper factory
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void escape() {
|
||||
for (KeywordSearchQuery q : queryDelegates) {
|
||||
boolean shouldEscape = queries.get(q.getQueryString());
|
||||
if (shouldEscape) {
|
||||
q.escape();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FsContent> performQuery() {
|
||||
//not done here
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEscapedQueryString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
final String SEP = queryType == QueryType.WORD ? " " : "|";
|
||||
for (KeywordSearchQuery q : queryDelegates) {
|
||||
sb.append(q.getEscapedQueryString()).append(SEP);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getQueryString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
final String SEP = queryType == QueryType.WORD ? " " : "|";
|
||||
for (KeywordSearchQuery q : queryDelegates) {
|
||||
sb.append(q.getQueryString()).append(SEP);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validate() {
|
||||
boolean allValid = true;
|
||||
for (KeywordSearchQuery tcq : queryDelegates) {
|
||||
if (!tcq.validate()) {
|
||||
logger.log(Level.WARNING, "Query has invalid syntax: " + tcq.getQueryString());
|
||||
allValid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return allValid;
|
||||
}
|
||||
}
|
@ -20,18 +20,14 @@ package org.sleuthkit.autopsy.keywordsearch;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import org.apache.solr.client.solrj.SolrQuery;
|
||||
import org.apache.solr.client.solrj.SolrServerException;
|
||||
import org.openide.nodes.ChildFactory;
|
||||
import org.openide.nodes.Children;
|
||||
import org.openide.nodes.Node;
|
||||
@ -39,22 +35,19 @@ import org.sleuthkit.autopsy.datamodel.AbstractFsContentNode;
|
||||
import org.sleuthkit.autopsy.datamodel.AbstractFsContentNode.FsContentPropertyType;
|
||||
import org.sleuthkit.autopsy.datamodel.KeyValueNode;
|
||||
import org.sleuthkit.autopsy.datamodel.KeyValueThing;
|
||||
import org.sleuthkit.autopsy.keywordsearch.KeywordSearchQueryManager.Presentation;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.File;
|
||||
import org.sleuthkit.datamodel.FsContent;
|
||||
|
||||
/**
|
||||
*
|
||||
* factory responsible for assembling nodes in the right way
|
||||
* factory produces top level nodes with query
|
||||
* responsible for assembling nodes and columns in the right way
|
||||
* and performing lazy queries as needed
|
||||
*/
|
||||
public class KeywordSearchResultFactory extends ChildFactory<KeyValueThing> {
|
||||
|
||||
public enum Presentation {
|
||||
|
||||
COLLAPSE, STRUCTURE
|
||||
};
|
||||
|
||||
//common properties (superset of all Node properties) to be displayed as columns
|
||||
//these are merged with FsContentPropertyType defined properties
|
||||
public static enum CommonPropertyTypes {
|
||||
@ -133,17 +126,66 @@ public class KeywordSearchResultFactory extends ChildFactory<KeyValueThing> {
|
||||
|
||||
@Override
|
||||
protected Node createNodeForKey(KeyValueThing thing) {
|
||||
return new KeyValueNode(thing, Children.create(new RegexResultChildFactory(things), true));
|
||||
ChildFactory<KeyValueThing> childFactory = null;
|
||||
switch (presentation) {
|
||||
case COLLAPSE:
|
||||
childFactory = null;
|
||||
break;
|
||||
case DETAIL:
|
||||
childFactory = new ResulTermsMatchesChildFactory(things);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
return new KeyValueNode(thing, Children.create(childFactory, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* factory produces collapsed view of all fscontent matches per query
|
||||
* the node produced is a child node
|
||||
* The factory actually executes query.
|
||||
*/
|
||||
class ResulCollapsedChildFactory extends ChildFactory<KeyValueThing> {
|
||||
|
||||
KeyValueThing queryThing;
|
||||
|
||||
ResulCollapsedChildFactory(KeyValueThing queryThing) {
|
||||
this.queryThing = queryThing;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean createKeys(List<KeyValueThing> toPopulate) {
|
||||
String origQuery = queryThing.getName();
|
||||
TermComponentQuery tcq = new TermComponentQuery(origQuery);
|
||||
Map<String,Object> map = new LinkedHashMap<String,Object>();
|
||||
if (tcq.validate()) {
|
||||
map.put("query_valid", true);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
map.put("query_valid", false);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//return toPopulate.addAll(things);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node createNodeForKey(KeyValueThing thing) {
|
||||
return new KeyValueNode(thing, Children.LEAF);
|
||||
//return new KeyValueNode(thing, Children.create(new ResultFilesChildFactory(thing), true));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* factory produces top level result nodes showing *exact* regex match result
|
||||
*/
|
||||
class RegexResultChildFactory extends ChildFactory<KeyValueThing> {
|
||||
class ResulTermsMatchesChildFactory extends ChildFactory<KeyValueThing> {
|
||||
|
||||
Collection<KeyValueThing> things;
|
||||
|
||||
RegexResultChildFactory(Collection<KeyValueThing> things) {
|
||||
ResulTermsMatchesChildFactory(Collection<KeyValueThing> things) {
|
||||
this.things = things;
|
||||
}
|
||||
|
||||
@ -155,7 +197,7 @@ public class KeywordSearchResultFactory extends ChildFactory<KeyValueThing> {
|
||||
@Override
|
||||
protected Node createNodeForKey(KeyValueThing thing) {
|
||||
//return new KeyValueNode(thing, Children.LEAF);
|
||||
return new KeyValueNode(thing, Children.create(new RegexResultDetailsChildFactory(thing), true));
|
||||
return new KeyValueNode(thing, Children.create(new ResultFilesChildFactory(thing), true));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -164,11 +206,11 @@ public class KeywordSearchResultFactory extends ChildFactory<KeyValueThing> {
|
||||
* To implement exact regex match detail view, we need to extract files content
|
||||
* returned by Lucene and further narrow down by applying a Java regex
|
||||
*/
|
||||
class RegexResultDetailsChildFactory extends ChildFactory<KeyValueThing> {
|
||||
class ResultFilesChildFactory extends ChildFactory<KeyValueThing> {
|
||||
|
||||
private KeyValueThing thing;
|
||||
|
||||
RegexResultDetailsChildFactory(KeyValueThing thing) {
|
||||
ResultFilesChildFactory(KeyValueThing thing) {
|
||||
this.thing = thing;
|
||||
}
|
||||
|
||||
@ -200,7 +242,7 @@ public class KeywordSearchResultFactory extends ChildFactory<KeyValueThing> {
|
||||
final Content content = thingContent.getContent();
|
||||
final String query = thingContent.getQuery();
|
||||
|
||||
final String contentStr = getSolrContent(content);
|
||||
final String contentStr = KeywordSearch.getServer().getCore().getSolrContent(content);
|
||||
|
||||
//make sure the file contains a match (this gets rid of large number of false positives)
|
||||
//TODO option in GUI to include approximate matches (faster)
|
||||
@ -228,19 +270,7 @@ public class KeywordSearchResultFactory extends ChildFactory<KeyValueThing> {
|
||||
}
|
||||
}
|
||||
|
||||
private String getSolrContent(final Content content) {
|
||||
final Server.Core solrCore = KeywordSearch.getServer().getCore();
|
||||
final SolrQuery q = new SolrQuery();
|
||||
q.setQuery("*:*");
|
||||
q.addFilterQuery("id:" + content.getId());
|
||||
q.setFields("content");
|
||||
try {
|
||||
return (String) solrCore.query(q).getResults().get(0).getFieldValue("content");
|
||||
} catch (SolrServerException ex) {
|
||||
logger.log(Level.WARNING, "Error getting content from Solr and validating regex match", ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -42,7 +42,6 @@ import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
public class LuceneQuery implements KeywordSearchQuery {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(LuceneQuery.class.getName());
|
||||
|
||||
private String query; //original unescaped query
|
||||
private String queryEscaped;
|
||||
private boolean isEscaped;
|
||||
@ -58,8 +57,17 @@ public class LuceneQuery implements KeywordSearchQuery {
|
||||
queryEscaped = KeywordSearchUtil.escapeLuceneQuery(query);
|
||||
isEscaped = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String getEscapedQueryString() {
|
||||
return this.queryEscaped;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getQueryString() {
|
||||
return this.query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Just perform the query and return result without updating the GUI
|
||||
* This utility is used in this class, can be potentially reused by other classes
|
||||
@ -76,7 +84,7 @@ public class LuceneQuery implements KeywordSearchQuery {
|
||||
Server.Core solrCore = KeywordSearch.getServer().getCore();
|
||||
|
||||
SolrQuery q = new SolrQuery();
|
||||
|
||||
|
||||
q.setQuery(queryEscaped);
|
||||
q.setRows(ROWS_PER_FETCH);
|
||||
q.setFields("id");
|
||||
@ -120,7 +128,7 @@ public class LuceneQuery implements KeywordSearchQuery {
|
||||
public void execute() {
|
||||
escape();
|
||||
List<FsContent> matches = performQuery();
|
||||
|
||||
|
||||
String pathText = "Lucene query: " + query;
|
||||
Node rootNode = new KeywordSearchNode(matches, query);
|
||||
Node filteredRootNode = new TableFilterNode(rootNode, true);
|
||||
@ -131,6 +139,6 @@ public class LuceneQuery implements KeywordSearchQuery {
|
||||
|
||||
@Override
|
||||
public boolean validate() {
|
||||
throw new UnsupportedOperationException("Not supported yet.");
|
||||
return query != null && ! query.equals("");
|
||||
}
|
||||
}
|
||||
|
@ -42,19 +42,22 @@ import org.apache.commons.httpclient.NoHttpResponseException;
|
||||
import org.openide.modules.InstalledFileLocator;
|
||||
import org.openide.util.Exceptions;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
|
||||
/**
|
||||
* Handles for keeping track of a Solr server and its cores
|
||||
*/
|
||||
class Server {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(Server.class.getName());
|
||||
|
||||
private static final String DEFAULT_CORE_NAME = "coreCase";
|
||||
// TODO: DEFAULT_CORE_NAME needs to be replaced with unique names to support multiple open cases
|
||||
|
||||
public static final String CORE_EVT = "CORE_EVT";
|
||||
public enum CORE_EVT_STATES { STOPPED, STARTED };
|
||||
public static final String CORE_EVT = "CORE_EVT";
|
||||
|
||||
public enum CORE_EVT_STATES {
|
||||
|
||||
STOPPED, STARTED
|
||||
};
|
||||
private CommonsHttpSolrServer solrServer;
|
||||
private String instanceDir;
|
||||
private File solrFolder;
|
||||
@ -70,22 +73,22 @@ class Server {
|
||||
} catch (MalformedURLException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
|
||||
|
||||
serverAction = new ServerAction();
|
||||
solrFolder = InstalledFileLocator.getDefault().locate("solr", Server.class.getPackage().getName(), false);
|
||||
instanceDir = solrFolder.getAbsolutePath() + File.separator + "solr";
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void finalize() throws java.lang.Throwable {
|
||||
stop();
|
||||
super.finalize();
|
||||
}
|
||||
|
||||
|
||||
public void addServerActionListener(PropertyChangeListener l) {
|
||||
serverAction.addPropertyChangeListener(l);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper threads to handle stderr/stdout from Solr process
|
||||
*/
|
||||
@ -111,8 +114,7 @@ class Server {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Tries to start a Solr instance in a separate process. Returns immediately
|
||||
* (probably before the server is ready) and doesn't check whether it was
|
||||
@ -122,11 +124,11 @@ class Server {
|
||||
logger.log(Level.INFO, "Starting Solr server from: " + solrFolder.getAbsolutePath());
|
||||
try {
|
||||
Process start = Runtime.getRuntime().exec("java -DSTOP.PORT=8079 -DSTOP.KEY=mysecret -jar start.jar", null, solrFolder);
|
||||
|
||||
|
||||
// Handle output to prevent process from blocking
|
||||
(new InputStreamPrinterThread(start.getInputStream())).start();
|
||||
(new InputStreamPrinterThread(start.getErrorStream())).start();
|
||||
|
||||
|
||||
} catch (IOException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
@ -144,15 +146,14 @@ class Server {
|
||||
logger.log(Level.INFO, "Stopping Solr server from: " + solrFolder.getAbsolutePath());
|
||||
Process stop = Runtime.getRuntime().exec("java -DSTOP.PORT=8079 -DSTOP.KEY=mysecret -jar start.jar --stop", null, solrFolder);
|
||||
return stop.waitFor() == 0;
|
||||
|
||||
|
||||
} catch (InterruptedException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
} catch (IOException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Tests if there's a Solr server running by sending it a core-status request.
|
||||
* @return false if the request failed with a connection error, otherwise true
|
||||
@ -165,9 +166,9 @@ class Server {
|
||||
|
||||
CoreAdminRequest.getStatus(null, solrServer);
|
||||
} catch (SolrServerException ex) {
|
||||
|
||||
|
||||
Throwable cause = ex.getRootCause();
|
||||
|
||||
|
||||
// TODO: check if SocketExceptions should actually happen (is
|
||||
// probably caused by starting a connection as the server finishes
|
||||
// shutting down)
|
||||
@ -182,11 +183,9 @@ class Server {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**** Convenience methods for use while we only open one case at a time ****/
|
||||
|
||||
private Core currentCore = null;
|
||||
|
||||
|
||||
void openCore() {
|
||||
if (currentCore != null) {
|
||||
throw new RuntimeException("Already an open Core!");
|
||||
@ -194,7 +193,7 @@ class Server {
|
||||
currentCore = openCore(Case.getCurrentCase());
|
||||
serverAction.putValue(CORE_EVT, CORE_EVT_STATES.STARTED);
|
||||
}
|
||||
|
||||
|
||||
void closeCore() {
|
||||
if (currentCore == null) {
|
||||
throw new RuntimeException("No currently open Core!");
|
||||
@ -203,18 +202,15 @@ class Server {
|
||||
currentCore = null;
|
||||
serverAction.putValue(CORE_EVT, CORE_EVT_STATES.STOPPED);
|
||||
}
|
||||
|
||||
|
||||
Core getCore() {
|
||||
if (currentCore == null) {
|
||||
throw new RuntimeException("No currently open Core!");
|
||||
}
|
||||
return currentCore;
|
||||
}
|
||||
|
||||
|
||||
/**** end single-case specific methods ****/
|
||||
|
||||
|
||||
/**** end single-case specific methods ****/
|
||||
/**
|
||||
* Open a core for the given case
|
||||
* @param c
|
||||
@ -258,7 +254,6 @@ class Server {
|
||||
|
||||
// handle to the core in Solr
|
||||
private String name;
|
||||
|
||||
// the server to access a core needs to be built from a URL with the
|
||||
// core in it, and is only good for core-specific operations
|
||||
private SolrServer solrCore;
|
||||
@ -271,20 +266,33 @@ class Server {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
Ingester getIngester() {
|
||||
|
||||
public Ingester getIngester() {
|
||||
return new Ingester(this.solrCore);
|
||||
}
|
||||
|
||||
QueryResponse query(SolrQuery sq) throws SolrServerException {
|
||||
|
||||
public QueryResponse query(SolrQuery sq) throws SolrServerException {
|
||||
return solrCore.query(sq);
|
||||
}
|
||||
|
||||
TermsResponse queryTerms(SolrQuery sq) throws SolrServerException {
|
||||
|
||||
public TermsResponse queryTerms(SolrQuery sq) throws SolrServerException {
|
||||
QueryResponse qres = solrCore.query(sq);
|
||||
return qres.getTermsResponse();
|
||||
}
|
||||
|
||||
|
||||
public String getSolrContent(final Content content) {
|
||||
final SolrQuery q = new SolrQuery();
|
||||
q.setQuery("*:*");
|
||||
q.addFilterQuery("id:" + content.getId());
|
||||
q.setFields("content");
|
||||
try {
|
||||
return (String) solrCore.query(q).getResults().get(0).getFieldValue("content");
|
||||
} catch (SolrServerException ex) {
|
||||
logger.log(Level.WARNING, "Error getting content from Solr and validating regex match", ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
void close() {
|
||||
try {
|
||||
CoreAdminRequest.unloadCore(this.name, solrServer);
|
||||
@ -294,7 +302,7 @@ class Server {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Execute query that gets only number of all Solr documents indexed
|
||||
* without actually returning the documents
|
||||
@ -302,17 +310,17 @@ class Server {
|
||||
* @throws SolrServerException
|
||||
*/
|
||||
public int queryNumIndexedFiles() throws SolrServerException {
|
||||
SolrQuery q = new SolrQuery("*:*");
|
||||
q.setRows(0);
|
||||
return (int)query(q).getResults().getNumFound();
|
||||
SolrQuery q = new SolrQuery("*:*");
|
||||
q.setRows(0);
|
||||
return (int) query(q).getResults().getNumFound();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class ServerAction extends AbstractAction {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
logger.log(Level.INFO, e.paramString().trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,9 +24,12 @@ import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.PatternSyntaxException;
|
||||
import javax.swing.SwingWorker;
|
||||
@ -42,6 +45,7 @@ import org.openide.nodes.Node;
|
||||
import org.openide.windows.TopComponent;
|
||||
import org.sleuthkit.autopsy.corecomponents.DataResultTopComponent;
|
||||
import org.sleuthkit.autopsy.datamodel.KeyValueThing;
|
||||
import org.sleuthkit.autopsy.keywordsearch.KeywordSearchQueryManager.Presentation;
|
||||
import org.sleuthkit.datamodel.FsContent;
|
||||
|
||||
public class TermComponentQuery implements KeywordSearchQuery {
|
||||
@ -51,31 +55,23 @@ public class TermComponentQuery implements KeywordSearchQuery {
|
||||
private static final String TERMS_SEARCH_FIELD = "content_ws";
|
||||
private static final String TERMS_HANDLER = "/terms";
|
||||
private static final int TERMS_TIMEOUT = 90 * 1000; //in ms
|
||||
|
||||
private static Logger logger = Logger.getLogger(TermComponentQuery.class.getName());
|
||||
|
||||
|
||||
|
||||
private String termsQuery;
|
||||
private String queryEscaped;
|
||||
private boolean isEscaped;
|
||||
|
||||
|
||||
public TermComponentQuery(String query) {
|
||||
this.termsQuery = query;
|
||||
this.queryEscaped = query;
|
||||
isEscaped = false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void escape() {
|
||||
//treat as literal
|
||||
queryEscaped = Pattern.quote(termsQuery);
|
||||
isEscaped = true;
|
||||
//will use prefix terms component query instead of regex
|
||||
//to treat the query as a word
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean validate() {
|
||||
@ -90,8 +86,10 @@ public class TermComponentQuery implements KeywordSearchQuery {
|
||||
return valid;
|
||||
}
|
||||
|
||||
|
||||
protected void executeQuery() {
|
||||
/*
|
||||
* helper method to create a Solr terms component query
|
||||
*/
|
||||
protected SolrQuery createQuery() {
|
||||
final SolrQuery q = new SolrQuery();
|
||||
q.setQueryType(TERMS_HANDLER);
|
||||
q.setTerms(true);
|
||||
@ -104,24 +102,90 @@ public class TermComponentQuery implements KeywordSearchQuery {
|
||||
q.addTermsField(TERMS_SEARCH_FIELD);
|
||||
q.setTimeAllowed(TERMS_TIMEOUT);
|
||||
|
||||
return q;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* execute query and return terms
|
||||
* helper method, can be called from the same or threaded query context
|
||||
*/
|
||||
protected List<Term> executeQuery(SolrQuery q) {
|
||||
Server.Core solrCore = KeywordSearch.getServer().getCore();
|
||||
|
||||
List<Term> terms = null;
|
||||
try {
|
||||
TermsResponse tr = solrCore.queryTerms(q);
|
||||
terms = tr.getTerms(TERMS_SEARCH_FIELD);
|
||||
return terms;
|
||||
} catch (SolrServerException ex) {
|
||||
logger.log(Level.SEVERE, "Error executing the regex terms query: " + termsQuery, ex);
|
||||
return null; //no need to create result view, just display error dialog
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getEscapedQueryString() {
|
||||
return this.queryEscaped;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getQueryString() {
|
||||
return this.termsQuery;
|
||||
}
|
||||
|
||||
/**
|
||||
* return collapsed matches with all files for the query
|
||||
* without per match breakdown
|
||||
*/
|
||||
@Override
|
||||
public List<FsContent> performQuery() {
|
||||
List<FsContent> results = new ArrayList();
|
||||
|
||||
final SolrQuery q = createQuery();
|
||||
List<Term> terms = executeQuery(q);
|
||||
//get unique match result files
|
||||
Set<FsContent> uniqueMatches = new TreeSet<FsContent>();
|
||||
|
||||
for (Term term : terms) {
|
||||
String word = term.getTerm();
|
||||
LuceneQuery filesQuery = new LuceneQuery(word);
|
||||
filesQuery.escape();
|
||||
List<FsContent> matches = filesQuery.performQuery();
|
||||
uniqueMatches.addAll(matches);
|
||||
}
|
||||
|
||||
//filter out non-matching files
|
||||
//escape regex if needed
|
||||
//(if the original query was not literal, this must be)
|
||||
String literalQuery = null;
|
||||
if (isEscaped) {
|
||||
literalQuery = this.queryEscaped;
|
||||
} else {
|
||||
literalQuery = Pattern.quote(this.termsQuery);
|
||||
}
|
||||
for (FsContent f : uniqueMatches) {
|
||||
Pattern p = Pattern.compile(literalQuery, Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
|
||||
final String contentStr = KeywordSearch.getServer().getCore().getSolrContent(f);
|
||||
Matcher m = p.matcher(contentStr);
|
||||
if (m.find()) {
|
||||
results.add(f);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
SolrQuery q = createQuery();
|
||||
|
||||
logger.log(Level.INFO, "Executing TermsComponent query: " + q.toString());
|
||||
|
||||
final SwingWorker worker = new TermsQueryWorker(q);
|
||||
worker.execute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FsContent> performQuery() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
executeQuery();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* map Terms to generic Nodes with key/value pairs properties
|
||||
@ -146,9 +210,9 @@ public class TermComponentQuery implements KeywordSearchQuery {
|
||||
}
|
||||
|
||||
Node rootNode = null;
|
||||
if (things.size() > 0) {
|
||||
if (things.size() > 0) {
|
||||
Children childThingNodes =
|
||||
Children.create(new KeywordSearchResultFactory(termsQuery, things, KeywordSearchResultFactory.Presentation.COLLAPSE), true);
|
||||
Children.create(new KeywordSearchResultFactory(termsQuery, things, Presentation.DETAIL), true);
|
||||
|
||||
rootNode = new AbstractNode(childThingNodes);
|
||||
} else {
|
||||
@ -156,17 +220,13 @@ public class TermComponentQuery implements KeywordSearchQuery {
|
||||
}
|
||||
|
||||
final String pathText = "RegEx query";
|
||||
// String pathText = "RegEx query: " + termsQuery
|
||||
// String pathText = "RegEx query: " + termsQuery
|
||||
//+ " Files with exact matches: " + Long.toString(totalMatches) + " (also listing approximate matches)";
|
||||
|
||||
TopComponent searchResultWin = DataResultTopComponent.createInstance("Keyword search", pathText, rootNode, things.size());
|
||||
searchResultWin.requestActive(); // make it the active top component
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class TermsQueryWorker extends SwingWorker<List<Term>, Void> {
|
||||
|
||||
@ -183,17 +243,7 @@ public class TermComponentQuery implements KeywordSearchQuery {
|
||||
progress.start();
|
||||
progress.progress("Running Terms query.");
|
||||
|
||||
Server.Core solrCore = KeywordSearch.getServer().getCore();
|
||||
|
||||
|
||||
List<Term> terms = null;
|
||||
try {
|
||||
TermsResponse tr = solrCore.queryTerms(q);
|
||||
terms = tr.getTerms(TERMS_SEARCH_FIELD);
|
||||
} catch (SolrServerException ex) {
|
||||
logger.log(Level.SEVERE, "Error executing the regex terms query: " + termsQuery, ex);
|
||||
return null; //no need to create result view, just display error dialog
|
||||
}
|
||||
List<Term> terms = executeQuery(q);
|
||||
|
||||
progress.progress("Terms query completed.");
|
||||
|
||||
@ -223,8 +273,6 @@ public class TermComponentQuery implements KeywordSearchQuery {
|
||||
progress.finish();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user