mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-17 18:17:43 +00:00
Preliminary version of large files string extraction support - no full hit navitation yet.
Also, some refactoring and cleanup of old keyword search code.
This commit is contained in:
parent
463acb26ea
commit
c3a2f3a13c
@ -43,8 +43,13 @@ public class FsContentStringStream extends InputStream {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//args
|
||||||
private FsContent content;
|
private FsContent content;
|
||||||
private String encoding;
|
private String encoding;
|
||||||
|
private boolean preserveOnBuffBoundary;
|
||||||
|
|
||||||
|
//internal data
|
||||||
private long contentOffset = 0; //offset in fscontent read into curReadBuf
|
private long contentOffset = 0; //offset in fscontent read into curReadBuf
|
||||||
private static final int READ_BUF_SIZE = 256;
|
private static final int READ_BUF_SIZE = 256;
|
||||||
private static final byte[] curReadBuf = new byte[READ_BUF_SIZE];
|
private static final byte[] curReadBuf = new byte[READ_BUF_SIZE];
|
||||||
@ -64,16 +69,29 @@ public class FsContentStringStream extends InputStream {
|
|||||||
private static final Logger logger = Logger.getLogger(FsContentStringStream.class.getName());
|
private static final Logger logger = Logger.getLogger(FsContentStringStream.class.getName());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Construct new string stream from FsContent
|
||||||
* @param content to extract strings from
|
* @param content to extract strings from
|
||||||
* @param encoding target encoding, current only ASCII supported
|
* @param encoding target encoding, currently UTF-8
|
||||||
|
* @param preserveOnBuffBoundary whether to preserve or split string on a buffer boundary. If false, will pack into read buffer up to max. possible, potentially splitting a string. If false, the string will be preserved for next read.
|
||||||
*/
|
*/
|
||||||
public FsContentStringStream(FsContent content, Encoding encoding) {
|
public FsContentStringStream(FsContent content, Encoding encoding, boolean preserveOnBuffBoundary) {
|
||||||
this.content = content;
|
this.content = content;
|
||||||
this.encoding = encoding.toString();
|
this.encoding = encoding.toString();
|
||||||
|
this.preserveOnBuffBoundary = preserveOnBuffBoundary;
|
||||||
//logger.log(Level.INFO, "FILE: " + content.getParentPath() + "/" + content.getName());
|
//logger.log(Level.INFO, "FILE: " + content.getParentPath() + "/" + content.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct new string stream from FsContent
|
||||||
|
* Do not attempt to fill entire read buffer if that would break a string
|
||||||
|
*
|
||||||
|
* @param content to extract strings from
|
||||||
|
* @param encoding target encoding, currently UTF-8
|
||||||
|
*/
|
||||||
|
public FsContentStringStream(FsContent content, Encoding encoding) {
|
||||||
|
this(content, encoding, false);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int read(byte[] b, int off, int len) throws IOException {
|
public int read(byte[] b, int off, int len) throws IOException {
|
||||||
if (b == null) {
|
if (b == null) {
|
||||||
@ -190,7 +208,7 @@ public class FsContentStringStream extends InputStream {
|
|||||||
//check if temp still has chars to qualify as a string
|
//check if temp still has chars to qualify as a string
|
||||||
//we might need to break up temp into 2 parts for next read() call
|
//we might need to break up temp into 2 parts for next read() call
|
||||||
//consume as many as possible to fill entire user buffer
|
//consume as many as possible to fill entire user buffer
|
||||||
if (tempStringLen >= MIN_PRINTABLE_CHARS) {
|
if (!this.preserveOnBuffBoundary && tempStringLen >= MIN_PRINTABLE_CHARS) {
|
||||||
if (newCurLen > len) {
|
if (newCurLen > len) {
|
||||||
int appendChars = len - curStringLen;
|
int appendChars = len - curStringLen;
|
||||||
//save part for next user read(), need to break up temp string
|
//save part for next user read(), need to break up temp string
|
||||||
|
@ -504,6 +504,9 @@
|
|||||||
<field name="atime" type="tdate" indexed="true" stored="true"/>
|
<field name="atime" type="tdate" indexed="true" stored="true"/>
|
||||||
<field name="mtime" type="tdate" indexed="true" stored="true"/>
|
<field name="mtime" type="tdate" indexed="true" stored="true"/>
|
||||||
<field name="crtime" type="tdate" indexed="true" stored="true"/>
|
<field name="crtime" type="tdate" indexed="true" stored="true"/>
|
||||||
|
<!-- file chunk-specific fields (optional for others) -->
|
||||||
|
<!-- for a parent file with no content, number of chunks are specified -->
|
||||||
|
<field name="num_chunks" type="int" indexed="true" stored="true" required="false" />
|
||||||
|
|
||||||
<!-- Common metadata fields, named specifically to match up with
|
<!-- Common metadata fields, named specifically to match up with
|
||||||
SolrCell metadata when parsing rich documents such as Word, PDF.
|
SolrCell metadata when parsing rich documents such as Word, PDF.
|
||||||
|
@ -39,3 +39,11 @@ KeywordSearchPanel.cutMenuItem.text=Cut
|
|||||||
KeywordSearchPanel.copyMenuItem.text=Copy
|
KeywordSearchPanel.copyMenuItem.text=Copy
|
||||||
KeywordSearchPanel.pasteMenuItem.text=Paste
|
KeywordSearchPanel.pasteMenuItem.text=Paste
|
||||||
KeywordSearchPanel.selectAllMenuItem.text=Select All
|
KeywordSearchPanel.selectAllMenuItem.text=Select All
|
||||||
|
ExtractedContentPanel.pageButtonsLabel.text=Page
|
||||||
|
ExtractedContentPanel.pageNextButton.text=
|
||||||
|
ExtractedContentPanel.pagePreviousButton.actionCommand=pagePreviousButton
|
||||||
|
ExtractedContentPanel.pagePreviousButton.text=
|
||||||
|
ExtractedContentPanel.pagesLabel.text=Page:
|
||||||
|
ExtractedContentPanel.pageOfLabel.text=of
|
||||||
|
ExtractedContentPanel.pageCurLabel.text=-
|
||||||
|
ExtractedContentPanel.pageTotalLabel.text=-
|
||||||
|
@ -0,0 +1,102 @@
|
|||||||
|
/*
|
||||||
|
* 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.InputStreamReader;
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
import org.apache.solr.common.util.ContentStream;
|
||||||
|
import org.sleuthkit.autopsy.datamodel.FsContentStringStream.Encoding;
|
||||||
|
import org.sleuthkit.datamodel.FsContent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stream of bytes representing string with specified encoding
|
||||||
|
* to feed into Solr as ContentStream
|
||||||
|
*/
|
||||||
|
public class ByteContentStream implements ContentStream {
|
||||||
|
//input
|
||||||
|
private byte[] content; //extracted subcontent
|
||||||
|
private long contentSize;
|
||||||
|
private FsContent fsContent; //origin
|
||||||
|
private Encoding encoding;
|
||||||
|
|
||||||
|
private InputStream stream;
|
||||||
|
|
||||||
|
private static Logger logger = Logger.getLogger(FsContentStringContentStream.class.getName());
|
||||||
|
|
||||||
|
public ByteContentStream(byte [] content, long contentSize, FsContent fsContent, Encoding encoding) {
|
||||||
|
this.content = content;
|
||||||
|
this.fsContent = fsContent;
|
||||||
|
this.encoding = encoding;
|
||||||
|
stream = new ByteArrayInputStream(content, 0, (int)contentSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getByteContent() {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FsContent getFsContent() {
|
||||||
|
return fsContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getContentType() {
|
||||||
|
return "text/plain;charset=" + encoding.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return fsContent.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Reader getReader() throws IOException {
|
||||||
|
return new InputStreamReader(stream);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getSize() {
|
||||||
|
return contentSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSourceInfo() {
|
||||||
|
return "File:" + fsContent.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream getStream() throws IOException {
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void finalize() throws Throwable {
|
||||||
|
super.finalize();
|
||||||
|
|
||||||
|
stream.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,113 @@
|
|||||||
|
/*
|
||||||
|
* 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.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.sleuthkit.datamodel.FsContent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents result of keyword search query containing the Content it hit
|
||||||
|
* and chunk information, if the result hit is a content chunk
|
||||||
|
*/
|
||||||
|
public class ContentHit {
|
||||||
|
|
||||||
|
private FsContent content;
|
||||||
|
private int chunkID = 0;
|
||||||
|
|
||||||
|
ContentHit(FsContent content) {
|
||||||
|
this.content = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
ContentHit(FsContent content, int chunkID) {
|
||||||
|
this.content = content;
|
||||||
|
this.chunkID = chunkID;
|
||||||
|
}
|
||||||
|
|
||||||
|
FsContent getContent() {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
long getId() {
|
||||||
|
return content.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
int getChunkId() {
|
||||||
|
return chunkID;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isChunk() {
|
||||||
|
return chunkID != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final ContentHit other = (ContentHit) obj;
|
||||||
|
if (this.content != other.content && (this.content == null || !this.content.equals(other.content))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.chunkID != other.chunkID) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int hash = 3;
|
||||||
|
hash = 41 * hash + (this.content != null ? this.content.hashCode() : 0);
|
||||||
|
hash = 41 * hash + this.chunkID;
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Map<FsContent, Integer> flattenResults(List<ContentHit> hits) {
|
||||||
|
Map<FsContent, Integer> ret = new LinkedHashMap<FsContent, Integer>();
|
||||||
|
for (ContentHit h : hits) {
|
||||||
|
FsContent f = h.getContent();
|
||||||
|
if (!ret.containsKey(f)) {
|
||||||
|
ret.put(f, h.getChunkId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
//flatten results to get unique fscontent per hit, with first chunk id encountered
|
||||||
|
static LinkedHashMap<FsContent, Integer> flattenResults(Map<String, List<ContentHit>> results) {
|
||||||
|
LinkedHashMap<FsContent, Integer> flattened = new LinkedHashMap<FsContent, Integer>();
|
||||||
|
|
||||||
|
for (String key : results.keySet()) {
|
||||||
|
for (ContentHit hit : results.get(key)) {
|
||||||
|
FsContent fsContent = hit.getContent();
|
||||||
|
//flatten, record first chunk encountered
|
||||||
|
if (!flattened.containsKey(fsContent)) {
|
||||||
|
flattened.put(fsContent, hit.getChunkId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return flattened;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,147 @@
|
|||||||
|
/*
|
||||||
|
* 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.HashMap;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Paging tracker / find functionality for a given content
|
||||||
|
* Supports keeping track of paging for multiple contents.
|
||||||
|
*/
|
||||||
|
public class ExtractedContentPaging {
|
||||||
|
|
||||||
|
static class PageInfo {
|
||||||
|
|
||||||
|
PageInfo(int total) {
|
||||||
|
this.total = total;
|
||||||
|
if (this.total == 0) {
|
||||||
|
//no chunks
|
||||||
|
this.current = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.current = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int current;
|
||||||
|
int total;
|
||||||
|
}
|
||||||
|
private static final Logger logger = Logger.getLogger(ExtractedContentPaging.class.getName());
|
||||||
|
|
||||||
|
public ExtractedContentPaging() {
|
||||||
|
sources = new HashMap<MarkupSource, PageInfo>();
|
||||||
|
}
|
||||||
|
//maps markup source to page info being tracked
|
||||||
|
private HashMap<MarkupSource, PageInfo> sources;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* add pages tracking for the content
|
||||||
|
* needs to be called first for each content
|
||||||
|
* @param source
|
||||||
|
* @param totalPages
|
||||||
|
*/
|
||||||
|
void add(MarkupSource source, int totalPages) {
|
||||||
|
sources.put(source, new PageInfo(totalPages));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* check if the source paging if currently being tracked
|
||||||
|
* @param contentID content to check for
|
||||||
|
* @return true if it is being tracked already
|
||||||
|
*/
|
||||||
|
boolean isTracked(MarkupSource source) {
|
||||||
|
return sources.containsKey(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get total number of pages in the source
|
||||||
|
* @param contentID content to check for
|
||||||
|
* @return number of matches in the source
|
||||||
|
*/
|
||||||
|
int getTotalPages(MarkupSource source) {
|
||||||
|
if (!isTracked(source)) {
|
||||||
|
throw new IllegalStateException("Source is not being tracked");
|
||||||
|
}
|
||||||
|
return sources.get(source).total;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get current page
|
||||||
|
* @param contentID content to check for
|
||||||
|
* @return current page
|
||||||
|
*/
|
||||||
|
int getCurrentPage(MarkupSource source) {
|
||||||
|
if (!isTracked(source)) {
|
||||||
|
throw new IllegalStateException("Source is not being tracked");
|
||||||
|
}
|
||||||
|
return sources.get(source).current;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if there is a next page
|
||||||
|
* @param contentID content to check for
|
||||||
|
* @return true if the source has next page
|
||||||
|
*/
|
||||||
|
boolean hasNext(MarkupSource source) {
|
||||||
|
if (!isTracked(source)) {
|
||||||
|
throw new IllegalStateException("Source is not being tracked");
|
||||||
|
}
|
||||||
|
PageInfo info = sources.get(source);
|
||||||
|
return info.current < info.total;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if there is a previous page
|
||||||
|
* @param contentID content to check for
|
||||||
|
* @return true if the source has previous page
|
||||||
|
*/
|
||||||
|
boolean hasPrevious(MarkupSource source) {
|
||||||
|
if (!isTracked(source)) {
|
||||||
|
throw new IllegalStateException("Source is not being tracked");
|
||||||
|
}
|
||||||
|
PageInfo info = sources.get(source);
|
||||||
|
return info.current > 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* make step toward next page
|
||||||
|
* requires call to hasNext() first
|
||||||
|
* @param contentID content to check for
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void next(MarkupSource source) {
|
||||||
|
if (!isTracked(source)) {
|
||||||
|
throw new IllegalStateException("Source is not being tracked");
|
||||||
|
}
|
||||||
|
sources.get(source).current++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* make step toward previous page
|
||||||
|
* requires call to hasPrevious() first
|
||||||
|
* @param source
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void previous(MarkupSource source) {
|
||||||
|
if (!isTracked(source)) {
|
||||||
|
throw new IllegalStateException("Source is not being tracked");
|
||||||
|
}
|
||||||
|
sources.get(source).current--;
|
||||||
|
}
|
||||||
|
}
|
@ -40,7 +40,7 @@
|
|||||||
<Layout>
|
<Layout>
|
||||||
<DimensionLayout dim="0">
|
<DimensionLayout dim="0">
|
||||||
<Group type="103" groupAlignment="0" attributes="0">
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
<Group type="102" alignment="1" attributes="0">
|
<Group type="102" attributes="0">
|
||||||
<EmptySpace max="-2" attributes="0"/>
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
<Component id="hitLabel" min="-2" max="-2" attributes="0"/>
|
<Component id="hitLabel" min="-2" max="-2" attributes="0"/>
|
||||||
<EmptySpace type="separate" max="-2" attributes="0"/>
|
<EmptySpace type="separate" max="-2" attributes="0"/>
|
||||||
@ -49,17 +49,31 @@
|
|||||||
<Component id="hitOfLabel" min="-2" max="-2" attributes="0"/>
|
<Component id="hitOfLabel" min="-2" max="-2" attributes="0"/>
|
||||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||||
<Component id="hitTotalLabel" min="-2" max="-2" attributes="0"/>
|
<Component id="hitTotalLabel" min="-2" max="-2" attributes="0"/>
|
||||||
<EmptySpace min="-2" pref="50" max="-2" attributes="0"/>
|
<EmptySpace min="-2" pref="26" max="-2" attributes="0"/>
|
||||||
<Component id="hitButtonsLabel" min="-2" max="-2" attributes="0"/>
|
<Component id="hitButtonsLabel" min="-2" max="-2" attributes="0"/>
|
||||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
<Component id="hitPreviousButton" min="-2" pref="23" max="-2" attributes="0"/>
|
<Component id="hitPreviousButton" min="-2" pref="23" max="-2" attributes="0"/>
|
||||||
<EmptySpace min="-2" pref="0" max="-2" attributes="0"/>
|
<EmptySpace min="-2" pref="0" max="-2" attributes="0"/>
|
||||||
<Component id="hitNextButton" min="-2" pref="23" max="-2" attributes="0"/>
|
<Component id="hitNextButton" min="-2" pref="23" max="-2" attributes="0"/>
|
||||||
<EmptySpace pref="78" max="32767" attributes="0"/>
|
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||||
|
<Component id="pagesLabel" min="-2" max="-2" attributes="0"/>
|
||||||
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
|
<Component id="pageCurLabel" min="-2" pref="12" max="-2" attributes="0"/>
|
||||||
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
|
<Component id="pageOfLabel" min="-2" max="-2" attributes="0"/>
|
||||||
|
<EmptySpace min="-2" pref="14" max="-2" attributes="0"/>
|
||||||
|
<Component id="pageTotalLabel" min="-2" pref="18" max="-2" attributes="0"/>
|
||||||
|
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||||
|
<Component id="pageButtonsLabel" min="-2" max="-2" attributes="0"/>
|
||||||
|
<EmptySpace type="unrelated" min="-2" max="-2" attributes="0"/>
|
||||||
|
<Component id="pagePreviousButton" min="-2" max="-2" attributes="0"/>
|
||||||
|
<EmptySpace min="-2" pref="0" max="-2" attributes="0"/>
|
||||||
|
<Component id="pageNextButton" min="-2" max="-2" attributes="0"/>
|
||||||
|
<EmptySpace pref="65" max="32767" attributes="0"/>
|
||||||
<Component id="sourceComboBox" min="-2" max="-2" attributes="0"/>
|
<Component id="sourceComboBox" min="-2" max="-2" attributes="0"/>
|
||||||
<EmptySpace max="-2" attributes="0"/>
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
</Group>
|
</Group>
|
||||||
<Component id="jScrollPane1" alignment="0" pref="400" max="32767" attributes="0"/>
|
<Component id="jScrollPane1" alignment="0" pref="559" max="32767" attributes="0"/>
|
||||||
</Group>
|
</Group>
|
||||||
</DimensionLayout>
|
</DimensionLayout>
|
||||||
<DimensionLayout dim="1">
|
<DimensionLayout dim="1">
|
||||||
@ -67,16 +81,26 @@
|
|||||||
<Group type="102" alignment="0" attributes="0">
|
<Group type="102" alignment="0" attributes="0">
|
||||||
<Group type="103" groupAlignment="0" attributes="0">
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
<Component id="sourceComboBox" alignment="0" min="-2" max="-2" attributes="0"/>
|
<Component id="sourceComboBox" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||||
<Component id="hitPreviousButton" min="-2" pref="23" max="-2" attributes="0"/>
|
|
||||||
<Component id="hitNextButton" min="-2" pref="23" max="-2" attributes="0"/>
|
|
||||||
<Group type="103" groupAlignment="3" attributes="0">
|
<Group type="103" groupAlignment="3" attributes="0">
|
||||||
<Component id="hitCountLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
<Component id="hitCountLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||||
<Component id="hitOfLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
<Component id="hitOfLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||||
<Component id="hitTotalLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
<Component id="hitTotalLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||||
<Component id="hitButtonsLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
|
||||||
<Component id="hitLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
<Component id="hitLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||||
|
<Component id="hitButtonsLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||||
</Group>
|
</Group>
|
||||||
|
<Component id="hitPreviousButton" min="-2" pref="23" max="-2" attributes="0"/>
|
||||||
|
<Component id="hitNextButton" min="-2" pref="23" max="-2" attributes="0"/>
|
||||||
|
<Group type="103" groupAlignment="3" attributes="0">
|
||||||
|
<Component id="pageButtonsLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||||
|
<Component id="pageTotalLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||||
|
<Component id="pagesLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||||
|
<Component id="pageCurLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||||
|
<Component id="pageOfLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||||
</Group>
|
</Group>
|
||||||
|
<Component id="pageNextButton" min="-2" max="-2" attributes="0"/>
|
||||||
|
<Component id="pagePreviousButton" min="-2" pref="23" max="-2" attributes="0"/>
|
||||||
|
</Group>
|
||||||
|
<EmptySpace min="-2" pref="0" max="-2" attributes="0"/>
|
||||||
<Component id="jScrollPane1" pref="293" max="32767" attributes="0"/>
|
<Component id="jScrollPane1" pref="293" max="32767" attributes="0"/>
|
||||||
</Group>
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
@ -173,6 +197,11 @@
|
|||||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
<ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="ExtractedContentPanel.hitPreviousButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
<ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="ExtractedContentPanel.hitPreviousButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
</Property>
|
</Property>
|
||||||
|
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
|
||||||
|
<Border info="org.netbeans.modules.form.compat2.border.EmptyBorderInfo">
|
||||||
|
<EmptyBorder/>
|
||||||
|
</Border>
|
||||||
|
</Property>
|
||||||
<Property name="borderPainted" type="boolean" value="false"/>
|
<Property name="borderPainted" type="boolean" value="false"/>
|
||||||
<Property name="contentAreaFilled" type="boolean" value="false"/>
|
<Property name="contentAreaFilled" type="boolean" value="false"/>
|
||||||
<Property name="disabledIcon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
<Property name="disabledIcon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||||
@ -197,6 +226,11 @@
|
|||||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
<ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="ExtractedContentPanel.hitNextButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
<ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="ExtractedContentPanel.hitNextButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
</Property>
|
</Property>
|
||||||
|
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
|
||||||
|
<Border info="org.netbeans.modules.form.compat2.border.EmptyBorderInfo">
|
||||||
|
<EmptyBorder/>
|
||||||
|
</Border>
|
||||||
|
</Property>
|
||||||
<Property name="borderPainted" type="boolean" value="false"/>
|
<Property name="borderPainted" type="boolean" value="false"/>
|
||||||
<Property name="contentAreaFilled" type="boolean" value="false"/>
|
<Property name="contentAreaFilled" type="boolean" value="false"/>
|
||||||
<Property name="disabledIcon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
<Property name="disabledIcon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||||
@ -213,5 +247,92 @@
|
|||||||
</Property>
|
</Property>
|
||||||
</Properties>
|
</Properties>
|
||||||
</Component>
|
</Component>
|
||||||
|
<Component class="javax.swing.JLabel" name="pageButtonsLabel">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="ExtractedContentPanel.pageButtonsLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
</Component>
|
||||||
|
<Component class="javax.swing.JButton" name="pagePreviousButton">
|
||||||
|
<Properties>
|
||||||
|
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||||
|
<Image iconType="3" name="/org/sleuthkit/autopsy/keywordsearch/btn_step_back.png"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="ExtractedContentPanel.pagePreviousButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="actionCommand" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="ExtractedContentPanel.pagePreviousButton.actionCommand" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
|
||||||
|
<Border info="org.netbeans.modules.form.compat2.border.EmptyBorderInfo">
|
||||||
|
<EmptyBorder/>
|
||||||
|
</Border>
|
||||||
|
</Property>
|
||||||
|
<Property name="borderPainted" type="boolean" value="false"/>
|
||||||
|
<Property name="contentAreaFilled" type="boolean" value="false"/>
|
||||||
|
<Property name="disabledIcon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||||
|
<Image iconType="3" name="/org/sleuthkit/autopsy/keywordsearch/btn_step_back_disabled.png"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
|
||||||
|
<Insets value="[2, 0, 2, 0]"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
</Component>
|
||||||
|
<Component class="javax.swing.JButton" name="pageNextButton">
|
||||||
|
<Properties>
|
||||||
|
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||||
|
<Image iconType="3" name="/org/sleuthkit/autopsy/keywordsearch/btn_step_forward.png"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="ExtractedContentPanel.pageNextButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
|
||||||
|
<Border info="org.netbeans.modules.form.compat2.border.EmptyBorderInfo">
|
||||||
|
<EmptyBorder/>
|
||||||
|
</Border>
|
||||||
|
</Property>
|
||||||
|
<Property name="borderPainted" type="boolean" value="false"/>
|
||||||
|
<Property name="contentAreaFilled" type="boolean" value="false"/>
|
||||||
|
<Property name="disabledIcon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||||
|
<Image iconType="3" name="/org/sleuthkit/autopsy/keywordsearch/btn_step_forward_disabled.png"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
|
||||||
|
<Insets value="[2, 0, 2, 0]"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[23, 23]"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
</Component>
|
||||||
|
<Component class="javax.swing.JLabel" name="pagesLabel">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="ExtractedContentPanel.pagesLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
</Component>
|
||||||
|
<Component class="javax.swing.JLabel" name="pageCurLabel">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="ExtractedContentPanel.pageCurLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
</Component>
|
||||||
|
<Component class="javax.swing.JLabel" name="pageOfLabel">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="ExtractedContentPanel.pageOfLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
</Component>
|
||||||
|
<Component class="javax.swing.JLabel" name="pageTotalLabel">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="ExtractedContentPanel.pageTotalLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
</Component>
|
||||||
</SubComponents>
|
</SubComponents>
|
||||||
</Form>
|
</Form>
|
||||||
|
@ -45,7 +45,11 @@ class ExtractedContentPanel extends javax.swing.JPanel {
|
|||||||
|
|
||||||
private static Logger logger = Logger.getLogger(ExtractedContentPanel.class.getName());
|
private static Logger logger = Logger.getLogger(ExtractedContentPanel.class.getName());
|
||||||
|
|
||||||
ExtractedContentPanel() {
|
private ExtractedContentViewer viewer;
|
||||||
|
|
||||||
|
ExtractedContentPanel(ExtractedContentViewer viewer) {
|
||||||
|
this.viewer = viewer;
|
||||||
|
|
||||||
initComponents();
|
initComponents();
|
||||||
|
|
||||||
initControls();
|
initControls();
|
||||||
@ -85,7 +89,8 @@ class ExtractedContentPanel extends javax.swing.JPanel {
|
|||||||
@Override
|
@Override
|
||||||
public void itemStateChanged(ItemEvent e) {
|
public void itemStateChanged(ItemEvent e) {
|
||||||
if (e.getStateChange() == ItemEvent.SELECTED) {
|
if (e.getStateChange() == ItemEvent.SELECTED) {
|
||||||
setPanelText(((MarkupSource) e.getItem()).getMarkup());
|
MarkupSource source = (MarkupSource) e.getItem();
|
||||||
|
setPanelText(viewer.getDisplayText(source));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -130,6 +135,13 @@ class ExtractedContentPanel extends javax.swing.JPanel {
|
|||||||
hitButtonsLabel = new javax.swing.JLabel();
|
hitButtonsLabel = new javax.swing.JLabel();
|
||||||
hitPreviousButton = new javax.swing.JButton();
|
hitPreviousButton = new javax.swing.JButton();
|
||||||
hitNextButton = new javax.swing.JButton();
|
hitNextButton = new javax.swing.JButton();
|
||||||
|
pageButtonsLabel = new javax.swing.JLabel();
|
||||||
|
pagePreviousButton = new javax.swing.JButton();
|
||||||
|
pageNextButton = new javax.swing.JButton();
|
||||||
|
pagesLabel = new javax.swing.JLabel();
|
||||||
|
pageCurLabel = new javax.swing.JLabel();
|
||||||
|
pageOfLabel = new javax.swing.JLabel();
|
||||||
|
pageTotalLabel = new javax.swing.JLabel();
|
||||||
|
|
||||||
copyMenuItem.setText(org.openide.util.NbBundle.getMessage(ExtractedContentPanel.class, "ExtractedContentPanel.copyMenuItem.text")); // NOI18N
|
copyMenuItem.setText(org.openide.util.NbBundle.getMessage(ExtractedContentPanel.class, "ExtractedContentPanel.copyMenuItem.text")); // NOI18N
|
||||||
rightClickMenu.add(copyMenuItem);
|
rightClickMenu.add(copyMenuItem);
|
||||||
@ -163,6 +175,7 @@ class ExtractedContentPanel extends javax.swing.JPanel {
|
|||||||
|
|
||||||
hitPreviousButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/btn_step_back.png"))); // NOI18N
|
hitPreviousButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/btn_step_back.png"))); // NOI18N
|
||||||
hitPreviousButton.setText(org.openide.util.NbBundle.getMessage(ExtractedContentPanel.class, "ExtractedContentPanel.hitPreviousButton.text")); // NOI18N
|
hitPreviousButton.setText(org.openide.util.NbBundle.getMessage(ExtractedContentPanel.class, "ExtractedContentPanel.hitPreviousButton.text")); // NOI18N
|
||||||
|
hitPreviousButton.setBorder(javax.swing.BorderFactory.createEmptyBorder(1, 1, 1, 1));
|
||||||
hitPreviousButton.setBorderPainted(false);
|
hitPreviousButton.setBorderPainted(false);
|
||||||
hitPreviousButton.setContentAreaFilled(false);
|
hitPreviousButton.setContentAreaFilled(false);
|
||||||
hitPreviousButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/btn_step_back_disabled.png"))); // NOI18N
|
hitPreviousButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/btn_step_back_disabled.png"))); // NOI18N
|
||||||
@ -172,6 +185,7 @@ class ExtractedContentPanel extends javax.swing.JPanel {
|
|||||||
|
|
||||||
hitNextButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/btn_step_forward.png"))); // NOI18N
|
hitNextButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/btn_step_forward.png"))); // NOI18N
|
||||||
hitNextButton.setText(org.openide.util.NbBundle.getMessage(ExtractedContentPanel.class, "ExtractedContentPanel.hitNextButton.text")); // NOI18N
|
hitNextButton.setText(org.openide.util.NbBundle.getMessage(ExtractedContentPanel.class, "ExtractedContentPanel.hitNextButton.text")); // NOI18N
|
||||||
|
hitNextButton.setBorder(javax.swing.BorderFactory.createEmptyBorder(1, 1, 1, 1));
|
||||||
hitNextButton.setBorderPainted(false);
|
hitNextButton.setBorderPainted(false);
|
||||||
hitNextButton.setContentAreaFilled(false);
|
hitNextButton.setContentAreaFilled(false);
|
||||||
hitNextButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/btn_step_forward_disabled.png"))); // NOI18N
|
hitNextButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/btn_step_forward_disabled.png"))); // NOI18N
|
||||||
@ -179,11 +193,39 @@ class ExtractedContentPanel extends javax.swing.JPanel {
|
|||||||
hitNextButton.setPreferredSize(new java.awt.Dimension(23, 23));
|
hitNextButton.setPreferredSize(new java.awt.Dimension(23, 23));
|
||||||
hitNextButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/btn_step_forward_hover.png"))); // NOI18N
|
hitNextButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/btn_step_forward_hover.png"))); // NOI18N
|
||||||
|
|
||||||
|
pageButtonsLabel.setText(org.openide.util.NbBundle.getMessage(ExtractedContentPanel.class, "ExtractedContentPanel.pageButtonsLabel.text")); // NOI18N
|
||||||
|
|
||||||
|
pagePreviousButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/btn_step_back.png"))); // NOI18N
|
||||||
|
pagePreviousButton.setText(org.openide.util.NbBundle.getMessage(ExtractedContentPanel.class, "ExtractedContentPanel.pagePreviousButton.text")); // NOI18N
|
||||||
|
pagePreviousButton.setActionCommand(org.openide.util.NbBundle.getMessage(ExtractedContentPanel.class, "ExtractedContentPanel.pagePreviousButton.actionCommand")); // NOI18N
|
||||||
|
pagePreviousButton.setBorder(javax.swing.BorderFactory.createEmptyBorder(1, 1, 1, 1));
|
||||||
|
pagePreviousButton.setBorderPainted(false);
|
||||||
|
pagePreviousButton.setContentAreaFilled(false);
|
||||||
|
pagePreviousButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/btn_step_back_disabled.png"))); // NOI18N
|
||||||
|
pagePreviousButton.setMargin(new java.awt.Insets(2, 0, 2, 0));
|
||||||
|
|
||||||
|
pageNextButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/btn_step_forward.png"))); // NOI18N
|
||||||
|
pageNextButton.setText(org.openide.util.NbBundle.getMessage(ExtractedContentPanel.class, "ExtractedContentPanel.pageNextButton.text")); // NOI18N
|
||||||
|
pageNextButton.setBorder(javax.swing.BorderFactory.createEmptyBorder(1, 1, 1, 1));
|
||||||
|
pageNextButton.setBorderPainted(false);
|
||||||
|
pageNextButton.setContentAreaFilled(false);
|
||||||
|
pageNextButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/btn_step_forward_disabled.png"))); // NOI18N
|
||||||
|
pageNextButton.setMargin(new java.awt.Insets(2, 0, 2, 0));
|
||||||
|
pageNextButton.setPreferredSize(new java.awt.Dimension(23, 23));
|
||||||
|
|
||||||
|
pagesLabel.setText(org.openide.util.NbBundle.getMessage(ExtractedContentPanel.class, "ExtractedContentPanel.pagesLabel.text")); // NOI18N
|
||||||
|
|
||||||
|
pageCurLabel.setText(org.openide.util.NbBundle.getMessage(ExtractedContentPanel.class, "ExtractedContentPanel.pageCurLabel.text")); // NOI18N
|
||||||
|
|
||||||
|
pageOfLabel.setText(org.openide.util.NbBundle.getMessage(ExtractedContentPanel.class, "ExtractedContentPanel.pageOfLabel.text")); // NOI18N
|
||||||
|
|
||||||
|
pageTotalLabel.setText(org.openide.util.NbBundle.getMessage(ExtractedContentPanel.class, "ExtractedContentPanel.pageTotalLabel.text")); // NOI18N
|
||||||
|
|
||||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||||
this.setLayout(layout);
|
this.setLayout(layout);
|
||||||
layout.setHorizontalGroup(
|
layout.setHorizontalGroup(
|
||||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
|
.addGroup(layout.createSequentialGroup()
|
||||||
.addContainerGap()
|
.addContainerGap()
|
||||||
.addComponent(hitLabel)
|
.addComponent(hitLabel)
|
||||||
.addGap(18, 18, 18)
|
.addGap(18, 18, 18)
|
||||||
@ -192,30 +234,53 @@ class ExtractedContentPanel extends javax.swing.JPanel {
|
|||||||
.addComponent(hitOfLabel)
|
.addComponent(hitOfLabel)
|
||||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||||
.addComponent(hitTotalLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
.addComponent(hitTotalLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
.addGap(50, 50, 50)
|
.addGap(26, 26, 26)
|
||||||
.addComponent(hitButtonsLabel)
|
.addComponent(hitButtonsLabel)
|
||||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
.addComponent(hitPreviousButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
|
.addComponent(hitPreviousButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
.addGap(0, 0, 0)
|
.addGap(0, 0, 0)
|
||||||
.addComponent(hitNextButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
|
.addComponent(hitNextButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 78, Short.MAX_VALUE)
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||||
|
.addComponent(pagesLabel)
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
|
.addComponent(pageCurLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 12, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
|
.addComponent(pageOfLabel)
|
||||||
|
.addGap(14, 14, 14)
|
||||||
|
.addComponent(pageTotalLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 18, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||||
|
.addComponent(pageButtonsLabel)
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||||
|
.addComponent(pagePreviousButton)
|
||||||
|
.addGap(0, 0, 0)
|
||||||
|
.addComponent(pageNextButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 65, Short.MAX_VALUE)
|
||||||
.addComponent(sourceComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
.addComponent(sourceComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
.addContainerGap())
|
.addContainerGap())
|
||||||
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE)
|
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 559, Short.MAX_VALUE)
|
||||||
);
|
);
|
||||||
layout.setVerticalGroup(
|
layout.setVerticalGroup(
|
||||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
.addGroup(layout.createSequentialGroup()
|
.addGroup(layout.createSequentialGroup()
|
||||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
.addComponent(sourceComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
.addComponent(sourceComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
.addComponent(hitPreviousButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
|
|
||||||
.addComponent(hitNextButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
|
|
||||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||||
.addComponent(hitCountLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
.addComponent(hitCountLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
.addComponent(hitOfLabel)
|
.addComponent(hitOfLabel)
|
||||||
.addComponent(hitTotalLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
.addComponent(hitTotalLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
.addComponent(hitButtonsLabel)
|
.addComponent(hitLabel)
|
||||||
.addComponent(hitLabel)))
|
.addComponent(hitButtonsLabel))
|
||||||
|
.addComponent(hitPreviousButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
|
.addComponent(hitNextButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
|
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||||
|
.addComponent(pageButtonsLabel)
|
||||||
|
.addComponent(pageTotalLabel)
|
||||||
|
.addComponent(pagesLabel)
|
||||||
|
.addComponent(pageCurLabel)
|
||||||
|
.addComponent(pageOfLabel))
|
||||||
|
.addComponent(pageNextButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
|
.addComponent(pagePreviousButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||||
|
.addGap(0, 0, 0)
|
||||||
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 293, Short.MAX_VALUE))
|
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 293, Short.MAX_VALUE))
|
||||||
);
|
);
|
||||||
}// </editor-fold>//GEN-END:initComponents
|
}// </editor-fold>//GEN-END:initComponents
|
||||||
@ -231,11 +296,24 @@ class ExtractedContentPanel extends javax.swing.JPanel {
|
|||||||
private javax.swing.JButton hitPreviousButton;
|
private javax.swing.JButton hitPreviousButton;
|
||||||
private javax.swing.JLabel hitTotalLabel;
|
private javax.swing.JLabel hitTotalLabel;
|
||||||
private javax.swing.JScrollPane jScrollPane1;
|
private javax.swing.JScrollPane jScrollPane1;
|
||||||
|
private javax.swing.JLabel pageButtonsLabel;
|
||||||
|
private javax.swing.JLabel pageCurLabel;
|
||||||
|
private javax.swing.JButton pageNextButton;
|
||||||
|
private javax.swing.JLabel pageOfLabel;
|
||||||
|
private javax.swing.JButton pagePreviousButton;
|
||||||
|
private javax.swing.JLabel pageTotalLabel;
|
||||||
|
private javax.swing.JLabel pagesLabel;
|
||||||
private javax.swing.JPopupMenu rightClickMenu;
|
private javax.swing.JPopupMenu rightClickMenu;
|
||||||
private javax.swing.JMenuItem selectAllMenuItem;
|
private javax.swing.JMenuItem selectAllMenuItem;
|
||||||
private javax.swing.JComboBox sourceComboBox;
|
private javax.swing.JComboBox sourceComboBox;
|
||||||
// End of variables declaration//GEN-END:variables
|
// End of variables declaration//GEN-END:variables
|
||||||
|
|
||||||
|
|
||||||
|
void refreshCurrentMarkup() {
|
||||||
|
MarkupSource ms = (MarkupSource)sourceComboBox.getSelectedItem();
|
||||||
|
setPanelText(viewer.getDisplayText(ms));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the available sources (selects the first source in the list by
|
* Set the available sources (selects the first source in the list by
|
||||||
* default)
|
* default)
|
||||||
@ -285,7 +363,7 @@ class ExtractedContentPanel extends javax.swing.JPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void scrollToAnchor(String anchor) {
|
void scrollToAnchor(String anchor) {
|
||||||
extractedTextPane.scrollToReference(anchor);
|
extractedTextPane.scrollToReference(anchor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -293,7 +371,7 @@ class ExtractedContentPanel extends javax.swing.JPanel {
|
|||||||
*
|
*
|
||||||
* @param current, current hit to update the display with
|
* @param current, current hit to update the display with
|
||||||
*/
|
*/
|
||||||
public void updateCurrentDisplay(int current) {
|
void updateCurrentMatchDisplay(int current) {
|
||||||
hitCountLabel.setText(Integer.toString(current));
|
hitCountLabel.setText(Integer.toString(current));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -301,23 +379,54 @@ class ExtractedContentPanel extends javax.swing.JPanel {
|
|||||||
*
|
*
|
||||||
* @param total total number of hits to update the display with
|
* @param total total number of hits to update the display with
|
||||||
*/
|
*/
|
||||||
public void updateTotalDisplay(int total) {
|
void updateTotaMatcheslDisplay(int total) {
|
||||||
hitTotalLabel.setText(Integer.toString(total));
|
hitTotalLabel.setText(Integer.toString(total));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* reset the current/total display
|
*
|
||||||
|
* @param current, current page to update the display with
|
||||||
*/
|
*/
|
||||||
public void resetHitDisplay() {
|
void updateCurrentPageDisplay(int current) {
|
||||||
|
pageCurLabel.setText(Integer.toString(current));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param total total number of pages to update the display with
|
||||||
|
*/
|
||||||
|
void updateTotalPageslDisplay(int total) {
|
||||||
|
pageTotalLabel.setText(Integer.toString(total));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void resetDisplay() {
|
||||||
|
resetHitDisplay();
|
||||||
|
resetPagesDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* reset the current/total hits display
|
||||||
|
*/
|
||||||
|
void resetHitDisplay() {
|
||||||
hitTotalLabel.setText("-");
|
hitTotalLabel.setText("-");
|
||||||
hitCountLabel.setText("-");
|
hitCountLabel.setText("-");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* reset the current/total pages display
|
||||||
|
*/
|
||||||
|
void resetPagesDisplay() {
|
||||||
|
pageCurLabel.setText("-");
|
||||||
|
pageTotalLabel.setText("-");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* enable previous match control
|
* enable previous match control
|
||||||
* @param enable whether to enable or disable
|
* @param enable whether to enable or disable
|
||||||
*/
|
*/
|
||||||
public void enablePrevControl(boolean enable) {
|
void enablePrevMatchControl(boolean enable) {
|
||||||
hitPreviousButton.setEnabled(enable);
|
hitPreviousButton.setEnabled(enable);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -325,19 +434,44 @@ class ExtractedContentPanel extends javax.swing.JPanel {
|
|||||||
* enable next match control
|
* enable next match control
|
||||||
* @param enable whether to enable or disable
|
* @param enable whether to enable or disable
|
||||||
*/
|
*/
|
||||||
public void enableNextControl(boolean enable) {
|
void enableNextMatchControl(boolean enable) {
|
||||||
hitNextButton.setEnabled(enable);
|
hitNextButton.setEnabled(enable);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addPrevControlListener(ActionListener l) {
|
void addPrevMatchControlListener(ActionListener l) {
|
||||||
hitPreviousButton.addActionListener(l);
|
hitPreviousButton.addActionListener(l);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addNextControlListener(ActionListener l) {
|
void addNextMatchControlListener(ActionListener l) {
|
||||||
hitNextButton.addActionListener(l);
|
hitNextButton.addActionListener(l);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addSourceComboControlListener(ActionListener l) {
|
|
||||||
|
/**
|
||||||
|
* enable previous oage control
|
||||||
|
* @param enable whether to enable or disable
|
||||||
|
*/
|
||||||
|
void enablePrevPageControl(boolean enable) {
|
||||||
|
pagePreviousButton.setEnabled(enable);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* enable next page control
|
||||||
|
* @param enable whether to enable or disable
|
||||||
|
*/
|
||||||
|
void enableNextPageControl(boolean enable) {
|
||||||
|
pageNextButton.setEnabled(enable);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addPrevPageControlListener(ActionListener l) {
|
||||||
|
pagePreviousButton.addActionListener(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addNextPageControlListener(ActionListener l) {
|
||||||
|
pageNextButton.addActionListener(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addSourceComboControlListener(ActionListener l) {
|
||||||
sourceComboBox.addActionListener(l);
|
sourceComboBox.addActionListener(l);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,16 +19,15 @@
|
|||||||
package org.sleuthkit.autopsy.keywordsearch;
|
package org.sleuthkit.autopsy.keywordsearch;
|
||||||
|
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
|
import java.awt.Cursor;
|
||||||
import java.awt.EventQueue;
|
import java.awt.EventQueue;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
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.SolrServerException;
|
||||||
import org.openide.nodes.Node;
|
import org.openide.nodes.Node;
|
||||||
import org.openide.util.lookup.ServiceProvider;
|
import org.openide.util.lookup.ServiceProvider;
|
||||||
@ -49,9 +48,13 @@ public class ExtractedContentViewer implements DataContentViewer {
|
|||||||
private static final Logger logger = Logger.getLogger(ExtractedContentViewer.class.getName());
|
private static final Logger logger = Logger.getLogger(ExtractedContentViewer.class.getName());
|
||||||
private ExtractedContentPanel panel;
|
private ExtractedContentPanel panel;
|
||||||
private ExtractedContentFind find;
|
private ExtractedContentFind find;
|
||||||
|
private ExtractedContentPaging paging;
|
||||||
|
private Node currentNode = null;
|
||||||
|
private MarkupSource currentSource = null;
|
||||||
|
|
||||||
public ExtractedContentViewer() {
|
public ExtractedContentViewer() {
|
||||||
find = new ExtractedContentFind();
|
find = new ExtractedContentFind();
|
||||||
|
paging = new ExtractedContentPaging();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -59,25 +62,35 @@ public class ExtractedContentViewer implements DataContentViewer {
|
|||||||
|
|
||||||
// to clear the viewer
|
// to clear the viewer
|
||||||
if (selectedNode == null) {
|
if (selectedNode == null) {
|
||||||
|
currentNode = null;
|
||||||
resetComponent();
|
resetComponent();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.currentNode = selectedNode;
|
||||||
|
|
||||||
// sources are custom markup from the node (if available) and default
|
// sources are custom markup from the node (if available) and default
|
||||||
// markup is fetched from solr
|
// markup is fetched from solr
|
||||||
List<MarkupSource> sources = new ArrayList<MarkupSource>();
|
List<MarkupSource> sources = new ArrayList<MarkupSource>();
|
||||||
|
|
||||||
|
//add additional registered sources for this node
|
||||||
sources.addAll(selectedNode.getLookup().lookupAll(MarkupSource.class));
|
sources.addAll(selectedNode.getLookup().lookupAll(MarkupSource.class));
|
||||||
|
|
||||||
|
|
||||||
if (solrHasContent(selectedNode)) {
|
if (solrHasContent(selectedNode)) {
|
||||||
|
Content content = selectedNode.getLookup().lookup(Content.class);
|
||||||
|
if (content == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
sources.add(new MarkupSource() {
|
//add to page tracking if not there yet
|
||||||
|
final long contentID = content.getId();
|
||||||
|
|
||||||
|
MarkupSource newSource = new MarkupSource() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getMarkup() {
|
public String getMarkup(int pageNum) {
|
||||||
try {
|
try {
|
||||||
String content = StringEscapeUtils.escapeHtml(getSolrContent(selectedNode));
|
String content = StringEscapeUtils.escapeHtml(getSolrContent(selectedNode, this));
|
||||||
return "<pre>" + content.trim() + "</pre>";
|
return "<pre>" + content.trim() + "</pre>";
|
||||||
} catch (SolrServerException ex) {
|
} catch (SolrServerException ex) {
|
||||||
logger.log(Level.WARNING, "Couldn't get extracted content.", ex);
|
logger.log(Level.WARNING, "Couldn't get extracted content.", ex);
|
||||||
@ -104,9 +117,41 @@ public class ExtractedContentViewer implements DataContentViewer {
|
|||||||
public int getNumberHits() {
|
public int getNumberHits() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getNumberPages() {
|
||||||
|
final Server solrServer = KeywordSearch.getServer();
|
||||||
|
int numChunks = 0;
|
||||||
|
try {
|
||||||
|
numChunks = solrServer.queryNumFileChunks(contentID);
|
||||||
|
|
||||||
|
} catch (SolrServerException ex) {
|
||||||
|
logger.log(Level.WARNING, "Could not get number of chunks: ", ex);
|
||||||
|
|
||||||
|
} catch (NoOpenCoreException ex) {
|
||||||
|
logger.log(Level.WARNING, "Could not get number of chunks: ", ex);
|
||||||
}
|
}
|
||||||
|
return numChunks;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
currentSource = newSource;
|
||||||
|
sources.add(newSource);
|
||||||
|
|
||||||
|
//init paging for all sources for this node, if not inited
|
||||||
|
for (MarkupSource source : sources) {
|
||||||
|
if (!paging.isTracked(source)) {
|
||||||
|
int numPages = source.getNumberPages();
|
||||||
|
paging.add(source, numPages);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final int totalPages = paging.getTotalPages(newSource);
|
||||||
|
final int currentPage = paging.getCurrentPage(newSource);
|
||||||
|
|
||||||
|
updatePageControls(currentPage, totalPages);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// first source will be the default displayed
|
// first source will be the default displayed
|
||||||
setPanel(sources);
|
setPanel(sources);
|
||||||
@ -142,9 +187,11 @@ public class ExtractedContentViewer implements DataContentViewer {
|
|||||||
@Override
|
@Override
|
||||||
public Component getComponent() {
|
public Component getComponent() {
|
||||||
if (panel == null) {
|
if (panel == null) {
|
||||||
panel = new ExtractedContentPanel();
|
panel = new ExtractedContentPanel(this);
|
||||||
panel.addPrevControlListener(new PrevFindActionListener());
|
panel.addPrevMatchControlListener(new PrevFindActionListener());
|
||||||
panel.addNextControlListener(new NextFindActionListener());
|
panel.addNextMatchControlListener(new NextFindActionListener());
|
||||||
|
panel.addPrevPageControlListener(new PrevPageActionListener());
|
||||||
|
panel.addNextPageControlListener(new NextPageActionListener());
|
||||||
panel.addSourceComboControlListener(new SourceChangeActionListener());
|
panel.addSourceComboControlListener(new SourceChangeActionListener());
|
||||||
}
|
}
|
||||||
return panel;
|
return panel;
|
||||||
@ -153,7 +200,7 @@ public class ExtractedContentViewer implements DataContentViewer {
|
|||||||
@Override
|
@Override
|
||||||
public void resetComponent() {
|
public void resetComponent() {
|
||||||
setPanel(new ArrayList<MarkupSource>());
|
setPanel(new ArrayList<MarkupSource>());
|
||||||
panel.resetHitDisplay();
|
panel.resetDisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -169,7 +216,8 @@ public class ExtractedContentViewer implements DataContentViewer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isPreferred(Node node, boolean isSupported) {
|
public boolean isPreferred(Node node,
|
||||||
|
boolean isSupported) {
|
||||||
BlackboardArtifact art = node.getLookup().lookup(BlackboardArtifact.class);
|
BlackboardArtifact art = node.getLookup().lookup(BlackboardArtifact.class);
|
||||||
return isSupported && (art == null || art.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID());
|
return isSupported && (art == null || art.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID());
|
||||||
}
|
}
|
||||||
@ -198,19 +246,14 @@ public class ExtractedContentViewer implements DataContentViewer {
|
|||||||
|
|
||||||
final Server solrServer = KeywordSearch.getServer();
|
final Server solrServer = KeywordSearch.getServer();
|
||||||
|
|
||||||
SolrQuery q = new SolrQuery();
|
final long contentID = content.getId();
|
||||||
q.setQuery("*:*");
|
|
||||||
q.addFilterQuery("id:" + content.getId());
|
|
||||||
q.setFields("id");
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return !solrServer.query(q).getResults().isEmpty();
|
return solrServer.queryIsIndexed(contentID);
|
||||||
}
|
} catch (NoOpenCoreException ex) {
|
||||||
catch (NoOpenCoreException ex) {
|
|
||||||
logger.log(Level.WARNING, "Couldn't determine whether content is supported.", ex);
|
logger.log(Level.WARNING, "Couldn't determine whether content is supported.", ex);
|
||||||
return false;
|
return false;
|
||||||
}
|
} catch (SolrServerException ex) {
|
||||||
catch (SolrServerException ex) {
|
|
||||||
logger.log(Level.WARNING, "Couldn't determine whether content is supported.", ex);
|
logger.log(Level.WARNING, "Couldn't determine whether content is supported.", ex);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -223,25 +266,38 @@ public class ExtractedContentViewer implements DataContentViewer {
|
|||||||
* @return the extracted content
|
* @return the extracted content
|
||||||
* @throws SolrServerException if something goes wrong
|
* @throws SolrServerException if something goes wrong
|
||||||
*/
|
*/
|
||||||
private String getSolrContent(Node node) throws SolrServerException {
|
private String getSolrContent(Node node, MarkupSource source) throws SolrServerException {
|
||||||
Server solrServer = KeywordSearch.getServer();
|
Content contentObj = node.getLookup().lookup(Content.class);
|
||||||
SolrQuery q = new SolrQuery();
|
|
||||||
q.setQuery("*:*");
|
|
||||||
q.addFilterQuery("id:" + node.getLookup().lookup(Content.class).getId());
|
|
||||||
q.setFields("content");
|
|
||||||
|
|
||||||
String content;
|
final Server solrServer = KeywordSearch.getServer();
|
||||||
|
|
||||||
|
//if no paging, curChunk is 0
|
||||||
|
int curPage = paging.getCurrentPage(source);
|
||||||
|
|
||||||
|
String content = null;
|
||||||
try {
|
try {
|
||||||
content = (String) solrServer.query(q).getResults().get(0).getFieldValue("content");
|
content = (String) solrServer.getSolrContent(contentObj, curPage);
|
||||||
}
|
} catch (NoOpenCoreException ex) {
|
||||||
catch (NoOpenCoreException ex) {
|
logger.log(Level.WARNING, "Couldn't get text content.", ex);
|
||||||
logger.log(Level.WARNING, "Couldn't get Solr content.", ex);
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
class NextFindActionListener implements ActionListener {
|
/**
|
||||||
|
* figure out text to show from the page number and source
|
||||||
|
*
|
||||||
|
* @param source the current source
|
||||||
|
* @return text for the source and the current page num
|
||||||
|
*/
|
||||||
|
|
||||||
|
String getDisplayText(MarkupSource source) {
|
||||||
|
currentSource = source;
|
||||||
|
int pageNum = paging.getCurrentPage(currentSource);
|
||||||
|
return source.getMarkup(pageNum);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class NextFindActionListener implements ActionListener {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
@ -253,21 +309,21 @@ public class ExtractedContentViewer implements DataContentViewer {
|
|||||||
panel.scrollToAnchor(source.getAnchorPrefix() + Long.toString(indexVal));
|
panel.scrollToAnchor(source.getAnchorPrefix() + Long.toString(indexVal));
|
||||||
|
|
||||||
//update display
|
//update display
|
||||||
panel.updateCurrentDisplay(find.getCurrentIndexI(source) + 1);
|
panel.updateCurrentMatchDisplay(find.getCurrentIndexI(source) + 1);
|
||||||
panel.updateTotalDisplay(find.getCurrentIndexTotal(source));
|
panel.updateTotaMatcheslDisplay(find.getCurrentIndexTotal(source));
|
||||||
|
|
||||||
//update controls if needed
|
//update controls if needed
|
||||||
if (!find.hasNext(source)) {
|
if (!find.hasNext(source)) {
|
||||||
panel.enableNextControl(false);
|
panel.enableNextMatchControl(false);
|
||||||
}
|
}
|
||||||
if (find.hasPrevious(source)) {
|
if (find.hasPrevious(source)) {
|
||||||
panel.enablePrevControl(true);
|
panel.enablePrevMatchControl(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class PrevFindActionListener implements ActionListener {
|
private class PrevFindActionListener implements ActionListener {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
@ -279,21 +335,21 @@ public class ExtractedContentViewer implements DataContentViewer {
|
|||||||
panel.scrollToAnchor(source.getAnchorPrefix() + Long.toString(indexVal));
|
panel.scrollToAnchor(source.getAnchorPrefix() + Long.toString(indexVal));
|
||||||
|
|
||||||
//update display
|
//update display
|
||||||
panel.updateCurrentDisplay(find.getCurrentIndexI(source) + 1);
|
panel.updateCurrentMatchDisplay(find.getCurrentIndexI(source) + 1);
|
||||||
panel.updateTotalDisplay(find.getCurrentIndexTotal(source));
|
panel.updateTotaMatcheslDisplay(find.getCurrentIndexTotal(source));
|
||||||
|
|
||||||
//update controls if needed
|
//update controls if needed
|
||||||
if (!find.hasPrevious(source)) {
|
if (!find.hasPrevious(source)) {
|
||||||
panel.enablePrevControl(false);
|
panel.enablePrevMatchControl(false);
|
||||||
}
|
}
|
||||||
if (find.hasNext(source)) {
|
if (find.hasNext(source)) {
|
||||||
panel.enableNextControl(true);
|
panel.enableNextMatchControl(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SourceChangeActionListener implements ActionListener {
|
private class SourceChangeActionListener implements ActionListener {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
@ -302,26 +358,109 @@ public class ExtractedContentViewer implements DataContentViewer {
|
|||||||
//setup find controls
|
//setup find controls
|
||||||
if (source != null && source.isSearchable()) {
|
if (source != null && source.isSearchable()) {
|
||||||
find.init(source);
|
find.init(source);
|
||||||
panel.updateCurrentDisplay(find.getCurrentIndexI(source) + 1);
|
panel.updateCurrentMatchDisplay(find.getCurrentIndexI(source) + 1);
|
||||||
panel.updateTotalDisplay(find.getCurrentIndexTotal(source));
|
panel.updateTotaMatcheslDisplay(find.getCurrentIndexTotal(source));
|
||||||
|
|
||||||
if (find.hasNext(source)) {
|
if (find.hasNext(source)) {
|
||||||
panel.enableNextControl(true);
|
panel.enableNextMatchControl(true);
|
||||||
} else {
|
} else {
|
||||||
panel.enableNextControl(false);
|
panel.enableNextMatchControl(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (find.hasPrevious(source)) {
|
if (find.hasPrevious(source)) {
|
||||||
panel.enablePrevControl(true);
|
panel.enablePrevMatchControl(true);
|
||||||
} else {
|
} else {
|
||||||
panel.enablePrevControl(false);
|
panel.enablePrevMatchControl(false);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
panel.enableNextControl(false);
|
panel.enableNextMatchControl(false);
|
||||||
panel.enablePrevControl(false);
|
panel.enablePrevMatchControl(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updatePageControls(int currentPage, int totalPages) {
|
||||||
|
|
||||||
|
if (totalPages == 0) {
|
||||||
|
//no chunks case
|
||||||
|
panel.updateTotalPageslDisplay(1);
|
||||||
|
panel.updateCurrentPageDisplay(1);
|
||||||
|
} else {
|
||||||
|
panel.updateTotalPageslDisplay(totalPages);
|
||||||
|
panel.updateCurrentPageDisplay(currentPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (totalPages < 2) {
|
||||||
|
panel.enableNextPageControl(false);
|
||||||
|
panel.enablePrevPageControl(false);
|
||||||
|
} else {
|
||||||
|
if (currentPage < totalPages) {
|
||||||
|
panel.enableNextPageControl(true);
|
||||||
|
} else {
|
||||||
|
panel.enableNextPageControl(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentPage > 1) {
|
||||||
|
panel.enablePrevPageControl(true);
|
||||||
|
} else {
|
||||||
|
panel.enablePrevPageControl(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class NextPageActionListener implements ActionListener {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
|
||||||
|
if (paging.hasNext(currentSource)) {
|
||||||
|
paging.next(currentSource);
|
||||||
|
|
||||||
|
//set new text
|
||||||
|
panel.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
||||||
|
panel.refreshCurrentMarkup();
|
||||||
|
panel.setCursor(null);
|
||||||
|
|
||||||
|
//update display
|
||||||
|
panel.updateCurrentPageDisplay(paging.getCurrentPage(currentSource));
|
||||||
|
|
||||||
|
//update controls if needed
|
||||||
|
if (!paging.hasNext(currentSource)) {
|
||||||
|
panel.enableNextPageControl(false);
|
||||||
|
}
|
||||||
|
if (paging.hasPrevious(currentSource)) {
|
||||||
|
panel.enablePrevPageControl(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class PrevPageActionListener implements ActionListener {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
if (paging.hasPrevious(currentSource)) {
|
||||||
|
paging.previous(currentSource);
|
||||||
|
|
||||||
|
//set new text
|
||||||
|
panel.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
||||||
|
panel.refreshCurrentMarkup();
|
||||||
|
panel.setCursor(null);
|
||||||
|
|
||||||
|
//update display
|
||||||
|
panel.updateCurrentPageDisplay(paging.getCurrentPage(currentSource));
|
||||||
|
|
||||||
|
//update controls if needed
|
||||||
|
if (!paging.hasPrevious(currentSource)) {
|
||||||
|
panel.enablePrevPageControl(false);
|
||||||
|
}
|
||||||
|
if (paging.hasNext(currentSource)) {
|
||||||
|
panel.enableNextPageControl(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,157 @@
|
|||||||
|
/*
|
||||||
|
* 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.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
import org.openide.util.Exceptions;
|
||||||
|
import org.sleuthkit.autopsy.datamodel.FsContentStringStream;
|
||||||
|
import org.sleuthkit.autopsy.keywordsearch.Ingester.IngesterException;
|
||||||
|
import org.sleuthkit.datamodel.File;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility to extract and index a file as file chunks
|
||||||
|
*/
|
||||||
|
public class FileExtract {
|
||||||
|
|
||||||
|
private int numChunks;
|
||||||
|
public static final long MAX_CHUNK_SIZE = 10 * 1024 * 1024L;
|
||||||
|
private static final Logger logger = Logger.getLogger(FileExtract.class.getName());
|
||||||
|
private static final long MAX_STRING_CHUNK_SIZE = 1 * 1024 * 1024L;
|
||||||
|
private File sourceFile;
|
||||||
|
|
||||||
|
//single static buffer for all extractions. Safe, indexing can only happen in one thread
|
||||||
|
private static final byte[] STRING_CHUNK_BUF = new byte[(int) MAX_STRING_CHUNK_SIZE];
|
||||||
|
|
||||||
|
public FileExtract(File sourceFile) {
|
||||||
|
this.sourceFile = sourceFile;
|
||||||
|
numChunks = 0; //unknown until indexing is done
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNumChunks() {
|
||||||
|
return this.numChunks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public File getSourceFile() {
|
||||||
|
return sourceFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean index(Ingester ingester) throws IngesterException {
|
||||||
|
boolean success = false;
|
||||||
|
|
||||||
|
FsContentStringStream stringStream = null;
|
||||||
|
try {
|
||||||
|
success = true;
|
||||||
|
//break string into chunks
|
||||||
|
//Note: could use DataConversion.toString() since we are operating on fixed chunks
|
||||||
|
//but FsContentStringStream handles string boundary case better
|
||||||
|
stringStream = new FsContentStringStream(sourceFile, FsContentStringStream.Encoding.UTF8, true);
|
||||||
|
long readSize = 0;
|
||||||
|
|
||||||
|
while ((readSize = stringStream.read(STRING_CHUNK_BUF, 0, (int) MAX_STRING_CHUNK_SIZE)) != -1) {
|
||||||
|
//FileOutputStream debug = new FileOutputStream("c:\\temp\\" + sourceFile.getName() + Integer.toString(this.numChunks+1));
|
||||||
|
//debug.write(STRING_CHUNK_BUF, 0, (int)readSize);
|
||||||
|
|
||||||
|
FileExtractedChild chunk = new FileExtractedChild(this, this.numChunks + 1);
|
||||||
|
|
||||||
|
try {
|
||||||
|
chunk.index(ingester, STRING_CHUNK_BUF, readSize);
|
||||||
|
++this.numChunks;
|
||||||
|
} catch (IngesterException ingEx) {
|
||||||
|
success = false;
|
||||||
|
logger.log(Level.WARNING, "Ingester had a problem with extracted strings from file '" + sourceFile.getName() + "' (id: " + sourceFile.getId() + ").", ingEx);
|
||||||
|
}
|
||||||
|
//debug.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//after all chunks, ingest the parent file without content itself, and store numChunks
|
||||||
|
ingester.ingest(this);
|
||||||
|
|
||||||
|
} catch (IOException ex) {
|
||||||
|
logger.log(Level.WARNING, "Unable to read string stream and send to Solr, file: " + sourceFile.getName(), ex);
|
||||||
|
success = false;
|
||||||
|
} finally {
|
||||||
|
if (stringStream != null) {
|
||||||
|
try {
|
||||||
|
stringStream.close();
|
||||||
|
} catch (IOException ex) {
|
||||||
|
Exceptions.printStackTrace(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Represents each string chunk, a child of FileExtracted file
|
||||||
|
*/
|
||||||
|
class FileExtractedChild {
|
||||||
|
|
||||||
|
private int chunkID;
|
||||||
|
private FileExtract parent;
|
||||||
|
|
||||||
|
FileExtractedChild(FileExtract parent, int chunkID) {
|
||||||
|
this.parent = parent;
|
||||||
|
this.chunkID = chunkID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileExtract getParentFile() {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getChunkId() {
|
||||||
|
return chunkID;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* return String representation of the absolute id (parent and child)
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String getIdString() {
|
||||||
|
return getFileExtractChildId(this.parent.getSourceFile().getId(), this.chunkID);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean index(Ingester ingester, byte[] content, long contentSize) throws IngesterException {
|
||||||
|
boolean success = true;
|
||||||
|
ByteContentStream bcs = new ByteContentStream(content, contentSize, parent.getSourceFile(), FsContentStringStream.Encoding.UTF8);
|
||||||
|
try {
|
||||||
|
ingester.ingest(this, bcs);
|
||||||
|
//logger.log(Level.INFO, "Ingesting string chunk: " + this.getName() + ": " + chunkID);
|
||||||
|
|
||||||
|
} catch (Exception ingEx) {
|
||||||
|
success = false;
|
||||||
|
throw new IngesterException("Problem ingesting file string chunk: " + parent.getSourceFile().getId() + ", chunk: " + chunkID, ingEx);
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getFileExtractChildId(long parentID, int childID) {
|
||||||
|
return Long.toString(parentID) + "_" + Integer.toString(childID);
|
||||||
|
}
|
||||||
|
}
|
@ -35,12 +35,11 @@ import org.sleuthkit.datamodel.FsContent;
|
|||||||
*/
|
*/
|
||||||
public class FsContentStringContentStream implements ContentStream {
|
public class FsContentStringContentStream implements ContentStream {
|
||||||
//input
|
//input
|
||||||
|
|
||||||
private FsContent content;
|
private FsContent content;
|
||||||
private Encoding encoding;
|
private Encoding encoding;
|
||||||
|
|
||||||
//converted
|
//converted
|
||||||
private FsContentStringStream stream;
|
private FsContentStringStream stream;
|
||||||
|
|
||||||
private static Logger logger = Logger.getLogger(FsContentStringContentStream.class.getName());
|
private static Logger logger = Logger.getLogger(FsContentStringContentStream.class.getName());
|
||||||
|
|
||||||
public FsContentStringContentStream(FsContent content, Encoding encoding) {
|
public FsContentStringContentStream(FsContent content, Encoding encoding) {
|
||||||
@ -53,7 +52,6 @@ public class FsContentStringContentStream implements ContentStream {
|
|||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getContentType() {
|
public String getContentType() {
|
||||||
return "text/plain;charset=" + encoding.toString();
|
return "text/plain;charset=" + encoding.toString();
|
||||||
@ -86,4 +84,10 @@ public class FsContentStringContentStream implements ContentStream {
|
|||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void finalize() throws Throwable {
|
||||||
|
super.finalize();
|
||||||
|
|
||||||
|
stream.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,8 +26,8 @@ import org.apache.solr.client.solrj.SolrQuery;
|
|||||||
import org.apache.solr.client.solrj.SolrRequest.METHOD;
|
import org.apache.solr.client.solrj.SolrRequest.METHOD;
|
||||||
import org.apache.solr.client.solrj.SolrServerException;
|
import org.apache.solr.client.solrj.SolrServerException;
|
||||||
import org.apache.solr.client.solrj.response.QueryResponse;
|
import org.apache.solr.client.solrj.response.QueryResponse;
|
||||||
|
import org.openide.util.Exceptions;
|
||||||
import org.sleuthkit.autopsy.datamodel.HighlightLookup;
|
import org.sleuthkit.autopsy.datamodel.HighlightLookup;
|
||||||
import org.sleuthkit.autopsy.keywordsearch.Server.Core;
|
|
||||||
import org.sleuthkit.datamodel.Content;
|
import org.sleuthkit.datamodel.Content;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -45,6 +45,7 @@ class HighlightedMatchesSource implements MarkupSource, HighlightLookup {
|
|||||||
private String solrQuery;
|
private String solrQuery;
|
||||||
private Server solrServer;
|
private Server solrServer;
|
||||||
private int numberHits;
|
private int numberHits;
|
||||||
|
private int numberPages;
|
||||||
private boolean isRegex = false;
|
private boolean isRegex = false;
|
||||||
private boolean group = true;
|
private boolean group = true;
|
||||||
|
|
||||||
@ -56,6 +57,15 @@ class HighlightedMatchesSource implements MarkupSource, HighlightLookup {
|
|||||||
|
|
||||||
this.solrServer = KeywordSearch.getServer();
|
this.solrServer = KeywordSearch.getServer();
|
||||||
|
|
||||||
|
this.numberPages = 0;
|
||||||
|
try {
|
||||||
|
this.numberPages = solrServer.queryNumFileChunks(content.getId());
|
||||||
|
} catch (SolrServerException ex) {
|
||||||
|
logger.log(Level.WARNING, "Could not get number pages for content: " + content.getId());
|
||||||
|
} catch (NoOpenCoreException ex) {
|
||||||
|
logger.log(Level.WARNING, "Could not get number pages for content: " + content.getId());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HighlightedMatchesSource(Content content, String solrQuery, boolean isRegex, boolean group) {
|
HighlightedMatchesSource(Content content, String solrQuery, boolean isRegex, boolean group) {
|
||||||
@ -68,7 +78,13 @@ class HighlightedMatchesSource implements MarkupSource, HighlightLookup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getMarkup() {
|
public int getNumberPages() {
|
||||||
|
return this.numberPages;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMarkup(int pageNum) {
|
||||||
String highLightField = null;
|
String highLightField = null;
|
||||||
|
|
||||||
String highlightQuery = solrQuery;
|
String highlightQuery = solrQuery;
|
||||||
@ -110,17 +126,25 @@ class HighlightedMatchesSource implements MarkupSource, HighlightLookup {
|
|||||||
// q.setQuery(highLightField + ":" + highlightQuery);
|
// q.setQuery(highLightField + ":" + highlightQuery);
|
||||||
//else q.setQuery(highlightQuery); //use default field, simplifies query
|
//else q.setQuery(highlightQuery); //use default field, simplifies query
|
||||||
|
|
||||||
q.addFilterQuery("id:" + content.getId());
|
final long contentId = content.getId();
|
||||||
|
|
||||||
|
String contentIdStr = Long.toString(contentId);
|
||||||
|
if (pageNum > 0)
|
||||||
|
contentIdStr += "_" + Integer.toString(pageNum);
|
||||||
|
|
||||||
|
final String filterQuery = Server.Schema.ID.toString() + ":" + contentIdStr;
|
||||||
|
q.addFilterQuery(filterQuery);
|
||||||
q.addHighlightField(highLightField); //for exact highlighting, try content_ws field (with stored="true" in Solr schema)
|
q.addHighlightField(highLightField); //for exact highlighting, try content_ws field (with stored="true" in Solr schema)
|
||||||
q.setHighlightSimplePre(HIGHLIGHT_PRE);
|
q.setHighlightSimplePre(HIGHLIGHT_PRE);
|
||||||
q.setHighlightSimplePost(HIGHLIGHT_POST);
|
q.setHighlightSimplePost(HIGHLIGHT_POST);
|
||||||
q.setHighlightFragsize(0); // don't fragment the highlight
|
q.setHighlightFragsize(0); // don't fragment the highlight
|
||||||
|
q.setParam("hl.maxAnalyzedChars", Server.HL_ANALYZE_CHARS_UNLIMITED); //analyze all content
|
||||||
|
|
||||||
try {
|
try {
|
||||||
QueryResponse response = solrServer.query(q, METHOD.POST);
|
QueryResponse response = solrServer.query(q, METHOD.POST);
|
||||||
Map<String, Map<String, List<String>>> responseHighlight = response.getHighlighting();
|
Map<String, Map<String, List<String>>> responseHighlight = response.getHighlighting();
|
||||||
long contentID = content.getId();
|
|
||||||
Map<String, List<String>> responseHighlightID = responseHighlight.get(Long.toString(contentID));
|
Map<String, List<String>> responseHighlightID = responseHighlight.get(contentIdStr);
|
||||||
if (responseHighlightID == null) {
|
if (responseHighlightID == null) {
|
||||||
return NO_MATCHES;
|
return NO_MATCHES;
|
||||||
}
|
}
|
||||||
@ -135,11 +159,11 @@ class HighlightedMatchesSource implements MarkupSource, HighlightLookup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (NoOpenCoreException ex) {
|
catch (NoOpenCoreException ex) {
|
||||||
logger.log(Level.WARNING, "Couldn't query markup.", ex);
|
logger.log(Level.WARNING, "Couldn't query markup for page: " + pageNum, ex);
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
catch (SolrServerException ex) {
|
catch (SolrServerException ex) {
|
||||||
logger.log(Level.INFO, "Could not query markup. ", ex);
|
logger.log(Level.WARNING, "Could not query markup for page: " + pageNum, ex);
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
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;
|
||||||
@ -44,7 +45,7 @@ import org.sleuthkit.datamodel.ReadContentInputStream;
|
|||||||
/**
|
/**
|
||||||
* Handles indexing files on a Solr core.
|
* Handles indexing files on a Solr core.
|
||||||
*/
|
*/
|
||||||
class Ingester {
|
public class Ingester {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(Ingester.class.getName());
|
private static final Logger logger = Logger.getLogger(Ingester.class.getName());
|
||||||
private boolean uncommitedIngests = false;
|
private boolean uncommitedIngests = false;
|
||||||
@ -57,6 +58,7 @@ class Ingester {
|
|||||||
"bmp", "gif", "png", "jpeg", "tiff", "mp3", "aiff", "au", "midi", "wav",
|
"bmp", "gif", "png", "jpeg", "tiff", "mp3", "aiff", "au", "midi", "wav",
|
||||||
"pst", "xml", "class", "dwg", "eml", "emlx", "mbox", "mht"};
|
"pst", "xml", "class", "dwg", "eml", "emlx", "mbox", "mht"};
|
||||||
|
|
||||||
|
|
||||||
Ingester() {
|
Ingester() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,9 +81,29 @@ class Ingester {
|
|||||||
* @throws IngesterException if there was an error processing a specific
|
* @throws IngesterException if there was an error processing a specific
|
||||||
* file, but the Solr server is probably fine.
|
* file, but the Solr server is probably fine.
|
||||||
*/
|
*/
|
||||||
public void ingest(FsContentStringContentStream fcs) throws IngesterException {
|
void ingest(FsContentStringContentStream fcs) throws IngesterException {
|
||||||
Map<String, String> params = getFsContentFields(fcs.getFsContent());
|
Map<String, String> params = getFsContentFields(fcs.getFsContent());
|
||||||
ingest(fcs, params, fcs.getFsContent());
|
ingest(fcs, params, fcs.getFsContent().getSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ingest(FileExtract fe) throws IngesterException {
|
||||||
|
Map<String, String> params = getFsContentFields(fe.getSourceFile());
|
||||||
|
|
||||||
|
params.put(Server.Schema.NUM_CHUNKS.toString(), Integer.toString(fe.getNumChunks()));
|
||||||
|
|
||||||
|
ingest(new NullContentStream(fe.getSourceFile()), params, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
//chunk stream
|
||||||
|
void ingest(FileExtractedChild fec, ByteContentStream bcs) throws IngesterException {
|
||||||
|
FsContent sourceFsContent = bcs.getFsContent();
|
||||||
|
Map<String, String> params = getFsContentFields(sourceFsContent);
|
||||||
|
|
||||||
|
//overwrite, TODO set separately
|
||||||
|
params.put(Server.Schema.ID.toString(),
|
||||||
|
FileExtractedChild.getFileExtractChildId(sourceFsContent.getId(), fec.getChunkId()));
|
||||||
|
|
||||||
|
ingest(bcs, params, FileExtract.MAX_CHUNK_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -92,8 +114,8 @@ class Ingester {
|
|||||||
* @throws IngesterException if there was an error processing a specific
|
* @throws IngesterException if there was an error processing a specific
|
||||||
* file, but the Solr server is probably fine.
|
* file, but the Solr server is probably fine.
|
||||||
*/
|
*/
|
||||||
public void ingest(FsContent f) throws IngesterException {
|
void ingest(FsContent f) throws IngesterException {
|
||||||
ingest(new FscContentStream(f), getFsContentFields(f), f);
|
ingest(new FscContentStream(f), getFsContentFields(f), f.getSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -103,12 +125,12 @@ class Ingester {
|
|||||||
*/
|
*/
|
||||||
private Map<String, String> getFsContentFields(FsContent fsc) {
|
private Map<String, String> getFsContentFields(FsContent fsc) {
|
||||||
Map<String, String> fields = new HashMap<String, String>();
|
Map<String, String> fields = new HashMap<String, String>();
|
||||||
fields.put("id", Long.toString(fsc.getId()));
|
fields.put(Server.Schema.ID.toString(), Long.toString(fsc.getId()));
|
||||||
fields.put("file_name", fsc.getName());
|
fields.put(Server.Schema.FILE_NAME.toString(), fsc.getName());
|
||||||
fields.put("ctime", fsc.getCtimeAsDate());
|
fields.put(Server.Schema.CTIME.toString(), fsc.getCtimeAsDate());
|
||||||
fields.put("atime", fsc.getAtimeAsDate());
|
fields.put(Server.Schema.ATIME.toString(), fsc.getAtimeAsDate());
|
||||||
fields.put("mtime", fsc.getMtimeAsDate());
|
fields.put(Server.Schema.MTIME.toString(), fsc.getMtimeAsDate());
|
||||||
fields.put("crtime", fsc.getMtimeAsDate());
|
fields.put(Server.Schema.CRTIME.toString(), fsc.getMtimeAsDate());
|
||||||
return fields;
|
return fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,11 +139,11 @@ class Ingester {
|
|||||||
*
|
*
|
||||||
* @param ContentStream to ingest
|
* @param ContentStream to ingest
|
||||||
* @param fields content specific fields
|
* @param fields content specific fields
|
||||||
* @param sourceContent fsContent from which the cs content stream originated from
|
* @param size size of the content
|
||||||
* @throws IngesterException if there was an error processing a specific
|
* @throws IngesterException if there was an error processing a specific
|
||||||
* content, but the Solr server is probably fine.
|
* content, but the Solr server is probably fine.
|
||||||
*/
|
*/
|
||||||
private void ingest(ContentStream cs, Map<String, String> fields, final FsContent sourceContent) throws IngesterException {
|
private void ingest(ContentStream cs, Map<String, String> fields, final long size) throws IngesterException {
|
||||||
final ContentStreamUpdateRequest up = new ContentStreamUpdateRequest("/update/extract");
|
final ContentStreamUpdateRequest up = new ContentStreamUpdateRequest("/update/extract");
|
||||||
up.addContentStream(cs);
|
up.addContentStream(cs);
|
||||||
setFields(up, fields);
|
setFields(up, fields);
|
||||||
@ -138,7 +160,7 @@ class Ingester {
|
|||||||
final Future<?> f = upRequestExecutor.submit(new UpRequestTask(up));
|
final Future<?> f = upRequestExecutor.submit(new UpRequestTask(up));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
f.get(getTimeout(sourceContent), TimeUnit.SECONDS);
|
f.get(getTimeout(size), TimeUnit.SECONDS);
|
||||||
} catch (TimeoutException te) {
|
} catch (TimeoutException te) {
|
||||||
logger.log(Level.WARNING, "Solr timeout encountered, trying to restart Solr");
|
logger.log(Level.WARNING, "Solr timeout encountered, trying to restart Solr");
|
||||||
//restart may be needed to recover from some error conditions
|
//restart may be needed to recover from some error conditions
|
||||||
@ -162,13 +184,11 @@ class Ingester {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* return timeout that should be use to index the content
|
* return timeout that should be used to index the content
|
||||||
* TODO adjust them more as needed, and handle file chunks
|
* @param size size of the content
|
||||||
* @param f the source FsContent
|
|
||||||
* @return time in seconds to use a timeout
|
* @return time in seconds to use a timeout
|
||||||
*/
|
*/
|
||||||
private static int getTimeout(FsContent f) {
|
private static int getTimeout(long size) {
|
||||||
final long size = f.getSize();
|
|
||||||
if (size < 1024 * 1024L) //1MB
|
if (size < 1024 * 1024L) //1MB
|
||||||
{
|
{
|
||||||
return 60;
|
return 60;
|
||||||
@ -291,6 +311,48 @@ class Ingester {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ContentStream associated with FsContent, but forced with no content
|
||||||
|
*/
|
||||||
|
private static class NullContentStream implements ContentStream {
|
||||||
|
|
||||||
|
FsContent f;
|
||||||
|
|
||||||
|
NullContentStream(FsContent f) {
|
||||||
|
this.f = f;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return f.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSourceInfo() {
|
||||||
|
return "File:" + f.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getContentType() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getSize() {
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream getStream() throws IOException {
|
||||||
|
return new ByteArrayInputStream(new byte[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Reader getReader() throws IOException {
|
||||||
|
throw new UnsupportedOperationException("Not supported yet.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates that there was an error with the specific ingest operation,
|
* Indicates that there was an error with the specific ingest operation,
|
||||||
* but it's still okay to continue ingesting files.
|
* but it's still okay to continue ingesting files.
|
||||||
|
@ -45,17 +45,19 @@ import org.sleuthkit.datamodel.File;
|
|||||||
class KeywordSearchFilterNode extends FilterNode {
|
class KeywordSearchFilterNode extends FilterNode {
|
||||||
|
|
||||||
String solrQuery;
|
String solrQuery;
|
||||||
|
int previewChunk;
|
||||||
|
|
||||||
KeywordSearchFilterNode(HighlightedMatchesSource highlights, Node original, String solrQuery) {
|
KeywordSearchFilterNode(HighlightedMatchesSource highlights, Node original, String solrQuery, int previewChunk) {
|
||||||
super(original, null, new ProxyLookup(Lookups.singleton(highlights), original.getLookup()));
|
super(original, null, new ProxyLookup(Lookups.singleton(highlights), original.getLookup()));
|
||||||
this.solrQuery = solrQuery;
|
this.solrQuery = solrQuery;
|
||||||
|
this.previewChunk = previewChunk;
|
||||||
}
|
}
|
||||||
|
|
||||||
String getSnippet() {
|
String getSnippet() {
|
||||||
final Content content = this.getOriginal().getLookup().lookup(Content.class);
|
final Content content = this.getOriginal().getLookup().lookup(Content.class);
|
||||||
String snippet;
|
String snippet;
|
||||||
try {
|
try {
|
||||||
snippet = LuceneQuery.querySnippet(solrQuery, content.getId(), false, true);
|
snippet = LuceneQuery.querySnippet(solrQuery, content.getId(), previewChunk, false, true);
|
||||||
} catch (NoOpenCoreException ex) {
|
} catch (NoOpenCoreException ex) {
|
||||||
//logger.log(Level.WARNING, "Could not perform the snippet query. ", ex);
|
//logger.log(Level.WARNING, "Could not perform the snippet query. ", ex);
|
||||||
return "";
|
return "";
|
||||||
|
@ -18,6 +18,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.keywordsearch;
|
package org.sleuthkit.autopsy.keywordsearch;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import org.openide.util.Exceptions;
|
||||||
import org.sleuthkit.autopsy.datamodel.FsContentStringStream;
|
import org.sleuthkit.autopsy.datamodel.FsContentStringStream;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
@ -28,7 +30,6 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import javax.swing.SwingUtilities;
|
|
||||||
import javax.swing.SwingWorker;
|
import javax.swing.SwingWorker;
|
||||||
import javax.swing.Timer;
|
import javax.swing.Timer;
|
||||||
import org.apache.commons.lang.StringEscapeUtils;
|
import org.apache.commons.lang.StringEscapeUtils;
|
||||||
@ -47,6 +48,7 @@ import org.sleuthkit.autopsy.keywordsearch.Ingester.IngesterException;
|
|||||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
|
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
|
||||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||||
|
import org.sleuthkit.datamodel.File;
|
||||||
import org.sleuthkit.datamodel.FsContent;
|
import org.sleuthkit.datamodel.FsContent;
|
||||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||||
import org.sleuthkit.datamodel.TskData;
|
import org.sleuthkit.datamodel.TskData;
|
||||||
@ -59,7 +61,7 @@ public final class KeywordSearchIngestService implements IngestServiceFsContent
|
|||||||
public static final String MODULE_DESCRIPTION = "Performs file indexing and periodic search using keywords and regular expressions in lists.";
|
public static final String MODULE_DESCRIPTION = "Performs file indexing and periodic search using keywords and regular expressions in lists.";
|
||||||
private static KeywordSearchIngestService instance = null;
|
private static KeywordSearchIngestService instance = null;
|
||||||
private IngestManagerProxy managerProxy;
|
private IngestManagerProxy managerProxy;
|
||||||
private static final long MAX_STRING_EXTRACT_SIZE = 1 * (1 << 10) * (1 << 10);
|
private static final long MAX_STRING_CHUNK_SIZE = 1 * (1 << 10) * (1 << 10);
|
||||||
private static final long MAX_INDEX_SIZE = 100 * (1 << 10) * (1 << 10);
|
private static final long MAX_INDEX_SIZE = 100 * (1 << 10) * (1 << 10);
|
||||||
private Ingester ingester = null;
|
private Ingester ingester = null;
|
||||||
private volatile boolean commitIndex = false; //whether to commit index next time
|
private volatile boolean commitIndex = false; //whether to commit index next time
|
||||||
@ -71,7 +73,7 @@ public final class KeywordSearchIngestService implements IngestServiceFsContent
|
|||||||
private Indexer indexer;
|
private Indexer indexer;
|
||||||
private Searcher searcher;
|
private Searcher searcher;
|
||||||
private volatile boolean searcherDone = true;
|
private volatile boolean searcherDone = true;
|
||||||
private Map<Keyword, List<FsContent>> currentResults;
|
private Map<Keyword, List<ContentHit>> currentResults;
|
||||||
private volatile int messageID = 0;
|
private volatile int messageID = 0;
|
||||||
private boolean processedFiles;
|
private boolean processedFiles;
|
||||||
private volatile boolean finalRun = false;
|
private volatile boolean finalRun = false;
|
||||||
@ -79,13 +81,12 @@ public final class KeywordSearchIngestService implements IngestServiceFsContent
|
|||||||
private final String hashDBServiceName = "Hash Lookup";
|
private final String hashDBServiceName = "Hash Lookup";
|
||||||
private SleuthkitCase caseHandle = null;
|
private SleuthkitCase caseHandle = null;
|
||||||
boolean initialized = false;
|
boolean initialized = false;
|
||||||
|
private final byte[] STRING_CHUNK_BUF = new byte[(int) MAX_STRING_CHUNK_SIZE];
|
||||||
|
|
||||||
public enum IngestStatus {
|
public enum IngestStatus {
|
||||||
|
|
||||||
INGESTED, EXTRACTED_INGESTED, SKIPPED,};
|
INGESTED, EXTRACTED_INGESTED, SKIPPED,};
|
||||||
private Map<Long, IngestStatus> ingestStatus;
|
private Map<Long, IngestStatus> ingestStatus;
|
||||||
private Map<String, List<FsContent>> reportedHits; //already reported hits
|
|
||||||
|
|
||||||
public static synchronized KeywordSearchIngestService getDefault() {
|
public static synchronized KeywordSearchIngestService getDefault() {
|
||||||
if (instance == null) {
|
if (instance == null) {
|
||||||
@ -220,8 +221,6 @@ public final class KeywordSearchIngestService implements IngestServiceFsContent
|
|||||||
|
|
||||||
ingestStatus = new HashMap<Long, IngestStatus>();
|
ingestStatus = new HashMap<Long, IngestStatus>();
|
||||||
|
|
||||||
reportedHits = new HashMap<String, List<FsContent>>();
|
|
||||||
|
|
||||||
keywords = new ArrayList<Keyword>();
|
keywords = new ArrayList<Keyword>();
|
||||||
keywordLists = new ArrayList<String>();
|
keywordLists = new ArrayList<String>();
|
||||||
keywordToList = new HashMap<String, String>();
|
keywordToList = new HashMap<String, String>();
|
||||||
@ -237,7 +236,7 @@ public final class KeywordSearchIngestService implements IngestServiceFsContent
|
|||||||
finalRunComplete = false;
|
finalRunComplete = false;
|
||||||
searcherDone = true; //make sure to start the initial searcher
|
searcherDone = true; //make sure to start the initial searcher
|
||||||
//keeps track of all results per run not to repeat reporting the same hits
|
//keeps track of all results per run not to repeat reporting the same hits
|
||||||
currentResults = new HashMap<Keyword, List<FsContent>>();
|
currentResults = new HashMap<Keyword, List<ContentHit>>();
|
||||||
|
|
||||||
indexer = new Indexer();
|
indexer = new Indexer();
|
||||||
|
|
||||||
@ -416,18 +415,18 @@ public final class KeywordSearchIngestService implements IngestServiceFsContent
|
|||||||
private final Logger logger = Logger.getLogger(Indexer.class.getName());
|
private final Logger logger = Logger.getLogger(Indexer.class.getName());
|
||||||
private static final String DELETED_MSG = "The file is an unallocated or orphan file (deleted) and entire content is no longer recoverable. ";
|
private static final String DELETED_MSG = "The file is an unallocated or orphan file (deleted) and entire content is no longer recoverable. ";
|
||||||
|
|
||||||
private boolean extractAndIngest(FsContent f) {
|
private boolean extractAndIngest(File file) {
|
||||||
boolean success = false;
|
boolean indexed = false;
|
||||||
FsContentStringContentStream fscs = new FsContentStringContentStream(f, FsContentStringStream.Encoding.UTF8);
|
FileExtract fe = new FileExtract(file);
|
||||||
try {
|
try {
|
||||||
ingester.ingest(fscs);
|
indexed = fe.index(ingester);
|
||||||
success = true;
|
} catch (IngesterException ex) {
|
||||||
} catch (IngesterException ingEx) {
|
logger.log(Level.WARNING, "Error extracting strings and indexing file: " + file.getName(), ex);
|
||||||
logger.log(Level.WARNING, "Ingester had a problem with extracted strings from file '" + f.getName() + "' (id: " + f.getId() + ").", ingEx);
|
indexed = false;
|
||||||
} catch (Exception ingEx) {
|
|
||||||
logger.log(Level.WARNING, "Ingester had a problem with extracted strings from file '" + f.getName() + "' (id: " + f.getId() + ").", ingEx);
|
|
||||||
}
|
}
|
||||||
return success;
|
|
||||||
|
return indexed;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void indexFile(FsContent fsContent) {
|
private void indexFile(FsContent fsContent) {
|
||||||
@ -436,17 +435,21 @@ public final class KeywordSearchIngestService implements IngestServiceFsContent
|
|||||||
if (!fsContent.isFile()) {
|
if (!fsContent.isFile()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
File file = (File) fsContent;
|
||||||
|
|
||||||
if (size == 0 || size > MAX_INDEX_SIZE) {
|
boolean ingestible = Ingester.isIngestible(file);
|
||||||
|
|
||||||
|
//limit size of entire file, do not limit strings
|
||||||
|
if (size == 0 || (ingestible && size > MAX_INDEX_SIZE)) {
|
||||||
ingestStatus.put(fsContent.getId(), IngestStatus.SKIPPED);
|
ingestStatus.put(fsContent.getId(), IngestStatus.SKIPPED);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean ingestible = Ingester.isIngestible(fsContent);
|
|
||||||
final String fileName = fsContent.getName();
|
final String fileName = file.getName();
|
||||||
|
|
||||||
String deletedMessage = "";
|
String deletedMessage = "";
|
||||||
if ((fsContent.getMeta_flags() & (TskData.TSK_FS_META_FLAG_ENUM.ORPHAN.getMetaFlag() | TskData.TSK_FS_META_FLAG_ENUM.UNALLOC.getMetaFlag())) != 0) {
|
if ((file.getMeta_flags() & (TskData.TSK_FS_META_FLAG_ENUM.ORPHAN.getMetaFlag() | TskData.TSK_FS_META_FLAG_ENUM.UNALLOC.getMetaFlag())) != 0) {
|
||||||
deletedMessage = DELETED_MSG;
|
deletedMessage = DELETED_MSG;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -454,38 +457,34 @@ public final class KeywordSearchIngestService implements IngestServiceFsContent
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
//logger.log(Level.INFO, "indexing: " + fsContent.getName());
|
//logger.log(Level.INFO, "indexing: " + fsContent.getName());
|
||||||
ingester.ingest(fsContent);
|
ingester.ingest(file);
|
||||||
ingestStatus.put(fsContent.getId(), IngestStatus.INGESTED);
|
ingestStatus.put(file.getId(), IngestStatus.INGESTED);
|
||||||
} catch (IngesterException e) {
|
} catch (IngesterException e) {
|
||||||
ingestStatus.put(fsContent.getId(), IngestStatus.SKIPPED);
|
ingestStatus.put(file.getId(), IngestStatus.SKIPPED);
|
||||||
//try to extract strings
|
//try to extract strings
|
||||||
boolean processed = processNonIngestible(fsContent);
|
boolean processed = processNonIngestible(file);
|
||||||
//postIngestibleErrorMessage(processed, fileName, deletedMessage);
|
//postIngestibleErrorMessage(processed, fileName, deletedMessage);
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
ingestStatus.put(fsContent.getId(), IngestStatus.SKIPPED);
|
ingestStatus.put(file.getId(), IngestStatus.SKIPPED);
|
||||||
//try to extract strings
|
//try to extract strings
|
||||||
boolean processed = processNonIngestible(fsContent);
|
boolean processed = processNonIngestible(file);
|
||||||
|
|
||||||
//postIngestibleErrorMessage(processed, fileName, deletedMessage);
|
//postIngestibleErrorMessage(processed, fileName, deletedMessage);
|
||||||
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
boolean processed = processNonIngestible(fsContent);
|
boolean processed = processNonIngestible(file);
|
||||||
//postNonIngestibleErrorMessage(processed, fsContent, deletedMessage);
|
//postNonIngestibleErrorMessage(processed, fsContent, deletedMessage);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void postNonIngestibleErrorMessage(boolean stringsExtracted, FsContent fsContent, String deletedMessage) {
|
private void postNonIngestibleErrorMessage(boolean stringsExtracted, File file, String deletedMessage) {
|
||||||
String fileName = fsContent.getName();
|
String fileName = file.getName();
|
||||||
if (!stringsExtracted) {
|
if (!stringsExtracted) {
|
||||||
if (fsContent.getSize() < MAX_STRING_EXTRACT_SIZE) {
|
|
||||||
managerProxy.postMessage(IngestMessage.createErrorMessage(++messageID, KeywordSearchIngestService.instance, "Error indexing strings: " + fileName, "Error encountered extracting string content from this file (of unsupported format). " + deletedMessage + "The file will not be included in the search results.<br />File: " + fileName));
|
|
||||||
} else {
|
|
||||||
managerProxy.postMessage(IngestMessage.createMessage(++messageID, IngestMessage.MessageType.INFO, KeywordSearchIngestService.instance, "Skipped indexing strings: " + fileName, "Skipped extracting string content from this file (of unsupported format) due to the file size. The file will not be included in the search results.<br />File: " + fileName));
|
managerProxy.postMessage(IngestMessage.createMessage(++messageID, IngestMessage.MessageType.INFO, KeywordSearchIngestService.instance, "Skipped indexing strings: " + fileName, "Skipped extracting string content from this file (of unsupported format) due to the file size. The file will not be included in the search results.<br />File: " + fileName));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -497,20 +496,16 @@ public final class KeywordSearchIngestService implements IngestServiceFsContent
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean processNonIngestible(FsContent fsContent) {
|
private boolean processNonIngestible(File file) {
|
||||||
if (fsContent.getSize() < MAX_STRING_EXTRACT_SIZE) {
|
if (!extractAndIngest(file)) {
|
||||||
if (!extractAndIngest(fsContent)) {
|
logger.log(Level.WARNING, "Failed to extract strings and ingest, file '" + file.getName() + "' (id: " + file.getId() + ").");
|
||||||
logger.log(Level.WARNING, "Failed to extract strings and ingest, file '" + fsContent.getName() + "' (id: " + fsContent.getId() + ").");
|
ingestStatus.put(file.getId(), IngestStatus.SKIPPED);
|
||||||
ingestStatus.put(fsContent.getId(), IngestStatus.SKIPPED);
|
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
ingestStatus.put(fsContent.getId(), IngestStatus.EXTRACTED_INGESTED);
|
ingestStatus.put(file.getId(), IngestStatus.EXTRACTED_INGESTED);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
ingestStatus.put(fsContent.getId(), IngestStatus.SKIPPED);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -565,7 +560,7 @@ public final class KeywordSearchIngestService implements IngestServiceFsContent
|
|||||||
del = new TermComponentQuery(keywordQuery);
|
del = new TermComponentQuery(keywordQuery);
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, List<FsContent>> queryResult = null;
|
Map<String, List<ContentHit>> queryResult = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
queryResult = del.performQuery();
|
queryResult = del.performQuery();
|
||||||
@ -581,23 +576,23 @@ public final class KeywordSearchIngestService implements IngestServiceFsContent
|
|||||||
}
|
}
|
||||||
|
|
||||||
//calculate new results but substracting results already obtained in this run
|
//calculate new results but substracting results already obtained in this run
|
||||||
Map<Keyword, List<FsContent>> newResults = new HashMap<Keyword, List<FsContent>>();
|
Map<Keyword, List<ContentHit>> newResults = new HashMap<Keyword, List<ContentHit>>();
|
||||||
|
|
||||||
for (String termResult : queryResult.keySet()) {
|
for (String termResult : queryResult.keySet()) {
|
||||||
List<FsContent> queryTermResults = queryResult.get(termResult);
|
List<ContentHit> queryTermResults = queryResult.get(termResult);
|
||||||
Keyword termResultK = new Keyword(termResult, !isRegex);
|
Keyword termResultK = new Keyword(termResult, !isRegex);
|
||||||
List<FsContent> curTermResults = currentResults.get(termResultK);
|
List<ContentHit> curTermResults = currentResults.get(termResultK);
|
||||||
if (curTermResults == null) {
|
if (curTermResults == null) {
|
||||||
currentResults.put(termResultK, queryTermResults);
|
currentResults.put(termResultK, queryTermResults);
|
||||||
newResults.put(termResultK, queryTermResults);
|
newResults.put(termResultK, queryTermResults);
|
||||||
} else {
|
} else {
|
||||||
//some fscontent hits already exist for this keyword
|
//some fscontent hits already exist for this keyword
|
||||||
for (FsContent res : queryTermResults) {
|
for (ContentHit res : queryTermResults) {
|
||||||
if (!curTermResults.contains(res)) {
|
if (! previouslyHit(curTermResults, res)) {
|
||||||
//add to new results
|
//add to new results
|
||||||
List<FsContent> newResultsFs = newResults.get(termResultK);
|
List<ContentHit> newResultsFs = newResults.get(termResultK);
|
||||||
if (newResultsFs == null) {
|
if (newResultsFs == null) {
|
||||||
newResultsFs = new ArrayList<FsContent>();
|
newResultsFs = new ArrayList<ContentHit>();
|
||||||
newResults.put(termResultK, newResultsFs);
|
newResults.put(termResultK, newResultsFs);
|
||||||
}
|
}
|
||||||
newResultsFs.add(res);
|
newResultsFs.add(res);
|
||||||
@ -614,12 +609,28 @@ public final class KeywordSearchIngestService implements IngestServiceFsContent
|
|||||||
//write results to BB
|
//write results to BB
|
||||||
Collection<BlackboardArtifact> newArtifacts = new ArrayList<BlackboardArtifact>(); //new artifacts to report
|
Collection<BlackboardArtifact> newArtifacts = new ArrayList<BlackboardArtifact>(); //new artifacts to report
|
||||||
for (final Keyword hitTerm : newResults.keySet()) {
|
for (final Keyword hitTerm : newResults.keySet()) {
|
||||||
List<FsContent> fsContentHits = newResults.get(hitTerm);
|
List<ContentHit> contentHitsAll = newResults.get(hitTerm);
|
||||||
for (final FsContent hitFile : fsContentHits) {
|
Map<FsContent,Integer>contentHitsFlattened = ContentHit.flattenResults(contentHitsAll);
|
||||||
|
for (final FsContent hitFile : contentHitsFlattened.keySet()) {
|
||||||
if (this.isCancelled()) {
|
if (this.isCancelled()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
KeywordWriteResult written = del.writeToBlackBoard(hitTerm.getQuery(), hitFile, listName);
|
|
||||||
|
String snippet = null;
|
||||||
|
final String snippetQuery = KeywordSearchUtil.escapeLuceneQuery(hitTerm.getQuery(), true, false);
|
||||||
|
int chunkId = contentHitsFlattened.get(hitFile);
|
||||||
|
try {
|
||||||
|
snippet = LuceneQuery.querySnippet(snippetQuery, hitFile.getId(), chunkId, isRegex, true);
|
||||||
|
} catch (NoOpenCoreException e) {
|
||||||
|
logger.log(Level.WARNING, "Error querying snippet: " + snippetQuery, e);
|
||||||
|
//no reason to continie
|
||||||
|
return null;
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.log(Level.WARNING, "Error querying snippet: " + snippetQuery, e);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
KeywordWriteResult written = del.writeToBlackBoard(hitTerm.getQuery(), hitFile, snippet, listName);
|
||||||
if (written == null) {
|
if (written == null) {
|
||||||
//logger.log(Level.INFO, "BB artifact for keyword not written: " + hitTerm.toString());
|
//logger.log(Level.INFO, "BB artifact for keyword not written: " + hitTerm.toString());
|
||||||
continue;
|
continue;
|
||||||
@ -726,4 +737,17 @@ public final class KeywordSearchIngestService implements IngestServiceFsContent
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//check if fscontent already hit, ignore chunks
|
||||||
|
private static boolean previouslyHit(List<ContentHit> contents, ContentHit hit) {
|
||||||
|
boolean ret = false;
|
||||||
|
long hitId = hit.getId();
|
||||||
|
for (ContentHit c : contents) {
|
||||||
|
if (c.getId() == hitId) {
|
||||||
|
ret = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.keywordsearch;
|
package org.sleuthkit.autopsy.keywordsearch;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Map;
|
||||||
import org.openide.nodes.AbstractNode;
|
import org.openide.nodes.AbstractNode;
|
||||||
import org.openide.nodes.Node;
|
import org.openide.nodes.Node;
|
||||||
import org.sleuthkit.autopsy.datamodel.RootContentChildren;
|
import org.sleuthkit.autopsy.datamodel.RootContentChildren;
|
||||||
@ -30,8 +31,9 @@ import org.sleuthkit.datamodel.FsContent;
|
|||||||
*/
|
*/
|
||||||
class KeywordSearchNode extends AbstractNode {
|
class KeywordSearchNode extends AbstractNode {
|
||||||
|
|
||||||
KeywordSearchNode(List<FsContent> keys, final String solrQuery) {
|
KeywordSearchNode(final Map<FsContent,Integer> keys, final String solrQuery) {
|
||||||
super(new RootContentChildren(keys) {
|
|
||||||
|
super(new RootContentChildren(new ArrayList(keys.keySet())) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Node[] createNodes(Object key) {
|
protected Node[] createNodes(Object key) {
|
||||||
@ -43,7 +45,8 @@ class KeywordSearchNode extends AbstractNode {
|
|||||||
int i = 0;
|
int i = 0;
|
||||||
for (Node original : originalNodes) {
|
for (Node original : originalNodes) {
|
||||||
HighlightedMatchesSource markup = new HighlightedMatchesSource((Content)key, solrQuery, false);
|
HighlightedMatchesSource markup = new HighlightedMatchesSource((Content)key, solrQuery, false);
|
||||||
filterNodes[i++] = new KeywordSearchFilterNode(markup, original, solrQuery);
|
int previewChunk = keys.get((FsContent)key);
|
||||||
|
filterNodes[i++] = new KeywordSearchFilterNode(markup, original, solrQuery, previewChunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
return filterNodes;
|
return filterNodes;
|
||||||
|
@ -39,7 +39,7 @@ public interface KeywordSearchQuery {
|
|||||||
* @throws NoOpenCoreException if query failed due to server error, this could be a notification to stop processing
|
* @throws NoOpenCoreException if query failed due to server error, this could be a notification to stop processing
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public Map<String,List<FsContent>> performQuery() throws NoOpenCoreException;
|
public Map<String,List<ContentHit>> performQuery() throws NoOpenCoreException;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -59,6 +59,12 @@ public interface KeywordSearchQuery {
|
|||||||
*/
|
*/
|
||||||
public boolean isEscaped();
|
public boolean isEscaped();
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return true if query is a literal query (non regex)
|
||||||
|
*/
|
||||||
|
public boolean isLiteral();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* return original query string
|
* return original query string
|
||||||
* @return the query String supplied originally
|
* @return the query String supplied originally
|
||||||
@ -82,11 +88,12 @@ public interface KeywordSearchQuery {
|
|||||||
* this method is useful if something else should keep track of partial results to write
|
* this method is useful if something else should keep track of partial results to write
|
||||||
* @param termHit term for only which to write results
|
* @param termHit term for only which to write results
|
||||||
* @param newFsHit fscontent for which to write results for this hit
|
* @param newFsHit fscontent for which to write results for this hit
|
||||||
|
* @param snippet snippet preview with hit context, or null if there is no snippet
|
||||||
* @param listName listname
|
* @param listName listname
|
||||||
* @return collection of results (with cached bb artifacts/attributes) created and written
|
* @return collection of results (with cached bb artifacts/attributes) created and written
|
||||||
* @throws NoOpenCoreException if could not write to bb because required query failed due to server error, this could be a notification to stop processing
|
* @throws NoOpenCoreException if could not write to bb because required query failed due to server error, this could be a notification to stop processing
|
||||||
*/
|
*/
|
||||||
public KeywordWriteResult writeToBlackBoard(String termHit, FsContent newFsHit, String listName) throws NoOpenCoreException;
|
public KeywordWriteResult writeToBlackBoard(String termHit, FsContent newFsHit, String snippet, String listName);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,7 +143,7 @@ public class KeywordSearchQueryManager implements KeywordSearchQuery {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, List<FsContent>> performQuery() {
|
public Map<String, List<ContentHit>> performQuery() {
|
||||||
throw new UnsupportedOperationException("performQuery() unsupported");
|
throw new UnsupportedOperationException("performQuery() unsupported");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,9 +192,16 @@ public class KeywordSearchQueryManager implements KeywordSearchQuery {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLiteral() {
|
||||||
|
throw new UnsupportedOperationException("Not supported yet.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public KeywordWriteResult writeToBlackBoard(String termHit, FsContent newFsHit, String listName) {
|
public KeywordWriteResult writeToBlackBoard(String termHit, FsContent newFsHit, String snippet, String listName) {
|
||||||
throw new UnsupportedOperationException("writeToBlackBoard() unsupported by manager");
|
throw new UnsupportedOperationException("writeToBlackBoard() unsupported by manager");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,17 +225,14 @@ public class KeywordSearchResultFactory extends ChildFactory<KeyValueQuery> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//execute the query and get fscontents matching
|
//execute the query and get fscontents matching
|
||||||
Map<String, List<FsContent>> tcqRes;
|
Map<String, List<ContentHit>> tcqRes;
|
||||||
try {
|
try {
|
||||||
tcqRes = tcq.performQuery();
|
tcqRes = tcq.performQuery();
|
||||||
} catch (NoOpenCoreException ex) {
|
} catch (NoOpenCoreException ex) {
|
||||||
logger.log(Level.WARNING, "Could not perform the query. ", ex);
|
logger.log(Level.WARNING, "Could not perform the query. ", ex);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
final Set<FsContent> fsContents = new HashSet<FsContent>();
|
final Map<FsContent, Integer> hitContents = ContentHit.flattenResults(tcqRes);
|
||||||
for (String key : tcqRes.keySet()) {
|
|
||||||
fsContents.addAll(tcqRes.get(key));
|
|
||||||
}
|
|
||||||
|
|
||||||
//get listname
|
//get listname
|
||||||
String listName = "";
|
String listName = "";
|
||||||
@ -247,23 +244,27 @@ public class KeywordSearchResultFactory extends ChildFactory<KeyValueQuery> {
|
|||||||
final boolean literal_query = tcq.isEscaped();
|
final boolean literal_query = tcq.isEscaped();
|
||||||
|
|
||||||
int resID = 0;
|
int resID = 0;
|
||||||
for (final FsContent f : fsContents) {
|
for (final FsContent f : hitContents.keySet()) {
|
||||||
|
final int previewChunk = hitContents.get(f);
|
||||||
//get unique match result files
|
//get unique match result files
|
||||||
Map<String, Object> resMap = new LinkedHashMap<String, Object>();
|
Map<String, Object> resMap = new LinkedHashMap<String, Object>();
|
||||||
AbstractFsContentNode.fillPropertyMap(resMap, f);
|
AbstractFsContentNode.fillPropertyMap(resMap, f);
|
||||||
setCommonProperty(resMap, CommonPropertyTypes.MATCH, f.getName());
|
setCommonProperty(resMap, CommonPropertyTypes.MATCH, f.getName());
|
||||||
if (literal_query) {
|
|
||||||
|
if (true) {
|
||||||
try {
|
try {
|
||||||
String snippet;
|
String snippet;
|
||||||
snippet = LuceneQuery.querySnippet(tcq.getEscapedQueryString(), f.getId(), false, true);
|
//TODO reuse snippet in ResultWriter
|
||||||
|
snippet = LuceneQuery.querySnippet(tcq.getEscapedQueryString(), f.getId(), previewChunk, !literal_query, true);
|
||||||
setCommonProperty(resMap, CommonPropertyTypes.CONTEXT, snippet);
|
setCommonProperty(resMap, CommonPropertyTypes.CONTEXT, snippet);
|
||||||
} catch (NoOpenCoreException ex) {
|
} catch (NoOpenCoreException ex) {
|
||||||
logger.log(Level.WARNING, "Could not perform the query. ", ex);
|
logger.log(Level.WARNING, "Could not perform the snippet query. ", ex);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final String highlightQueryEscaped = getHighlightQuery(tcq, literal_query, tcqRes, f);
|
final String highlightQueryEscaped = getHighlightQuery(tcq, literal_query, tcqRes, f);
|
||||||
toPopulate.add(new KeyValueQueryContent(f.getName(), resMap, ++resID, f, highlightQueryEscaped, tcq));
|
toPopulate.add(new KeyValueQueryContent(f.getName(), resMap, ++resID, f, highlightQueryEscaped, tcq, previewChunk));
|
||||||
}
|
}
|
||||||
//write to bb
|
//write to bb
|
||||||
new ResultWriter(tcqRes, tcq, listName).execute();
|
new ResultWriter(tcqRes, tcq, listName).execute();
|
||||||
@ -272,7 +273,7 @@ public class KeywordSearchResultFactory extends ChildFactory<KeyValueQuery> {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getHighlightQuery(KeywordSearchQuery tcq, boolean literal_query, Map<String, List<FsContent>> tcqRes, FsContent f) {
|
private String getHighlightQuery(KeywordSearchQuery tcq, boolean literal_query, Map<String, List<ContentHit>> tcqRes, FsContent f) {
|
||||||
String highlightQueryEscaped = null;
|
String highlightQueryEscaped = null;
|
||||||
if (literal_query) {
|
if (literal_query) {
|
||||||
//literal, treat as non-regex, non-term component query
|
//literal, treat as non-regex, non-term component query
|
||||||
@ -290,8 +291,13 @@ public class KeywordSearchResultFactory extends ChildFactory<KeyValueQuery> {
|
|||||||
//find terms for this file hit
|
//find terms for this file hit
|
||||||
List<String> hitTerms = new ArrayList<String>();
|
List<String> hitTerms = new ArrayList<String>();
|
||||||
for (String term : tcqRes.keySet()) {
|
for (String term : tcqRes.keySet()) {
|
||||||
if (tcqRes.get(term).contains(f)) {
|
List<ContentHit> hitList = tcqRes.get(term);
|
||||||
|
|
||||||
|
for (ContentHit h : hitList) {
|
||||||
|
if (h.getContent().equals(f)) {
|
||||||
hitTerms.add(term);
|
hitTerms.add(term);
|
||||||
|
break; //go to next term
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -305,8 +311,8 @@ public class KeywordSearchResultFactory extends ChildFactory<KeyValueQuery> {
|
|||||||
highlightQuery.append("\"");
|
highlightQuery.append("\"");
|
||||||
if (lastTerm != curTerm) {
|
if (lastTerm != curTerm) {
|
||||||
highlightQuery.append(" "); //acts as OR ||
|
highlightQuery.append(" "); //acts as OR ||
|
||||||
//force white-space separated index and stored content
|
//force HIGHLIGHT_FIELD_REGEX index and stored content
|
||||||
//in each term after first. First term taken case by HighlightedMatchesSource
|
//in each term after first. First term taken care by HighlightedMatchesSource
|
||||||
highlightQuery.append(LuceneQuery.HIGHLIGHT_FIELD_REGEX).append(":");
|
highlightQuery.append(LuceneQuery.HIGHLIGHT_FIELD_REGEX).append(":");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -327,11 +333,12 @@ public class KeywordSearchResultFactory extends ChildFactory<KeyValueQuery> {
|
|||||||
final KeyValueQueryContent thingContent = (KeyValueQueryContent) thing;
|
final KeyValueQueryContent thingContent = (KeyValueQueryContent) thing;
|
||||||
final Content content = thingContent.getContent();
|
final Content content = thingContent.getContent();
|
||||||
final String queryStr = thingContent.getQueryStr();
|
final String queryStr = thingContent.getQueryStr();
|
||||||
|
final int previewChunk = thingContent.getPreviewChunk();
|
||||||
|
|
||||||
Node kvNode = new KeyValueNode(thingContent, Children.LEAF, Lookups.singleton(content));
|
Node kvNode = new KeyValueNode(thingContent, Children.LEAF, Lookups.singleton(content));
|
||||||
//wrap in KeywordSearchFilterNode for the markup content, might need to override FilterNode for more customization
|
//wrap in KeywordSearchFilterNode for the markup content, might need to override FilterNode for more customization
|
||||||
HighlightedMatchesSource highlights = new HighlightedMatchesSource(content, queryStr, !thingContent.getQuery().isEscaped(), false);
|
HighlightedMatchesSource highlights = new HighlightedMatchesSource(content, queryStr, !thingContent.getQuery().isEscaped(), false);
|
||||||
return new KeywordSearchFilterNode(highlights, kvNode, queryStr);
|
return new KeywordSearchFilterNode(highlights, kvNode, queryStr, previewChunk);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -379,7 +386,7 @@ public class KeywordSearchResultFactory extends ChildFactory<KeyValueQuery> {
|
|||||||
LuceneQuery filesQuery = new LuceneQuery(keywordQuery);
|
LuceneQuery filesQuery = new LuceneQuery(keywordQuery);
|
||||||
filesQuery.escape();
|
filesQuery.escape();
|
||||||
|
|
||||||
Map<String, List<FsContent>> matchesRes;
|
Map<String, List<ContentHit>> matchesRes;
|
||||||
try {
|
try {
|
||||||
matchesRes = filesQuery.performQuery();
|
matchesRes = filesQuery.performQuery();
|
||||||
} catch (NoOpenCoreException ex) {
|
} catch (NoOpenCoreException ex) {
|
||||||
@ -387,23 +394,18 @@ public class KeywordSearchResultFactory extends ChildFactory<KeyValueQuery> {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<FsContent> matches = new HashSet<FsContent>();
|
|
||||||
for (String key : matchesRes.keySet()) {
|
|
||||||
matches.addAll(matchesRes.get(key));
|
|
||||||
}
|
|
||||||
|
|
||||||
//get unique match result files
|
//get unique match result files
|
||||||
final Set<FsContent> uniqueMatches = new LinkedHashSet<FsContent>();
|
final Map<FsContent, Integer> uniqueMatches = ContentHit.flattenResults(matchesRes);
|
||||||
uniqueMatches.addAll(matches);
|
|
||||||
|
|
||||||
int resID = 0;
|
int resID = 0;
|
||||||
|
|
||||||
final KeywordSearchQuery origQuery = thing.getQuery();
|
final KeywordSearchQuery origQuery = thing.getQuery();
|
||||||
|
|
||||||
for (final FsContent f : uniqueMatches) {
|
for (final FsContent f : uniqueMatches.keySet()) {
|
||||||
|
final int previewChunkId = uniqueMatches.get(f);
|
||||||
Map<String, Object> resMap = new LinkedHashMap<String, Object>();
|
Map<String, Object> resMap = new LinkedHashMap<String, Object>();
|
||||||
AbstractFsContentNode.fillPropertyMap(resMap, (File) f);
|
AbstractFsContentNode.fillPropertyMap(resMap, (File) f);
|
||||||
toPopulate.add(new KeyValueQueryContent(f.getName(), resMap, ++resID, f, keywordQuery, thing.getQuery()));
|
toPopulate.add(new KeyValueQueryContent(f.getName(), resMap, ++resID, f, keywordQuery, thing.getQuery(), previewChunkId));
|
||||||
|
|
||||||
}
|
}
|
||||||
//write to bb
|
//write to bb
|
||||||
@ -417,11 +419,12 @@ public class KeywordSearchResultFactory extends ChildFactory<KeyValueQuery> {
|
|||||||
final KeyValueQueryContent thingContent = (KeyValueQueryContent) thing;
|
final KeyValueQueryContent thingContent = (KeyValueQueryContent) thing;
|
||||||
final Content content = thingContent.getContent();
|
final Content content = thingContent.getContent();
|
||||||
final String query = thingContent.getQueryStr();
|
final String query = thingContent.getQueryStr();
|
||||||
|
final int previewChunk = thingContent.getPreviewChunk();
|
||||||
|
|
||||||
Node kvNode = new KeyValueNode(thingContent, Children.LEAF, Lookups.singleton(content));
|
Node kvNode = new KeyValueNode(thingContent, Children.LEAF, Lookups.singleton(content));
|
||||||
//wrap in KeywordSearchFilterNode for the markup content
|
//wrap in KeywordSearchFilterNode for the markup content
|
||||||
HighlightedMatchesSource highlights = new HighlightedMatchesSource(content, query, !thingContent.getQuery().isEscaped());
|
HighlightedMatchesSource highlights = new HighlightedMatchesSource(content, query, !thingContent.getQuery().isEscaped());
|
||||||
return new KeywordSearchFilterNode(highlights, kvNode, query);
|
return new KeywordSearchFilterNode(highlights, kvNode, query, previewChunk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -434,6 +437,7 @@ public class KeywordSearchResultFactory extends ChildFactory<KeyValueQuery> {
|
|||||||
private Content content;
|
private Content content;
|
||||||
private String queryStr;
|
private String queryStr;
|
||||||
private KeywordSearchQuery query;
|
private KeywordSearchQuery query;
|
||||||
|
private int previewChunk;
|
||||||
|
|
||||||
Content getContent() {
|
Content getContent() {
|
||||||
return content;
|
return content;
|
||||||
@ -443,10 +447,15 @@ public class KeywordSearchResultFactory extends ChildFactory<KeyValueQuery> {
|
|||||||
return queryStr;
|
return queryStr;
|
||||||
}
|
}
|
||||||
|
|
||||||
public KeyValueQueryContent(String name, Map<String, Object> map, int id, Content content, String queryStr, KeywordSearchQuery query) {
|
int getPreviewChunk() {
|
||||||
|
return previewChunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KeyValueQueryContent(String name, Map<String, Object> map, int id, Content content, String queryStr, KeywordSearchQuery query, int previewChunk) {
|
||||||
super(name, map, id, query);
|
super(name, map, id, query);
|
||||||
this.content = content;
|
this.content = content;
|
||||||
this.queryStr = queryStr;
|
this.queryStr = queryStr;
|
||||||
|
this.previewChunk = previewChunk;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -460,13 +469,15 @@ public class KeywordSearchResultFactory extends ChildFactory<KeyValueQuery> {
|
|||||||
private ProgressHandle progress;
|
private ProgressHandle progress;
|
||||||
private KeywordSearchQuery query;
|
private KeywordSearchQuery query;
|
||||||
private String listName;
|
private String listName;
|
||||||
private Map<String, List<FsContent>> hits;
|
private Map<String, List<ContentHit>> hits;
|
||||||
final Collection<BlackboardArtifact> na = new ArrayList<BlackboardArtifact>();
|
final Collection<BlackboardArtifact> na = new ArrayList<BlackboardArtifact>();
|
||||||
|
private static final int QUERY_DISPLAY_LEN = 40;
|
||||||
|
|
||||||
ResultWriter(Map<String, List<FsContent>> hits, KeywordSearchQuery query, String listName) {
|
ResultWriter(Map<String, List<ContentHit>> hits, KeywordSearchQuery query, String listName) {
|
||||||
this.hits = hits;
|
this.hits = hits;
|
||||||
this.query = query;
|
this.query = query;
|
||||||
this.listName = listName;
|
this.listName = listName;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -484,7 +495,7 @@ public class KeywordSearchResultFactory extends ChildFactory<KeyValueQuery> {
|
|||||||
protected Object doInBackground() throws Exception {
|
protected Object doInBackground() throws Exception {
|
||||||
registerWriter(this);
|
registerWriter(this);
|
||||||
final String queryStr = query.getQueryString();
|
final String queryStr = query.getQueryString();
|
||||||
final String queryDisp = queryStr.length() > 40 ? queryStr.substring(0, 39) + " ..." : queryStr;
|
final String queryDisp = queryStr.length() > QUERY_DISPLAY_LEN ? queryStr.substring(0, QUERY_DISPLAY_LEN - 1) + " ..." : queryStr;
|
||||||
progress = ProgressHandleFactory.createHandle("Saving results: " + queryDisp, new Cancellable() {
|
progress = ProgressHandleFactory.createHandle("Saving results: " + queryDisp, new Cancellable() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -500,12 +511,28 @@ public class KeywordSearchResultFactory extends ChildFactory<KeyValueQuery> {
|
|||||||
if (this.isCancelled()) {
|
if (this.isCancelled()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
for (FsContent f : hits.get(hit)) {
|
Map<FsContent, Integer> flattened = ContentHit.flattenResults(hits.get(hit));
|
||||||
KeywordWriteResult written = query.writeToBlackBoard(hit, f, listName);
|
for (FsContent f : flattened.keySet()) {
|
||||||
|
int chunkId = flattened.get(f);
|
||||||
|
final String snippetQuery = KeywordSearchUtil.escapeLuceneQuery(hit, true, false);
|
||||||
|
String snippet = null;
|
||||||
|
try {
|
||||||
|
snippet = LuceneQuery.querySnippet(snippetQuery, f.getId(), chunkId, !query.isLiteral(), true);
|
||||||
|
} catch (NoOpenCoreException e) {
|
||||||
|
logger.log(Level.WARNING, "Error querying snippet: " + snippetQuery, e);
|
||||||
|
//no reason to continie
|
||||||
|
return null;
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.log(Level.WARNING, "Error querying snippet: " + snippetQuery, e);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (snippet != null) {
|
||||||
|
KeywordWriteResult written = query.writeToBlackBoard(hit, f, snippet, listName);
|
||||||
if (written != null) {
|
if (written != null) {
|
||||||
na.add(written.getArtifact());
|
na.add(written.getArtifact());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,16 +18,12 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.keywordsearch;
|
package org.sleuthkit.autopsy.keywordsearch;
|
||||||
|
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.sql.Statement;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import org.apache.commons.lang.StringEscapeUtils;
|
import org.apache.commons.lang.StringEscapeUtils;
|
||||||
@ -39,7 +35,6 @@ import org.apache.solr.client.solrj.response.TermsResponse.Term;
|
|||||||
import org.apache.solr.common.SolrDocument;
|
import org.apache.solr.common.SolrDocument;
|
||||||
import org.apache.solr.common.SolrDocumentList;
|
import org.apache.solr.common.SolrDocumentList;
|
||||||
import org.openide.nodes.Node;
|
import org.openide.nodes.Node;
|
||||||
import org.openide.util.Exceptions;
|
|
||||||
import org.openide.windows.TopComponent;
|
import org.openide.windows.TopComponent;
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
import org.sleuthkit.autopsy.corecomponents.DataResultTopComponent;
|
import org.sleuthkit.autopsy.corecomponents.DataResultTopComponent;
|
||||||
@ -61,10 +56,10 @@ public class LuceneQuery implements KeywordSearchQuery {
|
|||||||
private boolean isEscaped;
|
private boolean isEscaped;
|
||||||
private Keyword keywordQuery = null;
|
private Keyword keywordQuery = null;
|
||||||
//use different highlight Solr fields for regex and literal search
|
//use different highlight Solr fields for regex and literal search
|
||||||
static final String HIGHLIGHT_FIELD_LITERAL = "content";
|
static final String HIGHLIGHT_FIELD_LITERAL = Server.Schema.CONTENT.toString();
|
||||||
static final String HIGHLIGHT_FIELD_REGEX = "content";
|
static final String HIGHLIGHT_FIELD_REGEX = Server.Schema.CONTENT.toString();
|
||||||
//TODO use content_ws stored="true" in solr schema for perfect highlight hits
|
//TODO use content_ws stored="true" in solr schema for perfect highlight hits
|
||||||
//static final String HIGHLIGHT_FIELD_REGEX = "content_ws";
|
//static final String HIGHLIGHT_FIELD_REGEX = Server.Schema.CONTENT_WS.toString()
|
||||||
|
|
||||||
public LuceneQuery(Keyword keywordQuery) {
|
public LuceneQuery(Keyword keywordQuery) {
|
||||||
this(keywordQuery.getQuery());
|
this(keywordQuery.getQuery());
|
||||||
@ -88,6 +83,13 @@ public class LuceneQuery implements KeywordSearchQuery {
|
|||||||
return isEscaped;
|
return isEscaped;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLiteral() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getEscapedQueryString() {
|
public String getEscapedQueryString() {
|
||||||
return this.queryEscaped;
|
return this.queryEscaped;
|
||||||
@ -104,8 +106,8 @@ public class LuceneQuery implements KeywordSearchQuery {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, List<FsContent>> performQuery() throws NoOpenCoreException {
|
public Map<String, List<ContentHit>> performQuery() throws NoOpenCoreException {
|
||||||
Map<String, List<FsContent>> results = new HashMap<String, List<FsContent>>();
|
Map<String, List<ContentHit>> results = new HashMap<String, List<ContentHit>>();
|
||||||
//in case of single term literal query there is only 1 term
|
//in case of single term literal query there is only 1 term
|
||||||
results.put(query, performLuceneQuery());
|
results.put(query, performLuceneQuery());
|
||||||
|
|
||||||
@ -115,8 +117,8 @@ public class LuceneQuery implements KeywordSearchQuery {
|
|||||||
@Override
|
@Override
|
||||||
public void execute() {
|
public void execute() {
|
||||||
escape();
|
escape();
|
||||||
Set<FsContent> fsMatches = new HashSet<FsContent>();
|
|
||||||
final Map<String, List<FsContent>> matches;
|
final Map<String, List<ContentHit>> matches;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
matches = performQuery();
|
matches = performQuery();
|
||||||
@ -124,10 +126,6 @@ public class LuceneQuery implements KeywordSearchQuery {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (String key : matches.keySet()) {
|
|
||||||
fsMatches.addAll(matches.get(key));
|
|
||||||
}
|
|
||||||
|
|
||||||
String pathText = "Keyword query: " + query;
|
String pathText = "Keyword query: " + query;
|
||||||
|
|
||||||
if (matches.isEmpty()) {
|
if (matches.isEmpty()) {
|
||||||
@ -135,10 +133,13 @@ public class LuceneQuery implements KeywordSearchQuery {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//map of unique fs hit and chunk id or 0
|
||||||
|
LinkedHashMap<FsContent,Integer> fsMatches = ContentHit.flattenResults(matches);
|
||||||
|
|
||||||
//get listname
|
//get listname
|
||||||
String listName = "";
|
String listName = "";
|
||||||
|
|
||||||
Node rootNode = new KeywordSearchNode(new ArrayList<FsContent>(fsMatches), queryEscaped);
|
Node rootNode = new KeywordSearchNode(fsMatches, queryEscaped);
|
||||||
Node filteredRootNode = new TableFilterNode(rootNode, true);
|
Node filteredRootNode = new TableFilterNode(rootNode, true);
|
||||||
|
|
||||||
TopComponent searchResultWin = DataResultTopComponent.createInstance("Keyword search", pathText, filteredRootNode, matches.size());
|
TopComponent searchResultWin = DataResultTopComponent.createInstance("Keyword search", pathText, filteredRootNode, matches.size());
|
||||||
@ -154,9 +155,8 @@ public class LuceneQuery implements KeywordSearchQuery {
|
|||||||
return query != null && !query.equals("");
|
return query != null && !query.equals("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public KeywordWriteResult writeToBlackBoard(String termHit, FsContent newFsHit, String listName) throws NoOpenCoreException {
|
public KeywordWriteResult writeToBlackBoard(String termHit, FsContent newFsHit, String snippet, String listName) {
|
||||||
final String MODULE_NAME = KeywordSearchIngestService.MODULE_NAME;
|
final String MODULE_NAME = KeywordSearchIngestService.MODULE_NAME;
|
||||||
|
|
||||||
KeywordWriteResult writeResult = null;
|
KeywordWriteResult writeResult = null;
|
||||||
@ -170,18 +170,6 @@ public class LuceneQuery implements KeywordSearchQuery {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
String snippet = null;
|
|
||||||
try {
|
|
||||||
snippet = LuceneQuery.querySnippet(queryEscaped, newFsHit.getId(), false, true);
|
|
||||||
}
|
|
||||||
catch (NoOpenCoreException e) {
|
|
||||||
logger.log(Level.WARNING, "Error querying snippet: " + query, e);
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
logger.log(Level.WARNING, "Error querying snippet: " + query, e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (snippet != null) {
|
if (snippet != null) {
|
||||||
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW.getTypeID(), MODULE_NAME, "", snippet));
|
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW.getTypeID(), MODULE_NAME, "", snippet));
|
||||||
}
|
}
|
||||||
@ -219,9 +207,9 @@ public class LuceneQuery implements KeywordSearchQuery {
|
|||||||
* @param query
|
* @param query
|
||||||
* @return matches List
|
* @return matches List
|
||||||
*/
|
*/
|
||||||
private List<FsContent> performLuceneQuery() throws NoOpenCoreException {
|
private List<ContentHit> performLuceneQuery() throws NoOpenCoreException {
|
||||||
|
|
||||||
List<FsContent> matches = new ArrayList<FsContent>();
|
List<ContentHit> matches = new ArrayList<ContentHit>();
|
||||||
|
|
||||||
boolean allMatchesFetched = false;
|
boolean allMatchesFetched = false;
|
||||||
final int ROWS_PER_FETCH = 10000;
|
final int ROWS_PER_FETCH = 10000;
|
||||||
@ -232,21 +220,18 @@ public class LuceneQuery implements KeywordSearchQuery {
|
|||||||
|
|
||||||
q.setQuery(queryEscaped);
|
q.setQuery(queryEscaped);
|
||||||
q.setRows(ROWS_PER_FETCH);
|
q.setRows(ROWS_PER_FETCH);
|
||||||
q.setFields("id");
|
q.setFields(Server.Schema.ID.toString());
|
||||||
|
|
||||||
|
|
||||||
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 = solrServer.query(q, METHOD.POST);
|
QueryResponse response = solrServer.query(q, METHOD.POST);
|
||||||
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;
|
||||||
|
SleuthkitCase sc = null;
|
||||||
SleuthkitCase sc;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
sc = Case.getCurrentCase().getSleuthkitCase();
|
sc = Case.getCurrentCase().getSleuthkitCase();
|
||||||
} catch (IllegalStateException ex) {
|
} catch (IllegalStateException ex) {
|
||||||
@ -255,17 +240,39 @@ public class LuceneQuery implements KeywordSearchQuery {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (SolrDocument resultDoc : resultList) {
|
for (SolrDocument resultDoc : resultList) {
|
||||||
long id = Long.parseLong((String) resultDoc.getFieldValue("id"));
|
final String resultID = (String) resultDoc.getFieldValue(Server.Schema.ID.toString());
|
||||||
|
|
||||||
// TODO: has to be a better way to get files. Also, need to
|
final int sepIndex = resultID.indexOf('_');
|
||||||
// check that we actually get 1 hit for each id
|
|
||||||
ResultSet rs = sc.runQuery("select * from tsk_files where obj_id=" + id);
|
if (sepIndex != -1) {
|
||||||
matches.addAll(sc.resultSetToFsContents(rs));
|
//file chunk result
|
||||||
final Statement s = rs.getStatement();
|
final long fileID = Long.parseLong(resultID.substring(0, sepIndex));
|
||||||
rs.close();
|
final int chunkId = Integer.parseInt(resultID.substring(sepIndex+1));
|
||||||
if (s != null) {
|
logger.log(Level.INFO, "file id: " + fileID + ", chunkID: " + chunkId);
|
||||||
s.close();
|
|
||||||
|
try {
|
||||||
|
FsContent resultFsContent = sc.getFsContentById(fileID);
|
||||||
|
matches.add(new ContentHit(resultFsContent, chunkId));
|
||||||
|
|
||||||
|
} catch (TskException ex) {
|
||||||
|
logger.log(Level.WARNING, "Could not get the fscontent for keyword hit, ", ex);
|
||||||
|
//something wrong with case/db
|
||||||
|
return matches;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
final long fileID = Long.parseLong(resultID);
|
||||||
|
|
||||||
|
try {
|
||||||
|
FsContent resultFsContent = sc.getFsContentById(fileID);
|
||||||
|
matches.add(new ContentHit(resultFsContent));
|
||||||
|
} catch (TskException ex) {
|
||||||
|
logger.log(Level.WARNING, "Could not get the fscontent for keyword hit, ", ex);
|
||||||
|
//something wrong with case/db
|
||||||
|
return matches;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -275,9 +282,6 @@ public class LuceneQuery implements KeywordSearchQuery {
|
|||||||
} catch (SolrServerException ex) {
|
} catch (SolrServerException ex) {
|
||||||
logger.log(Level.WARNING, "Error executing Lucene Solr Query: " + query, ex);
|
logger.log(Level.WARNING, "Error executing Lucene Solr Query: " + query, ex);
|
||||||
// TODO: handle bad query strings, among other issues
|
// TODO: handle bad query strings, among other issues
|
||||||
} catch (SQLException ex) {
|
|
||||||
logger.log(Level.WARNING, "Error interpreting results from Lucene Solr Query: " + query, ex);
|
|
||||||
return matches;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -293,6 +297,20 @@ public class LuceneQuery implements KeywordSearchQuery {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static String querySnippet(String query, long contentID, boolean isRegex, boolean group) throws NoOpenCoreException {
|
public static String querySnippet(String query, long contentID, boolean isRegex, boolean group) throws NoOpenCoreException {
|
||||||
|
return querySnippet(query, contentID, 0, isRegex, group);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* return snippet preview context
|
||||||
|
* @param query the keyword query for text to highlight. Lucene special cahrs should already be escaped.
|
||||||
|
* @param contentID content id associated with the hit
|
||||||
|
* @param chunkID chunk id associated with the content hit, or 0 if no chunks
|
||||||
|
* @param isRegex whether the query is a regular expression (different Solr fields are then used to generate the preview)
|
||||||
|
* @param group whether the query should look for all terms grouped together in the query order, or not
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String querySnippet(String query, long contentID, int chunkID, boolean isRegex, boolean group) throws NoOpenCoreException {
|
||||||
final int SNIPPET_LENGTH = 45;
|
final int SNIPPET_LENGTH = 45;
|
||||||
|
|
||||||
Server solrServer = KeywordSearch.getServer();
|
Server solrServer = KeywordSearch.getServer();
|
||||||
@ -323,17 +341,27 @@ public class LuceneQuery implements KeywordSearchQuery {
|
|||||||
//quote only if user supplies quotes
|
//quote only if user supplies quotes
|
||||||
q.setQuery(query);
|
q.setQuery(query);
|
||||||
}
|
}
|
||||||
q.addFilterQuery("id:" + contentID);
|
|
||||||
|
String contentIDStr = null;
|
||||||
|
|
||||||
|
if (chunkID == 0)
|
||||||
|
contentIDStr = Long.toString(contentID);
|
||||||
|
else
|
||||||
|
contentIDStr = FileExtractedChild.getFileExtractChildId(contentID, chunkID);
|
||||||
|
|
||||||
|
String idQuery = Server.Schema.ID.toString() + ":" + contentIDStr;
|
||||||
|
q.addFilterQuery(idQuery);
|
||||||
q.addHighlightField(highlightField);
|
q.addHighlightField(highlightField);
|
||||||
q.setHighlightSimplePre("«");
|
q.setHighlightSimplePre("«");
|
||||||
q.setHighlightSimplePost("»");
|
q.setHighlightSimplePost("»");
|
||||||
q.setHighlightSnippets(1);
|
q.setHighlightSnippets(1);
|
||||||
q.setHighlightFragsize(SNIPPET_LENGTH);
|
q.setHighlightFragsize(SNIPPET_LENGTH);
|
||||||
|
q.setParam("hl.maxAnalyzedChars", Server.HL_ANALYZE_CHARS_UNLIMITED); //analyze all content
|
||||||
|
|
||||||
try {
|
try {
|
||||||
QueryResponse response = solrServer.query(q);
|
QueryResponse response = solrServer.query(q);
|
||||||
Map<String, Map<String, List<String>>> responseHighlight = response.getHighlighting();
|
Map<String, Map<String, List<String>>> responseHighlight = response.getHighlighting();
|
||||||
Map<String, List<String>> responseHighlightID = responseHighlight.get(Long.toString(contentID));
|
Map<String, List<String>> responseHighlightID = responseHighlight.get(contentIDStr);
|
||||||
if (responseHighlightID == null) {
|
if (responseHighlightID == null) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
@ -25,10 +25,12 @@ package org.sleuthkit.autopsy.keywordsearch;
|
|||||||
public interface MarkupSource {
|
public interface MarkupSource {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param pageNum page number to get markup for
|
||||||
* @return text optionally marked up with the subsest of HTML that Swing
|
* @return text optionally marked up with the subsest of HTML that Swing
|
||||||
* components can handle in their setText() method.
|
* components can handle in their setText() method.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
String getMarkup();
|
String getMarkup(int pageNum);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -53,4 +55,10 @@ public interface MarkupSource {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
String toString();
|
String toString();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get number pages/chunks
|
||||||
|
* @return number pages
|
||||||
|
*/
|
||||||
|
int getNumberPages();
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,7 @@ import org.openide.modules.InstalledFileLocator;
|
|||||||
import org.openide.util.Exceptions;
|
import org.openide.util.Exceptions;
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
|
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.Version;
|
||||||
import org.sleuthkit.datamodel.Content;
|
import org.sleuthkit.datamodel.Content;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -56,6 +57,77 @@ import org.sleuthkit.datamodel.Content;
|
|||||||
*/
|
*/
|
||||||
class Server {
|
class Server {
|
||||||
|
|
||||||
|
public static enum Schema {
|
||||||
|
|
||||||
|
ID {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "id";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
CONTENT {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "content";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
CONTENT_WS {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "content_ws";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
FILE_NAME {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "file_name";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
CTIME {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ctime";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ATIME {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "atime";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MTIME {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "mtime";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
CRTIME {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "crtime";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
NUM_CHUNKS {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "num_chunks";
|
||||||
|
}
|
||||||
|
},};
|
||||||
|
|
||||||
|
public static final String HL_ANALYZE_CHARS_UNLIMITED = "-1";
|
||||||
|
|
||||||
|
//max content size we can send to Solr
|
||||||
|
public static final long MAX_CONTENT_SIZE = 1L * 1024 * 1024 * 1024;
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(Server.class.getName());
|
private static final Logger logger = Logger.getLogger(Server.class.getName());
|
||||||
private static final String DEFAULT_CORE_NAME = "coreCase";
|
private static final String DEFAULT_CORE_NAME = "coreCase";
|
||||||
// TODO: DEFAULT_CORE_NAME needs to be replaced with unique names to support multiple open cases
|
// TODO: DEFAULT_CORE_NAME needs to be replaced with unique names to support multiple open cases
|
||||||
@ -144,6 +216,10 @@ class Server {
|
|||||||
while ((line = br.readLine()) != null) {
|
while ((line = br.readLine()) != null) {
|
||||||
bw.write(line);
|
bw.write(line);
|
||||||
bw.newLine();
|
bw.newLine();
|
||||||
|
if (Version.getBuildType() == Version.Type.DEVELOPMENT) {
|
||||||
|
//flush buffers if dev version for debugging
|
||||||
|
bw.flush();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
Exceptions.printStackTrace(ex);
|
Exceptions.printStackTrace(ex);
|
||||||
@ -294,6 +370,34 @@ class Server {
|
|||||||
return currentCore.queryNumIndexedFiles();
|
return currentCore.queryNumIndexedFiles();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if the file is indexed (either as a whole as a chunk)
|
||||||
|
* @param contentID
|
||||||
|
* @return true if it is indexed
|
||||||
|
* @throws SolrServerException, NoOpenCoreException
|
||||||
|
*/
|
||||||
|
public boolean queryIsIndexed(long contentID) throws SolrServerException, NoOpenCoreException {
|
||||||
|
if (currentCore == null) {
|
||||||
|
throw new NoOpenCoreException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return currentCore.queryIsIndexed(contentID);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute query that gets number of indexed file chunks for a file
|
||||||
|
* @param fileID file id of the original file broken into chunks and indexed
|
||||||
|
* @return int representing number of indexed file chunks, 0 if there is no chunks
|
||||||
|
* @throws SolrServerException
|
||||||
|
*/
|
||||||
|
public int queryNumFileChunks(long fileID) throws SolrServerException, NoOpenCoreException {
|
||||||
|
if (currentCore == null) {
|
||||||
|
throw new NoOpenCoreException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return currentCore.queryNumFileChunks(fileID);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute solr query
|
* Execute solr query
|
||||||
* @param sq query
|
* @param sq query
|
||||||
@ -348,7 +452,22 @@ class Server {
|
|||||||
if (currentCore == null) {
|
if (currentCore == null) {
|
||||||
throw new NoOpenCoreException();
|
throw new NoOpenCoreException();
|
||||||
}
|
}
|
||||||
return currentCore.getSolrContent(content);
|
return currentCore.getSolrContent(content.getId(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute Solr query to get content text from content chunk
|
||||||
|
* @param content to get the text for
|
||||||
|
* @param chunkID chunk number to query (starting at 1), or 0 if there is no chunks for that content
|
||||||
|
* @return content text string
|
||||||
|
* @throws SolrServerException
|
||||||
|
* @throws NoOpenCoreException
|
||||||
|
*/
|
||||||
|
public String getSolrContent(final Content content, int chunkID) throws SolrServerException, NoOpenCoreException {
|
||||||
|
if (currentCore == null) {
|
||||||
|
throw new NoOpenCoreException();
|
||||||
|
}
|
||||||
|
return currentCore.getSolrContent(content.getId(), chunkID);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -436,15 +555,19 @@ class Server {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getSolrContent(final Content content) {
|
|
||||||
|
private String getSolrContent(long contentID, int chunkID) {
|
||||||
final SolrQuery q = new SolrQuery();
|
final SolrQuery q = new SolrQuery();
|
||||||
q.setQuery("*:*");
|
q.setQuery("*:*");
|
||||||
q.addFilterQuery("id:" + content.getId());
|
String filterQuery = Schema.ID.toString() + ":" + contentID;
|
||||||
q.setFields("content");
|
if (chunkID != 0)
|
||||||
|
filterQuery = filterQuery + "_" + chunkID;
|
||||||
|
q.addFilterQuery(filterQuery);
|
||||||
|
q.setFields(Schema.CONTENT.toString());
|
||||||
try {
|
try {
|
||||||
return (String) solrCore.query(q).getResults().get(0).getFieldValue("content");
|
return (String) solrCore.query(q).getResults().get(0).getFieldValue(Schema.CONTENT.toString());
|
||||||
} catch (SolrServerException ex) {
|
} catch (SolrServerException ex) {
|
||||||
logger.log(Level.WARNING, "Error getting content from Solr and validating regex match", ex);
|
logger.log(Level.WARNING, "Error getting content from Solr", ex);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -470,6 +593,32 @@ class Server {
|
|||||||
q.setRows(0);
|
q.setRows(0);
|
||||||
return (int) query(q).getResults().getNumFound();
|
return (int) query(q).getResults().getNumFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if the file is indexed (either as a whole as a chunk)
|
||||||
|
* @param contentID
|
||||||
|
* @return true if it is indexed
|
||||||
|
* @throws SolrServerException
|
||||||
|
*/
|
||||||
|
private boolean queryIsIndexed(long contentID) throws SolrServerException {
|
||||||
|
SolrQuery q = new SolrQuery("*:*");
|
||||||
|
q.addFilterQuery(Server.Schema.ID.toString() + ":" + Long.toString(contentID));
|
||||||
|
//q.setFields(Server.Schema.ID.toString());
|
||||||
|
q.setRows(0);
|
||||||
|
return (int) query(q).getResults().getNumFound() != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute query that gets number of indexed file chunks for a file
|
||||||
|
* @param contentID file id of the original file broken into chunks and indexed
|
||||||
|
* @return int representing number of indexed file chunks, 0 if there is no chunks
|
||||||
|
* @throws SolrServerException
|
||||||
|
*/
|
||||||
|
private int queryNumFileChunks(long contentID) throws SolrServerException {
|
||||||
|
SolrQuery q = new SolrQuery("id:" + Long.toString(contentID) + "_*");
|
||||||
|
q.setRows(0);
|
||||||
|
return (int) query(q).getResults().getNumFound();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ServerAction extends AbstractAction {
|
class ServerAction extends AbstractAction {
|
||||||
|
@ -56,7 +56,7 @@ public class TermComponentQuery implements KeywordSearchQuery {
|
|||||||
|
|
||||||
private static final int TERMS_UNLIMITED = -1;
|
private static final int TERMS_UNLIMITED = -1;
|
||||||
//corresponds to field in Solr schema, analyzed with white-space tokenizer only
|
//corresponds to field in Solr schema, analyzed with white-space tokenizer only
|
||||||
private static final String TERMS_SEARCH_FIELD = "content_ws";
|
private static final String TERMS_SEARCH_FIELD = Server.Schema.CONTENT_WS.toString();
|
||||||
private static final String TERMS_HANDLER = "/terms";
|
private static final String TERMS_HANDLER = "/terms";
|
||||||
private static final int TERMS_TIMEOUT = 90 * 1000; //in ms
|
private static final int TERMS_TIMEOUT = 90 * 1000; //in ms
|
||||||
private static Logger logger = Logger.getLogger(TermComponentQuery.class.getName());
|
private static Logger logger = Logger.getLogger(TermComponentQuery.class.getName());
|
||||||
@ -102,6 +102,11 @@ public class TermComponentQuery implements KeywordSearchQuery {
|
|||||||
return isEscaped;
|
return isEscaped;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLiteral() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* helper method to create a Solr terms component query
|
* helper method to create a Solr terms component query
|
||||||
*/
|
*/
|
||||||
@ -154,23 +159,9 @@ public class TermComponentQuery implements KeywordSearchQuery {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public KeywordWriteResult writeToBlackBoard(String termHit, FsContent newFsHit, String listName) throws NoOpenCoreException {
|
public KeywordWriteResult writeToBlackBoard(String termHit, FsContent newFsHit, String snippet, String listName) {
|
||||||
final String MODULE_NAME = KeywordSearchIngestService.MODULE_NAME;
|
final String MODULE_NAME = KeywordSearchIngestService.MODULE_NAME;
|
||||||
|
|
||||||
//snippet
|
|
||||||
String snippet = null;
|
|
||||||
try {
|
|
||||||
snippet = LuceneQuery.querySnippet(KeywordSearchUtil.escapeLuceneQuery(termHit, true, false), newFsHit.getId(), true, true);
|
|
||||||
}
|
|
||||||
catch (NoOpenCoreException e) {
|
|
||||||
logger.log(Level.WARNING, "Error querying snippet: " + termHit, e);
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
logger.log(Level.WARNING, "Error querying snippet: " + termHit, e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (snippet == null || snippet.equals("")) {
|
if (snippet == null || snippet.equals("")) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -225,8 +216,8 @@ public class TermComponentQuery implements KeywordSearchQuery {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, List<FsContent>> performQuery() throws NoOpenCoreException{
|
public Map<String, List<ContentHit>> performQuery() throws NoOpenCoreException{
|
||||||
Map<String, List<FsContent>> results = new HashMap<String, List<FsContent>>();
|
Map<String, List<ContentHit>> results = new HashMap<String, List<ContentHit>>();
|
||||||
|
|
||||||
final SolrQuery q = createQuery();
|
final SolrQuery q = createQuery();
|
||||||
terms = executeQuery(q);
|
terms = executeQuery(q);
|
||||||
@ -241,12 +232,12 @@ public class TermComponentQuery implements KeywordSearchQuery {
|
|||||||
|
|
||||||
LuceneQuery filesQuery = new LuceneQuery(queryStr);
|
LuceneQuery filesQuery = new LuceneQuery(queryStr);
|
||||||
try {
|
try {
|
||||||
Map<String, List<FsContent>> subResults = filesQuery.performQuery();
|
Map<String, List<ContentHit>> subResults = filesQuery.performQuery();
|
||||||
Set<FsContent> filesResults = new HashSet<FsContent>();
|
Set<ContentHit> filesResults = new HashSet<ContentHit>();
|
||||||
for (String key : subResults.keySet()) {
|
for (String key : subResults.keySet()) {
|
||||||
filesResults.addAll(subResults.get(key));
|
filesResults.addAll(subResults.get(key));
|
||||||
}
|
}
|
||||||
results.put(term.getTerm(), new ArrayList<FsContent>(filesResults));
|
results.put(term.getTerm(), new ArrayList<ContentHit>(filesResults));
|
||||||
}
|
}
|
||||||
catch (NoOpenCoreException e) {
|
catch (NoOpenCoreException e) {
|
||||||
logger.log(Level.WARNING, "Error executing Solr query,", e);
|
logger.log(Level.WARNING, "Error executing Solr query,", e);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user