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;
58 @SuppressWarnings(
"PMD.SingularField")
59 class FileSearchPanel extends javax.swing.JPanel {
61 private static final Logger logger = Logger.getLogger(FileSearchPanel.class.getName());
62 private static final long serialVersionUID = 1L;
63 private final List<FileSearchFilter> filters =
new ArrayList<>();
64 private static int resultWindowCount = 0;
65 private static final MimeTypeFilter mimeTypeFilter =
new MimeTypeFilter();
66 private static final DataSourceFilter dataSourceFilter =
new DataSourceFilter();
67 private static final String EMPTY_WHERE_CLAUSE = NbBundle.getMessage(DateSearchFilter.class,
"FileSearchPanel.emptyWhereClause.text");
68 private static SwingWorker<TableFilterNode, Void> searchWorker = null;
79 customizeComponents();
85 private void customizeComponents() {
87 JLabel label =
new JLabel(NbBundle.getMessage(
this.getClass(),
"FileSearchPanel.custComp.label.text"));
88 label.setAlignmentX(Component.LEFT_ALIGNMENT);
89 label.setBorder(
new EmptyBorder(0, 0, 10, 0));
91 JPanel panel1 =
new JPanel();
92 panel1.setLayout(
new GridLayout(1, 2));
93 panel1.add(
new JLabel(
""));
94 JPanel panel2 =
new JPanel();
95 panel2.setLayout(
new GridLayout(1, 2, 20, 0));
96 JPanel panel3 =
new JPanel();
97 panel3.setLayout(
new GridLayout(1, 2, 20, 0));
98 JPanel panel4 =
new JPanel();
99 panel4.setLayout(
new GridLayout(1, 2, 20, 0));
100 JPanel panel5 =
new JPanel();
101 panel5.setLayout(
new GridLayout(1, 2, 20, 0));
104 NameSearchFilter nameFilter =
new NameSearchFilter();
105 SizeSearchFilter sizeFilter =
new SizeSearchFilter();
106 DateSearchFilter dateFilter =
new DateSearchFilter();
107 KnownStatusSearchFilter knowStatusFilter =
new KnownStatusSearchFilter();
108 HashSearchFilter hashFilter =
new HashSearchFilter();
109 panel2.add(
new FilterArea(NbBundle.getMessage(
this.getClass(),
"FileSearchPanel.filterTitle.name"), nameFilter));
111 panel3.add(
new FilterArea(NbBundle.getMessage(
this.getClass(),
"FileSearchPanel.filterTitle.metadata"), sizeFilter));
113 panel2.add(
new FilterArea(NbBundle.getMessage(
this.getClass(),
"FileSearchPanel.filterTitle.metadata"), dateFilter));
114 panel3.add(
new FilterArea(NbBundle.getMessage(
this.getClass(),
"FileSearchPanel.filterTitle.knownStatus"), knowStatusFilter));
116 panel5.add(
new FilterArea(NbBundle.getMessage(
this.getClass(),
"HashSearchPanel.md5CheckBox.text"), hashFilter));
117 panel5.add(
new JLabel(
""));
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 filterPanel.add(panel1);
121 filterPanel.add(panel2);
122 filterPanel.add(panel3);
123 filterPanel.add(panel4);
124 filterPanel.add(panel5);
126 filters.add(nameFilter);
127 filters.add(sizeFilter);
128 filters.add(dateFilter);
129 filters.add(knowStatusFilter);
130 filters.add(hashFilter);
131 filters.add(mimeTypeFilter);
132 filters.add(dataSourceFilter);
134 for (FileSearchFilter filter : this.getFilters()) {
135 filter.addPropertyChangeListener(
new PropertyChangeListener() {
137 public void propertyChange(PropertyChangeEvent evt) {
138 searchButton.setEnabled(isValidSearch());
142 addListenerToAll(
new ActionListener() {
144 public void actionPerformed(ActionEvent e) {
148 searchButton.setEnabled(isValidSearch());
156 void setDataSourceFilter(
long dataSourceId) {
157 dataSourceFilter.setSelectedDataSource(dataSourceId);
163 private boolean isValidSearch() {
164 boolean enabled =
false;
165 for (FileSearchFilter filter : this.getFilters()) {
166 if (filter.isEnabled()) {
168 if (!filter.isValid()) {
169 errorLabel.setText(filter.getLastError());
174 errorLabel.setText(
"");
182 @NbBundle.Messages({
"FileSearchPanel.emptyNode.display.text=No results found.",
183 "FileSearchPanel.searchingNode.display.text=Performing file search by attributes. Please wait.",
184 "FileSearchPanel.searchingPath.text=File Search In Progress",
185 "FileSearchPanel.cancelledSearch.text=Search Was Cancelled"})
186 private void search() {
187 if (searchWorker != null && searchWorker.isDone()) {
188 searchWorker.cancel(
true);
191 if (this.isValidSearch()) {
193 Case currentCase = Case.getCurrentCaseThrows();
194 Node emptyNode =
new TableFilterNode(
new EmptyNode(Bundle.FileSearchPanel_searchingNode_display_text()),
true);
195 String title = NbBundle.getMessage(this.getClass(),
"FileSearchPanel.search.results.title", ++resultWindowCount);
196 String pathText = Bundle.FileSearchPanel_searchingPath_text();
197 final DataResultTopComponent searchResultWin = DataResultTopComponent.createInstance(title, pathText,
199 searchResultWin.requestActive();
200 searchResultWin.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
201 searchWorker =
new SwingWorker<TableFilterNode, Void>() {
202 List<AbstractFile> contentList = null;
205 protected TableFilterNode doInBackground() throws Exception {
207 SleuthkitCase tskDb = currentCase.getSleuthkitCase();
208 contentList = tskDb.findAllFilesWhere(getQuery());
210 }
catch (TskCoreException ex) {
211 Logger logger = Logger.getLogger(this.getClass().getName());
212 logger.log(Level.WARNING,
"Error while trying to get the number of matches.", ex);
214 if (contentList == null) {
215 contentList = Collections.<AbstractFile>emptyList();
217 if (contentList.isEmpty()) {
218 return new TableFilterNode(
new EmptyNode(Bundle.FileSearchPanel_emptyNode_display_text()),
true);
220 SearchNode sn =
new SearchNode(contentList);
221 return new TableFilterNode(sn,
true, sn.getName());
225 protected void done() {
226 String pathText = NbBundle.getMessage(this.getClass(),
"FileSearchPanel.search.results.pathText");
228 TableFilterNode tableFilterNode =
get();
229 if (tableFilterNode == null) {
230 tableFilterNode =
new TableFilterNode(
new EmptyNode(Bundle.FileSearchPanel_emptyNode_display_text()),
true);
232 if (searchResultWin != null && searchResultWin.isOpened()) {
233 searchResultWin.setNode(tableFilterNode);
234 searchResultWin.requestActive();
242 if (contentList.size() > 10000) {
244 String msg = NbBundle.getMessage(this.getClass(),
"FileSearchPanel.search.results.msg", contentList.size());
245 String details = NbBundle.getMessage(this.getClass(),
"FileSearchPanel.search.results.details");
246 MessageNotifyUtil.Notify.info(msg, details);
248 }
catch (InterruptedException | ExecutionException ex) {
249 logger.log(Level.SEVERE,
"Error while performing file search by attributes", ex);
250 }
catch (CancellationException ex) {
251 if (searchResultWin != null && searchResultWin.isOpened()) {
252 Node emptyNode =
new TableFilterNode(
new EmptyNode(Bundle.FileSearchPanel_cancelledSearch_text()),
true);
253 searchResultWin.setNode(emptyNode);
254 pathText = Bundle.FileSearchPanel_cancelledSearch_text();
256 logger.log(Level.INFO,
"File search by attributes was cancelled", ex);
258 if (searchResultWin != null && searchResultWin.isOpened()) {
259 searchResultWin.setPath(pathText);
260 searchResultWin.requestActive();
261 searchResultWin.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
266 if (searchResultWin != null && searchResultWin.isOpened()) {
267 searchResultWin.addPropertyChangeListener(
new PropertyChangeListener() {
269 public void propertyChange(PropertyChangeEvent evt) {
270 if (evt.getPropertyName().equals(
"tcClosed") && !searchWorker.isDone() && evt.getOldValue() == null) {
271 searchWorker.cancel(
true);
272 logger.log(Level.INFO,
"User has closed the results window while search was in progress, search will be cancelled");
277 searchWorker.execute();
279 throw new FilterValidationException(
280 NbBundle.getMessage(
this.getClass(),
"FileSearchPanel.search.exception.noFilterSelected.msg"));
282 }
catch (FilterValidationException | NoCurrentCaseException ex) {
283 NotifyDescriptor d =
new NotifyDescriptor.Message(
284 NbBundle.getMessage(
this.getClass(),
"FileSearchPanel.search.validationErr.msg", ex.getMessage()));
285 DialogDisplayer.getDefault().notify(d);
307 private String getQuery() throws FilterValidationException {
312 for (FileSearchFilter f : this.getEnabledFilters()) {
313 String result = f.getPredicate();
314 if (!result.isEmpty()) {
316 query +=
" AND (" + result +
")";
318 query +=
" (" + result +
")";
324 if (query.isEmpty()) {
325 throw new FilterValidationException(EMPTY_WHERE_CLAUSE);
330 private Collection<FileSearchFilter> getFilters() {
334 private Collection<FileSearchFilter> getEnabledFilters() {
335 Collection<FileSearchFilter> enabledFilters =
new ArrayList<>();
337 for (FileSearchFilter f : this.getFilters()) {
339 enabledFilters.add(f);
343 return enabledFilters;
350 void resetCaseDependentFilters() {
351 dataSourceFilter.resetDataSourceFilter();
352 mimeTypeFilter.resetMimeTypeFilter();
355 void addListenerToAll(ActionListener l) {
356 searchButton.addActionListener(l);
357 for (FileSearchFilter fsf : getFilters()) {
358 fsf.addActionListener(l);
367 @SuppressWarnings(
"unchecked")
369 private
void initComponents() {
371 filterPanel =
new javax.swing.JPanel();
372 searchButton =
new javax.swing.JButton();
373 errorLabel =
new javax.swing.JLabel();
375 setPreferredSize(
new java.awt.Dimension(300, 300));
377 filterPanel.setBorder(javax.swing.BorderFactory.createEmptyBorder(10, 10, 10, 10));
378 filterPanel.setPreferredSize(
new java.awt.Dimension(300, 400));
379 filterPanel.setLayout(
new javax.swing.BoxLayout(filterPanel, javax.swing.BoxLayout.Y_AXIS));
381 searchButton.setText(
org.openide.util.NbBundle.getMessage(FileSearchPanel.class,
"FileSearchPanel.searchButton.text"));
383 errorLabel.setText(
org.openide.util.NbBundle.getMessage(FileSearchPanel.class,
"FileSearchPanel.errorLabel.text"));
384 errorLabel.setForeground(
new java.awt.Color(255, 51, 51));
386 javax.swing.GroupLayout layout =
new javax.swing.GroupLayout(
this);
387 this.setLayout(layout);
388 layout.setHorizontalGroup(
389 layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
390 .addComponent(filterPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
391 .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
393 .addComponent(errorLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
394 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
395 .addComponent(searchButton)
398 layout.setVerticalGroup(
399 layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
400 .addGroup(layout.createSequentialGroup()
401 .addComponent(filterPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 266, Short.MAX_VALUE)
402 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
403 .addComponent(searchButton)
404 .addComponent(errorLabel))
410 private javax.swing.JLabel errorLabel;
411 private javax.swing.JPanel filterPanel;
412 private javax.swing.JButton searchButton;