/* * Autopsy Forensic Browser * * Copyright 2013 Basis Technology Corp. * Contact: carrier <at> sleuthkit <dot> 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.report; import java.awt.Dimension; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.File; import java.io.IOException; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ExecutionException; import java.util.logging.Level; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.SwingWorker; import org.openide.filesystems.FileUtil; import org.openide.util.NbBundle; import org.openide.windows.WindowManager; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.report.ReportProgressPanel.ReportStatus; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; class ReportGenerator { private static final Logger logger = Logger.getLogger(ReportGenerator.class.getName()); private Case currentCase = Case.getCurrentCase(); /** * Progress reportGenerationPanel that can be used to check for cancellation. */ private ReportProgressPanel progressPanel; private final String reportPath; private final ReportGenerationPanel reportGenerationPanel = new ReportGenerationPanel(); static final String REPORTS_DIR = "Reports"; //NON-NLS private List<String> errorList; /** * Displays the list of errors during report generation in user-friendly * way. MessageNotifyUtil used to display bubble notification. */ private void displayReportErrors() { if (!errorList.isEmpty()) { String errorString = ""; for (String error : errorList) { errorString += error + "\n"; } MessageNotifyUtil.Notify.error( NbBundle.getMessage(this.getClass(), "ReportGenerator.notifyErr.errsDuringRptGen"), errorString); } } /** * Creates a report generator. */ ReportGenerator() { // Create the root reports directory path of the form: <CASE DIRECTORY>/Reports/<Case fileName> <Timestamp>/ DateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy-HH-mm-ss"); Date date = new Date(); String dateNoTime = dateFormat.format(date); this.reportPath = currentCase.getReportDirectory() + File.separator + currentCase.getName() + " " + dateNoTime + File.separator; this.errorList = new ArrayList<>(); // Create the root reports directory. try { FileUtil.createFolder(new File(this.reportPath)); } 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 } } /** * Display the progress panels to the user, and add actions to close the * parent dialog. */ private void displayProgressPanel() { final JDialog dialog = new JDialog((JFrame) WindowManager.getDefault().getMainWindow(), true); dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE); dialog.setTitle(NbBundle.getMessage(this.getClass(), "ReportGenerator.displayProgress.title.text")); dialog.add(this.reportGenerationPanel); dialog.pack(); reportGenerationPanel.addCloseAction(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { dialog.dispose(); } }); dialog.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { reportGenerationPanel.close(); } }); Dimension screenDimension = Toolkit.getDefaultToolkit().getScreenSize(); int w = dialog.getSize().width; int h = dialog.getSize().height; // set the location of the popUp Window on the center of the screen dialog.setLocation((screenDimension.width - w) / 2, (screenDimension.height - h) / 2); dialog.setVisible(true); } /** * Run the GeneralReportModules using a SwingWorker. */ void generateGeneralReport(GeneralReportModule generalReportModule) { if (generalReportModule != null) { setupProgressPanel(generalReportModule); ReportWorker worker = new ReportWorker(() -> { generalReportModule.generateReport(reportPath, progressPanel); }); worker.execute(); displayProgressPanel(); } } /** * Run the TableReportModules using a SwingWorker. * * @param artifactTypeSelections the enabled/disabled state of the artifact * types to be included in the report * @param tagSelections the enabled/disabled state of the tag names * to be included in the report */ void generateTableReport(TableReportModule tableReport, Map<BlackboardArtifact.Type, Boolean> artifactTypeSelections, Map<String, Boolean> tagNameSelections) { if (tableReport != null && null != artifactTypeSelections) { setupProgressPanel(tableReport); ReportWorker worker = new ReportWorker(() -> { tableReport.startReport(reportPath); TableReportGenerator generator = new TableReportGenerator(artifactTypeSelections, tagNameSelections, progressPanel, tableReport); generator.execute(); tableReport.endReport(); // finish progress, wrap up progressPanel.complete(ReportProgressPanel.ReportStatus.COMPLETE); errorList = generator.getErrorList(); }); worker.execute(); displayProgressPanel(); } } /** * Run the FileReportModules using a SwingWorker. * * @param enabledInfo the Information that should be included about each * file in the report. */ void generateFileListReport(FileReportModule fileReportModule, Map<FileReportDataTypes, Boolean> enabledInfo) { if (fileReportModule != null && null != enabledInfo) { List<FileReportDataTypes> enabled = new ArrayList<>(); for (Entry<FileReportDataTypes, Boolean> e : enabledInfo.entrySet()) { if (e.getValue()) { enabled.add(e.getKey()); } } setupProgressPanel(fileReportModule); ReportWorker worker = new ReportWorker(() -> { if (progressPanel.getStatus() != ReportStatus.CANCELED) { progressPanel.start(); progressPanel.updateStatusLabel( NbBundle.getMessage(this.getClass(), "ReportGenerator.progress.queryingDb.text")); } List<AbstractFile> files = getFiles(); int numFiles = files.size(); if (progressPanel.getStatus() != ReportStatus.CANCELED) { fileReportModule.startReport(reportPath); fileReportModule.startTable(enabled); } progressPanel.setIndeterminate(false); progressPanel.setMaximumProgress(numFiles); int i = 0; // Add files to report. for (AbstractFile file : files) { // Check to see if any reports have been cancelled. if (progressPanel.getStatus() == ReportStatus.CANCELED) { return; } else { fileReportModule.addRow(file, enabled); progressPanel.increment(); } if ((i % 100) == 0) { progressPanel.updateStatusLabel( NbBundle.getMessage(this.getClass(), "ReportGenerator.progress.processingFile.text", file.getName())); } i++; } fileReportModule.endTable(); fileReportModule.endReport(); progressPanel.complete(ReportStatus.COMPLETE); }); worker.execute(); displayProgressPanel(); } } /** * Get all files in the image. * * @return */ private List<AbstractFile> getFiles() { List<AbstractFile> absFiles; try { SleuthkitCase skCase = Case.getCurrentCase().getSleuthkitCase(); absFiles = skCase.findAllFilesWhere("meta_type != " + TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_DIR.getValue()); //NON-NLS return absFiles; } catch (TskCoreException ex) { MessageNotifyUtil.Notify.show( NbBundle.getMessage(this.getClass(), "ReportGenerator.errors.reportErrorTitle"), NbBundle.getMessage(this.getClass(), "ReportGenerator.errors.reportErrorText") + ex.getLocalizedMessage(), MessageNotifyUtil.MessageType.ERROR); logger.log(Level.SEVERE, "failed to generate reports. Unable to get all files in the image.", ex); //NON-NLS return Collections.<AbstractFile>emptyList(); } } private void setupProgressPanel(ReportModule module) { String reportFilePath = module.getRelativeFilePath(); if (!reportFilePath.isEmpty()) { this.progressPanel = reportGenerationPanel.addReport(module.getName(), reportPath + reportFilePath); } else { this.progressPanel = reportGenerationPanel.addReport(module.getName(), null); } } private class ReportWorker extends SwingWorker<Void, Void> { private final Runnable doInBackground; private ReportWorker(Runnable doInBackground) { this.doInBackground = doInBackground; } @Override protected Void doInBackground() throws Exception { doInBackground.run(); return null; } @Override protected void done() { try { get(); } catch (InterruptedException | ExecutionException ex) { MessageNotifyUtil.Notify.show( NbBundle.getMessage(this.getClass(), "ReportGenerator.errors.reportErrorTitle"), NbBundle.getMessage(this.getClass(), "ReportGenerator.errors.reportErrorText") + ex.getLocalizedMessage(), MessageNotifyUtil.MessageType.ERROR); logger.log(Level.SEVERE, "failed to generate reports", ex); //NON-NLS } // catch and ignore if we were cancelled catch (java.util.concurrent.CancellationException ex) { } finally { displayReportErrors(); errorList.clear(); } } } }