mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-08 14:19:32 +00:00
Merge branch 'keyword-search-prototype' of github.com:sleuthkit/autopsy into keyword-search-prototype
This commit is contained in:
commit
ca437d65c9
@ -57,11 +57,11 @@ class HighlightedMatchesSource implements MarkupSource {
|
|||||||
public String getMarkup() {
|
public String getMarkup() {
|
||||||
|
|
||||||
SolrQuery q = new SolrQuery();
|
SolrQuery q = new SolrQuery();
|
||||||
final String queryEscaped = KeywordSearchUtil.escapeLuceneQuery(solrQuery, true);
|
final String queryEscaped = KeywordSearchUtil.escapeLuceneQuery(solrQuery, true, false);
|
||||||
|
|
||||||
q.setQuery(queryEscaped);
|
q.setQuery(queryEscaped);
|
||||||
q.addFilterQuery("id:" + content.getId());
|
q.addFilterQuery("id:" + content.getId());
|
||||||
q.addHighlightField("content");
|
q.addHighlightField("content"); //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
|
||||||
|
@ -155,6 +155,9 @@
|
|||||||
<ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="KeywordSearchListTopComponent.loadListButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
<ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="KeywordSearchListTopComponent.loadListButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
</Property>
|
</Property>
|
||||||
</Properties>
|
</Properties>
|
||||||
|
<Events>
|
||||||
|
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="loadListButtonActionPerformed"/>
|
||||||
|
</Events>
|
||||||
</Component>
|
</Component>
|
||||||
<Component class="javax.swing.JButton" name="deleteWordButton">
|
<Component class="javax.swing.JButton" name="deleteWordButton">
|
||||||
<Properties>
|
<Properties>
|
||||||
|
@ -21,9 +21,12 @@ package org.sleuthkit.autopsy.keywordsearch;
|
|||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.LinkedHashMap;
|
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.TreeSet;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
@ -76,9 +79,6 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
|
|||||||
deleteWordButton.setToolTipText("Delete selected keyword(s) from the list");
|
deleteWordButton.setToolTipText("Delete selected keyword(s) from the list");
|
||||||
deleteAllWordsButton.setToolTipText("Delete all keywords from the list (clear it)");
|
deleteAllWordsButton.setToolTipText("Delete all keywords from the list (clear it)");
|
||||||
|
|
||||||
loadListButton.setEnabled(false);
|
|
||||||
saveListButton.setEnabled(false);
|
|
||||||
|
|
||||||
keywordTable.setAutoscrolls(true);
|
keywordTable.setAutoscrolls(true);
|
||||||
keywordTable.setTableHeader(null);
|
keywordTable.setTableHeader(null);
|
||||||
keywordTable.setShowHorizontalLines(false);
|
keywordTable.setShowHorizontalLines(false);
|
||||||
@ -174,6 +174,11 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
|
|||||||
});
|
});
|
||||||
|
|
||||||
org.openide.awt.Mnemonics.setLocalizedText(loadListButton, org.openide.util.NbBundle.getMessage(KeywordSearchListTopComponent.class, "KeywordSearchListTopComponent.loadListButton.text")); // NOI18N
|
org.openide.awt.Mnemonics.setLocalizedText(loadListButton, org.openide.util.NbBundle.getMessage(KeywordSearchListTopComponent.class, "KeywordSearchListTopComponent.loadListButton.text")); // NOI18N
|
||||||
|
loadListButton.addActionListener(new java.awt.event.ActionListener() {
|
||||||
|
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||||
|
loadListButtonActionPerformed(evt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
org.openide.awt.Mnemonics.setLocalizedText(deleteWordButton, org.openide.util.NbBundle.getMessage(KeywordSearchListTopComponent.class, "KeywordSearchListTopComponent.deleteWordButton.text")); // NOI18N
|
org.openide.awt.Mnemonics.setLocalizedText(deleteWordButton, org.openide.util.NbBundle.getMessage(KeywordSearchListTopComponent.class, "KeywordSearchListTopComponent.deleteWordButton.text")); // NOI18N
|
||||||
deleteWordButton.addActionListener(new java.awt.event.ActionListener() {
|
deleteWordButton.addActionListener(new java.awt.event.ActionListener() {
|
||||||
@ -274,7 +279,6 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
|
|||||||
}// </editor-fold>//GEN-END:initComponents
|
}// </editor-fold>//GEN-END:initComponents
|
||||||
|
|
||||||
private void searchButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchButtonActionPerformed
|
private void searchButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchButtonActionPerformed
|
||||||
// TODO add your handling code here:
|
|
||||||
}//GEN-LAST:event_searchButtonActionPerformed
|
}//GEN-LAST:event_searchButtonActionPerformed
|
||||||
|
|
||||||
private void addWordButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addWordButtonActionPerformed
|
private void addWordButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addWordButtonActionPerformed
|
||||||
@ -318,11 +322,39 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
|
|||||||
}//GEN-LAST:event_addWordButtonActionPerformed
|
}//GEN-LAST:event_addWordButtonActionPerformed
|
||||||
|
|
||||||
private void saveListButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_saveListButtonActionPerformed
|
private void saveListButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_saveListButtonActionPerformed
|
||||||
|
KeywordSearchListsXML writer = KeywordSearchListsXML.getInstance();
|
||||||
|
|
||||||
|
//TODO popup with name / check if overwrite, then save
|
||||||
|
|
||||||
|
String listName = "initial";
|
||||||
|
|
||||||
|
List<String> keywords = tableModel.getAllKeywords();
|
||||||
|
|
||||||
|
boolean shouldWrite = false;
|
||||||
|
boolean written = false;
|
||||||
|
if (writer.listExists(listName)) {
|
||||||
|
boolean replace = KeywordSearchUtil.displayConfirmDialog("Save Keyword List", "Keyword List <" + listName + "> already exists, do you want to replace it?",
|
||||||
|
KeywordSearchUtil.DIALOG_MESSAGE_TYPE.WARN);
|
||||||
|
if (replace) {
|
||||||
|
shouldWrite = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
shouldWrite = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldWrite) {
|
||||||
|
writer.addList(listName, keywords);
|
||||||
|
written = writer.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (written) {
|
||||||
|
KeywordSearchUtil.displayDialog("Save Keyword List", "Keyword List <" + listName + "> saved", KeywordSearchUtil.DIALOG_MESSAGE_TYPE.INFO);
|
||||||
|
}
|
||||||
|
|
||||||
}//GEN-LAST:event_saveListButtonActionPerformed
|
}//GEN-LAST:event_saveListButtonActionPerformed
|
||||||
|
|
||||||
private void chLiteralWordActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_chLiteralWordActionPerformed
|
private void chLiteralWordActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_chLiteralWordActionPerformed
|
||||||
|
|
||||||
}//GEN-LAST:event_chLiteralWordActionPerformed
|
}//GEN-LAST:event_chLiteralWordActionPerformed
|
||||||
|
|
||||||
private void deleteWordButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteWordButtonActionPerformed
|
private void deleteWordButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteWordButtonActionPerformed
|
||||||
@ -332,6 +364,24 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
|
|||||||
private void deleteAllWordsButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteAllWordsButtonActionPerformed
|
private void deleteAllWordsButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteAllWordsButtonActionPerformed
|
||||||
tableModel.deleteAll();
|
tableModel.deleteAll();
|
||||||
}//GEN-LAST:event_deleteAllWordsButtonActionPerformed
|
}//GEN-LAST:event_deleteAllWordsButtonActionPerformed
|
||||||
|
|
||||||
|
private void loadListButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_loadListButtonActionPerformed
|
||||||
|
KeywordSearchListsXML loader = KeywordSearchListsXML.getInstance();
|
||||||
|
|
||||||
|
//TODO popup widget with all lists in a a table, user picks name, then load into the model
|
||||||
|
String listName = "initial";
|
||||||
|
|
||||||
|
KeywordSearchList list = loader.getList(listName);
|
||||||
|
if (list != null) {
|
||||||
|
List<String> keywords = list.getKeywords();
|
||||||
|
|
||||||
|
//TODO clear/append option ?
|
||||||
|
tableModel.deleteAll();
|
||||||
|
tableModel.addKeywords(keywords);
|
||||||
|
KeywordSearchUtil.displayDialog("Save Keyword List", "Keyword List <" + listName + "> loaded", KeywordSearchUtil.DIALOG_MESSAGE_TYPE.INFO);
|
||||||
|
}
|
||||||
|
|
||||||
|
}//GEN-LAST:event_loadListButtonActionPerformed
|
||||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||||
private javax.swing.JButton addWordButton;
|
private javax.swing.JButton addWordButton;
|
||||||
private javax.swing.JTextField addWordField;
|
private javax.swing.JTextField addWordField;
|
||||||
@ -352,12 +402,10 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void componentOpened() {
|
public void componentOpened() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void componentClosed() {
|
public void componentClosed() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void writeProperties(java.util.Properties p) {
|
void writeProperties(java.util.Properties p) {
|
||||||
@ -441,7 +489,7 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
|
|||||||
|
|
||||||
private static Logger logger = Logger.getLogger(KeywordTableModel.class.getName());
|
private static Logger logger = Logger.getLogger(KeywordTableModel.class.getName());
|
||||||
//data
|
//data
|
||||||
private List<TableEntry> keywordData = new ArrayList<TableEntry>();
|
private Set<TableEntry> keywordData = new TreeSet<TableEntry>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getColumnCount() {
|
public int getColumnCount() {
|
||||||
@ -456,12 +504,18 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
|
|||||||
@Override
|
@Override
|
||||||
public Object getValueAt(int rowIndex, int columnIndex) {
|
public Object getValueAt(int rowIndex, int columnIndex) {
|
||||||
Object ret = null;
|
Object ret = null;
|
||||||
|
TableEntry entry = null;
|
||||||
|
//iterate until row
|
||||||
|
Iterator<TableEntry> it = keywordData.iterator();
|
||||||
|
for (int i = 0; i <= rowIndex; ++i) {
|
||||||
|
entry = it.next();
|
||||||
|
}
|
||||||
switch (columnIndex) {
|
switch (columnIndex) {
|
||||||
case 0:
|
case 0:
|
||||||
ret = (Object) keywordData.get(rowIndex).keyword;
|
ret = (Object) entry.keyword;
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
ret = (Object) keywordData.get(rowIndex).isActive;
|
ret = (Object) entry.isActive;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
logger.log(Level.SEVERE, "Invalid table column index: " + columnIndex);
|
logger.log(Level.SEVERE, "Invalid table column index: " + columnIndex);
|
||||||
@ -478,7 +532,13 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
|
|||||||
@Override
|
@Override
|
||||||
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
|
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
|
||||||
if (columnIndex == 1) {
|
if (columnIndex == 1) {
|
||||||
keywordData.get(rowIndex).isActive = (Boolean) aValue;
|
TableEntry entry = null;
|
||||||
|
//iterate until row
|
||||||
|
Iterator<TableEntry> it = keywordData.iterator();
|
||||||
|
for (int i = 0; i <= rowIndex; ++i) {
|
||||||
|
entry = it.next();
|
||||||
|
}
|
||||||
|
entry.isActive = (Boolean) aValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -511,13 +571,24 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
void addKeyword(String keyword) {
|
void addKeyword(String keyword) {
|
||||||
keywordData.add(0, new TableEntry(keyword));
|
if (!keywordExists(keyword)) {
|
||||||
this.fireTableRowsInserted(keywordData.size() - 1, keywordData.size());
|
keywordData.add(new TableEntry(keyword));
|
||||||
|
}
|
||||||
|
fireTableDataChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void addKeywords(List<String> keywords) {
|
||||||
|
for (String keyword : keywords) {
|
||||||
|
if (!keywordExists(keyword)) {
|
||||||
|
keywordData.add(new TableEntry(keyword));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fireTableDataChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void deleteAll() {
|
void deleteAll() {
|
||||||
keywordData.clear();
|
keywordData.clear();
|
||||||
initEmpty();
|
fireTableDataChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void deleteSelected() {
|
void deleteSelected() {
|
||||||
@ -535,14 +606,7 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void initEmpty() {
|
class TableEntry implements Comparable {
|
||||||
for (int i = 0; i < 10; ++i) {
|
|
||||||
keywordData.add(0, new TableEntry("", false));
|
|
||||||
}
|
|
||||||
fireTableDataChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
class TableEntry {
|
|
||||||
|
|
||||||
String keyword;
|
String keyword;
|
||||||
Boolean isActive;
|
Boolean isActive;
|
||||||
@ -556,6 +620,11 @@ public final class KeywordSearchListTopComponent extends TopComponent implements
|
|||||||
this.keyword = keyword;
|
this.keyword = keyword;
|
||||||
this.isActive = false;
|
this.isActive = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(Object o) {
|
||||||
|
return this.keyword.compareTo(((TableEntry) o).keyword);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,334 @@
|
|||||||
|
/*
|
||||||
|
* 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.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.text.DateFormat;
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
import javax.xml.parsers.DocumentBuilder;
|
||||||
|
import javax.xml.parsers.DocumentBuilderFactory;
|
||||||
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
|
import javax.xml.transform.OutputKeys;
|
||||||
|
import javax.xml.transform.Result;
|
||||||
|
import javax.xml.transform.Transformer;
|
||||||
|
import javax.xml.transform.TransformerConfigurationException;
|
||||||
|
import javax.xml.transform.TransformerException;
|
||||||
|
import javax.xml.transform.TransformerFactory;
|
||||||
|
import javax.xml.transform.dom.DOMSource;
|
||||||
|
import javax.xml.transform.stream.StreamResult;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.AutopsyPropFile;
|
||||||
|
import org.w3c.dom.Document;
|
||||||
|
import org.w3c.dom.Element;
|
||||||
|
import org.w3c.dom.NodeList;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages reading and writing of keyword lists to user settings XML file keywords.xml
|
||||||
|
*/
|
||||||
|
public class KeywordSearchListsXML {
|
||||||
|
|
||||||
|
private static final String ROOT_EL = "keyword_lists";
|
||||||
|
private static final String LIST_EL = "keyword_list";
|
||||||
|
private static final String LIST_NAME_ATTR = "name";
|
||||||
|
private static final String LIST_CREATE_ATTR = "created";
|
||||||
|
private static final String LIST_MOD_ATTR = "modified";
|
||||||
|
private static final String KEYWORD_EL = "keyword";
|
||||||
|
private static final String LISTS_FILE_NAME = "keywords.xml";
|
||||||
|
private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
|
||||||
|
private static final String ENCODING = "UTF-8";
|
||||||
|
private String LISTS_FILE = AutopsyPropFile.getUserDirPath() + File.separator + LISTS_FILE_NAME;
|
||||||
|
private static final Logger logger = Logger.getLogger(KeywordSearchListsXML.class.getName());
|
||||||
|
|
||||||
|
Map<String, KeywordSearchList> theLists; //the keyword data
|
||||||
|
|
||||||
|
static KeywordSearchListsXML theInstance = null;
|
||||||
|
|
||||||
|
private KeywordSearchListsXML() {
|
||||||
|
}
|
||||||
|
|
||||||
|
static KeywordSearchListsXML getInstance() {
|
||||||
|
if (theInstance == null) {
|
||||||
|
theInstance = new KeywordSearchListsXML();
|
||||||
|
theInstance.reload();
|
||||||
|
}
|
||||||
|
return theInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* load the file or create new
|
||||||
|
*/
|
||||||
|
public void reload() {
|
||||||
|
boolean created = false;
|
||||||
|
theLists = new LinkedHashMap<String, KeywordSearchList>();
|
||||||
|
if (!this.listFileExists()) {
|
||||||
|
//create new if it doesn't exist
|
||||||
|
save();
|
||||||
|
created = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!load() && !created) {
|
||||||
|
//create new if failed to load
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get all loaded keyword lists
|
||||||
|
* @return List of keyword list objects
|
||||||
|
*/
|
||||||
|
Map<String, KeywordSearchList> getLists() {
|
||||||
|
return theLists;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get list by name or null
|
||||||
|
* @param name id of the list
|
||||||
|
* @return keyword list representation
|
||||||
|
*/
|
||||||
|
KeywordSearchList getList(String name) {
|
||||||
|
return theLists.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* check if list with given name id exists
|
||||||
|
* @param name id to check
|
||||||
|
* @return true if list already exists or false otherwise
|
||||||
|
*/
|
||||||
|
boolean listExists(String name) {
|
||||||
|
return getList(name) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* adds the new word list using name id
|
||||||
|
* replacing old one if exists with the same name
|
||||||
|
* requires following call to save() to make permanent changes
|
||||||
|
* @param name the name of the new list or list to replace
|
||||||
|
* @param newList list of keywords
|
||||||
|
* @return true if old list was replaced
|
||||||
|
*/
|
||||||
|
boolean addList(String name, List<String> newList) {
|
||||||
|
boolean replaced = false;
|
||||||
|
KeywordSearchList curList = getList(name);
|
||||||
|
final Date now = new Date();
|
||||||
|
if (curList == null) {
|
||||||
|
theLists.put(name, new KeywordSearchList(name, now, now, newList));
|
||||||
|
} else {
|
||||||
|
theLists.put(name, new KeywordSearchList(name, curList.getDateCreated(), now, newList));
|
||||||
|
replaced = true;
|
||||||
|
}
|
||||||
|
return replaced;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* writes out current list replacing the last lists file
|
||||||
|
*/
|
||||||
|
boolean save() {
|
||||||
|
boolean success = false;
|
||||||
|
DateFormat dateFormatter = new SimpleDateFormat(DATE_FORMAT);
|
||||||
|
DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance();
|
||||||
|
|
||||||
|
try {
|
||||||
|
DocumentBuilder docBuilder = dbfac.newDocumentBuilder();
|
||||||
|
Document doc = docBuilder.newDocument();
|
||||||
|
|
||||||
|
Element rootEl = doc.createElement(ROOT_EL);
|
||||||
|
doc.appendChild(rootEl);
|
||||||
|
|
||||||
|
for (String listName : theLists.keySet()) {
|
||||||
|
KeywordSearchList list = theLists.get(listName);
|
||||||
|
String created = dateFormatter.format(list.getDateCreated());
|
||||||
|
String modified = dateFormatter.format(list.getDateModified());
|
||||||
|
List<String> keywords = list.getKeywords();
|
||||||
|
|
||||||
|
Element listEl = doc.createElement(LIST_EL);
|
||||||
|
listEl.setAttribute(LIST_NAME_ATTR, listName);
|
||||||
|
listEl.setAttribute(LIST_CREATE_ATTR, created);
|
||||||
|
listEl.setAttribute(LIST_MOD_ATTR, modified);
|
||||||
|
|
||||||
|
for (String keyword : keywords) {
|
||||||
|
Element keywordEl = doc.createElement(KEYWORD_EL);
|
||||||
|
keywordEl.setTextContent(keyword);
|
||||||
|
listEl.appendChild(keywordEl);
|
||||||
|
}
|
||||||
|
rootEl.appendChild(listEl);
|
||||||
|
}
|
||||||
|
|
||||||
|
success = saveDoc(doc);
|
||||||
|
} catch (ParserConfigurationException e) {
|
||||||
|
logger.log(Level.SEVERE, "Error saving keyword list: can't initialize parser.", e);
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* load and parse XML, then dispose
|
||||||
|
*/
|
||||||
|
private boolean load() {
|
||||||
|
final Document doc = loadDoc();
|
||||||
|
if (doc == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
DateFormat dateFormatter = new SimpleDateFormat(DATE_FORMAT);
|
||||||
|
|
||||||
|
|
||||||
|
Element root = doc.getDocumentElement();
|
||||||
|
if (root == null) {
|
||||||
|
logger.log(Level.SEVERE, "Error loading keyword list: invalid file format.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
NodeList listsNList = root.getElementsByTagName(LIST_EL);
|
||||||
|
int numLists = listsNList.getLength();
|
||||||
|
for (int i = 0; i < numLists; ++i) {
|
||||||
|
Element listEl = (Element) listsNList.item(i);
|
||||||
|
final String name = listEl.getAttribute(LIST_NAME_ATTR);
|
||||||
|
final String created = listEl.getAttribute(LIST_CREATE_ATTR);
|
||||||
|
final String modified = listEl.getAttribute(LIST_MOD_ATTR);
|
||||||
|
Date createdDate = dateFormatter.parse(created);
|
||||||
|
Date modDate = dateFormatter.parse(modified);
|
||||||
|
List<String> words = new ArrayList<String>();
|
||||||
|
KeywordSearchList list = new KeywordSearchList(name, createdDate, modDate, words);
|
||||||
|
|
||||||
|
//parse all words
|
||||||
|
NodeList wordsNList = listEl.getElementsByTagName(KEYWORD_EL);
|
||||||
|
final int numKeywords = wordsNList.getLength();
|
||||||
|
for (int j = 0; j < numKeywords; ++j) {
|
||||||
|
Element wordEl = (Element) wordsNList.item(j);
|
||||||
|
words.add(wordEl.getTextContent());
|
||||||
|
|
||||||
|
}
|
||||||
|
theLists.put(name, list);
|
||||||
|
}
|
||||||
|
} catch (ParseException e) {
|
||||||
|
//error parsing dates
|
||||||
|
logger.log(Level.SEVERE, "Error loading keyword list: can't parse dates.", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean listFileExists() {
|
||||||
|
File f = new File(LISTS_FILE);
|
||||||
|
return f.exists() && f.canRead() && f.canWrite();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Document loadDoc() {
|
||||||
|
DocumentBuilderFactory builderFactory =
|
||||||
|
DocumentBuilderFactory.newInstance();
|
||||||
|
|
||||||
|
Document ret = null;
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
DocumentBuilder builder = builderFactory.newDocumentBuilder();
|
||||||
|
ret = builder.parse(
|
||||||
|
new FileInputStream(LISTS_FILE));
|
||||||
|
} catch (ParserConfigurationException e) {
|
||||||
|
logger.log(Level.SEVERE, "Error loading keyword list: can't initialize parser.", e);
|
||||||
|
|
||||||
|
} catch (SAXException e) {
|
||||||
|
logger.log(Level.SEVERE, "Error loading keyword list: can't parse XML.", e);
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
//error reading file
|
||||||
|
logger.log(Level.SEVERE, "Error loading keyword list: can't read file.", e);
|
||||||
|
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean saveDoc(final Document doc) {
|
||||||
|
TransformerFactory xf = TransformerFactory.newInstance();
|
||||||
|
xf.setAttribute("indent-number", new Integer(1));
|
||||||
|
boolean success = false;
|
||||||
|
try {
|
||||||
|
Transformer xformer = xf.newTransformer();
|
||||||
|
xformer.setOutputProperty(OutputKeys.METHOD, "xml");
|
||||||
|
xformer.setOutputProperty(OutputKeys.INDENT, "yes");
|
||||||
|
xformer.setOutputProperty(OutputKeys.ENCODING, ENCODING);
|
||||||
|
xformer.setOutputProperty(OutputKeys.STANDALONE, "yes");
|
||||||
|
xformer.setOutputProperty(OutputKeys.VERSION, "1.0");
|
||||||
|
Result out = new StreamResult(new OutputStreamWriter(new FileOutputStream(new File(LISTS_FILE)), ENCODING));
|
||||||
|
xformer.transform(new DOMSource(doc), out);
|
||||||
|
success = true;
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
logger.log(Level.SEVERE, "Should not happen", e);
|
||||||
|
} catch (TransformerConfigurationException e) {
|
||||||
|
logger.log(Level.SEVERE, "Error writing keyword lists XML", e);
|
||||||
|
} catch (TransformerException e) {
|
||||||
|
logger.log(Level.SEVERE, "Error writing keyword lists XML", e);
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
logger.log(Level.SEVERE, "Error writing keyword lists XML: cannot write to file: " + LISTS_FILE, e);
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* a representation of a single keyword list
|
||||||
|
* created or loaded
|
||||||
|
*/
|
||||||
|
class KeywordSearchList {
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
private Date created;
|
||||||
|
private Date modified;
|
||||||
|
private List<String> keywords;
|
||||||
|
|
||||||
|
KeywordSearchList(String name, Date created, Date modified, List<String> keywords) {
|
||||||
|
this.name = name;
|
||||||
|
this.created = created;
|
||||||
|
this.modified = modified;
|
||||||
|
this.keywords = keywords;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
Date getDateCreated() {
|
||||||
|
return created;
|
||||||
|
}
|
||||||
|
|
||||||
|
Date getDateModified() {
|
||||||
|
return modified;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> getKeywords() {
|
||||||
|
return keywords;
|
||||||
|
}
|
||||||
|
}
|
@ -70,8 +70,7 @@ public class KeywordSearchResultFactory extends ChildFactory<KeyValueThing> {
|
|||||||
public String toString() {
|
public String toString() {
|
||||||
return "Match";
|
return "Match";
|
||||||
}
|
}
|
||||||
},
|
},}
|
||||||
}
|
|
||||||
private Presentation presentation;
|
private Presentation presentation;
|
||||||
private Collection<String> queries;
|
private Collection<String> queries;
|
||||||
private Collection<KeyValueThing> things;
|
private Collection<KeyValueThing> things;
|
||||||
@ -149,6 +148,7 @@ public class KeywordSearchResultFactory extends ChildFactory<KeyValueThing> {
|
|||||||
childFactory = new ResultCollapsedChildFactory(thing);
|
childFactory = new ResultCollapsedChildFactory(thing);
|
||||||
final Node ret = new KeyValueNode(thing, Children.create(childFactory, true));
|
final Node ret = new KeyValueNode(thing, Children.create(childFactory, true));
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
//DataResultViewerTable view = Utilities.actionsGlobalContext().lookup(DataResultViewerTable.class);
|
//DataResultViewerTable view = Utilities.actionsGlobalContext().lookup(DataResultViewerTable.class);
|
||||||
@ -199,11 +199,11 @@ public class KeywordSearchResultFactory extends ChildFactory<KeyValueThing> {
|
|||||||
final int lastTerm = terms.size() - 1;
|
final int lastTerm = terms.size() - 1;
|
||||||
int curTerm = 0;
|
int curTerm = 0;
|
||||||
for (Term term : terms) {
|
for (Term term : terms) {
|
||||||
final String termS = KeywordSearchUtil.escapeLuceneQuery(term.getTerm(), true);
|
final String termS = KeywordSearchUtil.escapeLuceneQuery(term.getTerm(), true, false);
|
||||||
if (!termS.contains("*")) {
|
if (!termS.contains("*")) {
|
||||||
highlightQuery.append(termS);
|
highlightQuery.append(termS);
|
||||||
if (lastTerm != curTerm) {
|
if (lastTerm != curTerm) {
|
||||||
highlightQuery.append(" ");
|
highlightQuery.append(" "); //acts as OR ||
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -304,25 +304,27 @@ public class KeywordSearchResultFactory extends ChildFactory<KeyValueThing> {
|
|||||||
|
|
||||||
final String contentStr = KeywordSearch.getServer().getCore().getSolrContent(content);
|
final String contentStr = KeywordSearch.getServer().getCore().getSolrContent(content);
|
||||||
|
|
||||||
//make sure the file contains a match (this gets rid of large number of false positives)
|
//postprocess
|
||||||
//TODO option in GUI to include approximate matches (faster)
|
//make sure Solr result contains a match (this gets rid of large number of false positives)
|
||||||
boolean matchFound = false;
|
boolean postprocess = true;
|
||||||
|
boolean matchFound = true;
|
||||||
|
if (postprocess) {
|
||||||
if (contentStr != null) {//if not null, some error getting from Solr, handle it by not filtering out
|
if (contentStr != null) {//if not null, some error getting from Solr, handle it by not filtering out
|
||||||
//perform java regex to validate match from Solr
|
//perform java regex to validate match from Solr
|
||||||
String origQuery = thingContent.getQuery();
|
String origQuery = thingContent.getQuery();
|
||||||
|
|
||||||
//escape the regex query because it may contain special characters from the previous match
|
//since query is a match result, we can assume literal pattern
|
||||||
//since it's a match result, we can assume literal pattern
|
|
||||||
origQuery = Pattern.quote(origQuery);
|
origQuery = Pattern.quote(origQuery);
|
||||||
Pattern p = Pattern.compile(origQuery, Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
|
Pattern p = Pattern.compile(origQuery, Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
|
||||||
|
|
||||||
Matcher m = p.matcher(contentStr);
|
Matcher m = p.matcher(contentStr);
|
||||||
matchFound = m.find();
|
matchFound = m.find();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (matchFound) {
|
if (matchFound) {
|
||||||
Node kvNode = new KeyValueNode(thingContent, Children.LEAF);
|
Node kvNode = new KeyValueNode(thingContent, Children.LEAF);
|
||||||
//wrap in KeywordSearchFilterNode for the markup content, might need to override FilterNode for more customization
|
//wrap in KeywordSearchFilterNode for the markup content
|
||||||
HighlightedMatchesSource highlights = new HighlightedMatchesSource(content, query);
|
HighlightedMatchesSource highlights = new HighlightedMatchesSource(content, query);
|
||||||
return new KeywordSearchFilterNode(highlights, kvNode, query);
|
return new KeywordSearchFilterNode(highlights, kvNode, query);
|
||||||
} else {
|
} else {
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.sleuthkit.autopsy.keywordsearch;
|
package org.sleuthkit.autopsy.keywordsearch;
|
||||||
|
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
@ -31,8 +30,10 @@ import org.sleuthkit.datamodel.TskException;
|
|||||||
|
|
||||||
public class KeywordSearchUtil {
|
public class KeywordSearchUtil {
|
||||||
|
|
||||||
public enum DIALOG_MESSAGE_TYPE {ERROR, WARN, INFO};
|
public enum DIALOG_MESSAGE_TYPE {
|
||||||
|
|
||||||
|
ERROR, WARN, INFO
|
||||||
|
};
|
||||||
private static final Logger logger = Logger.getLogger(KeywordSearchUtil.class.getName());
|
private static final Logger logger = Logger.getLogger(KeywordSearchUtil.class.getName());
|
||||||
|
|
||||||
public static String buildDirName(FsContent f) {
|
public static String buildDirName(FsContent f) {
|
||||||
@ -65,7 +66,7 @@ public class KeywordSearchUtil {
|
|||||||
* such as /+-&|!(){}[]^"~*?:\ and treat the whole query as literal word
|
* such as /+-&|!(){}[]^"~*?:\ and treat the whole query as literal word
|
||||||
* @return encoded query
|
* @return encoded query
|
||||||
*/
|
*/
|
||||||
public static String escapeLuceneQuery(String query, boolean escapeLuceneChars) {
|
public static String escapeLuceneQuery(String query, boolean escapeLuceneChars, boolean encode) {
|
||||||
String queryEscaped = null;
|
String queryEscaped = null;
|
||||||
String inputString = query;
|
String inputString = query;
|
||||||
|
|
||||||
@ -79,27 +80,29 @@ public class KeywordSearchUtil {
|
|||||||
}
|
}
|
||||||
sb.append(c);
|
sb.append(c);
|
||||||
}
|
}
|
||||||
inputString = sb.toString();
|
queryEscaped = inputString = sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (encode) {
|
||||||
try {
|
try {
|
||||||
queryEscaped = URLEncoder.encode(inputString, "UTF-8");
|
queryEscaped = URLEncoder.encode(inputString, "UTF-8");
|
||||||
}
|
} catch (UnsupportedEncodingException ex) {
|
||||||
catch (UnsupportedEncodingException ex) {
|
|
||||||
logger.log(Level.SEVERE, "Error escaping URL query, should not happen.", ex);
|
logger.log(Level.SEVERE, "Error escaping URL query, should not happen.", ex);
|
||||||
queryEscaped = query;
|
queryEscaped = query;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return queryEscaped;
|
return queryEscaped;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void displayDialog(final String title, final String message, final DIALOG_MESSAGE_TYPE type) {
|
public static void displayDialog(final String title, final String message, final DIALOG_MESSAGE_TYPE type) {
|
||||||
int messageType;
|
int messageType;
|
||||||
if (type == DIALOG_MESSAGE_TYPE.ERROR)
|
if (type == DIALOG_MESSAGE_TYPE.ERROR) {
|
||||||
messageType = JOptionPane.ERROR_MESSAGE;
|
messageType = JOptionPane.ERROR_MESSAGE;
|
||||||
else if (type == DIALOG_MESSAGE_TYPE.WARN)
|
} else if (type == DIALOG_MESSAGE_TYPE.WARN) {
|
||||||
messageType = JOptionPane.WARNING_MESSAGE;
|
messageType = JOptionPane.WARNING_MESSAGE;
|
||||||
else messageType = JOptionPane.INFORMATION_MESSAGE;
|
} else {
|
||||||
|
messageType = JOptionPane.INFORMATION_MESSAGE;
|
||||||
|
}
|
||||||
|
|
||||||
final Component parentComponent = null; // Use default window frame.
|
final Component parentComponent = null; // Use default window frame.
|
||||||
JOptionPane.showMessageDialog(
|
JOptionPane.showMessageDialog(
|
||||||
@ -108,4 +111,20 @@ public class KeywordSearchUtil {
|
|||||||
title,
|
title,
|
||||||
messageType);
|
messageType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean displayConfirmDialog(final String title, final String message, final DIALOG_MESSAGE_TYPE type) {
|
||||||
|
int messageType;
|
||||||
|
if (type == DIALOG_MESSAGE_TYPE.ERROR) {
|
||||||
|
messageType = JOptionPane.ERROR_MESSAGE;
|
||||||
|
} else if (type == DIALOG_MESSAGE_TYPE.WARN) {
|
||||||
|
messageType = JOptionPane.WARNING_MESSAGE;
|
||||||
|
} else {
|
||||||
|
messageType = JOptionPane.INFORMATION_MESSAGE;
|
||||||
|
}
|
||||||
|
if (JOptionPane.showConfirmDialog(null, message, title, JOptionPane.YES_NO_OPTION, messageType) == JOptionPane.YES_OPTION) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ public class LuceneQuery implements KeywordSearchQuery {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void escape() {
|
public void escape() {
|
||||||
queryEscaped = KeywordSearchUtil.escapeLuceneQuery(query, true);
|
queryEscaped = KeywordSearchUtil.escapeLuceneQuery(query, true, true);
|
||||||
isEscaped = true;
|
isEscaped = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,11 +168,12 @@ public class TermComponentQuery implements KeywordSearchQuery {
|
|||||||
final int lastTerm = terms.size() - 1;
|
final int lastTerm = terms.size() - 1;
|
||||||
int curTerm = 0;
|
int curTerm = 0;
|
||||||
for (Term term : terms) {
|
for (Term term : terms) {
|
||||||
final String termS = term.getTerm();
|
final String termS = KeywordSearchUtil.escapeLuceneQuery(term.getTerm(), true, false);
|
||||||
|
//final String termS = term.getTerm();
|
||||||
if (!termS.contains("*")) {
|
if (!termS.contains("*")) {
|
||||||
filesQueryB.append(termS);
|
filesQueryB.append(TERMS_SEARCH_FIELD).append(":").append(termS);
|
||||||
if (curTerm != lastTerm) {
|
if (curTerm != lastTerm) {
|
||||||
filesQueryB.append(" ");
|
filesQueryB.append(" "); //acts as OR ||
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
++curTerm;
|
++curTerm;
|
||||||
@ -181,7 +182,7 @@ public class TermComponentQuery implements KeywordSearchQuery {
|
|||||||
|
|
||||||
if (!terms.isEmpty()) {
|
if (!terms.isEmpty()) {
|
||||||
LuceneQuery filesQuery = new LuceneQuery(filesQueryB.toString());
|
LuceneQuery filesQuery = new LuceneQuery(filesQueryB.toString());
|
||||||
filesQuery.escape();
|
//filesQuery.escape();
|
||||||
try {
|
try {
|
||||||
uniqueMatches = filesQuery.performQuery();
|
uniqueMatches = filesQuery.performQuery();
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
@ -190,8 +191,10 @@ public class TermComponentQuery implements KeywordSearchQuery {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//result postprocessing
|
||||||
//filter out non-matching files using the original query (whether literal or not)
|
//filter out non-matching files using the original query (whether literal or not)
|
||||||
//TODO this could be costly, for now just testing how it performs
|
boolean postprocess = false;
|
||||||
|
if (postprocess) {
|
||||||
for (FsContent f : uniqueMatches) {
|
for (FsContent f : uniqueMatches) {
|
||||||
Pattern p = Pattern.compile(queryEscaped, Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
|
Pattern p = Pattern.compile(queryEscaped, Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
|
||||||
final String contentStr = KeywordSearch.getServer().getCore().getSolrContent(f);
|
final String contentStr = KeywordSearch.getServer().getCore().getSolrContent(f);
|
||||||
@ -200,6 +203,9 @@ public class TermComponentQuery implements KeywordSearchQuery {
|
|||||||
results.add(f);
|
results.add(f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
results.addAll(uniqueMatches);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user