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 MimeTypeFilter mimeTypeFilter =
new MimeTypeFilter();
66 private static 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();
110 panel2.add(
new FilterArea(NbBundle.getMessage(
this.getClass(),
"FileSearchPanel.filterTitle.name"), nameFilter));
112 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 panel5.add(
new JLabel(
""));
119 panel4.add(
new FilterArea(NbBundle.getMessage(
this.getClass(),
"FileSearchPanel.filterTitle.metadata"), mimeTypeFilter));
120 panel4.add(
new FilterArea(NbBundle.getMessage(
this.getClass(),
"DataSourcePanel.dataSourceCheckBox.text"), dataSourceFilter));
121 filterPanel.add(panel1);
122 filterPanel.add(panel2);
123 filterPanel.add(panel3);
124 filterPanel.add(panel4);
125 filterPanel.add(panel5);
127 filters.add(nameFilter);
128 filters.add(sizeFilter);
129 filters.add(dateFilter);
130 filters.add(knowStatusFilter);
131 filters.add(hashFilter);
132 filters.add(mimeTypeFilter);
133 filters.add(dataSourceFilter);
135 for (FileSearchFilter filter : this.getFilters()) {
136 filter.addPropertyChangeListener(
new PropertyChangeListener() {
138 public void propertyChange(PropertyChangeEvent evt) {
139 searchButton.setEnabled(isValidSearch());
143 addListenerToAll(
new ActionListener() {
145 public void actionPerformed(ActionEvent e) {
149 searchButton.setEnabled(isValidSearch());
155 private boolean isValidSearch() {
156 boolean enabled =
false;
157 for (FileSearchFilter filter : this.getFilters()) {
158 if (filter.isEnabled()) {
160 if (!filter.isValid()) {
161 errorLabel.setText(filter.getLastError());
166 errorLabel.setText(
"");
174 @NbBundle.Messages({
"FileSearchPanel.emptyNode.display.text=No results found.",
175 "FileSearchPanel.searchingNode.display.text=Performing file search by attributes. Please wait.",
176 "FileSearchPanel.searchingPath.text=File Search In Progress",
177 "FileSearchPanel.cancelledSearch.text=Search Was Cancelled"})
178 private void search() {
179 if (searchWorker != null && searchWorker.isDone()) {
180 searchWorker.cancel(
true);
183 if (this.isValidSearch()) {
185 Case currentCase = Case.getCurrentCaseThrows();
186 Node emptyNode =
new TableFilterNode(
new EmptyNode(Bundle.FileSearchPanel_searchingNode_display_text()),
true);
187 String title = NbBundle.getMessage(this.getClass(),
"FileSearchPanel.search.results.title", ++resultWindowCount);
188 String pathText = Bundle.FileSearchPanel_searchingPath_text();
189 final DataResultTopComponent searchResultWin = DataResultTopComponent.createInstance(title, pathText,
191 searchResultWin.requestActive();
192 searchResultWin.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
193 searchWorker =
new SwingWorker<TableFilterNode, Void>() {
194 List<AbstractFile> contentList = null;
197 protected TableFilterNode doInBackground() throws Exception {
199 SleuthkitCase tskDb = currentCase.getSleuthkitCase();
200 contentList = tskDb.findAllFilesWhere(getQuery());
202 }
catch (TskCoreException ex) {
203 Logger logger = Logger.getLogger(this.getClass().getName());
204 logger.log(Level.WARNING,
"Error while trying to get the number of matches.", ex);
206 if (contentList == null) {
207 contentList = Collections.<AbstractFile>emptyList();
209 if (contentList.isEmpty()) {
210 return new TableFilterNode(
new EmptyNode(Bundle.FileSearchPanel_emptyNode_display_text()),
true);
212 SearchNode sn =
new SearchNode(contentList);
213 return new TableFilterNode(sn,
true, sn.getName());
217 protected void done() {
218 String pathText = NbBundle.getMessage(this.getClass(),
"FileSearchPanel.search.results.pathText");
220 TableFilterNode tableFilterNode =
get();
221 if (tableFilterNode == null) {
222 tableFilterNode =
new TableFilterNode(
new EmptyNode(Bundle.FileSearchPanel_emptyNode_display_text()),
true);
224 if (searchResultWin != null && searchResultWin.isOpened()) {
225 searchResultWin.setNode(tableFilterNode);
226 searchResultWin.requestActive();
234 if (contentList.size() > 10000) {
236 String msg = NbBundle.getMessage(this.getClass(),
"FileSearchPanel.search.results.msg", contentList.size());
237 String details = NbBundle.getMessage(this.getClass(),
"FileSearchPanel.search.results.details");
238 MessageNotifyUtil.Notify.info(msg, details);
240 }
catch (InterruptedException | ExecutionException ex) {
241 logger.log(Level.SEVERE,
"Error while performing file search by attributes", ex);
242 }
catch (CancellationException ex) {
243 if (searchResultWin != null && searchResultWin.isOpened()) {
244 Node emptyNode =
new TableFilterNode(
new EmptyNode(Bundle.FileSearchPanel_cancelledSearch_text()),
true);
245 searchResultWin.setNode(emptyNode);
246 pathText = Bundle.FileSearchPanel_cancelledSearch_text();
248 logger.log(Level.INFO,
"File search by attributes was cancelled", ex);
250 if (searchResultWin != null && searchResultWin.isOpened()) {
251 searchResultWin.setPath(pathText);
252 searchResultWin.requestActive();
253 searchResultWin.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
258 if (searchResultWin != null && searchResultWin.isOpened()) {
259 searchResultWin.addPropertyChangeListener(
new PropertyChangeListener() {
261 public void propertyChange(PropertyChangeEvent evt) {
262 if (evt.getPropertyName().equals(
"tcClosed") && !searchWorker.isDone() && evt.getOldValue() == null) {
263 searchWorker.cancel(
true);
264 logger.log(Level.INFO,
"User has closed the results window while search was in progress, search will be cancelled");
269 searchWorker.execute();
271 throw new FilterValidationException(
272 NbBundle.getMessage(
this.getClass(),
"FileSearchPanel.search.exception.noFilterSelected.msg"));
274 }
catch (FilterValidationException | NoCurrentCaseException ex) {
275 NotifyDescriptor d =
new NotifyDescriptor.Message(
276 NbBundle.getMessage(
this.getClass(),
"FileSearchPanel.search.validationErr.msg", ex.getMessage()));
277 DialogDisplayer.getDefault().notify(d);
299 private String getQuery() throws FilterValidationException {
304 for (FileSearchFilter f : this.getEnabledFilters()) {
305 String result = f.getPredicate();
306 if (!result.isEmpty()) {
308 query +=
" AND (" + result +
")";
310 query +=
" (" + result +
")";
316 if (query.isEmpty()) {
317 throw new FilterValidationException(EMPTY_WHERE_CLAUSE);
322 private Collection<FileSearchFilter> getFilters() {
326 private Collection<FileSearchFilter> getEnabledFilters() {
327 Collection<FileSearchFilter> enabledFilters =
new ArrayList<>();
329 for (FileSearchFilter f : this.getFilters()) {
331 enabledFilters.add(f);
335 return enabledFilters;
342 void resetCaseDependentFilters() {
343 dataSourceFilter.resetDataSourceFilter();
344 mimeTypeFilter.resetMimeTypeFilter();
347 void addListenerToAll(ActionListener l) {
348 searchButton.addActionListener(l);
349 for (FileSearchFilter fsf : getFilters()) {
350 fsf.addActionListener(l);
359 @SuppressWarnings(
"unchecked")
361 private
void initComponents() {
363 filterPanel =
new javax.swing.JPanel();
364 searchButton =
new javax.swing.JButton();
365 errorLabel =
new javax.swing.JLabel();
367 setPreferredSize(
new java.awt.Dimension(300, 300));
369 filterPanel.setBorder(javax.swing.BorderFactory.createEmptyBorder(10, 10, 10, 10));
370 filterPanel.setPreferredSize(
new java.awt.Dimension(300, 400));
371 filterPanel.setLayout(
new javax.swing.BoxLayout(filterPanel, javax.swing.BoxLayout.Y_AXIS));
373 searchButton.setText(
org.openide.util.NbBundle.getMessage(FileSearchPanel.class,
"FileSearchPanel.searchButton.text"));
375 errorLabel.setText(
org.openide.util.NbBundle.getMessage(FileSearchPanel.class,
"FileSearchPanel.errorLabel.text"));
376 errorLabel.setForeground(
new java.awt.Color(255, 51, 51));
378 javax.swing.GroupLayout layout =
new javax.swing.GroupLayout(
this);
379 this.setLayout(layout);
380 layout.setHorizontalGroup(
381 layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
382 .addComponent(filterPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
383 .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
385 .addComponent(errorLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
386 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
387 .addComponent(searchButton)
390 layout.setVerticalGroup(
391 layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
392 .addGroup(layout.createSequentialGroup()
393 .addComponent(filterPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 266, Short.MAX_VALUE)
394 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
395 .addComponent(searchButton)
396 .addComponent(errorLabel))
402 private javax.swing.JLabel errorLabel;
403 private javax.swing.JPanel filterPanel;
404 private javax.swing.JButton searchButton;