From 9f437f8634d1a4843372f4430f9cd3fc7dfefc2a Mon Sep 17 00:00:00 2001 From: Ann Priestman Date: Wed, 29 May 2019 07:25:21 -0400 Subject: [PATCH] Adding UI --- .../autopsy/newpackage/Bundle.properties | 19 + .../newpackage/Bundle.properties-MERGED | 68 +- .../autopsy/newpackage/FileSearch.java | 41 +- .../autopsy/newpackage/FileSearchData.java | 31 +- .../autopsy/newpackage/FileSearchDialog.form | 481 ++++++++++++++ .../autopsy/newpackage/FileSearchDialog.java | 609 ++++++++++++++++++ .../newpackage/FileSearchFiltering.java | 217 ++++--- .../newpackage/FileSearchTestAction.java | 96 ++- .../autopsy/newpackage/FileSorter.java | 34 +- 9 files changed, 1442 insertions(+), 154 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/newpackage/Bundle.properties create mode 100644 Core/src/org/sleuthkit/autopsy/newpackage/FileSearchDialog.form create mode 100644 Core/src/org/sleuthkit/autopsy/newpackage/FileSearchDialog.java diff --git a/Core/src/org/sleuthkit/autopsy/newpackage/Bundle.properties b/Core/src/org/sleuthkit/autopsy/newpackage/Bundle.properties new file mode 100644 index 0000000000..a8635f2c5b --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/newpackage/Bundle.properties @@ -0,0 +1,19 @@ +FileSearchDialog.jLabel1.text=File Type +FileSearchDialog.dsCheckBox.text=Data source +FileSearchDialog.cancelButton.text=Cancel +FileSearchDialog.freqCheckBox.text=CR Frequency +FileSearchDialog.sizeCheckBox.text=Size +FileSearchDialog.kwCheckBox.text=Keyword +FileSearchDialog.addParentButton.text=Add +FileSearchDialog.deleteParentButton.text=Delete +FileSearchDialog.parentFullRadioButton.text=Full +FileSearchDialog.parentSubstringRadioButton.text=Substring +FileSearchDialog.parentTextField.text= +FileSearchDialog.jLabel2.text=(All will be used) +FileSearchDialog.jLabel3.text=Group by attribute: +FileSearchDialog.jLabel4.text=Order groups by: +FileSearchDialog.orderAttrRadioButton.text=Attribute +FileSearchDialog.orderSizeRadioButton.text=Group Size +FileSearchDialog.jLabel5.text=Order files by: +FileSearchDialog.parentCheckBox.text=Parent +FileSearchDialog.searchButton.text=Search diff --git a/Core/src/org/sleuthkit/autopsy/newpackage/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/newpackage/Bundle.properties-MERGED index 3297140c42..f9be8768f9 100644 --- a/Core/src/org/sleuthkit/autopsy/newpackage/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/newpackage/Bundle.properties-MERGED @@ -4,6 +4,13 @@ CTL_FileSearchTestAction=Test file search FileSearch.DataSourceGroupKey.datasourceAndID={0}(ID: {1}) # {0} - Data source ID FileSearch.DataSourceGroupKey.idOnly=Data source (ID: {0}) +FileSearch.GroupingAttributeType.datasource.displayName=Data source +FileSearch.GroupingAttributeType.fileType.displayName=File type +FileSearch.GroupingAttributeType.frequency.displayName=Past occurrences +FileSearch.GroupingAttributeType.keywordList.displayName=Keyword list names +FileSearch.GroupingAttributeType.none.displayName=None +FileSearch.GroupingAttributeType.parent.displayName=Parent folder +FileSearch.GroupingAttributeType.size.displayName=Size FileSearch.KeywordListGroupKey.noKeywords=None FileSearch.NoGroupingGroupKey.allFiles=All Files FileSearchData.FileSize.large.displayName=200MB-1GB @@ -21,29 +28,58 @@ FileSearchData.Frequency.common.displayName=Common FileSearchData.Frequency.rare.displayName=Rare FileSearchData.Frequency.unique.displayName=Unique FileSearchData.Frequency.unknown.displayName=Unknown +FileSearchDialog.jLabel1.text=File Type +FileSearchDialog.dsCheckBox.text=Data source +FileSearchDialog.cancelButton.text=Cancel +FileSearchDialog.freqCheckBox.text=CR Frequency +FileSearchDialog.sizeCheckBox.text=Size +FileSearchDialog.kwCheckBox.text=Keyword +FileSearchDialog.addParentButton.text=Add +FileSearchDialog.deleteParentButton.text=Delete +FileSearchDialog.parentFullRadioButton.text=Full +FileSearchDialog.parentSubstringRadioButton.text=Substring +FileSearchDialog.parentTextField.text= +FileSearchDialog.jLabel2.text=(All will be used) +FileSearchDialog.jLabel3.text=Group by attribute: +FileSearchDialog.jLabel4.text=Order groups by: +FileSearchDialog.orderAttrRadioButton.text=Attribute +FileSearchDialog.orderSizeRadioButton.text=Group Size +FileSearchDialog.jLabel5.text=Order files by: +FileSearchDialog.parentCheckBox.text=Parent +FileSearchDialog.searchButton.text=Search # {0} - Data source name # {1} - Data source ID -FileSearchFiltering.DataSourceSubFilter.datasource={0}({1}) +FileSearchFiltering.DataSourceFilter.datasource={0}({1}) # {0} - filters -FileSearchFiltering.DataSourceSubFilter.desc=Files in data source(s): {0} -FileSearchFiltering.DataSourceSubFilter.or=\ or +FileSearchFiltering.DataSourceFilter.desc=Files in data source(s): {0} +FileSearchFiltering.DataSourceFilter.or=\ or # {0} - filters -FileSearchFiltering.FileTypeSubFilter.desc=Files with type: {0} -FileSearchFiltering.FileTypeSubFilter.or=\ or +FileSearchFiltering.FileTypeFilter.desc=Files with type: {0} +FileSearchFiltering.FileTypeFilter.or=\ or # {0} - filters -FileSearchFiltering.FrequencySubFilter.desc=Files with frequency: {0} -FileSearchFiltering.FrequencySubFilter.or=\ or -FileSearchFiltering.KeywordListSubFilter.comma=, +FileSearchFiltering.FrequencyFilter.desc=Files with frequency: {0} +FileSearchFiltering.FrequencyFilter.or=\ or +FileSearchFiltering.KeywordListFilter.comma=, # {0} - filters -FileSearchFiltering.KeywordListSubFilter.desc=Files with keywords in list(s): {0} +FileSearchFiltering.KeywordListFilter.desc=Files with keywords in list(s): {0} # {0} - filters -FileSearchFiltering.ParentSubFilter.desc=Files with paths matching: {0} -FileSearchFiltering.ParentSubFilter.exact=(exact match) -FileSearchFiltering.ParentSubFilter.or=\ or -FileSearchFiltering.ParentSubFilter.substring=(substring) +FileSearchFiltering.ParentFilter.desc=Files with paths matching: {0} +FileSearchFiltering.ParentFilter.exact=(exact match) +FileSearchFiltering.ParentFilter.or=\ or +FileSearchFiltering.ParentFilter.substring=(substring) +# {0} - search term +FileSearchFiltering.ParentSearchTerm.fullString=\ {0} (exact) +FileSearchFiltering.ParentSearchTerm.subString=\ {0} (substring) # {0} - filters -FileSearchFiltering.SizeSubFilter.desc=Files with size in range(s): {0} -FileSearchFiltering.SizeSubFilter.or=\ or +FileSearchFiltering.SizeFilter.desc=Files with size in range(s): {0} +FileSearchFiltering.SizeFilter.or=\ or # {0} - Minimum bytes # {1} - Maximum bytes -FileSearchFiltering.SizeSubFilter.range=({0} to {1}) +FileSearchFiltering.SizeFilter.range=({0} to {1}) +FileSorter.SortingMethod.datasource.displayName=By data source +FileSorter.SortingMethod.filename.displayName=By file name +FileSorter.SortingMethod.filesize.displayName=By file size +FileSorter.SortingMethod.filetype.displayName=By file type +FileSorter.SortingMethod.frequency.displayName=By central repo frequency +FileSorter.SortingMethod.keywordlist.displayName=By keyword list names +FileSorter.SortingMethod.parent.displayName=By parent path diff --git a/Core/src/org/sleuthkit/autopsy/newpackage/FileSearch.java b/Core/src/org/sleuthkit/autopsy/newpackage/FileSearch.java index 2ffcce9f5e..a938c0450d 100644 --- a/Core/src/org/sleuthkit/autopsy/newpackage/FileSearch.java +++ b/Core/src/org/sleuthkit/autopsy/newpackage/FileSearch.java @@ -108,6 +108,45 @@ class FileSearch { } } + /** + * Enum for the attribute types that can be used for grouping. + */ + @NbBundle.Messages({ + "FileSearch.GroupingAttributeType.fileType.displayName=File type", + "FileSearch.GroupingAttributeType.frequency.displayName=Past occurrences", + "FileSearch.GroupingAttributeType.keywordList.displayName=Keyword list names", + "FileSearch.GroupingAttributeType.size.displayName=Size", + "FileSearch.GroupingAttributeType.datasource.displayName=Data source", + "FileSearch.GroupingAttributeType.parent.displayName=Parent folder", + "FileSearch.GroupingAttributeType.none.displayName=None", + }) + enum GroupingAttributeType { + FILE_TYPE(new FileTypeAttribute(), Bundle.FileSearch_GroupingAttributeType_fileType_displayName()), + FREQUENCY(new FrequencyAttribute(), Bundle.FileSearch_GroupingAttributeType_frequency_displayName()), + KEYWORD_LIST_NAME(new KeywordListAttribute(), Bundle.FileSearch_GroupingAttributeType_keywordList_displayName()), + FILE_SIZE(new FileSizeAttribute(), Bundle.FileSearch_GroupingAttributeType_size_displayName()), + DATA_SOURCE(new DataSourceAttribute(), Bundle.FileSearch_GroupingAttributeType_datasource_displayName()), + PARENT_PATH(new ParentPathAttribute(), Bundle.FileSearch_GroupingAttributeType_parent_displayName()), + NO_GROUPING(new NoGroupingAttribute(), Bundle.FileSearch_GroupingAttributeType_none_displayName()); + + private final AttributeType attributeType; + private final String displayName; + + GroupingAttributeType(AttributeType attributeType, String displayName) { + this.attributeType = attributeType; + this.displayName = displayName; + } + + @Override + public String toString() { + return displayName; + } + + AttributeType getAttributeType() { + return attributeType; + } + } + /** * Base class for the grouping attributes. */ @@ -403,7 +442,7 @@ class FileSearch { @Override String getDisplayName() { - return fileType.getDisplayName(); + return fileType.toString(); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/newpackage/FileSearchData.java b/Core/src/org/sleuthkit/autopsy/newpackage/FileSearchData.java index 187c71ab9e..8d57695613 100644 --- a/Core/src/org/sleuthkit/autopsy/newpackage/FileSearchData.java +++ b/Core/src/org/sleuthkit/autopsy/newpackage/FileSearchData.java @@ -19,7 +19,9 @@ package org.sleuthkit.autopsy.newpackage; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; +import java.util.List; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.datamodel.utils.FileTypeUtils; @@ -78,6 +80,15 @@ class FileSearchData { return COMMON; } + /** + * Get the list of enums that are valid for filtering. + * + * @return enums that can be used to filter + */ + static List getOptionsForFiltering() { + return Arrays.asList(UNIQUE, RARE, COMMON); + } + @Override public String toString() { return displayName; @@ -207,11 +218,16 @@ class FileSearchData { } /** - * Get the display name for this type + * Get the MIME types matching this category. * - * @return the display name + * @return Collection of MIME type strings */ - String getDisplayName() { + Collection getMediaTypes() { + return mediaTypes; + } + + @Override + public String toString() { return displayName; } @@ -239,6 +255,15 @@ class FileSearchData { } return OTHER; } + + /** + * Get the list of enums that are valid for filtering. + * + * @return enums that can be used to filter + */ + static List getOptionsForFiltering() { + return Arrays.asList(IMAGE, AUDIO, VIDEO, EXECUTABLE, DOCUMENTS); + } } private FileSearchData() { diff --git a/Core/src/org/sleuthkit/autopsy/newpackage/FileSearchDialog.form b/Core/src/org/sleuthkit/autopsy/newpackage/FileSearchDialog.form new file mode 100644 index 0000000000..31242773a6 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/newpackage/FileSearchDialog.form @@ -0,0 +1,481 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/newpackage/FileSearchDialog.java b/Core/src/org/sleuthkit/autopsy/newpackage/FileSearchDialog.java new file mode 100644 index 0000000000..c488fb4034 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/newpackage/FileSearchDialog.java @@ -0,0 +1,609 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.sleuthkit.autopsy.newpackage; + +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.newpackage.FileSearch.GroupingAttributeType; +import org.sleuthkit.autopsy.newpackage.FileSearchData.FileType; +import org.sleuthkit.autopsy.newpackage.FileSearchData.FileSize; +import org.sleuthkit.autopsy.newpackage.FileSearchData.Frequency; +import org.sleuthkit.autopsy.newpackage.FileSearchFiltering.ParentSearchTerm; +import org.sleuthkit.autopsy.newpackage.FileSorter.SortingMethod; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.DataSource; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import javax.swing.DefaultListModel; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardAttribute; + +/** + * + */ +public class FileSearchDialog extends javax.swing.JDialog { + + private DefaultListModel parentListModel; + private ButtonPressed buttonPressed = ButtonPressed.SEARCH; + + /** + * Creates new form FileSearchDialog + */ + public FileSearchDialog(java.awt.Frame parent, boolean modal) { + super(parent, modal); + initComponents(); + customizeComponents(); + } + + private void customizeComponents() { + + int count = 0; + DefaultListModel fileTypeListModel = (DefaultListModel)fileTypeList.getModel(); + for (FileType type : FileType.getOptionsForFiltering()) { + fileTypeListModel.add(count, type); + count++; + } + + SleuthkitCase skCase = Case.getCurrentCase().getSleuthkitCase(); + count = 0; + try { + DefaultListModel dsListModel = (DefaultListModel)dsList.getModel(); + for(DataSource ds : skCase.getDataSources()) { + dsListModel.add(count, new DataSourceItem(ds)); + } + } catch (Exception ex) { + dsCheckBox.setEnabled(false); + dsList.setEnabled(false); + } + + count = 0; + DefaultListModel frequencyListModel = (DefaultListModel)freqList.getModel(); + for (Frequency freq : Frequency.getOptionsForFiltering()) { + frequencyListModel.add(count, freq); + } + + count = 0; + DefaultListModel sizeListModel = (DefaultListModel)sizeList.getModel(); + for (FileSize size : FileSize.values()) { + sizeListModel.add(count, size); + } + + count = 0; + try { + DefaultListModel kwListModel = (DefaultListModel)kwList.getModel(); + + // TODO - use query + List arts = skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT); + List setNames = new ArrayList<>(); + for (BlackboardArtifact art : arts) { + for (BlackboardAttribute attr : art.getAttributes()) { + if (attr.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()) { + String setName = attr.getValueString(); + if ( ! setNames.contains(setName)) { + setNames.add(setName); + } + } + } + } + Collections.sort(setNames); + for(String name : setNames) { + kwListModel.add(count, name); + } + } catch (Exception ex) { + kwCheckBox.setEnabled(false); + kwList.setEnabled(false); + } + + parentButtonGroup.add(parentFullRadioButton); + parentButtonGroup.add(parentSubstringRadioButton); + parentFullRadioButton.setSelected(true); + parentListModel = (DefaultListModel)parentList.getModel(); + + for (GroupingAttributeType type : GroupingAttributeType.values()) { + groupComboBox.addItem(type); + } + + orderButtonGroup.add(orderAttrRadioButton); + orderButtonGroup.add(orderSizeRadioButton); + orderAttrRadioButton.setSelected(true); + + for (SortingMethod method : SortingMethod.values()) { + fileOrderComboBox.addItem(method); + } + } + + List getFilters() { + List filters = new ArrayList<>(); + + // There will always be a file type selected + filters.add(new FileSearchFiltering.FileTypeFilter(fileTypeList.getSelectedValuesList())); + + if (parentCheckBox.isSelected()) { + filters.add(new FileSearchFiltering.ParentFilter(parentList.getSelectedValuesList())); + } + + if (dsCheckBox.isSelected()) { + List dataSources = dsList.getSelectedValuesList().stream().map(t -> t.ds).collect(Collectors.toList()); + filters.add(new FileSearchFiltering.DataSourceFilter(dataSources)); + } + + if (freqCheckBox.isSelected()) { + filters.add(new FileSearchFiltering.FrequencyFilter(freqList.getSelectedValuesList())); + } + + if (sizeCheckBox.isSelected()) { + filters.add(new FileSearchFiltering.SizeFilter(sizeList.getSelectedValuesList())); + } + + if (kwCheckBox.isSelected()) { + filters.add(new FileSearchFiltering.KeywordListFilter(kwList.getSelectedValuesList())); + } + + return filters; + } + + FileSearch.AttributeType getGroupingAttribute() { + GroupingAttributeType groupingAttrType = (GroupingAttributeType)groupComboBox.getSelectedItem(); + return groupingAttrType.getAttributeType(); + } + + FileGroup.GroupSortingAlgorithm getGroupSortingMethod() { + if (orderAttrRadioButton.isSelected()) { + return FileGroup.GroupSortingAlgorithm.BY_GROUP_KEY; + } + return FileGroup.GroupSortingAlgorithm.BY_GROUP_SIZE; + } + + SortingMethod getFileSortingMethod() { + return (SortingMethod)fileOrderComboBox.getSelectedItem(); + } + + + private class DataSourceItem { + private DataSource ds; + + DataSourceItem(DataSource ds) { + this.ds = ds; + } + + @Override + public String toString() { + return ds.getName() + " (ID: " + ds.getId() + ")"; + } + } + + boolean searchCancelled() { + return buttonPressed.equals(ButtonPressed.CANCEL); + } + + private enum ButtonPressed { + SEARCH, + CANCEL + } + + /** + * 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") + // //GEN-BEGIN:initComponents + private void initComponents() { + + parentButtonGroup = new javax.swing.ButtonGroup(); + orderButtonGroup = new javax.swing.ButtonGroup(); + jLabel1 = new javax.swing.JLabel(); + cancelButton = new javax.swing.JButton(); + dsCheckBox = new javax.swing.JCheckBox(); + jScrollPane1 = new javax.swing.JScrollPane(); + fileTypeList = new javax.swing.JList<>(); + jScrollPane2 = new javax.swing.JScrollPane(); + dsList = new javax.swing.JList<>(); + freqCheckBox = new javax.swing.JCheckBox(); + jScrollPane3 = new javax.swing.JScrollPane(); + freqList = new javax.swing.JList<>(); + jScrollPane4 = new javax.swing.JScrollPane(); + sizeList = new javax.swing.JList<>(); + sizeCheckBox = new javax.swing.JCheckBox(); + jScrollPane5 = new javax.swing.JScrollPane(); + kwList = new javax.swing.JList<>(); + kwCheckBox = new javax.swing.JCheckBox(); + jScrollPane6 = new javax.swing.JScrollPane(); + parentList = new javax.swing.JList<>(); + parentCheckBox = new javax.swing.JCheckBox(); + deleteParentButton = new javax.swing.JButton(); + addParentButton = new javax.swing.JButton(); + parentTextField = new javax.swing.JTextField(); + parentFullRadioButton = new javax.swing.JRadioButton(); + parentSubstringRadioButton = new javax.swing.JRadioButton(); + jLabel2 = new javax.swing.JLabel(); + jLabel3 = new javax.swing.JLabel(); + groupComboBox = new javax.swing.JComboBox<>(); + jLabel4 = new javax.swing.JLabel(); + orderAttrRadioButton = new javax.swing.JRadioButton(); + orderSizeRadioButton = new javax.swing.JRadioButton(); + jLabel5 = new javax.swing.JLabel(); + fileOrderComboBox = new javax.swing.JComboBox<>(); + searchButton = new javax.swing.JButton(); + + setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + + org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(FileSearchDialog.class, "FileSearchDialog.jLabel1.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(cancelButton, org.openide.util.NbBundle.getMessage(FileSearchDialog.class, "FileSearchDialog.cancelButton.text")); // NOI18N + cancelButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + cancelButtonActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(dsCheckBox, org.openide.util.NbBundle.getMessage(FileSearchDialog.class, "FileSearchDialog.dsCheckBox.text")); // NOI18N + dsCheckBox.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + dsCheckBoxActionPerformed(evt); + } + }); + + fileTypeList.setModel(new DefaultListModel()); + jScrollPane1.setViewportView(fileTypeList); + + dsList.setModel(new DefaultListModel()); + dsList.setEnabled(false); + jScrollPane2.setViewportView(dsList); + + org.openide.awt.Mnemonics.setLocalizedText(freqCheckBox, org.openide.util.NbBundle.getMessage(FileSearchDialog.class, "FileSearchDialog.freqCheckBox.text")); // NOI18N + freqCheckBox.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + freqCheckBoxActionPerformed(evt); + } + }); + + freqList.setModel(new DefaultListModel()); + freqList.setEnabled(false); + jScrollPane3.setViewportView(freqList); + + sizeList.setModel(new DefaultListModel()); + sizeList.setEnabled(false); + jScrollPane4.setViewportView(sizeList); + + org.openide.awt.Mnemonics.setLocalizedText(sizeCheckBox, org.openide.util.NbBundle.getMessage(FileSearchDialog.class, "FileSearchDialog.sizeCheckBox.text")); // NOI18N + sizeCheckBox.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + sizeCheckBoxActionPerformed(evt); + } + }); + + kwList.setModel(new DefaultListModel()); + kwList.setEnabled(false); + jScrollPane5.setViewportView(kwList); + + org.openide.awt.Mnemonics.setLocalizedText(kwCheckBox, org.openide.util.NbBundle.getMessage(FileSearchDialog.class, "FileSearchDialog.kwCheckBox.text")); // NOI18N + kwCheckBox.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + kwCheckBoxActionPerformed(evt); + } + }); + + parentList.setModel(new DefaultListModel()); + parentList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION); + parentList.setEnabled(false); + parentList.addListSelectionListener(new javax.swing.event.ListSelectionListener() { + public void valueChanged(javax.swing.event.ListSelectionEvent evt) { + parentListValueChanged(evt); + } + }); + jScrollPane6.setViewportView(parentList); + + org.openide.awt.Mnemonics.setLocalizedText(parentCheckBox, org.openide.util.NbBundle.getMessage(FileSearchDialog.class, "FileSearchDialog.parentCheckBox.text")); // NOI18N + parentCheckBox.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + parentCheckBoxActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(deleteParentButton, org.openide.util.NbBundle.getMessage(FileSearchDialog.class, "FileSearchDialog.deleteParentButton.text")); // NOI18N + deleteParentButton.setEnabled(false); + deleteParentButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + deleteParentButtonActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(addParentButton, org.openide.util.NbBundle.getMessage(FileSearchDialog.class, "FileSearchDialog.addParentButton.text")); // NOI18N + addParentButton.setEnabled(false); + addParentButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + addParentButtonActionPerformed(evt); + } + }); + + parentTextField.setText(org.openide.util.NbBundle.getMessage(FileSearchDialog.class, "FileSearchDialog.parentTextField.text")); // NOI18N + parentTextField.setEnabled(false); + + org.openide.awt.Mnemonics.setLocalizedText(parentFullRadioButton, org.openide.util.NbBundle.getMessage(FileSearchDialog.class, "FileSearchDialog.parentFullRadioButton.text")); // NOI18N + parentFullRadioButton.setEnabled(false); + + org.openide.awt.Mnemonics.setLocalizedText(parentSubstringRadioButton, org.openide.util.NbBundle.getMessage(FileSearchDialog.class, "FileSearchDialog.parentSubstringRadioButton.text")); // NOI18N + parentSubstringRadioButton.setEnabled(false); + + org.openide.awt.Mnemonics.setLocalizedText(jLabel2, org.openide.util.NbBundle.getMessage(FileSearchDialog.class, "FileSearchDialog.jLabel2.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(jLabel3, org.openide.util.NbBundle.getMessage(FileSearchDialog.class, "FileSearchDialog.jLabel3.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(jLabel4, org.openide.util.NbBundle.getMessage(FileSearchDialog.class, "FileSearchDialog.jLabel4.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(orderAttrRadioButton, org.openide.util.NbBundle.getMessage(FileSearchDialog.class, "FileSearchDialog.orderAttrRadioButton.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(orderSizeRadioButton, org.openide.util.NbBundle.getMessage(FileSearchDialog.class, "FileSearchDialog.orderSizeRadioButton.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(jLabel5, org.openide.util.NbBundle.getMessage(FileSearchDialog.class, "FileSearchDialog.jLabel5.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(searchButton, org.openide.util.NbBundle.getMessage(FileSearchDialog.class, "FileSearchDialog.searchButton.text")); // NOI18N + searchButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + searchButtonActionPerformed(evt); + } + }); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().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() + .addGap(0, 0, Short.MAX_VALUE) + .addComponent(searchButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(cancelButton)) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(dsCheckBox) + .addComponent(jLabel1) + .addComponent(freqCheckBox) + .addComponent(sizeCheckBox) + .addComponent(kwCheckBox) + .addComponent(parentCheckBox) + .addComponent(jLabel2)) + .addGap(18, 18, 18) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(parentTextField) + .addGroup(layout.createSequentialGroup() + .addComponent(parentFullRadioButton) + .addGap(18, 18, 18) + .addComponent(parentSubstringRadioButton) + .addGap(0, 0, Short.MAX_VALUE))) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(deleteParentButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(addParentButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))) + .addComponent(jScrollPane3, javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jScrollPane1, javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jScrollPane2, javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jScrollPane4, javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jScrollPane5) + .addComponent(jScrollPane6, javax.swing.GroupLayout.Alignment.LEADING)) + .addGap(35, 35, 35) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jLabel3) + .addComponent(jLabel4) + .addComponent(jLabel5)) + .addGap(18, 18, 18) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(orderAttrRadioButton) + .addComponent(groupComboBox, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(orderSizeRadioButton) + .addComponent(fileOrderComboBox, 0, 144, Short.MAX_VALUE)) + .addGap(0, 93, Short.MAX_VALUE))) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jLabel1) + .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 82, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel3) + .addComponent(groupComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(5, 5, 5) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel4) + .addComponent(orderAttrRadioButton)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(orderSizeRadioButton))) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(dsCheckBox) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel5) + .addComponent(fileOrderComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, 56, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(freqCheckBox) + .addComponent(jScrollPane3, javax.swing.GroupLayout.PREFERRED_SIZE, 54, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jScrollPane4, javax.swing.GroupLayout.PREFERRED_SIZE, 49, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(sizeCheckBox)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jScrollPane5, javax.swing.GroupLayout.PREFERRED_SIZE, 49, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(kwCheckBox)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jScrollPane6, javax.swing.GroupLayout.PREFERRED_SIZE, 49, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGroup(layout.createSequentialGroup() + .addComponent(parentCheckBox) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jLabel2))) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(deleteParentButton) + .addComponent(parentFullRadioButton) + .addComponent(parentSubstringRadioButton)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(addParentButton) + .addComponent(parentTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 8, Short.MAX_VALUE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(cancelButton) + .addComponent(searchButton)) + .addContainerGap()) + ); + + pack(); + }// //GEN-END:initComponents + + private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed + setVisible(false); + dispose(); + buttonPressed = ButtonPressed.CANCEL; + }//GEN-LAST:event_cancelButtonActionPerformed + + private void dsCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_dsCheckBoxActionPerformed + dsList.setEnabled(dsCheckBox.isSelected()); + }//GEN-LAST:event_dsCheckBoxActionPerformed + + private void parentListValueChanged(javax.swing.event.ListSelectionEvent evt) {//GEN-FIRST:event_parentListValueChanged + if (parentList.getSelectedValuesList().isEmpty()) { + deleteParentButton.setEnabled(false); + } else { + deleteParentButton.setEnabled(true); + } + }//GEN-LAST:event_parentListValueChanged + + private void deleteParentButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteParentButtonActionPerformed + int index = parentList.getSelectedIndex(); + parentListModel.remove(index); + }//GEN-LAST:event_deleteParentButtonActionPerformed + + private void addParentButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addParentButtonActionPerformed + if ( ! parentTextField.getText().isEmpty()) { + ParentSearchTerm searchTerm; + if (parentFullRadioButton.isSelected()) { + searchTerm = new ParentSearchTerm(parentTextField.getText(), true); + } else { + searchTerm = new ParentSearchTerm(parentTextField.getText(), false); + } + parentListModel.add(parentListModel.size(), searchTerm); + } + }//GEN-LAST:event_addParentButtonActionPerformed + + private void freqCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_freqCheckBoxActionPerformed + freqList.setEnabled(freqCheckBox.isSelected()); + }//GEN-LAST:event_freqCheckBoxActionPerformed + + private void sizeCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_sizeCheckBoxActionPerformed + sizeList.setEnabled(sizeCheckBox.isSelected()); + }//GEN-LAST:event_sizeCheckBoxActionPerformed + + private void kwCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_kwCheckBoxActionPerformed + kwList.setEnabled(kwCheckBox.isSelected()); + }//GEN-LAST:event_kwCheckBoxActionPerformed + + private void parentCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_parentCheckBoxActionPerformed + parentList.setEnabled(parentCheckBox.isSelected()); + parentFullRadioButton.setEnabled(parentCheckBox.isSelected()); + parentSubstringRadioButton.setEnabled(parentCheckBox.isSelected()); + addParentButton.setEnabled(parentCheckBox.isSelected()); + deleteParentButton.setEnabled(parentCheckBox.isSelected()); + }//GEN-LAST:event_parentCheckBoxActionPerformed + + private void searchButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchButtonActionPerformed + buttonPressed = ButtonPressed.SEARCH; + setVisible(false); + }//GEN-LAST:event_searchButtonActionPerformed + + /** + * @param args the command line arguments + */ + public static void main(String args[]) { + /* Set the Nimbus look and feel */ + // + /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel. + * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html + */ + try { + for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) { + if ("Nimbus".equals(info.getName())) { + javax.swing.UIManager.setLookAndFeel(info.getClassName()); + break; + } + } + } catch (ClassNotFoundException ex) { + java.util.logging.Logger.getLogger(FileSearchDialog.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); + } catch (InstantiationException ex) { + java.util.logging.Logger.getLogger(FileSearchDialog.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); + } catch (IllegalAccessException ex) { + java.util.logging.Logger.getLogger(FileSearchDialog.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); + } catch (javax.swing.UnsupportedLookAndFeelException ex) { + java.util.logging.Logger.getLogger(FileSearchDialog.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); + } + // + + /* Create and display the dialog */ + java.awt.EventQueue.invokeLater(new Runnable() { + public void run() { + FileSearchDialog dialog = new FileSearchDialog(new javax.swing.JFrame(), true); + dialog.addWindowListener(new java.awt.event.WindowAdapter() { + @Override + public void windowClosing(java.awt.event.WindowEvent e) { + System.exit(0); + } + }); + dialog.setVisible(true); + } + }); + } + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton addParentButton; + private javax.swing.JButton cancelButton; + private javax.swing.JButton deleteParentButton; + private javax.swing.JCheckBox dsCheckBox; + private javax.swing.JList dsList; + private javax.swing.JComboBox fileOrderComboBox; + private javax.swing.JList fileTypeList; + private javax.swing.JCheckBox freqCheckBox; + private javax.swing.JList freqList; + private javax.swing.JComboBox groupComboBox; + private javax.swing.JLabel jLabel1; + private javax.swing.JLabel jLabel2; + private javax.swing.JLabel jLabel3; + private javax.swing.JLabel jLabel4; + private javax.swing.JLabel jLabel5; + private javax.swing.JScrollPane jScrollPane1; + private javax.swing.JScrollPane jScrollPane2; + private javax.swing.JScrollPane jScrollPane3; + private javax.swing.JScrollPane jScrollPane4; + private javax.swing.JScrollPane jScrollPane5; + private javax.swing.JScrollPane jScrollPane6; + private javax.swing.JCheckBox kwCheckBox; + private javax.swing.JList kwList; + private javax.swing.JRadioButton orderAttrRadioButton; + private javax.swing.ButtonGroup orderButtonGroup; + private javax.swing.JRadioButton orderSizeRadioButton; + private javax.swing.ButtonGroup parentButtonGroup; + private javax.swing.JCheckBox parentCheckBox; + private javax.swing.JRadioButton parentFullRadioButton; + private javax.swing.JList parentList; + private javax.swing.JRadioButton parentSubstringRadioButton; + private javax.swing.JTextField parentTextField; + private javax.swing.JButton searchButton; + private javax.swing.JCheckBox sizeCheckBox; + private javax.swing.JList sizeList; + // End of variables declaration//GEN-END:variables +} diff --git a/Core/src/org/sleuthkit/autopsy/newpackage/FileSearchFiltering.java b/Core/src/org/sleuthkit/autopsy/newpackage/FileSearchFiltering.java index 63c17bcf28..a097a46c3c 100644 --- a/Core/src/org/sleuthkit/autopsy/newpackage/FileSearchFiltering.java +++ b/Core/src/org/sleuthkit/autopsy/newpackage/FileSearchFiltering.java @@ -24,6 +24,7 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; import org.sleuthkit.autopsy.datamodel.utils.FileTypeUtils; import org.sleuthkit.autopsy.newpackage.FileSearchData.FileSize; +import org.sleuthkit.autopsy.newpackage.FileSearchData.FileType; import org.sleuthkit.autopsy.newpackage.FileSearchData.Frequency; import org.sleuthkit.datamodel.AbstractFile; @@ -50,6 +51,7 @@ class FileSearchFiltering { * @param filters The filters to run * @param caseDb The case database * @param crDb The central repo. Can be null as long as no filters need it. + * * @return */ static List runQueries(List filters, SleuthkitCase caseDb, EamDb centralRepoDb) throws FileSearchException { @@ -77,32 +79,33 @@ class FileSearchFiltering { } } + if (combinedQuery.isEmpty()) { + // The file search filter is required, so this should never be empty. + throw new FileSearchException("Selected filters do not include a case database query"); + } + try { // Get all matching abstract files List resultList = new ArrayList<>(); - if ( ! combinedQuery.isEmpty()) { - logger.log(Level.INFO, "Running SQL query: {0}", combinedQuery); - List sqlResults = caseDb.findAllFilesWhere(combinedQuery); - - // If there are no results, return now - if (sqlResults.isEmpty()) { - return resultList; - } - - // Wrap each result in a ResultFile - for (AbstractFile abstractFile : sqlResults) { - resultList.add(new ResultFile(abstractFile)); - } + + logger.log(Level.INFO, "Running SQL query: {0}", combinedQuery); + List sqlResults = caseDb.findAllFilesWhere(combinedQuery); + + // If there are no results, return now + if (sqlResults.isEmpty()) { + return resultList; + } + + // Wrap each result in a ResultFile + for (AbstractFile abstractFile : sqlResults) { + resultList.add(new ResultFile(abstractFile)); } - // Now run any non-SQL filters. Note that resultList could be empty at this point if we had no SQL queries - - // applyAlternateFilter() will interpret this as no filters have been run up to this point - // and act accordingly. - // TODO maybe it is an error to have no SQL query - for (FileFilter subFilter : filters) { - if (subFilter.useAlternateFilter()) { - resultList = subFilter.applyAlternateFilter(resultList, caseDb, centralRepoDb); + // Now run any non-SQL filters. + for (FileFilter filter : filters) { + if (filter.useAlternateFilter()) { + resultList = filter.applyAlternateFilter(resultList, caseDb, centralRepoDb); } // There are no matches for the filters run so far, so return @@ -137,8 +140,6 @@ class FileSearchFiltering { /** * Run a secondary filter that does not operate on tsk_files. - * If currentResults is empty, the assumption is that this is the first filter being run, not that - * there are no matches. * * @param currentResults The current list of matching files; empty if no filters have yet been run. * @param caseDb The case database @@ -164,15 +165,15 @@ class FileSearchFiltering { /** * A filter for specifying the file size */ - static class SizeSubFilter extends FileFilter { + static class SizeFilter extends FileFilter { private final List fileSizes; /** - * Create the SizeSubFilter + * Create the SizeFilter * * @param fileSizes the file sizes that should match */ - SizeSubFilter(List fileSizes) { + SizeFilter(List fileSizes) { this.fileSizes = fileSizes; } @@ -194,28 +195,28 @@ class FileSearchFiltering { @NbBundle.Messages({ "# {0} - filters", - "FileSearchFiltering.SizeSubFilter.desc=Files with size in range(s): {0}", - "FileSearchFiltering.SizeSubFilter.or= or ", + "FileSearchFiltering.SizeFilter.desc=Files with size in range(s): {0}", + "FileSearchFiltering.SizeFilter.or= or ", "# {0} - Minimum bytes", "# {1} - Maximum bytes", - "FileSearchFiltering.SizeSubFilter.range=({0} to {1})", + "FileSearchFiltering.SizeFilter.range=({0} to {1})", }) @Override String getDesc() { String desc = ""; // NON-NLS for (FileSize size : fileSizes) { if ( ! desc.isEmpty()) { - desc += Bundle.FileSearchFiltering_SizeSubFilter_or(); + desc += Bundle.FileSearchFiltering_SizeFilter_or(); } - desc += Bundle.FileSearchFiltering_SizeSubFilter_range(size.getMinBytes(), size.getMaxBytes()); + desc += Bundle.FileSearchFiltering_SizeFilter_range(size.getMinBytes(), size.getMaxBytes()); } - desc = Bundle.FileSearchFiltering_SizeSubFilter_desc(desc); + desc = Bundle.FileSearchFiltering_SizeFilter_desc(desc); return desc; } } /** - * A utility class for the ParentSubFilter to store the search string + * A utility class for the ParentFilter to store the search string * and whether it is a full path or a substring. */ static class ParentSearchTerm { @@ -247,20 +248,33 @@ class FileSearchFiltering { return "parent_path LIKE \'%" + searchStr + "%\'"; // NON-NLS } } + + @NbBundle.Messages({ + "# {0} - search term", + "FileSearchFiltering.ParentSearchTerm.fullString= {0} (exact)", + "FileSearchFiltering.ParentSearchTerm.subString= {0} (substring)", + }) + @Override + public String toString() { + if (isFullPath) { + return Bundle.FileSearchFiltering_ParentSearchTerm_fullString(searchStr); + } + return Bundle.FileSearchFiltering_ParentSearchTerm_subString(searchStr); + } } /** * A filter for specifying parent path (either full path or substring) */ - static class ParentSubFilter extends FileFilter { + static class ParentFilter extends FileFilter { private final List parentSearchTerms; /** - * Create the ParentSubFilter + * Create the ParentFilter * * @param parentSearchTerms Full paths or substrings to filter on */ - ParentSubFilter(List parentSearchTerms) { + ParentFilter(List parentSearchTerms) { this.parentSearchTerms = parentSearchTerms; } @@ -278,25 +292,25 @@ class FileSearchFiltering { @NbBundle.Messages({ "# {0} - filters", - "FileSearchFiltering.ParentSubFilter.desc=Files with paths matching: {0}", - "FileSearchFiltering.ParentSubFilter.or= or ", - "FileSearchFiltering.ParentSubFilter.exact=(exact match)", - "FileSearchFiltering.ParentSubFilter.substring=(substring)", + "FileSearchFiltering.ParentFilter.desc=Files with paths matching: {0}", + "FileSearchFiltering.ParentFilter.or= or ", + "FileSearchFiltering.ParentFilter.exact=(exact match)", + "FileSearchFiltering.ParentFilter.substring=(substring)", }) @Override String getDesc() { String desc = ""; // NON-NLS for (ParentSearchTerm searchTerm : parentSearchTerms) { if ( ! desc.isEmpty()) { - desc += Bundle.FileSearchFiltering_ParentSubFilter_or(); + desc += Bundle.FileSearchFiltering_ParentFilter_or(); } if (searchTerm.isFullPath) { - desc += searchTerm.searchStr + Bundle.FileSearchFiltering_ParentSubFilter_exact(); + desc += searchTerm.searchStr + Bundle.FileSearchFiltering_ParentFilter_exact(); } else { - desc += searchTerm.searchStr + Bundle.FileSearchFiltering_ParentSubFilter_substring(); + desc += searchTerm.searchStr + Bundle.FileSearchFiltering_ParentFilter_substring(); } } - desc = Bundle.FileSearchFiltering_ParentSubFilter_desc(desc); + desc = Bundle.FileSearchFiltering_ParentFilter_desc(desc); return desc; } } @@ -304,15 +318,15 @@ class FileSearchFiltering { /** * A filter for specifying data sources */ - static class DataSourceSubFilter extends FileFilter { + static class DataSourceFilter extends FileFilter { private final List dataSources; /** - * Create the DataSourceSubFilter + * Create the DataSourceFilter * * @param dataSources the data sources to filter on */ - DataSourceSubFilter(List dataSources) { + DataSourceFilter(List dataSources) { this.dataSources = dataSources; } @@ -331,22 +345,22 @@ class FileSearchFiltering { @NbBundle.Messages({ "# {0} - filters", - "FileSearchFiltering.DataSourceSubFilter.desc=Files in data source(s): {0}", - "FileSearchFiltering.DataSourceSubFilter.or= or ", + "FileSearchFiltering.DataSourceFilter.desc=Files in data source(s): {0}", + "FileSearchFiltering.DataSourceFilter.or= or ", "# {0} - Data source name", "# {1} - Data source ID", - "FileSearchFiltering.DataSourceSubFilter.datasource={0}({1})", + "FileSearchFiltering.DataSourceFilter.datasource={0}({1})", }) @Override String getDesc() { String desc = ""; // NON-NLS for (DataSource ds : dataSources) { if ( ! desc.isEmpty()) { - desc += Bundle.FileSearchFiltering_DataSourceSubFilter_or(); + desc += Bundle.FileSearchFiltering_DataSourceFilter_or(); } - desc += Bundle.FileSearchFiltering_DataSourceSubFilter_datasource(ds.getName(), ds.getId()); + desc += Bundle.FileSearchFiltering_DataSourceFilter_datasource(ds.getName(), ds.getId()); } - desc = Bundle.FileSearchFiltering_DataSourceSubFilter_desc(desc); + desc = Bundle.FileSearchFiltering_DataSourceFilter_desc(desc); return desc; } } @@ -355,14 +369,14 @@ class FileSearchFiltering { * A filter for specifying keyword list names. * A file must contain a keyword from one of the given lists to pass. */ - static class KeywordListSubFilter extends FileFilter { + static class KeywordListFilter extends FileFilter { private final List listNames; /** - * Create the KeywordListSubFilter + * Create the KeywordListFilter * @param listNames */ - KeywordListSubFilter(List listNames) { + KeywordListFilter(List listNames) { this.listNames = listNames; } @@ -385,19 +399,19 @@ class FileSearchFiltering { @NbBundle.Messages({ "# {0} - filters", - "FileSearchFiltering.KeywordListSubFilter.desc=Files with keywords in list(s): {0}", - "FileSearchFiltering.KeywordListSubFilter.comma=, ", + "FileSearchFiltering.KeywordListFilter.desc=Files with keywords in list(s): {0}", + "FileSearchFiltering.KeywordListFilter.comma=, ", }) @Override String getDesc() { String desc = ""; // NON-NLS for (String listName : listNames) { if ( ! desc.isEmpty()) { - desc += Bundle.FileSearchFiltering_KeywordListSubFilter_comma(); + desc += Bundle.FileSearchFiltering_KeywordListFilter_comma(); } desc += listName; } - desc = Bundle.FileSearchFiltering_KeywordListSubFilter_desc(desc); + desc = Bundle.FileSearchFiltering_KeywordListFilter_desc(desc); return desc; } } @@ -405,21 +419,21 @@ class FileSearchFiltering { /** * A filter for specifying file types. */ - static class FileTypeSubFilter extends FileFilter { - private final List categories; + static class FileTypeFilter extends FileFilter { + private final List categories; /** - * Create the FileTypeSubFilter + * Create the FileTypeFilter * @param categories List of file types to filter on */ - FileTypeSubFilter(List categories) { + FileTypeFilter(List categories) { this.categories = categories; } @Override String getWhereClause() { String queryStr = ""; // NON-NLS - for (FileTypeUtils.FileTypeCategory cat : categories) { + for (FileType cat : categories) { for (String type : cat.getMediaTypes()) { if (! queryStr.isEmpty()) { queryStr += ","; // NON-NLS @@ -433,19 +447,19 @@ class FileSearchFiltering { @NbBundle.Messages({ "# {0} - filters", - "FileSearchFiltering.FileTypeSubFilter.desc=Files with type: {0}", - "FileSearchFiltering.FileTypeSubFilter.or= or ", + "FileSearchFiltering.FileTypeFilter.desc=Files with type: {0}", + "FileSearchFiltering.FileTypeFilter.or= or ", }) @Override String getDesc() { String desc = ""; - for (FileTypeUtils.FileTypeCategory cat : categories) { + for (FileType cat : categories) { if ( ! desc.isEmpty()) { - desc += Bundle.FileSearchFiltering_FileTypeSubFilter_or(); + desc += Bundle.FileSearchFiltering_FileTypeFilter_or(); } - desc += cat.getDisplayName(); + desc += cat.toString(); } - desc = Bundle.FileSearchFiltering_FileTypeSubFilter_desc(desc); + desc = Bundle.FileSearchFiltering_FileTypeFilter_desc(desc); return desc; } } @@ -453,16 +467,16 @@ class FileSearchFiltering { /** * A filter for specifying frequency in the central repository. */ - static class FrequencySubFilter extends FileFilter { + static class FrequencyFilter extends FileFilter { private final List frequencies; /** - * Create the FrequencySubFilter + * Create the FrequencyFilter * * @param frequencies List of frequencies that will pass the filter */ - FrequencySubFilter(List frequencies) { + FrequencyFilter(List frequencies) { this.frequencies = frequencies; } @@ -486,50 +500,47 @@ class FileSearchFiltering { throw new FileSearchException("Can not run Frequency filter with null Central Repository DB"); // NON-NLS } - // For the moment, we have to have run some kind of SQL filter before getting to this point. - // TODO what we should do if this is the only filter? - if ( ! currentResults.isEmpty()) { - - // We can try to make this more efficient later - for now, check the frequency of each file individually - List frequencyResults = new ArrayList<>(); - for (ResultFile file : currentResults) { - try { - if (file.getAbstractFile().getMd5Hash() != null && ! file.getAbstractFile().getMd5Hash().isEmpty()) { - CorrelationAttributeInstance.Type attributeType = centralRepoDb.getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID); - long count = centralRepoDb.getCountUniqueCaseDataSourceTuplesHavingTypeValue(attributeType, file.getAbstractFile().getMd5Hash()); - file.setFrequency(Frequency.fromCount(count)); - } - - if (frequencies.contains(file.getFrequency())) { - frequencyResults.add(file); - } - } catch (EamDbException | CorrelationAttributeNormalizationException ex) { - throw new FileSearchException("Error querying central repository", ex); // NON-NLS - } - } - return frequencyResults; - } else { - // We need to load all files with the specified frequency - this will be essentially the same - // as a common properties search. TODO - do we want to do this or throw an error? - return currentResults; + // We have to have run some kind of SQL filter before getting to this point, + // and should have checked afterward to see if the results were empty. + if (currentResults.isEmpty()) { + throw new FileSearchException("Can not run on empty list"); // NON-NLS } + + // We can try to make this more efficient later - for now, check the frequency of each file individually + List frequencyResults = new ArrayList<>(); + for (ResultFile file : currentResults) { + try { + if (file.getAbstractFile().getMd5Hash() != null && ! file.getAbstractFile().getMd5Hash().isEmpty()) { + CorrelationAttributeInstance.Type attributeType = centralRepoDb.getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID); + long count = centralRepoDb.getCountUniqueCaseDataSourceTuplesHavingTypeValue(attributeType, file.getAbstractFile().getMd5Hash()); + file.setFrequency(Frequency.fromCount(count)); + } + + if (frequencies.contains(file.getFrequency())) { + frequencyResults.add(file); + } + } catch (EamDbException | CorrelationAttributeNormalizationException ex) { + throw new FileSearchException("Error querying central repository", ex); // NON-NLS + } + } + return frequencyResults; } @NbBundle.Messages({ "# {0} - filters", - "FileSearchFiltering.FrequencySubFilter.desc=Files with frequency: {0}", - "FileSearchFiltering.FrequencySubFilter.or= or ", + "FileSearchFiltering.FrequencyFilter.desc=Files with frequency: {0}", + "FileSearchFiltering.FrequencyFilter.or= or ", }) @Override String getDesc() { String desc = ""; // NON-NLS for (Frequency freq : frequencies) { if ( ! desc.isEmpty()) { - desc += Bundle.FileSearchFiltering_FrequencySubFilter_or(); + desc += Bundle.FileSearchFiltering_FrequencyFilter_or(); } desc += freq.name(); } - return Bundle.FileSearchFiltering_FrequencySubFilter_desc(this); + return Bundle.FileSearchFiltering_FrequencyFilter_desc(this); } } diff --git a/Core/src/org/sleuthkit/autopsy/newpackage/FileSearchTestAction.java b/Core/src/org/sleuthkit/autopsy/newpackage/FileSearchTestAction.java index 1de8ff3ec3..d6d3d9ffc2 100644 --- a/Core/src/org/sleuthkit/autopsy/newpackage/FileSearchTestAction.java +++ b/Core/src/org/sleuthkit/autopsy/newpackage/FileSearchTestAction.java @@ -19,6 +19,7 @@ package org.sleuthkit.autopsy.newpackage; import java.util.*; +import javax.swing.JDialog; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; import org.openide.awt.ActionID; import org.openide.awt.ActionReference; @@ -48,33 +49,86 @@ public final class FileSearchTestAction extends CallableSystemAction { @Override @SuppressWarnings("fallthrough") public void performAction() { + + EamDb crDb = null; + if (EamDb.isEnabled()) { + try { + crDb = EamDb.getInstance(); + } catch (Exception ex) { + ex.printStackTrace(); + return; + } + } + + FileSearchDialog dialog = new FileSearchDialog(null, true); + + while ( ! dialog.searchCancelled()) { + + // Display the dialog + dialog.setVisible(true); + + // Get the selected filters + List filters = dialog.getFilters(); + + // Get the grouping attribute and group sorting method + FileSearch.AttributeType groupingAttr = dialog.getGroupingAttribute(); + FileGroup.GroupSortingAlgorithm groupSortAlgorithm = dialog.getGroupSortingMethod(); + + // Get the file sorting method + FileSorter.SortingMethod fileSort = dialog.getFileSortingMethod(); + + try { + + // Make a list of attributes that we want to add values for. Eventually this can be based on user input but + // for now just add all of them. + List attributesNeededForSorting = Arrays.asList(new FileSearch.DataSourceAttribute(), + new FileSearch.FileSizeAttribute(), new FileSearch.FileTypeAttribute(), new FileSearch.FrequencyAttribute(), + new FileSearch.KeywordListAttribute(), new FileSearch.ParentPathAttribute()); + + FileSearch.runFileSearch(filters, + groupingAttr, + groupSortAlgorithm, + fileSort, + attributesNeededForSorting, + Case.getCurrentCase().getSleuthkitCase(), crDb); + + + + } catch (Exception ex) { + ex.printStackTrace(); + return; + } + + } + + System.out.println("\n#########################\nTesting file search!!!"); // Set up all the test filters - FileSearchFiltering.FileFilter size_medSmallXS = new FileSearchFiltering.SizeSubFilter(Arrays.asList(FileSearchData.FileSize.MEDIUM, FileSearchData.FileSize.SMALL, FileSearchData.FileSize.XS)); - FileSearchFiltering.FileFilter size_XL = new FileSearchFiltering.SizeSubFilter(Arrays.asList(FileSearchData.FileSize.XL)); - FileSearchFiltering.FileFilter size_largeXL = new FileSearchFiltering.SizeSubFilter(Arrays.asList(FileSearchData.FileSize.LARGE, FileSearchData.FileSize.XL)); - FileSearchFiltering.FileFilter size_medLargeXL = new FileSearchFiltering.SizeSubFilter(Arrays.asList(FileSearchData.FileSize.MEDIUM, FileSearchData.FileSize.LARGE, FileSearchData.FileSize.XL)); + FileSearchFiltering.FileFilter size_medSmallXS = new FileSearchFiltering.SizeFilter(Arrays.asList(FileSearchData.FileSize.MEDIUM, FileSearchData.FileSize.SMALL, FileSearchData.FileSize.XS)); + FileSearchFiltering.FileFilter size_XL = new FileSearchFiltering.SizeFilter(Arrays.asList(FileSearchData.FileSize.XL)); + FileSearchFiltering.FileFilter size_largeXL = new FileSearchFiltering.SizeFilter(Arrays.asList(FileSearchData.FileSize.LARGE, FileSearchData.FileSize.XL)); + FileSearchFiltering.FileFilter size_medLargeXL = new FileSearchFiltering.SizeFilter(Arrays.asList(FileSearchData.FileSize.MEDIUM, FileSearchData.FileSize.LARGE, FileSearchData.FileSize.XL)); - FileSearchFiltering.FileFilter kw_alphaBeta = new FileSearchFiltering.KeywordListSubFilter(Arrays.asList("Alpha", "Beta")); - FileSearchFiltering.FileFilter kw_alpha = new FileSearchFiltering.KeywordListSubFilter(Arrays.asList("Alpha")); + FileSearchFiltering.FileFilter kw_alphaBeta = new FileSearchFiltering.KeywordListFilter(Arrays.asList("Alpha", "Beta")); + FileSearchFiltering.FileFilter kw_alpha = new FileSearchFiltering.KeywordListFilter(Arrays.asList("Alpha")); - FileSearchFiltering.FileFilter freq_uniqueRare = new FileSearchFiltering.FrequencySubFilter(Arrays.asList(FileSearchData.Frequency.UNIQUE, FileSearchData.Frequency.RARE)); + FileSearchFiltering.FileFilter freq_uniqueRare = new FileSearchFiltering.FrequencyFilter(Arrays.asList(FileSearchData.Frequency.UNIQUE, FileSearchData.Frequency.RARE)); - FileSearchFiltering.FileFilter path_II = new FileSearchFiltering.ParentSubFilter(Arrays.asList(new FileSearchFiltering.ParentSearchTerm("II", false))); - FileSearchFiltering.FileFilter path_IIfolderA = new FileSearchFiltering.ParentSubFilter(Arrays.asList(new FileSearchFiltering.ParentSearchTerm("II", false), + FileSearchFiltering.FileFilter path_II = new FileSearchFiltering.ParentFilter(Arrays.asList(new FileSearchFiltering.ParentSearchTerm("II", false))); + FileSearchFiltering.FileFilter path_IIfolderA = new FileSearchFiltering.ParentFilter(Arrays.asList(new FileSearchFiltering.ParentSearchTerm("II", false), new FileSearchFiltering.ParentSearchTerm("/Rare in CR/Folder A/", true))); - FileSearchFiltering.FileFilter type_video = new FileSearchFiltering.FileTypeSubFilter(Arrays.asList(FileTypeUtils.FileTypeCategory.VIDEO)); - FileSearchFiltering.FileFilter type_imageAudio = new FileSearchFiltering.FileTypeSubFilter(Arrays.asList(FileTypeUtils.FileTypeCategory.IMAGE, - FileTypeUtils.FileTypeCategory.AUDIO)); - FileSearchFiltering.FileFilter type_image = new FileSearchFiltering.FileTypeSubFilter(Arrays.asList(FileTypeUtils.FileTypeCategory.IMAGE)); - FileSearchFiltering.FileFilter type_doc = new FileSearchFiltering.FileTypeSubFilter(Arrays.asList(FileTypeUtils.FileTypeCategory.DOCUMENTS)); + FileSearchFiltering.FileFilter type_video = new FileSearchFiltering.FileTypeFilter(Arrays.asList(FileSearchData.FileType.VIDEO)); + FileSearchFiltering.FileFilter type_imageAudio = new FileSearchFiltering.FileTypeFilter(Arrays.asList(FileSearchData.FileType.IMAGE, + FileSearchData.FileType.AUDIO)); + FileSearchFiltering.FileFilter type_image = new FileSearchFiltering.FileTypeFilter(Arrays.asList(FileSearchData.FileType.IMAGE)); + FileSearchFiltering.FileFilter type_doc = new FileSearchFiltering.FileTypeFilter(Arrays.asList(FileSearchData.FileType.DOCUMENTS)); FileSearchFiltering.FileFilter ds_46 = null; try { DataSource ds = Case.getCurrentCase().getSleuthkitCase().getDataSource(46); - ds_46 = new FileSearchFiltering.DataSourceSubFilter(Arrays.asList(ds)); + ds_46 = new FileSearchFiltering.DataSourceFilter(Arrays.asList(ds)); } catch (Exception ex) { ex.printStackTrace(); return; @@ -88,7 +142,8 @@ public final class FileSearchTestAction extends CallableSystemAction { List filters = new ArrayList<>(); filters.add(size_medSmallXS); //filters.add(kw_alpha); - //filters.add(freq_uniqueRare); + filters.add(freq_uniqueRare); + //filters.add(path_IIfolderA); // Choose grouping attribute //FileSearch.AttributeType groupingAttr = new FileSearch.FileTypeAttribute(); @@ -105,14 +160,7 @@ public final class FileSearchTestAction extends CallableSystemAction { // Choose file sorting method FileSorter.SortingMethod fileSort = FileSorter.SortingMethod.BY_FILE_SIZE; - EamDb crDb = null; - if (EamDb.isEnabled()) { - try { - crDb = EamDb.getInstance(); - } catch (Exception ex) { - ex.printStackTrace(); - } - } + try { diff --git a/Core/src/org/sleuthkit/autopsy/newpackage/FileSorter.java b/Core/src/org/sleuthkit/autopsy/newpackage/FileSorter.java index 6c67a8ad4f..79dd24dbac 100644 --- a/Core/src/org/sleuthkit/autopsy/newpackage/FileSorter.java +++ b/Core/src/org/sleuthkit/autopsy/newpackage/FileSorter.java @@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.newpackage; import java.util.ArrayList; import java.util.Comparator; import java.util.List; +import org.openide.util.NbBundle; /** * Class used to sort ResultFiles using the supplied method. @@ -217,13 +218,32 @@ class FileSorter implements Comparator { /** * Enum for selecting the primary method for sorting result files. */ + @NbBundle.Messages({ + "FileSorter.SortingMethod.datasource.displayName=By data source", + "FileSorter.SortingMethod.filename.displayName=By file name", + "FileSorter.SortingMethod.filesize.displayName=By file size", + "FileSorter.SortingMethod.filetype.displayName=By file type", + "FileSorter.SortingMethod.frequency.displayName=By central repo frequency", + "FileSorter.SortingMethod.keywordlist.displayName=By keyword list names", + "FileSorter.SortingMethod.parent.displayName=By parent path"}) enum SortingMethod { - BY_DATA_SOURCE, // Sort in increasing order of data source ID - BY_FILE_NAME, // Sort alphabetically by file name - BY_FILE_SIZE, // Sort in decreasing order of size - BY_FILE_TYPE, // Sort in order of file type (defined in FileType enum), with secondary sort on MIME type - BY_FREQUENCY, // Sort by decreasing rarity in the central repository - BY_KEYWORD_LIST_NAMES, // Sort alphabetically by list of keyword list names found - BY_PARENT_PATH; // Sort alphabetically by path + BY_FILE_NAME(Bundle.FileSorter_SortingMethod_filename_displayName()), // Sort alphabetically by file name + BY_DATA_SOURCE(Bundle.FileSorter_SortingMethod_datasource_displayName()), // Sort in increasing order of data source ID + BY_FILE_SIZE(Bundle.FileSorter_SortingMethod_filesize_displayName()), // Sort in decreasing order of size + BY_FILE_TYPE(Bundle.FileSorter_SortingMethod_filetype_displayName()), // Sort in order of file type (defined in FileType enum), with secondary sort on MIME type + BY_FREQUENCY(Bundle.FileSorter_SortingMethod_frequency_displayName()), // Sort by decreasing rarity in the central repository + BY_KEYWORD_LIST_NAMES(Bundle.FileSorter_SortingMethod_keywordlist_displayName()), // Sort alphabetically by list of keyword list names found + BY_PARENT_PATH(Bundle.FileSorter_SortingMethod_parent_displayName()); // Sort alphabetically by path + + private final String displayName; + + SortingMethod(String displayName) { + this.displayName = displayName; + } + + @Override + public String toString() { + return displayName; + } } }