Basic keyword index/search functionality

This commit is contained in:
Peter J. Martel 2011-11-28 19:39:00 -05:00
parent 0c717f8dcb
commit 6338302fd6
15 changed files with 774 additions and 145 deletions

View File

@ -0,0 +1,66 @@
<?xml version="1.1" encoding="UTF-8" ?>
<Form version="1.4" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<EmptySpace pref="334" max="32767" attributes="0"/>
<Component id="sourceComboBox" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
<Component id="jScrollPane1" alignment="0" pref="400" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="sourceComboBox" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="jScrollPane1" pref="274" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javax.swing.JScrollPane" name="jScrollPane1">
<AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JTextPane" name="extractedTextPane">
<Properties>
<Property name="editable" type="boolean" value="false"/>
<Property name="autoscrolls" type="boolean" value="false"/>
</Properties>
</Component>
</SubComponents>
</Container>
<Component class="javax.swing.JComboBox" name="sourceComboBox">
<Properties>
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
<StringArray count="4">
<StringItem index="0" value="Item 1"/>
<StringItem index="1" value="Item 2"/>
<StringItem index="2" value="Item 3"/>
<StringItem index="3" value="Item 4"/>
</StringArray>
</Property>
</Properties>
</Component>
</SubComponents>
</Form>

View File

@ -0,0 +1,110 @@
/*
* 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.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.Collections;
import java.util.List;
class ExtractedContentPanel extends javax.swing.JPanel {
/** Creates new form ExtractedContentPanel */
ExtractedContentPanel() {
initComponents();
// DefaultCaret caret = (DefaultCaret)extractedTextPane.getCaret();
// caret.setUpdatePolicy(DefaultCaret.NEVER_UPDATE);
extractedTextPane.setContentType("text/html");
sourceComboBox.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED) {
setPanelText(((MarkupSource) e.getItem()).getMarkup());
}
}
});
setSources(Collections.EMPTY_LIST);
}
/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
jScrollPane1 = new javax.swing.JScrollPane();
extractedTextPane = new javax.swing.JTextPane();
sourceComboBox = new javax.swing.JComboBox();
extractedTextPane.setEditable(false);
extractedTextPane.setAutoscrolls(false);
jScrollPane1.setViewportView(extractedTextPane);
sourceComboBox.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" }));
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap(334, Short.MAX_VALUE)
.addComponent(sourceComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap())
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(sourceComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 274, Short.MAX_VALUE))
);
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JTextPane extractedTextPane;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JComboBox sourceComboBox;
// End of variables declaration//GEN-END:variables
void setSources(List<MarkupSource> sources) {
sourceComboBox.removeAllItems();
setPanelText(null);
for (MarkupSource ms : sources) {
sourceComboBox.addItem(ms);
}
if (!sources.isEmpty()) {
sourceComboBox.setSelectedIndex(0);
}
}
private void setPanelText(String text) {
extractedTextPane.setText(text);
extractedTextPane.setCaretPosition(0);
}
}

View File

@ -0,0 +1,149 @@
/*
* 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.awt.Component;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServer;
import org.apache.solr.client.solrj.SolrServerException;
import org.openide.nodes.Node;
import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
import org.sleuthkit.autopsy.datamodel.ContentNode;
@ServiceProvider(service = DataContentViewer.class)
public class ExtractedContentViewer implements DataContentViewer {
private static final Logger logger = Logger.getLogger(ExtractedContentViewer.class.getName());
private ExtractedContentPanel panel;
public ExtractedContentViewer() {
}
@Override
public void setNode(final ContentNode selectedNode) {
// to clear it
if (selectedNode == null) {
resetComponent();
return;
}
// custom markup from the node (if available) and default markup
// fetched from solr
List<MarkupSource> sources = new ArrayList<MarkupSource>();
sources.addAll(((Node) selectedNode).getLookup().lookupAll(MarkupSource.class));
sources.add(new MarkupSource() {
@Override
public String getMarkup() {
try {
String content = getSolrContent(selectedNode);
return "<pre>" + content + "</pre>";
} catch (SolrServerException ex) {
logger.log(Level.WARNING, "Couldn't get extracted content.", ex);
return "";
}
}
@Override
public String toString() {
return "Extracted Content";
}
});
// first source will be the default displayed
setPanel(sources);
}
@Override
public String getTitle() {
return "Extracted Content";
}
@Override
public DataContentViewer getInstance() {
return new ExtractedContentViewer();
}
@Override
public Component getComponent() {
if (panel == null) {
panel = new ExtractedContentPanel();
}
return panel;
}
@Override
public void resetComponent() {
setPanel(Collections.EMPTY_LIST);
}
@Override
public boolean isSupported(ContentNode node) {
if (node == null) {
return false;
}
Collection<? extends MarkupSource> sources = ((Node) node).getLookup().lookupAll(MarkupSource.class);
if (!sources.isEmpty()) {
return true;
}
SolrServer solr = Server.getServer().getSolr();
SolrQuery q = new SolrQuery();
q.setQuery("*:*");
q.addFilterQuery("id:" + node.getContent().getId());
q.setFields("id");
try {
return !solr.query(q).getResults().isEmpty();
} catch (SolrServerException ex) {
logger.log(Level.WARNING, "Couldn't determine whether content is supported.", ex);
return false;
}
}
private void setPanel(List<MarkupSource> sources) {
if (panel != null) {
panel.setSources(sources);
}
}
private String getSolrContent(ContentNode cNode) throws SolrServerException {
SolrServer solr = Server.getServer().getSolr();
SolrQuery q = new SolrQuery();
q.setQuery("*:*");
q.addFilterQuery("id:" + cNode.getContent().getId());
q.setFields("content");
String queryURL = q.toString();
String content = (String) solr.query(q).getResults().get(0).getFieldValue("content");
return content;
}
}

View File

@ -1,3 +1,21 @@
/*
* 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.sql.ResultSet;
@ -22,17 +40,14 @@ import org.sleuthkit.datamodel.TskException;
import org.sleuthkit.datamodel.Volume;
import org.sleuthkit.datamodel.VolumeSystem;
class GetIngestableFilesContentVisitor implements ContentVisitor<Collection<FsContent>> {
private static Logger logger = Logger.getLogger(GetIngestableFilesContentVisitor.class.getName());
// supported extensions list from http://www.lucidimagination.com/devzone/technical-articles/content-extraction-tika
private static final String[] supportedExtensions = {"tar", "jar", "zip", "bzip2",
"gz", "tgz", "doc", "xls", "ppt", "rtf", "pdf", "html", "xhtml", "txt",
"bmp", "gif", "png", "jpeg", "tiff", "mp3", "aiff", "au", "midi", "wav",
"pst", "xml", "class"};
// the full predicate of a SQLite statement to match supported extensions
private static final String extensionsLikePredicate;
@ -67,7 +82,7 @@ class GetIngestableFilesContentVisitor implements ContentVisitor<Collection<FsCo
public Collection<FsContent> visit(File file) {
String extension = getExtension(file.getName());
if (Arrays.asList(supportedExtensions).contains(extension)) {
return Collections.singleton((FsContent)file);
return Collections.singleton((FsContent) file);
} else {
return Collections.EMPTY_LIST;
}
@ -78,7 +93,7 @@ class GetIngestableFilesContentVisitor implements ContentVisitor<Collection<FsCo
SleuthkitCase sc = Case.getCurrentCase().getSleuthkitCase();
String query = "SELECT * FROM tsk_files WHERE fs_obj_id = " + fs.getId()
//+ " AND (" + extensionsLikePredicate + ")"
+ " AND (" + extensionsLikePredicate + ")"
+ " AND (known != " + FileKnown.KNOWN.toLong() + ")";
try {
ResultSet rs = sc.runQuery(query);
@ -104,7 +119,6 @@ class GetIngestableFilesContentVisitor implements ContentVisitor<Collection<FsCo
return getAllFromChildren(vs);
}
private Collection<FsContent> getAllFromChildren(Content c) {
Collection<FsContent> all = new ArrayList<FsContent>();

View File

@ -0,0 +1,68 @@
/*
* 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.List;
import java.util.logging.Logger;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.sleuthkit.datamodel.Content;
class HighlightedMatchesSource implements MarkupSource {
private static final Logger logger = Logger.getLogger(HighlightedMatchesSource.class.getName());
Content content;
String solrQuery;
HighlightedMatchesSource(Content content, String solrQuery) {
this.content = content;
this.solrQuery = solrQuery;
}
@Override
public String getMarkup() {
SolrQuery q = new SolrQuery();
q.setQuery(solrQuery);
q.addFilterQuery("id:" + content.getId());
q.addHighlightField("content");
q.setHighlightSimplePre("<span style=\"background:yellow\">");
q.setHighlightSimplePost("</span>");
q.setHighlightFragsize(0); // don't fragment the highlight
try {
QueryResponse response = Server.getServer().getSolr().query(q);
List<String> contentHighlights = response.getHighlighting().get(Long.toString(content.getId())).get("content");
if (contentHighlights == null) {
return "<span style=\"background:red\">No matches in content.</span>";
} else {
return "<pre>" + contentHighlights.get(0) + "</pre>";
}
} catch (SolrServerException ex) {
throw new RuntimeException(ex);
}
}
@Override
public String toString() {
return "Search Matches";
}
}

View File

@ -1,3 +1,21 @@
/*
* 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.awt.Dimension;
@ -62,7 +80,7 @@ public class IndexContentFilesAction extends AbstractAction {
return null;
}
this.publish("Indexing " + (finishedFiles+1) + "/" + fileCount + ": " + f.getName());
this.publish("Indexing " + (finishedFiles + 1) + "/" + fileCount + ": " + f.getName());
try {
ingester.ingest(f);
@ -126,6 +144,7 @@ public class IndexContentFilesAction extends AbstractAction {
popUpWindow.setLocation((int) ((screenDimension.getWidth() - w) / 2), (int) ((screenDimension.getHeight() - h) / 2));
popUpWindow.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
// deal with being Xed out of

View File

@ -1,20 +1,29 @@
/*
* IndexProgressPanel.java
* Autopsy Forensic Browser
*
* Created on Nov 15, 2011, 5:19:59 PM
* 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.awt.event.ActionListener;
/**
*
* @author pmartel
*/
public class IndexProgressPanel extends javax.swing.JPanel {
class IndexProgressPanel extends javax.swing.JPanel {
/** Creates new form IndexProgressPanel */
public IndexProgressPanel() {
IndexProgressPanel() {
initComponents();
progressBar.setMinimum(0);
progressBar.setMaximum(100);
@ -69,7 +78,6 @@ public class IndexProgressPanel extends javax.swing.JPanel {
private javax.swing.JLabel statusText;
// End of variables declaration//GEN-END:variables
/**
* Sets a listener for the Cancel button
* @param e The action listener

View File

@ -1,6 +1,23 @@
/*
* 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.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
@ -18,15 +35,13 @@ import org.apache.solr.common.SolrException;
import org.apache.solr.common.SolrException.ErrorCode;
import org.apache.solr.common.util.ContentStream;
import org.sleuthkit.datamodel.FsContent;
import org.sleuthkit.datamodel.TskException;
/**
* Handles ingesting files to a Solr server, given the url string for it
*/
class Ingester {
private static final Logger logger = Logger.getLogger(Ingester.class.getName());
private SolrServer solr;
private boolean uncommitedIngests = false;
@ -95,11 +110,12 @@ class Ingester {
// When Tika has problems with a document, it throws a server error
// but it's okay to continue with other documents
if (ec.equals(ErrorCode.SERVER_ERROR)) {
throw new IngesterException("Problem posting file contents to Solr. SolrException error code: " + ec , ex);
throw new IngesterException("Problem posting file contents to Solr. SolrException error code: " + ec, ex);
} else {
throw ex;
}
}
uncommitedIngests = true;
}
@ -151,17 +167,7 @@ class Ingester {
@Override
public InputStream getStream() throws IOException {
try {
long size = f.getSize();
if (size > 0) {
return new ByteArrayInputStream(f.read(0, f.getSize()));
} else {
// can't read files with size 0
return new ByteArrayInputStream(new byte[0]);
}
} catch (TskException ex) {
throw new IOException("Error reading file '" + f.getName() + "' (id: " + f.getId() + ")", ex);
}
return new ReadContentInputStream(f);
}
@Override
@ -171,6 +177,7 @@ class Ingester {
}
static class IngesterException extends Exception {
IngesterException(String message, Throwable ex) {
super(message, ex);
}

View File

@ -1,3 +1,21 @@
/*
* 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.awt.event.ActionEvent;
@ -22,7 +40,7 @@ import org.sleuthkit.autopsy.corecomponents.DataResultTopComponent;
import org.sleuthkit.datamodel.FsContent;
import org.sleuthkit.datamodel.SleuthkitCase;
@ServiceProvider(service=DataExplorer.class, position=300)
@ServiceProvider(service = DataExplorer.class, position = 300)
public class KeywordSearchDataExplorer implements DataExplorer {
private static KeywordSearchDataExplorer theInstance;
@ -90,6 +108,7 @@ public class KeywordSearchDataExplorer implements DataExplorer {
}
} catch (SolrServerException ex) {
throw new RuntimeException(ex);
// TODO: handle bad query strings, among other issues
} catch (SQLException ex) {
// TODO: handle error getting files from database
@ -98,13 +117,12 @@ public class KeywordSearchDataExplorer implements DataExplorer {
}
String pathText = "Solr query: " + solrQuery;
Node rootNode = new KeywordSearchNode(matches, pathText);
Node rootNode = new KeywordSearchNode(matches, solrQuery);
TopComponent searchResultWin = DataResultTopComponent.createInstance("Keyword search", pathText, rootNode, matches.size());
searchResultWin.requestActive(); // make it the active top component
}
@Override
public org.openide.windows.TopComponent getTopComponent() {
return this.tc;
@ -112,7 +130,5 @@ public class KeywordSearchDataExplorer implements DataExplorer {
@Override
public void propertyChange(PropertyChangeEvent evt) {
}
}

View File

@ -1,9 +1,31 @@
/*
* 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.sql.SQLException;
import java.util.List;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.Node;
import org.openide.util.Lookup;
import org.openide.util.lookup.Lookups;
import org.openide.util.lookup.ProxyLookup;
import org.sleuthkit.autopsy.datamodel.ContentFilterNode;
import org.sleuthkit.autopsy.datamodel.ContentNode;
import org.sleuthkit.autopsy.datamodel.ContentNodeVisitor;
import org.sleuthkit.autopsy.datamodel.RootContentChildren;
@ -12,11 +34,31 @@ import org.sleuthkit.datamodel.FsContent;
import org.sleuthkit.datamodel.TskException;
class KeywordSearchNode extends AbstractNode implements ContentNode {
private String searchText;
KeywordSearchNode(List<FsContent> keys, String searchText) {
super(new RootContentChildren(keys));
this.searchText = searchText;
private String solrQuery;
KeywordSearchNode(List<FsContent> keys, final String solrQuery) {
super(new RootContentChildren(keys) {
// Use filter node to add a MarkupSource for the search results
// to the lookup
@Override
protected Node[] createNodes(Content key) {
Node[] originalNodes = super.createNodes(key);
Node[] filterNodes = new Node[originalNodes.length];
int i = 0;
for (Node original : originalNodes) {
MarkupSource markup = new HighlightedMatchesSource(key, solrQuery);
Lookup filterLookup = new ProxyLookup(Lookups.singleton(markup), original.getLookup());
filterNodes[i++] = new ContentFilterNode((ContentNode) original, null, filterLookup);
}
return filterNodes;
}
});
this.solrQuery = solrQuery;
}
@Override
@ -30,21 +72,20 @@ class KeywordSearchNode extends AbstractNode implements ContentNode {
Object[][] objs;
int maxRows = 0;
if(totalNodes > rows){
if (totalNodes > rows) {
objs = new Object[rows][];
maxRows = rows;
}
else{
} else {
objs = new Object[totalNodes][];
maxRows = totalNodes;
}
for(int i = 0; i < maxRows; i++){
for (int i = 0; i < maxRows; i++) {
PropertySet[] props = getChildren().getNodeAt(i).getPropertySets();
Property[] property = props[0].getProperties();
objs[i] = new Object[property.length];
for(int j = 0; j < property.length; j++){
for (int j = 0; j < property.length; j++) {
try {
objs[i][j] = property[j].getValue();
} catch (Exception ex) {
@ -55,7 +96,6 @@ class KeywordSearchNode extends AbstractNode implements ContentNode {
return objs;
}
@Override
public byte[] read(long offset, long len) throws TskException {
throw new UnsupportedOperationException("Not supported yet.");
@ -68,7 +108,7 @@ class KeywordSearchNode extends AbstractNode implements ContentNode {
@Override
public String[] getDisplayPath() {
return new String[] {this.searchText};
return new String[]{"Solr query: " + this.solrQuery};
}
@Override
@ -80,5 +120,4 @@ class KeywordSearchNode extends AbstractNode implements ContentNode {
public <T> T accept(ContentNodeVisitor<T> v) {
throw new UnsupportedOperationException("Not supported yet.");
}
}

View File

@ -17,19 +17,11 @@
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="queryLabel" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="jScrollPane1" pref="302" max="32767" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="searchButton" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="jScrollPane1" alignment="0" pref="302" max="32767" attributes="0"/>
<Component id="searchButton" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>

View File

@ -1,22 +1,26 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
/*
* KeywordSearchTopComponent.java
* Autopsy Forensic Browser
*
* Created on Nov 17, 2011, 6:39:26 PM
* 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.awt.event.ActionListener;
import org.openide.windows.TopComponent;
/**
*
* @author pmartel
*/
public class KeywordSearchTopComponent extends TopComponent {
/** Creates new form KeywordSearchTopComponent */
@ -93,4 +97,14 @@ public class KeywordSearchTopComponent extends TopComponent {
return queryTextArea.getText();
}
/**
* Overwrite when you want to change default persistence type. Default
* persistence type is PERSISTENCE_ALWAYS
*
* @return TopComponent.PERSISTENCE_ALWAYS
*/
@Override
public int getPersistenceType() {
return TopComponent.PERSISTENCE_NEVER;
}
}

View File

@ -0,0 +1,34 @@
/*
* 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;
public interface MarkupSource {
/**
* @return text optionally marked up with the subsest of HTML that Swing
* components can handle in their setText() method.
*/
String getMarkup();
/**
* @return title of markup source
*/
@Override
String toString();
}

View File

@ -0,0 +1,87 @@
/*
* 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.io.IOException;
import java.io.InputStream;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskException;
class ReadContentInputStream extends InputStream {
private long position;
private long length;
private Content content;
ReadContentInputStream(Content content) {
this.content = content;
this.position = 0;
this.length = content.getSize();
}
@Override
public int read() throws IOException {
byte[] buff = new byte[1];
return (read(buff) != -1) ? buff[0] : -1;
}
@Override
public int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
// must return 0 for zero-length arrays
if (b.length == 0) {
return 0;
}
// will get an error from TSK if we try to read an empty file
if (this.length == 0) {
return -1;
}
if (position < length) {
// data remains to be read
int lenToRead = (int) Math.min(len, length - position);
try {
byte[] buff = content.read(position, lenToRead);
int lenRead = buff.length;
if (lenRead == 0) {
// TSK could not read the whole file, ending partway
return -1;
} else {
System.arraycopy(buff, 0, b, off, lenRead);
position += lenRead;
return lenRead;
}
} catch (TskException ex) {
throw new IOException(ex);
}
} else {
// at end of file
return -1;
}
}
}

View File

@ -1,28 +1,35 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
* 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.net.MalformedURLException;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServer;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.CommonsHttpSolrServer;
import org.apache.solr.client.solrj.response.QueryResponse;
/**
*
* @author pmartel
*/
public class Server {
class Server {
private static final String url = "http://localhost:8983/solr";
private static final Server S = new Server(url);
static Server getServer() {
return S;
}
private SolrServer solr;
Server(String url) {
@ -40,5 +47,4 @@ public class Server {
SolrServer getSolr() {
return this.solr;
}
}