mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-06 21:00:22 +00:00
Basic keyword index/search functionality
This commit is contained in:
parent
0c717f8dcb
commit
6338302fd6
@ -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>
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
package org.sleuthkit.autopsy.keywordsearch;
|
||||||
|
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
@ -22,29 +40,26 @@ import org.sleuthkit.datamodel.TskException;
|
|||||||
import org.sleuthkit.datamodel.Volume;
|
import org.sleuthkit.datamodel.Volume;
|
||||||
import org.sleuthkit.datamodel.VolumeSystem;
|
import org.sleuthkit.datamodel.VolumeSystem;
|
||||||
|
|
||||||
|
|
||||||
class GetIngestableFilesContentVisitor implements ContentVisitor<Collection<FsContent>> {
|
class GetIngestableFilesContentVisitor implements ContentVisitor<Collection<FsContent>> {
|
||||||
|
|
||||||
private static Logger logger = Logger.getLogger(GetIngestableFilesContentVisitor.class.getName());
|
private static Logger logger = Logger.getLogger(GetIngestableFilesContentVisitor.class.getName());
|
||||||
|
|
||||||
// supported extensions list from http://www.lucidimagination.com/devzone/technical-articles/content-extraction-tika
|
// supported extensions list from http://www.lucidimagination.com/devzone/technical-articles/content-extraction-tika
|
||||||
private static final String[] supportedExtensions = {"tar", "jar", "zip", "bzip2",
|
private static final String[] supportedExtensions = {"tar", "jar", "zip", "bzip2",
|
||||||
"gz", "tgz", "doc", "xls", "ppt", "rtf", "pdf", "html", "xhtml", "txt",
|
"gz", "tgz", "doc", "xls", "ppt", "rtf", "pdf", "html", "xhtml", "txt",
|
||||||
"bmp", "gif", "png", "jpeg", "tiff", "mp3", "aiff", "au", "midi", "wav",
|
"bmp", "gif", "png", "jpeg", "tiff", "mp3", "aiff", "au", "midi", "wav",
|
||||||
"pst", "xml", "class"};
|
"pst", "xml", "class"};
|
||||||
|
|
||||||
// the full predicate of a SQLite statement to match supported extensions
|
// the full predicate of a SQLite statement to match supported extensions
|
||||||
private static final String extensionsLikePredicate;
|
private static final String extensionsLikePredicate;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
StringBuilder likes = new StringBuilder("0");
|
StringBuilder likes = new StringBuilder("0");
|
||||||
|
|
||||||
for (String ext : supportedExtensions) {
|
for (String ext : supportedExtensions) {
|
||||||
likes.append(" OR (name LIKE '%.");
|
likes.append(" OR (name LIKE '%.");
|
||||||
likes.append(ext);
|
likes.append(ext);
|
||||||
likes.append("')");
|
likes.append("')");
|
||||||
}
|
}
|
||||||
|
|
||||||
extensionsLikePredicate = likes.toString();
|
extensionsLikePredicate = likes.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,10 +67,10 @@ class GetIngestableFilesContentVisitor implements ContentVisitor<Collection<FsCo
|
|||||||
public Collection<FsContent> visit(Directory drctr) {
|
public Collection<FsContent> visit(Directory drctr) {
|
||||||
return getAllFromChildren(drctr);
|
return getAllFromChildren(drctr);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getExtension(String fileName) {
|
private String getExtension(String fileName) {
|
||||||
int lastDot = fileName.lastIndexOf(".");
|
int lastDot = fileName.lastIndexOf(".");
|
||||||
|
|
||||||
if (lastDot >= 0) {
|
if (lastDot >= 0) {
|
||||||
return fileName.substring(lastDot + 1, fileName.length()).toLowerCase();
|
return fileName.substring(lastDot + 1, fileName.length()).toLowerCase();
|
||||||
} else {
|
} else {
|
||||||
@ -67,7 +82,7 @@ class GetIngestableFilesContentVisitor implements ContentVisitor<Collection<FsCo
|
|||||||
public Collection<FsContent> visit(File file) {
|
public Collection<FsContent> visit(File file) {
|
||||||
String extension = getExtension(file.getName());
|
String extension = getExtension(file.getName());
|
||||||
if (Arrays.asList(supportedExtensions).contains(extension)) {
|
if (Arrays.asList(supportedExtensions).contains(extension)) {
|
||||||
return Collections.singleton((FsContent)file);
|
return Collections.singleton((FsContent) file);
|
||||||
} else {
|
} else {
|
||||||
return Collections.EMPTY_LIST;
|
return Collections.EMPTY_LIST;
|
||||||
}
|
}
|
||||||
@ -76,9 +91,9 @@ class GetIngestableFilesContentVisitor implements ContentVisitor<Collection<FsCo
|
|||||||
@Override
|
@Override
|
||||||
public Collection<FsContent> visit(FileSystem fs) {
|
public Collection<FsContent> visit(FileSystem fs) {
|
||||||
SleuthkitCase sc = Case.getCurrentCase().getSleuthkitCase();
|
SleuthkitCase sc = Case.getCurrentCase().getSleuthkitCase();
|
||||||
|
|
||||||
String query = "SELECT * FROM tsk_files WHERE fs_obj_id = " + fs.getId()
|
String query = "SELECT * FROM tsk_files WHERE fs_obj_id = " + fs.getId()
|
||||||
//+ " AND (" + extensionsLikePredicate + ")"
|
+ " AND (" + extensionsLikePredicate + ")"
|
||||||
+ " AND (known != " + FileKnown.KNOWN.toLong() + ")";
|
+ " AND (known != " + FileKnown.KNOWN.toLong() + ")";
|
||||||
try {
|
try {
|
||||||
ResultSet rs = sc.runQuery(query);
|
ResultSet rs = sc.runQuery(query);
|
||||||
@ -103,11 +118,10 @@ class GetIngestableFilesContentVisitor implements ContentVisitor<Collection<FsCo
|
|||||||
public Collection<FsContent> visit(VolumeSystem vs) {
|
public Collection<FsContent> visit(VolumeSystem vs) {
|
||||||
return getAllFromChildren(vs);
|
return getAllFromChildren(vs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private Collection<FsContent> getAllFromChildren(Content c) {
|
private Collection<FsContent> getAllFromChildren(Content c) {
|
||||||
Collection<FsContent> all = new ArrayList<FsContent>();
|
Collection<FsContent> all = new ArrayList<FsContent>();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (Content child : c.getChildren()) {
|
for (Content child : c.getChildren()) {
|
||||||
all.addAll(child.accept(this));
|
all.addAll(child.accept(this));
|
||||||
@ -115,7 +129,7 @@ class GetIngestableFilesContentVisitor implements ContentVisitor<Collection<FsCo
|
|||||||
} catch (TskException ex) {
|
} catch (TskException ex) {
|
||||||
logger.log(Level.SEVERE, "Error getting Content children", ex);
|
logger.log(Level.SEVERE, "Error getting Content children", ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
return all;
|
return all;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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";
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
package org.sleuthkit.autopsy.keywordsearch;
|
||||||
|
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
@ -37,7 +55,7 @@ public class IndexContentFilesAction extends AbstractAction {
|
|||||||
|
|
||||||
// create the popUp window for it
|
// create the popUp window for it
|
||||||
String title = "Indexing files in " + name;
|
String title = "Indexing files in " + name;
|
||||||
|
|
||||||
final JFrame frame = new JFrame(title);
|
final JFrame frame = new JFrame(title);
|
||||||
final JDialog popUpWindow = new JDialog(frame, title, true); // to make the popUp Window modal
|
final JDialog popUpWindow = new JDialog(frame, title, true); // to make the popUp Window modal
|
||||||
|
|
||||||
@ -62,7 +80,7 @@ public class IndexContentFilesAction extends AbstractAction {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.publish("Indexing " + (finishedFiles+1) + "/" + fileCount + ": " + f.getName());
|
this.publish("Indexing " + (finishedFiles + 1) + "/" + fileCount + ": " + f.getName());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ingester.ingest(f);
|
ingester.ingest(f);
|
||||||
@ -84,7 +102,7 @@ public class IndexContentFilesAction extends AbstractAction {
|
|||||||
if (!this.isCancelled()) {
|
if (!this.isCancelled()) {
|
||||||
get();
|
get();
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (InterruptedException ex) {
|
} catch (InterruptedException ex) {
|
||||||
// shouldn't be interrupted except by cancel
|
// shouldn't be interrupted except by cancel
|
||||||
throw new RuntimeException(ex);
|
throw new RuntimeException(ex);
|
||||||
@ -124,8 +142,9 @@ public class IndexContentFilesAction extends AbstractAction {
|
|||||||
double w = popUpWindow.getSize().getWidth();
|
double w = popUpWindow.getSize().getWidth();
|
||||||
double h = popUpWindow.getSize().getHeight();
|
double h = popUpWindow.getSize().getHeight();
|
||||||
popUpWindow.setLocation((int) ((screenDimension.getWidth() - w) / 2), (int) ((screenDimension.getHeight() - h) / 2));
|
popUpWindow.setLocation((int) ((screenDimension.getWidth() - w) / 2), (int) ((screenDimension.getHeight() - h) / 2));
|
||||||
|
|
||||||
popUpWindow.addWindowListener(new WindowAdapter() {
|
popUpWindow.addWindowListener(new WindowAdapter() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void windowClosing(WindowEvent e) {
|
public void windowClosing(WindowEvent e) {
|
||||||
// deal with being Xed out of
|
// deal with being Xed out of
|
||||||
@ -134,8 +153,8 @@ public class IndexContentFilesAction extends AbstractAction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
task.execute();
|
task.execute();
|
||||||
// display the window
|
// display the window
|
||||||
popUpWindow.setVisible(true);
|
popUpWindow.setVisible(true);
|
||||||
|
@ -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;
|
package org.sleuthkit.autopsy.keywordsearch;
|
||||||
|
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
|
|
||||||
/**
|
class IndexProgressPanel extends javax.swing.JPanel {
|
||||||
*
|
|
||||||
* @author pmartel
|
|
||||||
*/
|
|
||||||
public class IndexProgressPanel extends javax.swing.JPanel {
|
|
||||||
|
|
||||||
/** Creates new form IndexProgressPanel */
|
/** Creates new form IndexProgressPanel */
|
||||||
public IndexProgressPanel() {
|
IndexProgressPanel() {
|
||||||
initComponents();
|
initComponents();
|
||||||
progressBar.setMinimum(0);
|
progressBar.setMinimum(0);
|
||||||
progressBar.setMaximum(100);
|
progressBar.setMaximum(100);
|
||||||
@ -69,7 +78,6 @@ public class IndexProgressPanel extends javax.swing.JPanel {
|
|||||||
private javax.swing.JLabel statusText;
|
private javax.swing.JLabel statusText;
|
||||||
// End of variables declaration//GEN-END:variables
|
// End of variables declaration//GEN-END:variables
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a listener for the Cancel button
|
* Sets a listener for the Cancel button
|
||||||
* @param e The action listener
|
* @param e The action listener
|
||||||
@ -77,12 +85,12 @@ public class IndexProgressPanel extends javax.swing.JPanel {
|
|||||||
void addCancelButtonActionListener(ActionListener e) {
|
void addCancelButtonActionListener(ActionListener e) {
|
||||||
this.cancelButton.addActionListener(e);
|
this.cancelButton.addActionListener(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setProgressBar(int percent) {
|
void setProgressBar(int percent) {
|
||||||
progressBar.setIndeterminate(false);
|
progressBar.setIndeterminate(false);
|
||||||
progressBar.setValue(percent);
|
progressBar.setValue(percent);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setStatusText(String text) {
|
void setStatusText(String text) {
|
||||||
statusText.setText(text);
|
statusText.setText(text);
|
||||||
}
|
}
|
||||||
|
@ -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;
|
package org.sleuthkit.autopsy.keywordsearch;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
@ -18,18 +35,16 @@ import org.apache.solr.common.SolrException;
|
|||||||
import org.apache.solr.common.SolrException.ErrorCode;
|
import org.apache.solr.common.SolrException.ErrorCode;
|
||||||
import org.apache.solr.common.util.ContentStream;
|
import org.apache.solr.common.util.ContentStream;
|
||||||
import org.sleuthkit.datamodel.FsContent;
|
import org.sleuthkit.datamodel.FsContent;
|
||||||
import org.sleuthkit.datamodel.TskException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles ingesting files to a Solr server, given the url string for it
|
* Handles ingesting files to a Solr server, given the url string for it
|
||||||
*/
|
*/
|
||||||
class Ingester {
|
class Ingester {
|
||||||
private static final Logger logger = Logger.getLogger(Ingester.class.getName());
|
|
||||||
|
|
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(Ingester.class.getName());
|
||||||
private SolrServer solr;
|
private SolrServer solr;
|
||||||
private boolean uncommitedIngests = false;
|
private boolean uncommitedIngests = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* New Ingester connected to the server at given url
|
* New Ingester connected to the server at given url
|
||||||
* @param url Should be something like "http://localhost:8983/solr"
|
* @param url Should be something like "http://localhost:8983/solr"
|
||||||
@ -41,15 +56,15 @@ class Ingester {
|
|||||||
throw new RuntimeException(ex);
|
throw new RuntimeException(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ingester(SolrServer solr) {
|
Ingester(SolrServer solr) {
|
||||||
this.solr = solr;
|
this.solr = solr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void finalize() throws Throwable {
|
protected void finalize() throws Throwable {
|
||||||
super.finalize();
|
super.finalize();
|
||||||
|
|
||||||
// Warn if files might have been left uncommited.
|
// Warn if files might have been left uncommited.
|
||||||
if (uncommitedIngests) {
|
if (uncommitedIngests) {
|
||||||
logger.warning("Ingester was used to add files that it never committed!");
|
logger.warning("Ingester was used to add files that it never committed!");
|
||||||
@ -78,9 +93,9 @@ class Ingester {
|
|||||||
up.addContentStream(new FscContentStream(f));
|
up.addContentStream(new FscContentStream(f));
|
||||||
setFields(up, fields);
|
setFields(up, fields);
|
||||||
up.setAction(AbstractUpdateRequest.ACTION.COMMIT, true, true);
|
up.setAction(AbstractUpdateRequest.ACTION.COMMIT, true, true);
|
||||||
|
|
||||||
up.setParam("commit", "false");
|
up.setParam("commit", "false");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
solr.request(up);
|
solr.request(up);
|
||||||
// should't get any checked exceptions, but Tika problems result in
|
// should't get any checked exceptions, but Tika problems result in
|
||||||
@ -91,18 +106,19 @@ class Ingester {
|
|||||||
throw new RuntimeException(ex);
|
throw new RuntimeException(ex);
|
||||||
} catch (SolrException ex) {
|
} catch (SolrException ex) {
|
||||||
ErrorCode ec = ErrorCode.getErrorCode(ex.code());
|
ErrorCode ec = ErrorCode.getErrorCode(ex.code());
|
||||||
|
|
||||||
// When Tika has problems with a document, it throws a server error
|
// When Tika has problems with a document, it throws a server error
|
||||||
// but it's okay to continue with other documents
|
// but it's okay to continue with other documents
|
||||||
if (ec.equals(ErrorCode.SERVER_ERROR)) {
|
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 {
|
} else {
|
||||||
throw ex;
|
throw ex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uncommitedIngests = true;
|
uncommitedIngests = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void commit() {
|
void commit() {
|
||||||
uncommitedIngests = false;
|
uncommitedIngests = false;
|
||||||
try {
|
try {
|
||||||
@ -151,17 +167,7 @@ class Ingester {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InputStream getStream() throws IOException {
|
public InputStream getStream() throws IOException {
|
||||||
try {
|
return new ReadContentInputStream(f);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -169,8 +175,9 @@ class Ingester {
|
|||||||
throw new UnsupportedOperationException("Not supported yet.");
|
throw new UnsupportedOperationException("Not supported yet.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static class IngesterException extends Exception {
|
static class IngesterException extends Exception {
|
||||||
|
|
||||||
IngesterException(String message, Throwable ex) {
|
IngesterException(String message, Throwable ex) {
|
||||||
super(message, ex);
|
super(message, ex);
|
||||||
}
|
}
|
||||||
|
@ -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;
|
package org.sleuthkit.autopsy.keywordsearch;
|
||||||
|
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
@ -22,16 +40,16 @@ import org.sleuthkit.autopsy.corecomponents.DataResultTopComponent;
|
|||||||
import org.sleuthkit.datamodel.FsContent;
|
import org.sleuthkit.datamodel.FsContent;
|
||||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||||
|
|
||||||
@ServiceProvider(service=DataExplorer.class, position=300)
|
@ServiceProvider(service = DataExplorer.class, position = 300)
|
||||||
public class KeywordSearchDataExplorer implements DataExplorer {
|
public class KeywordSearchDataExplorer implements DataExplorer {
|
||||||
|
|
||||||
private static KeywordSearchDataExplorer theInstance;
|
private static KeywordSearchDataExplorer theInstance;
|
||||||
private KeywordSearchTopComponent tc;
|
private KeywordSearchTopComponent tc;
|
||||||
|
|
||||||
public KeywordSearchDataExplorer() {
|
public KeywordSearchDataExplorer() {
|
||||||
this.setTheInstance();
|
this.setTheInstance();
|
||||||
this.tc = new KeywordSearchTopComponent();
|
this.tc = new KeywordSearchTopComponent();
|
||||||
this.tc.addSearchButtonListener(new ActionListener() {
|
this.tc.addSearchButtonListener(new ActionListener() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
@ -39,7 +57,7 @@ public class KeywordSearchDataExplorer implements DataExplorer {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void setTheInstance() {
|
private synchronized void setTheInstance() {
|
||||||
if (theInstance == null) {
|
if (theInstance == null) {
|
||||||
theInstance = this;
|
theInstance = this;
|
||||||
@ -47,41 +65,41 @@ public class KeywordSearchDataExplorer implements DataExplorer {
|
|||||||
throw new RuntimeException("NOOO!!! Mulitple instances of KeywordSearchTopComponent! BAD!");
|
throw new RuntimeException("NOOO!!! Mulitple instances of KeywordSearchTopComponent! BAD!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void search(String solrQuery) {
|
private void search(String solrQuery) {
|
||||||
|
|
||||||
List<FsContent> matches = new ArrayList<FsContent>();
|
List<FsContent> matches = new ArrayList<FsContent>();
|
||||||
|
|
||||||
boolean allMatchesFetched = false;
|
boolean allMatchesFetched = false;
|
||||||
final int ROWS_PER_FETCH = 10000;
|
final int ROWS_PER_FETCH = 10000;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
SolrServer solr = Server.getServer().getSolr();
|
SolrServer solr = Server.getServer().getSolr();
|
||||||
|
|
||||||
SolrQuery q = new SolrQuery();
|
SolrQuery q = new SolrQuery();
|
||||||
q.setQuery(solrQuery);
|
q.setQuery(solrQuery);
|
||||||
q.setRows(ROWS_PER_FETCH);
|
q.setRows(ROWS_PER_FETCH);
|
||||||
q.setFields("id");
|
q.setFields("id");
|
||||||
|
|
||||||
for (int start = 0; !allMatchesFetched; start = start + ROWS_PER_FETCH) {
|
for (int start = 0; !allMatchesFetched; start = start + ROWS_PER_FETCH) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
q.setStart(start);
|
q.setStart(start);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
QueryResponse response = solr.query(q);
|
QueryResponse response = solr.query(q);
|
||||||
SolrDocumentList resultList = response.getResults();
|
SolrDocumentList resultList = response.getResults();
|
||||||
long results = resultList.getNumFound();
|
long results = resultList.getNumFound();
|
||||||
|
|
||||||
allMatchesFetched = start + ROWS_PER_FETCH >= results;
|
allMatchesFetched = start + ROWS_PER_FETCH >= results;
|
||||||
|
|
||||||
for (SolrDocument resultDoc : resultList) {
|
for (SolrDocument resultDoc : resultList) {
|
||||||
long id = Long.parseLong((String) resultDoc.getFieldValue("id"));
|
long id = Long.parseLong((String) resultDoc.getFieldValue("id"));
|
||||||
|
|
||||||
SleuthkitCase sc = Case.getCurrentCase().getSleuthkitCase();
|
SleuthkitCase sc = Case.getCurrentCase().getSleuthkitCase();
|
||||||
|
|
||||||
// TODO: has to be a better way to get files. Also, need to
|
// TODO: has to be a better way to get files. Also, need to
|
||||||
// check that we actually get 1 hit for each id
|
// check that we actually get 1 hit for each id
|
||||||
ResultSet rs = sc.runQuery("select * from tsk_files where obj_id=" + id);
|
ResultSet rs = sc.runQuery("select * from tsk_files where obj_id=" + id);
|
||||||
@ -90,21 +108,21 @@ public class KeywordSearchDataExplorer implements DataExplorer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} catch (SolrServerException ex) {
|
} catch (SolrServerException ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
// TODO: handle bad query strings, among other issues
|
// TODO: handle bad query strings, among other issues
|
||||||
} catch (SQLException ex) {
|
} catch (SQLException ex) {
|
||||||
// TODO: handle error getting files from database
|
// TODO: handle error getting files from database
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String pathText = "Solr query: " + solrQuery;
|
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());
|
TopComponent searchResultWin = DataResultTopComponent.createInstance("Keyword search", pathText, rootNode, matches.size());
|
||||||
searchResultWin.requestActive(); // make it the active top component
|
searchResultWin.requestActive(); // make it the active top component
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public org.openide.windows.TopComponent getTopComponent() {
|
public org.openide.windows.TopComponent getTopComponent() {
|
||||||
return this.tc;
|
return this.tc;
|
||||||
@ -112,7 +130,5 @@ public class KeywordSearchDataExplorer implements DataExplorer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void propertyChange(PropertyChangeEvent evt) {
|
public void propertyChange(PropertyChangeEvent evt) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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;
|
package org.sleuthkit.autopsy.keywordsearch;
|
||||||
|
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.openide.nodes.AbstractNode;
|
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.ContentNode;
|
||||||
import org.sleuthkit.autopsy.datamodel.ContentNodeVisitor;
|
import org.sleuthkit.autopsy.datamodel.ContentNodeVisitor;
|
||||||
import org.sleuthkit.autopsy.datamodel.RootContentChildren;
|
import org.sleuthkit.autopsy.datamodel.RootContentChildren;
|
||||||
@ -11,12 +33,32 @@ import org.sleuthkit.datamodel.Content;
|
|||||||
import org.sleuthkit.datamodel.FsContent;
|
import org.sleuthkit.datamodel.FsContent;
|
||||||
import org.sleuthkit.datamodel.TskException;
|
import org.sleuthkit.datamodel.TskException;
|
||||||
|
|
||||||
class KeywordSearchNode extends AbstractNode implements ContentNode {
|
class KeywordSearchNode extends AbstractNode implements ContentNode {
|
||||||
private String searchText;
|
|
||||||
|
private String solrQuery;
|
||||||
KeywordSearchNode(List<FsContent> keys, String searchText) {
|
|
||||||
super(new RootContentChildren(keys));
|
KeywordSearchNode(List<FsContent> keys, final String solrQuery) {
|
||||||
this.searchText = searchText;
|
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
|
@Override
|
||||||
@ -30,21 +72,20 @@ class KeywordSearchNode extends AbstractNode implements ContentNode {
|
|||||||
|
|
||||||
Object[][] objs;
|
Object[][] objs;
|
||||||
int maxRows = 0;
|
int maxRows = 0;
|
||||||
if(totalNodes > rows){
|
if (totalNodes > rows) {
|
||||||
objs = new Object[rows][];
|
objs = new Object[rows][];
|
||||||
maxRows = rows;
|
maxRows = rows;
|
||||||
}
|
} else {
|
||||||
else{
|
|
||||||
objs = new Object[totalNodes][];
|
objs = new Object[totalNodes][];
|
||||||
maxRows = totalNodes;
|
maxRows = totalNodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
for(int i = 0; i < maxRows; i++){
|
for (int i = 0; i < maxRows; i++) {
|
||||||
PropertySet[] props = getChildren().getNodeAt(i).getPropertySets();
|
PropertySet[] props = getChildren().getNodeAt(i).getPropertySets();
|
||||||
Property[] property = props[0].getProperties();
|
Property[] property = props[0].getProperties();
|
||||||
objs[i] = new Object[property.length];
|
objs[i] = new Object[property.length];
|
||||||
|
|
||||||
for(int j = 0; j < property.length; j++){
|
for (int j = 0; j < property.length; j++) {
|
||||||
try {
|
try {
|
||||||
objs[i][j] = property[j].getValue();
|
objs[i][j] = property[j].getValue();
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
@ -55,7 +96,6 @@ class KeywordSearchNode extends AbstractNode implements ContentNode {
|
|||||||
return objs;
|
return objs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] read(long offset, long len) throws TskException {
|
public byte[] read(long offset, long len) throws TskException {
|
||||||
throw new UnsupportedOperationException("Not supported yet.");
|
throw new UnsupportedOperationException("Not supported yet.");
|
||||||
@ -68,7 +108,7 @@ class KeywordSearchNode extends AbstractNode implements ContentNode {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] getDisplayPath() {
|
public String[] getDisplayPath() {
|
||||||
return new String[] {this.searchText};
|
return new String[]{"Solr query: " + this.solrQuery};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -80,5 +120,4 @@ class KeywordSearchNode extends AbstractNode implements ContentNode {
|
|||||||
public <T> T accept(ContentNodeVisitor<T> v) {
|
public <T> T accept(ContentNodeVisitor<T> v) {
|
||||||
throw new UnsupportedOperationException("Not supported yet.");
|
throw new UnsupportedOperationException("Not supported yet.");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -17,19 +17,11 @@
|
|||||||
<DimensionLayout dim="0">
|
<DimensionLayout dim="0">
|
||||||
<Group type="103" groupAlignment="0" attributes="0">
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
<Group type="102" attributes="0">
|
<Group type="102" attributes="0">
|
||||||
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
<Group type="103" groupAlignment="0" attributes="0">
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
<Group type="102" attributes="0">
|
<Component id="queryLabel" min="-2" max="-2" attributes="0"/>
|
||||||
<EmptySpace max="-2" attributes="0"/>
|
<Component id="jScrollPane1" alignment="0" pref="302" max="32767" attributes="0"/>
|
||||||
<Component id="queryLabel" min="-2" max="-2" attributes="0"/>
|
<Component id="searchButton" alignment="0" 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>
|
|
||||||
</Group>
|
</Group>
|
||||||
<EmptySpace max="-2" attributes="0"/>
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
</Group>
|
</Group>
|
||||||
|
@ -1,22 +1,26 @@
|
|||||||
/*
|
/*
|
||||||
* To change this template, choose Tools | Templates
|
* Autopsy Forensic Browser
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* KeywordSearchTopComponent.java
|
|
||||||
*
|
*
|
||||||
* 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;
|
package org.sleuthkit.autopsy.keywordsearch;
|
||||||
|
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
import org.openide.windows.TopComponent;
|
import org.openide.windows.TopComponent;
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author pmartel
|
|
||||||
*/
|
|
||||||
public class KeywordSearchTopComponent extends TopComponent {
|
public class KeywordSearchTopComponent extends TopComponent {
|
||||||
|
|
||||||
/** Creates new form KeywordSearchTopComponent */
|
/** Creates new form KeywordSearchTopComponent */
|
||||||
@ -88,9 +92,19 @@ public class KeywordSearchTopComponent extends TopComponent {
|
|||||||
void addSearchButtonListener(ActionListener l) {
|
void addSearchButtonListener(ActionListener l) {
|
||||||
searchButton.addActionListener(l);
|
searchButton.addActionListener(l);
|
||||||
}
|
}
|
||||||
|
|
||||||
String getQueryText() {
|
String getQueryText() {
|
||||||
return queryTextArea.getText();
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,30 +1,37 @@
|
|||||||
/*
|
/*
|
||||||
* To change this template, choose Tools | Templates
|
* Autopsy Forensic Browser
|
||||||
* and open the template in the editor.
|
*
|
||||||
|
* 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;
|
package org.sleuthkit.autopsy.keywordsearch;
|
||||||
|
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import org.apache.solr.client.solrj.SolrQuery;
|
|
||||||
import org.apache.solr.client.solrj.SolrServer;
|
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.impl.CommonsHttpSolrServer;
|
||||||
import org.apache.solr.client.solrj.response.QueryResponse;
|
|
||||||
|
|
||||||
/**
|
class Server {
|
||||||
*
|
|
||||||
* @author pmartel
|
|
||||||
*/
|
|
||||||
public class Server {
|
|
||||||
private static final String url = "http://localhost:8983/solr";
|
private static final String url = "http://localhost:8983/solr";
|
||||||
private static final Server S = new Server(url);
|
private static final Server S = new Server(url);
|
||||||
|
|
||||||
static Server getServer() {
|
static Server getServer() {
|
||||||
return S;
|
return S;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SolrServer solr;
|
private SolrServer solr;
|
||||||
|
|
||||||
Server(String url) {
|
Server(String url) {
|
||||||
try {
|
try {
|
||||||
this.solr = new CommonsHttpSolrServer(url);
|
this.solr = new CommonsHttpSolrServer(url);
|
||||||
@ -32,13 +39,12 @@ public class Server {
|
|||||||
throw new RuntimeException(ex);
|
throw new RuntimeException(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ingester getIngester() {
|
Ingester getIngester() {
|
||||||
return new Ingester(this.solr);
|
return new Ingester(this.solr);
|
||||||
}
|
}
|
||||||
|
|
||||||
SolrServer getSolr() {
|
SolrServer getSolr() {
|
||||||
return this.solr;
|
return this.solr;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user