Autopsy  4.14.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
XRYDataSourceProcessor.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2019-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  */
19 package org.sleuthkit.autopsy.datasourceprocessors.xry;
20 
21 import com.google.common.collect.Lists;
22 import java.io.File;
23 import java.io.IOException;
24 import java.io.UncheckedIOException;
25 import java.nio.file.Files;
26 import java.nio.file.LinkOption;
27 import java.nio.file.Path;
28 import java.nio.file.attribute.BasicFileAttributes;
29 import java.util.Iterator;
30 import java.util.List;
31 import java.util.UUID;
32 import java.util.concurrent.ExecutionException;
33 import java.util.logging.Level;
34 import java.util.stream.Collectors;
35 import java.util.stream.Stream;
36 import javax.swing.JPanel;
37 import javax.swing.SwingWorker;
38 import org.openide.util.NbBundle;
39 import org.openide.util.lookup.ServiceProvider;
40 import org.openide.util.lookup.ServiceProviders;
50 import org.sleuthkit.datamodel.AbstractFile;
51 import org.sleuthkit.datamodel.Blackboard.BlackboardException;
52 import org.sleuthkit.datamodel.LocalFilesDataSource;
53 import org.sleuthkit.datamodel.TskCoreException;
54 import org.sleuthkit.datamodel.TskDataException;
55 
59 @ServiceProviders(value = {
60  @ServiceProvider(service = DataSourceProcessor.class),
61  @ServiceProvider(service = AutoIngestDataSourceProcessor.class)
62 })
64 
65  private final XRYDataSourceProcessorConfigPanel configPanel;
66 
67  private static final int XRY_FILES_DEPTH = 1;
68 
69  //Background processor to relieve the EDT from adding files to the case
70  //database and parsing the report files.
72 
73  private static final Logger logger = Logger.getLogger(XRYDataSourceProcessor.class.getName());
74 
76  configPanel = XRYDataSourceProcessorConfigPanel.getInstance();
77  }
78 
79  @Override
80  @NbBundle.Messages({
81  "XRYDataSourceProcessor.dataSourceType=XRY Text Export"
82  })
83  public String getDataSourceType() {
84  return Bundle.XRYDataSourceProcessor_dataSourceType();
85  }
86 
87  @Override
88  public JPanel getPanel() {
89  return configPanel;
90  }
91 
98  @Override
99  @NbBundle.Messages({
100  "XRYDataSourceProcessor.noPathSelected=Please select a folder containing exported XRY text files",
101  "XRYDataSourceProcessor.notReadable=Selected path is not readable",
102  "XRYDataSourceProcessor.notXRYFolder=Selected folder did not contain any XRY text files",
103  "XRYDataSourceProcessor.ioError=I/O error occured trying to test the selected folder",
104  "XRYDataSourceProcessor.childNotReadable=Top level path [ %s ] is not readable",
105  "XRYDataSourceProcessor.notAFolder=The selected path is not a folder"
106  })
107  public boolean isPanelValid() {
108  configPanel.clearErrorText();
109  String selectedFilePath = configPanel.getSelectedFilePath();
110  if (selectedFilePath.isEmpty()) {
111  configPanel.setErrorText(Bundle.XRYDataSourceProcessor_noPathSelected());
112  return false;
113  }
114 
115  File selectedFile = new File(selectedFilePath);
116  Path selectedPath = selectedFile.toPath();
117 
118  //Test permissions
119  if (!Files.isReadable(selectedPath)) {
120  configPanel.setErrorText(Bundle.XRYDataSourceProcessor_notReadable());
121  return false;
122  }
123 
124  try {
125  BasicFileAttributes attr = Files.readAttributes(selectedPath,
126  BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
127 
128  if (!attr.isDirectory()) {
129  configPanel.setErrorText(Bundle.XRYDataSourceProcessor_notAFolder());
130  return false;
131  }
132 
133  //Ensure all of the XRY_FILES_DEPTH paths are readable.
134  try (Stream<Path> allFiles = Files.walk(selectedPath, XRY_FILES_DEPTH)) {
135  Iterator<Path> allFilesIterator = allFiles.iterator();
136  while (allFilesIterator.hasNext()) {
137  Path currentPath = allFilesIterator.next();
138  if (!Files.isReadable(currentPath)) {
139  Path fileName = currentPath.subpath(currentPath.getNameCount() - 2,
140  currentPath.getNameCount());
141  configPanel.setErrorText(String.format(
142  Bundle.XRYDataSourceProcessor_childNotReadable(),
143  fileName.toString()));
144  return false;
145  }
146  }
147  }
148 
149  //Validate the folder.
150  if (!XRYFolder.isXRYFolder(selectedPath)) {
151  configPanel.setErrorText(Bundle.XRYDataSourceProcessor_notXRYFolder());
152  return false;
153  }
154  } catch (IOException | UncheckedIOException ex) {
155  configPanel.setErrorText(Bundle.XRYDataSourceProcessor_ioError());
156  logger.log(Level.WARNING, "[XRY DSP] I/O exception encountered trying to test the XRY folder.", ex);
157  return false;
158  }
159 
160  return true;
161  }
162 
174  @Override
175  public int canProcess(Path dataSourcePath) throws AutoIngestDataSourceProcessorException {
176  try {
177  if (XRYFolder.isXRYFolder(dataSourcePath)) {
178  return 100;
179  }
180  } catch (IOException ex) {
181  throw new AutoIngestDataSourceProcessorException("[XRY DSP] encountered I/O error " + ex.getMessage(), ex);
182  }
183  return 0;
184  }
185 
195  @Override
196  @NbBundle.Messages({
197  "XRYDataSourceProcessor.noCurrentCase=No case is open."
198  })
199  public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
200  progressMonitor.setIndeterminate(true);
201 
202  String selectedFilePath = configPanel.getSelectedFilePath();
203  File selectedFile = new File(selectedFilePath);
204  Path selectedPath = selectedFile.toPath();
205 
206  try {
207  XRYFolder xryFolder = new XRYFolder(selectedPath);
208  Case currentCase = Case.getCurrentCaseThrows();
209  String uniqueUUID = UUID.randomUUID().toString();
210  //Move heavy lifting to a background task.
211  swingWorker = new XRYReportProcessorSwingWorker(xryFolder, progressMonitor,
212  callback, currentCase, uniqueUUID);
213  swingWorker.execute();
214  } catch (NoCurrentCaseException ex) {
215  logger.log(Level.WARNING, "[XRY DSP] No case is currently open.", ex);
217  Lists.newArrayList(Bundle.XRYDataSourceProcessor_noCurrentCase(),
218  ex.getMessage()), Lists.newArrayList());
219  }
220  }
221 
235  @Override
236  public void process(String deviceId, Path dataSourcePath, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callBack) {
237  progressMonitor.setIndeterminate(true);
238 
239  try {
240  XRYFolder xryFolder = new XRYFolder(dataSourcePath);
241  Case currentCase = Case.getCurrentCaseThrows();
242  //Move heavy lifting to a background task.
243  swingWorker = new XRYReportProcessorSwingWorker(xryFolder, progressMonitor,
244  callBack, currentCase, deviceId);
245  swingWorker.execute();
246  } catch (NoCurrentCaseException ex) {
247  logger.log(Level.WARNING, "[XRY DSP] No case is currently open.", ex);
249  Lists.newArrayList(Bundle.XRYDataSourceProcessor_noCurrentCase(),
250  ex.getMessage()), Lists.newArrayList());
251  }
252  }
253 
254  @Override
255  public void cancel() {
256  if (swingWorker != null) {
257  swingWorker.cancel(true);
258  }
259  }
260 
261  @Override
262  public void reset() {
263  //Clear the current selected file path.
264  configPanel.clearSelectedFilePath();
265  }
266 
271  private class XRYReportProcessorSwingWorker extends SwingWorker<LocalFilesDataSource, Void> {
272 
275  private final Case currentCase;
276  private final XRYFolder xryFolder;
277  private final String uniqueUUID;
278 
279  public XRYReportProcessorSwingWorker(XRYFolder folder,
280  DataSourceProcessorProgressMonitor progressMonitor,
282  Case currentCase, String uniqueUUID) {
283 
284  this.xryFolder = folder;
285  this.progressMonitor = progressMonitor;
286  this.callback = callback;
287  this.currentCase = currentCase;
288  this.uniqueUUID = uniqueUUID;
289  }
290 
291  @Override
292  @NbBundle.Messages({
293  "XRYDataSourceProcessor.preppingFiles=Preparing to add files to the case database",
294  "XRYDataSourceProcessor.processingFiles=Processing all XRY files..."
295  })
296  protected LocalFilesDataSource doInBackground() throws TskCoreException,
297  TskDataException, IOException, BlackboardException {
298  progressMonitor.setProgressText(Bundle.XRYDataSourceProcessor_preppingFiles());
299 
300  List<Path> nonXRYFiles = xryFolder.getNonXRYFiles();
301  List<String> filePaths = nonXRYFiles.stream()
302  //Map paths to string representations.
303  .map(Path::toString)
304  .collect(Collectors.toList());
305  LocalFilesDataSource dataSource = currentCase.getServices().getFileManager().addLocalFilesDataSource(
306  uniqueUUID,
307  "XRY Text Export", //Name
308  "", //Timezone
309  filePaths,
310  new ProgressMonitorAdapter(progressMonitor));
311 
312  //Process the report files.
313  progressMonitor.setProgressText(Bundle.XRYDataSourceProcessor_processingFiles());
314  XRYReportProcessor.process(xryFolder, dataSource, currentCase.getSleuthkitCase());
315  return dataSource;
316  }
317 
318  @Override
319  @NbBundle.Messages({
320  "XRYDataSourceProcessor.unexpectedError=Internal error occurred while processing XRY report"
321  })
322  public void done() {
323  try {
324  LocalFilesDataSource newDataSource = get();
326  Lists.newArrayList(), Lists.newArrayList(newDataSource));
327  } catch (InterruptedException ex) {
328  logger.log(Level.WARNING, "[XRY DSP] Thread was interrupted while processing the XRY report."
329  + " The case may or may not have the complete XRY report.", ex);
331  Lists.newArrayList(), Lists.newArrayList());
332  } catch (ExecutionException ex) {
333  logger.log(Level.SEVERE, "[XRY DSP] Unexpected internal error while processing XRY report.", ex);
335  Lists.newArrayList(Bundle.XRYDataSourceProcessor_unexpectedError(),
336  ex.toString()), Lists.newArrayList());
337  }
338  }
339 
344  private class ProgressMonitorAdapter implements FileManager.FileAddProgressUpdater {
345 
347 
349  this.progressMonitor = progressMonitor;
350  }
351 
352  @Override
353  @NbBundle.Messages({
354  "XRYDataSourceProcessor.fileAdded=Added %s to the case database"
355  })
356  public void fileAdded(AbstractFile newFile) {
357  progressMonitor.setProgressText(String.format(Bundle.XRYDataSourceProcessor_fileAdded(), newFile.getName()));
358  }
359  }
360  }
361 }
void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback)
void done(DataSourceProcessorResult result, List< String > errList, List< Content > newDataSources)
synchronized LocalFilesDataSource addLocalFilesDataSource(String deviceId, String rootVirtualDirectoryName, String timeZone, List< String > localFilePaths, FileAddProgressUpdater progressUpdater)
XRYReportProcessorSwingWorker(XRYFolder folder, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback, Case currentCase, String uniqueUUID)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
void process(String deviceId, Path dataSourcePath, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callBack)

Copyright © 2012-2020 Basis Technology. Generated on: Wed Apr 8 2020
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.