Autopsy  4.19.1
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;
51 import org.sleuthkit.datamodel.AbstractFile;
52 import org.sleuthkit.datamodel.SleuthkitCase;
53 import org.sleuthkit.datamodel.TskCoreException;
54 
58 @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
59 class FileSearchPanel extends javax.swing.JPanel {
60 
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; //keep track of result windows so they get unique names
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;
69 
70  enum EVENT {
71  CHECKED
72  }
73 
77  FileSearchPanel() {
78  initComponents();
79  customizeComponents();
80  }
81 
85  private void customizeComponents() {
86 
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));
90 
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));
102 
103  // Create and add filter areas
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));
110 
111  panel3.add(new FilterArea(NbBundle.getMessage(this.getClass(), "FileSearchPanel.filterTitle.metadata"), sizeFilter));
112 
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));
115 
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);
125 
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);
133 
134  for (FileSearchFilter filter : this.getFilters()) {
135  filter.addPropertyChangeListener(new PropertyChangeListener() {
136  @Override
137  public void propertyChange(PropertyChangeEvent evt) {
138  searchButton.setEnabled(isValidSearch());
139  }
140  });
141  }
142  addListenerToAll(new ActionListener() {
143  @Override
144  public void actionPerformed(ActionEvent e) {
145  search();
146  }
147  });
148  searchButton.setEnabled(isValidSearch());
149  }
150 
156  void setDataSourceFilter(long dataSourceId) {
157  dataSourceFilter.setSelectedDataSource(dataSourceId);
158  }
159 
163  private boolean isValidSearch() {
164  boolean enabled = false;
165  for (FileSearchFilter filter : this.getFilters()) {
166  if (filter.isEnabled()) {
167  enabled = true;
168  if (!filter.isValid()) {
169  errorLabel.setText(filter.getLastError());
170  return false;
171  }
172  }
173  }
174  errorLabel.setText("");
175  return enabled;
176  }
177 
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);
189  }
190  try {
191  if (this.isValidSearch()) {
192  // try to get the number of matches first
193  Case currentCase = Case.getCurrentCaseThrows(); // get the most updated case
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,
198  emptyNode, 0);
199  searchResultWin.requestActive(); // make it the active top component
200  searchResultWin.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
201  searchWorker = new SwingWorker<TableFilterNode, Void>() {
202  List<AbstractFile> contentList = null;
203 
204  @Override
205  protected TableFilterNode doInBackground() throws Exception {
206  try {
207  SleuthkitCase tskDb = currentCase.getSleuthkitCase();
208  contentList = tskDb.findAllFilesWhere(getQuery());
209 
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); //NON-NLS
213  }
214  if (contentList == null) {
215  contentList = Collections.<AbstractFile>emptyList();
216  }
217  if (contentList.isEmpty()) {
218  return new TableFilterNode(new EmptyNode(Bundle.FileSearchPanel_emptyNode_display_text()), true);
219  }
220  SearchNode sn = new SearchNode(contentList);
221  return new TableFilterNode(sn, true, sn.getName());
222  }
223 
224  @Override
225  protected void done() {
226  String pathText = NbBundle.getMessage(this.getClass(), "FileSearchPanel.search.results.pathText");
227  try {
228  TableFilterNode tableFilterNode = get();
229  if (tableFilterNode == null) { //just incase this get() gets modified to return null or somehow can return null
230  tableFilterNode = new TableFilterNode(new EmptyNode(Bundle.FileSearchPanel_emptyNode_display_text()), true);
231  }
232  if (searchResultWin != null && searchResultWin.isOpened()) {
233  searchResultWin.setNode(tableFilterNode);
234  searchResultWin.requestActive(); // make it the active top component
235  }
242  if (contentList.size() > 10000) {
243  // show info
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);
247  }
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();
255  }
256  logger.log(Level.INFO, "File search by attributes was cancelled", ex);
257  } finally {
258  if (searchResultWin != null && searchResultWin.isOpened()) {
259  searchResultWin.setPath(pathText);
260  searchResultWin.requestActive(); // make it the active top component
261  searchResultWin.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
262  }
263  }
264  }
265  };
266  if (searchResultWin != null && searchResultWin.isOpened()) {
267  searchResultWin.addPropertyChangeListener(new PropertyChangeListener() {
268  @Override
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");
273  }
274  }
275  });
276  }
277  searchWorker.execute();
278  } else {
279  throw new FilterValidationException(
280  NbBundle.getMessage(this.getClass(), "FileSearchPanel.search.exception.noFilterSelected.msg"));
281  }
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);
286  }
287  }
288 
307  private String getQuery() throws FilterValidationException {
308 
309  //String query = "SELECT " + tempQuery + " FROM tsk_files WHERE ";
310  String query = "";
311  int i = 0;
312  for (FileSearchFilter f : this.getEnabledFilters()) {
313  String result = f.getPredicate();
314  if (!result.isEmpty()) {
315  if (i > 0) {
316  query += " AND (" + result + ")"; //NON-NLS
317  } else {
318  query += " (" + result + ")"; //NON-NLS
319  }
320  ++i;
321  }
322  }
323 
324  if (query.isEmpty()) {
325  throw new FilterValidationException(EMPTY_WHERE_CLAUSE);
326  }
327  return query;
328  }
329 
330  private Collection<FileSearchFilter> getFilters() {
331  return filters;
332  }
333 
334  private Collection<FileSearchFilter> getEnabledFilters() {
335  Collection<FileSearchFilter> enabledFilters = new ArrayList<>();
336 
337  for (FileSearchFilter f : this.getFilters()) {
338  if (f.isEnabled()) {
339  enabledFilters.add(f);
340  }
341  }
342 
343  return enabledFilters;
344  }
345 
350  void resetCaseDependentFilters() {
351  dataSourceFilter.resetDataSourceFilter();
352  mimeTypeFilter.resetMimeTypeFilter();
353  }
354 
355  void addListenerToAll(ActionListener l) {
356  searchButton.addActionListener(l);
357  for (FileSearchFilter fsf : getFilters()) {
358  fsf.addActionListener(l);
359  }
360  }
361 
367  @SuppressWarnings("unchecked")
368  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
369  private void initComponents() {
370 
371  filterPanel = new javax.swing.JPanel();
372  searchButton = new javax.swing.JButton();
373  errorLabel = new javax.swing.JLabel();
374 
375  setPreferredSize(new java.awt.Dimension(300, 300));
376 
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));
380 
381  searchButton.setText(org.openide.util.NbBundle.getMessage(FileSearchPanel.class, "FileSearchPanel.searchButton.text")); // NOI18N
382 
383  errorLabel.setText(org.openide.util.NbBundle.getMessage(FileSearchPanel.class, "FileSearchPanel.errorLabel.text")); // NOI18N
384  errorLabel.setForeground(new java.awt.Color(255, 51, 51));
385 
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()
392  .addContainerGap()
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)
396  .addContainerGap())
397  );
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))
405  .addContainerGap())
406  );
407  }// </editor-fold>//GEN-END:initComponents
408 
409  // Variables declaration - do not modify//GEN-BEGIN:variables
410  private javax.swing.JLabel errorLabel;
411  private javax.swing.JPanel filterPanel;
412  private javax.swing.JButton searchButton;
413  // End of variables declaration//GEN-END:variables
414 }

Copyright © 2012-2021 Basis Technology. Generated on: Thu Sep 30 2021
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.