Merge remote-tracking branch 'upstream/develop' into timeline_and_image_analyzer

This commit is contained in:
jmillman
2014-09-04 10:49:42 -04:00
26 changed files with 4704 additions and 14 deletions
@@ -59,6 +59,7 @@ import org.sleuthkit.datamodel.SleuthkitJNI.CaseDbHandle.AddImageProcess;
* open at a time. Use getCurrentCase() to retrieve the object for the current
* case.
*/
@SuppressWarnings("deprecation") // TODO: Remove this when ErrorObserver is replaced.
public class Case implements SleuthkitCase.ErrorObserver {
private static final String autopsyVer = Version.getVersion(); // current version of autopsy. Change it when the version is changed
@@ -44,13 +44,13 @@ import org.sleuthkit.datamodel.TskException;
*/
public class BlackboardArtifactNode extends DisplayableItemNode {
private BlackboardArtifact artifact;
private Content associated;
private final BlackboardArtifact artifact;
private final Content associated;
private List<NodeProperty<? extends Object>> customProperties;
static final Logger logger = Logger.getLogger(BlackboardArtifactNode.class.getName());
/**
* Artifact types which should have the associated content's full unique path
* as a property.
/*
* Artifact types which should have the full unique path of the associated
* content as a property.
*/
private static final Integer[] SHOW_UNIQUE_PATH = new Integer[] {
BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID(),
@@ -58,6 +58,13 @@ public class BlackboardArtifactNode extends DisplayableItemNode {
BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID(),
};
// TODO (RC): This is an unattractive alternative to subclassing BlackboardArtifactNode,
// cut from the same cloth as the equally unattractive SHOW_UNIQUE_PATH array
// above. It should be removed when and if the subclassing is implemented.
private static final Integer[] SHOW_FILE_METADATA = new Integer[] {
BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID(),
};
/**
* Construct blackboard artifact node from an artifact and using provided
* icon
@@ -179,6 +186,30 @@ public class BlackboardArtifactNode extends DisplayableItemNode {
NO_DESCR,
sourcePath));
}
if (Arrays.asList(SHOW_FILE_METADATA).contains(artifactTypeId)) {
AbstractFile file = associated instanceof AbstractFile ? (AbstractFile)associated : null;
ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileModifiedTime.name"),
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileModifiedTime.displayName"),
"",
file != null ? ContentUtils.getStringTime(file.getMtime(), file) : ""));
ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileChangedTime.name"),
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileChangedTime.displayName"),
"",
file != null ? ContentUtils.getStringTime(file.getCtime(), file) : ""));
ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileAccessedTime.name"),
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileAccessedTime.displayName"),
"",
file != null ? ContentUtils.getStringTime(file.getAtime(), file) : ""));
ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileCreatedTime.name"),
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileCreatedTime.displayName"),
"",
file != null ? ContentUtils.getStringTime(file.getCrtime(), file) : ""));
ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileSize.name"),
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.fileSize.displayName"),
"",
associated.getSize()));
}
} else {
String dataSourceStr = "";
try {
@@ -225,7 +256,7 @@ public class BlackboardArtifactNode extends DisplayableItemNode {
* @param np NodeProperty to add
*/
public <T> void addNodeProperty(NodeProperty<T> np) {
if (customProperties == null) {
if (null == customProperties) {
//lazy create the list
customProperties = new ArrayList<>();
}
@@ -240,6 +271,7 @@ public class BlackboardArtifactNode extends DisplayableItemNode {
* put
* @param artifact to extract properties from
*/
@SuppressWarnings("deprecation") // TODO: Remove this when TSK_TAGGED_ARTIFACT rows are removed in a database upgrade.
private void fillPropertyMap(Map<String, Object> map, BlackboardArtifact artifact) {
try {
for (BlackboardAttribute attribute : artifact.getAttributes()) {
@@ -247,8 +279,8 @@ public class BlackboardArtifactNode extends DisplayableItemNode {
//skip some internal attributes that user shouldn't see
if (attributeTypeID == ATTRIBUTE_TYPE.TSK_PATH_ID.getTypeID()
|| attributeTypeID == ATTRIBUTE_TYPE.TSK_TAGGED_ARTIFACT.getTypeID()
|| attributeTypeID == ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT.getTypeID()) {
continue;
|| attributeTypeID == ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT.getTypeID()
|| attributeTypeID == ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()) {
} else {
switch (attribute.getValueType()) {
case STRING:
@@ -337,7 +369,6 @@ public class BlackboardArtifactNode extends DisplayableItemNode {
List<BlackboardAttribute> attributes = artifact.getAttributes();
String keyword = null;
String regexp = null;
String origQuery = null;
for (BlackboardAttribute att : attributes) {
final int attributeTypeID = att.getAttributeTypeID();
if (attributeTypeID == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID()) {
@@ -348,6 +379,7 @@ public class BlackboardArtifactNode extends DisplayableItemNode {
}
if (keyword != null) {
boolean isRegexp = (regexp != null && !regexp.equals(""));
String origQuery;
if (isRegexp) {
origQuery = regexp;
} else {
@@ -410,9 +442,9 @@ public class BlackboardArtifactNode extends DisplayableItemNode {
return "encrypted-file.png"; //NON-NLS
case TSK_EXT_MISMATCH_DETECTED:
return "mismatch-16.png"; //NON-NLS
default:
return "artifact-icon.png"; //NON-NLS
}
return "artifact-icon.png"; //NON-NLS
}
@Override
@@ -108,7 +108,7 @@ public class InterestingHits implements AutopsyVisitableItem {
String value = rs.getString("value_text"); //NON-NLS
long artifactId = rs.getLong("artifact_id"); //NON-NLS
if (!interestingItemsMap.containsKey(value)) {
interestingItemsMap.put(value, new HashSet<Long>());
interestingItemsMap.put(value, new HashSet<>());
}
interestingItemsMap.get(value).add(artifactId);
}
@@ -238,7 +238,7 @@ public class InterestingHits implements AutopsyVisitableItem {
}
public class SetNameNode extends DisplayableItemNode implements Observer {
private String setName;
private final String setName;
public SetNameNode(String setName) {//, Set<Long> children) {
super(Children.create(new HitFactory(setName), true), Lookups.singleton(setName));
this.setName = setName;
@@ -286,7 +286,7 @@ public class InterestingHits implements AutopsyVisitableItem {
}
private class HitFactory extends ChildFactory<Long> implements Observer {
private String setName;
private final String setName;
private HitFactory(String setName) {
super();
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 686 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

@@ -0,0 +1,36 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014 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.ingest;
import org.sleuthkit.datamodel.Content;
/**
* An adapter that provides a no-op implementation of the startUp() method for
* data source ingest modules.
*/
public abstract class DataSourceIngestModuleAdapter implements DataSourceIngestModule {
@Override
public void startUp(IngestJobContext context) throws IngestModuleException {
}
@Override
public abstract ProcessResult process(Content dataSource, DataSourceIngestModuleProgress progressBar);
}
@@ -0,0 +1,39 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014 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.ingest;
import org.sleuthkit.datamodel.AbstractFile;
/**
* An adapter that provides no-op implementations of the startUp() and
* shutDown() methods for file ingest modules.
*/
public abstract class FileIngestModuleAdapter implements FileIngestModule {
@Override
public void startUp(IngestJobContext context) throws IngestModuleException {
}
@Override
public abstract ProcessResult process(AbstractFile file);
@Override
public void shutDown() {
}
}
@@ -36,6 +36,7 @@ import org.sleuthkit.autopsy.modules.exif.ExifParserModuleFactory;
import org.sleuthkit.autopsy.modules.fileextmismatch.FileExtMismatchDetectorModuleFactory;
import org.sleuthkit.autopsy.modules.filetypeid.FileTypeIdModuleFactory;
import org.sleuthkit.autopsy.modules.hashdatabase.HashLookupModuleFactory;
import org.sleuthkit.autopsy.modules.interestingitems.InterestingItemsIngestModuleFactory;
import org.sleuthkit.autopsy.modules.sevenzip.ArchiveFileExtractorModuleFactory;
import org.sleuthkit.autopsy.python.JythonModuleLoader;
@@ -58,6 +59,7 @@ final class IngestModuleFactoryLoader {
add(FileExtMismatchDetectorModuleFactory.class.getCanonicalName());
add(E01VerifierModuleFactory.class.getCanonicalName());
add(AndroidModuleFactory.class.getCanonicalName());
add(InterestingItemsIngestModuleFactory.class.getCanonicalName());
}
};
@@ -0,0 +1,64 @@
OpenIDE-Module-Display-Category=Ingest Module
OpenIDE-Module-Long-Description=Interesting Files Identifier ingest module. \n\n Identifies interesting files as defined by interesting files rule sets.
OpenIDE-Module-Short-Description=Interesting Files Identifier ingest module.
OpenIDE-Module-Name=Interesting Files Identifier
InterestingItemsIdentifierIngestModule.moduleName=Interesting Files Identifier
InterestingItemsIdentifierIngestModule.moduleDescription=Identifies interesting items as defined by interesting item rule sets.
InterestingFilesIdentifierJobSettingsPanel.filesSetTable.columnModel.title0=Title 1
InterestingFilesIdentifierJobSettingsPanel.filesSetTable.columnModel.title3=Title 4
InterestingFilesIdentifierJobSettingsPanel.filesSetTable.columnModel.title2=Title 3
InterestingFilesIdentifierJobSettingsPanel.filesSetTable.columnModel.title1=Title 2
InterestingItemDefsPanel.newSetButton.text=New Set...
InterestingItemDefsPanel.editSetButton.text=Edit Set...
InterestingItemDefsPanel.deleteSetButton.text=Delete Set
InterestingItemDefsPanel.newRuleButton.text=New Rule...
InterestingItemDefsPanel.editRuleButton.text=Edit Rule...
InterestingItemDefsPanel.deleteRuleButton.text=Delete Rule
InterestingItemDefsPanel.ruleNameTextField.text=
InterestingItemDefsPanel.setsListLabel.text=Interesting Files Set Definitions:
InterestingItemDefsPanel.selectedSetLabel.text=Selected Set:
InterestingItemDefsPanel.rulesListLabel.text=Rules:
InterestingItemDefsPanel.selectedRuleLabel.text=Selected Rule:
InterestingItemDefsPanel.setDescPanel.border.title=Description
FilesSetPanel.title=Interesting Files Set
FilesSetPanel.messages.filesSetsMustBeNamed=Interesting files sets must be named.
FilesSetPanel.ignoreKnownFilesCheckbox.text=Ignore Known Files
FilesSetPanel.descriptionPanel.border.title=Description
FilesSetPanel.nameLabel.text=Set Name:
FilesSetPanel.descPanel.border.title=Description
FilesSetRulePanel.title=Interesting Files Set Rule
FilesSetRulePanel.extensionRadioButton.text=Extension Only
FilesSetRulePanel.pathPanel.border.title=With parent path:
FilesSetRulePanel.pathRegexCheckBox.text=Regex
FilesSetRulePanel.pathTextField.text=
FilesSetRulePanel.fullNameRadioButton.text=Full Name
FilesSetRulePanel.nameRegexCheckbox.text=Regex
FilesSetRulePanel.namePanel.AccessibleContext.accessibleName=Name
FilesSetRulePanel.namePanel.border.title=Named:
FilesSetRulePanel.ruleNameTextField.text=
FilesSetRulePanel.nameTextField.text=
FilesSetRulePanel.ruleNameLabel.text=Rule Name:
FilesSetRulePanel.typePanel.border.title=Search for:
FilesSetRulePanel.messages.filesSetRulesMustBeNamed=Interesting files set rules must be named.
FilesSetRulePanel.messages.emptyNameFilter=Interesting files sets rules must have a name for which to search.
FilesSetRulePanel.messages.invalidNameRegex=The name regular expression is not valid:\n\n{0}
FilesSetRulePanel.messages.invalidCharInName=The name cannot contain \\, /, :, *, ?, \", <, or > unless it is a regular expression.
FilesSetRulePanel.messages.invalidCharInPath=The path cannot contain \\, :, *, ?, \", <, or > unless it is a regular expression.
FilesSetRulePanel.messages.invalidPathRegex=The path regular expression is not valid:\n\n{0}
FilesSetRulePanel.dirsRadioButton.text=Directories
FilesSetRulePanel.filesRadioButton.text=Files
InterestingItemDefsPanel.typePanel.border.title=Search for:
InterestingItemDefsPanel.bothRadioButton.text=Files and Directories
InterestingItemDefsPanel.dirsRadioButton.text=Directories
InterestingItemDefsPanel.filesRadioButton.text=Files
InterestingItemDefsPanel.fileNamePanel.border.title=Name
InterestingItemDefsPanel.fileNameRegexCheckbox.text=Regex
InterestingItemDefsPanel.fileNameExtensionRadioButton.text=Extension Only
InterestingItemDefsPanel.fileNameTextField.text=
InterestingItemDefsPanel.fileNameRadioButton.text=File Name
FilesSetRulePanel.pathSeparatorInfoLabel.text=Use / as path separator
FilesSetRulePanel.filesAndDirsRadioButton.text=Files and Directories
InterestingItemDefsPanel.rulePathFilterTextField.text=
InterestingItemDefsPanel.rulePathPanel.border.title=Path
InterestingItemDefsPanel.rulePathFilterRegexCheckBox.text=Regex
InterestingItemDefsPanel.ignoreKnownFilesCheckbox.text=Ignore Known Files
@@ -0,0 +1,63 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014 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.modules.interestingitems;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings;
/**
* Ingest job settings for interesting files identifier ingest modules.
*/
final class FilesIdentifierIngestJobSettings implements IngestModuleIngestJobSettings {
private static final long serialVersionUID = 1L;
private final Set<String> enabledFilesSetNames = new HashSet<>();
/**
* Construct the ingest job settings for an interesting files identifier
* ingest module.
*
* @param enabledFilesSetNames The names of the interesting files sets
* that are enabled for the ingest job.
*/
FilesIdentifierIngestJobSettings(List<String> enabledFilesSetNames) {
this.enabledFilesSetNames.addAll(enabledFilesSetNames);
}
/**
* @inheritDoc
*/
@Override
public long getVersionNumber() {
return FilesIdentifierIngestJobSettings.serialVersionUID;
}
/**
* Determines whether or not an interesting files set definition is enabled
* for an ingest job.
*
* @param filesSetName The name of the files set definition to check.
* @return True if the file set is enabled, false otherwise.
*/
boolean isInterestingFilesSetEnabled(String filesSetName) {
return (this.enabledFilesSetNames.contains(filesSetName));
}
}
@@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="filesSetScrollPane" pref="254" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="filesSetScrollPane" pref="188" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javax.swing.JScrollPane" name="filesSetScrollPane">
<AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JTable" name="filesSetTable">
<Properties>
<Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="f0" green="f0" red="f0" type="rgb"/>
</Property>
<Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.editors2.TableModelEditor">
<Table columnCount="0" rowCount="0"/>
</Property>
<Property name="columnModel" type="javax.swing.table.TableColumnModel" editor="org.netbeans.modules.form.editors2.TableColumnModelEditor">
<TableColumnModel selectionModel="0"/>
</Property>
<Property name="showHorizontalLines" type="boolean" value="false"/>
<Property name="showVerticalLines" type="boolean" value="false"/>
<Property name="tableHeader" type="javax.swing.table.JTableHeader" editor="org.netbeans.modules.form.editors2.JTableHeaderEditor">
<TableHeader reorderingAllowed="true" resizingAllowed="true"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Form>
@@ -0,0 +1,303 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014 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.modules.interestingitems;
import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import java.util.TreeMap;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableColumn;
import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings;
import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel;
/**
* Ingest job settings panel for interesting files identifier ingest modules.
*/
final class FilesIdentifierIngestJobSettingsPanel extends IngestModuleIngestJobSettingsPanel implements Observer {
private final FilesSetsTableModel tableModel;
// This map of interesting interesting files set names to files sets is used
// both to sort interesting files sets by name and to detect when a new
// files set is defined, so that the new set can be enabled by default.
private TreeMap<String, FilesSet> filesSetSnapshot;
/**
* A factory method that avoids publishing the "this" reference from the
* constructor.
*
* @return An instance of the ingest job settings panel interesting files
* identifier ingest modules.
*/
static FilesIdentifierIngestJobSettingsPanel makePanel(FilesIdentifierIngestJobSettings settings) {
FilesIdentifierIngestJobSettingsPanel panel = new FilesIdentifierIngestJobSettingsPanel(settings);
// Observe the interesting item definitions manager for changes to the
// interesting file set definitions. This is used to keep this panel in
// synch with changes made using the global settings/option panel for
// this module.
InterestingItemDefsManager.getInstance().addObserver(panel);
return panel;
}
/**
* Constructs an ingest job settings panel for interesting files identifier
* ingest modules.
*/
private FilesIdentifierIngestJobSettingsPanel(FilesIdentifierIngestJobSettings settings) {
initComponents();
/* Make a table row object for each interesting files set, bundling the
* set with an enabled flag. The files sets are loaded into a tree map
* so they are sorted by name. The keys set also serves as a cache of
* set names so that new sets can be easily detected in the override of
* Observer.update().
*/
List<FilesSetRow> filesSetRows = new ArrayList<>();
this.filesSetSnapshot = new TreeMap<>(InterestingItemDefsManager.getInstance().getInterestingFilesSets());
for (FilesSet set : this.filesSetSnapshot.values()) {
filesSetRows.add(new FilesSetRow(set, settings.isInterestingFilesSetEnabled(set.getName())));
}
// Make a table model to manage the row objects.
this.tableModel = new FilesSetsTableModel(filesSetRows);
// Set up the table component that presents the table model that allows
// users to enable and disable interesting files set definitions for an
// ingest job.
this.filesSetTable.setModel(tableModel);
this.filesSetTable.setTableHeader(null);
this.filesSetTable.setRowSelectionAllowed(false);
final int width = this.filesSetScrollPane.getPreferredSize().width;
this.filesSetTable.setAutoResizeMode(JTable.AUTO_RESIZE_NEXT_COLUMN);
TableColumn column;
for (int i = 0; i < this.filesSetTable.getColumnCount(); i++) {
column = this.filesSetTable.getColumnModel().getColumn(i);
if (i == 0) {
column.setPreferredWidth(((int) (width * 0.07)));
} else {
column.setPreferredWidth(((int) (width * 0.92)));
}
}
}
/**
* @inheritDoc
*/
@Override
public IngestModuleIngestJobSettings getSettings() {
List<String> enabledInterestingFilesSets = new ArrayList<>();
this.tableModel.filesSetRows.stream().filter((rowModel) -> (rowModel.isEnabled())).forEach((rowModel) -> {
enabledInterestingFilesSets.add(rowModel.getFilesSet().getName());
});
return new FilesIdentifierIngestJobSettings(enabledInterestingFilesSets);
}
/**
* @inheritDoc
*/
@Override
public void update(Observable o, Object arg
) {
// Get the user's current enabled/disabled settings.
FilesIdentifierIngestJobSettings settings = (FilesIdentifierIngestJobSettings) this.getSettings();
// Refresh the view of the interesting files set definitions.
List<FilesSetRow> rowModels = new ArrayList<>();
TreeMap<String, FilesSet> newFilesSetSnapshot = new TreeMap<>(InterestingItemDefsManager.getInstance().getInterestingFilesSets());
for (FilesSet set : newFilesSetSnapshot.values()) {
if (this.filesSetSnapshot.keySet().contains(set.getName())) {
// Preserve the current enabled/diabled state of the set.
rowModels.add(new FilesSetRow(set, settings.isInterestingFilesSetEnabled(set.getName())));
} else {
// New sets are enabled by default.
rowModels.add(new FilesSetRow(set, true));
}
}
this.tableModel.resetTableData(rowModels);
// Cache the snapshot so it will be avaialble for the next update.
this.filesSetSnapshot = newFilesSetSnapshot;
}
/**
* Table model for a JTable component that allows users to enable and
* disable interesting files set definitions for an ingest job.
*/
private final static class FilesSetsTableModel extends AbstractTableModel {
private List<FilesSetRow> filesSetRows;
/**
* Constructs a table model for a JTable component that allows users to
* enable and disable interesting files set definitions for an ingest
* job.
*
* @param filesSetRows A collection of row objects that bundles an
* interesting files set with an enabled flag
*/
FilesSetsTableModel(List<FilesSetRow> filesSetRows) {
this.filesSetRows = filesSetRows;
}
/**
* Refreshes the table with a new set of rows.
*
* @param filesSetRows A collection of row objects that bundles an
* interesting files set with an enabled flag
*/
void resetTableData(List<FilesSetRow> filesSetRows) {
this.filesSetRows = filesSetRows;
this.fireTableDataChanged();
}
/**
* @inheritDoc
*/
@Override
public int getRowCount() {
return this.filesSetRows.size();
}
/**
* @inheritDoc
*/
@Override
public int getColumnCount() {
return 2;
}
/**
* @inheritDoc
*/
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
if (columnIndex == 0) {
return this.filesSetRows.get(rowIndex).isEnabled();
} else {
return this.filesSetRows.get(rowIndex).getFilesSet().getName();
}
}
/**
* @inheritDoc
*/
@Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
return (columnIndex == 0);
}
/**
* @inheritDoc
*/
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
if (columnIndex == 0) {
this.filesSetRows.get(rowIndex).setEnabled((Boolean) aValue);
}
}
/**
* @inheritDoc
*/
@Override
public Class<?> getColumnClass(int c) {
return getValueAt(0, c).getClass();
}
}
/**
* Bundles an interesting files set with an enabled flag.
*/
private final static class FilesSetRow {
private final FilesSet set;
private boolean enabled;
FilesSetRow(FilesSet set, boolean enabled) {
this.set = set;
this.enabled = enabled;
}
FilesSet getFilesSet() {
return this.set;
}
boolean isEnabled() {
return this.enabled;
}
void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
filesSetScrollPane = new javax.swing.JScrollPane();
filesSetTable = new javax.swing.JTable();
filesSetTable.setBackground(new java.awt.Color(240, 240, 240));
filesSetTable.setModel(new javax.swing.table.DefaultTableModel(
new Object [][] {
},
new String [] {
}
));
filesSetTable.setShowHorizontalLines(false);
filesSetTable.setShowVerticalLines(false);
filesSetScrollPane.setViewportView(filesSetTable);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(filesSetScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 254, Short.MAX_VALUE)
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap()
.addComponent(filesSetScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 188, Short.MAX_VALUE)
.addContainerGap())
);
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JScrollPane filesSetScrollPane;
private javax.swing.JTable filesSetTable;
// End of variables declaration//GEN-END:variables
}
@@ -0,0 +1,131 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014 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.modules.interestingitems;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.ingest.FileIngestModule;
import org.sleuthkit.autopsy.ingest.IngestJobContext;
import org.sleuthkit.autopsy.ingest.IngestModuleReferenceCounter;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.TskCoreException;
/**
* A file ingest module that generates interesting files set hit artifacts for
* files that match interesting files set definitions.
*/
final class FilesIdentifierIngestModule implements FileIngestModule {
private static final Object sharedResourcesLock = new Object();
private static final Logger logger = Logger.getLogger(FilesIdentifierIngestModule.class.getName());
private static final IngestModuleReferenceCounter refCounter = new IngestModuleReferenceCounter();
private static final Map<Long, List<FilesSet>> interestingFileSetsByJob = new ConcurrentHashMap<>();
private final FilesIdentifierIngestJobSettings settings;
private IngestJobContext context;
/**
* Construct an interesting files identifier ingest module for an ingest
* job.
*
* @param settings An ingest job settings object for the module.
*/
FilesIdentifierIngestModule(FilesIdentifierIngestJobSettings settings) {
this.settings = settings;
}
/**
* @inheritDoc
*/
@Override
public void startUp(IngestJobContext context) throws IngestModuleException {
this.context = context;
synchronized (FilesIdentifierIngestModule.sharedResourcesLock) {
if (FilesIdentifierIngestModule.refCounter.incrementAndGet(context.getJobId()) == 1) {
// Starting up the first instance of this module for this ingest
// job, so get the interesting file sets definitions snapshot
// for the job. Note that getting this snapshot atomically via a
// synchronized definitions manager method eliminates the need
// to disable the interesting files set definition UI during ingest.
List<FilesSet> filesSets = new ArrayList<>();
for (FilesSet set : InterestingItemDefsManager.getInstance().getInterestingFilesSets().values()) {
if (settings.isInterestingFilesSetEnabled(set.getName())) {
filesSets.add(set);
}
}
FilesIdentifierIngestModule.interestingFileSetsByJob.put(context.getJobId(), filesSets);
}
}
}
/**
* @inheritDoc
*/
@Override
public ProcessResult process(AbstractFile file) {
// See if the file belongs to any defined interesting files set.
List<FilesSet> filesSets = FilesIdentifierIngestModule.interestingFileSetsByJob.get(this.context.getJobId());
for (FilesSet filesSet : filesSets) {
String ruleSatisfied = filesSet.fileIsMemberOf(file);
if (ruleSatisfied != null) {
try {
// Post an interesting files set hit artifact to the
// blackboard.
String moduleName = InterestingItemsIngestModuleFactory.getModuleName();
BlackboardArtifact artifact = file.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT);
// Add a set name attribute to the artifact. This adds a
// fair amount of redundant data to the attributes table
// (i.e., rows that differ only in artifact id), but doing
// otherwise would requires reworking the interesting files
// set hit artifact.
BlackboardAttribute setNameAttribute = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID(), moduleName, filesSet.getName());
artifact.addAttribute(setNameAttribute);
// Add a category attribute to the artifact to record the
// interesting files set membership rule that was satisfied.
BlackboardAttribute ruleNameAttribute = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CATEGORY.getTypeID(), moduleName, ruleSatisfied);
artifact.addAttribute(ruleNameAttribute);
} catch (TskCoreException ex) {
FilesIdentifierIngestModule.logger.log(Level.SEVERE, "Error posting to the blackboard", ex);
}
}
}
return ProcessResult.OK;
}
/**
* @inheritDoc
*/
@Override
public void shutDown() {
if (refCounter.decrementAndGet(this.context.getJobId()) == 0) {
// Shutting down the last instance of this module for this ingest
// job, so discard the interesting file sets definitions snapshot
// for the job.
FilesIdentifierIngestModule.interestingFileSetsByJob.remove(this.context.getJobId());
}
}
}
@@ -0,0 +1,635 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014 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.modules.interestingitems;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.TskData;
/**
* A collection of set membership rules that define an interesting files set.
* The rules are independent, i.e., if any rule is satisfied by a file, the file
* belongs to the set.
*
* Interesting files set definition objects are immutable, so they may be safely
* published to multiple threads.
*/
final class FilesSet {
private final String name;
private final String description;
private final boolean ignoreKnownFiles;
private final Map<String, Rule> rules = new HashMap<>();
/**
* Constructs an interesting files set.
*
* @param name The name of the set.
* @param description A description of the set, may be null.
* @param ignoreKnownFiles Whether or not to exclude known files from the
* set.
* @param rules The rules that define the set. May be null, but a set with
* no rules is the empty set.
*/
FilesSet(String name, String description, boolean ignoreKnownFiles, Map<String, Rule> rules) {
if ((name == null) || (name.isEmpty())) {
throw new IllegalArgumentException("Interesting files set name cannot be null or empty");
}
this.name = name;
this.description = (description != null ? description : "");
this.ignoreKnownFiles = ignoreKnownFiles;
if (rules != null) {
this.rules.putAll(rules);
}
}
/**
* Gets the name of this interesting files set.
*
* @return A name string.
*/
String getName() {
return this.name;
}
/**
* Gets the description of this interesting files set.
*
* @return A description string, possibly the empty string.
*/
String getDescription() {
return this.description;
}
/**
* Returns whether or not this interesting files set ignores known files,
* i.e., files marked as known by a look up in a known files hash set such
* as the National Software Reference Library (NSRL). Note that the
* interesting files set does not do hash set look ups; it simply queries
* the known status of the files when testing them for set membership.
*
* @return True if known files are ignored, false otherwise.
*/
boolean ignoresKnownFiles() {
return this.ignoreKnownFiles;
}
/**
* Gets a copy of the set membership rules of this interesting files set.
*
* @return A map of set membership rule names to rules, possibly empty.
*/
Map<String, Rule> getRules() {
return new HashMap<>(this.rules);
}
/**
* Determines whether a file is a member of this interesting files set.
*
* @param file A file to test for set membership.
* @return The name of the first set membership rule satisfied by the file,
* will be null if the file does not belong to the set.
*/
String fileIsMemberOf(AbstractFile file) {
if ((this.ignoreKnownFiles) && (file.getKnown() == TskData.FileKnown.KNOWN)) {
return null;
}
for (Rule rule : rules.values()) {
if (rule.isSatisfied(file)) {
return rule.getName();
}
}
return null;
}
@Override
public String toString() {
// This override is designed to provide a display name for use with
// javax.swing.DefaultListModel<E>.
return this.name;
}
/**
* A set membership rule for an interesting files set. The immutability of a
* rule object allows it to be safely published to multiple threads.
*/
static class Rule {
private final String ruleName;
private final FileNameFilter fileNameFilter;
private final MetaTypeFilter metaTypeFilter;
private final ParentPathFilter pathFilter;
private final List<FileAttributeFilter> filters = new ArrayList<>();
/**
* Construct an interesting files set membership rule.
*
* @param ruleName The name of the rule.
* @param fileNameFilter A file name filter.
* @param metaTypeFilter A file meta-type filter.
* @param pathFilter A file path filter, may be null.
*/
Rule(String ruleName, FileNameFilter fileNameFilter, MetaTypeFilter metaTypeFilter, ParentPathFilter pathFilter) {
if ((ruleName == null) || (ruleName.isEmpty())) {
throw new IllegalArgumentException("Interesting files set rule name cannot be null or empty");
}
if (fileNameFilter == null) {
throw new IllegalArgumentException("Interesting files set rule file name filter cannot be null");
}
if (metaTypeFilter == null) {
throw new IllegalArgumentException("Interesting files set rule meta-type filter cannot be null");
}
this.ruleName = ruleName;
this.fileNameFilter = fileNameFilter;
this.filters.add(fileNameFilter);
this.metaTypeFilter = metaTypeFilter;
this.filters.add(this.metaTypeFilter);
this.pathFilter = pathFilter;
if (this.pathFilter != null) {
this.filters.add(this.pathFilter);
}
}
/**
* Get the name of the rule.
*
* @return A name string.
*/
String getName() {
return ruleName;
}
/**
* Get the file name filter for the rule.
*
* @return A file name filter.
*/
FileNameFilter getFileNameFilter() {
return this.fileNameFilter;
}
/**
* Get the meta-type filter for the rule.
*
* @return A meta-type filter.
*/
MetaTypeFilter getMetaTypeFilter() {
return this.metaTypeFilter;
}
/**
* Get the path filter for the rule.
*
* @return A path filter, may be null.
*/
ParentPathFilter getPathFilter() {
return this.pathFilter;
}
/**
* Determines whether or not a file satisfies the rule.
*
* @param file The file to test.
* @return True if the rule is satisfied, false otherwise.
*/
boolean isSatisfied(AbstractFile file) {
for (FileAttributeFilter filter : filters) {
if (!filter.passes(file)) {
return false;
}
}
return true;
}
/**
* @inheritDoc
*/
@Override
public String toString() {
// This override is designed to provide a display name for use with
// javax.swing.DefaultListModel<E>.
return this.ruleName;
}
/**
* An interface for the file attribute filters of which interesting
* files set membership rules are composed.
*/
static interface FileAttributeFilter {
/**
* Tests whether or not a file satisfies the conditions of a filter.
*
* @param file The file to test.
* @return True if the file passes the test, false otherwise.
*/
boolean passes(AbstractFile file);
}
/**
* A file meta-type filter for an interesting files set membership rule.
* The immutability of a meta-type filter object allows it to be safely
* published to multiple threads.
*/
static final class MetaTypeFilter implements FileAttributeFilter {
enum Type {
FILES,
DIRECTORIES,
FILES_AND_DIRECTORIES
}
private final Type type;
/**
* Construct a meta-type filter.
*
* @param metaType The meta-type to match, must.
*/
MetaTypeFilter(Type type) {
this.type = type;
}
/**
* @inheritDoc
*/
@Override
public boolean passes(AbstractFile file) {
switch (this.type) {
case FILES:
return file.getMetaType() == TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG;
case DIRECTORIES:
return file.getMetaType() == TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_DIR;
default:
return file.getMetaType() == TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG
|| file.getMetaType() == TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_DIR;
}
}
/**
* Gets the meta-type the filter matches.
*
* @return A member of the MetaTypeFilter.Type enumeration.
*/
Type getMetaType() {
return this.type;
}
}
/**
* An interface for file attribute filters that do textual matching.
*/
static interface TextFilter extends FileAttributeFilter {
/**
* Gets the text the filter matches.
*
* @return The text.
*/
String getTextToMatch();
/**
* Queries whether or not the text the filter matches is a regular
* expression.
*
* @return True if the text to be matched is a regular expression,
* false otherwise.
*/
boolean isRegex();
/**
* Determines whether a string of text matches the filter.
*
* @param textToMatch The text string.
* @return True if the text matches, false otherwise.
*/
boolean textMatches(String textToMatch);
}
/**
* An abstract base class for file attribute filters that do textual
* matching.
*/
private static abstract class AbstractTextFilter implements TextFilter {
private final TextMatcher textMatcher;
/**
* Construct a case-insensitive text filter.
*
* @param text The text to be matched.
*/
AbstractTextFilter(String text) {
this.textMatcher = new FilesSet.Rule.CaseInsensitiveStringComparisionMatcher(text);
}
/**
* Construct a regular expression text filter.
*
* @param regex The regular expression to be matched.
*/
AbstractTextFilter(Pattern regex) {
this.textMatcher = new FilesSet.Rule.RegexMatcher(regex);
}
/**
* Get the text the filter matches.
*
* @return The text.
*/
@Override
public String getTextToMatch() {
return this.textMatcher.getTextToMatch();
}
/**
* Queries whether or not the text the filter matches is a regular
* expression.
*
* @return True if the text to be matched is a regular expression,
* false otherwise.
*/
@Override
public boolean isRegex() {
return this.textMatcher.isRegex();
}
/**
* Determines whether a string of text matches the filter.
*
* @param textToMatch The text string.
* @return True if the text matches, false otherwise.
*/
@Override
public boolean textMatches(String textToMatch) {
return this.textMatcher.textMatches(textToMatch);
}
/**
* @inheritDoc
*/
@Override
public abstract boolean passes(AbstractFile file);
}
/**
* A file path filter for an interesting files set membership rule. The
* immutability of a path filter object allows it to be safely published
* to multiple threads.
*/
static final class ParentPathFilter extends AbstractTextFilter {
/**
* Construct a case-insensitive file path filter.
*
* @param path The path to be matched.
*/
ParentPathFilter(String path) {
super(path);
}
/**
* Construct a file path regular expression filter.
*
* @param path The path regular expression to be matched.
*/
ParentPathFilter(Pattern path) {
super(path);
}
/**
* @inheritDoc
*/
@Override
public boolean passes(AbstractFile file) {
return this.textMatches(file.getParentPath());
}
}
/**
* A "tagging" interface to group name and extension filters separately
* from path filters for type safety when constructing rules.
*/
static interface FileNameFilter extends TextFilter {
}
/**
* A file name filter for an interesting files set membership rule. The
* immutability of a file name filter object allows it to be safely
* published to multiple threads.
*/
static final class FullNameFilter extends AbstractTextFilter implements FileNameFilter {
/**
* Construct a case-insensitive full file name filter.
*
* @param name The file name to be matched.
*/
FullNameFilter(String name) {
super(name);
}
/**
* Construct a full file name regular expression filter.
*
* @param name The file name regular expression to be matched.
*/
FullNameFilter(Pattern name) {
super(name);
}
/**
* @inheritDoc
*/
@Override
public boolean passes(AbstractFile file) {
return this.textMatches(file.getName());
}
}
/**
* A file name extension filter for an interesting files set membership
* rule. The immutability of a file name extension filter object allows
* it to be safely published to multiple threads.
*/
static final class ExtensionFilter extends AbstractTextFilter implements FileNameFilter {
/**
* Construct a case-insensitive file name extension filter.
*
* @param extension The file name extension to be matched.
*/
ExtensionFilter(String extension) {
// If there is a leading ".", strip it since
// AbstractFile.getFileNameExtension() returns just the
// extension chars and not the dot.
super(extension.startsWith(".") ? extension.substring(1) : extension);
}
/**
* Construct a file name extension regular expression filter.
*
* @param extension The file name extension regular expression to be
* matched.
*/
ExtensionFilter(Pattern extension) {
super(extension.pattern());
}
/**
* @inheritDoc
*/
@Override
public boolean passes(AbstractFile file) {
return this.textMatches(file.getNameExtension());
}
}
/**
* An interface for objects that do textual matches, used to compose a
* text filter.
*/
private static interface TextMatcher {
/**
* Get the text the matcher examines.
*
* @return The text.
*/
String getTextToMatch();
/**
* Queries whether or not the text the matcher examines is a regular
* expression.
*
* @return True if the text to be matched is a regular expression,
* false otherwise.
*/
boolean isRegex();
/**
* Determines whether a string of text is matched.
*
* @param textToMatch The text string.
* @return True if the text matches, false otherwise.
*/
boolean textMatches(String subject);
}
/**
* A text matcher that does a case-insensitive string comparison.
*/
private static class CaseInsensitiveStringComparisionMatcher implements TextMatcher {
private final String textToMatch;
/**
* Construct a text matcher that does a case-insensitive string
* comparison.
*
* @param textToMatch The text to match.
*/
CaseInsensitiveStringComparisionMatcher(String textToMatch) {
this.textToMatch = textToMatch;
}
/**
* @inheritDoc
*/
@Override
public String getTextToMatch() {
return this.textToMatch;
}
/**
* @inheritDoc
*/
@Override
public boolean isRegex() {
return false;
}
/**
* @inheritDoc
*/
@Override
public boolean textMatches(String subject) {
return subject.equalsIgnoreCase(textToMatch);
}
}
/**
* A text matcher that does regular expression matching.
*/
private static class RegexMatcher implements TextMatcher {
private final Pattern regex;
/**
* Construct a text matcher that does a regular expression
* comparison.
*
* @param regex The regular expression to match.
*/
RegexMatcher(Pattern regex) {
this.regex = regex;
}
/**
* @inheritDoc
*/
@Override
public String getTextToMatch() {
return this.regex.pattern();
}
/**
* @inheritDoc
*/
@Override
public boolean isRegex() {
return true;
}
/**
* @inheritDoc
*/
@Override
public boolean textMatches(String subject) {
// A single match is sufficient.
return this.regex.matcher(subject).find();
}
}
}
}
@@ -0,0 +1,118 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<Component id="nameLabel" min="-2" pref="71" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="nameTextField" min="-2" pref="299" max="-2" attributes="0"/>
</Group>
<Component id="descPanel" max="32767" attributes="0"/>
<Component id="ignoreKnownFilesCheckbox" alignment="0" max="32767" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="nameLabel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="nameTextField" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="descPanel" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="ignoreKnownFilesCheckbox" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="nameLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties" key="FilesSetPanel.nameLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="nameTextField">
</Component>
<Container class="javax.swing.JPanel" name="descPanel">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo">
<TitledBorder title="Description">
<ResourceString PropertyName="titleX" bundle="org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties" key="FilesSetPanel.descPanel.border.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</TitledBorder>
</Border>
</Property>
</Properties>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="descScrollPanel" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<EmptySpace max="32767" attributes="0"/>
<Component id="descScrollPanel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javax.swing.JScrollPane" name="descScrollPanel">
<AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JTextArea" name="descTextArea">
<Properties>
<Property name="columns" type="int" value="20"/>
<Property name="rows" type="int" value="5"/>
</Properties>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Container>
<Component class="javax.swing.JCheckBox" name="ignoreKnownFilesCheckbox">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties" key="FilesSetPanel.ignoreKnownFilesCheckbox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Form>
@@ -0,0 +1,179 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014 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.modules.interestingitems;
import org.openide.DialogDisplayer;
import org.openide.NotifyDescriptor;
import org.openide.util.NbBundle;
/**
* A panel that allows a user to create and edit interesting files set
* definitions.
*/
public class FilesSetPanel extends javax.swing.JPanel {
/**
* Construct a files set panel in create mode.
*/
FilesSetPanel() {
initComponents();
}
/**
* Construct a files set panel in edit mode.
*
* @param filesSet The files set to be edited.
*/
FilesSetPanel(FilesSet filesSet) {
initComponents();
this.nameTextField.setText(filesSet.getName());
this.descTextArea.setText(filesSet.getDescription());
this.ignoreKnownFilesCheckbox.setSelected(filesSet.ignoresKnownFiles());
}
/**
* Returns whether or not the data entered in the panel constitutes a valid
* interesting files set definition, displaying a dialog explaining the
* deficiency if the definition is invalid
*
* @return True if the definition is valid, false otherwise.
*/
boolean isValidDefinition() {
if (this.nameTextField.getText().isEmpty()) {
NotifyDescriptor notifyDesc = new NotifyDescriptor.Message(
NbBundle.getMessage(FilesSetPanel.class, "FilesSetPanel.messages.filesSetsMustBeNamed"),
NotifyDescriptor.WARNING_MESSAGE);
DialogDisplayer.getDefault().notify(notifyDesc);
return false;
}
return true;
}
/**
* Get the name for the interesting files set defined using this panel.
*
* @return A name string.
*/
String getFilesSetName() {
return this.nameTextField.getText();
}
/**
* Get the description for the interesting files set defined using this
* panel.
*
* @return A description string.
*/
String getFilesSetDescription() {
return this.descTextArea.getText();
}
/**
* Get whether or not the interesting files set defined using this panel
* ignores known files.
*
* @return True if the set ignores known files, false otherwise.
*/
boolean getFileSetIgnoresKnownFiles() {
return this.ignoreKnownFilesCheckbox.isSelected();
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
nameLabel = new javax.swing.JLabel();
nameTextField = new javax.swing.JTextField();
descPanel = new javax.swing.JPanel();
descScrollPanel = new javax.swing.JScrollPane();
descTextArea = new javax.swing.JTextArea();
ignoreKnownFilesCheckbox = new javax.swing.JCheckBox();
org.openide.awt.Mnemonics.setLocalizedText(nameLabel, org.openide.util.NbBundle.getMessage(FilesSetPanel.class, "FilesSetPanel.nameLabel.text")); // NOI18N
descPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(FilesSetPanel.class, "FilesSetPanel.descPanel.border.title"))); // NOI18N
descTextArea.setColumns(20);
descTextArea.setRows(5);
descScrollPanel.setViewportView(descTextArea);
javax.swing.GroupLayout descPanelLayout = new javax.swing.GroupLayout(descPanel);
descPanel.setLayout(descPanelLayout);
descPanelLayout.setHorizontalGroup(
descPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(descPanelLayout.createSequentialGroup()
.addContainerGap()
.addComponent(descScrollPanel)
.addContainerGap())
);
descPanelLayout.setVerticalGroup(
descPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, descPanelLayout.createSequentialGroup()
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(descScrollPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap())
);
org.openide.awt.Mnemonics.setLocalizedText(ignoreKnownFilesCheckbox, org.openide.util.NbBundle.getMessage(FilesSetPanel.class, "FilesSetPanel.ignoreKnownFilesCheckbox.text")); // NOI18N
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addComponent(nameLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 71, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(nameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 299, javax.swing.GroupLayout.PREFERRED_SIZE))
.addComponent(descPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(ignoreKnownFilesCheckbox, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(nameLabel)
.addComponent(nameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(descPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(ignoreKnownFilesCheckbox)
.addContainerGap())
);
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JPanel descPanel;
private javax.swing.JScrollPane descScrollPanel;
private javax.swing.JTextArea descTextArea;
private javax.swing.JCheckBox ignoreKnownFilesCheckbox;
private javax.swing.JLabel nameLabel;
private javax.swing.JTextField nameTextField;
// End of variables declaration//GEN-END:variables
}
@@ -0,0 +1,316 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<NonVisualComponents>
<Component class="javax.swing.ButtonGroup" name="nameButtonGroup">
</Component>
<Component class="javax.swing.ButtonGroup" name="typeButtonGroup">
</Component>
</NonVisualComponents>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" max="-2" attributes="0">
<Component id="typePanel" alignment="0" max="32767" attributes="0"/>
<Component id="namePanel" alignment="0" max="32767" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Component id="ruleNameLabel" min="-2" pref="59" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="ruleNameTextField" min="-2" pref="256" max="-2" attributes="0"/>
</Group>
<Component id="pathPanel" alignment="0" max="32767" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="ruleNameTextField" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="ruleNameLabel" alignment="3" max="32767" attributes="0"/>
</Group>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="typePanel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="namePanel" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="pathPanel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="ruleNameLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties" key="FilesSetRulePanel.ruleNameLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="ruleNameTextField">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties" key="FilesSetRulePanel.ruleNameTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Container class="javax.swing.JPanel" name="namePanel">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo">
<TitledBorder title="Named:">
<ResourceString PropertyName="titleX" bundle="org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties" key="FilesSetRulePanel.namePanel.border.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</TitledBorder>
</Border>
</Property>
</Properties>
<AccessibilityProperties>
<Property name="AccessibleContext.accessibleName" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties" key="FilesSetRulePanel.namePanel.AccessibleContext.accessibleName" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</AccessibilityProperties>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="fullNameRadioButton" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="10" max="-2" attributes="0"/>
<Component id="extensionRadioButton" min="-2" pref="114" max="-2" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="nameRegexCheckbox" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
<Component id="nameTextField" alignment="0" max="32767" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="nameTextField" min="-2" pref="20" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="fullNameRadioButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="extensionRadioButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="nameRegexCheckbox" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JCheckBox" name="nameRegexCheckbox">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties" key="FilesSetRulePanel.nameRegexCheckbox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JRadioButton" name="extensionRadioButton">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="nameButtonGroup"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties" key="FilesSetRulePanel.extensionRadioButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="nameTextField">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties" key="FilesSetRulePanel.nameTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JRadioButton" name="fullNameRadioButton">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="nameButtonGroup"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties" key="FilesSetRulePanel.fullNameRadioButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="typePanel">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo">
<TitledBorder title="Search for:">
<ResourceString PropertyName="titleX" bundle="org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties" key="FilesSetRulePanel.typePanel.border.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</TitledBorder>
</Border>
</Property>
</Properties>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="filesRadioButton" min="-2" max="-2" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="dirsRadioButton" min="-2" max="-2" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="filesAndDirsRadioButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="filesRadioButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="dirsRadioButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="filesAndDirsRadioButton" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JRadioButton" name="filesRadioButton">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="typeButtonGroup"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties" key="FilesSetRulePanel.filesRadioButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="filesRadioButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JRadioButton" name="dirsRadioButton">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="typeButtonGroup"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties" key="FilesSetRulePanel.dirsRadioButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="dirsRadioButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JRadioButton" name="filesAndDirsRadioButton">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="typeButtonGroup"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties" key="FilesSetRulePanel.filesAndDirsRadioButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="filesAndDirsRadioButtonActionPerformed"/>
</Events>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="pathPanel">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo">
<TitledBorder title="With parent path:">
<ResourceString PropertyName="titleX" bundle="org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties" key="FilesSetRulePanel.pathPanel.border.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</TitledBorder>
</Border>
</Property>
</Properties>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" max="-2" attributes="0">
<Group type="102" attributes="0">
<Component id="pathRegexCheckBox" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Component id="pathSeparatorInfoLabel" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="pathTextField" min="-2" pref="283" max="-2" attributes="0"/>
</Group>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="pathTextField" min="-2" pref="20" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="pathRegexCheckBox" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="pathSeparatorInfoLabel" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JCheckBox" name="pathRegexCheckBox">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties" key="FilesSetRulePanel.pathRegexCheckBox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="pathTextField">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties" key="FilesSetRulePanel.pathTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="pathSeparatorInfoLabel">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/images/info-icon-16.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties" key="FilesSetRulePanel.pathSeparatorInfoLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Form>
@@ -0,0 +1,558 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014 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.modules.interestingitems;
import java.util.List;
import java.util.logging.Level;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import org.openide.DialogDisplayer;
import org.openide.NotifyDescriptor;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.Logger;
/**
* A panel that allows a user to create and edit interesting files set
* membership rules.
*/
final class FilesSetRulePanel extends javax.swing.JPanel {
private static final Logger logger = Logger.getLogger(FilesSetRulePanel.class.getName());
private static final String SLEUTHKIT_PATH_SEPARATOR = "/"; // NON-NLS
private static final List<String> ILLEGAL_FILE_NAME_CHARS = InterestingItemDefsManager.getIllegalFileNameChars();
private static final List<String> ILLEGAL_FILE_PATH_CHARS = InterestingItemDefsManager.getIllegalFilePathChars();
/**
* Constructs a files set rule panel in create rule mode.
*/
FilesSetRulePanel() {
initComponents();
populateComponentsWithDefaultValues();
}
/**
* Constructs a files set rule panel in edit rule mode.
*
* @param rule The files set rule to be edited.
*/
FilesSetRulePanel(FilesSet.Rule rule) {
initComponents();
populateRuleNameComponent(rule);
populateTypeFilterComponents(rule);
populateNameFilterComponents(rule);
populatePathFilterComponents(rule);
}
/**
* Populates the UI components with default values.
*/
private void populateComponentsWithDefaultValues() {
this.filesRadioButton.setSelected(true);
this.fullNameRadioButton.setSelected(true);
}
/**
* Populates the UI component that displays the rule name.
*
* @param rule The files set rule to be edited.
*/
private void populateRuleNameComponent(FilesSet.Rule rule) {
this.ruleNameTextField.setText(rule.getName());
}
/**
* Populates the UI components that display the meta-type filter for a rule.
*
* @param rule The files set rule to be edited.
*/
private void populateTypeFilterComponents(FilesSet.Rule rule) {
FilesSet.Rule.MetaTypeFilter typeFilter = rule.getMetaTypeFilter();
switch (typeFilter.getMetaType()) {
case FILES:
this.filesRadioButton.setSelected(true);
break;
case DIRECTORIES:
this.dirsRadioButton.setSelected(true);
break;
case FILES_AND_DIRECTORIES:
this.filesAndDirsRadioButton.setSelected(true);
break;
}
}
/**
* Populates the UI components that display the name filter for a rule.
*
* @param rule The files set rule to be edited.
*/
private void populateNameFilterComponents(FilesSet.Rule rule) {
FilesSet.Rule.FileNameFilter nameFilter = rule.getFileNameFilter();
this.nameTextField.setText(nameFilter.getTextToMatch());
this.nameRegexCheckbox.setSelected(nameFilter.isRegex());
if (nameFilter instanceof FilesSet.Rule.FullNameFilter) {
this.fullNameRadioButton.setSelected(true);
} else {
this.extensionRadioButton.setSelected(true);
}
}
/**
* Populates the UI components that display the optional path filter for a
* rule.
*
* @param rule The files set rule to be edited.
*/
private void populatePathFilterComponents(FilesSet.Rule rule) {
FilesSet.Rule.ParentPathFilter pathFilter = rule.getPathFilter();
if (pathFilter != null) {
this.pathTextField.setText(pathFilter.getTextToMatch());
this.pathRegexCheckBox.setSelected(pathFilter.isRegex());
}
}
/**
* Returns whether or not the data entered in the panel constitutes a valid
* interesting files set membership rule definition, displaying a dialog
* explaining the deficiency if the definition is invalid.
*
* @return True if the definition is valid, false otherwise.
*/
boolean isValidRuleDefinition() {
// The rule must have a name.
if (this.ruleNameTextField.getText().isEmpty()) {
NotifyDescriptor notifyDesc = new NotifyDescriptor.Message(
NbBundle.getMessage(FilesSetPanel.class, "FilesSetRulePanel.messages.filesSetRulesMustBeNamed"),
NotifyDescriptor.WARNING_MESSAGE);
DialogDisplayer.getDefault().notify(notifyDesc);
return false;
}
// The rule must have name filter text.
if (this.nameTextField.getText().isEmpty()) {
NotifyDescriptor notifyDesc = new NotifyDescriptor.Message(
NbBundle.getMessage(FilesSetPanel.class, "FilesSetRulePanel.messages.emptyNameFilter"),
NotifyDescriptor.WARNING_MESSAGE);
DialogDisplayer.getDefault().notify(notifyDesc);
return false;
}
// The name filter must either be a regular expression that compiles or
// a string without illegal file name chars.
if (this.nameRegexCheckbox.isSelected()) {
try {
Pattern.compile(this.nameTextField.getText());
} catch (PatternSyntaxException ex) {
NotifyDescriptor notifyDesc = new NotifyDescriptor.Message(
NbBundle.getMessage(FilesSetPanel.class, "FilesSetRulePanel.messages.invalidNameRegex", ex.getLocalizedMessage()),
NotifyDescriptor.WARNING_MESSAGE);
DialogDisplayer.getDefault().notify(notifyDesc);
return false;
}
} else {
if (!FilesSetRulePanel.containsOnlyLegalChars(this.nameTextField.getText(), FilesSetRulePanel.ILLEGAL_FILE_NAME_CHARS)) {
NotifyDescriptor notifyDesc = new NotifyDescriptor.Message(
NbBundle.getMessage(FilesSetPanel.class, "FilesSetRulePanel.messages.invalidCharInName"),
NotifyDescriptor.WARNING_MESSAGE);
DialogDisplayer.getDefault().notify(notifyDesc);
return false;
}
}
// The path filter, if specified, must either be a regular expression
// that compiles or a string without illegal file path chars.
if (!this.pathTextField.getText().isEmpty()) {
if (this.pathRegexCheckBox.isSelected()) {
try {
Pattern.compile(this.pathTextField.getText());
} catch (PatternSyntaxException ex) {
NotifyDescriptor notifyDesc = new NotifyDescriptor.Message(
NbBundle.getMessage(FilesSetPanel.class, "FilesSetRulePanel.messages.invalidPathRegex", ex.getLocalizedMessage()),
NotifyDescriptor.WARNING_MESSAGE);
DialogDisplayer.getDefault().notify(notifyDesc);
return false;
}
} else {
if (!FilesSetRulePanel.containsOnlyLegalChars(this.pathTextField.getText(), FilesSetRulePanel.ILLEGAL_FILE_PATH_CHARS)) {
NotifyDescriptor notifyDesc = new NotifyDescriptor.Message(
NbBundle.getMessage(FilesSetPanel.class, "FilesSetRulePanel.messages.invalidCharInPath"),
NotifyDescriptor.WARNING_MESSAGE);
DialogDisplayer.getDefault().notify(notifyDesc);
return false;
}
}
}
return true;
}
/**
* Gets the name of the files set rule created or edited.
*
* @return A name string.
*/
String getRuleName() {
return this.ruleNameTextField.getText();
}
/**
* Gets the name filter for the rule that was created or edited. Should only
* be called if isValidDefintion() returns true.
*
* @return A name filter.
* @throws IllegalStateException if the specified name filter is not valid.
*/
FilesSet.Rule.FileNameFilter getFileNameFilter() throws IllegalStateException {
FilesSet.Rule.FileNameFilter filter = null;
if (!this.nameTextField.getText().isEmpty()) {
if (this.nameRegexCheckbox.isSelected()) {
try {
Pattern pattern = Pattern.compile(this.nameTextField.getText());
if (this.fullNameRadioButton.isSelected()) {
filter = new FilesSet.Rule.FullNameFilter(pattern);
} else {
filter = new FilesSet.Rule.ExtensionFilter(pattern);
}
} catch (PatternSyntaxException ex) {
logger.log(Level.SEVERE, "Attempt to get regex name filter that does not compile", ex); // NON-NLS
throw new IllegalStateException("The files set rule panel name filter is not in a valid state"); // NON-NLS
}
} else {
if (FilesSetRulePanel.containsOnlyLegalChars(this.nameTextField.getText(), FilesSetRulePanel.ILLEGAL_FILE_NAME_CHARS)) {
if (this.fullNameRadioButton.isSelected()) {
filter = new FilesSet.Rule.FullNameFilter(this.nameTextField.getText());
} else {
filter = new FilesSet.Rule.ExtensionFilter(this.nameTextField.getText());
}
} else {
logger.log(Level.SEVERE, "Attempt to get name filter with illegal chars"); // NON-NLS
throw new IllegalStateException("The files set rule panel name filter is not in a valid state"); // NON-NLS
}
}
}
return filter;
}
/**
* Gets the file meta-type filter for the rule that was created or edited.
*
* @return A type filter.
*/
FilesSet.Rule.MetaTypeFilter getMetaTypeFilter() {
if (this.filesRadioButton.isSelected()) {
return new FilesSet.Rule.MetaTypeFilter(FilesSet.Rule.MetaTypeFilter.Type.FILES);
} else if (this.dirsRadioButton.isSelected()) {
return new FilesSet.Rule.MetaTypeFilter(FilesSet.Rule.MetaTypeFilter.Type.DIRECTORIES);
} else {
return new FilesSet.Rule.MetaTypeFilter(FilesSet.Rule.MetaTypeFilter.Type.FILES_AND_DIRECTORIES);
}
}
/**
* Gets the optional path filter for the rule that was created or edited.
* Should only be called if isValidDefintion() returns true.
*
* @return A path filter or null if no path filter was specified.
* @throws IllegalStateException if the specified path filter is not valid.
*/
FilesSet.Rule.ParentPathFilter getPathFilter() throws IllegalStateException {
FilesSet.Rule.ParentPathFilter filter = null;
if (!this.pathTextField.getText().isEmpty()) {
if (this.pathRegexCheckBox.isSelected()) {
try {
filter = new FilesSet.Rule.ParentPathFilter(Pattern.compile(this.pathTextField.getText()));
} catch (PatternSyntaxException ex) {
logger.log(Level.SEVERE, "Attempt to get malformed path filter", ex); // NON-NLS
throw new IllegalStateException("The files set rule panel path filter is not in a valid state"); // NON-NLS
}
} else {
String path = this.pathTextField.getText();
if (FilesSetRulePanel.containsOnlyLegalChars(path, FilesSetRulePanel.ILLEGAL_FILE_PATH_CHARS)) {
// Add a leading path separator if omitted.
if (!path.startsWith(FilesSetRulePanel.SLEUTHKIT_PATH_SEPARATOR)) {
path = FilesSetRulePanel.SLEUTHKIT_PATH_SEPARATOR + path;
}
// Add a trailing path separator if omitted.
if (!path.endsWith(FilesSetRulePanel.SLEUTHKIT_PATH_SEPARATOR)) {
path += FilesSetRulePanel.SLEUTHKIT_PATH_SEPARATOR;
}
filter = new FilesSet.Rule.ParentPathFilter(path);
} else {
logger.log(Level.SEVERE, "Attempt to get path filter with illegal chars"); // NON-NLS
throw new IllegalStateException("The files set rule panel path filter is not in a valid state"); // NON-NLS
}
}
}
return filter;
}
/**
* Checks an input string for the use of illegal characters.
*
* @param toBeChecked The input string.
* @param illegalChars The characters deemed to be illegal.
* @return True if the string does not contain illegal characters, false
* otherwise.
*/
private static boolean containsOnlyLegalChars(String toBeChecked, List<String> illegalChars) {
for (String illegalChar : illegalChars) {
if (toBeChecked.contains(illegalChar)) {
return false;
}
}
return true;
}
/**
* Sets the state of the name filter UI components consistent with the state
* of the UI components in the type button group.
*/
private void setComponentsForSearchType() {
if (!this.filesRadioButton.isSelected()) {
this.fullNameRadioButton.setSelected(true);
this.extensionRadioButton.setEnabled(false);
} else {
this.extensionRadioButton.setEnabled(true);
}
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
nameButtonGroup = new javax.swing.ButtonGroup();
typeButtonGroup = new javax.swing.ButtonGroup();
ruleNameLabel = new javax.swing.JLabel();
ruleNameTextField = new javax.swing.JTextField();
namePanel = new javax.swing.JPanel();
nameRegexCheckbox = new javax.swing.JCheckBox();
extensionRadioButton = new javax.swing.JRadioButton();
nameTextField = new javax.swing.JTextField();
fullNameRadioButton = new javax.swing.JRadioButton();
typePanel = new javax.swing.JPanel();
filesRadioButton = new javax.swing.JRadioButton();
dirsRadioButton = new javax.swing.JRadioButton();
filesAndDirsRadioButton = new javax.swing.JRadioButton();
pathPanel = new javax.swing.JPanel();
pathRegexCheckBox = new javax.swing.JCheckBox();
pathTextField = new javax.swing.JTextField();
pathSeparatorInfoLabel = new javax.swing.JLabel();
org.openide.awt.Mnemonics.setLocalizedText(ruleNameLabel, org.openide.util.NbBundle.getMessage(FilesSetRulePanel.class, "FilesSetRulePanel.ruleNameLabel.text")); // NOI18N
ruleNameTextField.setText(org.openide.util.NbBundle.getMessage(FilesSetRulePanel.class, "FilesSetRulePanel.ruleNameTextField.text")); // NOI18N
namePanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(FilesSetRulePanel.class, "FilesSetRulePanel.namePanel.border.title"))); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(nameRegexCheckbox, org.openide.util.NbBundle.getMessage(FilesSetRulePanel.class, "FilesSetRulePanel.nameRegexCheckbox.text")); // NOI18N
nameButtonGroup.add(extensionRadioButton);
org.openide.awt.Mnemonics.setLocalizedText(extensionRadioButton, org.openide.util.NbBundle.getMessage(FilesSetRulePanel.class, "FilesSetRulePanel.extensionRadioButton.text")); // NOI18N
nameTextField.setText(org.openide.util.NbBundle.getMessage(FilesSetRulePanel.class, "FilesSetRulePanel.nameTextField.text")); // NOI18N
nameButtonGroup.add(fullNameRadioButton);
org.openide.awt.Mnemonics.setLocalizedText(fullNameRadioButton, org.openide.util.NbBundle.getMessage(FilesSetRulePanel.class, "FilesSetRulePanel.fullNameRadioButton.text")); // NOI18N
javax.swing.GroupLayout namePanelLayout = new javax.swing.GroupLayout(namePanel);
namePanel.setLayout(namePanelLayout);
namePanelLayout.setHorizontalGroup(
namePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(namePanelLayout.createSequentialGroup()
.addContainerGap()
.addGroup(namePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(namePanelLayout.createSequentialGroup()
.addComponent(fullNameRadioButton)
.addGap(10, 10, 10)
.addComponent(extensionRadioButton, javax.swing.GroupLayout.PREFERRED_SIZE, 114, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(18, 18, 18)
.addComponent(nameRegexCheckbox)
.addGap(0, 0, Short.MAX_VALUE))
.addComponent(nameTextField))
.addContainerGap())
);
namePanelLayout.setVerticalGroup(
namePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(namePanelLayout.createSequentialGroup()
.addContainerGap()
.addComponent(nameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(namePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(fullNameRadioButton)
.addComponent(extensionRadioButton)
.addComponent(nameRegexCheckbox))
.addContainerGap())
);
typePanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(FilesSetRulePanel.class, "FilesSetRulePanel.typePanel.border.title"))); // NOI18N
typeButtonGroup.add(filesRadioButton);
org.openide.awt.Mnemonics.setLocalizedText(filesRadioButton, org.openide.util.NbBundle.getMessage(FilesSetRulePanel.class, "FilesSetRulePanel.filesRadioButton.text")); // NOI18N
filesRadioButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
filesRadioButtonActionPerformed(evt);
}
});
typeButtonGroup.add(dirsRadioButton);
org.openide.awt.Mnemonics.setLocalizedText(dirsRadioButton, org.openide.util.NbBundle.getMessage(FilesSetRulePanel.class, "FilesSetRulePanel.dirsRadioButton.text")); // NOI18N
dirsRadioButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
dirsRadioButtonActionPerformed(evt);
}
});
typeButtonGroup.add(filesAndDirsRadioButton);
org.openide.awt.Mnemonics.setLocalizedText(filesAndDirsRadioButton, org.openide.util.NbBundle.getMessage(FilesSetRulePanel.class, "FilesSetRulePanel.filesAndDirsRadioButton.text")); // NOI18N
filesAndDirsRadioButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
filesAndDirsRadioButtonActionPerformed(evt);
}
});
javax.swing.GroupLayout typePanelLayout = new javax.swing.GroupLayout(typePanel);
typePanel.setLayout(typePanelLayout);
typePanelLayout.setHorizontalGroup(
typePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(typePanelLayout.createSequentialGroup()
.addContainerGap()
.addComponent(filesRadioButton)
.addGap(18, 18, 18)
.addComponent(dirsRadioButton)
.addGap(18, 18, 18)
.addComponent(filesAndDirsRadioButton)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
typePanelLayout.setVerticalGroup(
typePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(typePanelLayout.createSequentialGroup()
.addContainerGap()
.addGroup(typePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(filesRadioButton)
.addComponent(dirsRadioButton)
.addComponent(filesAndDirsRadioButton))
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
pathPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(FilesSetRulePanel.class, "FilesSetRulePanel.pathPanel.border.title"))); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(pathRegexCheckBox, org.openide.util.NbBundle.getMessage(FilesSetRulePanel.class, "FilesSetRulePanel.pathRegexCheckBox.text")); // NOI18N
pathTextField.setText(org.openide.util.NbBundle.getMessage(FilesSetRulePanel.class, "FilesSetRulePanel.pathTextField.text")); // NOI18N
pathSeparatorInfoLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/info-icon-16.png"))); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(pathSeparatorInfoLabel, org.openide.util.NbBundle.getMessage(FilesSetRulePanel.class, "FilesSetRulePanel.pathSeparatorInfoLabel.text")); // NOI18N
javax.swing.GroupLayout pathPanelLayout = new javax.swing.GroupLayout(pathPanel);
pathPanel.setLayout(pathPanelLayout);
pathPanelLayout.setHorizontalGroup(
pathPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(pathPanelLayout.createSequentialGroup()
.addContainerGap()
.addGroup(pathPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
.addGroup(pathPanelLayout.createSequentialGroup()
.addComponent(pathRegexCheckBox)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(pathSeparatorInfoLabel))
.addComponent(pathTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 283, javax.swing.GroupLayout.PREFERRED_SIZE))
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
pathPanelLayout.setVerticalGroup(
pathPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(pathPanelLayout.createSequentialGroup()
.addContainerGap()
.addComponent(pathTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGroup(pathPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(pathRegexCheckBox)
.addComponent(pathSeparatorInfoLabel))
.addContainerGap())
);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
.addComponent(typePanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(namePanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup()
.addComponent(ruleNameLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 59, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(ruleNameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 256, javax.swing.GroupLayout.PREFERRED_SIZE))
.addComponent(pathPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(ruleNameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(ruleNameLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addGap(18, 18, 18)
.addComponent(typePanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(namePanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(pathPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap())
);
namePanel.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(FilesSetRulePanel.class, "FilesSetRulePanel.namePanel.AccessibleContext.accessibleName")); // NOI18N
}// </editor-fold>//GEN-END:initComponents
private void filesAndDirsRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_filesAndDirsRadioButtonActionPerformed
setComponentsForSearchType();
}//GEN-LAST:event_filesAndDirsRadioButtonActionPerformed
private void dirsRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_dirsRadioButtonActionPerformed
setComponentsForSearchType();
}//GEN-LAST:event_dirsRadioButtonActionPerformed
private void filesRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_filesRadioButtonActionPerformed
setComponentsForSearchType();
}//GEN-LAST:event_filesRadioButtonActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JRadioButton dirsRadioButton;
private javax.swing.JRadioButton extensionRadioButton;
private javax.swing.JRadioButton filesAndDirsRadioButton;
private javax.swing.JRadioButton filesRadioButton;
private javax.swing.JRadioButton fullNameRadioButton;
private javax.swing.ButtonGroup nameButtonGroup;
private javax.swing.JPanel namePanel;
private javax.swing.JCheckBox nameRegexCheckbox;
private javax.swing.JTextField nameTextField;
private javax.swing.JPanel pathPanel;
private javax.swing.JCheckBox pathRegexCheckBox;
private javax.swing.JLabel pathSeparatorInfoLabel;
private javax.swing.JTextField pathTextField;
private javax.swing.JLabel ruleNameLabel;
private javax.swing.JTextField ruleNameTextField;
private javax.swing.ButtonGroup typeButtonGroup;
private javax.swing.JPanel typePanel;
// End of variables declaration//GEN-END:variables
}
@@ -0,0 +1,577 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014 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.modules.interestingitems;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Observable;
import java.util.logging.Level;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
import org.sleuthkit.autopsy.coreutils.XMLUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
/**
* Provides access to interesting item definitions persisted to disk. Clients
* receive copies of the most recent interesting item definitions via
* synchronized methods, allowing the definitions to be safely published to
* multiple threads.
*/
final class InterestingItemDefsManager extends Observable {
private static final List<String> ILLEGAL_FILE_NAME_CHARS = Collections.unmodifiableList(new ArrayList<>(Arrays.asList("\\", "/", ":", "*", "?", "\"", "<", ">")));
private static final List<String> ILLEGAL_FILE_PATH_CHARS = Collections.unmodifiableList(new ArrayList<>(Arrays.asList("\\", ":", "*", "?", "\"", "<", ">")));
private static final String INTERESTING_FILES_SET_DEFS_FILE_NAME = "InterestingFilesSetDefs.xml"; //NON-NLS
private static final String DEFAULT_FILE_SET_DEFS_PATH = PlatformUtil.getUserConfigDirectory() + File.separator + INTERESTING_FILES_SET_DEFS_FILE_NAME;
private static InterestingItemDefsManager instance;
/**
* Gets the interesting item definitions manager singleton.
*/
synchronized static InterestingItemDefsManager getInstance() {
if (instance == null) {
instance = new InterestingItemDefsManager();
}
return instance;
}
/**
* Gets the set of chars deemed to be illegal in file names (Windows).
*
* @return A list of characters.
*/
static List<String> getIllegalFileNameChars() {
return InterestingItemDefsManager.ILLEGAL_FILE_NAME_CHARS;
}
/**
* Gets the set of chars deemed to be illegal in file path (SleuthKit/Windows).
*
* @return A list of characters.
*/
static List<String> getIllegalFilePathChars() {
return InterestingItemDefsManager.ILLEGAL_FILE_PATH_CHARS;
}
/**
* Gets a copy of the current interesting files set definitions.
*
* @return A map of interesting files set names to interesting file sets,
* possibly empty.
*/
synchronized Map<String, FilesSet> getInterestingFilesSets() {
return FilesSetXML.readDefinitionsFile(DEFAULT_FILE_SET_DEFS_PATH);
}
/**
* Sets the current interesting file sets definitions, replacing any
* previous definitions.
*
* @param filesSets A mapping of interesting files set names to files sets,
* used to enforce unique files set names.
*/
synchronized void setInterestingFilesSets(Map<String, FilesSet> filesSets) {
FilesSetXML.writeDefinitionsFile(DEFAULT_FILE_SET_DEFS_PATH, filesSets);
this.setChanged();
this.notifyObservers();
}
/**
* Reads and writes interesting files set definitions to and from disk in
* XML format.
*/
private final static class FilesSetXML {
private static final Logger logger = Logger.getLogger(FilesSetXML.class.getName());
private static final String XML_ENCODING = "UTF-8"; //NON-NLS
private static final List<String> illegalFileNameChars = InterestingItemDefsManager.getIllegalFileNameChars();
// The following tags and attributes are identical to those used in the
// TSK Framework interesting files set definitions file schema.
private static final String FILE_SETS_ROOT_TAG = "INTERESTING_FILE_SETS"; //NON-NLS
private static final String FILE_SET_TAG = "INTERESTING_FILE_SET"; //NON-NLS
private static final String NAME_RULE_TAG = "NAME"; //NON-NLS
private static final String EXTENSION_RULE_TAG = "EXTENSION"; //NON-NLS
private static final String NAME_ATTR = "name"; //NON-NLS
private static final String DESC_ATTR = "description"; //NON-NLS
private static final String IGNORE_KNOWN_FILES_ATTR = "ignoreKnown"; //NON-NLS
private static final String TYPE_FILTER_ATTR = "typeFilter"; //NON-NLS
private static final String PATH_FILTER_ATTR = "pathFilter"; //NON-NLS
private static final String TYPE_FILTER_VALUE_FILES = "file"; //NON-NLS
private static final String TYPE_FILTER_VALUE_DIRS = "dir"; //NON-NLS
// The following tags and attributes are currently specific to the
// Autopsy implementation of interesting files set definitions. Autopsy
// definitions that use these will not be able to be used by TSK
// Framework. However, Autopsy can accept TSK Framework definitions:
//
// 1. Rules do not have names in the TSK Framework schema, but rules do
// have names in the Autopsy schema. Names will be synthesized as needed
// to allow Autopsy to use TSK Framework interesting files set
// definitions.
// 2. The TSK Framework has an interesting files module that supports
// simple globbing with "*" characters. Name rules and path filters with
// "*" characters will be converted to regexes to allow Autopsy to use
// TSK Framework interesting files set definitions.
// 3. Type filters are required by Autopsy, but not by TSK Frmaework.
// Missing type filters will defualt to "files" filters.
private static final String REGEX_ATTR = "regex"; //NON-NLS
private static final String PATH_REGEX_ATTR = "pathRegex"; //NON-NLS
private static final String TYPE_FILTER_VALUE_FILES_AND_DIRS = "files_and_dirs"; //NON-NLS
private static final String UNNAMED_LEGACY_RULE_PREFIX = "Unnamed Rule "; // NON-NLS
private static int unnamedLegacyRuleCounter;
/**
* Reads interesting file set definitions from an XML file.
*
* @param filePath Path of the set definitions file as a string.
* @return The set definitions in a map of set names to sets.
*/
// Note: This method takes a file path to support the possibility of
// multiple intersting files set definition files, e.g., one for
// definitions that ship with Autopsy and one for user definitions.
static Map<String, FilesSet> readDefinitionsFile(String filePath) {
Map<String, FilesSet> filesSets = new HashMap<>();
// Check if the file exists.
File defsFile = new File(filePath);
if (!defsFile.exists()) {
return filesSets;
}
// Check if the file can be read.
if (!defsFile.canRead()) {
logger.log(Level.SEVERE, "Interesting file sets definition file at {0} exists, but cannot be read", filePath); // NON-NLS
return filesSets;
}
// Parse the XML in the file.
Document doc = XMLUtil.loadDoc(FilesSetXML.class, filePath);
if (doc == null) {
logger.log(Level.SEVERE, "Failed to parse interesting file sets definition file at {0}", filePath); // NON-NLS
return filesSets;
}
// Get the root element.
Element root = doc.getDocumentElement();
if (root == null) {
logger.log(Level.SEVERE, "Failed to get root {0} element tag of interesting file sets definition file at {1}", new Object[]{FilesSetXML.FILE_SETS_ROOT_TAG, filePath}); // NON-NLS
return filesSets;
}
// Read in the files set definitions.
NodeList setElems = root.getElementsByTagName(FILE_SET_TAG);
for (int i = 0; i < setElems.getLength(); ++i) {
readFilesSet((Element) setElems.item(i), filesSets, filePath);
}
return filesSets;
}
/**
* Reads in an interesting files set.
*
* @param setElem An interesting files set XML element
* @param filesSets A collection to which the set is to be added.
* @param filePath The source file, used for error reporting.
*/
private static void readFilesSet(Element setElem, Map<String, FilesSet> filesSets, String filePath) {
// The file set must have a unique name.
String setName = setElem.getAttribute(FilesSetXML.NAME_ATTR);
if (setName.isEmpty()) {
logger.log(Level.SEVERE, "Found {0} element without required {1} attribute, ignoring malformed file set definition in interesting file sets definition file at {2}", new Object[]{FilesSetXML.FILE_SET_TAG, FilesSetXML.NAME_ATTR, filePath}); // NON-NLS
return;
}
if (filesSets.containsKey(setName)) {
logger.log(Level.SEVERE, "Found duplicate definition of set named {0} in interesting file sets definition file at {1}, discarding duplicate set", new Object[]{setName, filePath}); // NON-NLS
return;
}
// The file set may have a description. The empty string is o.k.
String description = setElem.getAttribute(FilesSetXML.DESC_ATTR);
// The file set may or may not ignore known files. The default behavior
// is to not ignore them.
String ignoreKnown = setElem.getAttribute(FilesSetXML.IGNORE_KNOWN_FILES_ATTR);
boolean ignoreKnownFiles = false;
if (!ignoreKnown.isEmpty()) {
ignoreKnownFiles = Boolean.parseBoolean(ignoreKnown);
}
// Read file name set membership rules, if any.
FilesSetXML.unnamedLegacyRuleCounter = 1;
Map<String, FilesSet.Rule> rules = new HashMap<>();
NodeList nameRuleElems = setElem.getElementsByTagName(FilesSetXML.NAME_RULE_TAG);
for (int j = 0; j < nameRuleElems.getLength(); ++j) {
Element elem = (Element) nameRuleElems.item(j);
FilesSet.Rule rule = FilesSetXML.readFileNameRule(elem);
if (rule != null) {
if (!rules.containsKey(rule.getName())) {
rules.put(rule.getName(), rule);
} else {
logger.log(Level.SEVERE, "Found duplicate rule {0} for set named {1} in interesting file sets definition file at {2}, discarding malformed set", new Object[]{rule.getName(), setName, filePath}); // NON-NLS
return;
}
} else {
logger.log(Level.SEVERE, "Found malformed rule for set named {0} in interesting file sets definition file at {1}, discarding malformed set", new Object[]{setName, filePath}); // NON-NLS
return;
}
}
// Read file extension set membership rules, if any.
NodeList extRuleElems = setElem.getElementsByTagName(FilesSetXML.EXTENSION_RULE_TAG);
for (int j = 0; j < extRuleElems.getLength(); ++j) {
Element elem = (Element) extRuleElems.item(j);
FilesSet.Rule rule = FilesSetXML.readFileExtensionRule(elem);
if (rule != null) {
if (!rules.containsKey(rule.getName())) {
rules.put(rule.getName(), rule);
} else {
logger.log(Level.SEVERE, "Found duplicate rule {0} for set named {1} in interesting file sets definition file at {2}, discarding malformed set", new Object[]{rule.getName(), setName, filePath});
return;
}
} else {
logger.log(Level.SEVERE, "Found malformed rule for set named {0} in interesting file sets definition file at {1}, discarding malformed set", new Object[]{setName, filePath});
return;
}
}
// Make the files set. Note that degenerate sets with no rules are
// allowed to facilitate the separation of set definition and rule
// definitions. A set without rules is simply the empty set.
FilesSet set = new FilesSet(setName, description, ignoreKnownFiles, rules);
filesSets.put(set.getName(), set);
}
/**
* Construct an interesting files set file name rule from the data in an
* XML element.
*
* @param filePath The path of the definitions file.
* @param setName The name of the files set.
* @param elem The file name rule XML element.
* @return A file name rule, or null if there is an error (the error is
* logged).
*/
private static FilesSet.Rule readFileNameRule(Element elem) {
String ruleName = FilesSetXML.readRuleName(elem);
// The content of the rule tag is a file name filter. It may be a
// regex, or it may be from a TSK Framework rule definition with a
// "*" globbing char, or it may be simple text.
String content = elem.getTextContent();
FilesSet.Rule.FullNameFilter nameFilter;
String regex = elem.getAttribute(FilesSetXML.REGEX_ATTR);
if ((!regex.isEmpty() && regex.equalsIgnoreCase("true")) || content.contains("*")) { // NON_NLS
Pattern pattern = compileRegex(content);
if (pattern != null) {
nameFilter = new FilesSet.Rule.FullNameFilter(pattern);
} else {
logger.log(Level.SEVERE, "Error compiling " + FilesSetXML.NAME_RULE_TAG + " regex, ignoring malformed '{0}' rule definition", ruleName); // NON-NLS
return null;
}
} else {
for (String illegalChar : illegalFileNameChars) {
if (content.contains(illegalChar)) {
logger.log(Level.SEVERE, FilesSetXML.NAME_RULE_TAG + " content has illegal chars, ignoring malformed '{0}' rule definition", new Object[]{FilesSetXML.NAME_RULE_TAG, ruleName}); // NON-NLS
return null;
}
}
nameFilter = new FilesSet.Rule.FullNameFilter(content);
}
// Read in the type filter.
FilesSet.Rule.MetaTypeFilter metaTypeFilter = FilesSetXML.readMetaTypeFilter(elem);
if (metaTypeFilter == null) {
// Malformed attribute.
return null;
}
// Read in the optional path filter. Null is o.k., but if the attribute
// is there, be sure it is not malformed.
FilesSet.Rule.ParentPathFilter pathFilter = null;
if (!elem.getAttribute(FilesSetXML.PATH_FILTER_ATTR).isEmpty()
|| !elem.getAttribute(FilesSetXML.PATH_REGEX_ATTR).isEmpty()) {
pathFilter = FilesSetXML.readPathFilter(elem);
if (pathFilter == null) {
// Malformed attribute.
return null;
}
}
return new FilesSet.Rule(ruleName, nameFilter, metaTypeFilter, pathFilter);
}
/**
* Construct an interesting files set file name extension rule from the
* data in an XML element.
*
* @param elem The file name extension rule XML element.
* @return A file name extension rule, or null if there is an error (the
* error is logged).
*/
private static FilesSet.Rule readFileExtensionRule(Element elem) {
String ruleName = FilesSetXML.readRuleName(elem);
// The content of the rule tag is a file name extension filter. It may
// be a regex, or it may be from a TSK Framework rule definition
// with a "*" globbing char.
String content = elem.getTextContent();
FilesSet.Rule.ExtensionFilter extFilter;
String regex = elem.getAttribute(FilesSetXML.REGEX_ATTR);
if ((!regex.isEmpty() && regex.equalsIgnoreCase("true")) || content.contains("*")) { // NON_NLS
Pattern pattern = compileRegex(content);
if (pattern != null) {
extFilter = new FilesSet.Rule.ExtensionFilter(pattern);
} else {
logger.log(Level.SEVERE, "Error compiling " + FilesSetXML.EXTENSION_RULE_TAG + " regex, ignoring malformed {0} rule definition", ruleName); // NON-NLS
return null;
}
} else {
for (String illegalChar : illegalFileNameChars) {
if (content.contains(illegalChar)) {
logger.log(Level.SEVERE, "{0} content has illegal chars, ignoring malformed {1} rule definition", ruleName); // NON-NLS
return null;
}
}
extFilter = new FilesSet.Rule.ExtensionFilter(content);
}
// The rule must have a meta-type filter, unless a TSK Framework
// definitions file is being read.
FilesSet.Rule.MetaTypeFilter metaTypeFilter = null;
if (!elem.getAttribute(FilesSetXML.TYPE_FILTER_ATTR).isEmpty()) {
metaTypeFilter = FilesSetXML.readMetaTypeFilter(elem);
if (metaTypeFilter == null) {
// Malformed attribute.
return null;
}
} else {
metaTypeFilter = new FilesSet.Rule.MetaTypeFilter(FilesSet.Rule.MetaTypeFilter.Type.FILES);
}
// The rule may have a path filter. Null is o.k., but if the attribute
// is there, it must not be malformed.
FilesSet.Rule.ParentPathFilter pathFilter = null;
if (!elem.getAttribute(FilesSetXML.PATH_FILTER_ATTR).isEmpty()
|| !elem.getAttribute(FilesSetXML.PATH_REGEX_ATTR).isEmpty()) {
pathFilter = FilesSetXML.readPathFilter(elem);
if (pathFilter == null) {
// Malformed attribute.
return null;
}
}
return new FilesSet.Rule(ruleName, extFilter, metaTypeFilter, pathFilter);
}
/**
* Read a rule name attribute from a rule element.
*
* @param elem A rule element.
* @return A rule name.
*/
private static String readRuleName(Element elem) {
// The rule must have a name.
String ruleName = elem.getAttribute(FilesSetXML.NAME_ATTR);
if (ruleName.isEmpty()) {
// Assume a TSK Framework definitions file is being read and
// synthesize a rule name.
ruleName = UNNAMED_LEGACY_RULE_PREFIX + Integer.toString(FilesSetXML.unnamedLegacyRuleCounter++);
}
return ruleName;
}
/**
* Attempts to compile a regular expression.
*
* @param regex The regular expression.
* @return A pattern object, or null if the compilation fails.
*/
private static Pattern compileRegex(String regex) {
try {
return Pattern.compile(regex);
} catch (PatternSyntaxException ex) {
logger.log(Level.SEVERE, "Error compiling rule regex: " + ex.getMessage(), ex); // NON-NLS
return null;
}
}
/**
* Construct a meta-type filter for an interesting files set membership
* rule from data in an XML element.
*
* @param ruleElement The XML element.
* @return The meta-type filter, or null if there is an error (logged).
*/
private static FilesSet.Rule.MetaTypeFilter readMetaTypeFilter(Element ruleElement) {
FilesSet.Rule.MetaTypeFilter filter = null;
String filterAttribute = ruleElement.getAttribute(FilesSetXML.TYPE_FILTER_ATTR);
if (!filterAttribute.isEmpty()) {
switch (filterAttribute) {
case FilesSetXML.TYPE_FILTER_VALUE_FILES:
filter = new FilesSet.Rule.MetaTypeFilter(FilesSet.Rule.MetaTypeFilter.Type.FILES);
break;
case FilesSetXML.TYPE_FILTER_VALUE_DIRS:
filter = new FilesSet.Rule.MetaTypeFilter(FilesSet.Rule.MetaTypeFilter.Type.DIRECTORIES);
break;
case FilesSetXML.TYPE_FILTER_VALUE_FILES_AND_DIRS:
filter = new FilesSet.Rule.MetaTypeFilter(FilesSet.Rule.MetaTypeFilter.Type.FILES_AND_DIRECTORIES);
break;
default:
logger.log(Level.SEVERE, "Found {0} " + FilesSetXML.TYPE_FILTER_ATTR + " attribute with unrecognized value ''{0}'', ignoring malformed rule definition", filterAttribute); // NON-NLS
break;
}
} else {
// Accept TSK Framework interesting files set definitions,
// default to files.
filter = new FilesSet.Rule.MetaTypeFilter(FilesSet.Rule.MetaTypeFilter.Type.FILES);
}
return filter;
}
/**
* Construct a path filter for an interesting files set membership rule
* from data in an XML element.
*
* @param ruleElement The XML element.
* @return The path filter, or null if there is an error (logged).
*/
private static FilesSet.Rule.ParentPathFilter readPathFilter(Element ruleElement) {
FilesSet.Rule.ParentPathFilter filter = null;
String path = ruleElement.getAttribute(FilesSetXML.PATH_FILTER_ATTR);
String pathRegex = ruleElement.getAttribute(FilesSetXML.PATH_REGEX_ATTR);
if (!pathRegex.isEmpty() && path.isEmpty()) {
try {
Pattern pattern = Pattern.compile(pathRegex);
filter = new FilesSet.Rule.ParentPathFilter(pattern);
} catch (PatternSyntaxException ex) {
logger.log(Level.SEVERE, "Error compiling " + FilesSetXML.PATH_REGEX_ATTR + " regex, ignoring malformed path filter definition", ex); // NON-NLS
}
} else if (!path.isEmpty() && pathRegex.isEmpty()) {
filter = new FilesSet.Rule.ParentPathFilter(path);
}
return filter;
}
/**
* Writes interesting files set definitions to disk as an XML file,
* logging any errors.
*
* @param filePath Path of the set definitions file as a string.
* @returns True if the definitions are written to disk, false
* otherwise.
*/
// Note: This method takes a file path to support the possibility of
// multiple intersting files set definition files, e.g., one for
// definitions that ship with Autopsy and one for user definitions.
static boolean writeDefinitionsFile(String filePath, Map<String, FilesSet> interestingFilesSets) {
DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
try {
// Create the new XML document.
DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
Document doc = docBuilder.newDocument();
Element rootElement = doc.createElement(FilesSetXML.FILE_SETS_ROOT_TAG);
doc.appendChild(rootElement);
// Add the interesting files sets to the document.
for (FilesSet set : interestingFilesSets.values()) {
// Add the files set element and its attributes.
Element setElement = doc.createElement(FilesSetXML.FILE_SET_TAG);
setElement.setAttribute(FilesSetXML.NAME_ATTR, set.getName());
setElement.setAttribute(FilesSetXML.DESC_ATTR, set.getDescription());
setElement.setAttribute(FilesSetXML.IGNORE_KNOWN_FILES_ATTR, Boolean.toString(set.ignoresKnownFiles()));
// Add the child elements for the set membership rules.
for (FilesSet.Rule rule : set.getRules().values()) {
// Add a rule element with the appropriate name filter
// type tag.
FilesSet.Rule.FileNameFilter nameFilter = rule.getFileNameFilter();
Element ruleElement;
if (nameFilter instanceof FilesSet.Rule.FullNameFilter) {
ruleElement = doc.createElement(FilesSetXML.NAME_RULE_TAG);
} else {
ruleElement = doc.createElement(FilesSetXML.EXTENSION_RULE_TAG);
}
// Add the rule name attribute.
ruleElement.setAttribute(FilesSetXML.NAME_ATTR, rule.getName());
// Add the name filter regex attribute
ruleElement.setAttribute(FilesSetXML.REGEX_ATTR, Boolean.toString(nameFilter.isRegex()));
// Add the type filter attribute.
FilesSet.Rule.MetaTypeFilter typeFilter = rule.getMetaTypeFilter();
switch (typeFilter.getMetaType()) {
case FILES:
ruleElement.setAttribute(FilesSetXML.TYPE_FILTER_ATTR, FilesSetXML.TYPE_FILTER_VALUE_FILES);
break;
case DIRECTORIES:
ruleElement.setAttribute(FilesSetXML.TYPE_FILTER_ATTR, FilesSetXML.TYPE_FILTER_VALUE_DIRS);
break;
default:
ruleElement.setAttribute(FilesSetXML.TYPE_FILTER_ATTR, FilesSetXML.TYPE_FILTER_VALUE_FILES_AND_DIRS);
break;
}
// Add the optional path filter.
FilesSet.Rule.ParentPathFilter pathFilter = rule.getPathFilter();
if (pathFilter != null) {
if (pathFilter.isRegex()) {
ruleElement.setAttribute(FilesSetXML.PATH_REGEX_ATTR, pathFilter.getTextToMatch());
} else {
ruleElement.setAttribute(FilesSetXML.PATH_FILTER_ATTR, pathFilter.getTextToMatch());
}
}
// Add the name filter text as the rule element content.
ruleElement.setTextContent(nameFilter.getTextToMatch());
setElement.appendChild(ruleElement);
}
rootElement.appendChild(setElement);
}
// Overwrite the previous definitions file. Note that the utility
// method logs an error on failure.
return XMLUtil.saveDoc(FilesSetXML.class, filePath, XML_ENCODING, doc);
} catch (ParserConfigurationException ex) {
logger.log(Level.SEVERE, "Error writing interesting files definition file to " + filePath, ex); // NON-NLS
return false;
}
}
}
}
@@ -0,0 +1,110 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014 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.modules.interestingitems;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import javax.swing.JComponent;
import javax.swing.SwingUtilities;
import org.netbeans.spi.options.OptionsPanelController;
import org.openide.util.HelpCtx;
import org.openide.util.Lookup;
@OptionsPanelController.TopLevelRegistration(
categoryName = "#OptionsCategory_Name_InterestingItemDefinitions",
iconBase = "org/sleuthkit/autopsy/images/interesting_item_32x32.png",
keywords = "#OptionsCategory_Keywords_InterestingItemDefinitions",
keywordsCategory = "InterestingItemDefinitions",
position = 5
)
@org.openide.util.NbBundle.Messages({"OptionsCategory_Name_InterestingItemDefinitions=Interesting Item Definitions", "OptionsCategory_Keywords_InterestingItemDefinitions=InterestingItemDefinitions"})
public final class InterestingItemDefsOptionsPanelController extends OptionsPanelController {
private InterestingItemDefsPanel panel;
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
private boolean changed;
@Override
public void update() {
getPanel().load();
changed = false;
}
@Override
public void applyChanges() {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
getPanel().store();
changed = false;
}
});
}
@Override
public void cancel() {
// need not do anything special, if no changes have been persisted yet
}
@Override
public boolean isValid() {
return true;
}
@Override
public boolean isChanged() {
return changed;
}
@Override
public HelpCtx getHelpCtx() {
return null;
}
@Override
public JComponent getComponent(Lookup masterLookup) {
return getPanel();
}
@Override
public void addPropertyChangeListener(PropertyChangeListener l) {
pcs.addPropertyChangeListener(l);
}
@Override
public void removePropertyChangeListener(PropertyChangeListener l) {
pcs.removePropertyChangeListener(l);
}
private InterestingItemDefsPanel getPanel() {
if (panel == null) {
panel = new InterestingItemDefsPanel();
}
return panel;
}
void changed() {
if (!changed) {
changed = true;
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, false, true);
}
pcs.firePropertyChange(OptionsPanelController.PROP_VALID, null, null);
}
}
@@ -0,0 +1,558 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<NonVisualComponents>
<Component class="javax.swing.ButtonGroup" name="fileNameButtonGroup">
</Component>
</NonVisualComponents>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" max="-2" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="newSetButton" linkSize="1" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="editSetButton" linkSize="1" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="deleteSetButton" linkSize="1" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="setsListLabel" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="setsListScrollPane" max="32767" attributes="0"/>
<Component id="ignoreKnownFilesCheckbox" min="-2" max="-2" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Component id="selectedSetLabel" min="-2" pref="71" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="setNameTextField" min="-2" pref="208" max="-2" attributes="0"/>
</Group>
<Component id="setDescPanel" max="32767" attributes="0"/>
</Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="separator" min="-2" pref="2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" max="-2" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="selectedRuleLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="ruleNameTextField" max="32767" attributes="0"/>
</Group>
<Component id="rulesListLabel" min="-2" max="-2" attributes="0"/>
<Component id="fileNamePanel" alignment="0" max="32767" attributes="0"/>
<Component id="rulePathPanel" alignment="0" max="32767" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="10" pref="10" max="-2" attributes="0"/>
<Component id="newRuleButton" linkSize="1" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="editRuleButton" linkSize="1" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="deleteRuleButton" linkSize="1" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="rulesListScrollPane" max="32767" attributes="0"/>
<Component id="typePanel" alignment="0" max="32767" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" max="-2" attributes="0">
<Group type="102" attributes="0">
<Group type="103" groupAlignment="3" attributes="0">
<Component id="setsListLabel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="rulesListLabel" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Component id="setsListScrollPane" min="-2" pref="199" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="selectedSetLabel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="setNameTextField" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="setDescPanel" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="ignoreKnownFilesCheckbox" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<Component id="rulesListScrollPane" min="-2" pref="122" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="selectedRuleLabel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="ruleNameTextField" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="16" max="-2" attributes="0"/>
<Component id="typePanel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="fileNamePanel" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="rulePathPanel" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="deleteRuleButton" linkSize="2" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="editRuleButton" linkSize="2" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="newRuleButton" linkSize="2" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="deleteSetButton" linkSize="2" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="editSetButton" linkSize="2" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="newSetButton" linkSize="2" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<Component id="separator" alignment="0" min="-2" pref="474" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="setsListLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties" key="InterestingItemDefsPanel.setsListLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="rulesListLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties" key="InterestingItemDefsPanel.rulesListLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Container class="javax.swing.JScrollPane" name="setsListScrollPane">
<AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JList" name="setsList">
<Properties>
<Property name="selectionMode" type="int" value="0"/>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;FilesSet&gt;"/>
</AuxValues>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JScrollPane" name="rulesListScrollPane">
<AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JList" name="rulesList">
<Properties>
<Property name="selectionMode" type="int" value="0"/>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;FilesSet.Rule&gt;"/>
</AuxValues>
</Component>
</SubComponents>
</Container>
<Component class="javax.swing.JLabel" name="selectedSetLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties" key="InterestingItemDefsPanel.selectedSetLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JCheckBox" name="ignoreKnownFilesCheckbox">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties" key="InterestingItemDefsPanel.ignoreKnownFilesCheckbox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="selectedRuleLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties" key="InterestingItemDefsPanel.selectedRuleLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="newSetButton">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/images/add16.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties" key="InterestingItemDefsPanel.newSetButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="newSetButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="editSetButton">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/images/edit16.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties" key="InterestingItemDefsPanel.editSetButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="editSetButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="deleteSetButton">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/images/delete16.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties" key="InterestingItemDefsPanel.deleteSetButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="deleteSetButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="newRuleButton">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/images/add16.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties" key="InterestingItemDefsPanel.newRuleButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="newRuleButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="editRuleButton">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/images/edit16.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties" key="InterestingItemDefsPanel.editRuleButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="editRuleButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="deleteRuleButton">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/images/delete16.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties" key="InterestingItemDefsPanel.deleteRuleButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="deleteRuleButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JSeparator" name="separator">
<Properties>
<Property name="orientation" type="int" value="1"/>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="setNameTextField">
<Properties>
<Property name="editable" type="boolean" value="false"/>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="ruleNameTextField">
<Properties>
<Property name="editable" type="boolean" value="false"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties" key="InterestingItemDefsPanel.ruleNameTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Container class="javax.swing.JPanel" name="fileNamePanel">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo">
<TitledBorder title="Name">
<ResourceString PropertyName="titleX" bundle="org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties" key="InterestingItemDefsPanel.fileNamePanel.border.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</TitledBorder>
</Border>
</Property>
</Properties>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="fileNameTextField" alignment="0" max="32767" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Component id="fileNameRadioButton" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="10" max="-2" attributes="0"/>
<Component id="fileNameExtensionRadioButton" min="-2" pref="114" max="-2" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="fileNameRegexCheckbox" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="fileNameTextField" min="-2" pref="20" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="fileNameRadioButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="fileNameExtensionRadioButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="fileNameRegexCheckbox" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JCheckBox" name="fileNameRegexCheckbox">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties" key="InterestingItemDefsPanel.fileNameRegexCheckbox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
</Component>
<Component class="javax.swing.JRadioButton" name="fileNameExtensionRadioButton">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="fileNameButtonGroup"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties" key="InterestingItemDefsPanel.fileNameExtensionRadioButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="fileNameTextField">
<Properties>
<Property name="editable" type="boolean" value="false"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties" key="InterestingItemDefsPanel.fileNameTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JRadioButton" name="fileNameRadioButton">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="fileNameButtonGroup"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties" key="InterestingItemDefsPanel.fileNameRadioButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="rulePathPanel">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo">
<TitledBorder title="Path">
<ResourceString PropertyName="titleX" bundle="org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties" key="InterestingItemDefsPanel.rulePathPanel.border.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</TitledBorder>
</Border>
</Property>
</Properties>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="rulePathFilterRegexCheckBox" min="-2" max="-2" attributes="0"/>
<Component id="rulePathFilterTextField" alignment="0" min="-2" pref="283" max="-2" attributes="0"/>
</Group>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="rulePathFilterTextField" min="-2" pref="20" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="rulePathFilterRegexCheckBox" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JCheckBox" name="rulePathFilterRegexCheckBox">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties" key="InterestingItemDefsPanel.rulePathFilterRegexCheckBox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="rulePathFilterTextField">
<Properties>
<Property name="editable" type="boolean" value="false"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties" key="InterestingItemDefsPanel.rulePathFilterTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="setDescPanel">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo">
<TitledBorder title="Description">
<ResourceString PropertyName="titleX" bundle="org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties" key="InterestingItemDefsPanel.setDescPanel.border.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</TitledBorder>
</Border>
</Property>
</Properties>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="setDescScrollPanel" min="-2" pref="296" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<EmptySpace max="32767" attributes="0"/>
<Component id="setDescScrollPanel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javax.swing.JScrollPane" name="setDescScrollPanel">
<AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JTextArea" name="setDescriptionTextArea">
<Properties>
<Property name="editable" type="boolean" value="false"/>
<Property name="columns" type="int" value="20"/>
<Property name="rows" type="int" value="5"/>
</Properties>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="typePanel">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo">
<TitledBorder title="Search for:">
<ResourceString PropertyName="titleX" bundle="org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties" key="InterestingItemDefsPanel.typePanel.border.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</TitledBorder>
</Border>
</Property>
</Properties>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="filesRadioButton" min="-2" max="-2" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="dirsRadioButton" min="-2" max="-2" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="bothRadioButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="filesRadioButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="dirsRadioButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="bothRadioButton" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JRadioButton" name="filesRadioButton">
<Properties>
<Property name="selected" type="boolean" value="true"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties" key="InterestingItemDefsPanel.filesRadioButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
</Component>
<Component class="javax.swing.JRadioButton" name="dirsRadioButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties" key="InterestingItemDefsPanel.dirsRadioButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
</Component>
<Component class="javax.swing.JRadioButton" name="bothRadioButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties" key="InterestingItemDefsPanel.bothRadioButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Form>
@@ -0,0 +1,793 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014 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.modules.interestingitems;
import java.awt.EventQueue;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import javax.swing.DefaultListModel;
import javax.swing.JOptionPane;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.corecomponents.OptionsPanel;
import org.sleuthkit.autopsy.ingest.IngestModuleGlobalSettingsPanel;
/**
* A panel that allows a user to make interesting item definitions.
*/
final class InterestingItemDefsPanel extends IngestModuleGlobalSettingsPanel implements OptionsPanel {
private final DefaultListModel<FilesSet> setsListModel = new DefaultListModel<>();
private final DefaultListModel<FilesSet.Rule> rulesListModel = new DefaultListModel<>();
// The following is a map of interesting files set names to interesting
// files set definitions. It is a snapshot of the files set definitions
// obtained from the interesting item definitions manager at the time the
// the panel is loaded. When the panel saves or stores its settings, these
// definitions, possibly changed, are submitted back to the interesting item
// definitions manager. Note that it is a tree map to aid in displaying
// files sets in sorted order by name.
private TreeMap<String, FilesSet> filesSets;
/**
* Constructs an interesting item definitions panel.
*/
InterestingItemDefsPanel() {
this.initComponents();
this.setsList.setModel(setsListModel);
this.setsList.addListSelectionListener(new InterestingItemDefsPanel.SetsListSelectionListener());
this.rulesList.setModel(rulesListModel);
this.rulesList.addListSelectionListener(new InterestingItemDefsPanel.RulesListSelectionListener());
}
/**
* @inheritDoc
*/
@Override
public void saveSettings() {
InterestingItemDefsManager.getInstance().setInterestingFilesSets(this.filesSets);
}
/**
* @inheritDoc
*/
@Override
public void store() {
this.saveSettings();
}
/**
* @inheritDoc
*/
@Override
public void load() {
this.resetComponents();
// Get a working copy of the interesting files set definitions and sort
// by set name.
this.filesSets = new TreeMap<>(InterestingItemDefsManager.getInstance().getInterestingFilesSets());
// Populate the list model for the interesting files sets list
// component.
for (FilesSet set : this.filesSets.values()) {
this.setsListModel.addElement(set);
}
if (!this.filesSets.isEmpty()) {
// Select the first files set by default. The list selections
// listeners will then populate the other components.
EventQueue.invokeLater(() -> {
InterestingItemDefsPanel.this.setsList.setSelectedIndex(0);
});
}
}
/**
* Clears the list models and resets all of the components.
*/
private void resetComponents() {
this.resetRuleComponents();
this.setsListModel.clear();
this.setNameTextField.setText("");
this.setDescriptionTextArea.setText("");
this.ignoreKnownFilesCheckbox.setSelected(true);
this.newSetButton.setEnabled(true);
this.editSetButton.setEnabled(false);
this.deleteSetButton.setEnabled(false);
}
/**
* Clears the rules list model and resets all of the rule-related
* components.
*/
private void resetRuleComponents() {
this.ruleNameTextField.setText("");
this.fileNameTextField.setText("");
this.fileNameRadioButton.setSelected(true);
this.fileNameRegexCheckbox.setSelected(false);
this.filesRadioButton.setSelected(true);
this.rulePathFilterTextField.setText("");
this.rulePathFilterRegexCheckBox.setSelected(false);
this.newRuleButton.setEnabled(!this.setsListModel.isEmpty());
this.editRuleButton.setEnabled(false);
this.deleteRuleButton.setEnabled(false);
}
/**
* A list events listener for the interesting files sets list component.
*/
private final class SetsListSelectionListener implements ListSelectionListener {
@Override
public void valueChanged(ListSelectionEvent e) {
if (e.getValueIsAdjusting()) {
return;
}
InterestingItemDefsPanel.this.rulesListModel.clear();
InterestingItemDefsPanel.this.resetRuleComponents();
// Get the selected interesting files set and populate the set
// components.
FilesSet selectedSet = InterestingItemDefsPanel.this.setsList.getSelectedValue();
if (selectedSet != null) {
// Populate the components that display the properties of the
// selected files set.
InterestingItemDefsPanel.this.setNameTextField.setText(selectedSet.getName());
InterestingItemDefsPanel.this.setDescriptionTextArea.setText(selectedSet.getDescription());
InterestingItemDefsPanel.this.ignoreKnownFilesCheckbox.setSelected(selectedSet.ignoresKnownFiles());
// Enable the new, edit and delete set buttons.
InterestingItemDefsPanel.this.newSetButton.setEnabled(true);
InterestingItemDefsPanel.this.editSetButton.setEnabled(true);
InterestingItemDefsPanel.this.deleteSetButton.setEnabled(true);
// Populate the rule definitions list, sorted by name.
TreeMap<String, FilesSet.Rule> rules = new TreeMap<>(selectedSet.getRules());
for (FilesSet.Rule rule : rules.values()) {
InterestingItemDefsPanel.this.rulesListModel.addElement(rule);
}
// Select the first rule by default.
if (!InterestingItemDefsPanel.this.rulesListModel.isEmpty()) {
InterestingItemDefsPanel.this.rulesList.setSelectedIndex(0);
}
}
}
}
/**
* A list events listener for the interesting files set rules list
* component.
*/
private final class RulesListSelectionListener implements ListSelectionListener {
@Override
public void valueChanged(ListSelectionEvent e) {
if (e.getValueIsAdjusting()) {
return;
}
// Get the selected rule and populate the rule components.
FilesSet.Rule rule = InterestingItemDefsPanel.this.rulesList.getSelectedValue();
if (rule != null) {
// Get the filters that make up the rule.
FilesSet.Rule.FileNameFilter nameFilter = rule.getFileNameFilter();
FilesSet.Rule.MetaTypeFilter typeFilter = rule.getMetaTypeFilter();
FilesSet.Rule.ParentPathFilter pathFilter = rule.getPathFilter();
// Populate the components that display the properties of the
// selected rule.
InterestingItemDefsPanel.this.ruleNameTextField.setText(rule.getName());
InterestingItemDefsPanel.this.fileNameTextField.setText(nameFilter.getTextToMatch());
InterestingItemDefsPanel.this.fileNameRadioButton.setSelected(nameFilter instanceof FilesSet.Rule.FullNameFilter);
InterestingItemDefsPanel.this.fileNameExtensionRadioButton.setSelected(nameFilter instanceof FilesSet.Rule.ExtensionFilter);
InterestingItemDefsPanel.this.fileNameRegexCheckbox.setSelected(nameFilter.isRegex());
switch (typeFilter.getMetaType()) {
case FILES:
InterestingItemDefsPanel.this.filesRadioButton.setSelected(true);
break;
case DIRECTORIES:
InterestingItemDefsPanel.this.dirsRadioButton.setSelected(true);
break;
case FILES_AND_DIRECTORIES:
InterestingItemDefsPanel.this.bothRadioButton.setSelected(true);
break;
}
if (pathFilter != null) {
InterestingItemDefsPanel.this.rulePathFilterTextField.setText(pathFilter.getTextToMatch());
InterestingItemDefsPanel.this.rulePathFilterRegexCheckBox.setSelected(pathFilter.isRegex());
} else {
InterestingItemDefsPanel.this.rulePathFilterTextField.setText("");
InterestingItemDefsPanel.this.rulePathFilterRegexCheckBox.setSelected(false);
}
// Enable the new, edit and delete rule buttons.
InterestingItemDefsPanel.this.newRuleButton.setEnabled(true);
InterestingItemDefsPanel.this.editRuleButton.setEnabled(true);
InterestingItemDefsPanel.this.deleteRuleButton.setEnabled(true);
} else {
InterestingItemDefsPanel.this.resetRuleComponents();
}
}
}
/**
* Display an interesting files set definition panel in a dialog box and
* respond to user interactions with the dialog.
*
* @param selectedSet The currently selected files set, may be null to
* indicate a new interesting files set definition is to be created.
*/
private void doFileSetsDialog(FilesSet selectedSet) {
// Create a files set defintion panle.
FilesSetPanel panel;
if (selectedSet != null) {
// Editing an existing set definition.
panel = new FilesSetPanel(selectedSet);
} else {
// Creating a new set definition.
panel = new FilesSetPanel();
}
// Do a dialog box with the files set panel until the user either enters
// a valid definition or cancels. Note that the panel gives the user
// feedback when isValidDefinition() is called.
int option = JOptionPane.OK_OPTION;
do {
option = JOptionPane.showConfirmDialog(null, panel, NbBundle.getMessage(FilesSetPanel.class, "FilesSetPanel.title"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE);
} while (option == JOptionPane.OK_OPTION && !panel.isValidDefinition());
if (option == JOptionPane.OK_OPTION) {
Map<String, FilesSet.Rule> rules = new HashMap<>();
if (selectedSet != null) {
// Interesting file sets are immutable for thread safety,
// so editing a files set definition is a replacement operation.
// Preserve the existing rules from the set being edited.
rules.putAll(selectedSet.getRules());
}
this.replaceFilesSet(selectedSet, panel.getFilesSetName(), panel.getFilesSetDescription(), panel.getFileSetIgnoresKnownFiles(), rules);
}
}
/**
* Display an interesting files set membership rule definition panel in a
* dialog box and respond to user interactions with the dialog.
*
* @param selectedRule The currently selected rule, may be null to indicate
* a new rule definition is to be created.
*/
private void doFilesSetRuleDialog(FilesSet.Rule selectedRule) {
// Create a files set rule panel.
FilesSetRulePanel panel;
if (selectedRule != null) {
// Editing an existing rule definition.
panel = new FilesSetRulePanel(selectedRule);
} else {
// Creating a new rule definition.
panel = new FilesSetRulePanel();
}
// Do a dialog box with the files set panel until the user either enters
// a valid definition or cancels. Note that the panel gives the user
// feedback when isValidDefinition() is called.
int option = JOptionPane.OK_OPTION;
do {
option = JOptionPane.showConfirmDialog(null, panel, NbBundle.getMessage(FilesSetRulePanel.class, "FilesSetRulePanel.title"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE);
} while (option == JOptionPane.OK_OPTION && !panel.isValidRuleDefinition());
if (option == JOptionPane.OK_OPTION) {
// Interesting file sets are immutable for thread safety,
// so editing a files set rule definition is a replacement
// operation. Preserve the existing rules from the set being edited.
FilesSet selectedSet = this.setsList.getSelectedValue();
Map<String, FilesSet.Rule> rules = new HashMap<>(selectedSet.getRules());
// Remove the "old" rule definition and add the new/edited
// definition.
if (selectedRule != null) {
rules.remove(selectedRule.getName());
}
FilesSet.Rule newRule = new FilesSet.Rule(panel.getRuleName(), panel.getFileNameFilter(), panel.getMetaTypeFilter(), panel.getPathFilter());
rules.put(newRule.getName(), newRule);
// Add the new/edited files set definition, replacing any previous
// definition with the same name and refreshing the display.
this.replaceFilesSet(selectedSet, selectedSet.getName(), selectedSet.getDescription(), selectedSet.ignoresKnownFiles(), rules);
// Select the new/edited rule. Queue it up so it happens after the
// selection listeners react to the selection of the "new" files
// set.
EventQueue.invokeLater(() -> {
this.rulesList.setSelectedValue(newRule, true);
});
}
}
/**
* Adds an interesting files set definition to the collection of definitions
* owned by this panel. If there is a definition with the same name, it will
* be replaced, so this is an add/edit operation.
*
* @param oldSet A set to replace, null if the new set is not a replacement.
* @param name The name of the files set.
* @param description The description of the files set.
* @param ignoresKnownFiles Whether or not the files set ignores known
* files.
* @param rules The set membership rules for the set.
*/
void replaceFilesSet(FilesSet oldSet, String name, String description, boolean ignoresKnownFiles, Map<String, FilesSet.Rule> rules) {
if (oldSet != null) {
// Remove the set to be replaced from the working copy if the files
// set definitions.
this.filesSets.remove(oldSet.getName());
}
// Make the new/edited set definition and add it to the working copy of
// the files set definitions.
FilesSet newSet = new FilesSet(name, description, ignoresKnownFiles, rules);
this.filesSets.put(newSet.getName(), newSet);
// Redo the list model for the files set list component, which will make
// everything stays sorted as in the working copy tree set.
InterestingItemDefsPanel.this.setsListModel.clear();
for (FilesSet set : this.filesSets.values()) {
this.setsListModel.addElement(set);
}
// Select the new/edited files set definition in the set definitions
// list. This will cause the selection listeners to repopulate the
// subordinate components.
this.setsList.setSelectedValue(newSet, true);
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
fileNameButtonGroup = new javax.swing.ButtonGroup();
setsListLabel = new javax.swing.JLabel();
rulesListLabel = new javax.swing.JLabel();
setsListScrollPane = new javax.swing.JScrollPane();
setsList = new javax.swing.JList<FilesSet>();
rulesListScrollPane = new javax.swing.JScrollPane();
rulesList = new javax.swing.JList<FilesSet.Rule>();
selectedSetLabel = new javax.swing.JLabel();
ignoreKnownFilesCheckbox = new javax.swing.JCheckBox();
selectedRuleLabel = new javax.swing.JLabel();
newSetButton = new javax.swing.JButton();
editSetButton = new javax.swing.JButton();
deleteSetButton = new javax.swing.JButton();
newRuleButton = new javax.swing.JButton();
editRuleButton = new javax.swing.JButton();
deleteRuleButton = new javax.swing.JButton();
separator = new javax.swing.JSeparator();
setNameTextField = new javax.swing.JTextField();
ruleNameTextField = new javax.swing.JTextField();
fileNamePanel = new javax.swing.JPanel();
fileNameRegexCheckbox = new javax.swing.JCheckBox();
fileNameExtensionRadioButton = new javax.swing.JRadioButton();
fileNameTextField = new javax.swing.JTextField();
fileNameRadioButton = new javax.swing.JRadioButton();
rulePathPanel = new javax.swing.JPanel();
rulePathFilterRegexCheckBox = new javax.swing.JCheckBox();
rulePathFilterTextField = new javax.swing.JTextField();
setDescPanel = new javax.swing.JPanel();
setDescScrollPanel = new javax.swing.JScrollPane();
setDescriptionTextArea = new javax.swing.JTextArea();
typePanel = new javax.swing.JPanel();
filesRadioButton = new javax.swing.JRadioButton();
dirsRadioButton = new javax.swing.JRadioButton();
bothRadioButton = new javax.swing.JRadioButton();
org.openide.awt.Mnemonics.setLocalizedText(setsListLabel, org.openide.util.NbBundle.getMessage(InterestingItemDefsPanel.class, "InterestingItemDefsPanel.setsListLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(rulesListLabel, org.openide.util.NbBundle.getMessage(InterestingItemDefsPanel.class, "InterestingItemDefsPanel.rulesListLabel.text")); // NOI18N
setsList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
setsListScrollPane.setViewportView(setsList);
rulesList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
rulesListScrollPane.setViewportView(rulesList);
org.openide.awt.Mnemonics.setLocalizedText(selectedSetLabel, org.openide.util.NbBundle.getMessage(InterestingItemDefsPanel.class, "InterestingItemDefsPanel.selectedSetLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(ignoreKnownFilesCheckbox, org.openide.util.NbBundle.getMessage(InterestingItemDefsPanel.class, "InterestingItemDefsPanel.ignoreKnownFilesCheckbox.text")); // NOI18N
ignoreKnownFilesCheckbox.setEnabled(false);
org.openide.awt.Mnemonics.setLocalizedText(selectedRuleLabel, org.openide.util.NbBundle.getMessage(InterestingItemDefsPanel.class, "InterestingItemDefsPanel.selectedRuleLabel.text")); // NOI18N
newSetButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/add16.png"))); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(newSetButton, org.openide.util.NbBundle.getMessage(InterestingItemDefsPanel.class, "InterestingItemDefsPanel.newSetButton.text")); // NOI18N
newSetButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
newSetButtonActionPerformed(evt);
}
});
editSetButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/edit16.png"))); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(editSetButton, org.openide.util.NbBundle.getMessage(InterestingItemDefsPanel.class, "InterestingItemDefsPanel.editSetButton.text")); // NOI18N
editSetButton.setEnabled(false);
editSetButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
editSetButtonActionPerformed(evt);
}
});
deleteSetButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/delete16.png"))); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(deleteSetButton, org.openide.util.NbBundle.getMessage(InterestingItemDefsPanel.class, "InterestingItemDefsPanel.deleteSetButton.text")); // NOI18N
deleteSetButton.setEnabled(false);
deleteSetButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
deleteSetButtonActionPerformed(evt);
}
});
newRuleButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/add16.png"))); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(newRuleButton, org.openide.util.NbBundle.getMessage(InterestingItemDefsPanel.class, "InterestingItemDefsPanel.newRuleButton.text")); // NOI18N
newRuleButton.setEnabled(false);
newRuleButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
newRuleButtonActionPerformed(evt);
}
});
editRuleButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/edit16.png"))); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(editRuleButton, org.openide.util.NbBundle.getMessage(InterestingItemDefsPanel.class, "InterestingItemDefsPanel.editRuleButton.text")); // NOI18N
editRuleButton.setEnabled(false);
editRuleButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
editRuleButtonActionPerformed(evt);
}
});
deleteRuleButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/delete16.png"))); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(deleteRuleButton, org.openide.util.NbBundle.getMessage(InterestingItemDefsPanel.class, "InterestingItemDefsPanel.deleteRuleButton.text")); // NOI18N
deleteRuleButton.setEnabled(false);
deleteRuleButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
deleteRuleButtonActionPerformed(evt);
}
});
separator.setOrientation(javax.swing.SwingConstants.VERTICAL);
setNameTextField.setEditable(false);
ruleNameTextField.setEditable(false);
ruleNameTextField.setText(org.openide.util.NbBundle.getMessage(InterestingItemDefsPanel.class, "InterestingItemDefsPanel.ruleNameTextField.text")); // NOI18N
fileNamePanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(InterestingItemDefsPanel.class, "InterestingItemDefsPanel.fileNamePanel.border.title"))); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(fileNameRegexCheckbox, org.openide.util.NbBundle.getMessage(InterestingItemDefsPanel.class, "InterestingItemDefsPanel.fileNameRegexCheckbox.text")); // NOI18N
fileNameRegexCheckbox.setEnabled(false);
fileNameButtonGroup.add(fileNameExtensionRadioButton);
org.openide.awt.Mnemonics.setLocalizedText(fileNameExtensionRadioButton, org.openide.util.NbBundle.getMessage(InterestingItemDefsPanel.class, "InterestingItemDefsPanel.fileNameExtensionRadioButton.text")); // NOI18N
fileNameExtensionRadioButton.setEnabled(false);
fileNameTextField.setEditable(false);
fileNameTextField.setText(org.openide.util.NbBundle.getMessage(InterestingItemDefsPanel.class, "InterestingItemDefsPanel.fileNameTextField.text")); // NOI18N
fileNameButtonGroup.add(fileNameRadioButton);
org.openide.awt.Mnemonics.setLocalizedText(fileNameRadioButton, org.openide.util.NbBundle.getMessage(InterestingItemDefsPanel.class, "InterestingItemDefsPanel.fileNameRadioButton.text")); // NOI18N
fileNameRadioButton.setEnabled(false);
javax.swing.GroupLayout fileNamePanelLayout = new javax.swing.GroupLayout(fileNamePanel);
fileNamePanel.setLayout(fileNamePanelLayout);
fileNamePanelLayout.setHorizontalGroup(
fileNamePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(fileNamePanelLayout.createSequentialGroup()
.addContainerGap()
.addGroup(fileNamePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(fileNameTextField)
.addGroup(fileNamePanelLayout.createSequentialGroup()
.addComponent(fileNameRadioButton)
.addGap(10, 10, 10)
.addComponent(fileNameExtensionRadioButton, javax.swing.GroupLayout.PREFERRED_SIZE, 114, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(18, 18, 18)
.addComponent(fileNameRegexCheckbox)))
.addContainerGap())
);
fileNamePanelLayout.setVerticalGroup(
fileNamePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(fileNamePanelLayout.createSequentialGroup()
.addContainerGap()
.addComponent(fileNameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(fileNamePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(fileNameRadioButton)
.addComponent(fileNameExtensionRadioButton)
.addComponent(fileNameRegexCheckbox))
.addContainerGap())
);
rulePathPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(InterestingItemDefsPanel.class, "InterestingItemDefsPanel.rulePathPanel.border.title"))); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(rulePathFilterRegexCheckBox, org.openide.util.NbBundle.getMessage(InterestingItemDefsPanel.class, "InterestingItemDefsPanel.rulePathFilterRegexCheckBox.text")); // NOI18N
rulePathFilterRegexCheckBox.setEnabled(false);
rulePathFilterTextField.setEditable(false);
rulePathFilterTextField.setText(org.openide.util.NbBundle.getMessage(InterestingItemDefsPanel.class, "InterestingItemDefsPanel.rulePathFilterTextField.text")); // NOI18N
javax.swing.GroupLayout rulePathPanelLayout = new javax.swing.GroupLayout(rulePathPanel);
rulePathPanel.setLayout(rulePathPanelLayout);
rulePathPanelLayout.setHorizontalGroup(
rulePathPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(rulePathPanelLayout.createSequentialGroup()
.addContainerGap()
.addGroup(rulePathPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(rulePathFilterRegexCheckBox)
.addComponent(rulePathFilterTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 283, javax.swing.GroupLayout.PREFERRED_SIZE))
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
rulePathPanelLayout.setVerticalGroup(
rulePathPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(rulePathPanelLayout.createSequentialGroup()
.addContainerGap()
.addComponent(rulePathFilterTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(rulePathFilterRegexCheckBox)
.addContainerGap())
);
setDescPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(InterestingItemDefsPanel.class, "InterestingItemDefsPanel.setDescPanel.border.title"))); // NOI18N
setDescriptionTextArea.setEditable(false);
setDescriptionTextArea.setColumns(20);
setDescriptionTextArea.setRows(5);
setDescScrollPanel.setViewportView(setDescriptionTextArea);
javax.swing.GroupLayout setDescPanelLayout = new javax.swing.GroupLayout(setDescPanel);
setDescPanel.setLayout(setDescPanelLayout);
setDescPanelLayout.setHorizontalGroup(
setDescPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(setDescPanelLayout.createSequentialGroup()
.addContainerGap()
.addComponent(setDescScrollPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 296, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
setDescPanelLayout.setVerticalGroup(
setDescPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, setDescPanelLayout.createSequentialGroup()
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(setDescScrollPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap())
);
typePanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(InterestingItemDefsPanel.class, "InterestingItemDefsPanel.typePanel.border.title"))); // NOI18N
filesRadioButton.setSelected(true);
org.openide.awt.Mnemonics.setLocalizedText(filesRadioButton, org.openide.util.NbBundle.getMessage(InterestingItemDefsPanel.class, "InterestingItemDefsPanel.filesRadioButton.text")); // NOI18N
filesRadioButton.setEnabled(false);
org.openide.awt.Mnemonics.setLocalizedText(dirsRadioButton, org.openide.util.NbBundle.getMessage(InterestingItemDefsPanel.class, "InterestingItemDefsPanel.dirsRadioButton.text")); // NOI18N
dirsRadioButton.setEnabled(false);
org.openide.awt.Mnemonics.setLocalizedText(bothRadioButton, org.openide.util.NbBundle.getMessage(InterestingItemDefsPanel.class, "InterestingItemDefsPanel.bothRadioButton.text")); // NOI18N
bothRadioButton.setEnabled(false);
javax.swing.GroupLayout typePanelLayout = new javax.swing.GroupLayout(typePanel);
typePanel.setLayout(typePanelLayout);
typePanelLayout.setHorizontalGroup(
typePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(typePanelLayout.createSequentialGroup()
.addContainerGap()
.addComponent(filesRadioButton)
.addGap(18, 18, 18)
.addComponent(dirsRadioButton)
.addGap(18, 18, 18)
.addComponent(bothRadioButton)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
typePanelLayout.setVerticalGroup(
typePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(typePanelLayout.createSequentialGroup()
.addContainerGap()
.addGroup(typePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(filesRadioButton)
.addComponent(dirsRadioButton)
.addComponent(bothRadioButton))
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
.addGroup(layout.createSequentialGroup()
.addComponent(newSetButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(editSetButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(deleteSetButton))
.addComponent(setsListLabel)
.addComponent(setsListScrollPane)
.addComponent(ignoreKnownFilesCheckbox)
.addGroup(layout.createSequentialGroup()
.addComponent(selectedSetLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 71, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(setNameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 208, javax.swing.GroupLayout.PREFERRED_SIZE))
.addComponent(setDescPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(separator, javax.swing.GroupLayout.PREFERRED_SIZE, 2, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
.addGroup(layout.createSequentialGroup()
.addComponent(selectedRuleLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(ruleNameTextField))
.addComponent(rulesListLabel)
.addComponent(fileNamePanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(rulePathPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup()
.addGap(10, 10, 10)
.addComponent(newRuleButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(editRuleButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(deleteRuleButton))
.addComponent(rulesListScrollPane)
.addComponent(typePanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addContainerGap())
);
layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {deleteRuleButton, deleteSetButton, editRuleButton, editSetButton, newRuleButton, newSetButton});
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(setsListLabel)
.addComponent(rulesListLabel))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(setsListScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 199, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(selectedSetLabel)
.addComponent(setNameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(setDescPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(ignoreKnownFilesCheckbox))
.addGroup(layout.createSequentialGroup()
.addComponent(rulesListScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 122, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(selectedRuleLabel)
.addComponent(ruleNameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGap(16, 16, 16)
.addComponent(typePanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(fileNamePanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(rulePathPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(deleteRuleButton)
.addComponent(editRuleButton)
.addComponent(newRuleButton)
.addComponent(deleteSetButton)
.addComponent(editSetButton)
.addComponent(newSetButton)))
.addComponent(separator, javax.swing.GroupLayout.PREFERRED_SIZE, 474, javax.swing.GroupLayout.PREFERRED_SIZE))
.addContainerGap())
);
layout.linkSize(javax.swing.SwingConstants.VERTICAL, new java.awt.Component[] {deleteRuleButton, deleteSetButton, editRuleButton, editSetButton, newRuleButton, newSetButton});
}// </editor-fold>//GEN-END:initComponents
private void newSetButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_newSetButtonActionPerformed
this.doFileSetsDialog(null);
}//GEN-LAST:event_newSetButtonActionPerformed
private void editSetButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_editSetButtonActionPerformed
this.doFileSetsDialog(this.setsList.getSelectedValue());
}//GEN-LAST:event_editSetButtonActionPerformed
private void newRuleButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_newRuleButtonActionPerformed
this.doFilesSetRuleDialog(null);
}//GEN-LAST:event_newRuleButtonActionPerformed
private void editRuleButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_editRuleButtonActionPerformed
this.doFilesSetRuleDialog(this.rulesList.getSelectedValue());
}//GEN-LAST:event_editRuleButtonActionPerformed
private void deleteSetButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteSetButtonActionPerformed
FilesSet selectedSet = this.setsList.getSelectedValue();
this.filesSets.remove(selectedSet.getName());
this.setsListModel.removeElement(selectedSet);
// Select the first of the remaining set definitions. This will cause
// the selection listeners to repopulate the subordinate components.
if (!this.filesSets.isEmpty()) {
this.setsList.setSelectedIndex(0);
} else {
this.resetComponents();
}
}//GEN-LAST:event_deleteSetButtonActionPerformed
private void deleteRuleButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteRuleButtonActionPerformed
// Interesting file sets are immutable for thread safety,
// so editing a files set rule definition is a replacement
// operation. Preserve the existing rules from the set being
// edited, except for the deleted rule.
FilesSet oldSet = this.setsList.getSelectedValue();
Map<String, FilesSet.Rule> rules = new HashMap<>(oldSet.getRules());
FilesSet.Rule selectedRule = this.rulesList.getSelectedValue();
rules.remove(selectedRule.getName());
this.replaceFilesSet(oldSet, oldSet.getName(), oldSet.getDescription(), oldSet.ignoresKnownFiles(), rules);
}//GEN-LAST:event_deleteRuleButtonActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JRadioButton bothRadioButton;
private javax.swing.JButton deleteRuleButton;
private javax.swing.JButton deleteSetButton;
private javax.swing.JRadioButton dirsRadioButton;
private javax.swing.JButton editRuleButton;
private javax.swing.JButton editSetButton;
private javax.swing.ButtonGroup fileNameButtonGroup;
private javax.swing.JRadioButton fileNameExtensionRadioButton;
private javax.swing.JPanel fileNamePanel;
private javax.swing.JRadioButton fileNameRadioButton;
private javax.swing.JCheckBox fileNameRegexCheckbox;
private javax.swing.JTextField fileNameTextField;
private javax.swing.JRadioButton filesRadioButton;
private javax.swing.JCheckBox ignoreKnownFilesCheckbox;
private javax.swing.JButton newRuleButton;
private javax.swing.JButton newSetButton;
private javax.swing.JTextField ruleNameTextField;
private javax.swing.JCheckBox rulePathFilterRegexCheckBox;
private javax.swing.JTextField rulePathFilterTextField;
private javax.swing.JPanel rulePathPanel;
private javax.swing.JList<FilesSet.Rule> rulesList;
private javax.swing.JLabel rulesListLabel;
private javax.swing.JScrollPane rulesListScrollPane;
private javax.swing.JLabel selectedRuleLabel;
private javax.swing.JLabel selectedSetLabel;
private javax.swing.JSeparator separator;
private javax.swing.JPanel setDescPanel;
private javax.swing.JScrollPane setDescScrollPanel;
private javax.swing.JTextArea setDescriptionTextArea;
private javax.swing.JTextField setNameTextField;
private javax.swing.JList<FilesSet> setsList;
private javax.swing.JLabel setsListLabel;
private javax.swing.JScrollPane setsListScrollPane;
private javax.swing.JPanel typePanel;
// End of variables declaration//GEN-END:variables
}
@@ -0,0 +1,110 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014 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.modules.interestingitems;
import java.util.ArrayList;
import java.util.List;
import org.openide.util.NbBundle;
import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.coreutils.Version;
import org.sleuthkit.autopsy.ingest.FileIngestModule;
import org.sleuthkit.autopsy.ingest.IngestModuleFactory;
import org.sleuthkit.autopsy.ingest.IngestModuleFactoryAdapter;
import org.sleuthkit.autopsy.ingest.IngestModuleGlobalSettingsPanel;
import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings;
import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel;
/**
* A factory that creates ingest modules that use interesting files set
* definitions to identify files that may be of interest to the user.
*/
@ServiceProvider(service = IngestModuleFactory.class)
final public class InterestingItemsIngestModuleFactory extends IngestModuleFactoryAdapter {
@Override
public String getModuleDisplayName() {
return getModuleName();
}
static String getModuleName() {
return NbBundle.getMessage(InterestingItemsIngestModuleFactory.class, "InterestingItemsIdentifierIngestModule.moduleName");
}
@Override
public String getModuleDescription() {
return NbBundle.getMessage(InterestingItemsIngestModuleFactory.class, "InterestingItemsIdentifierIngestModule.moduleDescription");
}
@Override
public String getModuleVersionNumber() {
return Version.getVersion();
}
@Override
public boolean hasGlobalSettingsPanel() {
return true;
}
@Override
public IngestModuleGlobalSettingsPanel getGlobalSettingsPanel() {
InterestingItemDefsPanel panel = new InterestingItemDefsPanel();
panel.load();
return panel;
}
@Override
public IngestModuleIngestJobSettings getDefaultIngestJobSettings() {
// All interesting files set definitions are enabled by default. The
// names of the set definitions are stored instead of the set
// definitions to make per ingest job enabling and disabling of the
// definitions independent of the rules that make up the defintions.
// Doing so also keeps the serialization simple.
List<String> enabledFilesSetNames = new ArrayList<>();
for (String name : InterestingItemDefsManager.getInstance().getInterestingFilesSets().keySet()) {
enabledFilesSetNames.add(name);
}
return new FilesIdentifierIngestJobSettings(enabledFilesSetNames);
}
@Override
public boolean hasIngestJobSettingsPanel() {
return true;
}
@Override
public IngestModuleIngestJobSettingsPanel getIngestJobSettingsPanel(IngestModuleIngestJobSettings settings) {
if (!(settings instanceof FilesIdentifierIngestJobSettings)) {
throw new IllegalArgumentException("Settings not instanceof org.sleuthkit.autopsy.modules.interestingitems.InterestingItemsIngestJobSettings");
}
return FilesIdentifierIngestJobSettingsPanel.makePanel((FilesIdentifierIngestJobSettings) settings);
}
@Override
public boolean isFileIngestModuleFactory() {
return true;
}
@Override
public FileIngestModule createFileIngestModule(IngestModuleIngestJobSettings settings) {
if (!(settings instanceof FilesIdentifierIngestJobSettings)) {
throw new IllegalArgumentException("Settings not instanceof org.sleuthkit.autopsy.modules.interestingitems.InterestingItemsIngestJobSettings");
}
return new FilesIdentifierIngestModule((FilesIdentifierIngestJobSettings) settings);
}
}