Autopsy  4.6.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
ExtractAction.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2013-2018 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 content 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.directorytree;
20 
21 import java.awt.Component;
22 import java.awt.event.ActionEvent;
23 import java.io.File;
24 import java.util.ArrayList;
25 import java.util.Collection;
26 import java.util.HashSet;
27 import java.util.Iterator;
28 import java.util.Set;
29 import java.util.logging.Level;
30 import javax.swing.AbstractAction;
31 import javax.swing.JFileChooser;
32 import javax.swing.JOptionPane;
33 import javax.swing.SwingWorker;
34 import org.netbeans.api.progress.ProgressHandle;
35 import org.openide.util.Cancellable;
36 import org.openide.util.Exceptions;
37 import org.openide.util.NbBundle;
38 import org.openide.util.Utilities;
46 import org.sleuthkit.datamodel.AbstractFile;
47 import org.sleuthkit.datamodel.Content;
48 import org.sleuthkit.datamodel.TskCoreException;
49 
53 public final class ExtractAction extends AbstractAction {
54 
55  private Logger logger = Logger.getLogger(ExtractAction.class.getName());
56 
57  // This class is a singleton to support multi-selection of nodes, since
58  // org.openide.nodes.NodeOp.findActions(Node[] nodes) will only pick up an Action if every
59  // node in the array returns a reference to the same action object from Node.getActions(boolean).
60  private static ExtractAction instance;
61 
62  public static synchronized ExtractAction getInstance() {
63  if (null == instance) {
64  instance = new ExtractAction();
65  }
66  return instance;
67  }
68 
69  private ExtractAction() {
70  super(NbBundle.getMessage(ExtractAction.class, "ExtractAction.title.extractFiles.text"));
71  }
72 
79  @Override
80  public void actionPerformed(ActionEvent e) {
81  Collection<? extends AbstractFile> selectedFiles = Utilities.actionsGlobalContext().lookupAll(AbstractFile.class);
82  if (selectedFiles.size() > 1) {
83  extractFiles(e, selectedFiles);
84  } else if (selectedFiles.size() == 1) {
85  AbstractFile source = selectedFiles.iterator().next();
86  if (source.isDir()) {
87  extractFiles(e, selectedFiles);
88  } else {
89  extractFile(e, selectedFiles.iterator().next());
90  }
91  }
92  }
93 
100  @NbBundle.Messages ({"ExtractAction.noOpenCase.errMsg=No open case available."})
101  private void extractFile(ActionEvent e, AbstractFile selectedFile) {
102  Case openCase;
103  try {
104  openCase = Case.getOpenCase();
105  } catch (NoCurrentCaseException ex) {
106  JOptionPane.showMessageDialog((Component) e.getSource(), Bundle.ExtractAction_noOpenCase_errMsg());
107  logger.log(Level.INFO, "Exception while getting open case.", ex); //NON-NLS
108  return;
109  }
110  JFileChooser fileChooser = new JFileChooser();
111  fileChooser.setCurrentDirectory(new File(openCase.getExportDirectory()));
112  // If there is an attribute name, change the ":". Otherwise the extracted file will be hidden
113  fileChooser.setSelectedFile(new File(FileUtil.escapeFileName(selectedFile.getName())));
114  if (fileChooser.showSaveDialog((Component) e.getSource()) == JFileChooser.APPROVE_OPTION) {
115  ArrayList<FileExtractionTask> fileExtractionTasks = new ArrayList<>();
116  fileExtractionTasks.add(new FileExtractionTask(selectedFile, fileChooser.getSelectedFile()));
117  runExtractionTasks(e, fileExtractionTasks);
118  }
119  }
120 
127  private void extractFiles(ActionEvent e, Collection<? extends AbstractFile> selectedFiles) {
128  Case openCase;
129  try {
130  openCase = Case.getOpenCase();
131  } catch (NoCurrentCaseException ex) {
132  JOptionPane.showMessageDialog((Component) e.getSource(), Bundle.ExtractAction_noOpenCase_errMsg());
133  logger.log(Level.INFO, "Exception while getting open case.", ex); //NON-NLS
134  return;
135  }
136  JFileChooser folderChooser = new JFileChooser();
137  folderChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
138  folderChooser.setCurrentDirectory(new File(openCase.getExportDirectory()));
139  if (folderChooser.showSaveDialog((Component) e.getSource()) == JFileChooser.APPROVE_OPTION) {
140  File destinationFolder = folderChooser.getSelectedFile();
141  if (!destinationFolder.exists()) {
142  try {
143  destinationFolder.mkdirs();
144  } catch (Exception ex) {
145  JOptionPane.showMessageDialog((Component) e.getSource(), NbBundle.getMessage(this.getClass(),
146  "ExtractAction.extractFiles.cantCreateFolderErr.msg"));
147  logger.log(Level.INFO, "Unable to create folder(s) for user " + destinationFolder.getAbsolutePath(), ex); //NON-NLS
148  return;
149  }
150  }
151 
152  /* get the unique set of files from the list. A user once reported extraction taking
153  * days because it was extracting the same PST file 20k times. They selected 20k
154  * email messages in the tree and chose to extract them. */
155  Set<AbstractFile> uniqueFiles = new HashSet<>(selectedFiles);
156 
157  // make a task for each file
158  ArrayList<FileExtractionTask> fileExtractionTasks = new ArrayList<>();
159  for (AbstractFile source : uniqueFiles) {
160  // If there is an attribute name, change the ":". Otherwise the extracted file will be hidden
161  fileExtractionTasks.add(new FileExtractionTask(source, new File(destinationFolder, source.getId() + "-" + FileUtil.escapeFileName(source.getName()))));
162  }
163  runExtractionTasks(e, fileExtractionTasks);
164  }
165  }
166 
167  private void runExtractionTasks(ActionEvent e, ArrayList<FileExtractionTask> fileExtractionTasks) {
168 
169  // verify all of the sources and destinations are OK
170  for (Iterator<FileExtractionTask> it = fileExtractionTasks.iterator(); it.hasNext();) {
171  FileExtractionTask task = it.next();
172 
173  if (ContentUtils.isDotDirectory(task.source)) {
174  //JOptionPane.showMessageDialog((Component) e.getSource(), "Cannot extract virtual " + task.source.getName() + " directory.", "File is Virtual Directory", JOptionPane.WARNING_MESSAGE);
175  it.remove();
176  continue;
177  }
178 
179  /*
180  * This code assumes that each destination is unique. We previously satisfied
181  * that by adding the unique ID.
182  */
183  if (task.destination.exists()) {
184  if (JOptionPane.showConfirmDialog((Component) e.getSource(),
185  NbBundle.getMessage(this.getClass(), "ExtractAction.confDlg.destFileExist.msg", task.destination.getAbsolutePath()),
186  NbBundle.getMessage(this.getClass(), "ExtractAction.confDlg.destFileExist.title"),
187  JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
188  if (!FileUtil.deleteFileDir(task.destination)) {
189  JOptionPane.showMessageDialog((Component) e.getSource(),
190  NbBundle.getMessage(this.getClass(), "ExtractAction.msgDlg.cantOverwriteFile.msg", task.destination.getAbsolutePath()));
191  it.remove();
192  }
193  } else {
194  it.remove();
195  }
196  }
197  }
198 
199  // launch a thread to do the work
200  if (!fileExtractionTasks.isEmpty()) {
201  try {
202  FileExtracter extracter = new FileExtracter(fileExtractionTasks);
203  extracter.execute();
204  } catch (Exception ex) {
205  logger.log(Level.WARNING, "Unable to start background file extraction thread", ex); //NON-NLS
206  }
207  } else {
209  NbBundle.getMessage(this.getClass(), "ExtractAction.notifyDlg.noFileToExtr.msg"));
210  }
211  }
212 
213  private class FileExtractionTask {
214 
215  AbstractFile source;
216  File destination;
217 
218  FileExtractionTask(AbstractFile source, File destination) {
219  this.source = source;
220  this.destination = destination;
221  }
222  }
223 
227  private class FileExtracter extends SwingWorker<Object, Void> {
228 
229  private Logger logger = Logger.getLogger(FileExtracter.class.getName());
230  private ProgressHandle progress;
231  private ArrayList<FileExtractionTask> extractionTasks;
232 
233  FileExtracter(ArrayList<FileExtractionTask> extractionTasks) {
234  this.extractionTasks = extractionTasks;
235  }
236 
237  @Override
238  protected Object doInBackground() throws Exception {
239  if (extractionTasks.isEmpty()) {
240  return null;
241  }
242 
243  // Setup progress bar.
244  final String displayName = NbBundle.getMessage(this.getClass(), "ExtractAction.progress.extracting");
245  progress = ProgressHandle.createHandle(displayName, new Cancellable() {
246  @Override
247  public boolean cancel() {
248  if (progress != null) {
249  progress.setDisplayName(
250  NbBundle.getMessage(this.getClass(), "ExtractAction.progress.cancellingExtraction", displayName));
251  }
252  return ExtractAction.FileExtracter.this.cancel(true);
253  }
254  });
255  progress.start();
256  progress.switchToIndeterminate();
257 
258  /*
259  * @@@ Add back in -> Causes exceptions int workUnits = 0; for
260  * (FileExtractionTask task : extractionTasks) { workUnits +=
261  * calculateProgressBarWorkUnits(task.source); }
262  * progress.switchToDeterminate(workUnits);
263  */
264  // Do the extraction tasks.
265  for (FileExtractionTask task : this.extractionTasks) {
266  // @@@ Note, we are no longer passing in progress
267  ExtractFscContentVisitor.extract(task.source, task.destination, null, this);
268  }
269 
270  return null;
271  }
272 
273  @Override
274  protected void done() {
275  boolean msgDisplayed = false;
276  try {
277  super.get();
278  } catch (Exception ex) {
279  logger.log(Level.SEVERE, "Fatal error during file extraction", ex); //NON-NLS
281  NbBundle.getMessage(this.getClass(), "ExtractAction.done.notifyMsg.extractErr", ex.getMessage()));
282  msgDisplayed = true;
283  } finally {
284  progress.finish();
285  if (!this.isCancelled() && !msgDisplayed) {
287  NbBundle.getMessage(this.getClass(), "ExtractAction.done.notifyMsg.fileExtr.text"));
288  }
289  }
290  }
291 
292  private int calculateProgressBarWorkUnits(AbstractFile file) {
293  int workUnits = 0;
294  if (file.isFile()) {
295  workUnits += file.getSize();
296  } else {
297  try {
298  for (Content child : file.getChildren()) {
299  if (child instanceof AbstractFile) {
300  workUnits += calculateProgressBarWorkUnits((AbstractFile) child);
301  }
302  }
303  } catch (TskCoreException ex) {
304  logger.log(Level.SEVERE, "Could not get children of content", ex); //NON-NLS
305  }
306  }
307  return workUnits;
308  }
309  }
310 }
static boolean deleteFileDir(File path)
Definition: FileUtil.java:87
static synchronized ExtractAction getInstance()
static< T, V > void extract(Content cntnt, java.io.File dest, ProgressHandle progress, SwingWorker< T, V > worker)
void extractFile(ActionEvent e, AbstractFile selectedFile)
static String escapeFileName(String fileName)
Definition: FileUtil.java:169
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
void runExtractionTasks(ActionEvent e, ArrayList< FileExtractionTask > fileExtractionTasks)
void extractFiles(ActionEvent e, Collection<?extends AbstractFile > selectedFiles)
static boolean isDotDirectory(AbstractFile dir)

Copyright © 2012-2016 Basis Technology. Generated on: Mon May 7 2018
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.