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.concurrent.CancellationException;
33 import java.util.concurrent.ExecutionException;
34 import java.util.logging.Level;
35 import javax.swing.JLabel;
36 import javax.swing.JPanel;
37 import javax.swing.SwingWorker;
38 import javax.swing.border.EmptyBorder;
39 import org.openide.DialogDisplayer;
40 import org.openide.NotifyDescriptor;
41 import org.openide.nodes.Node;
42 import org.openide.util.NbBundle;
59 @SuppressWarnings(
"PMD.SingularField")
60 class FileSearchPanel extends javax.swing.JPanel {
62 private static final Logger logger = Logger.getLogger(FileSearchPanel.class.getName());
63 private static final long serialVersionUID = 1L;
64 private final List<FileSearchFilter> filters =
new ArrayList<>();
65 private static int resultWindowCount = 0;
66 private static final MimeTypeFilter mimeTypeFilter =
new MimeTypeFilter();
67 private static final DataSourceFilter dataSourceFilter =
new DataSourceFilter();
68 private static final String EMPTY_WHERE_CLAUSE = NbBundle.getMessage(DateSearchFilter.class,
"FileSearchPanel.emptyWhereClause.text");
69 private static SwingWorker<TableFilterNode, Void> searchWorker = null;
80 customizeComponents();
86 private void customizeComponents() {
88 JLabel label =
new JLabel(NbBundle.getMessage(
this.getClass(),
"FileSearchPanel.custComp.label.text"));
89 label.setAlignmentX(Component.LEFT_ALIGNMENT);
90 label.setBorder(
new EmptyBorder(0, 0, 10, 0));
92 JPanel panel1 =
new JPanel();
93 panel1.setLayout(
new GridLayout(1, 2));
94 panel1.add(
new JLabel(
""));
95 JPanel panel2 =
new JPanel();
96 panel2.setLayout(
new GridLayout(1, 2, 20, 0));
97 JPanel panel3 =
new JPanel();
98 panel3.setLayout(
new GridLayout(1, 2, 20, 0));
99 JPanel panel4 =
new JPanel();
100 panel4.setLayout(
new GridLayout(1, 2, 20, 0));
101 JPanel panel5 =
new JPanel();
102 panel5.setLayout(
new GridLayout(1, 2, 20, 0));
105 NameSearchFilter nameFilter =
new NameSearchFilter();
106 SizeSearchFilter sizeFilter =
new SizeSearchFilter();
107 DateSearchFilter dateFilter =
new DateSearchFilter();
108 KnownStatusSearchFilter knowStatusFilter =
new KnownStatusSearchFilter();
109 DeletedFileSearchFilter deleltedFilter =
new DeletedFileSearchFilter();
110 HashSearchFilter hashFilter =
new HashSearchFilter();
111 panel2.add(
new FilterArea(NbBundle.getMessage(
this.getClass(),
"FileSearchPanel.filterTitle.name"), nameFilter));
113 panel3.add(
new FilterArea(NbBundle.getMessage(
this.getClass(),
"FileSearchPanel.filterTitle.metadata"), sizeFilter));
114 panel2.add(
new FilterArea(NbBundle.getMessage(
this.getClass(),
"FileSearchPanel.filterTitle.metadata"), dateFilter));
115 panel3.add(
new FilterArea(NbBundle.getMessage(
this.getClass(),
"FileSearchPanel.filterTitle.knownStatus"), knowStatusFilter));
117 panel5.add(
new FilterArea(NbBundle.getMessage(
this.getClass(),
"HashSearchPanel.md5CheckBox.text"), hashFilter));
118 panel4.add(
new FilterArea(NbBundle.getMessage(
this.getClass(),
"FileSearchPanel.filterTitle.metadata"), mimeTypeFilter));
119 panel4.add(
new FilterArea(NbBundle.getMessage(
this.getClass(),
"DataSourcePanel.dataSourceCheckBox.text"), dataSourceFilter));
120 panel5.add(
new FilterArea(NbBundle.getMessage(
this.getClass(),
"DeletedFilesSearchPanel.deletedCheckbox.text"), deleltedFilter));
122 filterPanel.add(panel1);
123 filterPanel.add(panel2);
124 filterPanel.add(panel3);
125 filterPanel.add(panel4);
126 filterPanel.add(panel5);
128 filters.add(nameFilter);
129 filters.add(sizeFilter);
130 filters.add(dateFilter);
131 filters.add(knowStatusFilter);
132 filters.add(hashFilter);
133 filters.add(mimeTypeFilter);
134 filters.add(dataSourceFilter);
135 filters.add(deleltedFilter);
137 for (FileSearchFilter filter : this.getFilters()) {
138 filter.addPropertyChangeListener(
new PropertyChangeListener() {
140 public void propertyChange(PropertyChangeEvent evt) {
141 searchButton.setEnabled(isValidSearch());
145 addListenerToAll(
new ActionListener() {
147 public void actionPerformed(ActionEvent e) {
151 searchButton.setEnabled(isValidSearch());
159 void setDataSourceFilter(
long dataSourceId) {
160 dataSourceFilter.setSelectedDataSource(dataSourceId);
166 private boolean isValidSearch() {
167 boolean enabled =
false;
168 for (FileSearchFilter filter : this.getFilters()) {
169 if (filter.isEnabled()) {
171 if (!filter.isValid()) {
172 errorLabel.setText(filter.getLastError());
177 errorLabel.setText(
"");
185 @NbBundle.Messages({
"FileSearchPanel.emptyNode.display.text=No results found.",
186 "FileSearchPanel.searchingNode.display.text=Performing file search by attributes. Please wait.",
187 "FileSearchPanel.searchingPath.text=File Search In Progress",
188 "FileSearchPanel.cancelledSearch.text=Search Was Cancelled"})
189 private void search() {
190 if (searchWorker != null && searchWorker.isDone()) {
191 searchWorker.cancel(
true);
194 if (this.isValidSearch()) {
196 Case currentCase = Case.getCurrentCaseThrows();
197 Node emptyNode =
new TableFilterNode(
new EmptyNode(Bundle.FileSearchPanel_searchingNode_display_text()),
true);
198 String title = NbBundle.getMessage(this.getClass(),
"FileSearchPanel.search.results.title", ++resultWindowCount);
199 String pathText = Bundle.FileSearchPanel_searchingPath_text();
200 final DataResultTopComponent searchResultWin = DataResultTopComponent.createInstance(title, pathText,
202 searchResultWin.requestActive();
203 searchResultWin.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
204 searchWorker =
new SwingWorker<TableFilterNode, Void>() {
205 List<AbstractFile> contentList = null;
208 protected TableFilterNode doInBackground() throws Exception {
210 SleuthkitCase tskDb = currentCase.getSleuthkitCase();
211 contentList = tskDb.findAllFilesWhere(getQuery());
213 }
catch (TskCoreException ex) {
214 Logger logger = Logger.getLogger(this.getClass().getName());
215 logger.log(Level.WARNING,
"Error while trying to get the number of matches.", ex);
217 if (contentList == null) {
218 contentList = Collections.<AbstractFile>emptyList();
220 if (contentList.isEmpty()) {
221 return new TableFilterNode(
new EmptyNode(Bundle.FileSearchPanel_emptyNode_display_text()),
true);
223 SearchNode sn =
new SearchNode(contentList);
224 return new TableFilterNode(sn,
true, sn.getName());
228 protected void done() {
229 String pathText = NbBundle.getMessage(this.getClass(),
"FileSearchPanel.search.results.pathText");
231 TableFilterNode tableFilterNode =
get();
232 if (tableFilterNode == null) {
233 tableFilterNode =
new TableFilterNode(
new EmptyNode(Bundle.FileSearchPanel_emptyNode_display_text()),
true);
235 if (searchResultWin != null && searchResultWin.isOpened()) {
236 searchResultWin.setNode(tableFilterNode);
237 searchResultWin.requestActive();
245 if (contentList.size() > 10000) {
247 String msg = NbBundle.getMessage(this.getClass(),
"FileSearchPanel.search.results.msg", contentList.size());
248 String details = NbBundle.getMessage(this.getClass(),
"FileSearchPanel.search.results.details");
249 MessageNotifyUtil.Notify.info(msg, details);
251 }
catch (InterruptedException | ExecutionException ex) {
252 logger.log(Level.SEVERE,
"Error while performing file search by attributes", ex);
253 }
catch (CancellationException ex) {
254 if (searchResultWin != null && searchResultWin.isOpened()) {
255 Node emptyNode =
new TableFilterNode(
new EmptyNode(Bundle.FileSearchPanel_cancelledSearch_text()),
true);
256 searchResultWin.setNode(emptyNode);
257 pathText = Bundle.FileSearchPanel_cancelledSearch_text();
259 logger.log(Level.INFO,
"File search by attributes was cancelled", ex);
261 if (searchResultWin != null && searchResultWin.isOpened()) {
262 searchResultWin.setPath(pathText);
263 searchResultWin.requestActive();
264 searchResultWin.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
269 if (searchResultWin != null && searchResultWin.isOpened()) {
270 searchResultWin.addPropertyChangeListener(
new PropertyChangeListener() {
272 public void propertyChange(PropertyChangeEvent evt) {
273 if (evt.getPropertyName().equals(
"tcClosed") && !searchWorker.isDone() && evt.getOldValue() == null) {
274 searchWorker.cancel(
true);
275 logger.log(Level.INFO,
"User has closed the results window while search was in progress, search will be cancelled");
280 searchWorker.execute();
282 throw new FilterValidationException(
283 NbBundle.getMessage(
this.getClass(),
"FileSearchPanel.search.exception.noFilterSelected.msg"));
285 }
catch (FilterValidationException | NoCurrentCaseException ex) {
286 NotifyDescriptor d =
new NotifyDescriptor.Message(
287 NbBundle.getMessage(
this.getClass(),
"FileSearchPanel.search.validationErr.msg", ex.getMessage()));
288 DialogDisplayer.getDefault().notify(d);
302 private String getQuery() throws FilterValidationException {
307 for (FileSearchFilter f : this.getEnabledFilters()) {
308 String result = f.getPredicate();
309 if (!result.isEmpty()) {
311 query +=
" AND (" + result +
")";
313 query +=
" (" + result +
")";
319 if (query.isEmpty()) {
320 throw new FilterValidationException(EMPTY_WHERE_CLAUSE);
325 private Collection<FileSearchFilter> getFilters() {
329 private Collection<FileSearchFilter> getEnabledFilters() {
330 Collection<FileSearchFilter> enabledFilters =
new ArrayList<>();
332 for (FileSearchFilter f : this.getFilters()) {
334 enabledFilters.add(f);
338 return enabledFilters;
345 void resetCaseDependentFilters() {
346 dataSourceFilter.resetDataSourceFilter();
347 mimeTypeFilter.resetMimeTypeFilter();
350 void addListenerToAll(ActionListener l) {
351 searchButton.addActionListener(l);
352 for (FileSearchFilter fsf : getFilters()) {
353 fsf.addActionListener(l);
357 void addCloseListener(ActionListener l) {
358 closeButton.addActionListener(l);
366 @SuppressWarnings(
"unchecked")
368 private
void initComponents() {
369 java.awt.GridBagConstraints gridBagConstraints;
371 filterPanel =
new javax.swing.JPanel();
372 buttonPanel =
new javax.swing.JPanel();
373 searchButton =
new javax.swing.JButton();
374 closeButton =
new javax.swing.JButton();
375 errorLabel =
new javax.swing.JLabel();
377 setPreferredSize(
new java.awt.Dimension(300, 300));
378 setLayout(
new java.awt.GridBagLayout());
380 filterPanel.setBorder(javax.swing.BorderFactory.createEmptyBorder(10, 10, 10, 10));
381 filterPanel.setPreferredSize(
new java.awt.Dimension(300, 400));
382 filterPanel.setLayout(
new javax.swing.BoxLayout(filterPanel, javax.swing.BoxLayout.Y_AXIS));
383 gridBagConstraints =
new java.awt.GridBagConstraints();
384 gridBagConstraints.gridx = 0;
385 gridBagConstraints.gridy = 0;
386 gridBagConstraints.gridwidth = 2;
387 gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
388 gridBagConstraints.weightx = 1.0;
389 gridBagConstraints.weighty = 1.0;
390 add(filterPanel, gridBagConstraints);
392 buttonPanel.setLayout(
new java.awt.GridLayout(1, 0, 3, 0));
394 searchButton.setText(
org.openide.util.NbBundle.getMessage(FileSearchPanel.class,
"FileSearchPanel.searchButton.text"));
395 buttonPanel.add(searchButton);
397 closeButton.setText(
org.openide.util.NbBundle.getMessage(FileSearchPanel.class,
"FileSearchPanel.closeButton.text"));
398 buttonPanel.add(closeButton);
400 gridBagConstraints =
new java.awt.GridBagConstraints();
401 gridBagConstraints.gridx = 1;
402 gridBagConstraints.gridy = 1;
403 gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
404 gridBagConstraints.insets =
new java.awt.Insets(0, 0, 5, 5);
405 add(buttonPanel, gridBagConstraints);
407 errorLabel.setForeground(
new java.awt.Color(255, 51, 51));
408 errorLabel.setText(
org.openide.util.NbBundle.getMessage(FileSearchPanel.class,
"FileSearchPanel.errorLabel.text"));
409 gridBagConstraints =
new java.awt.GridBagConstraints();
410 gridBagConstraints.gridx = 0;
411 gridBagConstraints.gridy = 1;
412 gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
413 gridBagConstraints.weightx = 1.0;
414 add(errorLabel, gridBagConstraints);
418 private javax.swing.JPanel buttonPanel;
419 private javax.swing.JButton closeButton;
420 private javax.swing.JLabel errorLabel;
421 private javax.swing.JPanel filterPanel;
422 private javax.swing.JButton searchButton;