Autopsy  4.13.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 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.LocalFilesDataSource;
52 import org.sleuthkit.datamodel.TskCoreException;
53 import org.sleuthkit.datamodel.TskDataException;
54 
58 @ServiceProviders(value = {
59  @ServiceProvider(service = DataSourceProcessor.class),
60  @ServiceProvider(service = AutoIngestDataSourceProcessor.class)
61 })
63 
64  private final XRYDataSourceProcessorConfigPanel configPanel;
65 
66  private static final int XRY_FILES_DEPTH = 1;
67 
68  //Background processor to relieve the EDT from adding files to the case
69  //database and parsing the report files.
71 
72  private static final Logger logger = Logger.getLogger(XRYDataSourceProcessor.class.getName());
73 
75  configPanel = XRYDataSourceProcessorConfigPanel.getInstance();
76  }
77 
78  @Override
79  @NbBundle.Messages({
80  "XRYDataSourceProcessor.dataSourceType=XRY Text Export"
81  })
82  public String getDataSourceType() {
83  return Bundle.XRYDataSourceProcessor_dataSourceType();
84  }
85 
86  @Override
87  public JPanel getPanel() {
88  return configPanel;
89  }
90 
97  @Override
98  @NbBundle.Messages({
99  "XRYDataSourceProcessor.noPathSelected=Please select a folder containing exported XRY text files",
100  "XRYDataSourceProcessor.notReadable=Selected path is not readable",
101  "XRYDataSourceProcessor.notXRYFolder=Selected folder did not contain any XRY text files",
102  "XRYDataSourceProcessor.ioError=I/O error occured trying to test the selected folder",
103  "XRYDataSourceProcessor.childNotReadable=Top level path [ %s ] is not readable",
104  "XRYDataSourceProcessor.notAFolder=The selected path is not a folder"
105  })
106  public boolean isPanelValid() {
107  configPanel.clearErrorText();
108  String selectedFilePath = configPanel.getSelectedFilePath();
109  if (selectedFilePath.isEmpty()) {
110  configPanel.setErrorText(Bundle.XRYDataSourceProcessor_noPathSelected());
111  return false;
112  }
113 
114  File selectedFile = new File(selectedFilePath);
115  Path selectedPath = selectedFile.toPath();
116 
117  //Test permissions
118  if (!Files.isReadable(selectedPath)) {
119  configPanel.setErrorText(Bundle.XRYDataSourceProcessor_notReadable());
120  return false;
121  }
122 
123  try {
124  BasicFileAttributes attr = Files.readAttributes(selectedPath,
125  BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
126 
127  if (!attr.isDirectory()) {
128  configPanel.setErrorText(Bundle.XRYDataSourceProcessor_notAFolder());
129  return false;
130  }
131 
132  //Ensure all of the XRY_FILES_DEPTH paths are readable.
133  try (Stream<Path> allFiles = Files.walk(selectedPath, XRY_FILES_DEPTH)) {
134  Iterator<Path> allFilesIterator = allFiles.iterator();
135  while (allFilesIterator.hasNext()) {
136  Path currentPath = allFilesIterator.next();
137  if (!Files.isReadable(currentPath)) {
138  Path fileName = currentPath.subpath(currentPath.getNameCount() - 2,
139  currentPath.getNameCount());
140  configPanel.setErrorText(String.format(
141  Bundle.XRYDataSourceProcessor_childNotReadable(),
142  fileName.toString()));
143  return false;
144  }
145  }
146  }
147 
148  //Validate the folder.
149  if (!XRYFolder.isXRYFolder(selectedPath)) {
150  configPanel.setErrorText(Bundle.XRYDataSourceProcessor_notXRYFolder());
151  return false;
152  }
153  } catch (IOException | UncheckedIOException ex) {
154  configPanel.setErrorText(Bundle.XRYDataSourceProcessor_ioError());
155  logger.log(Level.WARNING, "[XRY DSP] I/O exception encountered trying to test the XRY folder.", ex);
156  return false;
157  }
158 
159  return true;
160  }
161 
173  @Override
174  public int canProcess(Path dataSourcePath) throws AutoIngestDataSourceProcessorException {
175  try {
176  if (XRYFolder.isXRYFolder(dataSourcePath)) {
177  return 100;
178  }
179  } catch (IOException ex) {
180  throw new AutoIngestDataSourceProcessorException("[XRY DSP] encountered I/O error " + ex.getMessage(), ex);
181  }
182  return 0;
183  }
184 
194  @Override
195  @NbBundle.Messages({
196  "XRYDataSourceProcessor.noCurrentCase=No case is open."
197  })
198  public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
199  progressMonitor.setIndeterminate(true);
200 
201  String selectedFilePath = configPanel.getSelectedFilePath();
202  File selectedFile = new File(selectedFilePath);
203  Path selectedPath = selectedFile.toPath();
204 
205  try {
206  XRYFolder xryFolder = new XRYFolder(selectedPath);
207  FileManager fileManager = Case.getCurrentCaseThrows()
209  String uniqueUUID = UUID.randomUUID().toString();
210  //Move heavy lifting to a background task.
211  swingWorker = new XRYReportProcessorSwingWorker(xryFolder, progressMonitor,
212  callback, fileManager, 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  FileManager fileManager = Case.getCurrentCaseThrows()
243  //Move heavy lifting to a background task.
244  swingWorker = new XRYReportProcessorSwingWorker(xryFolder, progressMonitor,
245  callBack, fileManager, deviceId);
246  swingWorker.execute();
247  } catch (NoCurrentCaseException ex) {
248  logger.log(Level.WARNING, "[XRY DSP] No case is currently open.", ex);
250  Lists.newArrayList(Bundle.XRYDataSourceProcessor_noCurrentCase(),
251  ex.getMessage()), Lists.newArrayList());
252  }
253  }
254 
255  @Override
256  public void cancel() {
257  if (swingWorker != null) {
258  swingWorker.cancel(true);
259  }
260  }
261 
262  @Override
263  public void reset() {
264  //Clear the current selected file path.
265  configPanel.clearSelectedFilePath();
266  }
267 
272  private class XRYReportProcessorSwingWorker extends SwingWorker<LocalFilesDataSource, Void> {
273 
276  private final FileManager fileManager;
277  private final XRYFolder xryFolder;
278  private final String uniqueUUID;
279 
280  public XRYReportProcessorSwingWorker(XRYFolder folder,
281  DataSourceProcessorProgressMonitor progressMonitor,
283  FileManager fileManager,
284  String uniqueUUID) {
285 
286  this.xryFolder = folder;
287  this.progressMonitor = progressMonitor;
288  this.callback = callback;
289  this.fileManager = fileManager;
290  this.uniqueUUID = uniqueUUID;
291  }
292 
293  @Override
294  @NbBundle.Messages({
295  "XRYDataSourceProcessor.preppingFiles=Preparing to add files to the case database",
296  "XRYDataSourceProcessor.processingFiles=Processing all XRY files..."
297  })
298  protected LocalFilesDataSource doInBackground() throws TskCoreException,
299  TskDataException, IOException {
300  progressMonitor.setProgressText(Bundle.XRYDataSourceProcessor_preppingFiles());
301 
302  List<Path> nonXRYFiles = xryFolder.getNonXRYFiles();
303  List<String> filePaths = nonXRYFiles.stream()
304  //Map paths to string representations.
305  .map(Path::toString)
306  .collect(Collectors.toList());
307  LocalFilesDataSource dataSource = fileManager.addLocalFilesDataSource(
308  uniqueUUID,
309  "XRY Text Export", //Name
310  "", //Timezone
311  filePaths,
312  new ProgressMonitorAdapter(progressMonitor));
313 
314  //Process the report files.
315  progressMonitor.setProgressText(Bundle.XRYDataSourceProcessor_processingFiles());
316  XRYReportProcessor.process(xryFolder, dataSource);
317  return dataSource;
318  }
319 
320  @Override
321  @NbBundle.Messages({
322  "XRYDataSourceProcessor.unexpectedError=Internal error occurred while processing XRY report"
323  })
324  public void done() {
325  try {
326  LocalFilesDataSource newDataSource = get();
328  Lists.newArrayList(), Lists.newArrayList(newDataSource));
329  } catch (InterruptedException ex) {
330  logger.log(Level.WARNING, "[XRY DSP] Thread was interrupted while processing the XRY report."
331  + " The case may or may not have the complete XRY report.", ex);
333  Lists.newArrayList(), Lists.newArrayList());
334  } catch (ExecutionException ex) {
335  logger.log(Level.SEVERE, "[XRY DSP] Unexpected internal error while processing XRY report.", ex);
337  Lists.newArrayList(Bundle.XRYDataSourceProcessor_unexpectedError(),
338  ex.toString()), Lists.newArrayList());
339  }
340  }
341 
346  private class ProgressMonitorAdapter implements FileManager.FileAddProgressUpdater {
347 
349 
351  this.progressMonitor = progressMonitor;
352  }
353 
354  @Override
355  @NbBundle.Messages({
356  "XRYDataSourceProcessor.fileAdded=Added %s to the case database"
357  })
358  public void fileAdded(AbstractFile newFile) {
359  progressMonitor.setProgressText(String.format(Bundle.XRYDataSourceProcessor_fileAdded(), newFile.getName()));
360  }
361  }
362  }
363 }
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)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
XRYReportProcessorSwingWorker(XRYFolder folder, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback, FileManager fileManager, String uniqueUUID)
void process(String deviceId, Path dataSourcePath, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callBack)

Copyright © 2012-2019 Basis Technology. Generated on: Tue Jan 7 2020
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.