Autopsy  4.1
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 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.NbBundle;
37 import org.openide.util.Utilities;
47 
51 public final class ExtractAction extends AbstractAction {
52 
53  private Logger logger = Logger.getLogger(ExtractAction.class.getName());
54 
55  // This class is a singleton to support multi-selection of nodes, since
56  // org.openide.nodes.NodeOp.findActions(Node[] nodes) will only pick up an Action if every
57  // node in the array returns a reference to the same action object from Node.getActions(boolean).
58  private static ExtractAction instance;
59 
60  public static synchronized ExtractAction getInstance() {
61  if (null == instance) {
62  instance = new ExtractAction();
63  }
64  return instance;
65  }
66 
67  private ExtractAction() {
68  super(NbBundle.getMessage(ExtractAction.class, "ExtractAction.title.extractFiles.text"));
69  }
70 
77  @Override
78  public void actionPerformed(ActionEvent e) {
79  Collection<? extends AbstractFile> selectedFiles = Utilities.actionsGlobalContext().lookupAll(AbstractFile.class);
80  if (selectedFiles.size() > 1) {
81  extractFiles(e, selectedFiles);
82  } else if (selectedFiles.size() == 1) {
83  AbstractFile source = selectedFiles.iterator().next();
84  if (source.isDir()) {
85  extractFiles(e, selectedFiles);
86  } else {
87  extractFile(e, selectedFiles.iterator().next());
88  }
89  }
90  }
91 
98  private void extractFile(ActionEvent e, AbstractFile selectedFile) {
99  JFileChooser fileChooser = new JFileChooser();
100  fileChooser.setCurrentDirectory(new File(Case.getCurrentCase().getExportDirectory()));
101  // If there is an attribute name, change the ":". Otherwise the extracted file will be hidden
102  fileChooser.setSelectedFile(new File(FileUtil.escapeFileName(selectedFile.getName())));
103  if (fileChooser.showSaveDialog((Component) e.getSource()) == JFileChooser.APPROVE_OPTION) {
104  ArrayList<FileExtractionTask> fileExtractionTasks = new ArrayList<>();
105  fileExtractionTasks.add(new FileExtractionTask(selectedFile, fileChooser.getSelectedFile()));
106  runExtractionTasks(e, fileExtractionTasks);
107  }
108  }
109 
116  private void extractFiles(ActionEvent e, Collection<? extends AbstractFile> selectedFiles) {
117  JFileChooser folderChooser = new JFileChooser();
118  folderChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
119  folderChooser.setCurrentDirectory(new File(Case.getCurrentCase().getExportDirectory()));
120  if (folderChooser.showSaveDialog((Component) e.getSource()) == JFileChooser.APPROVE_OPTION) {
121  File destinationFolder = folderChooser.getSelectedFile();
122  if (!destinationFolder.exists()) {
123  try {
124  destinationFolder.mkdirs();
125  } catch (Exception ex) {
126  JOptionPane.showMessageDialog((Component) e.getSource(), NbBundle.getMessage(this.getClass(),
127  "ExtractAction.extractFiles.cantCreateFolderErr.msg"));
128  logger.log(Level.INFO, "Unable to create folder(s) for user " + destinationFolder.getAbsolutePath(), ex); //NON-NLS
129  return;
130  }
131  }
132 
133  /* get the unique set of files from the list. A user once reported extraction taking
134  * days because it was extracting the same PST file 20k times. They selected 20k
135  * email messages in the tree and chose to extract them. */
136  Set<AbstractFile> uniqueFiles = new HashSet<>(selectedFiles);
137 
138  // make a task for each file
139  ArrayList<FileExtractionTask> fileExtractionTasks = new ArrayList<>();
140  for (AbstractFile source : uniqueFiles) {
141  // If there is an attribute name, change the ":". Otherwise the extracted file will be hidden
142  fileExtractionTasks.add(new FileExtractionTask(source, new File(destinationFolder, source.getId() + "-" + FileUtil.escapeFileName(source.getName()))));
143  }
144  runExtractionTasks(e, fileExtractionTasks);
145  }
146  }
147 
148  private void runExtractionTasks(ActionEvent e, ArrayList<FileExtractionTask> fileExtractionTasks) {
149 
150  // verify all of the sources and destinations are OK
151  for (Iterator<FileExtractionTask> it = fileExtractionTasks.iterator(); it.hasNext();) {
152  FileExtractionTask task = it.next();
153 
154  if (ContentUtils.isDotDirectory(task.source)) {
155  //JOptionPane.showMessageDialog((Component) e.getSource(), "Cannot extract virtual " + task.source.getName() + " directory.", "File is Virtual Directory", JOptionPane.WARNING_MESSAGE);
156  it.remove();
157  continue;
158  }
159 
160  /*
161  * This code assumes that each destination is unique. We previously satisfied
162  * that by adding the unique ID.
163  */
164  if (task.destination.exists()) {
165  if (JOptionPane.showConfirmDialog((Component) e.getSource(),
166  NbBundle.getMessage(this.getClass(), "ExtractAction.confDlg.destFileExist.msg", task.destination.getAbsolutePath()),
167  NbBundle.getMessage(this.getClass(), "ExtractAction.confDlg.destFileExist.title"),
168  JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
169  if (!FileUtil.deleteFileDir(task.destination)) {
170  JOptionPane.showMessageDialog((Component) e.getSource(),
171  NbBundle.getMessage(this.getClass(), "ExtractAction.msgDlg.cantOverwriteFile.msg", task.destination.getAbsolutePath()));
172  it.remove();
173  }
174  } else {
175  it.remove();
176  }
177  }
178  }
179 
180  // launch a thread to do the work
181  if (!fileExtractionTasks.isEmpty()) {
182  try {
183  FileExtracter extracter = new FileExtracter(fileExtractionTasks);
184  extracter.execute();
185  } catch (Exception ex) {
186  logger.log(Level.WARNING, "Unable to start background file extraction thread", ex); //NON-NLS
187  }
188  } else {
190  NbBundle.getMessage(this.getClass(), "ExtractAction.notifyDlg.noFileToExtr.msg"));
191  }
192  }
193 
194  private class FileExtractionTask {
195 
196  AbstractFile source;
197  File destination;
198 
199  FileExtractionTask(AbstractFile source, File destination) {
200  this.source = source;
201  this.destination = destination;
202  }
203  }
204 
208  private class FileExtracter extends SwingWorker<Object, Void> {
209 
210  private Logger logger = Logger.getLogger(FileExtracter.class.getName());
211  private ProgressHandle progress;
212  private ArrayList<FileExtractionTask> extractionTasks;
213 
214  FileExtracter(ArrayList<FileExtractionTask> extractionTasks) {
215  this.extractionTasks = extractionTasks;
216  }
217 
218  @Override
219  protected Object doInBackground() throws Exception {
220  if (extractionTasks.isEmpty()) {
221  return null;
222  }
223 
224  // Setup progress bar.
225  final String displayName = NbBundle.getMessage(this.getClass(), "ExtractAction.progress.extracting");
226  progress = ProgressHandle.createHandle(displayName, new Cancellable() {
227  @Override
228  public boolean cancel() {
229  if (progress != null) {
230  progress.setDisplayName(
231  NbBundle.getMessage(this.getClass(), "ExtractAction.progress.cancellingExtraction", displayName));
232  }
233  return ExtractAction.FileExtracter.this.cancel(true);
234  }
235  });
236  progress.start();
237  progress.switchToIndeterminate();
238 
239  /*
240  * @@@ Add back in -> Causes exceptions int workUnits = 0; for
241  * (FileExtractionTask task : extractionTasks) { workUnits +=
242  * calculateProgressBarWorkUnits(task.source); }
243  * progress.switchToDeterminate(workUnits);
244  */
245  // Do the extraction tasks.
246  for (FileExtractionTask task : this.extractionTasks) {
247  // @@@ Note, we are no longer passing in progress
248  ExtractFscContentVisitor.extract(task.source, task.destination, null, this);
249  }
250 
251  return null;
252  }
253 
254  @Override
255  protected void done() {
256  boolean msgDisplayed = false;
257  try {
258  super.get();
259  } catch (Exception ex) {
260  logger.log(Level.SEVERE, "Fatal error during file extraction", ex); //NON-NLS
262  NbBundle.getMessage(this.getClass(), "ExtractAction.done.notifyMsg.extractErr", ex.getMessage()));
263  msgDisplayed = true;
264  } finally {
265  progress.finish();
266  if (!this.isCancelled() && !msgDisplayed) {
268  NbBundle.getMessage(this.getClass(), "ExtractAction.done.notifyMsg.fileExtr.text"));
269  }
270  }
271  }
272 
274  int workUnits = 0;
275  if (file.isFile()) {
276  workUnits += file.getSize();
277  } else {
278  try {
279  for (Content child : file.getChildren()) {
280  if (child instanceof AbstractFile) {
281  workUnits += calculateProgressBarWorkUnits((AbstractFile) child);
282  }
283  }
284  } catch (TskCoreException ex) {
285  logger.log(Level.SEVERE, "Could not get children of content", ex); //NON-NLS
286  }
287  }
288  return workUnits;
289  }
290  }
291 }
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:161
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 Apr 24 2017
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.