Autopsy 4.22.1
Graphical digital forensics platform for The Sleuth Kit and other tools.
BaseDataSourceSummaryPanel.java
Go to the documentation of this file.
1/*
2 * Autopsy Forensic Browser
3 *
4 * Copyright 2020 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.datasourcesummary.ui;
20
21import java.beans.PropertyChangeEvent;
22import java.nio.file.Path;
23import java.nio.file.Paths;
24import java.util.Arrays;
25import java.util.Collections;
26import java.util.List;
27import java.util.Set;
28import java.util.logging.Level;
29import java.util.regex.Matcher;
30import java.util.regex.Pattern;
31import java.util.stream.Collectors;
32import javax.swing.JPanel;
33import javax.swing.SwingWorker;
34import org.apache.commons.collections.CollectionUtils;
35import org.apache.commons.io.FilenameUtils;
36import org.apache.commons.lang3.StringUtils;
37import org.openide.util.NbBundle.Messages;
38import org.sleuthkit.autopsy.casemodule.Case;
39import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
40import org.sleuthkit.autopsy.coreutils.Logger;
41import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult;
42import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
43import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents;
44import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataFetcher;
45import org.sleuthkit.autopsy.datasourcesummary.uiutils.EventUpdateHandler;
46import org.sleuthkit.autopsy.datasourcesummary.uiutils.GuiCellModel.DefaultMenuItem;
47import org.sleuthkit.autopsy.datasourcesummary.uiutils.GuiCellModel.MenuItem;
48import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableComponent;
49import org.sleuthkit.autopsy.datasourcesummary.uiutils.SwingWorkerSequentialExecutor;
50import org.sleuthkit.autopsy.datasourcesummary.uiutils.UpdateGovernor;
51import org.sleuthkit.autopsy.directorytree.DirectoryTreeTopComponent;
52import org.sleuthkit.autopsy.directorytree.ViewContextAction;
53import org.sleuthkit.autopsy.ingest.IngestManager.IngestJobEvent;
54import org.sleuthkit.autopsy.ingest.ModuleContentEvent;
55import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
56import org.sleuthkit.datamodel.AbstractFile;
57import org.sleuthkit.datamodel.BlackboardArtifact;
58import org.sleuthkit.datamodel.Content;
59import org.sleuthkit.datamodel.DataSource;
60import org.sleuthkit.datamodel.TskCoreException;
61
65@Messages({"BaseDataSourceSummaryPanel_goToArtifact=View Source Result",
66 "BaseDataSourceSummaryPanel_goToFile=View Source File in Directory"})
67abstract class BaseDataSourceSummaryPanel extends JPanel {
68
69 private static final long serialVersionUID = 1L;
70
71 private static final Logger logger = Logger.getLogger(BaseDataSourceSummaryPanel.class.getName());
72
74 private final EventUpdateHandler updateHandler;
75 private final List<UpdateGovernor> governors;
76
77 private Runnable notifyParentClose = null;
78
79 private DataSource dataSource;
80
87 private final UpdateGovernor updateGovernor = new UpdateGovernor() {
96 private boolean isInDataSource(BlackboardArtifact art, DataSource ds) {
97 try {
98
99 return (art.getDataSource() != null && art.getDataSource().getId() == ds.getId());
100 } catch (TskCoreException ex) {
101 logger.log(Level.WARNING, "There was an error fetching datasource for artifact.", ex);
102 return false;
103 }
104 }
105
106 @Override
107 public boolean isRefreshRequired(ModuleDataEvent evt) {
108 DataSource ds = getDataSource();
109 // make sure there is an event.
110 if (ds == null || evt == null) {
111 return false;
112 }
113
114 //if there are no artifacts with matching datasource, return
115 // if no artifacts are present, pass it on just in case there was something wrong with ModuleDataEvent
116 if (evt.getArtifacts() != null
117 && !evt.getArtifacts().isEmpty()
118 && !evt.getArtifacts().stream().anyMatch((art) -> isInDataSource(art, ds))) {
119 return false;
120 }
121
122 // otherwise, see if there is something that wants updates
123 for (UpdateGovernor governor : governors) {
124 if (governor.isRefreshRequired(evt)) {
125 return true;
126 }
127 }
128
129 return false;
130 }
131
132 @Override
133 public boolean isRefreshRequired(ModuleContentEvent evt) {
134 DataSource ds = getDataSource();
135 // make sure there is an event.
136 if (ds == null || evt == null) {
137 return false;
138 }
139
140 try {
141 // if the underlying content has a datasource and that datasource != the
142 // current datasource, return false
143 if (evt.getSource() instanceof Content
144 && ((Content) evt.getSource()).getDataSource() != null
145 && ((Content) evt.getSource()).getDataSource().getId() != ds.getId()) {
146 return false;
147 }
148 } catch (TskCoreException ex) {
149 // on an exception, keep going for tolerance sake
150 logger.log(Level.WARNING, "There was an error fetching datasource for content.", ex);
151 }
152
153 for (UpdateGovernor governor : governors) {
154 if (governor.isRefreshRequired(evt)) {
155 return true;
156 }
157 }
158
159 return false;
160 }
161
162 @Override
163 public boolean isRefreshRequired(AbstractFile file) {
164 DataSource currentDataSource = getDataSource();
165 if (currentDataSource == null || file == null) {
166 return false;
167 }
168
169 // make sure the file is for the current data source
170 Long fileDsId = null;
171 try {
172 Content fileDataSource = file.getDataSource();
173 fileDsId = fileDataSource.getId();
174 } catch (TskCoreException ex) {
175 logger.log(Level.WARNING, "Unable to get the datasource for newly added file", ex);
176 }
177
178 if (fileDsId != null && currentDataSource.getId() == fileDsId) {
179 for (UpdateGovernor governor : governors) {
180 if (governor.isRefreshRequired(file)) {
181 return true;
182 }
183 }
184 }
185
186 return false;
187 }
188
189 @Override
190 public boolean isRefreshRequired(IngestJobEvent evt) {
191 for (UpdateGovernor governor : governors) {
192 if (governor.isRefreshRequired(evt)) {
193 return true;
194 }
195 }
196
197 return false;
198 }
199
200 @Override
201 public boolean isRefreshRequiredForCaseEvent(PropertyChangeEvent evt) {
202 for (UpdateGovernor governor : governors) {
203 if (governor.isRefreshRequiredForCaseEvent(evt)) {
204 return true;
205 }
206 }
207
208 return false;
209 }
210
211 @Override
212 public Set<Case.Events> getCaseEventUpdates() {
213 // return the union of all case events sets from delegates.
214 return governors.stream()
215 .filter(governor -> governor.getCaseEventUpdates() != null)
216 .flatMap(governor -> governor.getCaseEventUpdates().stream())
217 .collect(Collectors.toSet());
218 }
219
220 @Override
221 public Set<IngestJobEvent> getIngestJobEventUpdates() {
222 // return the union of all case events sets from delegates.
223 return governors.stream()
224 .filter(governor -> governor.getIngestJobEventUpdates() != null)
225 .flatMap(governor -> governor.getIngestJobEventUpdates().stream())
226 .collect(Collectors.toSet());
227 }
228 };
229
236 protected BaseDataSourceSummaryPanel(UpdateGovernor... governors) {
237 this.governors = (governors == null) ? Collections.emptyList() : Arrays.asList(governors);
238 this.updateHandler = new EventUpdateHandler(this::onRefresh, updateGovernor);
239 }
240
244 void init() {
245 this.updateHandler.register();
246 }
247
255 protected MenuItem getArtifactNavigateItem(BlackboardArtifact artifact) {
256 if (artifact == null) {
257 return null;
258 }
259
260 return new DefaultMenuItem(
261 Bundle.BaseDataSourceSummaryPanel_goToArtifact(),
262 () -> {
263 final DirectoryTreeTopComponent dtc = DirectoryTreeTopComponent.findInstance();
264
265 // Navigate to the source context artifact.
266 if (dtc != null && artifact != null) {
267 dtc.viewArtifact(artifact);
268 }
269
270 notifyParentClose();
271 });
272 }
273
274 private static final Pattern windowsDrivePattern = Pattern.compile("^[A-Za-z]\\:(.*)$");
275
283 private String normalizePath(String path) {
284 if (path == null) {
285 return null;
286 }
287
288 String trimmed = path.trim();
289 Matcher match = windowsDrivePattern.matcher(trimmed);
290 if (match.find()) {
291 return FilenameUtils.normalize(match.group(1), true);
292 } else {
293 return FilenameUtils.normalize(trimmed, true);
294 }
295 }
296
303 protected MenuItem getFileNavigateItem(String path) {
304 if (StringUtils.isNotBlank(path)) {
305 Path p = Paths.get(path);
306 String fileName = normalizePath(p.getFileName().toString());
307 String directory = normalizePath(p.getParent().toString());
308
309 if (fileName != null && directory != null) {
310 try {
311 List<AbstractFile> files = Case.getCurrentCaseThrows().getSleuthkitCase().findFiles(getDataSource(), fileName, directory);
312 if (CollectionUtils.isNotEmpty(files)) {
313 return getFileNavigateItem(files.get(0));
314 }
315 } catch (TskCoreException | NoCurrentCaseException ex) {
316 logger.log(Level.WARNING, "There was an error fetching file for path: " + path, ex);
317 }
318 }
319 }
320
321 return null;
322 }
323
331 protected MenuItem getFileNavigateItem(AbstractFile file) {
332 if (file == null) {
333 return null;
334 }
335
336 return new DefaultMenuItem(
337 Bundle.BaseDataSourceSummaryPanel_goToFile(),
338 () -> {
339 new ViewContextAction(Bundle.BaseDataSourceSummaryPanel_goToFile(), file)
340 .actionPerformed(null);
341
342 notifyParentClose();
343 });
344 }
345
349 public void close() {
350 executor.cancelRunning();
351 updateHandler.unregister();
352 }
353
359 synchronized void setDataSource(DataSource dataSource) {
360 this.dataSource = dataSource;
361 this.executor.cancelRunning();
362 onNewDataSource(this.dataSource);
363 }
364
368 protected void notifyParentClose() {
369 if (notifyParentClose != null) {
370 notifyParentClose.run();
371 }
372 }
373
379 void setParentCloseListener(Runnable action) {
380 notifyParentClose = action;
381 }
382
386 protected synchronized DataSource getDataSource() {
387 return this.dataSource;
388 }
389
396 protected void submit(List<? extends SwingWorker<?, ?>> workers) {
397 executor.submit(workers);
398 }
399
405 synchronized void onRefresh() {
406 // trigger on new data source with the current data source
407 fetchInformation(this.dataSource);
408 }
409
416 protected abstract void fetchInformation(DataSource dataSource);
417
426 protected void fetchInformation(List<DataFetchComponents<DataSource, ?>> dataFetchComponents, DataSource dataSource) {
427 if (dataSource == null || !Case.isCaseOpen()) {
428 dataFetchComponents.forEach((item) -> item.getResultHandler()
429 .accept(DataFetchResult.getSuccessResult(null)));
430 } else {
431 // create swing workers to run for each loadable item
432 List<DataFetchWorker<?, ?>> workers = dataFetchComponents
433 .stream()
434 .map((components) -> new DataFetchWorker<>(components, dataSource))
435 .collect(Collectors.toList());
436
437 // submit swing workers to run
438 if (!workers.isEmpty()) {
439 submit(workers);
440 }
441 }
442 }
443
449 protected abstract void onNewDataSource(DataSource dataSource);
450
460 protected static <T> T getFetchResult(
461 DataFetcher<DataSource, T> dataFetcher,
462 String sheetName, DataSource ds) {
463
464 try {
465 return dataFetcher.runQuery(ds);
466 } catch (Exception ex) {
467 logger.log(Level.WARNING,
468 String.format("There was an error while acquiring data for exporting worksheet(s): '%s' for dataSource: %s",
469 sheetName == null ? "<null>" : sheetName,
470 ds == null || ds.getName() == null ? "<null>" : ds.getName()), ex);
471 return null;
472 }
473 }
474
484 protected void onNewDataSource(
485 List<DataFetchComponents<DataSource, ?>> dataFetchComponents,
486 List<? extends LoadableComponent<?>> loadableComponents,
487 DataSource dataSource) {
488 // if no data source is present or the case is not open,
489 // set results for tables to null.
490 if (dataSource == null || !Case.isCaseOpen()) {
491 dataFetchComponents.forEach((item) -> item.getResultHandler()
492 .accept(DataFetchResult.getSuccessResult(null)));
493
494 } else {
495 // set tables to display loading screen
496 loadableComponents.forEach((table) -> table.showDefaultLoadingMessage());
497
498 fetchInformation(dataSource);
499 }
500 }
501}
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.