19 package org.sleuthkit.autopsy.filesearch;
21 import java.awt.Component;
22 import java.awt.Cursor;
23 import java.awt.GridLayout;
24 import java.awt.event.ActionEvent;
25 import java.awt.event.ActionListener;
26 import java.beans.PropertyChangeEvent;
27 import java.beans.PropertyChangeListener;
28 import java.util.ArrayList;
29 import java.util.Collection;
30 import java.util.Collections;
31 import java.util.List;
32 import java.util.logging.Level;
33 import javax.swing.JLabel;
34 import javax.swing.JPanel;
35 import javax.swing.border.EmptyBorder;
36 import org.openide.DialogDisplayer;
37 import org.openide.NotifyDescriptor;
38 import org.openide.nodes.Node;
39 import org.openide.util.NbBundle;
40 import org.openide.windows.TopComponent;
56 @SuppressWarnings(
"PMD.SingularField")
57 class FileSearchPanel extends javax.swing.JPanel {
59 private final List<FileSearchFilter> filters =
new ArrayList<>();
60 private static int resultWindowCount = 0;
61 private static final String EMPTY_WHERE_CLAUSE = NbBundle.getMessage(DateSearchFilter.class,
"FileSearchPanel.emptyWhereClause.text");
72 customizeComponents();
79 private void customizeComponents() {
81 JLabel label =
new JLabel(NbBundle.getMessage(
this.getClass(),
"FileSearchPanel.custComp.label.text"));
82 label.setAlignmentX(Component.LEFT_ALIGNMENT);
83 label.setBorder(
new EmptyBorder(0, 0, 10, 0));
85 JPanel panel1 =
new JPanel();
86 panel1.setLayout(
new GridLayout(1,2));
87 panel1.add(
new JLabel(
""));
88 JPanel panel2 =
new JPanel();
89 panel2.setLayout(
new GridLayout(1,2, 20, 0));
90 JPanel panel3 =
new JPanel();
91 panel3.setLayout(
new GridLayout(1,2, 20, 0));
92 JPanel panel4 =
new JPanel();
93 panel4.setLayout(
new GridLayout(1,2, 20, 0));
94 JPanel panel5 =
new JPanel();
95 panel5.setLayout(
new GridLayout(1,2, 20, 0));
98 NameSearchFilter nameFilter =
new NameSearchFilter();
99 SizeSearchFilter sizeFilter =
new SizeSearchFilter();
100 DateSearchFilter dateFilter =
new DateSearchFilter();
101 KnownStatusSearchFilter knowStatusFilter =
new KnownStatusSearchFilter();
102 HashSearchFilter hashFilter =
new HashSearchFilter();
103 MimeTypeFilter mimeTypeFilter =
new MimeTypeFilter();
104 DataSourceFilter dataSourceFilter =
new DataSourceFilter();
106 panel2.add(
new FilterArea(NbBundle.getMessage(
this.getClass(),
"FileSearchPanel.filterTitle.name"),nameFilter));
108 panel3.add(
new FilterArea(NbBundle.getMessage(
this.getClass(),
"FileSearchPanel.filterTitle.metadata"),sizeFilter));
110 panel2.add(
new FilterArea(NbBundle.getMessage(
this.getClass(),
"FileSearchPanel.filterTitle.metadata"), dateFilter));
111 panel3.add(
new FilterArea(NbBundle.getMessage(
this.getClass(),
"FileSearchPanel.filterTitle.knownStatus"), knowStatusFilter));
113 panel5.add(
new FilterArea(NbBundle.getMessage(
this.getClass(),
"HashSearchPanel.md5CheckBox.text"), hashFilter));
114 panel5.add(
new JLabel(
""));
115 panel4.add(
new FilterArea(NbBundle.getMessage(
this.getClass(),
"FileSearchPanel.filterTitle.metadata"), mimeTypeFilter));
116 panel4.add(
new FilterArea(NbBundle.getMessage(
this.getClass(),
"DataSourcePanel.dataSourceCheckBox.text"), dataSourceFilter));
117 filterPanel.add(panel1);
118 filterPanel.add(panel2);
119 filterPanel.add(panel3);
120 filterPanel.add(panel4);
121 filterPanel.add(panel5);
123 filters.add(nameFilter);
124 filters.add(sizeFilter);
125 filters.add(dateFilter);
126 filters.add(knowStatusFilter);
127 filters.add(hashFilter);
128 filters.add(mimeTypeFilter);
129 filters.add(dataSourceFilter);
131 for (FileSearchFilter filter : this.getFilters()) {
132 filter.addPropertyChangeListener(
new PropertyChangeListener() {
134 public void propertyChange(PropertyChangeEvent evt) {
135 searchButton.setEnabled(isValidSearch());
139 addListenerToAll(
new ActionListener() {
141 public void actionPerformed(ActionEvent e) {
145 searchButton.setEnabled(isValidSearch());
151 private boolean isValidSearch() {
152 boolean enabled =
false;
153 for (FileSearchFilter filter : this.getFilters()) {
154 if (filter.isEnabled()) {
156 if (!filter.isValid()) {
157 errorLabel.setText(filter.getLastError());
163 errorLabel.setText(
"");
171 @NbBundle.Messages(
"FileSearchPanel.emptyNode.display.text=No results found.")
172 private void search() {
174 this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
176 if (this.isValidSearch()) {
177 String title = NbBundle.getMessage(this.getClass(),
"FileSearchPanel.search.results.title", ++resultWindowCount);
178 String pathText = NbBundle.getMessage(this.getClass(),
"FileSearchPanel.search.results.pathText");
181 Case currentCase = Case.getCurrentCaseThrows();
182 long totalMatches = 0;
183 List<AbstractFile> contentList = null;
185 SleuthkitCase tskDb = currentCase.getSleuthkitCase();
186 contentList = tskDb.findAllFilesWhere(this.getQuery());
188 }
catch (TskCoreException ex) {
189 Logger logger = Logger.getLogger(this.getClass().getName());
190 logger.log(Level.WARNING,
"Error while trying to get the number of matches.", ex);
193 if (contentList == null) {
194 contentList = Collections.<AbstractFile>emptyList();
197 SearchNode sn =
new SearchNode(contentList);
198 TableFilterNode tableFilterNode =
new TableFilterNode(sn,
true, sn.getName());
199 final TopComponent searchResultWin;
200 if (contentList.isEmpty()) {
201 Node emptyNode =
new TableFilterNode(
new EmptyNode(Bundle.FileSearchPanel_emptyNode_display_text()),
true);
202 searchResultWin = DataResultTopComponent.createInstance(title, pathText,
205 searchResultWin = DataResultTopComponent.createInstance(title, pathText,
206 tableFilterNode, contentList.size());
208 searchResultWin.requestActive();
215 if (totalMatches > 10000) {
217 String msg = NbBundle.getMessage(this.getClass(),
"FileSearchPanel.search.results.msg", totalMatches);
218 String details = NbBundle.getMessage(this.getClass(),
"FileSearchPanel.search.results.details");
219 MessageNotifyUtil.Notify.info(msg, details);
222 throw new FilterValidationException(
223 NbBundle.getMessage(
this.getClass(),
"FileSearchPanel.search.exception.noFilterSelected.msg"));
225 }
catch (FilterValidationException | NoCurrentCaseException ex) {
226 NotifyDescriptor d =
new NotifyDescriptor.Message(
227 NbBundle.getMessage(
this.getClass(),
"FileSearchPanel.search.validationErr.msg", ex.getMessage()));
228 DialogDisplayer.getDefault().notify(d);
230 this.setCursor(null);
252 private String getQuery() throws FilterValidationException {
257 for (FileSearchFilter f : this.getEnabledFilters()) {
258 String result = f.getPredicate();
259 if (!result.isEmpty()) {
261 query +=
" AND (" + result +
")";
263 query +=
" (" + result +
")";
269 if (query.isEmpty()) {
270 throw new FilterValidationException(EMPTY_WHERE_CLAUSE);
275 private Collection<FileSearchFilter> getFilters() {
279 private Collection<FileSearchFilter> getEnabledFilters() {
280 Collection<FileSearchFilter> enabledFilters =
new ArrayList<>();
282 for (FileSearchFilter f : this.getFilters()) {
284 enabledFilters.add(f);
288 return enabledFilters;
291 void addListenerToAll(ActionListener l) {
292 searchButton.addActionListener(l);
293 for (FileSearchFilter fsf : getFilters()) {
294 fsf.addActionListener(l);
303 @SuppressWarnings(
"unchecked")
305 private
void initComponents() {
307 filterPanel =
new javax.swing.JPanel();
308 searchButton =
new javax.swing.JButton();
309 errorLabel =
new javax.swing.JLabel();
311 setPreferredSize(
new java.awt.Dimension(300, 300));
313 filterPanel.setBorder(javax.swing.BorderFactory.createEmptyBorder(10, 10, 10, 10));
314 filterPanel.setPreferredSize(
new java.awt.Dimension(300, 400));
315 filterPanel.setLayout(
new javax.swing.BoxLayout(filterPanel, javax.swing.BoxLayout.Y_AXIS));
317 searchButton.setText(
org.openide.util.NbBundle.getMessage(FileSearchPanel.class,
"FileSearchPanel.searchButton.text"));
319 errorLabel.setText(
org.openide.util.NbBundle.getMessage(FileSearchPanel.class,
"FileSearchPanel.errorLabel.text"));
320 errorLabel.setForeground(
new java.awt.Color(255, 51, 51));
322 javax.swing.GroupLayout layout =
new javax.swing.GroupLayout(
this);
323 this.setLayout(layout);
324 layout.setHorizontalGroup(
325 layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
326 .addComponent(filterPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
327 .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
329 .addComponent(errorLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
330 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
331 .addComponent(searchButton)
334 layout.setVerticalGroup(
335 layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
336 .addGroup(layout.createSequentialGroup()
337 .addComponent(filterPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 266, Short.MAX_VALUE)
338 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
339 .addComponent(searchButton)
340 .addComponent(errorLabel))
346 private javax.swing.JLabel errorLabel;
347 private javax.swing.JPanel filterPanel;
348 private javax.swing.JButton searchButton;