Autopsy 4.22.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 */
19package org.sleuthkit.autopsy.filesearch;
20
21import java.awt.Component;
22import java.awt.Cursor;
23import java.awt.GridLayout;
24import java.awt.event.ActionEvent;
25import java.awt.event.ActionListener;
26import java.beans.PropertyChangeEvent;
27import java.beans.PropertyChangeListener;
28import java.util.ArrayList;
29import java.util.Collection;
30import java.util.Collections;
31import java.util.List;
32import java.util.concurrent.CancellationException;
33import java.util.concurrent.ExecutionException;
34import java.util.logging.Level;
35import javax.swing.JLabel;
36import javax.swing.JPanel;
37import javax.swing.SwingWorker;
38import javax.swing.border.EmptyBorder;
39import org.openide.DialogDisplayer;
40import org.openide.NotifyDescriptor;
41import org.openide.nodes.Node;
42import org.openide.util.NbBundle;
43import org.sleuthkit.autopsy.casemodule.Case;
44import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
45import org.sleuthkit.autopsy.corecomponents.DataResultTopComponent;
46import org.sleuthkit.autopsy.corecomponents.TableFilterNode;
47import org.sleuthkit.autopsy.coreutils.Logger;
48import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
49import org.sleuthkit.autopsy.datamodel.EmptyNode;
50import org.sleuthkit.autopsy.filesearch.DeletedFilesSearchPanel.DeletedFileSearchFilter;
51import org.sleuthkit.autopsy.filesearch.FileSearchFilter.FilterValidationException;
52import org.sleuthkit.datamodel.AbstractFile;
53import org.sleuthkit.datamodel.SleuthkitCase;
54import org.sleuthkit.datamodel.TskCoreException;
55
59@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
60class 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 {
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 {
283 NbBundle.getMessage(this.getClass(), "FileSearchPanel.search.exception.noFilterSelected.msg"));
284 }
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}
static DataResultTopComponent createInstance(String title, String description, Node node, int childNodeCount)
synchronized static Logger getLogger(String name)
Definition Logger.java:124

Copyright © 2012-2024 Sleuth Kit Labs. Generated on:
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.