Autopsy  4.21.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
FileSearchPanel.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2011-2021 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.filesearch;
20 
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;
52 import org.sleuthkit.datamodel.AbstractFile;
53 import org.sleuthkit.datamodel.SleuthkitCase;
54 import org.sleuthkit.datamodel.TskCoreException;
55 
59 @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
60 class FileSearchPanel extends javax.swing.JPanel {
61 
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; //keep track of result windows so they get unique names
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;
70 
71  enum EVENT {
72  CHECKED
73  }
74 
78  FileSearchPanel() {
79  initComponents();
80  customizeComponents();
81  }
82 
86  private void customizeComponents() {
87 
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));
91 
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));
103 
104  // Create and add filter areas
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));
112 
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));
116 
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));
121 
122  filterPanel.add(panel1);
123  filterPanel.add(panel2);
124  filterPanel.add(panel3);
125  filterPanel.add(panel4);
126  filterPanel.add(panel5);
127 
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);
136 
137  for (FileSearchFilter filter : this.getFilters()) {
138  filter.addPropertyChangeListener(new PropertyChangeListener() {
139  @Override
140  public void propertyChange(PropertyChangeEvent evt) {
141  searchButton.setEnabled(isValidSearch());
142  }
143  });
144  }
145  addListenerToAll(new ActionListener() {
146  @Override
147  public void actionPerformed(ActionEvent e) {
148  search();
149  }
150  });
151  searchButton.setEnabled(isValidSearch());
152  }
153 
159  void setDataSourceFilter(long dataSourceId) {
160  dataSourceFilter.setSelectedDataSource(dataSourceId);
161  }
162 
166  private boolean isValidSearch() {
167  boolean enabled = false;
168  for (FileSearchFilter filter : this.getFilters()) {
169  if (filter.isEnabled()) {
170  enabled = true;
171  if (!filter.isValid()) {
172  errorLabel.setText(filter.getLastError());
173  return false;
174  }
175  }
176  }
177  errorLabel.setText("");
178  return enabled;
179  }
180 
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);
192  }
193  try {
194  if (this.isValidSearch()) {
195  // try to get the number of matches first
196  Case currentCase = Case.getCurrentCaseThrows(); // get the most updated case
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,
201  emptyNode, 0);
202  searchResultWin.requestActive(); // make it the active top component
203  searchResultWin.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
204  searchWorker = new SwingWorker<TableFilterNode, Void>() {
205  List<AbstractFile> contentList = null;
206 
207  @Override
208  protected TableFilterNode doInBackground() throws Exception {
209  try {
210  SleuthkitCase tskDb = currentCase.getSleuthkitCase();
211  contentList = tskDb.findAllFilesWhere(getQuery());
212 
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); //NON-NLS
216  }
217  if (contentList == null) {
218  contentList = Collections.<AbstractFile>emptyList();
219  }
220  if (contentList.isEmpty()) {
221  return new TableFilterNode(new EmptyNode(Bundle.FileSearchPanel_emptyNode_display_text()), true);
222  }
223  SearchNode sn = new SearchNode(contentList);
224  return new TableFilterNode(sn, true, sn.getName());
225  }
226 
227  @Override
228  protected void done() {
229  String pathText = NbBundle.getMessage(this.getClass(), "FileSearchPanel.search.results.pathText");
230  try {
231  TableFilterNode tableFilterNode = get();
232  if (tableFilterNode == null) { //just incase this get() gets modified to return null or somehow can return null
233  tableFilterNode = new TableFilterNode(new EmptyNode(Bundle.FileSearchPanel_emptyNode_display_text()), true);
234  }
235  if (searchResultWin != null && searchResultWin.isOpened()) {
236  searchResultWin.setNode(tableFilterNode);
237  searchResultWin.requestActive(); // make it the active top component
238  }
245  if (contentList.size() > 10000) {
246  // show info
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);
250  }
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();
258  }
259  logger.log(Level.INFO, "File search by attributes was cancelled", ex);
260  } finally {
261  if (searchResultWin != null && searchResultWin.isOpened()) {
262  searchResultWin.setPath(pathText);
263  searchResultWin.requestActive(); // make it the active top component
264  searchResultWin.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
265  }
266  }
267  }
268  };
269  if (searchResultWin != null && searchResultWin.isOpened()) {
270  searchResultWin.addPropertyChangeListener(new PropertyChangeListener() {
271  @Override
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");
276  }
277  }
278  });
279  }
280  searchWorker.execute();
281  } else {
282  throw new FilterValidationException(
283  NbBundle.getMessage(this.getClass(), "FileSearchPanel.search.exception.noFilterSelected.msg"));
284  }
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);
289  }
290  }
291 
302  private String getQuery() throws FilterValidationException {
303 
304  //String query = "SELECT " + tempQuery + " FROM tsk_files WHERE ";
305  String query = "";
306  int i = 0;
307  for (FileSearchFilter f : this.getEnabledFilters()) {
308  String result = f.getPredicate();
309  if (!result.isEmpty()) {
310  if (i > 0) {
311  query += " AND (" + result + ")"; //NON-NLS
312  } else {
313  query += " (" + result + ")"; //NON-NLS
314  }
315  ++i;
316  }
317  }
318 
319  if (query.isEmpty()) {
320  throw new FilterValidationException(EMPTY_WHERE_CLAUSE);
321  }
322  return query;
323  }
324 
325  private Collection<FileSearchFilter> getFilters() {
326  return filters;
327  }
328 
329  private Collection<FileSearchFilter> getEnabledFilters() {
330  Collection<FileSearchFilter> enabledFilters = new ArrayList<>();
331 
332  for (FileSearchFilter f : this.getFilters()) {
333  if (f.isEnabled()) {
334  enabledFilters.add(f);
335  }
336  }
337 
338  return enabledFilters;
339  }
340 
345  void resetCaseDependentFilters() {
346  dataSourceFilter.resetDataSourceFilter();
347  mimeTypeFilter.resetMimeTypeFilter();
348  }
349 
350  void addListenerToAll(ActionListener l) {
351  searchButton.addActionListener(l);
352  for (FileSearchFilter fsf : getFilters()) {
353  fsf.addActionListener(l);
354  }
355  }
356 
357  void addCloseListener(ActionListener l) {
358  closeButton.addActionListener(l);
359  }
360 
366  @SuppressWarnings("unchecked")
367  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
368  private void initComponents() {
369  java.awt.GridBagConstraints gridBagConstraints;
370 
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();
376 
377  setPreferredSize(new java.awt.Dimension(300, 300));
378  setLayout(new java.awt.GridBagLayout());
379 
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);
391 
392  buttonPanel.setLayout(new java.awt.GridLayout(1, 0, 3, 0));
393 
394  searchButton.setText(org.openide.util.NbBundle.getMessage(FileSearchPanel.class, "FileSearchPanel.searchButton.text")); // NOI18N
395  buttonPanel.add(searchButton);
396 
397  closeButton.setText(org.openide.util.NbBundle.getMessage(FileSearchPanel.class, "FileSearchPanel.closeButton.text")); // NOI18N
398  buttonPanel.add(closeButton);
399 
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);
406 
407  errorLabel.setForeground(new java.awt.Color(255, 51, 51));
408  errorLabel.setText(org.openide.util.NbBundle.getMessage(FileSearchPanel.class, "FileSearchPanel.errorLabel.text")); // NOI18N
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);
415  }// </editor-fold>//GEN-END:initComponents
416 
417  // Variables declaration - do not modify//GEN-BEGIN:variables
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;
423  // End of variables declaration//GEN-END:variables
424 }

Copyright © 2012-2022 Basis Technology. Generated on: Tue Feb 6 2024
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.