From f003254ad547bbc7acb4679b2d018dcc8dfa85f6 Mon Sep 17 00:00:00 2001 From: Raman Date: Thu, 4 Jan 2018 15:31:54 -0500 Subject: [PATCH 01/12] 3414: Create generic Application Content Viewer --- .../ApplicationContentViewer.form | 41 +++ .../ApplicationContentViewer.java | 251 ++++++++++++++++++ .../autopsy/contentviewers/Bundle.properties | 2 + .../autopsy/contentviewers/ContentViewer.java | 50 ++++ .../contentviewers/JPEGViewerDummy.form | 58 ++++ .../contentviewers/JPEGViewerDummy.java | 89 +++++++ .../modules/filetypeid/FileTypeDetector.java | 34 ++- 7 files changed, 522 insertions(+), 3 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/contentviewers/ApplicationContentViewer.form create mode 100644 Core/src/org/sleuthkit/autopsy/contentviewers/ApplicationContentViewer.java create mode 100644 Core/src/org/sleuthkit/autopsy/contentviewers/ContentViewer.java create mode 100644 Core/src/org/sleuthkit/autopsy/contentviewers/JPEGViewerDummy.form create mode 100644 Core/src/org/sleuthkit/autopsy/contentviewers/JPEGViewerDummy.java diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/ApplicationContentViewer.form b/Core/src/org/sleuthkit/autopsy/contentviewers/ApplicationContentViewer.form new file mode 100644 index 0000000000..d07831cafe --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/ApplicationContentViewer.form @@ -0,0 +1,41 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/ApplicationContentViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/ApplicationContentViewer.java new file mode 100644 index 0000000000..30141c3f4c --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/ApplicationContentViewer.java @@ -0,0 +1,251 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.contentviewers; + +import com.google.common.base.Strings; +import java.awt.Component; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; +import org.openide.nodes.Node; +import org.openide.util.Exceptions; +import org.openide.util.NbBundle; +import org.openide.util.lookup.ServiceProvider; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Generic Application content viewer + */ +@ServiceProvider(service = DataContentViewer.class, position = 5) +public class ApplicationContentViewer extends javax.swing.JPanel implements DataContentViewer { + + private static final int CONFIDENCE_LEVEL = 7; + private static final long serialVersionUID = 1L; + private static final Logger LOGGER = Logger.getLogger(ApplicationContentViewer.class.getName()); + + private final Map mimeTypeToViewerMap = new HashMap<>(); + + // TBD: This hardcoded list of viewers should be replaced with a dynamic lookup + private static final ContentViewer[] KNOWN_VIEWERS = new ContentViewer[]{ + new JPEGViewerDummy() // this if for testing only + }; + + private ContentViewer lastViewer; + + /** + * Creates new form ApplicationContentViewer + */ + public ApplicationContentViewer() { + + // init the mimetype to viewer map + for (ContentViewer cv : KNOWN_VIEWERS) { + cv.getSupportedMIMETypes().forEach((mimeType) -> { + if (mimeTypeToViewerMap.containsKey(mimeType) == false) { + mimeTypeToViewerMap.put(mimeType, cv); + } else { + LOGGER.log(Level.WARNING, "Duplicate viewer for mimtype: {0}", mimeType); //NON-NLS + } + }); + } + + initComponents(); + + LOGGER.log(Level.INFO, "Created ApplicationContentViewer instance: {0}", this); //NON-NLS + } + + /** + * Get the ContentViewer for a given mimetype + * + * @param mimeType + * + * @return ContentViewer, null if no known content viewer supports the + * mimetype + */ + private ContentViewer getSupportingViewer(String mimeType) { + return mimeTypeToViewerMap.containsKey(mimeType) ? mimeTypeToViewerMap.get(mimeType) : null; + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + setLayout(new javax.swing.OverlayLayout(this)); + }// //GEN-END:initComponents + + + // Variables declaration - do not modify//GEN-BEGIN:variables + // End of variables declaration//GEN-END:variables + + @Override + public void setNode(Node selectedNode) { + if (selectedNode == null) { + return; + } + + AbstractFile file = selectedNode.getLookup().lookup(AbstractFile.class); + if (file == null) { + return; + } + + String mimeType = file.getMIMEType(); + if (Strings.isNullOrEmpty(mimeType)) { + LOGGER.log(Level.INFO, "Mimetype not known for file: {0}", file.getName()); //NON-NLS + try { + FileTypeDetector fileTypeDetector = new FileTypeDetector(); + mimeType = fileTypeDetector.detectFileType(file); + } catch (FileTypeDetector.FileTypeDetectorInitException ex) { + LOGGER.log(Level.SEVERE, "Failed to initialize FileTypeDetector.", ex); //NON-NLS + return; + } catch (TskCoreException ex) { + LOGGER.log(Level.SEVERE, String.format("Could not detect format using fileTypeDetector for file: %s", file), ex); //NON-NLS + return; + } + } + + if (mimeType.equalsIgnoreCase("application/octet-stream")) { + return; + } + else { + ContentViewer viewer = getSupportingViewer(mimeType); + if (viewer != null) { + lastViewer = viewer; + + viewer.setFile(file); + this.removeAll(); + this.add(viewer.getComponent()); + this.repaint(); + } + } + + } + + @Override + @NbBundle.Messages("ApplicationContentViewer.title=Application Content Viewer") + public String getTitle() { + return Bundle.ApplicationContentViewer_title(); + } + + @Override + @NbBundle.Messages("ApplicationContentViewer.toolTip=Displays file contents.") + public String getToolTip() { + return Bundle.ApplicationContentViewer_toolTip(); + } + + @Override + public DataContentViewer createInstance() { + return new ApplicationContentViewer(); + } + + @Override + public Component getComponent() { + return this; + } + + @Override + public void resetComponent() { + + if (lastViewer != null) { + lastViewer.resetComponent(); + } + this.removeAll(); + lastViewer = null; + } + + @Override + public boolean isSupported(Node node) { + + if (node == null) { + return false; + } + + AbstractFile aFile = node.getLookup().lookup(AbstractFile.class); + if (aFile == null) { + return false; + } + + String mimeType = aFile.getMIMEType(); + if (Strings.isNullOrEmpty(mimeType)) { + LOGGER.log(Level.INFO, "Mimetype not known for file: {0}", aFile.getName()); //NON-NLS + try { + FileTypeDetector fileTypeDetector = new FileTypeDetector(); + mimeType = fileTypeDetector.detectFileType(aFile); + } catch (FileTypeDetector.FileTypeDetectorInitException ex) { + LOGGER.log(Level.SEVERE, "Failed to initialize FileTypeDetector.", ex); //NON-NLS + return false; + } catch (TskCoreException ex) { + LOGGER.log(Level.SEVERE, String.format("Could not detect format using fileTypeDetector for file: %s", aFile), ex); //NON-NLS + return false; + } + } + + if (mimeType.equalsIgnoreCase("application/octet-stream")) { + return false; + } else { + return (getSupportingViewer(mimeType) != null); + } + + } + + @Override + public int isPreferred(Node node) { + if (node == null) { + return 0; + } + + AbstractFile file = node.getLookup().lookup(AbstractFile.class); + if (file == null) { + return 0; + } + + String mimeType = file.getMIMEType(); + if (Strings.isNullOrEmpty(mimeType)) { + LOGGER.log(Level.INFO, "Mimetype not known for file: {0}", file.getName()); //NON-NLS + try { + FileTypeDetector fileTypeDetector = new FileTypeDetector(); + mimeType = fileTypeDetector.detectFileType(file); + } catch (FileTypeDetector.FileTypeDetectorInitException ex) { + LOGGER.log(Level.SEVERE, "Failed to initialize FileTypeDetector.", ex); //NON-NLS + return 0; + } catch (TskCoreException ex) { + LOGGER.log(Level.SEVERE, String.format("Could not detect format using fileTypeDetector for file: %s", file), ex); //NON-NLS + return 0; + } + } + + if (mimeType.equalsIgnoreCase("application/octet-stream")) { + return 0; + } else { + if (null != getSupportingViewer(mimeType)) { + return CONFIDENCE_LEVEL; + } + } + + return 0; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties index 45916b87b3..cf082b2bab 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties @@ -33,3 +33,5 @@ MessageContentViewer.directionText.text=direction MessageContentViewer.ccLabel.text=CC: MessageContentViewer.attachmentsPanel.TabConstraints.tabTitle=Attachments MessageContentViewer.viewInNewWindowButton.text=View in New Window +JPEGViewerDummy.jLabel1.text=You are looking at a JPEG file: +JPEGViewerDummy.jTextField1.text=jTextField1 diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/ContentViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/ContentViewer.java new file mode 100644 index 0000000000..98dbbd0b08 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/ContentViewer.java @@ -0,0 +1,50 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.contentviewers; + +import java.awt.Component; +import java.util.List; +import org.sleuthkit.datamodel.AbstractFile; + +/** + * Defines an interface for application specific content viewer + * + */ +interface ContentViewer { + + /** + * Returns list of MIME types supported by this viewer + */ + List getSupportedMIMETypes(); + + /** + * Display the given file's content in the view panel + */ + void setFile(AbstractFile file); + + /** + * Returns panel + */ + Component getComponent(); + + /** + * Clears the data in the panel + */ + void resetComponent(); +} diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/JPEGViewerDummy.form b/Core/src/org/sleuthkit/autopsy/contentviewers/JPEGViewerDummy.form new file mode 100644 index 0000000000..587dd3c9a0 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/JPEGViewerDummy.form @@ -0,0 +1,58 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/JPEGViewerDummy.java b/Core/src/org/sleuthkit/autopsy/contentviewers/JPEGViewerDummy.java new file mode 100644 index 0000000000..cae2db12a0 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/JPEGViewerDummy.java @@ -0,0 +1,89 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.sleuthkit.autopsy.contentviewers; + +import java.awt.Component; +import java.util.Arrays; +import java.util.List; +import org.sleuthkit.datamodel.AbstractFile; + +public class JPEGViewerDummy extends javax.swing.JPanel implements ContentViewer { + + public static final String[] SUPPORTED_MIMETYPES = new String[]{"image/jpeg"}; + + /** + * Creates new form JPEGViewer + */ + public JPEGViewerDummy() { + initComponents(); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + jLabel1 = new javax.swing.JLabel(); + jTextField1 = new javax.swing.JTextField(); + + org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(JPEGViewerDummy.class, "JPEGViewerDummy.jLabel1.text")); // NOI18N + + jTextField1.setEditable(false); + jTextField1.setText(org.openide.util.NbBundle.getMessage(JPEGViewerDummy.class, "JPEGViewerDummy.jTextField1.text")); // NOI18N + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addGap(43, 43, 43) + .addComponent(jLabel1) + .addGap(35, 35, 35) + .addComponent(jTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap(120, Short.MAX_VALUE)) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel1) + .addComponent(jTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addContainerGap(269, Short.MAX_VALUE)) + ); + }// //GEN-END:initComponents + + @Override + public List getSupportedMIMETypes() { + return Arrays.asList(SUPPORTED_MIMETYPES); + } + + @Override + public Component getComponent() { + return this; + } + + @Override + public void resetComponent() { + this.jTextField1.setText(""); + } + + @Override + public void setFile(AbstractFile file) { + this.jTextField1.setText(file.getName()); + } + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel jLabel1; + private javax.swing.JTextField jTextField1; + // End of variables declaration//GEN-END:variables + +} diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeDetector.java b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeDetector.java index 5851b9887c..baa6a1135b 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeDetector.java +++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeDetector.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,6 +18,7 @@ */ package org.sleuthkit.autopsy.modules.filetypeid; +import com.google.common.base.Strings; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; @@ -205,9 +206,18 @@ public class FileTypeDetector { * * @throws TskCoreException If there is a problem writing the result to the * case database. + * + */ - public String detect(AbstractFile file) throws TskCoreException { - return detect(file, false); + public String detectFileType(AbstractFile file) throws TskCoreException { + String mimeType = file.getMIMEType(); + if (Strings.isNullOrEmpty(mimeType) == false) { + return mimeType; + } + else { + return detect(file, false); + } + } /** @@ -477,4 +487,22 @@ public class FileTypeDetector { return getFileType(file); } + /** + * Detects the MIME type of a file. The result is not added to the case + * database. + * + * @param file The file to test. + * + * @return A MIME type name. If file type could not be detected or results + * were uncertain, octet-stream is returned. + * + * @throws TskCoreException If there is a problem writing the result to the + * case database. + * + * @deprecated - use detectFileType to detect + */ + @Deprecated + public String detect(AbstractFile file) throws TskCoreException { + return detect(file, true); + } } From 7ff02fa62a1af599cd11b1a1435abb986d9f7f74 Mon Sep 17 00:00:00 2001 From: Ann Priestman Date: Mon, 8 Jan 2018 13:47:24 -0500 Subject: [PATCH 02/12] Added menu item to create live triage drive --- .../autopsy/livetriage/Bundle.properties | 11 + .../CreateLiveTriageDriveAction.java | 265 +++++++++++ .../autopsy/livetriage/SelectDriveDialog.form | 180 ++++++++ .../autopsy/livetriage/SelectDriveDialog.java | 423 ++++++++++++++++++ 4 files changed, 879 insertions(+) create mode 100644 Core/src/org/sleuthkit/autopsy/livetriage/Bundle.properties create mode 100644 Core/src/org/sleuthkit/autopsy/livetriage/CreateLiveTriageDriveAction.java create mode 100644 Core/src/org/sleuthkit/autopsy/livetriage/SelectDriveDialog.form create mode 100644 Core/src/org/sleuthkit/autopsy/livetriage/SelectDriveDialog.java diff --git a/Core/src/org/sleuthkit/autopsy/livetriage/Bundle.properties b/Core/src/org/sleuthkit/autopsy/livetriage/Bundle.properties new file mode 100644 index 0000000000..49fdf032cb --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/livetriage/Bundle.properties @@ -0,0 +1,11 @@ +SelectDriveDialog.bnOk.text=Ok +SelectDriveDialog.bnRefresh.text=Refresh +SelectDriveDialog.lbSelectDrive.text=Select the drive to copy the application and script to: +SelectDriveDialog.jLabel1.text=Select drive to use for live triage (may take time to load): +SelectDriveDialog.errorLabel.text=jLabel2 +SelectDriveDialog.jLabel2.text=This feature copies the application and a batch file to a removable drive, +SelectDriveDialog.jLabel3.text=allowing systems to be analyzed without installing the software or imaging +SelectDriveDialog.jLabel4.text=the drives. +SelectDriveDialog.jLabel5.text=To analyze a system, insert the drive and run "RunFromUSB.bat" as +SelectDriveDialog.jLabel6.text=administrator. +SelectDriveDialog.bnCancel.text=Cancel diff --git a/Core/src/org/sleuthkit/autopsy/livetriage/CreateLiveTriageDriveAction.java b/Core/src/org/sleuthkit/autopsy/livetriage/CreateLiveTriageDriveAction.java new file mode 100644 index 0000000000..531e978618 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/livetriage/CreateLiveTriageDriveAction.java @@ -0,0 +1,265 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.livetriage; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.InvalidPathException; +import java.util.logging.Level; +import javax.swing.JOptionPane; +import java.awt.Frame; +import javax.swing.SwingWorker; +import org.apache.commons.io.FileUtils; +import org.netbeans.api.progress.ProgressHandle; +import org.openide.awt.ActionID; +import org.openide.awt.ActionReference; +import org.openide.awt.ActionRegistration; +import org.openide.util.HelpCtx; +import org.openide.util.NbBundle; +import org.openide.util.actions.CallableSystemAction; +import org.openide.windows.WindowManager; +import org.sleuthkit.autopsy.core.UserPreferences; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; +import org.sleuthkit.autopsy.coreutils.PlatformUtil; + +@ActionID(category = "Tools", id = "org.sleuthkit.autopsy.livetriage.CreateLiveTriageDriveAction") +@ActionReference(path = "Menu/Tools", position = 1401) +@ActionRegistration(displayName = "#CTL_CreateLiveTriageDriveAction", lazy = false) +@NbBundle.Messages({"CTL_CreateLiveTriageDriveAction=Make Live Triage Drive"}) +public final class CreateLiveTriageDriveAction extends CallableSystemAction { + + private static final String DISPLAY_NAME = Bundle.CTL_CreateLiveTriageDriveAction(); + + @Override + public boolean isEnabled() { + return true; + } + + @NbBundle.Messages({"CreateLiveTriageDriveAction.error.title=Error creating live triage disk", + "CreateLiveTriageDriveAction.exenotfound.message=Executable could not be found", + "CreateLiveTriageDriveAction.batchFileError.message=Error creating batch file", + "CreateLiveTriageDriveAction.appPathError.message=Could not location application directory", + "CreateLiveTriageDriveAction.copyError.message=Could not copy application", + "CreateLiveTriageDriveAction.success.title=Success", + "CreateLiveTriageDriveAction.success.message=Live triage drive created. Use RunFromUSB.bat to run the application" + }) + @Override + @SuppressWarnings("fallthrough") + public void performAction() { + + Frame mainWindow = WindowManager.getDefault().getMainWindow(); + + // If this is an installed version, there should be an 64.exe file in the bin folder + String appName = UserPreferences.getAppName(); + String exeName = appName + "64.exe"; + String installPath = PlatformUtil.getInstallPath(); + + Path exePath = Paths.get(installPath, "bin", exeName); + System.out.println("Exe expected at " + exePath); + + // TEMP for testing - allows this to run from Netbeans + exePath = Paths.get("C:\\Program Files\\Autopsy-4.5.0", "bin", exeName); + + if(! exePath.toFile().exists()) { + JOptionPane.showMessageDialog(mainWindow, + Bundle.CreateLiveTriageDriveAction_exenotfound_message(), + Bundle.CreateLiveTriageDriveAction_error_title(), + JOptionPane.ERROR_MESSAGE); + return; + } + + Path applicationBasePath; + try { + applicationBasePath = exePath.getParent().getParent(); + } catch (InvalidPathException ex){ + JOptionPane.showMessageDialog(mainWindow, + Bundle.CreateLiveTriageDriveAction_appPathError_message(), + Bundle.CreateLiveTriageDriveAction_error_title(), + JOptionPane.ERROR_MESSAGE); + return; + } + + SelectDriveDialog driveDialog = new SelectDriveDialog(mainWindow, true); + driveDialog.display(); + + if(! driveDialog.getSelectedDrive().isEmpty()) { + String drivePath = driveDialog.getSelectedDrive(); + if(drivePath.startsWith("\\\\.\\")){ + drivePath = drivePath.substring(4); + } + System.out.println("Destination path: " + drivePath); + + CopyFilesWorker worker = new CopyFilesWorker(applicationBasePath, drivePath, appName); + worker.execute(); + } + + } + + private class CopyFilesWorker extends SwingWorker { + + private final Path sourceFolder; + private final String drivePath; + private final String appName; + private ProgressHandle progressHandle = null; + + CopyFilesWorker(Path sourceFolder, String drivePath, String appName){ + this.sourceFolder = sourceFolder; + this.drivePath = drivePath; + this.appName = appName; + } + + @NbBundle.Messages({"# {0} - drivePath", + "CopyFilesWorker.progressBar.text=Live Triage: Copying files to {0}"}) + @Override + protected Void doInBackground() throws Exception { + // Setup progress bar. + String displayName = NbBundle.getMessage(this.getClass(), + "CopyFilesWorker.progressBar.text", + drivePath); + + // There's no way to stop FileUtils.copyDirectory, so don't include cancellation + progressHandle = ProgressHandle.createHandle(displayName); + progressHandle.start(); + progressHandle.switchToIndeterminate(); + + copyBatchFile(drivePath, appName); + copyApplication(sourceFolder, drivePath, appName); + + return null; + } + + @NbBundle.Messages({"CopyFilesWorker.title=Create Live Triage Drive", + "CopyFilesWorker.error.text=Error copying live triage files", + "CopyFilesWorker.done.text=Finished creating live triage disk"}) + @Override + protected void done() { + try { + super.get(); + + MessageNotifyUtil.Notify.info(NbBundle.getMessage(CopyFilesWorker.class, "CopyFilesWorker.title"), + NbBundle.getMessage(CopyFilesWorker.class, "CopyFilesWorker.done.text")); + + } catch (Exception ex) { + Logger.getLogger(CreateLiveTriageDriveAction.class.getName()).log(Level.SEVERE, "Fatal error during live triage drive creation", ex); //NON-NLS + MessageNotifyUtil.Notify.info(NbBundle.getMessage(CopyFilesWorker.class, "CopyFilesWorker.title"), + NbBundle.getMessage(CopyFilesWorker.class, "CopyFilesWorker.error.text")); + } finally { + if(progressHandle != null){ + progressHandle.finish(); + } + } + } + } + + private void copyApplication(Path sourceFolder, String destBaseFolder, String appName) throws IOException { + + // Create an appName folder in the destination + Path destAppFolder = Paths.get(destBaseFolder, appName); + if(! destAppFolder.toFile().exists()) { + if(! destAppFolder.toFile().mkdirs()){ + throw new IOException("Failed to create directory " + destAppFolder.toString()); + } + } + + // Now copy the files + FileUtils.copyDirectory(sourceFolder.toFile(), destAppFolder.toFile()); + } + + private void copyBatchFile(String destPath, String appName) throws IOException, InvalidPathException { + Path batchFilePath = Paths.get(destPath, "RunFromUSB.bat"); + FileUtils.writeStringToFile(batchFilePath.toFile(), getBatchFileContents(appName), "UTF-8"); + + } + + private String getBatchFileContents(String appName){ + + String batchFile = + "@echo off\n" + + "SET appName=\"" + appName + "\"\n" + + "\n" + + "REM Create the configData directory. Exit if it does not exist after attempting to create it\n" + + "if not exist configData mkdir configData\n" + + "if not exist configData (\n" + + " echo Error creating directory configData\n" + + " goto end\n" + + ")\n" + + "\n" + + "REM Create the userdir sub directory. Exit if it does not exist after attempting to create it\n" + + "if not exist configData\\userdir mkdir configData\\userdir\n" + + "if not exist configData\\userdir (\n" + + " echo Error creating directory configData\\userdir\n" + + " goto end\n" + + ")\n" + + "\n" + + "REM Create the cachedir sub directory. Exit if it does not exist after attempting to create it\n" + + "REM If it exists to start with, delete it to clear out old data\n" + + "if exist configData\\cachedir rd /s /q configData\\cachedir\n" + + "mkdir configData\\cachedir\n" + + "if not exist configData\\cachedir (\n" + + " echo Error creating directory configData\\cachedir\n" + + " goto end\n" + + ")\n" + + "\n" + + "REM Create the temp sub directory. Exit if it does not exist after attempting to create it\n" + + "REM If it exists to start with, delete it to clear out old data\n" + + "if exist configData\\temp rd /s /q configData\\temp\n" + + "mkdir configData\\temp\n" + + "if not exist configData\\temp (\n" + + " echo Error creating directory configData\\temp\n" + + " goto end\n" + + ")\n" + + "\n" + + "REM Create the cases directory. It's ok if this fails.\n" + + "if not exist cases mkdir cases\n" + + "\n" + + "if exist %appName% (\n" + + " if not exist %appName%\\bin\\%appName%64.exe (\n" + + " echo %appName%\\bin\\%appName%64.exe does not exist\n" + + " goto end\n" + + " )\n" + + " %appName%\\bin\\%appName%64.exe --userdir ..\\configData\\userdir --cachedir ..\\configData\\cachedir -J-Djava.io.tmpdir=..\\configData\\temp\n" + + ") else (\n" + + " echo Could not find %appName% directory\n" + + " goto end\n" + + ")\n" + + "\n" + + ":end\n" + + "\n" + + "REM Keep the cmd window open in case there was an error\n" + + "@pause\n"; + return batchFile; + } + + @Override + public String getName() { + return DISPLAY_NAME; + } + + @Override + public HelpCtx getHelpCtx() { + return HelpCtx.DEFAULT_HELP; + } + + @Override + public boolean asynchronous() { + return false; // run on edt + } +} diff --git a/Core/src/org/sleuthkit/autopsy/livetriage/SelectDriveDialog.form b/Core/src/org/sleuthkit/autopsy/livetriage/SelectDriveDialog.form new file mode 100644 index 0000000000..87592836f0 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/livetriage/SelectDriveDialog.form @@ -0,0 +1,180 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/livetriage/SelectDriveDialog.java b/Core/src/org/sleuthkit/autopsy/livetriage/SelectDriveDialog.java new file mode 100644 index 0000000000..d50d7aee2c --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/livetriage/SelectDriveDialog.java @@ -0,0 +1,423 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.sleuthkit.autopsy.livetriage; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CancellationException; +import java.util.logging.Level; +import java.awt.Dimension; +import java.awt.Point; +import javax.swing.SwingWorker; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import javax.swing.event.TableModelListener; +import javax.swing.table.TableModel; +import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.coreutils.LocalDisk; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.PlatformUtil; + +/** + * + */ +class SelectDriveDialog extends javax.swing.JDialog { + + private List disks = new ArrayList<>(); + private final LocalDiskModel model = new LocalDiskModel(); + private final java.awt.Frame parent; + private String drivePath = ""; + + /** + * Creates new form SelectDriveDialog + */ + @NbBundle.Messages({"SelectDriveDialog.title=Create Live Triage Drive"}) + SelectDriveDialog(java.awt.Frame parent, boolean modal) { + super(parent, modal); + initComponents(); + this.parent = parent; + + model.loadDisks(); + bnOk.setEnabled(false); + diskTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { + @Override + public void valueChanged(ListSelectionEvent e) { + if (diskTable.getSelectedRow() >= 0 && diskTable.getSelectedRow() < disks.size()) { + bnOk.setEnabled(true); + } else { //The selection changed to nothing valid being selected, such as with ctrl+click + bnOk.setEnabled(false); + } + } + }); + } + + void display(){ + this.setTitle(Bundle.SelectDriveDialog_title()); + + final Dimension parentSize = parent.getSize(); + final Point parentLocationOnScreen = parent.getLocationOnScreen(); + final Dimension childSize = this.getSize(); + int x; + int y; + x = (parentSize.width - childSize.width) / 2; + y = (parentSize.height - childSize.height) / 2; + x += parentLocationOnScreen.x; + y += parentLocationOnScreen.y; + + setLocation(x, y); + setVisible(true); + } + + String getSelectedDrive(){ + return this.drivePath; + } + + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + jScrollPane1 = new javax.swing.JScrollPane(); + diskTable = new javax.swing.JTable(); + jLabel1 = new javax.swing.JLabel(); + bnRefresh = new javax.swing.JButton(); + bnOk = new javax.swing.JButton(); + errorLabel = new javax.swing.JLabel(); + jLabel2 = new javax.swing.JLabel(); + jLabel3 = new javax.swing.JLabel(); + jLabel4 = new javax.swing.JLabel(); + jSeparator1 = new javax.swing.JSeparator(); + jLabel5 = new javax.swing.JLabel(); + jLabel6 = new javax.swing.JLabel(); + bnCancel = new javax.swing.JButton(); + + setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + + diskTable.setModel(model); + jScrollPane1.setViewportView(diskTable); + + org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(SelectDriveDialog.class, "SelectDriveDialog.jLabel1.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(bnRefresh, org.openide.util.NbBundle.getMessage(SelectDriveDialog.class, "SelectDriveDialog.bnRefresh.text")); // NOI18N + bnRefresh.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + bnRefreshActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(bnOk, org.openide.util.NbBundle.getMessage(SelectDriveDialog.class, "SelectDriveDialog.bnOk.text")); // NOI18N + bnOk.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + bnOkActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(errorLabel, org.openide.util.NbBundle.getMessage(SelectDriveDialog.class, "SelectDriveDialog.errorLabel.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(jLabel2, org.openide.util.NbBundle.getMessage(SelectDriveDialog.class, "SelectDriveDialog.jLabel2.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(jLabel3, org.openide.util.NbBundle.getMessage(SelectDriveDialog.class, "SelectDriveDialog.jLabel3.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(jLabel4, org.openide.util.NbBundle.getMessage(SelectDriveDialog.class, "SelectDriveDialog.jLabel4.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(jLabel5, org.openide.util.NbBundle.getMessage(SelectDriveDialog.class, "SelectDriveDialog.jLabel5.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(jLabel6, org.openide.util.NbBundle.getMessage(SelectDriveDialog.class, "SelectDriveDialog.jLabel6.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(bnCancel, org.openide.util.NbBundle.getMessage(SelectDriveDialog.class, "SelectDriveDialog.bnCancel.text")); // NOI18N + bnCancel.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + bnCancelActionPerformed(evt); + } + }); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(jLabel6) + .addGroup(layout.createSequentialGroup() + .addComponent(bnRefresh, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(bnOk, javax.swing.GroupLayout.PREFERRED_SIZE, 101, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(bnCancel, javax.swing.GroupLayout.PREFERRED_SIZE, 101, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE) + .addComponent(jLabel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) + .addComponent(jLabel3, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(jLabel2, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addComponent(jLabel4) + .addComponent(jSeparator1) + .addComponent(jLabel5, javax.swing.GroupLayout.DEFAULT_SIZE, 368, Short.MAX_VALUE) + .addComponent(errorLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(jLabel2) + .addGap(1, 1, 1) + .addComponent(jLabel3) + .addGap(1, 1, 1) + .addComponent(jLabel4, javax.swing.GroupLayout.PREFERRED_SIZE, 14, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(jLabel5) + .addGap(1, 1, 1) + .addComponent(jLabel6) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jLabel1) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 112, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(bnRefresh) + .addComponent(bnCancel) + .addComponent(bnOk)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(errorLabel) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + + pack(); + }// //GEN-END:initComponents + + private void bnRefreshActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnRefreshActionPerformed + model.loadDisks(); + }//GEN-LAST:event_bnRefreshActionPerformed + + private void bnOkActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnOkActionPerformed + if (diskTable.getSelectedRow() >= 0 && diskTable.getSelectedRow() < disks.size()) { + LocalDisk selectedDisk = disks.get(diskTable.getSelectedRow()); + drivePath = selectedDisk.getPath(); + } else { + drivePath = ""; + } + dispose(); + }//GEN-LAST:event_bnOkActionPerformed + + private void bnCancelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnCancelActionPerformed + dispose(); + }//GEN-LAST:event_bnCancelActionPerformed + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton bnCancel; + private javax.swing.JButton bnOk; + private javax.swing.JButton bnRefresh; + private javax.swing.JTable diskTable; + private javax.swing.JLabel errorLabel; + private javax.swing.JLabel jLabel1; + private javax.swing.JLabel jLabel2; + private javax.swing.JLabel jLabel3; + private javax.swing.JLabel jLabel4; + private javax.swing.JLabel jLabel5; + private javax.swing.JLabel jLabel6; + private javax.swing.JScrollPane jScrollPane1; + private javax.swing.JSeparator jSeparator1; + // End of variables declaration//GEN-END:variables + + + /** + * Table model for displaying information from LocalDisk Objects in a table. + */ + @NbBundle.Messages({"SelectDriveDialog.localDiskModel.loading.msg=", + "SelectDriveDialog.localDiskModel.nodrives.msg=Executable could not be found", + "SelectDriveDialog.diskTable.column1.title=Disk Name", + "SelectDriveDialog.diskTable.column2.title=Disk Size", + "SelectDriveDialog.errLabel.disksNotDetected.text=Disks were not detected. On some systems it requires admin privileges", + "SelectDriveDialog.errLabel.disksNotDetected.toolTipText=Disks were not detected. On some systems it requires admin privileges", + "SelectDriveDialog.errLabel.drivesNotDetected.text=Local drives were not detected. Auto-detection not supported on this OS or admin privileges required", + "SelectDriveDialog.errLabel.drivesNotDetected.toolTipText=Local drives were not detected. Auto-detection not supported on this OS or admin privileges required", + "SelectDriveDialog.errLabel.someDisksNotDetected.text=Some disks were not detected. On some systems it requires admin privileges", + "SelectDriveDialog.errLabel.someDisksNotDetected.toolTipText=Some disks were not detected. On some systems it requires admin privileges" + + }) + private class LocalDiskModel implements TableModel { + + private LocalDiskThread worker = null; + private boolean ready = false; + private volatile boolean loadingDisks = false; + + //private String SELECT = "Select a local disk:"; + private final String LOADING = NbBundle.getMessage(this.getClass(), "SelectDriveDialog.localDiskModel.loading.msg"); + private final String NO_DRIVES = NbBundle.getMessage(this.getClass(), "SelectDriveDialog.localDiskModel.nodrives.msg"); + + private void loadDisks() { + + // if there is a worker already building the lists, then cancel it first. + if (loadingDisks && worker != null) { + worker.cancel(false); + } + + // Clear the lists + errorLabel.setText(""); + diskTable.setEnabled(false); + ready = false; + loadingDisks = true; + worker = new LocalDiskThread(); + worker.execute(); + } + + @Override + public int getRowCount() { + if (disks.isEmpty()) { + return 0; + } + return disks.size(); + } + + @Override + public int getColumnCount() { + return 2; + + } + + @Override + public String getColumnName(int columnIndex) { + switch (columnIndex) { + case 0: + return NbBundle.getMessage(this.getClass(), "SelectDriveDialog.diskTable.column1.title"); + case 1: + return NbBundle.getMessage(this.getClass(), "SelectDriveDialog.diskTable.column2.title"); + default: + return "Unnamed"; //NON-NLS + } + } + + @Override + public Class getColumnClass(int columnIndex) { + return String.class; + } + + @Override + public boolean isCellEditable(int rowIndex, int columnIndex) { + return false; + } + + @Override + public Object getValueAt(int rowIndex, int columnIndex) { + if (ready) { + if (disks.isEmpty()) { + return NO_DRIVES; + } + switch (columnIndex) { + case 0: + return disks.get(rowIndex).getName(); + case 1: + return disks.get(rowIndex).getReadableSize(); + default: + return disks.get(rowIndex).getPath(); + } + } else { + return LOADING; + } + } + + @Override + public void setValueAt(Object aValue, int rowIndex, int columnIndex) { + //setter does nothing they should not be able to modify table + } + + @Override + public void addTableModelListener(TableModelListener l) { + + } + + @Override + public void removeTableModelListener(TableModelListener l) { + + } + + /** + * Gets the lists of physical drives and partitions and combines them + * into a list of disks. + */ + class LocalDiskThread extends SwingWorker { + + private final Logger logger = Logger.getLogger(LocalDiskThread.class.getName()); + private List physicalDrives = new ArrayList<>(); + private List partitions = new ArrayList<>(); + + @Override + protected Object doInBackground() throws Exception { + // Populate the lists + //physicalDrives = new ArrayList<>(); + partitions = new ArrayList<>(); + //physicalDrives = PlatformUtil.getPhysicalDrives(); + partitions = PlatformUtil.getPartitions(); + return null; + } + + /** + * Display any error messages that might of occurred when getting + * the lists of physical drives or partitions. + */ + private void displayErrors() { + if (physicalDrives.isEmpty() && partitions.isEmpty()) { + if (PlatformUtil.isWindowsOS()) { + errorLabel.setText( + NbBundle.getMessage(this.getClass(), "SelectDriveDialog.errLabel.disksNotDetected.text")); + errorLabel.setToolTipText(NbBundle.getMessage(this.getClass(), + "SelectDriveDialog.errLabel.disksNotDetected.toolTipText")); + } else { + errorLabel.setText( + NbBundle.getMessage(this.getClass(), "SelectDriveDialog.errLabel.drivesNotDetected.text")); + errorLabel.setToolTipText(NbBundle.getMessage(this.getClass(), + "SelectDriveDialog.errLabel.drivesNotDetected.toolTipText")); + } + errorLabel.setVisible(true); + diskTable.setEnabled(false); + }/* else if (physicalDrives.isEmpty()) { + errorLabel.setText( + NbBundle.getMessage(this.getClass(), "SelectDriveDialog.errLabel.someDisksNotDetected.text")); + errorLabel.setToolTipText(NbBundle.getMessage(this.getClass(), + "SelectDriveDialog.errLabel.someDisksNotDetected.toolTipText")); + errorLabel.setVisible(true); + }*/ + } + + @Override + protected void done() { + try { + super.get(); //block and get all exceptions thrown while doInBackground() + } catch (CancellationException ex) { + logger.log(Level.INFO, "Loading local disks was canceled."); //NON-NLS + } catch (InterruptedException ex) { + logger.log(Level.INFO, "Loading local disks was interrupted."); //NON-NLS + } catch (Exception ex) { + logger.log(Level.SEVERE, "Fatal error when loading local disks", ex); //NON-NLS + } finally { + if (!this.isCancelled()) { + displayErrors(); + worker = null; + loadingDisks = false; + disks = new ArrayList<>(); + //disks.addAll(physicalDrives); + disks.addAll(partitions); + if (disks.size() > 0) { + diskTable.setEnabled(true); + diskTable.clearSelection(); + } + ready = true; + } + } + diskTable.revalidate(); + } + } + } +} From a055757c64dd0d2eb59474b068fdd658daf806d2 Mon Sep 17 00:00:00 2001 From: Ann Priestman Date: Mon, 8 Jan 2018 13:48:35 -0500 Subject: [PATCH 03/12] Formatting --- .../CreateLiveTriageDriveAction.java | 218 +++++++++--------- .../autopsy/livetriage/SelectDriveDialog.java | 18 +- 2 files changed, 117 insertions(+), 119 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/livetriage/CreateLiveTriageDriveAction.java b/Core/src/org/sleuthkit/autopsy/livetriage/CreateLiveTriageDriveAction.java index 531e978618..a90993bb73 100644 --- a/Core/src/org/sleuthkit/autopsy/livetriage/CreateLiveTriageDriveAction.java +++ b/Core/src/org/sleuthkit/autopsy/livetriage/CreateLiveTriageDriveAction.java @@ -45,14 +45,14 @@ import org.sleuthkit.autopsy.coreutils.PlatformUtil; @ActionRegistration(displayName = "#CTL_CreateLiveTriageDriveAction", lazy = false) @NbBundle.Messages({"CTL_CreateLiveTriageDriveAction=Make Live Triage Drive"}) public final class CreateLiveTriageDriveAction extends CallableSystemAction { - + private static final String DISPLAY_NAME = Bundle.CTL_CreateLiveTriageDriveAction(); - + @Override public boolean isEnabled() { return true; } - + @NbBundle.Messages({"CreateLiveTriageDriveAction.error.title=Error creating live triage disk", "CreateLiveTriageDriveAction.exenotfound.message=Executable could not be found", "CreateLiveTriageDriveAction.batchFileError.message=Error creating batch file", @@ -64,45 +64,45 @@ public final class CreateLiveTriageDriveAction extends CallableSystemAction { @Override @SuppressWarnings("fallthrough") public void performAction() { - + Frame mainWindow = WindowManager.getDefault().getMainWindow(); - + // If this is an installed version, there should be an 64.exe file in the bin folder String appName = UserPreferences.getAppName(); String exeName = appName + "64.exe"; String installPath = PlatformUtil.getInstallPath(); - + Path exePath = Paths.get(installPath, "bin", exeName); System.out.println("Exe expected at " + exePath); - + // TEMP for testing - allows this to run from Netbeans exePath = Paths.get("C:\\Program Files\\Autopsy-4.5.0", "bin", exeName); - - if(! exePath.toFile().exists()) { + + if (!exePath.toFile().exists()) { JOptionPane.showMessageDialog(mainWindow, - Bundle.CreateLiveTriageDriveAction_exenotfound_message(), - Bundle.CreateLiveTriageDriveAction_error_title(), - JOptionPane.ERROR_MESSAGE); + Bundle.CreateLiveTriageDriveAction_exenotfound_message(), + Bundle.CreateLiveTriageDriveAction_error_title(), + JOptionPane.ERROR_MESSAGE); return; - } - + } + Path applicationBasePath; try { applicationBasePath = exePath.getParent().getParent(); - } catch (InvalidPathException ex){ + } catch (InvalidPathException ex) { JOptionPane.showMessageDialog(mainWindow, - Bundle.CreateLiveTriageDriveAction_appPathError_message(), - Bundle.CreateLiveTriageDriveAction_error_title(), - JOptionPane.ERROR_MESSAGE); + Bundle.CreateLiveTriageDriveAction_appPathError_message(), + Bundle.CreateLiveTriageDriveAction_error_title(), + JOptionPane.ERROR_MESSAGE); return; } - + SelectDriveDialog driveDialog = new SelectDriveDialog(mainWindow, true); driveDialog.display(); - - if(! driveDialog.getSelectedDrive().isEmpty()) { + + if (!driveDialog.getSelectedDrive().isEmpty()) { String drivePath = driveDialog.getSelectedDrive(); - if(drivePath.startsWith("\\\\.\\")){ + if (drivePath.startsWith("\\\\.\\")) { drivePath = drivePath.substring(4); } System.out.println("Destination path: " + drivePath); @@ -110,154 +110,154 @@ public final class CreateLiveTriageDriveAction extends CallableSystemAction { CopyFilesWorker worker = new CopyFilesWorker(applicationBasePath, drivePath, appName); worker.execute(); } - + } - + private class CopyFilesWorker extends SwingWorker { private final Path sourceFolder; private final String drivePath; private final String appName; private ProgressHandle progressHandle = null; - - CopyFilesWorker(Path sourceFolder, String drivePath, String appName){ + + CopyFilesWorker(Path sourceFolder, String drivePath, String appName) { this.sourceFolder = sourceFolder; this.drivePath = drivePath; this.appName = appName; } - + @NbBundle.Messages({"# {0} - drivePath", "CopyFilesWorker.progressBar.text=Live Triage: Copying files to {0}"}) @Override protected Void doInBackground() throws Exception { // Setup progress bar. String displayName = NbBundle.getMessage(this.getClass(), - "CopyFilesWorker.progressBar.text", - drivePath); - + "CopyFilesWorker.progressBar.text", + drivePath); + // There's no way to stop FileUtils.copyDirectory, so don't include cancellation progressHandle = ProgressHandle.createHandle(displayName); progressHandle.start(); progressHandle.switchToIndeterminate(); - + copyBatchFile(drivePath, appName); copyApplication(sourceFolder, drivePath, appName); - + return null; } - + @NbBundle.Messages({"CopyFilesWorker.title=Create Live Triage Drive", - "CopyFilesWorker.error.text=Error copying live triage files", - "CopyFilesWorker.done.text=Finished creating live triage disk"}) + "CopyFilesWorker.error.text=Error copying live triage files", + "CopyFilesWorker.done.text=Finished creating live triage disk"}) @Override protected void done() { try { super.get(); - + MessageNotifyUtil.Notify.info(NbBundle.getMessage(CopyFilesWorker.class, "CopyFilesWorker.title"), - NbBundle.getMessage(CopyFilesWorker.class, "CopyFilesWorker.done.text")); - + NbBundle.getMessage(CopyFilesWorker.class, "CopyFilesWorker.done.text")); + } catch (Exception ex) { Logger.getLogger(CreateLiveTriageDriveAction.class.getName()).log(Level.SEVERE, "Fatal error during live triage drive creation", ex); //NON-NLS MessageNotifyUtil.Notify.info(NbBundle.getMessage(CopyFilesWorker.class, "CopyFilesWorker.title"), - NbBundle.getMessage(CopyFilesWorker.class, "CopyFilesWorker.error.text")); + NbBundle.getMessage(CopyFilesWorker.class, "CopyFilesWorker.error.text")); } finally { - if(progressHandle != null){ + if (progressHandle != null) { progressHandle.finish(); } - } + } } } - + private void copyApplication(Path sourceFolder, String destBaseFolder, String appName) throws IOException { - + // Create an appName folder in the destination Path destAppFolder = Paths.get(destBaseFolder, appName); - if(! destAppFolder.toFile().exists()) { - if(! destAppFolder.toFile().mkdirs()){ + if (!destAppFolder.toFile().exists()) { + if (!destAppFolder.toFile().mkdirs()) { throw new IOException("Failed to create directory " + destAppFolder.toString()); } } - + // Now copy the files FileUtils.copyDirectory(sourceFolder.toFile(), destAppFolder.toFile()); } - + private void copyBatchFile(String destPath, String appName) throws IOException, InvalidPathException { Path batchFilePath = Paths.get(destPath, "RunFromUSB.bat"); FileUtils.writeStringToFile(batchFilePath.toFile(), getBatchFileContents(appName), "UTF-8"); - + } - - private String getBatchFileContents(String appName){ - - String batchFile = - "@echo off\n" + - "SET appName=\"" + appName + "\"\n" + - "\n" + - "REM Create the configData directory. Exit if it does not exist after attempting to create it\n" + - "if not exist configData mkdir configData\n" + - "if not exist configData (\n" + - " echo Error creating directory configData\n" + - " goto end\n" + - ")\n" + - "\n" + - "REM Create the userdir sub directory. Exit if it does not exist after attempting to create it\n" + - "if not exist configData\\userdir mkdir configData\\userdir\n" + - "if not exist configData\\userdir (\n" + - " echo Error creating directory configData\\userdir\n" + - " goto end\n" + - ")\n" + - "\n" + - "REM Create the cachedir sub directory. Exit if it does not exist after attempting to create it\n" + - "REM If it exists to start with, delete it to clear out old data\n" + - "if exist configData\\cachedir rd /s /q configData\\cachedir\n" + - "mkdir configData\\cachedir\n" + - "if not exist configData\\cachedir (\n" + - " echo Error creating directory configData\\cachedir\n" + - " goto end\n" + - ")\n" + - "\n" + - "REM Create the temp sub directory. Exit if it does not exist after attempting to create it\n" + - "REM If it exists to start with, delete it to clear out old data\n" + - "if exist configData\\temp rd /s /q configData\\temp\n" + - "mkdir configData\\temp\n" + - "if not exist configData\\temp (\n" + - " echo Error creating directory configData\\temp\n" + - " goto end\n" + - ")\n" + - "\n" + - "REM Create the cases directory. It's ok if this fails.\n" + - "if not exist cases mkdir cases\n" + - "\n" + - "if exist %appName% (\n" + - " if not exist %appName%\\bin\\%appName%64.exe (\n" + - " echo %appName%\\bin\\%appName%64.exe does not exist\n" + - " goto end\n" + - " )\n" + - " %appName%\\bin\\%appName%64.exe --userdir ..\\configData\\userdir --cachedir ..\\configData\\cachedir -J-Djava.io.tmpdir=..\\configData\\temp\n" + - ") else (\n" + - " echo Could not find %appName% directory\n" + - " goto end\n" + - ")\n" + - "\n" + - ":end\n" + - "\n" + - "REM Keep the cmd window open in case there was an error\n" + - "@pause\n"; - return batchFile; + + private String getBatchFileContents(String appName) { + + String batchFile + = "@echo off\n" + + "SET appName=\"" + appName + "\"\n" + + "\n" + + "REM Create the configData directory. Exit if it does not exist after attempting to create it\n" + + "if not exist configData mkdir configData\n" + + "if not exist configData (\n" + + " echo Error creating directory configData\n" + + " goto end\n" + + ")\n" + + "\n" + + "REM Create the userdir sub directory. Exit if it does not exist after attempting to create it\n" + + "if not exist configData\\userdir mkdir configData\\userdir\n" + + "if not exist configData\\userdir (\n" + + " echo Error creating directory configData\\userdir\n" + + " goto end\n" + + ")\n" + + "\n" + + "REM Create the cachedir sub directory. Exit if it does not exist after attempting to create it\n" + + "REM If it exists to start with, delete it to clear out old data\n" + + "if exist configData\\cachedir rd /s /q configData\\cachedir\n" + + "mkdir configData\\cachedir\n" + + "if not exist configData\\cachedir (\n" + + " echo Error creating directory configData\\cachedir\n" + + " goto end\n" + + ")\n" + + "\n" + + "REM Create the temp sub directory. Exit if it does not exist after attempting to create it\n" + + "REM If it exists to start with, delete it to clear out old data\n" + + "if exist configData\\temp rd /s /q configData\\temp\n" + + "mkdir configData\\temp\n" + + "if not exist configData\\temp (\n" + + " echo Error creating directory configData\\temp\n" + + " goto end\n" + + ")\n" + + "\n" + + "REM Create the cases directory. It's ok if this fails.\n" + + "if not exist cases mkdir cases\n" + + "\n" + + "if exist %appName% (\n" + + " if not exist %appName%\\bin\\%appName%64.exe (\n" + + " echo %appName%\\bin\\%appName%64.exe does not exist\n" + + " goto end\n" + + " )\n" + + " %appName%\\bin\\%appName%64.exe --userdir ..\\configData\\userdir --cachedir ..\\configData\\cachedir -J-Djava.io.tmpdir=..\\configData\\temp\n" + + ") else (\n" + + " echo Could not find %appName% directory\n" + + " goto end\n" + + ")\n" + + "\n" + + ":end\n" + + "\n" + + "REM Keep the cmd window open in case there was an error\n" + + "@pause\n"; + return batchFile; } - + @Override public String getName() { return DISPLAY_NAME; } - + @Override public HelpCtx getHelpCtx() { return HelpCtx.DEFAULT_HELP; } - + @Override public boolean asynchronous() { return false; // run on edt diff --git a/Core/src/org/sleuthkit/autopsy/livetriage/SelectDriveDialog.java b/Core/src/org/sleuthkit/autopsy/livetriage/SelectDriveDialog.java index d50d7aee2c..1ca506d323 100644 --- a/Core/src/org/sleuthkit/autopsy/livetriage/SelectDriveDialog.java +++ b/Core/src/org/sleuthkit/autopsy/livetriage/SelectDriveDialog.java @@ -30,7 +30,7 @@ class SelectDriveDialog extends javax.swing.JDialog { private final LocalDiskModel model = new LocalDiskModel(); private final java.awt.Frame parent; private String drivePath = ""; - + /** * Creates new form SelectDriveDialog */ @@ -39,7 +39,7 @@ class SelectDriveDialog extends javax.swing.JDialog { super(parent, modal); initComponents(); this.parent = parent; - + model.loadDisks(); bnOk.setEnabled(false); diskTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { @@ -53,10 +53,10 @@ class SelectDriveDialog extends javax.swing.JDialog { } }); } - - void display(){ + + void display() { this.setTitle(Bundle.SelectDriveDialog_title()); - + final Dimension parentSize = parent.getSize(); final Point parentLocationOnScreen = parent.getLocationOnScreen(); final Dimension childSize = this.getSize(); @@ -70,12 +70,11 @@ class SelectDriveDialog extends javax.swing.JDialog { setLocation(x, y); setVisible(true); } - - String getSelectedDrive(){ + + String getSelectedDrive() { return this.drivePath; } - /** * This method is called from within the constructor to initialize the form. * WARNING: Do NOT modify this code. The content of this method is always @@ -231,7 +230,6 @@ class SelectDriveDialog extends javax.swing.JDialog { private javax.swing.JSeparator jSeparator1; // End of variables declaration//GEN-END:variables - /** * Table model for displaying information from LocalDisk Objects in a table. */ @@ -245,7 +243,7 @@ class SelectDriveDialog extends javax.swing.JDialog { "SelectDriveDialog.errLabel.drivesNotDetected.toolTipText=Local drives were not detected. Auto-detection not supported on this OS or admin privileges required", "SelectDriveDialog.errLabel.someDisksNotDetected.text=Some disks were not detected. On some systems it requires admin privileges", "SelectDriveDialog.errLabel.someDisksNotDetected.toolTipText=Some disks were not detected. On some systems it requires admin privileges" - + }) private class LocalDiskModel implements TableModel { From 15e296145df3a8d6f30c51a721ff034fc03551c2 Mon Sep 17 00:00:00 2001 From: Raman Date: Tue, 9 Jan 2018 09:39:26 -0500 Subject: [PATCH 04/12] Address review comments --- .../contentviewers/ApplicationContentViewer.java | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/ApplicationContentViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/ApplicationContentViewer.java index 4c079baca3..bf7c13279a 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/ApplicationContentViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/ApplicationContentViewer.java @@ -83,7 +83,7 @@ public class ApplicationContentViewer extends javax.swing.JPanel implements Data * mimetype */ private ContentViewer getSupportingViewer(String mimeType) { - return mimeTypeToViewerMap.containsKey(mimeType) ? mimeTypeToViewerMap.get(mimeType) : null; + return mimeTypeToViewerMap.get(mimeType); } /** @@ -104,6 +104,9 @@ public class ApplicationContentViewer extends javax.swing.JPanel implements Data @Override public void setNode(Node selectedNode) { + + resetComponent(); + if (selectedNode == null) { return; } @@ -208,16 +211,9 @@ public class ApplicationContentViewer extends javax.swing.JPanel implements Data @Override public int isPreferred(Node node) { - if (node == null) { - return 0; - } - AbstractFile file = node.getLookup().lookup(AbstractFile.class); - if (file == null) { - return 0; - } - String mimeType = file.getMIMEType(); + if (Strings.isNullOrEmpty(mimeType)) { LOGGER.log(Level.INFO, "Mimetype not known for file: {0}", file.getName()); //NON-NLS try { From 00e68c44aa6e14bb80d237068a98c0e7cd35246f Mon Sep 17 00:00:00 2001 From: Ann Priestman Date: Tue, 9 Jan 2018 09:44:27 -0500 Subject: [PATCH 05/12] Add fix to restore directory when running as admin --- .../autopsy/livetriage/CreateLiveTriageDriveAction.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/livetriage/CreateLiveTriageDriveAction.java b/Core/src/org/sleuthkit/autopsy/livetriage/CreateLiveTriageDriveAction.java index a90993bb73..5f10b4f9df 100644 --- a/Core/src/org/sleuthkit/autopsy/livetriage/CreateLiveTriageDriveAction.java +++ b/Core/src/org/sleuthkit/autopsy/livetriage/CreateLiveTriageDriveAction.java @@ -73,10 +73,6 @@ public final class CreateLiveTriageDriveAction extends CallableSystemAction { String installPath = PlatformUtil.getInstallPath(); Path exePath = Paths.get(installPath, "bin", exeName); - System.out.println("Exe expected at " + exePath); - - // TEMP for testing - allows this to run from Netbeans - exePath = Paths.get("C:\\Program Files\\Autopsy-4.5.0", "bin", exeName); if (!exePath.toFile().exists()) { JOptionPane.showMessageDialog(mainWindow, @@ -193,6 +189,11 @@ public final class CreateLiveTriageDriveAction extends CallableSystemAction { String batchFile = "@echo off\n" + + "\n" + + "REM This restores the working directory when using 'Run as administrator'" + + "@setlocal enableextensions\n" + + "@cd /d \"%~dp0\"" + + "\n" + "SET appName=\"" + appName + "\"\n" + "\n" + "REM Create the configData directory. Exit if it does not exist after attempting to create it\n" From 62e96ff4e06d61e6536434bc7d851db568de7e65 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\zhaohui" Date: Tue, 9 Jan 2018 17:11:45 -0500 Subject: [PATCH 06/12] 3449: Failed to generate report if couldn't create a report directory. --- .../autopsy/report/ReportGenerator.java | 44 +++++++------------ .../autopsy/report/ReportWizardAction.java | 21 ++++++--- 2 files changed, 31 insertions(+), 34 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportGenerator.java b/Core/src/org/sleuthkit/autopsy/report/ReportGenerator.java index 0c8aa263c3..1bb05652d4 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ReportGenerator.java +++ b/Core/src/org/sleuthkit/autopsy/report/ReportGenerator.java @@ -136,16 +136,9 @@ class ReportGenerator { /** * Run the GeneralReportModules using a SwingWorker. */ - void generateGeneralReport(GeneralReportModule generalReportModule) { + void generateGeneralReport(GeneralReportModule generalReportModule) throws IOException { if (generalReportModule != null) { - reportPathFormatString = String.format(reportPathFormatString, generalReportModule.getName()); - // Create the root reports directory. - try { - FileUtil.createFolder(new File(reportPathFormatString)); - } catch (IOException ex) { - errorList.add(NbBundle.getMessage(this.getClass(), "ReportGenerator.errList.failedMakeRptFolder")); - logger.log(Level.SEVERE, "Failed to make report folder, may be unable to generate reports.", ex); //NON-NLS - } + reportPathFormatString = createReportDirectory(generalReportModule, reportPathFormatString); setupProgressPanel(generalReportModule); ReportWorker worker = new ReportWorker(() -> { generalReportModule.generateReport(reportPathFormatString, progressPanel); @@ -163,16 +156,9 @@ class ReportGenerator { * @param tagSelections the enabled/disabled state of the tag names * to be included in the report */ - void generateTableReport(TableReportModule tableReport, Map artifactTypeSelections, Map tagNameSelections) { + void generateTableReport(TableReportModule tableReport, Map artifactTypeSelections, Map tagNameSelections) throws IOException { if (tableReport != null && null != artifactTypeSelections) { - reportPathFormatString = String.format(reportPathFormatString, tableReport.getName()); - // Create the root reports directory. - try { - FileUtil.createFolder(new File(reportPathFormatString)); - } catch (IOException ex) { - errorList.add(NbBundle.getMessage(this.getClass(), "ReportGenerator.errList.failedMakeRptFolder")); - logger.log(Level.SEVERE, "Failed to make report folder, may be unable to generate reports.", ex); //NON-NLS - } + reportPathFormatString = createReportDirectory(tableReport, reportPathFormatString); setupProgressPanel(tableReport); ReportWorker worker = new ReportWorker(() -> { tableReport.startReport(reportPathFormatString); @@ -194,16 +180,9 @@ class ReportGenerator { * @param enabledInfo the Information that should be included about each * file in the report. */ - void generateFileListReport(FileReportModule fileReportModule, Map enabledInfo) { + void generateFileListReport(FileReportModule fileReportModule, Map enabledInfo) throws IOException { if (fileReportModule != null && null != enabledInfo) { - reportPathFormatString = String.format(reportPathFormatString, fileReportModule.getName()); - // Create the root reports directory. - try { - FileUtil.createFolder(new File(reportPathFormatString)); - } catch (IOException ex) { - errorList.add(NbBundle.getMessage(this.getClass(), "ReportGenerator.errList.failedMakeRptFolder")); - logger.log(Level.SEVERE, "Failed to make report folder, may be unable to generate reports.", ex); //NON-NLS - } + reportPathFormatString = createReportDirectory(fileReportModule, reportPathFormatString); List enabled = new ArrayList<>(); for (Entry e : enabledInfo.entrySet()) { if (e.getValue()) { @@ -285,6 +264,17 @@ class ReportGenerator { } } + private static String createReportDirectory(ReportModule module, String pathFormat) throws IOException { + String reportPath = String.format(pathFormat, module.getName()); + // Create the root reports directory. + try { + FileUtil.createFolder(new File(reportPath)); + } catch (IOException ex) { + throw new IOException("Failed to make report folder, unable to generate reports.", ex); + } + return reportPath; + } + private class ReportWorker extends SwingWorker { private final Runnable doInBackground; diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportWizardAction.java b/Core/src/org/sleuthkit/autopsy/report/ReportWizardAction.java index 49f37008be..618cba2a38 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ReportWizardAction.java +++ b/Core/src/org/sleuthkit/autopsy/report/ReportWizardAction.java @@ -2,7 +2,7 @@ * * Autopsy Forensic Browser * - * Copyright 2013-2015 Basis Technology Corp. + * Copyright 2013-2018 Basis Technology Corp. * * Copyright 2012 42six Solutions. * Contact: aebadirad 42six com @@ -26,12 +26,14 @@ import java.awt.Component; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.beans.PropertyChangeEvent; +import java.io.IOException; import java.text.MessageFormat; import java.util.EnumSet; import java.util.Map; import javax.swing.ImageIcon; import javax.swing.JButton; import org.openide.DialogDisplayer; +import org.openide.NotifyDescriptor; import org.openide.WizardDescriptor; import org.openide.awt.ActionID; import org.openide.awt.ActionReference; @@ -70,12 +72,17 @@ public final class ReportWizardAction extends CallableSystemAction implements Pr TableReportModule tableReport = (TableReportModule) wiz.getProperty("tableModule"); GeneralReportModule generalReport = (GeneralReportModule) wiz.getProperty("generalModule"); FileReportModule fileReport = (FileReportModule) wiz.getProperty("fileModule"); - if (tableReport != null) { - generator.generateTableReport(tableReport, (Map) wiz.getProperty("artifactStates"), (Map) wiz.getProperty("tagStates")); //NON-NLS - } else if (generalReport != null) { - generator.generateGeneralReport(generalReport); - } else if (fileReport != null) { - generator.generateFileListReport(fileReport, (Map) wiz.getProperty("fileReportOptions")); //NON-NLS + try { + if (tableReport != null) { + generator.generateTableReport(tableReport, (Map) wiz.getProperty("artifactStates"), (Map) wiz.getProperty("tagStates")); //NON-NLS + } else if (generalReport != null) { + generator.generateGeneralReport(generalReport); + } else if (fileReport != null) { + generator.generateFileListReport(fileReport, (Map) wiz.getProperty("fileReportOptions")); //NON-NLS + } + } catch (IOException e) { + NotifyDescriptor descriptor = new NotifyDescriptor.Message(e.getMessage(), NotifyDescriptor.ERROR_MESSAGE); + DialogDisplayer.getDefault().notify(descriptor); } } } From 43d32eb1c809377eb1aa05f966d5ad47ebc5b432 Mon Sep 17 00:00:00 2001 From: Ann Priestman Date: Wed, 10 Jan 2018 09:31:24 -0500 Subject: [PATCH 07/12] Changed progress bar --- .../autopsy/livetriage/Bundle.properties | 6 +- .../CreateLiveTriageDriveAction.java | 86 ++++++++++------- .../autopsy/livetriage/SelectDriveDialog.form | 93 ++++++++----------- .../autopsy/livetriage/SelectDriveDialog.java | 79 +++++----------- 4 files changed, 114 insertions(+), 150 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/livetriage/Bundle.properties b/Core/src/org/sleuthkit/autopsy/livetriage/Bundle.properties index 49fdf032cb..61f522a6b4 100644 --- a/Core/src/org/sleuthkit/autopsy/livetriage/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/livetriage/Bundle.properties @@ -3,9 +3,5 @@ SelectDriveDialog.bnRefresh.text=Refresh SelectDriveDialog.lbSelectDrive.text=Select the drive to copy the application and script to: SelectDriveDialog.jLabel1.text=Select drive to use for live triage (may take time to load): SelectDriveDialog.errorLabel.text=jLabel2 -SelectDriveDialog.jLabel2.text=This feature copies the application and a batch file to a removable drive, -SelectDriveDialog.jLabel3.text=allowing systems to be analyzed without installing the software or imaging -SelectDriveDialog.jLabel4.text=the drives. -SelectDriveDialog.jLabel5.text=To analyze a system, insert the drive and run "RunFromUSB.bat" as -SelectDriveDialog.jLabel6.text=administrator. SelectDriveDialog.bnCancel.text=Cancel +SelectDriveDialog.jTextArea1.text=This feature copies the application and a batch file to a removable drive,\nallowing systems to be analyzed without installing the software or\nimaging the drives.\n\nTo analyze a system, insert the drive and run "RunFromUSB.bat" as\nadministrator, then select the "Local Disk" option on the Add Data Source\npanel. diff --git a/Core/src/org/sleuthkit/autopsy/livetriage/CreateLiveTriageDriveAction.java b/Core/src/org/sleuthkit/autopsy/livetriage/CreateLiveTriageDriveAction.java index 5f10b4f9df..1c54ee633b 100644 --- a/Core/src/org/sleuthkit/autopsy/livetriage/CreateLiveTriageDriveAction.java +++ b/Core/src/org/sleuthkit/autopsy/livetriage/CreateLiveTriageDriveAction.java @@ -23,11 +23,12 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.InvalidPathException; import java.util.logging.Level; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeEvent; import javax.swing.JOptionPane; import java.awt.Frame; import javax.swing.SwingWorker; import org.apache.commons.io.FileUtils; -import org.netbeans.api.progress.ProgressHandle; import org.openide.awt.ActionID; import org.openide.awt.ActionReference; import org.openide.awt.ActionRegistration; @@ -39,14 +40,18 @@ import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.coreutils.PlatformUtil; +import org.sleuthkit.autopsy.progress.ModalDialogProgressIndicator; @ActionID(category = "Tools", id = "org.sleuthkit.autopsy.livetriage.CreateLiveTriageDriveAction") @ActionReference(path = "Menu/Tools", position = 1401) @ActionRegistration(displayName = "#CTL_CreateLiveTriageDriveAction", lazy = false) @NbBundle.Messages({"CTL_CreateLiveTriageDriveAction=Make Live Triage Drive"}) -public final class CreateLiveTriageDriveAction extends CallableSystemAction { +public final class CreateLiveTriageDriveAction extends CallableSystemAction implements PropertyChangeListener { private static final String DISPLAY_NAME = Bundle.CTL_CreateLiveTriageDriveAction(); + private ModalDialogProgressIndicator progressIndicator = null; + private String drivePath = ""; + private CopyFilesWorker worker; @Override public boolean isEnabled() { @@ -57,7 +62,7 @@ public final class CreateLiveTriageDriveAction extends CallableSystemAction { "CreateLiveTriageDriveAction.exenotfound.message=Executable could not be found", "CreateLiveTriageDriveAction.batchFileError.message=Error creating batch file", "CreateLiveTriageDriveAction.appPathError.message=Could not location application directory", - "CreateLiveTriageDriveAction.copyError.message=Could not copy application", + "CreateLiveTriageDriveAction.copyError.message=Could not copy application. Only works on installed version.", "CreateLiveTriageDriveAction.success.title=Success", "CreateLiveTriageDriveAction.success.message=Live triage drive created. Use RunFromUSB.bat to run the application" }) @@ -97,16 +102,46 @@ public final class CreateLiveTriageDriveAction extends CallableSystemAction { driveDialog.display(); if (!driveDialog.getSelectedDrive().isEmpty()) { - String drivePath = driveDialog.getSelectedDrive(); + drivePath = driveDialog.getSelectedDrive(); if (drivePath.startsWith("\\\\.\\")) { drivePath = drivePath.substring(4); } - System.out.println("Destination path: " + drivePath); - - CopyFilesWorker worker = new CopyFilesWorker(applicationBasePath, drivePath, appName); + + worker = new CopyFilesWorker(applicationBasePath, drivePath, appName); + worker.addPropertyChangeListener(this); worker.execute(); } - + } + + @NbBundle.Messages({"# {0} - drivePath", + "CreateLiveTriageDriveAction.progressBar.text=Copying live triage files to {0}", + "CreateLiveTriageDriveAction.progressBar.title=Please wait"}) + @Override + public void propertyChange(PropertyChangeEvent evt) { + + if ("state".equals(evt.getPropertyName()) + && (SwingWorker.StateValue.STARTED.equals(evt.getNewValue()))) { + + // Setup progress bar. + String displayStr = NbBundle.getMessage(this.getClass(), "CreateLiveTriageDriveAction.progressBar.text", + drivePath); + + progressIndicator = new ModalDialogProgressIndicator(WindowManager.getDefault().getMainWindow(), + NbBundle.getMessage(this.getClass(), "CreateLiveTriageDriveAction.progressBar.title")); + progressIndicator.start(displayStr); + + } else if ("state".equals(evt.getPropertyName()) + && (SwingWorker.StateValue.DONE.equals(evt.getNewValue()))) { + if(progressIndicator != null){ + progressIndicator.finish(); + } + + if(worker.hadError()){ + MessageNotifyUtil.Message.error(NbBundle.getMessage(CopyFilesWorker.class, "CopyFilesWorker.error.text")); + } else { + MessageNotifyUtil.Message.info(NbBundle.getMessage(CopyFilesWorker.class, "CopyFilesWorker.done.text")); + } + } } private class CopyFilesWorker extends SwingWorker { @@ -114,27 +149,20 @@ public final class CreateLiveTriageDriveAction extends CallableSystemAction { private final Path sourceFolder; private final String drivePath; private final String appName; - private ProgressHandle progressHandle = null; + private boolean error = false; CopyFilesWorker(Path sourceFolder, String drivePath, String appName) { this.sourceFolder = sourceFolder; this.drivePath = drivePath; this.appName = appName; } - - @NbBundle.Messages({"# {0} - drivePath", - "CopyFilesWorker.progressBar.text=Live Triage: Copying files to {0}"}) + + boolean hadError(){ + return error; + } + @Override protected Void doInBackground() throws Exception { - // Setup progress bar. - String displayName = NbBundle.getMessage(this.getClass(), - "CopyFilesWorker.progressBar.text", - drivePath); - - // There's no way to stop FileUtils.copyDirectory, so don't include cancellation - progressHandle = ProgressHandle.createHandle(displayName); - progressHandle.start(); - progressHandle.switchToIndeterminate(); copyBatchFile(drivePath, appName); copyApplication(sourceFolder, drivePath, appName); @@ -142,25 +170,15 @@ public final class CreateLiveTriageDriveAction extends CallableSystemAction { return null; } - @NbBundle.Messages({"CopyFilesWorker.title=Create Live Triage Drive", - "CopyFilesWorker.error.text=Error copying live triage files", + @NbBundle.Messages({"CopyFilesWorker.error.text=Error copying live triage files", "CopyFilesWorker.done.text=Finished creating live triage disk"}) @Override protected void done() { try { super.get(); - - MessageNotifyUtil.Notify.info(NbBundle.getMessage(CopyFilesWorker.class, "CopyFilesWorker.title"), - NbBundle.getMessage(CopyFilesWorker.class, "CopyFilesWorker.done.text")); - } catch (Exception ex) { - Logger.getLogger(CreateLiveTriageDriveAction.class.getName()).log(Level.SEVERE, "Fatal error during live triage drive creation", ex); //NON-NLS - MessageNotifyUtil.Notify.info(NbBundle.getMessage(CopyFilesWorker.class, "CopyFilesWorker.title"), - NbBundle.getMessage(CopyFilesWorker.class, "CopyFilesWorker.error.text")); - } finally { - if (progressHandle != null) { - progressHandle.finish(); - } + error = true; + Logger.getLogger(CreateLiveTriageDriveAction.class.getName()).log(Level.SEVERE, "Fatal error during live triage drive creation", ex); //NON-NLS } } } diff --git a/Core/src/org/sleuthkit/autopsy/livetriage/SelectDriveDialog.form b/Core/src/org/sleuthkit/autopsy/livetriage/SelectDriveDialog.form index 87592836f0..ee776348dc 100644 --- a/Core/src/org/sleuthkit/autopsy/livetriage/SelectDriveDialog.form +++ b/Core/src/org/sleuthkit/autopsy/livetriage/SelectDriveDialog.form @@ -23,27 +23,21 @@ - + - - - + + - - - - - - + @@ -53,16 +47,8 @@ - - - - - - - - - - + + @@ -129,43 +115,8 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -176,5 +127,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/livetriage/SelectDriveDialog.java b/Core/src/org/sleuthkit/autopsy/livetriage/SelectDriveDialog.java index 1ca506d323..c13d82d324 100644 --- a/Core/src/org/sleuthkit/autopsy/livetriage/SelectDriveDialog.java +++ b/Core/src/org/sleuthkit/autopsy/livetriage/SelectDriveDialog.java @@ -90,13 +90,10 @@ class SelectDriveDialog extends javax.swing.JDialog { bnRefresh = new javax.swing.JButton(); bnOk = new javax.swing.JButton(); errorLabel = new javax.swing.JLabel(); - jLabel2 = new javax.swing.JLabel(); - jLabel3 = new javax.swing.JLabel(); - jLabel4 = new javax.swing.JLabel(); jSeparator1 = new javax.swing.JSeparator(); - jLabel5 = new javax.swing.JLabel(); - jLabel6 = new javax.swing.JLabel(); bnCancel = new javax.swing.JButton(); + jScrollPane2 = new javax.swing.JScrollPane(); + jTextArea1 = new javax.swing.JTextArea(); setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); @@ -121,16 +118,6 @@ class SelectDriveDialog extends javax.swing.JDialog { org.openide.awt.Mnemonics.setLocalizedText(errorLabel, org.openide.util.NbBundle.getMessage(SelectDriveDialog.class, "SelectDriveDialog.errorLabel.text")); // NOI18N - org.openide.awt.Mnemonics.setLocalizedText(jLabel2, org.openide.util.NbBundle.getMessage(SelectDriveDialog.class, "SelectDriveDialog.jLabel2.text")); // NOI18N - - org.openide.awt.Mnemonics.setLocalizedText(jLabel3, org.openide.util.NbBundle.getMessage(SelectDriveDialog.class, "SelectDriveDialog.jLabel3.text")); // NOI18N - - org.openide.awt.Mnemonics.setLocalizedText(jLabel4, org.openide.util.NbBundle.getMessage(SelectDriveDialog.class, "SelectDriveDialog.jLabel4.text")); // NOI18N - - org.openide.awt.Mnemonics.setLocalizedText(jLabel5, org.openide.util.NbBundle.getMessage(SelectDriveDialog.class, "SelectDriveDialog.jLabel5.text")); // NOI18N - - org.openide.awt.Mnemonics.setLocalizedText(jLabel6, org.openide.util.NbBundle.getMessage(SelectDriveDialog.class, "SelectDriveDialog.jLabel6.text")); // NOI18N - org.openide.awt.Mnemonics.setLocalizedText(bnCancel, org.openide.util.NbBundle.getMessage(SelectDriveDialog.class, "SelectDriveDialog.bnCancel.text")); // NOI18N bnCancel.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { @@ -138,6 +125,16 @@ class SelectDriveDialog extends javax.swing.JDialog { } }); + jScrollPane2.setBorder(null); + + jTextArea1.setBackground(new java.awt.Color(240, 240, 240)); + jTextArea1.setColumns(20); + jTextArea1.setFont(new java.awt.Font("Tahoma", 0, 11)); // NOI18N + jTextArea1.setRows(5); + jTextArea1.setText(org.openide.util.NbBundle.getMessage(SelectDriveDialog.class, "SelectDriveDialog.jTextArea1.text")); // NOI18N + jTextArea1.setBorder(null); + jScrollPane2.setViewportView(jTextArea1); + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup( @@ -145,38 +142,25 @@ class SelectDriveDialog extends javax.swing.JDialog { .addGroup(layout.createSequentialGroup() .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) - .addComponent(jLabel6) .addGroup(layout.createSequentialGroup() - .addComponent(bnRefresh, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(bnRefresh, javax.swing.GroupLayout.DEFAULT_SIZE, 112, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 48, Short.MAX_VALUE) .addComponent(bnOk, javax.swing.GroupLayout.PREFERRED_SIZE, 101, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(bnCancel, javax.swing.GroupLayout.PREFERRED_SIZE, 101, javax.swing.GroupLayout.PREFERRED_SIZE)) .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE) .addComponent(jLabel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) - .addComponent(jLabel3, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(jLabel2, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - .addComponent(jLabel4) .addComponent(jSeparator1) - .addComponent(jLabel5, javax.swing.GroupLayout.DEFAULT_SIZE, 368, Short.MAX_VALUE) - .addComponent(errorLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addComponent(errorLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(jScrollPane2)) .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() - .addComponent(jLabel2) - .addGap(1, 1, 1) - .addComponent(jLabel3) - .addGap(1, 1, 1) - .addComponent(jLabel4, javax.swing.GroupLayout.PREFERRED_SIZE, 14, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(jLabel5) - .addGap(1, 1, 1) - .addComponent(jLabel6) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, 107, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jLabel1) @@ -221,13 +205,10 @@ class SelectDriveDialog extends javax.swing.JDialog { private javax.swing.JTable diskTable; private javax.swing.JLabel errorLabel; private javax.swing.JLabel jLabel1; - private javax.swing.JLabel jLabel2; - private javax.swing.JLabel jLabel3; - private javax.swing.JLabel jLabel4; - private javax.swing.JLabel jLabel5; - private javax.swing.JLabel jLabel6; private javax.swing.JScrollPane jScrollPane1; + private javax.swing.JScrollPane jScrollPane2; private javax.swing.JSeparator jSeparator1; + private javax.swing.JTextArea jTextArea1; // End of variables declaration//GEN-END:variables /** @@ -238,11 +219,7 @@ class SelectDriveDialog extends javax.swing.JDialog { "SelectDriveDialog.diskTable.column1.title=Disk Name", "SelectDriveDialog.diskTable.column2.title=Disk Size", "SelectDriveDialog.errLabel.disksNotDetected.text=Disks were not detected. On some systems it requires admin privileges", - "SelectDriveDialog.errLabel.disksNotDetected.toolTipText=Disks were not detected. On some systems it requires admin privileges", - "SelectDriveDialog.errLabel.drivesNotDetected.text=Local drives were not detected. Auto-detection not supported on this OS or admin privileges required", - "SelectDriveDialog.errLabel.drivesNotDetected.toolTipText=Local drives were not detected. Auto-detection not supported on this OS or admin privileges required", - "SelectDriveDialog.errLabel.someDisksNotDetected.text=Some disks were not detected. On some systems it requires admin privileges", - "SelectDriveDialog.errLabel.someDisksNotDetected.toolTipText=Some disks were not detected. On some systems it requires admin privileges" + "SelectDriveDialog.errLabel.disksNotDetected.toolTipText=Disks were not detected." }) private class LocalDiskModel implements TableModel { @@ -348,15 +325,12 @@ class SelectDriveDialog extends javax.swing.JDialog { class LocalDiskThread extends SwingWorker { private final Logger logger = Logger.getLogger(LocalDiskThread.class.getName()); - private List physicalDrives = new ArrayList<>(); private List partitions = new ArrayList<>(); @Override protected Object doInBackground() throws Exception { // Populate the lists - //physicalDrives = new ArrayList<>(); partitions = new ArrayList<>(); - //physicalDrives = PlatformUtil.getPhysicalDrives(); partitions = PlatformUtil.getPartitions(); return null; } @@ -366,7 +340,7 @@ class SelectDriveDialog extends javax.swing.JDialog { * the lists of physical drives or partitions. */ private void displayErrors() { - if (physicalDrives.isEmpty() && partitions.isEmpty()) { + if (partitions.isEmpty()) { if (PlatformUtil.isWindowsOS()) { errorLabel.setText( NbBundle.getMessage(this.getClass(), "SelectDriveDialog.errLabel.disksNotDetected.text")); @@ -380,13 +354,7 @@ class SelectDriveDialog extends javax.swing.JDialog { } errorLabel.setVisible(true); diskTable.setEnabled(false); - }/* else if (physicalDrives.isEmpty()) { - errorLabel.setText( - NbBundle.getMessage(this.getClass(), "SelectDriveDialog.errLabel.someDisksNotDetected.text")); - errorLabel.setToolTipText(NbBundle.getMessage(this.getClass(), - "SelectDriveDialog.errLabel.someDisksNotDetected.toolTipText")); - errorLabel.setVisible(true); - }*/ + } } @Override @@ -405,7 +373,6 @@ class SelectDriveDialog extends javax.swing.JDialog { worker = null; loadingDisks = false; disks = new ArrayList<>(); - //disks.addAll(physicalDrives); disks.addAll(partitions); if (disks.size() > 0) { diskTable.setEnabled(true); From 6c973af0b6e1e163b03b8d59275fee063763d6c6 Mon Sep 17 00:00:00 2001 From: Ann Priestman Date: Wed, 10 Jan 2018 09:31:58 -0500 Subject: [PATCH 08/12] Formatting --- .../CreateLiveTriageDriveAction.java | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/livetriage/CreateLiveTriageDriveAction.java b/Core/src/org/sleuthkit/autopsy/livetriage/CreateLiveTriageDriveAction.java index 1c54ee633b..846632a092 100644 --- a/Core/src/org/sleuthkit/autopsy/livetriage/CreateLiveTriageDriveAction.java +++ b/Core/src/org/sleuthkit/autopsy/livetriage/CreateLiveTriageDriveAction.java @@ -106,40 +106,40 @@ public final class CreateLiveTriageDriveAction extends CallableSystemAction impl if (drivePath.startsWith("\\\\.\\")) { drivePath = drivePath.substring(4); } - + worker = new CopyFilesWorker(applicationBasePath, drivePath, appName); worker.addPropertyChangeListener(this); worker.execute(); } } - + @NbBundle.Messages({"# {0} - drivePath", - "CreateLiveTriageDriveAction.progressBar.text=Copying live triage files to {0}", - "CreateLiveTriageDriveAction.progressBar.title=Please wait"}) + "CreateLiveTriageDriveAction.progressBar.text=Copying live triage files to {0}", + "CreateLiveTriageDriveAction.progressBar.title=Please wait"}) @Override public void propertyChange(PropertyChangeEvent evt) { - + if ("state".equals(evt.getPropertyName()) - && (SwingWorker.StateValue.STARTED.equals(evt.getNewValue()))) { - + && (SwingWorker.StateValue.STARTED.equals(evt.getNewValue()))) { + // Setup progress bar. String displayStr = NbBundle.getMessage(this.getClass(), "CreateLiveTriageDriveAction.progressBar.text", drivePath); - + progressIndicator = new ModalDialogProgressIndicator(WindowManager.getDefault().getMainWindow(), NbBundle.getMessage(this.getClass(), "CreateLiveTriageDriveAction.progressBar.title")); progressIndicator.start(displayStr); - + } else if ("state".equals(evt.getPropertyName()) - && (SwingWorker.StateValue.DONE.equals(evt.getNewValue()))) { - if(progressIndicator != null){ + && (SwingWorker.StateValue.DONE.equals(evt.getNewValue()))) { + if (progressIndicator != null) { progressIndicator.finish(); } - - if(worker.hadError()){ + + if (worker.hadError()) { MessageNotifyUtil.Message.error(NbBundle.getMessage(CopyFilesWorker.class, "CopyFilesWorker.error.text")); } else { - MessageNotifyUtil.Message.info(NbBundle.getMessage(CopyFilesWorker.class, "CopyFilesWorker.done.text")); + MessageNotifyUtil.Message.info(NbBundle.getMessage(CopyFilesWorker.class, "CopyFilesWorker.done.text")); } } } @@ -156,11 +156,11 @@ public final class CreateLiveTriageDriveAction extends CallableSystemAction impl this.drivePath = drivePath; this.appName = appName; } - - boolean hadError(){ + + boolean hadError() { return error; } - + @Override protected Void doInBackground() throws Exception { From 30793eb51f962cd5a48e2d2535c8611e848b636f Mon Sep 17 00:00:00 2001 From: Raman Date: Thu, 11 Jan 2018 09:53:07 -0500 Subject: [PATCH 09/12] 3238: Add a SQLite content viewer: List tables - Also renamed the ContentViewer & ApplicationContentViewer based on review comments. --- .../autopsy/contentviewers/Bundle.properties | 2 + ...ContentViewer.java => FileTypeViewer.java} | 2 +- ...tionContentViewer.form => FileViewer.form} | 0 ...tionContentViewer.java => FileViewer.java} | 33 +- .../contentviewers/JPEGViewerDummy.java | 2 +- .../autopsy/contentviewers/SQLiteViewer.form | 153 +++++++ .../autopsy/contentviewers/SQLiteViewer.java | 389 ++++++++++++++++++ 7 files changed, 561 insertions(+), 20 deletions(-) rename Core/src/org/sleuthkit/autopsy/contentviewers/{ContentViewer.java => FileTypeViewer.java} (97%) rename Core/src/org/sleuthkit/autopsy/contentviewers/{ApplicationContentViewer.form => FileViewer.form} (100%) rename Core/src/org/sleuthkit/autopsy/contentviewers/{ApplicationContentViewer.java => FileViewer.java} (87%) create mode 100644 Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.form create mode 100644 Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties index cf082b2bab..8267587395 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties @@ -35,3 +35,5 @@ MessageContentViewer.attachmentsPanel.TabConstraints.tabTitle=Attachments MessageContentViewer.viewInNewWindowButton.text=View in New Window JPEGViewerDummy.jLabel1.text=You are looking at a JPEG file: JPEGViewerDummy.jTextField1.text=jTextField1 +SQLiteViewer.jLabel1.text=Table +SQLiteViewer.numEntriesField.text=num Entries diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/ContentViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/FileTypeViewer.java similarity index 97% rename from Core/src/org/sleuthkit/autopsy/contentviewers/ContentViewer.java rename to Core/src/org/sleuthkit/autopsy/contentviewers/FileTypeViewer.java index 98dbbd0b08..f4a677c4f4 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/ContentViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/FileTypeViewer.java @@ -26,7 +26,7 @@ import org.sleuthkit.datamodel.AbstractFile; * Defines an interface for application specific content viewer * */ -interface ContentViewer { +interface FileTypeViewer { /** * Returns list of MIME types supported by this viewer diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/ApplicationContentViewer.form b/Core/src/org/sleuthkit/autopsy/contentviewers/FileViewer.form similarity index 100% rename from Core/src/org/sleuthkit/autopsy/contentviewers/ApplicationContentViewer.form rename to Core/src/org/sleuthkit/autopsy/contentviewers/FileViewer.form diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/ApplicationContentViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/FileViewer.java similarity index 87% rename from Core/src/org/sleuthkit/autopsy/contentviewers/ApplicationContentViewer.java rename to Core/src/org/sleuthkit/autopsy/contentviewers/FileViewer.java index bf7c13279a..a58dea2d25 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/ApplicationContentViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/FileViewer.java @@ -24,42 +24,40 @@ import java.util.HashMap; import java.util.Map; import java.util.logging.Level; import org.openide.nodes.Node; -import org.openide.util.Exceptions; import org.openide.util.NbBundle; import org.openide.util.lookup.ServiceProvider; -import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector; import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.TskCoreException; /** * Generic Application content viewer */ @ServiceProvider(service = DataContentViewer.class, position = 5) -public class ApplicationContentViewer extends javax.swing.JPanel implements DataContentViewer { +public class FileViewer extends javax.swing.JPanel implements DataContentViewer { private static final int CONFIDENCE_LEVEL = 7; private static final long serialVersionUID = 1L; - private static final Logger LOGGER = Logger.getLogger(ApplicationContentViewer.class.getName()); + private static final Logger LOGGER = Logger.getLogger(FileViewer.class.getName()); - private final Map mimeTypeToViewerMap = new HashMap<>(); + private final Map mimeTypeToViewerMap = new HashMap<>(); // TBD: This hardcoded list of viewers should be replaced with a dynamic lookup - private static final ContentViewer[] KNOWN_VIEWERS = new ContentViewer[]{ - new JPEGViewerDummy() // this if for testing only + private static final FileTypeViewer[] KNOWN_VIEWERS = new FileTypeViewer[]{ + new JPEGViewerDummy(), // this if for testing only + new SQLiteViewer() }; - private ContentViewer lastViewer; + private FileTypeViewer lastViewer; /** * Creates new form ApplicationContentViewer */ - public ApplicationContentViewer() { + public FileViewer() { // init the mimetype to viewer map - for (ContentViewer cv : KNOWN_VIEWERS) { + for (FileTypeViewer cv : KNOWN_VIEWERS) { cv.getSupportedMIMETypes().forEach((mimeType) -> { if (mimeTypeToViewerMap.containsKey(mimeType) == false) { mimeTypeToViewerMap.put(mimeType, cv); @@ -75,14 +73,13 @@ public class ApplicationContentViewer extends javax.swing.JPanel implements Data } /** - * Get the ContentViewer for a given mimetype + * Get the FileTypeViewer for a given mimetype * * @param mimeType * - * @return ContentViewer, null if no known content viewer supports the - * mimetype + * @return FileTypeViewer, null if no known content viewer supports the mimetype */ - private ContentViewer getSupportingViewer(String mimeType) { + private FileTypeViewer getSupportingViewer(String mimeType) { return mimeTypeToViewerMap.get(mimeType); } @@ -132,7 +129,7 @@ public class ApplicationContentViewer extends javax.swing.JPanel implements Data return; } else { - ContentViewer viewer = getSupportingViewer(mimeType); + FileTypeViewer viewer = getSupportingViewer(mimeType); if (viewer != null) { lastViewer = viewer; @@ -146,7 +143,7 @@ public class ApplicationContentViewer extends javax.swing.JPanel implements Data } @Override - @NbBundle.Messages("ApplicationContentViewer.title=Application Content Viewer") + @NbBundle.Messages("ApplicationContentViewer.title=Application") public String getTitle() { return Bundle.ApplicationContentViewer_title(); } @@ -159,7 +156,7 @@ public class ApplicationContentViewer extends javax.swing.JPanel implements Data @Override public DataContentViewer createInstance() { - return new ApplicationContentViewer(); + return new FileViewer(); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/JPEGViewerDummy.java b/Core/src/org/sleuthkit/autopsy/contentviewers/JPEGViewerDummy.java index cae2db12a0..8aea7540e1 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/JPEGViewerDummy.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/JPEGViewerDummy.java @@ -10,7 +10,7 @@ import java.util.Arrays; import java.util.List; import org.sleuthkit.datamodel.AbstractFile; -public class JPEGViewerDummy extends javax.swing.JPanel implements ContentViewer { +public class JPEGViewerDummy extends javax.swing.JPanel implements FileTypeViewer { public static final String[] SUPPORTED_MIMETYPES = new String[]{"image/jpeg"}; diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.form b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.form new file mode 100644 index 0000000000..3cdee8658c --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.form @@ -0,0 +1,153 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
+
+
+
+
+
diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java new file mode 100644 index 0000000000..358277fd97 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java @@ -0,0 +1,389 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.sleuthkit.autopsy.contentviewers; + +import java.awt.Component; +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.logging.Level; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.Statement; +import java.sql.SQLException; +import java.util.Map; +import java.util.TreeMap; +import java.util.concurrent.ExecutionException; +import javax.swing.JComboBox; +import javax.swing.SwingWorker; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamOrganization; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.datamodel.ContentUtils; +import org.sleuthkit.datamodel.AbstractFile; + + +public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { + + public static final String[] SUPPORTED_MIMETYPES = new String[]{"application/x-sqlite3"}; + private static final Logger LOGGER = Logger.getLogger(FileViewer.class.getName()); + private Connection connection = null; + + private String tmpDBPathName = null; + private File tmpDBFile = null; + + // TBD: Change the value to be a Array of ColDefs + Map dbTablesMap = new TreeMap<>(); + + /** + * Creates new form SQLiteViewer + */ + public SQLiteViewer() { + initComponents(); + + customizeComponents(); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + jHdrPanel = new javax.swing.JPanel(); + tablesDropdownList = new javax.swing.JComboBox<>(); + jLabel1 = new javax.swing.JLabel(); + numEntriesField = new javax.swing.JTextField(); + jTableDataPanel = new javax.swing.JPanel(); + jScrollPane1 = new javax.swing.JScrollPane(); + jTable1 = new javax.swing.JTable(); + + tablesDropdownList.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" })); + tablesDropdownList.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + tablesDropdownListActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(SQLiteViewer.class, "SQLiteViewer.jLabel1.text")); // NOI18N + + numEntriesField.setEditable(false); + numEntriesField.setText(org.openide.util.NbBundle.getMessage(SQLiteViewer.class, "SQLiteViewer.numEntriesField.text")); // NOI18N + numEntriesField.setBorder(null); + numEntriesField.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + numEntriesFieldActionPerformed(evt); + } + }); + + javax.swing.GroupLayout jHdrPanelLayout = new javax.swing.GroupLayout(jHdrPanel); + jHdrPanel.setLayout(jHdrPanelLayout); + jHdrPanelLayout.setHorizontalGroup( + jHdrPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jHdrPanelLayout.createSequentialGroup() + .addContainerGap() + .addComponent(jLabel1) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(tablesDropdownList, javax.swing.GroupLayout.PREFERRED_SIZE, 130, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(23, 23, 23) + .addComponent(numEntriesField, javax.swing.GroupLayout.PREFERRED_SIZE, 71, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap(130, Short.MAX_VALUE)) + ); + jHdrPanelLayout.setVerticalGroup( + jHdrPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jHdrPanelLayout.createSequentialGroup() + .addContainerGap() + .addGroup(jHdrPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(tablesDropdownList, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(jLabel1) + .addComponent(numEntriesField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addContainerGap(16, Short.MAX_VALUE)) + ); + + jTable1.setModel(new javax.swing.table.DefaultTableModel( + new Object [][] { + {null, null, null, null}, + {null, null, null, null}, + {null, null, null, null}, + {null, null, null, null} + }, + new String [] { + "Title 1", "Title 2", "Title 3", "Title 4" + } + )); + jScrollPane1.setViewportView(jTable1); + + javax.swing.GroupLayout jTableDataPanelLayout = new javax.swing.GroupLayout(jTableDataPanel); + jTableDataPanel.setLayout(jTableDataPanelLayout); + jTableDataPanelLayout.setHorizontalGroup( + jTableDataPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jTableDataPanelLayout.createSequentialGroup() + .addContainerGap() + .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE) + .addGap(15, 15, 15)) + ); + jTableDataPanelLayout.setVerticalGroup( + jTableDataPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jTableDataPanelLayout.createSequentialGroup() + .addContainerGap() + .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 275, Short.MAX_VALUE) + .addContainerGap()) + ); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jHdrPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(jTableDataPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(jHdrPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(jTableDataPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + }// //GEN-END:initComponents + + private void numEntriesFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_numEntriesFieldActionPerformed + // TODO add your handling code here: + }//GEN-LAST:event_numEntriesFieldActionPerformed + + private void tablesDropdownListActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_tablesDropdownListActionPerformed + JComboBox cb = (JComboBox) evt.getSource(); + String tableName = (String) cb.getSelectedItem(); + if (null == tableName) { + return; + } + + readTable(tableName); + }//GEN-LAST:event_tablesDropdownListActionPerformed + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JPanel jHdrPanel; + private javax.swing.JLabel jLabel1; + private javax.swing.JScrollPane jScrollPane1; + private javax.swing.JTable jTable1; + private javax.swing.JPanel jTableDataPanel; + private javax.swing.JTextField numEntriesField; + private javax.swing.JComboBox tablesDropdownList; + // End of variables declaration//GEN-END:variables + + @Override + public List getSupportedMIMETypes() { + return Arrays.asList(SUPPORTED_MIMETYPES); + } + + @Override + public void setFile(AbstractFile file) { + processSQLiteFile( file); + } + + @Override + public Component getComponent() { + return this; + } + + @Override + public void resetComponent() { + + dbTablesMap.clear(); + + tablesDropdownList.setEnabled(true); + tablesDropdownList.removeAllItems(); + numEntriesField.setText(""); + + // close DB connection to file + if (null != connection) { + try { + connection.close(); + connection = null; + } catch (SQLException ex) { + LOGGER.log(Level.SEVERE, "Failed to close DB connection to file.", ex); //NON-NLS + } + } + + // delete last temp file + if (null != tmpDBFile) { + tmpDBFile.delete(); + tmpDBFile = null; + } + } + + private void customizeComponents() { + + // add a actionListener to jTablesComboBox + } + + /** + * Process the given SQLite DB file + * + * @param sqliteFile - + * + * @return none + */ + private void processSQLiteFile(AbstractFile sqliteFile) { + + tablesDropdownList.removeAllItems(); + + new SwingWorker() { + @Override + protected Boolean doInBackground() throws Exception { + + try { + tmpDBPathName = Case.getCurrentCase().getTempDirectory() + File.separator + sqliteFile.getName() + "-" + sqliteFile.getId(); + tmpDBFile = new File(tmpDBPathName); + + // Copy the file to temp folder + ContentUtils.writeToFile(sqliteFile, tmpDBFile); + System.out.println("RAMAN: SQLite file copied to: " + tmpDBPathName); + + // Open copy using JDBC + Class.forName("org.sqlite.JDBC"); //NON-NLS //load JDBC driver + connection = DriverManager.getConnection("jdbc:sqlite:" + tmpDBPathName); //NON-NLS + + // Read all table names and schema + return getTables(); + } catch (IOException ex) { + LOGGER.log(Level.SEVERE, "Failed to copy DB file.", ex); //NON-NLS + } catch (SQLException ex) { + LOGGER.log(Level.SEVERE, "Failed to Open DB.", ex); //NON-NLS + } catch (ClassNotFoundException ex) { + LOGGER.log(Level.SEVERE, "Failed to initialize JDBC Sqlite.", ex); //NON-NLS + } + return false; + } + + @Override + protected void done() { + super.done(); + try { + boolean status = get(); + if ( (status == true) && (dbTablesMap.size() > 0) ) { + dbTablesMap.keySet().forEach((tableName) -> { + tablesDropdownList.addItem(tableName); + }); + } + else { + // Populate error message + tablesDropdownList.addItem("No tables found"); + tablesDropdownList.setEnabled(false); + } + } catch (InterruptedException | ExecutionException ex) { + LOGGER.log(Level.SEVERE, "Unexpected exception while opening DB file", ex); //NON-NLS + } + } + }.execute(); + + } + + /** + * Gets the table names and their schema from loaded SQLite db file + * + * @return true if success, false otherwise + */ + private boolean getTables() { + + try { + Statement statement = connection.createStatement(); + + ResultSet resultSet = statement.executeQuery( + "SELECT name, sql FROM sqlite_master " + + " WHERE type= 'table' " + + " ORDER BY name;"); //NON-NLS + + while (resultSet.next()) { + String tableName = resultSet.getString("name"); //NON-NLS + String tableSQL = resultSet.getString("sql"); //NON-NLS + + dbTablesMap.put(tableName, tableSQL); + String query = "PRAGMA table_info(" + tableName + ")"; //NON-NLS + ResultSet rs2; + try { + Statement statement2 = connection.createStatement(); + rs2 = statement2.executeQuery(query); + while (rs2.next()) { + + System.out.println("RAMAN: Col Name = " + rs2.getString("name") ); + System.out.println("RAMAN: Col Type = " + rs2.getString("type") ); + + // RAMAN TBD: parse and save the table schema + } + } catch (Exception ex) { + LOGGER.log(Level.WARNING, "Error while trying to get columns from sqlite db." + connection, ex); //NON-NLS + } + } + } catch (Exception e) { + LOGGER.log(Level.SEVERE, "Error getting table names from the DB", e); //NON-NLS + } + return true; + } + + private void readTable(String tableName) { + + System.out.println("RAMAN: selected table = " + tableName); + + // TBD: need to handle cancelling if one is already in progress + new SwingWorker() { + @Override + protected Integer doInBackground() throws Exception { + + try { + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery( + "SELECT COUNT(*) as count FROM " + tableName ); //NON-NLS + + System.out.println("Row count = " + resultSet.getInt("count") ); + // Read all table names and schema + return resultSet.getInt("count"); + }catch (SQLException ex) { + LOGGER.log(Level.SEVERE, "Failed to Open DB.", ex); //NON-NLS + } + //NON-NLS + return 0; + } + + @Override + protected void done() { + super.done(); + try { + int numRows = get(); + numEntriesField.setText( numRows + " entries"); + } catch (InterruptedException | ExecutionException ex) { + LOGGER.log(Level.SEVERE, "Unexpected exception while reading table.", ex); //NON-NLS + } + } + }.execute(); + + } + + enum SQLStorageClass { + NULL, + INTEGER, + REAL, + TEXT, + BLOB + }; + + private class SQLColDef { + + private final String colName; + private final SQLStorageClass storageClass; + + SQLColDef(String colName, SQLStorageClass sc ) { + this.colName = colName; + this.storageClass = sc; + } + + } +} From 1f44f08783cc7891ea5c692563371ccfc7efde03 Mon Sep 17 00:00:00 2001 From: Raman Date: Thu, 11 Jan 2018 10:17:33 -0500 Subject: [PATCH 10/12] Formatted file, & minor cleanup --- .../autopsy/contentviewers/FileViewer.java | 2 +- .../autopsy/contentviewers/SQLiteViewer.java | 164 +++++++++--------- 2 files changed, 86 insertions(+), 80 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/FileViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/FileViewer.java index a58dea2d25..0d17ce2cd6 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/FileViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/FileViewer.java @@ -45,7 +45,7 @@ public class FileViewer extends javax.swing.JPanel implements DataContentViewer // TBD: This hardcoded list of viewers should be replaced with a dynamic lookup private static final FileTypeViewer[] KNOWN_VIEWERS = new FileTypeViewer[]{ - new JPEGViewerDummy(), // this if for testing only + // new JPEGViewerDummy(), // this if for testing only new SQLiteViewer() }; diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java index 358277fd97..fff2f5d781 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java @@ -1,7 +1,20 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.sleuthkit.autopsy.contentviewers; @@ -22,30 +35,28 @@ import java.util.concurrent.ExecutionException; import javax.swing.JComboBox; import javax.swing.SwingWorker; import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.centralrepository.datamodel.EamOrganization; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.ContentUtils; import org.sleuthkit.datamodel.AbstractFile; - public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { public static final String[] SUPPORTED_MIMETYPES = new String[]{"application/x-sqlite3"}; private static final Logger LOGGER = Logger.getLogger(FileViewer.class.getName()); private Connection connection = null; - + private String tmpDBPathName = null; private File tmpDBFile = null; - + // TBD: Change the value to be a Array of ColDefs Map dbTablesMap = new TreeMap<>(); - + /** * Creates new form SQLiteViewer */ public SQLiteViewer() { initComponents(); - + customizeComponents(); } @@ -164,7 +175,7 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { if (null == tableName) { return; } - + readTable(tableName); }//GEN-LAST:event_tablesDropdownListActionPerformed @@ -181,12 +192,12 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { @Override public List getSupportedMIMETypes() { - return Arrays.asList(SUPPORTED_MIMETYPES); + return Arrays.asList(SUPPORTED_MIMETYPES); } @Override public void setFile(AbstractFile file) { - processSQLiteFile( file); + processSQLiteFile(file); } @Override @@ -196,13 +207,13 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { @Override public void resetComponent() { - + dbTablesMap.clear(); - + tablesDropdownList.setEnabled(true); tablesDropdownList.removeAllItems(); numEntriesField.setText(""); - + // close DB connection to file if (null != connection) { try { @@ -212,23 +223,23 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { LOGGER.log(Level.SEVERE, "Failed to close DB connection to file.", ex); //NON-NLS } } - + // delete last temp file if (null != tmpDBFile) { tmpDBFile.delete(); tmpDBFile = null; } } - + private void customizeComponents() { - + // add a actionListener to jTablesComboBox } - + /** * Process the given SQLite DB file * - * @param sqliteFile - + * @param sqliteFile - * * @return none */ @@ -239,15 +250,13 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { new SwingWorker() { @Override protected Boolean doInBackground() throws Exception { - + try { + // Copy the file to temp folder tmpDBPathName = Case.getCurrentCase().getTempDirectory() + File.separator + sqliteFile.getName() + "-" + sqliteFile.getId(); tmpDBFile = new File(tmpDBPathName); - - // Copy the file to temp folder ContentUtils.writeToFile(sqliteFile, tmpDBFile); - System.out.println("RAMAN: SQLite file copied to: " + tmpDBPathName); - + // Open copy using JDBC Class.forName("org.sqlite.JDBC"); //NON-NLS //load JDBC driver connection = DriverManager.getConnection("jdbc:sqlite:" + tmpDBPathName); //NON-NLS @@ -266,18 +275,17 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { @Override protected void done() { - super.done(); + super.done(); try { boolean status = get(); - if ( (status == true) && (dbTablesMap.size() > 0) ) { + if ((status == true) && (dbTablesMap.size() > 0)) { dbTablesMap.keySet().forEach((tableName) -> { tablesDropdownList.addItem(tableName); }); - } - else { + } else { // Populate error message - tablesDropdownList.addItem("No tables found"); - tablesDropdownList.setEnabled(false); + tablesDropdownList.addItem("No tables found"); + tablesDropdownList.setEnabled(false); } } catch (InterruptedException | ExecutionException ex) { LOGGER.log(Level.SEVERE, "Unexpected exception while opening DB file", ex); //NON-NLS @@ -286,68 +294,66 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { }.execute(); } - + /** * Gets the table names and their schema from loaded SQLite db file * * @return true if success, false otherwise */ private boolean getTables() { - + try { Statement statement = connection.createStatement(); - - ResultSet resultSet = statement.executeQuery( - "SELECT name, sql FROM sqlite_master " - + " WHERE type= 'table' " - + " ORDER BY name;"); //NON-NLS - while (resultSet.next()) { - String tableName = resultSet.getString("name"); //NON-NLS - String tableSQL = resultSet.getString("sql"); //NON-NLS - - dbTablesMap.put(tableName, tableSQL); - String query = "PRAGMA table_info(" + tableName + ")"; //NON-NLS - ResultSet rs2; - try { - Statement statement2 = connection.createStatement(); - rs2 = statement2.executeQuery(query); - while (rs2.next()) { - - System.out.println("RAMAN: Col Name = " + rs2.getString("name") ); - System.out.println("RAMAN: Col Type = " + rs2.getString("type") ); - - // RAMAN TBD: parse and save the table schema - } - } catch (Exception ex) { - LOGGER.log(Level.WARNING, "Error while trying to get columns from sqlite db." + connection, ex); //NON-NLS + ResultSet resultSet = statement.executeQuery( + "SELECT name, sql FROM sqlite_master " + + " WHERE type= 'table' " + + " ORDER BY name;"); //NON-NLS + + while (resultSet.next()) { + String tableName = resultSet.getString("name"); //NON-NLS + String tableSQL = resultSet.getString("sql"); //NON-NLS + + dbTablesMap.put(tableName, tableSQL); + String query = "PRAGMA table_info(" + tableName + ")"; //NON-NLS + ResultSet rs2; + try { + Statement statement2 = connection.createStatement(); + rs2 = statement2.executeQuery(query); + while (rs2.next()) { + + // System.out.println("RAMAN: Col Name = " + rs2.getString("name")); + // System.out.println("RAMAN: Col Type = " + rs2.getString("type")); + + // RAMAN TBD: parse and save the table schema } + } catch (Exception ex) { + LOGGER.log(Level.WARNING, "Error while trying to get columns from sqlite db." + connection, ex); //NON-NLS } - } catch (Exception e) { - LOGGER.log(Level.SEVERE, "Error getting table names from the DB", e); //NON-NLS } + } catch (Exception e) { + LOGGER.log(Level.SEVERE, "Error getting table names from the DB", e); //NON-NLS + } return true; } - + private void readTable(String tableName) { - - System.out.println("RAMAN: selected table = " + tableName); - // TBD: need to handle cancelling if one is already in progress + new SwingWorker() { @Override protected Integer doInBackground() throws Exception { - + try { Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery( - "SELECT COUNT(*) as count FROM " + tableName ); //NON-NLS + "SELECT COUNT(*) as count FROM " + tableName); //NON-NLS - System.out.println("Row count = " + resultSet.getInt("count") ); - // Read all table names and schema + // TBD: read the rows here and popluate the ExplorerManager. + return resultSet.getInt("count"); - }catch (SQLException ex) { - LOGGER.log(Level.SEVERE, "Failed to Open DB.", ex); //NON-NLS + } catch (SQLException ex) { + LOGGER.log(Level.SEVERE, "Failed to get data for table.", ex); //NON-NLS } //NON-NLS return 0; @@ -355,35 +361,35 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { @Override protected void done() { - super.done(); + super.done(); try { int numRows = get(); - numEntriesField.setText( numRows + " entries"); + numEntriesField.setText(numRows + " entries"); } catch (InterruptedException | ExecutionException ex) { LOGGER.log(Level.SEVERE, "Unexpected exception while reading table.", ex); //NON-NLS } } }.execute(); - + } - + enum SQLStorageClass { NULL, - INTEGER, + INTEGER, REAL, TEXT, BLOB }; - + private class SQLColDef { - + private final String colName; private final SQLStorageClass storageClass; - - SQLColDef(String colName, SQLStorageClass sc ) { + + SQLColDef(String colName, SQLStorageClass sc) { this.colName = colName; this.storageClass = sc; } - + } } From d865b8ac7f23d446d91dae5ab4e91e027723bd62 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\zhaohui" Date: Tue, 16 Jan 2018 16:10:28 -0500 Subject: [PATCH 11/12] 3449: Use local variable instead of override a base format directory string. --- .../autopsy/report/ReportGenerator.java | 39 +++++++++---------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportGenerator.java b/Core/src/org/sleuthkit/autopsy/report/ReportGenerator.java index 1bb05652d4..ef20238c48 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ReportGenerator.java +++ b/Core/src/org/sleuthkit/autopsy/report/ReportGenerator.java @@ -63,7 +63,7 @@ class ReportGenerator { */ private ReportProgressPanel progressPanel; - private String reportPathFormatString; + private static final String REPORT_PATH_FMT_STR = "%s" + File.separator + "%s %s %s" + File.separator; private final ReportGenerationPanel reportGenerationPanel = new ReportGenerationPanel(); static final String REPORTS_DIR = "Reports"; //NON-NLS @@ -89,12 +89,6 @@ class ReportGenerator { * Creates a report generator. */ ReportGenerator() { - // Create the root reports directory path of the form: /Reports/ / - DateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy-HH-mm-ss"); - Date date = new Date(); - String dateNoTime = dateFormat.format(date); - this.reportPathFormatString = currentCase.getReportDirectory() + File.separator + currentCase.getDisplayName() + " %s " + dateNoTime + File.separator; - this.errorList = new ArrayList<>(); } @@ -138,10 +132,10 @@ class ReportGenerator { */ void generateGeneralReport(GeneralReportModule generalReportModule) throws IOException { if (generalReportModule != null) { - reportPathFormatString = createReportDirectory(generalReportModule, reportPathFormatString); - setupProgressPanel(generalReportModule); + String reportDir = createReportDirectory(generalReportModule); + setupProgressPanel(generalReportModule, reportDir); ReportWorker worker = new ReportWorker(() -> { - generalReportModule.generateReport(reportPathFormatString, progressPanel); + generalReportModule.generateReport(reportDir, progressPanel); }); worker.execute(); displayProgressPanel(); @@ -158,10 +152,10 @@ class ReportGenerator { */ void generateTableReport(TableReportModule tableReport, Map artifactTypeSelections, Map tagNameSelections) throws IOException { if (tableReport != null && null != artifactTypeSelections) { - reportPathFormatString = createReportDirectory(tableReport, reportPathFormatString); - setupProgressPanel(tableReport); + String reportDir = createReportDirectory(tableReport); + setupProgressPanel(tableReport, reportDir); ReportWorker worker = new ReportWorker(() -> { - tableReport.startReport(reportPathFormatString); + tableReport.startReport(reportDir); TableReportGenerator generator = new TableReportGenerator(artifactTypeSelections, tagNameSelections, progressPanel, tableReport); generator.execute(); tableReport.endReport(); @@ -182,14 +176,14 @@ class ReportGenerator { */ void generateFileListReport(FileReportModule fileReportModule, Map enabledInfo) throws IOException { if (fileReportModule != null && null != enabledInfo) { - reportPathFormatString = createReportDirectory(fileReportModule, reportPathFormatString); + String reportDir = createReportDirectory(fileReportModule); List enabled = new ArrayList<>(); for (Entry e : enabledInfo.entrySet()) { if (e.getValue()) { enabled.add(e.getKey()); } } - setupProgressPanel(fileReportModule); + setupProgressPanel(fileReportModule, reportDir); ReportWorker worker = new ReportWorker(() -> { if (progressPanel.getStatus() != ReportStatus.CANCELED) { progressPanel.start(); @@ -200,7 +194,7 @@ class ReportGenerator { List files = getFiles(); int numFiles = files.size(); if (progressPanel.getStatus() != ReportStatus.CANCELED) { - fileReportModule.startReport(reportPathFormatString); + fileReportModule.startReport(reportDir); fileReportModule.startTable(enabled); } progressPanel.setIndeterminate(false); @@ -255,17 +249,22 @@ class ReportGenerator { } } - private void setupProgressPanel(ReportModule module) { + private void setupProgressPanel(ReportModule module, String reportDir) { String reportFilePath = module.getRelativeFilePath(); if (!reportFilePath.isEmpty()) { - this.progressPanel = reportGenerationPanel.addReport(module.getName(), String.format(reportPathFormatString, module.getName()) + reportFilePath); + this.progressPanel = reportGenerationPanel.addReport(module.getName(), reportDir + reportFilePath); } else { this.progressPanel = reportGenerationPanel.addReport(module.getName(), null); } } - private static String createReportDirectory(ReportModule module, String pathFormat) throws IOException { - String reportPath = String.format(pathFormat, module.getName()); + private static String createReportDirectory(ReportModule module) throws IOException { + Case currentCase = Case.getCurrentCase(); + // Create the root reports directory path of the form: /Reports/ / + DateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy-HH-mm-ss"); + Date date = new Date(); + String dateNoTime = dateFormat.format(date); + String reportPath = String.format(REPORT_PATH_FMT_STR, currentCase.getReportDirectory(), currentCase.getDisplayName(), module.getName(), dateNoTime); // Create the root reports directory. try { FileUtil.createFolder(new File(reportPath)); From bc8642dbdb509221aac6fb355a9b6e58303d8582 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Tue, 16 Jan 2018 07:14:06 -0500 Subject: [PATCH 12/12] Merge pull request #3362 from APriestman/3165_liveTriageButton 3165 Added menu item to create live triage drive --- .../autopsy/livetriage/Bundle.properties | 7 + .../CreateLiveTriageDriveAction.java | 284 +++++++++++++ .../autopsy/livetriage/SelectDriveDialog.form | 163 ++++++++ .../autopsy/livetriage/SelectDriveDialog.java | 388 ++++++++++++++++++ 4 files changed, 842 insertions(+) create mode 100644 Core/src/org/sleuthkit/autopsy/livetriage/Bundle.properties create mode 100644 Core/src/org/sleuthkit/autopsy/livetriage/CreateLiveTriageDriveAction.java create mode 100644 Core/src/org/sleuthkit/autopsy/livetriage/SelectDriveDialog.form create mode 100644 Core/src/org/sleuthkit/autopsy/livetriage/SelectDriveDialog.java diff --git a/Core/src/org/sleuthkit/autopsy/livetriage/Bundle.properties b/Core/src/org/sleuthkit/autopsy/livetriage/Bundle.properties new file mode 100644 index 0000000000..61f522a6b4 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/livetriage/Bundle.properties @@ -0,0 +1,7 @@ +SelectDriveDialog.bnOk.text=Ok +SelectDriveDialog.bnRefresh.text=Refresh +SelectDriveDialog.lbSelectDrive.text=Select the drive to copy the application and script to: +SelectDriveDialog.jLabel1.text=Select drive to use for live triage (may take time to load): +SelectDriveDialog.errorLabel.text=jLabel2 +SelectDriveDialog.bnCancel.text=Cancel +SelectDriveDialog.jTextArea1.text=This feature copies the application and a batch file to a removable drive,\nallowing systems to be analyzed without installing the software or\nimaging the drives.\n\nTo analyze a system, insert the drive and run "RunFromUSB.bat" as\nadministrator, then select the "Local Disk" option on the Add Data Source\npanel. diff --git a/Core/src/org/sleuthkit/autopsy/livetriage/CreateLiveTriageDriveAction.java b/Core/src/org/sleuthkit/autopsy/livetriage/CreateLiveTriageDriveAction.java new file mode 100644 index 0000000000..846632a092 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/livetriage/CreateLiveTriageDriveAction.java @@ -0,0 +1,284 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.livetriage; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.InvalidPathException; +import java.util.logging.Level; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeEvent; +import javax.swing.JOptionPane; +import java.awt.Frame; +import javax.swing.SwingWorker; +import org.apache.commons.io.FileUtils; +import org.openide.awt.ActionID; +import org.openide.awt.ActionReference; +import org.openide.awt.ActionRegistration; +import org.openide.util.HelpCtx; +import org.openide.util.NbBundle; +import org.openide.util.actions.CallableSystemAction; +import org.openide.windows.WindowManager; +import org.sleuthkit.autopsy.core.UserPreferences; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; +import org.sleuthkit.autopsy.coreutils.PlatformUtil; +import org.sleuthkit.autopsy.progress.ModalDialogProgressIndicator; + +@ActionID(category = "Tools", id = "org.sleuthkit.autopsy.livetriage.CreateLiveTriageDriveAction") +@ActionReference(path = "Menu/Tools", position = 1401) +@ActionRegistration(displayName = "#CTL_CreateLiveTriageDriveAction", lazy = false) +@NbBundle.Messages({"CTL_CreateLiveTriageDriveAction=Make Live Triage Drive"}) +public final class CreateLiveTriageDriveAction extends CallableSystemAction implements PropertyChangeListener { + + private static final String DISPLAY_NAME = Bundle.CTL_CreateLiveTriageDriveAction(); + private ModalDialogProgressIndicator progressIndicator = null; + private String drivePath = ""; + private CopyFilesWorker worker; + + @Override + public boolean isEnabled() { + return true; + } + + @NbBundle.Messages({"CreateLiveTriageDriveAction.error.title=Error creating live triage disk", + "CreateLiveTriageDriveAction.exenotfound.message=Executable could not be found", + "CreateLiveTriageDriveAction.batchFileError.message=Error creating batch file", + "CreateLiveTriageDriveAction.appPathError.message=Could not location application directory", + "CreateLiveTriageDriveAction.copyError.message=Could not copy application. Only works on installed version.", + "CreateLiveTriageDriveAction.success.title=Success", + "CreateLiveTriageDriveAction.success.message=Live triage drive created. Use RunFromUSB.bat to run the application" + }) + @Override + @SuppressWarnings("fallthrough") + public void performAction() { + + Frame mainWindow = WindowManager.getDefault().getMainWindow(); + + // If this is an installed version, there should be an 64.exe file in the bin folder + String appName = UserPreferences.getAppName(); + String exeName = appName + "64.exe"; + String installPath = PlatformUtil.getInstallPath(); + + Path exePath = Paths.get(installPath, "bin", exeName); + + if (!exePath.toFile().exists()) { + JOptionPane.showMessageDialog(mainWindow, + Bundle.CreateLiveTriageDriveAction_exenotfound_message(), + Bundle.CreateLiveTriageDriveAction_error_title(), + JOptionPane.ERROR_MESSAGE); + return; + } + + Path applicationBasePath; + try { + applicationBasePath = exePath.getParent().getParent(); + } catch (InvalidPathException ex) { + JOptionPane.showMessageDialog(mainWindow, + Bundle.CreateLiveTriageDriveAction_appPathError_message(), + Bundle.CreateLiveTriageDriveAction_error_title(), + JOptionPane.ERROR_MESSAGE); + return; + } + + SelectDriveDialog driveDialog = new SelectDriveDialog(mainWindow, true); + driveDialog.display(); + + if (!driveDialog.getSelectedDrive().isEmpty()) { + drivePath = driveDialog.getSelectedDrive(); + if (drivePath.startsWith("\\\\.\\")) { + drivePath = drivePath.substring(4); + } + + worker = new CopyFilesWorker(applicationBasePath, drivePath, appName); + worker.addPropertyChangeListener(this); + worker.execute(); + } + } + + @NbBundle.Messages({"# {0} - drivePath", + "CreateLiveTriageDriveAction.progressBar.text=Copying live triage files to {0}", + "CreateLiveTriageDriveAction.progressBar.title=Please wait"}) + @Override + public void propertyChange(PropertyChangeEvent evt) { + + if ("state".equals(evt.getPropertyName()) + && (SwingWorker.StateValue.STARTED.equals(evt.getNewValue()))) { + + // Setup progress bar. + String displayStr = NbBundle.getMessage(this.getClass(), "CreateLiveTriageDriveAction.progressBar.text", + drivePath); + + progressIndicator = new ModalDialogProgressIndicator(WindowManager.getDefault().getMainWindow(), + NbBundle.getMessage(this.getClass(), "CreateLiveTriageDriveAction.progressBar.title")); + progressIndicator.start(displayStr); + + } else if ("state".equals(evt.getPropertyName()) + && (SwingWorker.StateValue.DONE.equals(evt.getNewValue()))) { + if (progressIndicator != null) { + progressIndicator.finish(); + } + + if (worker.hadError()) { + MessageNotifyUtil.Message.error(NbBundle.getMessage(CopyFilesWorker.class, "CopyFilesWorker.error.text")); + } else { + MessageNotifyUtil.Message.info(NbBundle.getMessage(CopyFilesWorker.class, "CopyFilesWorker.done.text")); + } + } + } + + private class CopyFilesWorker extends SwingWorker { + + private final Path sourceFolder; + private final String drivePath; + private final String appName; + private boolean error = false; + + CopyFilesWorker(Path sourceFolder, String drivePath, String appName) { + this.sourceFolder = sourceFolder; + this.drivePath = drivePath; + this.appName = appName; + } + + boolean hadError() { + return error; + } + + @Override + protected Void doInBackground() throws Exception { + + copyBatchFile(drivePath, appName); + copyApplication(sourceFolder, drivePath, appName); + + return null; + } + + @NbBundle.Messages({"CopyFilesWorker.error.text=Error copying live triage files", + "CopyFilesWorker.done.text=Finished creating live triage disk"}) + @Override + protected void done() { + try { + super.get(); + } catch (Exception ex) { + error = true; + Logger.getLogger(CreateLiveTriageDriveAction.class.getName()).log(Level.SEVERE, "Fatal error during live triage drive creation", ex); //NON-NLS + } + } + } + + private void copyApplication(Path sourceFolder, String destBaseFolder, String appName) throws IOException { + + // Create an appName folder in the destination + Path destAppFolder = Paths.get(destBaseFolder, appName); + if (!destAppFolder.toFile().exists()) { + if (!destAppFolder.toFile().mkdirs()) { + throw new IOException("Failed to create directory " + destAppFolder.toString()); + } + } + + // Now copy the files + FileUtils.copyDirectory(sourceFolder.toFile(), destAppFolder.toFile()); + } + + private void copyBatchFile(String destPath, String appName) throws IOException, InvalidPathException { + Path batchFilePath = Paths.get(destPath, "RunFromUSB.bat"); + FileUtils.writeStringToFile(batchFilePath.toFile(), getBatchFileContents(appName), "UTF-8"); + + } + + private String getBatchFileContents(String appName) { + + String batchFile + = "@echo off\n" + + "\n" + + "REM This restores the working directory when using 'Run as administrator'" + + "@setlocal enableextensions\n" + + "@cd /d \"%~dp0\"" + + "\n" + + "SET appName=\"" + appName + "\"\n" + + "\n" + + "REM Create the configData directory. Exit if it does not exist after attempting to create it\n" + + "if not exist configData mkdir configData\n" + + "if not exist configData (\n" + + " echo Error creating directory configData\n" + + " goto end\n" + + ")\n" + + "\n" + + "REM Create the userdir sub directory. Exit if it does not exist after attempting to create it\n" + + "if not exist configData\\userdir mkdir configData\\userdir\n" + + "if not exist configData\\userdir (\n" + + " echo Error creating directory configData\\userdir\n" + + " goto end\n" + + ")\n" + + "\n" + + "REM Create the cachedir sub directory. Exit if it does not exist after attempting to create it\n" + + "REM If it exists to start with, delete it to clear out old data\n" + + "if exist configData\\cachedir rd /s /q configData\\cachedir\n" + + "mkdir configData\\cachedir\n" + + "if not exist configData\\cachedir (\n" + + " echo Error creating directory configData\\cachedir\n" + + " goto end\n" + + ")\n" + + "\n" + + "REM Create the temp sub directory. Exit if it does not exist after attempting to create it\n" + + "REM If it exists to start with, delete it to clear out old data\n" + + "if exist configData\\temp rd /s /q configData\\temp\n" + + "mkdir configData\\temp\n" + + "if not exist configData\\temp (\n" + + " echo Error creating directory configData\\temp\n" + + " goto end\n" + + ")\n" + + "\n" + + "REM Create the cases directory. It's ok if this fails.\n" + + "if not exist cases mkdir cases\n" + + "\n" + + "if exist %appName% (\n" + + " if not exist %appName%\\bin\\%appName%64.exe (\n" + + " echo %appName%\\bin\\%appName%64.exe does not exist\n" + + " goto end\n" + + " )\n" + + " %appName%\\bin\\%appName%64.exe --userdir ..\\configData\\userdir --cachedir ..\\configData\\cachedir -J-Djava.io.tmpdir=..\\configData\\temp\n" + + ") else (\n" + + " echo Could not find %appName% directory\n" + + " goto end\n" + + ")\n" + + "\n" + + ":end\n" + + "\n" + + "REM Keep the cmd window open in case there was an error\n" + + "@pause\n"; + return batchFile; + } + + @Override + public String getName() { + return DISPLAY_NAME; + } + + @Override + public HelpCtx getHelpCtx() { + return HelpCtx.DEFAULT_HELP; + } + + @Override + public boolean asynchronous() { + return false; // run on edt + } +} diff --git a/Core/src/org/sleuthkit/autopsy/livetriage/SelectDriveDialog.form b/Core/src/org/sleuthkit/autopsy/livetriage/SelectDriveDialog.form new file mode 100644 index 0000000000..ee776348dc --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/livetriage/SelectDriveDialog.form @@ -0,0 +1,163 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/livetriage/SelectDriveDialog.java b/Core/src/org/sleuthkit/autopsy/livetriage/SelectDriveDialog.java new file mode 100644 index 0000000000..c13d82d324 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/livetriage/SelectDriveDialog.java @@ -0,0 +1,388 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.sleuthkit.autopsy.livetriage; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CancellationException; +import java.util.logging.Level; +import java.awt.Dimension; +import java.awt.Point; +import javax.swing.SwingWorker; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import javax.swing.event.TableModelListener; +import javax.swing.table.TableModel; +import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.coreutils.LocalDisk; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.PlatformUtil; + +/** + * + */ +class SelectDriveDialog extends javax.swing.JDialog { + + private List disks = new ArrayList<>(); + private final LocalDiskModel model = new LocalDiskModel(); + private final java.awt.Frame parent; + private String drivePath = ""; + + /** + * Creates new form SelectDriveDialog + */ + @NbBundle.Messages({"SelectDriveDialog.title=Create Live Triage Drive"}) + SelectDriveDialog(java.awt.Frame parent, boolean modal) { + super(parent, modal); + initComponents(); + this.parent = parent; + + model.loadDisks(); + bnOk.setEnabled(false); + diskTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { + @Override + public void valueChanged(ListSelectionEvent e) { + if (diskTable.getSelectedRow() >= 0 && diskTable.getSelectedRow() < disks.size()) { + bnOk.setEnabled(true); + } else { //The selection changed to nothing valid being selected, such as with ctrl+click + bnOk.setEnabled(false); + } + } + }); + } + + void display() { + this.setTitle(Bundle.SelectDriveDialog_title()); + + final Dimension parentSize = parent.getSize(); + final Point parentLocationOnScreen = parent.getLocationOnScreen(); + final Dimension childSize = this.getSize(); + int x; + int y; + x = (parentSize.width - childSize.width) / 2; + y = (parentSize.height - childSize.height) / 2; + x += parentLocationOnScreen.x; + y += parentLocationOnScreen.y; + + setLocation(x, y); + setVisible(true); + } + + String getSelectedDrive() { + return this.drivePath; + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + jScrollPane1 = new javax.swing.JScrollPane(); + diskTable = new javax.swing.JTable(); + jLabel1 = new javax.swing.JLabel(); + bnRefresh = new javax.swing.JButton(); + bnOk = new javax.swing.JButton(); + errorLabel = new javax.swing.JLabel(); + jSeparator1 = new javax.swing.JSeparator(); + bnCancel = new javax.swing.JButton(); + jScrollPane2 = new javax.swing.JScrollPane(); + jTextArea1 = new javax.swing.JTextArea(); + + setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + + diskTable.setModel(model); + jScrollPane1.setViewportView(diskTable); + + org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(SelectDriveDialog.class, "SelectDriveDialog.jLabel1.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(bnRefresh, org.openide.util.NbBundle.getMessage(SelectDriveDialog.class, "SelectDriveDialog.bnRefresh.text")); // NOI18N + bnRefresh.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + bnRefreshActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(bnOk, org.openide.util.NbBundle.getMessage(SelectDriveDialog.class, "SelectDriveDialog.bnOk.text")); // NOI18N + bnOk.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + bnOkActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(errorLabel, org.openide.util.NbBundle.getMessage(SelectDriveDialog.class, "SelectDriveDialog.errorLabel.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(bnCancel, org.openide.util.NbBundle.getMessage(SelectDriveDialog.class, "SelectDriveDialog.bnCancel.text")); // NOI18N + bnCancel.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + bnCancelActionPerformed(evt); + } + }); + + jScrollPane2.setBorder(null); + + jTextArea1.setBackground(new java.awt.Color(240, 240, 240)); + jTextArea1.setColumns(20); + jTextArea1.setFont(new java.awt.Font("Tahoma", 0, 11)); // NOI18N + jTextArea1.setRows(5); + jTextArea1.setText(org.openide.util.NbBundle.getMessage(SelectDriveDialog.class, "SelectDriveDialog.jTextArea1.text")); // NOI18N + jTextArea1.setBorder(null); + jScrollPane2.setViewportView(jTextArea1); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addGroup(layout.createSequentialGroup() + .addComponent(bnRefresh, javax.swing.GroupLayout.DEFAULT_SIZE, 112, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 48, Short.MAX_VALUE) + .addComponent(bnOk, javax.swing.GroupLayout.PREFERRED_SIZE, 101, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(bnCancel, javax.swing.GroupLayout.PREFERRED_SIZE, 101, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE) + .addComponent(jLabel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(jSeparator1) + .addComponent(errorLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(jScrollPane2)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, 107, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jLabel1) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 112, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(bnRefresh) + .addComponent(bnCancel) + .addComponent(bnOk)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(errorLabel) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + + pack(); + }// //GEN-END:initComponents + + private void bnRefreshActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnRefreshActionPerformed + model.loadDisks(); + }//GEN-LAST:event_bnRefreshActionPerformed + + private void bnOkActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnOkActionPerformed + if (diskTable.getSelectedRow() >= 0 && diskTable.getSelectedRow() < disks.size()) { + LocalDisk selectedDisk = disks.get(diskTable.getSelectedRow()); + drivePath = selectedDisk.getPath(); + } else { + drivePath = ""; + } + dispose(); + }//GEN-LAST:event_bnOkActionPerformed + + private void bnCancelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnCancelActionPerformed + dispose(); + }//GEN-LAST:event_bnCancelActionPerformed + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton bnCancel; + private javax.swing.JButton bnOk; + private javax.swing.JButton bnRefresh; + private javax.swing.JTable diskTable; + private javax.swing.JLabel errorLabel; + private javax.swing.JLabel jLabel1; + private javax.swing.JScrollPane jScrollPane1; + private javax.swing.JScrollPane jScrollPane2; + private javax.swing.JSeparator jSeparator1; + private javax.swing.JTextArea jTextArea1; + // End of variables declaration//GEN-END:variables + + /** + * Table model for displaying information from LocalDisk Objects in a table. + */ + @NbBundle.Messages({"SelectDriveDialog.localDiskModel.loading.msg=", + "SelectDriveDialog.localDiskModel.nodrives.msg=Executable could not be found", + "SelectDriveDialog.diskTable.column1.title=Disk Name", + "SelectDriveDialog.diskTable.column2.title=Disk Size", + "SelectDriveDialog.errLabel.disksNotDetected.text=Disks were not detected. On some systems it requires admin privileges", + "SelectDriveDialog.errLabel.disksNotDetected.toolTipText=Disks were not detected." + + }) + private class LocalDiskModel implements TableModel { + + private LocalDiskThread worker = null; + private boolean ready = false; + private volatile boolean loadingDisks = false; + + //private String SELECT = "Select a local disk:"; + private final String LOADING = NbBundle.getMessage(this.getClass(), "SelectDriveDialog.localDiskModel.loading.msg"); + private final String NO_DRIVES = NbBundle.getMessage(this.getClass(), "SelectDriveDialog.localDiskModel.nodrives.msg"); + + private void loadDisks() { + + // if there is a worker already building the lists, then cancel it first. + if (loadingDisks && worker != null) { + worker.cancel(false); + } + + // Clear the lists + errorLabel.setText(""); + diskTable.setEnabled(false); + ready = false; + loadingDisks = true; + worker = new LocalDiskThread(); + worker.execute(); + } + + @Override + public int getRowCount() { + if (disks.isEmpty()) { + return 0; + } + return disks.size(); + } + + @Override + public int getColumnCount() { + return 2; + + } + + @Override + public String getColumnName(int columnIndex) { + switch (columnIndex) { + case 0: + return NbBundle.getMessage(this.getClass(), "SelectDriveDialog.diskTable.column1.title"); + case 1: + return NbBundle.getMessage(this.getClass(), "SelectDriveDialog.diskTable.column2.title"); + default: + return "Unnamed"; //NON-NLS + } + } + + @Override + public Class getColumnClass(int columnIndex) { + return String.class; + } + + @Override + public boolean isCellEditable(int rowIndex, int columnIndex) { + return false; + } + + @Override + public Object getValueAt(int rowIndex, int columnIndex) { + if (ready) { + if (disks.isEmpty()) { + return NO_DRIVES; + } + switch (columnIndex) { + case 0: + return disks.get(rowIndex).getName(); + case 1: + return disks.get(rowIndex).getReadableSize(); + default: + return disks.get(rowIndex).getPath(); + } + } else { + return LOADING; + } + } + + @Override + public void setValueAt(Object aValue, int rowIndex, int columnIndex) { + //setter does nothing they should not be able to modify table + } + + @Override + public void addTableModelListener(TableModelListener l) { + + } + + @Override + public void removeTableModelListener(TableModelListener l) { + + } + + /** + * Gets the lists of physical drives and partitions and combines them + * into a list of disks. + */ + class LocalDiskThread extends SwingWorker { + + private final Logger logger = Logger.getLogger(LocalDiskThread.class.getName()); + private List partitions = new ArrayList<>(); + + @Override + protected Object doInBackground() throws Exception { + // Populate the lists + partitions = new ArrayList<>(); + partitions = PlatformUtil.getPartitions(); + return null; + } + + /** + * Display any error messages that might of occurred when getting + * the lists of physical drives or partitions. + */ + private void displayErrors() { + if (partitions.isEmpty()) { + if (PlatformUtil.isWindowsOS()) { + errorLabel.setText( + NbBundle.getMessage(this.getClass(), "SelectDriveDialog.errLabel.disksNotDetected.text")); + errorLabel.setToolTipText(NbBundle.getMessage(this.getClass(), + "SelectDriveDialog.errLabel.disksNotDetected.toolTipText")); + } else { + errorLabel.setText( + NbBundle.getMessage(this.getClass(), "SelectDriveDialog.errLabel.drivesNotDetected.text")); + errorLabel.setToolTipText(NbBundle.getMessage(this.getClass(), + "SelectDriveDialog.errLabel.drivesNotDetected.toolTipText")); + } + errorLabel.setVisible(true); + diskTable.setEnabled(false); + } + } + + @Override + protected void done() { + try { + super.get(); //block and get all exceptions thrown while doInBackground() + } catch (CancellationException ex) { + logger.log(Level.INFO, "Loading local disks was canceled."); //NON-NLS + } catch (InterruptedException ex) { + logger.log(Level.INFO, "Loading local disks was interrupted."); //NON-NLS + } catch (Exception ex) { + logger.log(Level.SEVERE, "Fatal error when loading local disks", ex); //NON-NLS + } finally { + if (!this.isCancelled()) { + displayErrors(); + worker = null; + loadingDisks = false; + disks = new ArrayList<>(); + disks.addAll(partitions); + if (disks.size() > 0) { + diskTable.setEnabled(true); + diskTable.clearSelection(); + } + ready = true; + } + } + diskTable.revalidate(); + } + } + } +}