From 1be56087bc4cb932fd3950335d04f4989584d17b Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Thu, 5 Jun 2014 15:55:52 -0400 Subject: [PATCH] Complete major functionality for external result imports and reports in Autopsy tree --- .../sleuthkit/autopsy/casemodule/Case.java | 18 +-- .../autopsy/datamodel/Bundle.properties | 6 +- .../sleuthkit/autopsy/datamodel/Reports.java | 76 +++++++++-- .../directorytree/DataResultFilterNode.java | 6 + ...ampleExecutableDataSourceIngestModule.java | 120 ++++++++++++++---- .../examples/SampleFileIngestModule.java | 19 ++- .../externalresults/ExternalResults.java | 30 +++-- .../ExternalResultsImporter.java | 31 ++--- .../ExternalResultsXMLParser.java | 19 +-- .../autopsy/report/ReportBodyFile.java | 16 ++- .../sleuthkit/autopsy/report/ReportExcel.java | 8 +- .../sleuthkit/autopsy/report/ReportHTML.java | 21 +-- .../sleuthkit/autopsy/report/ReportKML.java | 6 +- 13 files changed, 252 insertions(+), 124 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 45d8893cdb..65f2d7df58 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -1166,18 +1166,18 @@ public class Case implements SleuthkitCase.ErrorObserver { public void receiveError(String context, String errorMessage) { MessageNotifyUtil.Notify.error(context, errorMessage); } - - // RJCTODO: Clean this up + /** - * Inserts row into the reports table in the case database. - * @param [in] relPath The path of the report file, relative to the database (case directory in Autopsy). - * @param [in] displayName The display name for the new tag name. + * Adds a report to the case. + * + * @param [in] localPath The path of the report file, must be in the case directory or one of its subdirectories. + * @param [in] sourceModuleName The name of the module that created the report. + * @param [in] reportName The report name, may be empty. * @return A Report data transfer object (DTO) for the new row. * @throws TskCoreException - */ - public void addReport(String relPath, String displayName) throws TskCoreException { - Report report = this.db.addReport(relPath, displayName); - // RJCTODO: Fire event, perhaps with repoprt data + */ + public void addReport(String localPath, String srcModuleName, String reportName) throws TskCoreException { + Report report = this.db.addReport(localPath, srcModuleName, reportName); Case.pcs.firePropertyChange(Events.REPORT_ADDED.toString(), null, report); // RJCTODO: Need exception firewall, maybe thread to do publishing } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties index fd60ba834f..d98fa9147a 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties @@ -209,9 +209,9 @@ RecentFilesNode.createSheet.name.name=Name RecentFilesNode.createSheet.name.displayName=Name RecentFilesNode.createSheet.name.desc=no description RecentFilesNode.name.text=Recent Files -ReportNode.displayNameProperty.name=Display Name -ReportNode.displayNameProperty.displayName=Report Name -ReportNode.displayNameProperty.desc=Name of the report +ReportNode.sourceModuleNameProperty.name=Source Module Name +ReportNode.sourceModuleNameProperty.displayName=Source Module Name +ReportNode.sourceModuleNameProperty.desc=Name of the module that created the report ReportNode.createdTimeProperty.name=Created Time ReportNode.createdTimeProperty.displayName=Created Time ReportNode.createdTimeProperty.desc=Time report was created diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Reports.java b/Core/src/org/sleuthkit/autopsy/datamodel/Reports.java index 0ff2a3ff61..2b3636d7a5 100755 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Reports.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Reports.java @@ -18,11 +18,20 @@ */ package org.sleuthkit.autopsy.datamodel; +import java.awt.Desktop; +import java.awt.event.ActionEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.io.File; +import java.io.IOException; import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.logging.Level; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.JOptionPane; import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; import org.openide.nodes.Node; @@ -37,10 +46,10 @@ import org.sleuthkit.datamodel.TskCoreException; /** * Implements the Reports subtree of the Autopsy tree. */ -public class Reports implements AutopsyVisitableItem { +public final class Reports implements AutopsyVisitableItem { private static final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z"); - + @Override public T accept(AutopsyItemVisitor visitor) { // The CreateAutopsyNodeVisitor constructs a ReportsListNode when it @@ -51,7 +60,7 @@ public class Reports implements AutopsyVisitableItem { /** * The root node of the Reports subtree of the Autopsy tree. */ - public static class ReportsListNode extends DisplayableItemNode { + public static final class ReportsListNode extends DisplayableItemNode { private static final String DISPLAY_NAME = NbBundle.getMessage(ReportsListNode.class, "ReportsListNode.displayName"); private static final String ICON_PATH = "org/sleuthkit/autopsy/images/report_16.png"; //NON-NLS @@ -82,7 +91,7 @@ public class Reports implements AutopsyVisitableItem { * The child node factory that creates ReportNode children for a * ReportsListNode. */ - private static class ReportNodeFactory extends ChildFactory { + private static final class ReportNodeFactory extends ChildFactory { ReportNodeFactory() { Case.addPropertyChangeListener(new PropertyChangeListener() { @@ -116,7 +125,7 @@ public class Reports implements AutopsyVisitableItem { * A leaf node in the Reports subtree of the Autopsy tree, wraps a Report * object. */ - public static class ReportNode extends DisplayableItemNode { + public static final class ReportNode extends DisplayableItemNode { private static final String ICON_PATH = "org/sleuthkit/autopsy/images/report_16.png"; //NON-NLS private final Report report; @@ -124,8 +133,8 @@ public class Reports implements AutopsyVisitableItem { ReportNode(Report report) { super(Children.LEAF, Lookups.fixed(report)); this.report = report; - super.setName(this.report.getDisplayName()); - super.setDisplayName(this.report.getDisplayName()); + super.setName(this.report.getSourceModuleName()); + super.setDisplayName(this.report.getSourceModuleName()); this.setIconBaseWithExtension(ICON_PATH); } @@ -136,8 +145,8 @@ public class Reports implements AutopsyVisitableItem { @Override public T accept(DisplayableItemNodeVisitor visitor) { - // The GetPopupActionsDisplayableItemNodeVisitor gets the Actions for this class. - // The GetPreferredActionsDisplayableItemNodeVisitor RJCTODO + // The GetPopupActionsDisplayableItemNodeVisitor calls getActions(). // RJCTODO: Try to get rid of parent collapse all + // The GetPreferredActionsDisplayableItemNodeVisitor calls getPreferredAction(). // The IsLeafItemVisitor returns true. // The ShowItemVisitor always returns true. return visitor.visit(this); @@ -151,19 +160,58 @@ public class Reports implements AutopsyVisitableItem { propertiesSet = Sheet.createPropertiesSet(); sheet.put(propertiesSet); } - propertiesSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ReportNode.displayNameProperty.name"), - NbBundle.getMessage(this.getClass(), "ReportNode.displayNameProperty.displayName"), - NbBundle.getMessage(this.getClass(), "ReportNode.displayNameProperty.desc"), - this.report.getDisplayName())); + propertiesSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ReportNode.sourceModuleNameProperty.name"), + NbBundle.getMessage(this.getClass(), "ReportNode.sourceModuleNameProperty.displayName"), + NbBundle.getMessage(this.getClass(), "ReportNode.sourceModuleNameProperty.desc"), + this.report.getSourceModuleName())); propertiesSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ReportNode.createdTimeProperty.name"), NbBundle.getMessage(this.getClass(), "ReportNode.createdTimeProperty.displayName"), NbBundle.getMessage(this.getClass(), "ReportNode.createdTimeProperty.desc"), - dateFormatter.format(new java.util.Date(this.report.getCreatedTime() * 1000)).toString())); + dateFormatter.format(new java.util.Date(this.report.getCreatedTime() * 1000)).toString())); propertiesSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ReportNode.pathProperty.name"), NbBundle.getMessage(this.getClass(), "ReportNode.pathProperty.displayName"), NbBundle.getMessage(this.getClass(), "ReportNode.pathProperty.desc"), this.report.getPath())); return sheet; } + + @Override + public Action[] getActions(boolean popup) { + List actions = new ArrayList<>(); + actions.addAll(Arrays.asList(super.getActions(true))); + actions.add(new OpenReportAction()); + return actions.toArray(new Action[actions.size()]); + } + + @Override + public AbstractAction getPreferredAction() { + return new OpenReportAction(); + } + + private final class OpenReportAction extends AbstractAction { // RJCTODO: Needs name for menu + + private OpenReportAction() { + super("Open Report"); // RJCTODO: bundle + } + + @Override + public void actionPerformed(ActionEvent e) { + File file = new File(ReportNode.this.report.getPath()); + try { + Desktop.getDesktop().open(file); + } catch (IOException ex) { + // RJCTODO: Failed to open no associated editor or associated application failed to launch + // RJCTODO: Bundle + JOptionPane.showMessageDialog(null, "There is no associated editor for reports of this type or the associated application failed to launch.", + "Open Report Failure", JOptionPane.ERROR_MESSAGE); + } catch (UnsupportedOperationException ex) { + // RJCTODO + } catch (IllegalArgumentException ex) { + // RJCTODO: File does not exist + } catch (SecurityException ex) { + // RJCTODO: permission denied + } + } + } } } \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java index c564a73502..03ab86978f 100755 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java @@ -66,6 +66,7 @@ import org.sleuthkit.autopsy.datamodel.RecentFilesFilterNode; import org.sleuthkit.autopsy.datamodel.RecentFilesNode; import org.sleuthkit.autopsy.datamodel.FileTypesNode; import org.sleuthkit.autopsy.datamodel.KeywordHits; +import org.sleuthkit.autopsy.datamodel.Reports; import org.sleuthkit.autopsy.datamodel.Tags; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; @@ -522,6 +523,11 @@ public class DataResultFilterNode extends FilterNode { return openChild(khmln); } + @Override + public AbstractAction visit(Reports.ReportNode reportNode) { + return reportNode.getPreferredAction(); + } + @Override protected AbstractAction defaultVisit(DisplayableItemNode c) { return null; diff --git a/Core/src/org/sleuthkit/autopsy/examples/SampleExecutableDataSourceIngestModule.java b/Core/src/org/sleuthkit/autopsy/examples/SampleExecutableDataSourceIngestModule.java index 2cba176c37..507071b9d1 100755 --- a/Core/src/org/sleuthkit/autopsy/examples/SampleExecutableDataSourceIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/examples/SampleExecutableDataSourceIngestModule.java @@ -32,8 +32,6 @@ package org.sleuthkit.autopsy.examples; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; -import java.nio.file.Path; -import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; @@ -56,6 +54,7 @@ import org.sleuthkit.autopsy.externalresults.ExternalResultsXMLParser; import org.sleuthkit.autopsy.ingest.DataSourceIngestModule; import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress; import org.sleuthkit.autopsy.ingest.IngestJobContext; +import org.sleuthkit.autopsy.ingest.IngestMessage; import org.sleuthkit.autopsy.ingest.IngestModuleReferenceCounter; import org.sleuthkit.autopsy.ingest.IngestServices; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; @@ -126,7 +125,9 @@ public class SampleExecutableDataSourceIngestModule implements DataSourceIngestM List errors = resultsParser.getErrorInfo(); ExternalResultsImporter importer = new ExternalResultsImporter(); errors.addAll(importer.importResults(results)); - // RJCTODO: Report error messages to UI + for (ErrorInfo errorInfo : errors) { + IngestServices.getInstance().postMessage(IngestMessage.createErrorMessage(moduleName, "External Results Import Error", errorInfo.getMessage())); + } progressBar.progress(2); } catch (TskCoreException | InterruptedException | ParserConfigurationException | TransformerException | IOException ex) { Logger logger = IngestServices.getInstance().getLogger(moduleName); @@ -139,8 +140,8 @@ public class SampleExecutableDataSourceIngestModule implements DataSourceIngestM private void generateSimulatedResults(String resultsFilePath) throws ParserConfigurationException, IOException, TransformerConfigurationException, TransformerException { List derivedFilePaths = generateSimulatedDerivedFiles(); - String reportFilePath = generateSimulatedReport(); - generateSimulatedResultsFile(derivedFilePaths, reportFilePath, resultsFilePath); + List reportFilePaths = generateSimulatedReports(); + generateSimulatedResultsFile(derivedFilePaths, reportFilePaths, resultsFilePath); } private List generateSimulatedDerivedFiles() throws IOException { @@ -156,10 +157,14 @@ public class SampleExecutableDataSourceIngestModule implements DataSourceIngestM return filePaths; } - private String generateSimulatedReport() throws IOException { - String reportFileName = String.format("job_%d_report.txt", jobId); - String reportContents = "This is a simulated report."; - return generateFile(reportFileName, reportContents.getBytes()); + private List generateSimulatedReports() throws IOException { + List filePaths = new ArrayList<>(); + String fileContents = "This is a simulated report."; + for (int i = 0; i < 2; ++i) { + String fileName = String.format("job_%d_report_%d.txt", jobId, i); + filePaths.add(generateFile(fileName, fileContents.getBytes())); + } + return filePaths; } private String generateFile(String fileName, byte[] fileContents) throws IOException { @@ -175,7 +180,58 @@ public class SampleExecutableDataSourceIngestModule implements DataSourceIngestM return filePath; } - private void generateSimulatedResultsFile(List derivedFilePaths, String reportPath, String resultsFilePath) throws ParserConfigurationException, TransformerConfigurationException, TransformerException { + private void generateSimulatedResultsFile(List derivedFilePaths, List reportPaths, String resultsFilePath) throws ParserConfigurationException, TransformerConfigurationException, TransformerException { + // SAMPLE GENERATED BY THE CODE BELOW: + // + // + // + // + // + // C:\cases\Small\ModuleOutput\Sample Executable Ingest Module\job_1_derived_file_0.txt + // /WINDOWS/system32/ntmsapi.dll + // + // + // C:\cases\Small\ModuleOutput\Sample Executable Ingest Module\job_1_derived_file_1.txt + // /WINDOWS/system32/ntmsapi.dll/job_1_derived_file_0.txt + // + // + // + // + // /WINDOWS/system32/ntmsapi.dll + // + // SampleInterestingFilesSet + // Sample Executable Ingest Module + // + // + // + // /WINDOWS/system32/ntmsapi.dll/job_1_derived_file_0.txt + // + // One + // + // + // 2 + // + // + // 3 + // + // + // 4.0 + // + // + // + // + // + // C:\cases\Small\ModuleOutput\Sample Executable Ingest Module\job_1_report_0.txt + // Sample Executable Ingest Module + // Sample Report + // + // + // C:\cases\Small\ModuleOutput\Sample Executable Ingest Module\job_1_report_1.txt + // Sample Executable Ingest Module + // + // + // + // Create the XML DOM document and the root element. DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder(); @@ -190,8 +246,10 @@ public class SampleExecutableDataSourceIngestModule implements DataSourceIngestM // Add derived file elements to the derived files list element. Each // file element gets required local path and parent file child elements. // Note that the local path of the derived file must be to a location in - // a subdirectory of the case directory and the parent file must be - // specified using the path format used in the case database. + // the case directory or a subdirectory of the case directory and the + // parent file must be specified using the path format used in the case + // database, e.g., /WINDOWS/system32/ntmsapi.dll, where volume, file + // system, etc. are not in the path. for (int i = 0; i < derivedFilePaths.size(); ++i) { String filePath = derivedFilePaths.get(i); Element derivedFileElement = doc.createElement(ExternalResultsXMLParser.TagNames.DERIVED_FILE_ELEM.toString()); @@ -288,21 +346,29 @@ public class SampleExecutableDataSourceIngestModule implements DataSourceIngestM Element reportsListElement = doc.createElement(ExternalResultsXMLParser.TagNames.REPORTS_LIST_ELEM.toString()); rootElement.appendChild(reportsListElement); - // Add a report element to the reports list element. - Element reportElement = doc.createElement(ExternalResultsXMLParser.TagNames.REPORT_ELEM.toString()); - reportsListElement.appendChild(reportElement); - - // Add the required display name element to the report element. - Element reportDisplayNameElement = doc.createElement(ExternalResultsXMLParser.TagNames.DISPLAY_NAME_ELEM.toString()); - reportDisplayNameElement.setTextContent("Sample Report"); - reportElement.appendChild(reportDisplayNameElement); - - // Add the required local path element to the report element. Note that - // the local path must be an absolute path to a location in a - // subdirectory of the case direcotry. - Element reportPathElement = doc.createElement(ExternalResultsXMLParser.TagNames.LOCAL_PATH_ELEM.toString()); - reportPathElement.setTextContent(reportPath); - reportElement.appendChild(reportPathElement); + // Add report elements to the reports list element. Each report element + // gets required local path and source module child elements. There is + // also an optional report name element. Note that the local path of the + // report must be to a location in the case directory or a subdirectory + // of the case directory and the parent file must be specified using the + // path format used in the case database, e.g., /WINDOWS/system32/ntmsapi.dll, + // where volume, file system, etc. are not in the path. + for (int i = 0; i < reportPaths.size(); ++i) { + String reportPath = reportPaths.get(i); + Element reportElement = doc.createElement(ExternalResultsXMLParser.TagNames.REPORT_ELEM.toString()); + reportsListElement.appendChild(reportElement); + Element reportPathElement = doc.createElement(ExternalResultsXMLParser.TagNames.LOCAL_PATH_ELEM.toString()); + reportPathElement.setTextContent(reportPath); + reportElement.appendChild(reportPathElement); + Element reportSourceModuleElement = doc.createElement(ExternalResultsXMLParser.TagNames.SOURCE_MODULE_ELEM.toString()); + reportSourceModuleElement.setTextContent(moduleName); + reportElement.appendChild(reportSourceModuleElement); + if (i == 0) { + Element reportNameElement = doc.createElement(ExternalResultsXMLParser.TagNames.REPORT_NAME_ELEM.toString()); + reportNameElement.setTextContent("Sample Report"); + reportElement.appendChild(reportNameElement); + } + } TransformerFactory transformerFactory = TransformerFactory.newInstance(); Transformer transformer = transformerFactory.newTransformer(); diff --git a/Core/src/org/sleuthkit/autopsy/examples/SampleFileIngestModule.java b/Core/src/org/sleuthkit/autopsy/examples/SampleFileIngestModule.java index 150fe4c314..d5331692aa 100755 --- a/Core/src/org/sleuthkit/autopsy/examples/SampleFileIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/examples/SampleFileIngestModule.java @@ -79,21 +79,18 @@ class SampleFileIngestModule implements FileIngestModule { // modules. Case autopsyCase = Case.getCurrentCase(); SleuthkitCase sleuthkitCase = autopsyCase.getSleuthkitCase(); - - // See if the attribute type has already been defined. try { + // See if the attribute type has already been defined. attrId = sleuthkitCase.getAttrTypeID("ATTR_SAMPLE"); - } catch (TskCoreException e) { - // If not, create the the attribute type. - try { + if (attrId == -1) { attrId = sleuthkitCase.addAttrType("ATTR_SAMPLE", "Sample Attribute"); - } catch (TskCoreException ex) { - IngestServices ingestServices = IngestServices.getInstance(); - Logger logger = ingestServices.getLogger(SampleIngestModuleFactory.getModuleName()); - logger.log(Level.SEVERE, "Failed to create blackboard attribute", ex); - attrId = -1; - throw new IngestModuleException(ex.getLocalizedMessage()); } + } catch (TskCoreException ex) { + IngestServices ingestServices = IngestServices.getInstance(); + Logger logger = ingestServices.getLogger(SampleIngestModuleFactory.getModuleName()); + logger.log(Level.SEVERE, "Failed to create blackboard attribute", ex); + attrId = -1; + throw new IngestModuleException(ex.getLocalizedMessage()); } } } diff --git a/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResults.java b/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResults.java index c0531647c4..06afcabe14 100644 --- a/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResults.java +++ b/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResults.java @@ -57,14 +57,14 @@ final public class ExternalResults { return Collections.unmodifiableList(artifacts); } - void addReport(String displayName, String localPath) { - if (displayName.isEmpty()) { - throw new IllegalArgumentException("displayName argument is empty"); - } + void addReport(String localPath, String sourceModuleName, String reportName) { if (localPath.isEmpty()) { throw new IllegalArgumentException("localPath argument is empty"); } - Report report = new Report(displayName, localPath); + if (sourceModuleName.isEmpty()) { + throw new IllegalArgumentException("sourceModuleName argument is empty"); + } + Report report = new Report(localPath, sourceModuleName, reportName); reports.add(report); } @@ -157,21 +157,27 @@ final public class ExternalResults { static final class Report { - private final String displayName; private final String localPath; + private final String sourceModuleName; + private final String reportName; - Report(String displayName, String localPath) { - this.displayName = displayName; + Report(String localPath, String sourceModuleName, String displayName) { this.localPath = localPath; - } - - String getDisplayName() { - return displayName; + this.sourceModuleName = sourceModuleName; + this.reportName = displayName; } String getLocalPath() { return localPath; } + + String getSourceModuleName() { + return sourceModuleName; + } + + String getReportName() { + return reportName; + } } static final class DerivedFile { diff --git a/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResultsImporter.java b/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResultsImporter.java index 7a09882e62..c94c9eccbd 100644 --- a/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResultsImporter.java +++ b/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResultsImporter.java @@ -23,7 +23,6 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.List; import java.util.logging.Level; import org.sleuthkit.autopsy.casemodule.Case; @@ -65,7 +64,9 @@ public final class ExternalResultsImporter { importDerivedFiles(results); importArtifacts(results); importReports(results); - return Collections.unmodifiableList(this.errors); + List importErrors = new ArrayList(this.errors); + this.errors.clear(); + return importErrors; } private void importDerivedFiles(ExternalResults results) { @@ -78,7 +79,7 @@ public final class ExternalResultsImporter { String relativePath = this.getPathRelativeToCaseFolder(localPath); if (!relativePath.isEmpty()) { String parentFilePath = fileData.getParentPath(); - AbstractFile parentFile = findFileInCaseDatabase(results.getDataSource(), parentFilePath); + AbstractFile parentFile = findFileInCaseDatabase(parentFilePath); if (parentFile != null) { DerivedFile derivedFile = fileManager.addDerivedFile(localFile.getName(), relativePath, localFile.length(), 0, 0, 0, 0, // Do not currently have file times for derived files from external processes. @@ -110,19 +111,19 @@ public final class ExternalResultsImporter { try { // Add the artifact to the case database. boolean artifactTypeIsUserDefined = false; - int artifactTypeId = caseDb.getArtifactTypeIdIfExists(artifactData.getType()); + int artifactTypeId = caseDb.getArtifactTypeID(artifactData.getType()); if (artifactTypeId == -1) { artifactTypeId = caseDb.addArtifactType(artifactData.getType(), artifactData.getType()); artifactTypeIsUserDefined = true; } - Content sourceFile = findFileInCaseDatabase(results.getDataSource(), artifactData.getSourceFilePath()); + Content sourceFile = findFileInCaseDatabase(artifactData.getSourceFilePath()); if (sourceFile != null) { BlackboardArtifact artifact = sourceFile.newArtifact(artifactTypeId); // Add the artifact's attributes to the case database. Collection attributes = new ArrayList<>(); for (ExternalResults.ArtifactAttribute attributeData : artifactData.getAttributes()) { - int attributeTypeId = caseDb.getAttrTypeIdIfExists(attributeData.getType()); + int attributeTypeId = caseDb.getAttrTypeID(attributeData.getType()); if (attributeTypeId == -1) { attributeTypeId = caseDb.addAttrType(attributeData.getType(), attributeData.getType()); } @@ -157,6 +158,10 @@ public final class ExternalResultsImporter { if (!artifactTypeIsUserDefined) { IngestServices.getInstance().fireModuleDataEvent(new ModuleDataEvent(this.getClass().getSimpleName(), BlackboardArtifact.ARTIFACT_TYPE.fromID(artifactTypeId))); } + } else { + String errorMessage = String.format("Could not import %s artifact from %s, source file not found", artifactData.getType(), artifactData.getSourceFilePath()); + ExternalResultsImporter.logger.log(Level.SEVERE, errorMessage); + this.errors.add(new ErrorInfo(ExternalResultsImporter.class.getName(), errorMessage)); } } catch (TskCoreException ex) { String errorMessage = String.format("Could not import %s artifact from %s, error updating case database", artifactData.getType(), artifactData.getSourceFilePath()); @@ -172,10 +177,7 @@ public final class ExternalResultsImporter { try { File reportFile = new File(reportPath); if (reportFile.exists()) { - String relativePath = this.getPathRelativeToCaseFolder(reportPath); - if (!relativePath.isEmpty()) { - Case.getCurrentCase().addReport(relativePath, report.getDisplayName()); - } + Case.getCurrentCase().addReport(reportPath, report.getSourceModuleName(), report.getReportName()); } else { String errorMessage = String.format("Could not import report at %s, file does not exist", reportPath); ExternalResultsImporter.logger.log(Level.SEVERE, errorMessage); @@ -189,7 +191,7 @@ public final class ExternalResultsImporter { } } - private AbstractFile findFileInCaseDatabase(Content dataSource, String filePath) throws TskCoreException { + private AbstractFile findFileInCaseDatabase(String filePath) throws TskCoreException { AbstractFile file = null; // Split the path into the file name and the parent path. String fileName = filePath; @@ -203,10 +205,9 @@ public final class ExternalResultsImporter { String condition = "name='" + fileName + "' AND parent_path='" + parentPath + "'"; //NON-NLS List files = Case.getCurrentCase().getSleuthkitCase().findAllFilesWhere(condition); if (!files.isEmpty()) { - if (files.size() == 1) { - file = files.get(0); - } else { - String errorMessage = String.format("Parent file path %s is ambiguous", filePath); + file = files.get(0); + if (files.size() > 1) { + String errorMessage = String.format("Parent file path %s is ambiguous, using first file found", filePath); this.recordError(errorMessage); } } diff --git a/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResultsXMLParser.java b/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResultsXMLParser.java index 5f80433258..0465cf9404 100644 --- a/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResultsXMLParser.java +++ b/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResultsXMLParser.java @@ -19,7 +19,6 @@ package org.sleuthkit.autopsy.externalresults; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; @@ -61,7 +60,7 @@ public final class ExternalResultsXMLParser implements ExternalResultsParser { SOURCE_MODULE_ELEM("source_module"), //NON-NLS REPORTS_LIST_ELEM("reports"), //NON-NLS REPORT_ELEM("report"), //NON-NLS - DISPLAY_NAME_ELEM("display_name"); //NON-NLS + REPORT_NAME_ELEM("report_name"); //NON-NLS private final String text; private TagNames(final String text) { @@ -154,7 +153,7 @@ public final class ExternalResultsXMLParser implements ExternalResultsParser { @Override public List getErrorInfo() { - return Collections.unmodifiableList(this.errors); + return new ArrayList<>(this.errors); } private void parseDerivedFiles(Element rootElement) { @@ -266,17 +265,19 @@ public final class ExternalResultsXMLParser implements ExternalResultsParser { NodeList reportNodes = reportsListElem.getElementsByTagName(TagNames.REPORT_ELEM.toString()); for (int j = 0; j < reportNodes.getLength(); ++j) { Element reportElem = (Element) reportNodes.item(j); - // Get the display name. - String displayName = getChildElementContent(reportElem, TagNames.DISPLAY_NAME_ELEM.toString(), true); - if (displayName.isEmpty()) { - continue; - } // Get the local path. String path = getChildElementContent(reportElem, TagNames.LOCAL_PATH_ELEM.toString(), true); if (path.isEmpty()) { continue; } - this.resultsData.addReport(displayName, path); + // Get the source module. + String sourceModule = getChildElementContent(reportElem, TagNames.SOURCE_MODULE_ELEM.toString(), true); + if (path.isEmpty()) { + continue; + } + // Get the optional report name. + String reportName = getChildElementContent(reportElem, TagNames.REPORT_NAME_ELEM.toString(), false); + this.resultsData.addReport(path, sourceModule, reportName); } } } diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportBodyFile.java b/Core/src/org/sleuthkit/autopsy/report/ReportBodyFile.java index 83f1a9a7bf..06578dc719 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ReportBodyFile.java +++ b/Core/src/org/sleuthkit/autopsy/report/ReportBodyFile.java @@ -2,7 +2,7 @@ * * Autopsy Forensic Browser * - * Copyright 2012 Basis Technology Corp. + * Copyright 2012-2014 Basis Technology Corp. * * Copyright 2012 42six Solutions. * Contact: aebadirad 42six com @@ -25,8 +25,6 @@ package org.sleuthkit.autopsy.report; import java.io.BufferedWriter; import java.io.FileWriter; import java.io.IOException; -import java.sql.ResultSet; -import java.sql.SQLException; import java.util.List; import java.util.logging.Level; import javax.swing.JPanel; @@ -151,11 +149,17 @@ import org.sleuthkit.datamodel.*; logger.log(Level.WARNING, "Could not write the temp body file report.", ex); //NON-NLS } finally { try { - out.flush(); - out.close(); + if (out != null) { + out.flush(); + out.close(); + Case.getCurrentCase().addReport(reportPath, "TSK Body File", ""); + } } catch (IOException ex) { logger.log(Level.WARNING, "Could not flush and close the BufferedWriter.", ex); //NON-NLS - } + } catch (TskCoreException ex) { + String errorMessage = String.format("Error adding %s to case as a report", reportPath); //NON-NLS + logger.log(Level.SEVERE, errorMessage, ex); + } } progressPanel.complete(); } catch(TskCoreException ex) { diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportExcel.java b/Core/src/org/sleuthkit/autopsy/report/ReportExcel.java index 94116f324d..c504ef1646 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ReportExcel.java +++ b/Core/src/org/sleuthkit/autopsy/report/ReportExcel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013 Basis Technology Corp. + * Copyright 2013-2014 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -26,9 +26,11 @@ import java.util.logging.Level; import org.apache.poi.hssf.util.HSSFColor; import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.openide.util.Exceptions; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.TskCoreException; class ReportExcel implements TableReportModule { private static final Logger logger = Logger.getLogger(ReportExcel.class.getName()); @@ -110,8 +112,12 @@ import org.sleuthkit.autopsy.coreutils.Logger; try { out = new FileOutputStream(reportPath); wb.write(out); + Case.getCurrentCase().addReport(reportPath, "Excel Report", ""); } catch (IOException ex) { logger.log(Level.SEVERE, "Failed to write Excel report.", ex); //NON-NLS + } catch (TskCoreException ex) { + String errorMessage = String.format("Error adding %s to case as a report", reportPath); //NON-NLS + logger.log(Level.SEVERE, errorMessage, ex); } finally { if (out != null) { try { diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java b/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java index f7f9f88894..9553b299e8 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java +++ b/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java @@ -2,7 +2,7 @@ * * Autopsy Forensic Browser * - * Copyright 2012 Basis Technology Corp. + * Copyright 2012-2014 Basis Technology Corp. * * Copyright 2012 42six Solutions. * Contact: aebadirad 42six com @@ -803,8 +803,9 @@ import org.sleuthkit.datamodel.TskData.TSK_DB_FILES_TYPE_ENUM; */ private void writeIndex() { Writer indexOut = null; + String indexFilePath = path + "index.html"; try { - indexOut = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(path + "index.html"), "UTF-8")); //NON-NLS + indexOut = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(indexFilePath), "UTF-8")); //NON-NLS StringBuilder index = new StringBuilder(); index.append("\n").append( //NON-NLS NbBundle.getMessage(this.getClass(), "ReportHTML.writeIndex.title", currentCase.getName())).append( @@ -820,22 +821,12 @@ import org.sleuthkit.datamodel.TskData.TSK_DB_FILES_TYPE_ENUM; index.append("</frameset>\n"); //NON-NLS index.append("</html>"); //NON-NLS indexOut.write(index.toString()); - - // RJCTODO: Add this file as a report, clean this up, make utility - String relativePath = ""; - Path pathObj = Paths.get(path + "index.html"); - Path pathBase = Paths.get(Case.getCurrentCase().getCaseDirectory()); - try { - Path pathRelative = pathBase.relativize(pathObj); - relativePath = pathRelative.toString(); - } catch (IllegalArgumentException ex) { - // RJCTODO - } - Case.getCurrentCase().addReport("HTML Report", relativePath); + Case.getCurrentCase().addReport(indexFilePath, "HTML Report", ""); } catch (IOException ex) { logger.log(Level.SEVERE, "Error creating Writer for index.html: {0}", ex); //NON-NLS } catch (TskCoreException ex) { - Exceptions.printStackTrace(ex); // RJCTODO: Handle this + String errorMessage = String.format("Error adding %s to case as a report", indexFilePath); //NON-NLS + logger.log(Level.SEVERE, errorMessage, ex); } finally { try { if(indexOut != null) { diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportKML.java b/Core/src/org/sleuthkit/autopsy/report/ReportKML.java index b466d9bd0d..90b23fd442 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ReportKML.java +++ b/Core/src/org/sleuthkit/autopsy/report/ReportKML.java @@ -256,11 +256,13 @@ class ReportKML implements GeneralReportModule { FileOutputStream writer = new FileOutputStream(reportPath); outputter.output(kmlDocument, writer); writer.close(); + Case.getCurrentCase().addReport(reportPath, "KML Report", ""); } catch (IOException ex) { logger.log(Level.WARNING, "Could not write the KML file.", ex); //NON-NLS + } catch (TskCoreException ex) { + String errorMessage = String.format("Error adding %s to case as a report", reportPath); //NON-NLS + logger.log(Level.SEVERE, errorMessage, ex); } - - } catch (IOException ex) { logger.log(Level.WARNING, "Could not write the KML report.", ex); //NON-NLS }