diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index e050a8859a..5c68404478 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -119,7 +119,13 @@ public class Case implements SleuthkitCase.ErrorObserver { * closed, the "new value" will be null and the "old value" will be the * instance of the Case object being closed. */ - CURRENT_CASE; + CURRENT_CASE, + /** + * Name for property change events fired when a report is added to the + * case. The old value supplied by the event object is null and the new + * value is a reference to a Report object representing the new report. + */ + REPORT_ADDED; }; private String name; @@ -1164,4 +1170,27 @@ public class Case implements SleuthkitCase.ErrorObserver { public void receiveError(String context, String errorMessage) { MessageNotifyUtil.Notify.error(context, errorMessage); } + + /** + * 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 localPath, String srcModuleName, String reportName) throws TskCoreException { + Report report = this.db.addReport(localPath, srcModuleName, reportName); + try { + Case.pcs.firePropertyChange(Events.REPORT_ADDED.toString(), null, report); + } catch (Exception ex) { + String errorMessage = String.format("A Case %s listener threw an exception", Events.REPORT_ADDED.toString()); + logger.log(Level.SEVERE, errorMessage, ex); + } + } + + public List getAllReports() throws TskCoreException { + return this.db.getAllReports(); + } } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java b/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java index c23faecd1e..43126ea5b4 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java @@ -68,11 +68,17 @@ final class NewCaseVisualPanel1 extends JPanel implements DocumentListener{ /** * Gets the base directory that the user typed on the base directory text field. + * Will add file separator if it was not added. * * @return baseDirectory the base directory from the case dir text field */ public String getCaseParentDir(){ - return this.caseParentDirTextField.getText(); + String parentDir = this.caseParentDirTextField.getText(); + + if (parentDir.endsWith(File.separator) == false) { + parentDir = parentDir + File.separator; + } + return parentDir; } public JTextField getCaseParentDirTextField(){ @@ -244,11 +250,11 @@ final class NewCaseVisualPanel1 extends JPanel implements DocumentListener{ */ public void updateUI(DocumentEvent e) { - String caseName = this.caseNameTextField.getText(); - String caseDir = this.caseParentDirTextField.getText(); + String caseName = getCaseName(); + String parentDir = getCaseParentDir(); - if(!caseName.equals("") && !caseDir.equals("")){ - caseDirTextField.setText( caseDir + File.separator + caseName); + if(!caseName.equals("") && !parentDir.equals("")){ + caseDirTextField.setText( parentDir + caseName); wizPanel.setIsFinish(true); } else{ diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseWizardPanel1.java b/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseWizardPanel1.java index 1c0a934a2f..3bc8b33a51 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseWizardPanel1.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseWizardPanel1.java @@ -202,7 +202,7 @@ class NewCaseWizardPanel1 implements WizardDescriptor.ValidatingPanel | diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/FileManager.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/FileManager.java index 2b1887adc5..966df48bce 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/services/FileManager.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/FileManager.java @@ -158,7 +158,7 @@ public class FileManager implements Closeable { * * @param fileName file name the derived file * @param localPath local path of the derived file, including the file - * name. The path is relative to the database path. + * name. The path is relative to the case folder. * @param size size of the derived file in bytes * @param ctime * @param crtime diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties index a8565de787..d4fbb1e65c 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties @@ -11,6 +11,7 @@ Metadata.tableRowTitle.md5=MD5 Metadata.tableRowTitle.hashLookupResults=Hash Lookup Results Metadata.tableRowTitle.internalid=Internal ID Metadata.tableRowTitle.localPath=Local Path +Metadata.tableRowTitle.type=Type Metadata.title=Metadata Metadata.toolTip=Displays metadata about the file. Metadata.nodeText.nonFilePassedIn=Non-file passed in \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle_ja.properties index b3db39fbc1..6b9dec09fc 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle_ja.properties @@ -1,16 +1,17 @@ -Metadata.tableRowTitle.name=\u540D\u79F0 -Metadata.tableRowTitle.size=\u30B5\u30A4\u30BA -Metadata.tableRowTitle.fileNameAlloc=\u30D5\u30A1\u30A4\u30EB\u30A2\u30ED\u30B1\u30FC\u30B7\u30E7\u30F3\u72B6\u614B -Metadata.tableRowTitle.metadataAlloc=\u30E1\u30BF\u30C7\u30FC\u30BF\u30A2\u30ED\u30B1\u30FC\u30B7\u30E7\u30F3\u72B6\u614B -Metadata.tableRowTitle.modified=\u4FEE\u6B63\u6E08\u307F -Metadata.tableRowTitle.accessed=\u30A2\u30AF\u30BB\u30B9\u6E08\u307F -Metadata.tableRowTitle.created=\u4F5C\u6210\u6E08\u307F -Metadata.tableRowTitle.changed=\u5909\u66F4\u6E08\u307F -Metadata.tableRowContent.md5notCalc=\u672A\u8A08\u7B97 +Metadata.tableRowTitle.name=\u540d\u79f0 +Metadata.tableRowTitle.size=\u30b5\u30a4\u30ba +Metadata.tableRowTitle.fileNameAlloc=\u30d5\u30a1\u30a4\u30eb\u30a2\u30ed\u30b1\u30fc\u30b7\u30e7\u30f3\u72b6\u614b +Metadata.tableRowTitle.metadataAlloc=\u30e1\u30bf\u30c7\u30fc\u30bf\u30a2\u30ed\u30b1\u30fc\u30b7\u30e7\u30f3\u72b6\u614b +Metadata.tableRowTitle.modified=\u4fee\u6b63\u6e08\u307f +Metadata.tableRowTitle.accessed=\u30a2\u30af\u30bb\u30b9\u6e08\u307f +Metadata.tableRowTitle.created=\u4f5c\u6210\u6e08\u307f +Metadata.tableRowTitle.changed=\u5909\u66f4\u6e08\u307f +Metadata.tableRowContent.md5notCalc=\u672a\u8a08\u7b97 Metadata.tableRowTitle.md5=MD5 -Metadata.tableRowTitle.hashLookupResults=\u30CF\u30C3\u30B7\u30E5\u30EB\u30C3\u30AF\u30A2\u30C3\u30D7\u7D50\u679C -Metadata.tableRowTitle.internalid=\u5185\u90E8ID -Metadata.tableRowTitle.localPath=\u30ED\u30FC\u30AB\u30EB\u30D1\u30B9 -Metadata.title=\u30E1\u30BF\u30C7\u30FC\u30BF -Metadata.toolTip=\u30D5\u30A1\u30A4\u30EB\u306E\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u8868\u793A\u3057\u307E\u3059\u3002 -Metadata.nodeText.nonFilePassedIn=\u51E6\u7406\u4E2D\u306E\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u306F\u30D5\u30A1\u30A4\u30EB\u3067\u306F\u3042\u308A\u307E\u305B\u3093 \ No newline at end of file +Metadata.tableRowTitle.hashLookupResults=\u30cf\u30c3\u30b7\u30e5\u30eb\u30c3\u30af\u30a2\u30c3\u30d7\u7d50\u679c +Metadata.tableRowTitle.internalid=\u5185\u90e8ID +Metadata.tableRowTitle.localPath=\u30ed\u30fc\u30ab\u30eb\u30d1\u30b9 +Metadata.tableRowTitle.type=Type +Metadata.title=\u30e1\u30bf\u30c7\u30fc\u30bf +Metadata.toolTip=\u30d5\u30a1\u30a4\u30eb\u306e\u30e1\u30bf\u30c7\u30fc\u30bf\u3092\u8868\u793a\u3057\u307e\u3059\u3002 +Metadata.nodeText.nonFilePassedIn=\u51e6\u7406\u4e2d\u306e\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u306f\u30d5\u30a1\u30a4\u30eb\u3067\u306f\u3042\u308a\u307e\u305b\u3093 \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java b/Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java index 5f4ed40e26..eeec7f400e 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java @@ -24,7 +24,6 @@ import org.openide.nodes.Node; import org.openide.util.NbBundle; import org.openide.util.lookup.ServiceProvider; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; -import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode; import org.sleuthkit.autopsy.datamodel.ContentUtils; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.TskCoreException; @@ -135,7 +134,8 @@ public class Metadata extends javax.swing.JPanel implements DataContentViewer } catch (TskCoreException ex) { addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.name"), file.getParentPath() + "/" + file.getName()); } - + + addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.type"), file.getType().getName()); addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.size"), new Long(file.getSize()).toString() ); addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.fileNameAlloc"), file.getDirFlagAsString()); addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.metadataAlloc"), file.getMetaFlagsAsString()); diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/ErrorInfo.java b/Core/src/org/sleuthkit/autopsy/coreutils/ErrorInfo.java new file mode 100755 index 0000000000..e7da64bcf4 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/coreutils/ErrorInfo.java @@ -0,0 +1,56 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2014 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.coreutils; + +/** + * Encapsulates an error message and an associated exception, if any. + */ +final public class ErrorInfo { + private final String errorSource; + private final String message; + private final Exception exception; + + public ErrorInfo(String errorSource, String message) { + this.errorSource = errorSource; + this.message = message; + this.exception = null; + } + + public ErrorInfo(String errorSource, String message, Exception exception) { + this.errorSource = errorSource; + this.message = message; + this.exception = exception; + } + + public String getErrroSource() { + return this.errorSource; + } + + public String getMessage() { + return this.message; + } + + public boolean hasException() { + return exception != null; + } + + public Exception getException() { + return this.exception; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/XMLUtil.java b/Core/src/org/sleuthkit/autopsy/coreutils/XMLUtil.java index 3d95d8e529..a997f01dd5 100644 --- a/Core/src/org/sleuthkit/autopsy/coreutils/XMLUtil.java +++ b/Core/src/org/sleuthkit/autopsy/coreutils/XMLUtil.java @@ -126,12 +126,12 @@ public class XMLUtil { DocumentBuilder builder = builderFactory.newDocumentBuilder(); ret = builder.parse(new FileInputStream(xmlPath)); } catch (ParserConfigurationException e) { - Logger.getLogger(clazz.getName()).log(Level.SEVERE, "Error loading XML file: can't initialize parser.", e); //NON-NLS + Logger.getLogger(clazz.getName()).log(Level.SEVERE, "Error loading XML file " + xmlPath + " : can't initialize parser.", e); //NON-NLS } catch (SAXException e) { - Logger.getLogger(clazz.getName()).log(Level.SEVERE, "Error loading XML file: can't parse XML.", e); //NON-NLS + Logger.getLogger(clazz.getName()).log(Level.SEVERE, "Error loading XML file " + xmlPath + " : can't parse XML.", e); //NON-NLS } catch (IOException e) { //error reading file - Logger.getLogger(clazz.getName()).log(Level.SEVERE, "Error loading XML file: can't read file.", e); //NON-NLS + Logger.getLogger(clazz.getName()).log(Level.SEVERE, "Error loading XML file " + xmlPath + " : can't read file.", e); //NON-NLS } return ret; } @@ -144,7 +144,6 @@ public class XMLUtil { * @param xsdPath the full path to the file to validate against */ public static Document loadDoc(Class clazz, String xmlPath, String xsdPath) { - DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); Document ret = loadDoc(clazz, xmlPath); if (!XMLUtil.xmlIsValid(ret, clazz, xsdPath)) { Logger.getLogger(clazz.getName()).log(Level.WARNING, "Error loading XML file: could not validate against [{0}], results may not be accurate", xsdPath); //NON-NLS diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java index 409a9f6483..bce7717f6c 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java @@ -113,7 +113,11 @@ abstract class AbstractContentChildren extends Keys { } /** - * Creates appropriate Node for each supported artifact category / grouping + * Gets a DisplayableItemNode for use as a subtree root node for the Autopsy + * tree view from each type of AutopsyVisitableItem visited. There are + * AutopsyVisitableItems for the Data Sources, Views, Results, and Reports + * subtrees, and for the subtrees of Results (e.g., Extracted Content, + * Hash Set Hits, etc.). */ static class CreateAutopsyNodeVisitor extends AutopsyItemVisitor.Default { @@ -186,6 +190,11 @@ abstract class AbstractContentChildren extends Keys { return new ResultsNode(r.getSleuthkitCase()); } + @Override + public AbstractNode visit(Reports reportsItem) { + return new Reports.ReportsListNode(); + } + @Override protected AbstractNode defaultVisit(AutopsyVisitableItem di) { throw new UnsupportedOperationException( diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AutopsyItemVisitor.java b/Core/src/org/sleuthkit/autopsy/datamodel/AutopsyItemVisitor.java index 1f5cf9ae71..42d03c59a2 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AutopsyItemVisitor.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AutopsyItemVisitor.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011 Basis Technology Corp. + * Copyright 2011-2014 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,8 +18,6 @@ */ package org.sleuthkit.autopsy.datamodel; -/** - */ interface AutopsyItemVisitor { T visit(ExtractedContent ec); @@ -59,7 +57,9 @@ package org.sleuthkit.autopsy.datamodel; T visit(Views v); T visit(Results r); - + + T visit(Reports reportsItem); + static abstract public class Default implements AutopsyItemVisitor { protected abstract T defaultVisit(AutopsyVisitableItem ec); @@ -157,5 +157,10 @@ package org.sleuthkit.autopsy.datamodel; public T visit(Results r) { return defaultVisit(r); } + + @Override + public T visit(Reports reportsItem) { + return defaultVisit(reportsItem); + } } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties index b5fe77acdf..59193ed202 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties @@ -194,6 +194,12 @@ LocalFileNode.createSheet.noDescr.text=no description LocalFileNode.getActions.viewInNewWin.text=View in New Window LocalFileNode.getActions.openInExtViewer.text=Open in External Viewer LocalFileNode.getActions.searchFilesSameMd5.text=Search for files with the same MD5 hash +OpenReportAction.actionDisplayName=Open Report +OpenReportAction.actionPerformed.MessageBoxTitle=Open Report Failure +OpenReportAction.actionPerformed.NoAssociatedEditorMessage=There is no associated editor for reports of this type or the associated application failed to launch. +OpenReportAction.actionPerformed.NoOpenInEditorSupportMessage=This platform (operating system) does not support opening a file in an editor this way. +OpenReportAction.actionPerformed.MissingReportFileMessage=The report file no longer exists. +OpenReportAction.actionPerformed.ReportFileOpenPermissionDeniedMessage=Permission to open the report file was denied. RecentFiles.aut0DayFilter.displayName.text=Final Day RecentFiles.aut1dayFilter.displayName.text=Final Day - 1 RecentFiles.aut2dayFilter.displayName.text=Final Day - 2 @@ -209,6 +215,16 @@ RecentFilesNode.createSheet.name.name=Name RecentFilesNode.createSheet.name.displayName=Name RecentFilesNode.createSheet.name.desc=no description RecentFilesNode.name.text=Recent Files +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 +ReportNode.pathProperty.name=Report File Path +ReportNode.pathProperty.displayName=Report File Path +ReportNode.pathProperty.desc=Local path of the report file +ReportsListNode.displayName=Reports ResultsNode.name.text=Results ResultsNode.createSheet.name.name=Name ResultsNode.createSheet.name.displayName=Name diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java b/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java index dd93eb24ab..4a47fab11f 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java @@ -102,6 +102,10 @@ public interface DisplayableItemNodeVisitor { T visit(VirtualDirectoryNode ldn); + T visit(Reports.ReportsListNode reportsNode); + + T visit(Reports.ReportNode reportNode); + /** * Visitor with an implementable default behavior for all types. Override * specific visit types to not use the default behavior. @@ -302,5 +306,15 @@ public interface DisplayableItemNodeVisitor { public T visit(BlackboardArtifactTagNode node) { return defaultVisit(node); } + + @Override + public T visit(Reports.ReportsListNode node) { + return defaultVisit(node); + } + + @Override + public T visit(Reports.ReportNode node) { + return defaultVisit(node); + } } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Reports.java b/Core/src/org/sleuthkit/autopsy/datamodel/Reports.java new file mode 100755 index 0000000000..290216b495 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Reports.java @@ -0,0 +1,224 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2014 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.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; +import org.openide.nodes.Sheet; +import org.openide.util.NbBundle; +import org.openide.util.lookup.Lookups; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.Report; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Implements the Reports subtree of the Autopsy tree. + */ +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) { + // CreateAutopsyNodeVisitor.visit() constructs a ReportsListNode. + return visitor.visit(this); + } + + /** + * The root node of the Reports subtree of the Autopsy tree. + */ + 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 + + public ReportsListNode() { + super(Children.create(new ReportNodeFactory(), true)); + setName(DISPLAY_NAME); + setDisplayName(DISPLAY_NAME); + this.setIconBaseWithExtension(ICON_PATH); + } + + @Override + public boolean isLeafTypeNode() { + return true; + } + + @Override + public T accept(DisplayableItemNodeVisitor visitor) { + // - GetPopupActionsDisplayableItemNodeVisitor.visit() returns null. + // - GetPreferredActionsDisplayableItemNodeVisitor.visit() returns null. + // - IsLeafItemVisitor.visit() returns false. + // - ShowItemVisitor.visit() returns true. + return visitor.visit(this); + } + } + + /** + * The child node factory that creates ReportNode children for a + * ReportsListNode. + */ + private static final class ReportNodeFactory extends ChildFactory { + + ReportNodeFactory() { + Case.addPropertyChangeListener(new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + String eventType = evt.getPropertyName(); + if (eventType.equals(Case.Events.REPORT_ADDED.toString())) { + ReportNodeFactory.this.refresh(true); + } + } + }); + } + + @Override + protected boolean createKeys(List keys) { + try { + keys.addAll(Case.getCurrentCase().getAllReports()); + } catch (TskCoreException ex) { + Logger.getLogger(Reports.ReportNodeFactory.class.getName()).log(Level.SEVERE, "Failed to get reports", ex); //NON-NLS + } + return true; + } + + @Override + protected Node createNodeForKey(Report key) { + return new ReportNode(key); + } + } + + /** + * A leaf node in the Reports subtree of the Autopsy tree, wraps a Report + * object. + */ + 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; + + ReportNode(Report report) { + super(Children.LEAF, Lookups.fixed(report)); + this.report = report; + super.setName(this.report.getSourceModuleName()); + super.setDisplayName(this.report.getSourceModuleName()); + this.setIconBaseWithExtension(ICON_PATH); + } + + @Override + public boolean isLeafTypeNode() { + return true; + } + + @Override + public T accept(DisplayableItemNodeVisitor visitor) { + // - GetPopupActionsDisplayableItemNodeVisitor.visit() calls getActions(). + // - GetPreferredActionsDisplayableItemNodeVisitor.visit() calls getPreferredAction(). + // - IsLeafItemVisitor.visit() returns true. + // - ShowItemVisitor.visit() returns true. + return visitor.visit(this); + } + + @Override + protected Sheet createSheet() { + Sheet sheet = super.createSheet(); + Sheet.Set propertiesSet = sheet.get(Sheet.PROPERTIES); + if (propertiesSet == null) { + propertiesSet = Sheet.createPropertiesSet(); + sheet.put(propertiesSet); + } + 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())); + 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 { + + private OpenReportAction() { + super(NbBundle.getMessage(OpenReportAction.class, "OpenReportAction.actionDisplayName")); + } + @Override + public void actionPerformed(ActionEvent e) { + File file = new File(ReportNode.this.report.getPath()); + try { + Desktop.getDesktop().open(file); + } catch (IOException ex) { + JOptionPane.showMessageDialog(null, + NbBundle.getMessage(OpenReportAction.class, "OpenReportAction.actionPerformed.NoAssociatedEditorMessage"), + NbBundle.getMessage(OpenReportAction.class, "OpenReportAction.actionPerformed.MessageBoxTitle"), + JOptionPane.ERROR_MESSAGE); + } catch (UnsupportedOperationException ex) { + JOptionPane.showMessageDialog(null, + NbBundle.getMessage(OpenReportAction.class, "OpenReportAction.actionPerformed.NoOpenInEditorSupportMessage"), + NbBundle.getMessage(OpenReportAction.class, "OpenReportAction.actionPerformed.MessageBoxTitle"), + JOptionPane.ERROR_MESSAGE); + } catch (IllegalArgumentException ex) { + JOptionPane.showMessageDialog(null, + NbBundle.getMessage(OpenReportAction.class, "OpenReportAction.actionPerformed.MissingReportFileMessage"), + NbBundle.getMessage(OpenReportAction.class, "OpenReportAction.actionPerformed.MessageBoxTitle"), + JOptionPane.ERROR_MESSAGE); + } catch (SecurityException ex) { + JOptionPane.showMessageDialog(null, + NbBundle.getMessage(OpenReportAction.class, "OpenReportAction.actionPerformed.ReportFileOpenPermissionDeniedMessage"), + NbBundle.getMessage(OpenReportAction.class, "OpenReportAction.actionPerformed.MessageBoxTitle"), + JOptionPane.ERROR_MESSAGE); + } + } + } + } +} \ 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..a6e7dd1130 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; @@ -307,6 +308,12 @@ public class DataResultFilterNode extends FilterNode { return actions; } + @Override + public List visit(Reports.ReportsListNode ditem) { + // The base class Action is "Collapse All", inappropriate. + return null; + } + @Override protected List defaultVisit(DisplayableItemNode ditem) { //preserve the default node's actions @@ -522,6 +529,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/directorytree/DirectoryTreeTopComponent.java b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java index d9e0d8eb3f..785f9952df 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java @@ -74,6 +74,7 @@ import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskException; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.datamodel.ExtractedContent; +import org.sleuthkit.autopsy.datamodel.Reports; /** * Top component which displays something. @@ -358,6 +359,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat items.add(new DataSources(tskCase)); items.add(new Views(tskCase)); items.add(new Results(tskCase)); + items.add(new Reports()); contentChildren = new RootContentChildren(items); Node root = new AbstractNode(contentChildren) { /** diff --git a/Core/src/org/sleuthkit/autopsy/examples/Bundle.properties b/Core/src/org/sleuthkit/autopsy/examples/Bundle.properties index 74e5aa8e31..53be459ae2 100755 --- a/Core/src/org/sleuthkit/autopsy/examples/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/examples/Bundle.properties @@ -2,3 +2,5 @@ SampleContentViewer.jLabel1.text=jLabel1 SampleIngestModuleFactory.moduleName=Sample Ingest Module SampleIngestModuleFactory.moduleDescription=This module serves as a sample ingest module for software developers. SampleIngestModuleIngestJobSettingsPanel.skipKnownFilesCheckBox.text=Skip Known Files (NSRL) +SampleExecutableIngestModuleFactory.moduleName=Sample Executable Ingest Module +SampleExecutableIngestModuleFactory.moduleDescription=This module serves as a sample executable ingest module for software developers. diff --git a/Core/src/org/sleuthkit/autopsy/examples/SampleExecutableDataSourceIngestModule.java b/Core/src/org/sleuthkit/autopsy/examples/SampleExecutableDataSourceIngestModule.java new file mode 100755 index 0000000000..507071b9d1 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/examples/SampleExecutableDataSourceIngestModule.java @@ -0,0 +1,379 @@ +/* + * Sample module in the public domain. Feel free to use this as a template + * for your modules. + * + * Contact: Brian Carrier [carrier sleuthkit [dot] org] + * + * This is free and unencumbered software released into the public domain. + * + * Anyone is free to copy, modify, publish, use, compile, sell, or + * distribute this software, either in source code form or as a compiled + * binary, for any purpose, commercial or non-commercial, and by any + * means. + * + * In jurisdictions that recognize copyright laws, the author or authors + * of this software dedicate any and all copyright interest in the + * software to the public domain. We make this dedication for the benefit + * of the public at large and to the detriment of our heirs and + * successors. We intend this dedication to be an overt act of + * relinquishment in perpetuity of all present and future rights to this + * software under copyright law. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +package org.sleuthkit.autopsy.examples; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.coreutils.ErrorInfo; +import org.sleuthkit.autopsy.coreutils.ExecUtil; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.externalresults.ExternalResults; +import org.sleuthkit.autopsy.externalresults.ExternalResultsImporter; +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; +import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.TskCoreException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +/** + * Sample data source ingest module that doesn't do much. Demonstrates use of + * utility classes: ExecUtils and the org.sleuthkit.autopsy.externalresults + * package. + */ +public class SampleExecutableDataSourceIngestModule implements DataSourceIngestModule { + + private static final IngestModuleReferenceCounter refCounter = new IngestModuleReferenceCounter(); + private static final String moduleName = SampleExecutableIngestModuleFactory.getModuleName(); + private final String fileInCaseDatabase = "/WINDOWS/system32/ntmsapi.dll"; // Probably + private long jobId; + private String outputDirPath; + private String derivedFileInCaseDatabase; + + @Override + public void startUp(IngestJobContext context) throws IngestModuleException { + jobId = context.getJobId(); + if (refCounter.incrementAndGet(jobId) == 1) { + // Create an output directory for this job. + outputDirPath = Case.getCurrentCase().getModulesOutputDirAbsPath() + File.separator + moduleName; //NON-NLS + File outputDir = new File(outputDirPath); + if (outputDir.exists() == false) { + outputDir.mkdirs(); + } + } + } + + @Override + public ProcessResult process(Content dataSource, DataSourceIngestModuleProgress progressBar) { + if (refCounter.get(jobId) == 1) { + try { + // There will be two tasks: data source analysis and import of + // the results of the analysis. + progressBar.switchToDeterminate(2); + + // Do the analysis. The following sample code could be used to + // run an executable. In this case the executable would take + // two command line arguments, the path to the data source to be + // analyzed and the path to a results file to be generated. The + // results file would be an an XML file (see org.sleuthkit.autopsy.externalresults.autopsy_external_results.xsd) + // with instructions for the import of blackboard artifacts, + // derived files, and reports generated by the analysis. In this + // sample ingest module, the generation of the analysis results is + // simulated. + String resultsFilePath = outputDirPath + File.separator + String.format("job_%d_results.xml", jobId); + boolean haveRealExecutable = false; + if (haveRealExecutable) { + String dataSourcePath = dataSource.getImage().getPaths()[0]; + ExecUtil executor = new ExecUtil(); + executor.execute("some.exe", dataSourcePath, resultsFilePath); + } else { + generateSimulatedResults(resultsFilePath); + } + progressBar.progress(1); + + // Import the results of the analysis. + ExternalResultsXMLParser resultsParser = new ExternalResultsXMLParser(dataSource, resultsFilePath); + ExternalResults results = resultsParser.parse(); + List errors = resultsParser.getErrorInfo(); + ExternalResultsImporter importer = new ExternalResultsImporter(); + errors.addAll(importer.importResults(results)); + 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); + logger.log(Level.SEVERE, "Failed to simulate analysis and results import", ex); //NON-NLS + return ProcessResult.ERROR; + } + } + return ProcessResult.OK; + } + + private void generateSimulatedResults(String resultsFilePath) throws ParserConfigurationException, IOException, TransformerConfigurationException, TransformerException { + List derivedFilePaths = generateSimulatedDerivedFiles(); + List reportFilePaths = generateSimulatedReports(); + generateSimulatedResultsFile(derivedFilePaths, reportFilePaths, resultsFilePath); + } + + private List generateSimulatedDerivedFiles() throws IOException { + List filePaths = new ArrayList<>(); + String fileContents = "This is a simulated derived file."; + for (int i = 0; i < 2; ++i) { + String fileName = String.format("job_%d_derived_file_%d.txt", jobId, i); + filePaths.add(generateFile(fileName, fileContents.getBytes())); + if (i == 0) { + this.derivedFileInCaseDatabase = this.fileInCaseDatabase + "/" + fileName; + } + } + return filePaths; + } + + 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 { + String filePath = outputDirPath + File.separator + fileName; + File file = new File(filePath); + if (!file.exists()) { + file.createNewFile(); + } + try (FileOutputStream fileStream = new FileOutputStream(file)) { + fileStream.write(fileContents); + fileStream.flush(); + } + return filePath; + } + + 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(); + Document doc = docBuilder.newDocument(); + Element rootElement = doc.createElement(ExternalResultsXMLParser.TagNames.ROOT_ELEM.toString()); + doc.appendChild(rootElement); + + // Add a derived files list element to the root element. + Element derivedFilesListElement = doc.createElement(ExternalResultsXMLParser.TagNames.DERIVED_FILES_LIST_ELEM.toString()); + rootElement.appendChild(derivedFilesListElement); + + // 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 + // 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()); + derivedFilesListElement.appendChild(derivedFileElement); + Element localPathElement = doc.createElement(ExternalResultsXMLParser.TagNames.LOCAL_PATH_ELEM.toString()); + localPathElement.setTextContent(filePath); + derivedFileElement.appendChild(localPathElement); + Element parentPathElement = doc.createElement(ExternalResultsXMLParser.TagNames.PARENT_FILE_ELEM.toString()); + if (i == 0) { + parentPathElement.setTextContent(this.fileInCaseDatabase); + } else { + parentPathElement.setTextContent(this.derivedFileInCaseDatabase); + } + derivedFileElement.appendChild(parentPathElement); + } + + // Add an artifacts list element to the root element. + Element artifactsListElement = doc.createElement(ExternalResultsXMLParser.TagNames.ARTIFACTS_LIST_ELEM.toString()); + rootElement.appendChild(artifactsListElement); + + // Add an artifact element to the artifacts list element with the required + // artifact type attribute. A standard artifact type is used as the type + // attribute of this artifact element. + Element artifactElement = doc.createElement(ExternalResultsXMLParser.TagNames.ARTIFACT_ELEM.toString()); + artifactElement.setAttribute(ExternalResultsXMLParser.AttributeNames.TYPE_ATTR.toString(), ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getLabel()); + artifactsListElement.appendChild(artifactElement); + + // Add the required source file element to the artifact element. Note + // that source file must be either the local path of a derived file or a + // file in the case database. + Element fileElement = doc.createElement(ExternalResultsXMLParser.TagNames.SOURCE_FILE_ELEM.toString()); + fileElement.setTextContent(this.fileInCaseDatabase); + artifactElement.appendChild(fileElement); + + // Add an artifact attribute element to the artifact element. A standard + // artifact attribute type is used as the required type XML attribute of + // the artifact attribute element. + Element artifactAttrElement = doc.createElement(ExternalResultsXMLParser.TagNames.ATTRIBUTE_ELEM.toString()); + artifactAttrElement.setAttribute(ExternalResultsXMLParser.AttributeNames.TYPE_ATTR.toString(), ATTRIBUTE_TYPE.TSK_SET_NAME.getLabel()); + artifactElement.appendChild(artifactAttrElement); + + // Add the required value element to the artifact attribute element, + // with an optional type XML attribute of ExternalXML.VALUE_TYPE_TEXT, + // which is the default. + Element artifactAttributeValueElement = doc.createElement(ExternalResultsXMLParser.TagNames.VALUE_ELEM.toString()); + artifactAttributeValueElement.setTextContent("SampleInterestingFilesSet"); + artifactAttrElement.appendChild(artifactAttributeValueElement); + + // Add an optional source module element to the artifact attribute + // element. + Element artifactAttrSourceElement = doc.createElement(ExternalResultsXMLParser.TagNames.SOURCE_MODULE_ELEM.toString()); + artifactAttrSourceElement.setTextContent(moduleName); + artifactAttrElement.appendChild(artifactAttrSourceElement); + + // Add an artifact element with a user-defined type. + artifactElement = doc.createElement(ExternalResultsXMLParser.TagNames.ARTIFACT_ELEM.toString()); + artifactElement.setAttribute(ExternalResultsXMLParser.AttributeNames.TYPE_ATTR.toString(), "SampleArtifactType"); + artifactsListElement.appendChild(artifactElement); + + // Add the required source file element. + fileElement = doc.createElement(ExternalResultsXMLParser.TagNames.SOURCE_FILE_ELEM.toString()); + fileElement.setTextContent(this.derivedFileInCaseDatabase); + artifactElement.appendChild(fileElement); + + // Add artifact attribute elements with user-defined types to the + // artifact element, adding value elements of assorted types. + for (int i = 0; i < 4; ++i) { + artifactAttrElement = doc.createElement(ExternalResultsXMLParser.TagNames.ATTRIBUTE_ELEM.toString()); + artifactAttrElement.setAttribute(ExternalResultsXMLParser.AttributeNames.TYPE_ATTR.toString(), "SampleArtifactAttributeType"); + artifactElement.appendChild(artifactAttrElement); + artifactAttributeValueElement = doc.createElement(ExternalResultsXMLParser.TagNames.VALUE_ELEM.toString()); + switch (i) { + case 0: + artifactAttributeValueElement.setAttribute(ExternalResultsXMLParser.AttributeNames.TYPE_ATTR.toString(), ExternalResultsXMLParser.AttributeValues.VALUE_TYPE_TEXT.toString()); + artifactAttributeValueElement.setTextContent("One"); + break; + case 1: + artifactAttributeValueElement.setAttribute(ExternalResultsXMLParser.AttributeNames.TYPE_ATTR.toString(), ExternalResultsXMLParser.AttributeValues.VALUE_TYPE_INT32.toString()); + artifactAttributeValueElement.setTextContent("2"); + break; + case 2: + artifactAttributeValueElement.setAttribute(ExternalResultsXMLParser.AttributeNames.TYPE_ATTR.toString(), ExternalResultsXMLParser.AttributeValues.VALUE_TYPE_INT64.toString()); + artifactAttributeValueElement.setTextContent("3"); + break; + case 3: + artifactAttributeValueElement.setAttribute(ExternalResultsXMLParser.AttributeNames.TYPE_ATTR.toString(), ExternalResultsXMLParser.AttributeValues.VALUE_TYPE_DOUBLE.toString()); + artifactAttributeValueElement.setTextContent("4.0"); + break; + } + artifactAttrElement.appendChild(artifactAttributeValueElement); + } + + // Add a reports list element to the root element. + Element reportsListElement = doc.createElement(ExternalResultsXMLParser.TagNames.REPORTS_LIST_ELEM.toString()); + rootElement.appendChild(reportsListElement); + + // 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(); + DOMSource source = new DOMSource(doc); + StreamResult result = new StreamResult(new File(resultsFilePath)); + transformer.transform(source, result); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/examples/SampleExecutableIngestModuleFactory.java b/Core/src/org/sleuthkit/autopsy/examples/SampleExecutableIngestModuleFactory.java new file mode 100755 index 0000000000..2f92ebd597 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/examples/SampleExecutableIngestModuleFactory.java @@ -0,0 +1,79 @@ +/* + * Sample ingest module factory in the public domain. + * Feel free to use this as a template for your inget module factories. + * + * Contact: Brian Carrier [carrier sleuthkit [dot] org] + * + * This is free and unencumbered software released into the public domain. + * + * Anyone is free to copy, modify, publish, use, compile, sell, or + * distribute this software, either in source code form or as a compiled + * binary, for any purpose, commercial or non-commercial, and by any + * means. + * + * In jurisdictions that recognize copyright laws, the author or authors + * of this software dedicate any and all copyright interest in the + * software to the public domain. We make this dedication for the benefit + * of the public at large and to the detriment of our heirs and + * successors. We intend this dedication to be an overt act of + * relinquishment in perpetuity of all present and future rights to this + * software under copyright law. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +package org.sleuthkit.autopsy.examples; + +import org.openide.util.NbBundle; +import org.openide.util.lookup.ServiceProvider; +import static org.sleuthkit.autopsy.examples.SampleIngestModuleFactory.getModuleName; +import org.sleuthkit.autopsy.ingest.DataSourceIngestModule; +import org.sleuthkit.autopsy.ingest.IngestModuleFactory; +import org.sleuthkit.autopsy.ingest.IngestModuleFactoryAdapter; +import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings; + +/** + * A factory that creates sample executable data source ingest modules. + */ +@ServiceProvider(service = IngestModuleFactory.class) // Sample is discarded at runtime +public class SampleExecutableIngestModuleFactory extends IngestModuleFactoryAdapter { + + private static final String VERSION_NUMBER = "1.0.0"; + + // This class method allows the ingest module instances created by this + // factory to use the same display name that is provided to the Autopsy + // ingest framework by the factory. + static String getModuleName() { + return NbBundle.getMessage(SampleIngestModuleFactory.class, "SampleExecutableIngestModuleFactory.moduleName"); + } + + @Override + public String getModuleDisplayName() { + return getModuleName(); + } + + @Override + public String getModuleDescription() { + return NbBundle.getMessage(SampleIngestModuleFactory.class, "SampleExecutableIngestModuleFactory.moduleDescription"); + } + + @Override + public String getModuleVersionNumber() { + return VERSION_NUMBER; + } + + @Override + public boolean isDataSourceIngestModuleFactory() { + return true; + } + + @Override + public DataSourceIngestModule createDataSourceIngestModule(IngestModuleIngestJobSettings ingestOptions) { + return new SampleExecutableDataSourceIngestModule(); + } +} 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/Bundle.properties b/Core/src/org/sleuthkit/autopsy/externalresults/Bundle.properties new file mode 100644 index 0000000000..3a33f900fe --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/externalresults/Bundle.properties @@ -0,0 +1,10 @@ +# To change this template, choose Tools | Templates +# and open the template in the editor. + +ExternalResultsIngestModule.moduleName.text=External Results Importer +ExternalResultsIngestModule.properties.text=ExternalResultsImporter +ExternalResultsIngestModule.moduleDesc.text=Parse command line tool output to create blackboard entries and add files to the database. +ExternalResultsIngestModule.startUp.exception.importdir=Import directory does not exist and was not able to be created. +ExternalResultsIngestModule.process.exception.datasourcepath=Error getting the data source path. +ExternalResultsIngestModule.process.exception.run=Error running the external command: +ExternalResultsIngestModule.process.shutdown.finished=Finished \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResults.java b/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResults.java new file mode 100644 index 0000000000..06afcabe14 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResults.java @@ -0,0 +1,201 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2014 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.externalresults; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import org.sleuthkit.datamodel.Content; + +/** + * + */ +final public class ExternalResults { + + private final Content dataSource; + private final List artifacts = new ArrayList<>(); + private final List reports = new ArrayList<>(); + private final List derivedFiles = new ArrayList<>(); + + ExternalResults(Content dataSource) { + this.dataSource = dataSource; + } + + Content getDataSource() { + return this.dataSource; + } + + Artifact addArtifact(String type, String sourceFilePath) { + if (type.isEmpty()) { + throw new IllegalArgumentException("type argument is empty"); + } + if (sourceFilePath.isEmpty()) { + throw new IllegalArgumentException("source argument is empty"); + } + Artifact artifact = new Artifact(type, sourceFilePath); + artifacts.add(artifact); + return artifact; + } + + List getArtifacts() { + return Collections.unmodifiableList(artifacts); + } + + void addReport(String localPath, String sourceModuleName, String reportName) { + if (localPath.isEmpty()) { + throw new IllegalArgumentException("localPath argument is empty"); + } + if (sourceModuleName.isEmpty()) { + throw new IllegalArgumentException("sourceModuleName argument is empty"); + } + Report report = new Report(localPath, sourceModuleName, reportName); + reports.add(report); + } + + List getReports() { + return Collections.unmodifiableList(reports); + } + + void addDerivedFile(String localPath, String parentPath) { + if (localPath.isEmpty()) { + throw new IllegalArgumentException("localPath argument is empty"); + } + if (parentPath.isEmpty()) { + throw new IllegalArgumentException("parentPath argument is empty"); + } + DerivedFile file = new DerivedFile(localPath, parentPath); + derivedFiles.add(file); + } + + List getDerivedFiles() { + return Collections.unmodifiableList(derivedFiles); + } + + static final class Artifact { + + private final String type; + private final String sourceFilePath; + private final ArrayList attributes = new ArrayList<>(); + + Artifact(String type, String sourceFilePath) { + this.type = type; + this.sourceFilePath = sourceFilePath; + } + + String getType() { + return type; + } + + String getSourceFilePath() { + return sourceFilePath; + } + + void addAttribute(String type, String value, String valueType, String sourceModule) { + if (type.isEmpty()) { + throw new IllegalArgumentException("type argument is empty"); + } + if (value.isEmpty()) { + throw new IllegalArgumentException("value argument is empty"); + } + if (valueType.isEmpty()) { + throw new IllegalArgumentException("value type argument is empty"); + } + attributes.add(new ArtifactAttribute(type, value, valueType, sourceModule)); + } + + List getAttributes() { + return Collections.unmodifiableList(attributes); + } + } + + static final class ArtifactAttribute { + + private final String type; + private final String valueType; + private final String value; + private final String sourceModule; + + private ArtifactAttribute(String type, String value, String valueType, String sourceModule) { + this.type = type; + this.value = value; + this.valueType = valueType; + this.sourceModule = sourceModule; + } + + String getType() { + return type; + } + + String getValue() { + return value; + } + + String getValueType() { + return valueType; + } + + String getSourceModule() { + return sourceModule; + } + } + + static final class Report { + + private final String localPath; + private final String sourceModuleName; + private final String reportName; + + Report(String localPath, String sourceModuleName, String displayName) { + this.localPath = localPath; + this.sourceModuleName = sourceModuleName; + this.reportName = displayName; + } + + String getLocalPath() { + return localPath; + } + + String getSourceModuleName() { + return sourceModuleName; + } + + String getReportName() { + return reportName; + } + } + + static final class DerivedFile { + + private final String localPath; + private final String parentPath; + + DerivedFile(String localPath, String parentPath) { + this.localPath = localPath; + this.parentPath = parentPath; + } + + String getLocalPath() { + return localPath; + } + + String getParentPath() { + return parentPath; + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResultsImporter.java b/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResultsImporter.java new file mode 100644 index 0000000000..67cc2bb804 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResultsImporter.java @@ -0,0 +1,263 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2014 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this localFile 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.externalresults; + +import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.logging.Level; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.services.FileManager; +import org.sleuthkit.autopsy.coreutils.ErrorInfo; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.ingest.IngestServices; +import org.sleuthkit.autopsy.ingest.ModuleContentEvent; +import org.sleuthkit.autopsy.ingest.ModuleDataEvent; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.DerivedFile; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Uses a standard representation of results data (e.g., artifacts, derived + * files, reports) to import results generated by a process external to Autopsy + * into Autopsy. + */ +public final class ExternalResultsImporter { + + private static final Logger logger = Logger.getLogger(ExternalResultsImporter.class.getName()); + private static final HashSet standardArtifactTypeIds = new HashSet<>(); + private final List errors = new ArrayList<>(); + + static { + for (BlackboardArtifact.ARTIFACT_TYPE artifactType : BlackboardArtifact.ARTIFACT_TYPE.values()) { + standardArtifactTypeIds.add(artifactType.getTypeID()); + } + } + + /** + * Import results generated by a process external to Autopsy into Autopsy. + * + * @param results A standard representation of results data (e.g., + * artifacts, derived files, reports)from the data source. + * @return A collection of error messages, possibly empty. The error + * messages are already logged but are provided to allow the caller to + * provide additional user feedback via the Autopsy user interface. + */ + public List importResults(ExternalResults results) { + // Import files first, they may be artifactData sources. + importDerivedFiles(results); + importArtifacts(results); + importReports(results); + List importErrors = new ArrayList(this.errors); + this.errors.clear(); + return importErrors; + } + + private void importDerivedFiles(ExternalResults results) { + FileManager fileManager = Case.getCurrentCase().getServices().getFileManager(); + for (ExternalResults.DerivedFile fileData : results.getDerivedFiles()) { + String localPath = fileData.getLocalPath(); + try { + File localFile = new File(localPath); + if (localFile.exists()) { + String relativePath = this.getPathRelativeToCaseFolder(localPath); + if (!relativePath.isEmpty()) { + String parentFilePath = fileData.getParentPath(); + 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. + true, parentFile, + "", "", "", ""); // Not currently providing derivation info for derived files from external processes. + IngestServices.getInstance().fireModuleContentEvent(new ModuleContentEvent(derivedFile)); + } else { + String errorMessage = String.format("Could not import derived file at %s, parent file %s not found", localPath, parentFilePath); + ExternalResultsImporter.logger.log(Level.SEVERE, errorMessage); + this.errors.add(new ErrorInfo(ExternalResultsImporter.class.getName(), errorMessage)); + } + } + } else { + String errorMessage = String.format("Could not import derived file at %s, file does not exist", localPath); + 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 derived file at %s, error querying/updating case database", localPath); + ExternalResultsImporter.logger.log(Level.SEVERE, errorMessage, ex); + this.errors.add(new ErrorInfo(ExternalResultsImporter.class.getName(), errorMessage, ex)); + } + } + } + + private void importArtifacts(ExternalResults results) { + SleuthkitCase caseDb = Case.getCurrentCase().getSleuthkitCase(); + for (ExternalResults.Artifact artifactData : results.getArtifacts()) { + try { + // Add the artifact to the case database. + int artifactTypeId = caseDb.getArtifactTypeID(artifactData.getType()); + if (artifactTypeId == -1) { + artifactTypeId = caseDb.addArtifactType(artifactData.getType(), artifactData.getType()); + } + 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.getAttrTypeID(attributeData.getType()); + if (attributeTypeId == -1) { + attributeTypeId = caseDb.addAttrType(attributeData.getType(), attributeData.getType()); + } + switch (attributeData.getValueType()) { + case "text": //NON-NLS + attributes.add(new BlackboardAttribute(attributeTypeId, attributeData.getSourceModule(), attributeData.getValue())); + break; + case "int32": //NON-NLS + int intValue = Integer.parseInt(attributeData.getValue()); + attributes.add(new BlackboardAttribute(attributeTypeId, attributeData.getSourceModule(), intValue)); + break; + case "int64": //NON-NLS + long longValue = Long.parseLong(attributeData.getValue()); + attributes.add(new BlackboardAttribute(attributeTypeId, attributeData.getSourceModule(), longValue)); + break; + case "double": //NON-NLS + double doubleValue = Double.parseDouble(attributeData.getValue()); + attributes.add(new BlackboardAttribute(attributeTypeId, attributeData.getSourceModule(), doubleValue)); + break; + default: + String errorMessage = String.format("Could not import %s attribute, value = %s, for %s artifact from %s, unrecognized attribute value type: %s", + attributeData.getType(), attributeData.getValue(), + artifactData.getType(), artifactData.getSourceFilePath(), + attributeData.getValueType()); + ExternalResultsImporter.logger.log(Level.SEVERE, errorMessage); + this.errors.add(new ErrorInfo(ExternalResultsImporter.class.getName(), errorMessage)); + break; + } + } + artifact.addAttributes(attributes); + + if (standardArtifactTypeIds.contains(artifactTypeId)) { + 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()); + ExternalResultsImporter.logger.log(Level.SEVERE, errorMessage, ex); + this.errors.add(new ErrorInfo(ExternalResultsImporter.class.getName(), errorMessage, ex)); + } + } + } + + private void importReports(ExternalResults results) { + for (ExternalResults.Report report : results.getReports()) { + String reportPath = report.getLocalPath(); + try { + File reportFile = new File(reportPath); + if (reportFile.exists()) { + 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); + this.errors.add(new ErrorInfo(ExternalResultsImporter.class.getName(), errorMessage)); + } + } catch (TskCoreException ex) { + String errorMessage = String.format("Could not import report at %s, error updating case database", reportPath); + ExternalResultsImporter.logger.log(Level.SEVERE, errorMessage, ex); + this.errors.add(new ErrorInfo(ExternalResultsImporter.class.getName(), errorMessage, ex)); + } + } + } + + private AbstractFile findFileInCaseDatabase(String filePath) throws TskCoreException { + AbstractFile file = null; + // Split the path into the file name and the parent path. + String fileName = filePath; + String parentPath = ""; + int charPos = filePath.lastIndexOf("/"); + if (charPos >= 0) { + fileName = filePath.substring(charPos + 1); + parentPath = filePath.substring(0, charPos + 1); + } + // Find the file. + String condition = "name='" + fileName + "' AND parent_path='" + parentPath + "'"; //NON-NLS + List files = Case.getCurrentCase().getSleuthkitCase().findAllFilesWhere(condition); + if (!files.isEmpty()) { + 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); + } + } + return file; + } + + private String getPathRelativeToCaseFolder(String localPath) { + String relativePath = ""; + String caseDirectoryPath = Case.getCurrentCase().getCaseDirectory(); + Path path = Paths.get(localPath); + if (path.isAbsolute()) { + Path pathBase = Paths.get(caseDirectoryPath); + try { + Path pathRelative = pathBase.relativize(path); + relativePath = pathRelative.toString(); + } catch (IllegalArgumentException ex) { + String errorMessage = String.format("Did not convert %s to relative path, not in a subdirectory of case directory %s", + localPath, caseDirectoryPath); + this.recordError(errorMessage, ex); + } + } else { + String errorMessage = String.format("Expected %s to be an absolute path to a file in a subdirectory of case directory %s", + localPath, caseDirectoryPath); + this.recordError(errorMessage); + } + return relativePath; + } + +// private static boolean isStandardArtifactType(int artifactTypeId) { +// for (BlackboardArtifact.ARTIFACT_TYPE art : BlackboardArtifact.ARTIFACT_TYPE.values()) { +// if (art.getTypeID() == artifactTypeId) { +// return true; +// } +// } +// return false; +// } +// + private void recordError(String errorMessage) { + ExternalResultsImporter.logger.log(Level.SEVERE, errorMessage); + this.errors.add(new ErrorInfo(this.getClass().getName(), errorMessage)); + } + + private void recordError(String errorMessage, Exception ex) { + ExternalResultsImporter.logger.log(Level.SEVERE, errorMessage, ex); + this.errors.add(new ErrorInfo(this.getClass().getName(), errorMessage)); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResultsParser.java b/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResultsParser.java new file mode 100755 index 0000000000..2914ee4607 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResultsParser.java @@ -0,0 +1,49 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2014 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.externalresults; + +import java.util.List; +import org.sleuthkit.autopsy.coreutils.ErrorInfo; + +/** + * Interface for parsers that convert some representation of results data (e.g., + * artifacts, derived files, reports) generated by a process external to Autopsy + * into a form ready for import into Autopsy. + */ +public interface ExternalResultsParser { + + /** + * Converts some representation of results data generated by a process + * external to Autopsy and supplied to the parser via its constructor into a + * form ready for import into Autopsy. + * + * @return External results data in a form ready for import into Autopsy. + */ + ExternalResults parse(); + + /** + * Gets error information describing any errors encountered while parsing + * the input results representation. + * + * @return A collection of error messages, possibly empty. The error + * messages are already logged but are provided to allow the caller to + * provide additional user feedback via the Autopsy user interface. + */ + List getErrorInfo(); +} diff --git a/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResultsXMLParser.java b/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResultsXMLParser.java new file mode 100644 index 0000000000..0465cf9404 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResultsXMLParser.java @@ -0,0 +1,343 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2014 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.externalresults; + +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.sleuthkit.autopsy.coreutils.ErrorInfo; +import org.sleuthkit.autopsy.coreutils.XMLUtil; +import org.sleuthkit.datamodel.Content; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +/** + * Parses an XML representation of of results data (e.g., artifacts, derived + * files, reports) generated by a process external to Autopsy. + */ +public final class ExternalResultsXMLParser implements ExternalResultsParser { + + private static final Logger logger = Logger.getLogger(ExternalResultsXMLParser.class.getName()); + private static final String XSD_FILE = "autopsy_external_results.xsd"; //NON-NLS + private final Content dataSource; + private final String resultsFilePath; + private ExternalResults resultsData; + private List errors = new ArrayList<>(); + + /** + * Tag names for an external results XML file. + */ + public enum TagNames { + + ROOT_ELEM("autopsy_results"), //NON-NLS + DERIVED_FILES_LIST_ELEM("derived_files"), //NON-NLS + DERIVED_FILE_ELEM("derived_file"), //NON-NLS + LOCAL_PATH_ELEM("local_path"), //NON-NLS + PARENT_FILE_ELEM("parent_file"), //NON-NLS + ARTIFACTS_LIST_ELEM("artifacts"), //NON-NLS + ARTIFACT_ELEM("artifact"), //NON-NLS + SOURCE_FILE_ELEM("source_file"), //NON-NLS + ATTRIBUTE_ELEM("attribute"), //NON-NLS + VALUE_ELEM("value"), //NON-NLS + SOURCE_MODULE_ELEM("source_module"), //NON-NLS + REPORTS_LIST_ELEM("reports"), //NON-NLS + REPORT_ELEM("report"), //NON-NLS + REPORT_NAME_ELEM("report_name"); //NON-NLS + private final String text; + + private TagNames(final String text) { + this.text = text; + } + + @Override + public String toString() { + return this.text; + } + } + + /** + * Attribute names for an external results XML file. + */ + public enum AttributeNames { + + TYPE_ATTR("type"); //NON-NLS + private final String text; + + private AttributeNames(final String text) { + this.text = text; + } + + @Override + public String toString() { + return this.text; + } + } + + /** + * Attribute values for an external results XML file. + */ + public enum AttributeValues { + + VALUE_TYPE_TEXT("text"), //NON-NLS + VALUE_TYPE_INT32("int32"), //NON-NLS + VALUE_TYPE_INT64("int64"), //NON-NLS + VALUE_TYPE_DOUBLE("double"); //NON-NLS + private final String text; + + private AttributeValues(final String text) { + this.text = text; + } + + @Override + public String toString() { + return this.text; + } + } + + /** + * Constructor. + * + * @param importFilePath Full path of the results file to be parsed. + */ + public ExternalResultsXMLParser(Content dataSource, String resultsFilePath) { + this.dataSource = dataSource; + this.resultsFilePath = resultsFilePath; + } + + @Override + public ExternalResults parse() { + this.errors.clear(); + this.resultsData = new ExternalResults(dataSource); + try { + // Note that XMLUtil.loadDoc() logs a warning if the file does not + // conform to the XSD, but still returns a Document object. Until + // this behavior is improved, validation is still required. If + // XMLUtil.loadDoc() does return null, it failed to load the + // document and it logged the error. + final Document doc = XMLUtil.loadDoc(ExternalResultsXMLParser.class, this.resultsFilePath, XSD_FILE); + if (doc != null) { + final Element rootElem = doc.getDocumentElement(); + if (rootElem != null && rootElem.getNodeName().equals(TagNames.ROOT_ELEM.toString())) { + parseDerivedFiles(rootElem); + parseArtifacts(rootElem); + parseReports(rootElem); + } else { + String errorMessage = String.format("Did not find %s root element of %s", TagNames.ROOT_ELEM.toString(), this.resultsFilePath); + recordError(errorMessage); + } + } + } catch (Exception ex) { + String errorMessage = String.format("Error parsing %s", this.resultsFilePath); + recordError(errorMessage, ex); + } + return this.resultsData; + } + + @Override + public List getErrorInfo() { + return new ArrayList<>(this.errors); + } + + private void parseDerivedFiles(Element rootElement) { + // Get the derived file lists. + NodeList derivedFilesListNodes = rootElement.getElementsByTagName(TagNames.DERIVED_FILES_LIST_ELEM.toString()); + for (int i = 0; i < derivedFilesListNodes.getLength(); ++i) { + Element derivedFilesListElem = (Element) derivedFilesListNodes.item(i); + // Get the derived files. + NodeList derivedFileNodes = derivedFilesListElem.getElementsByTagName(TagNames.DERIVED_FILE_ELEM.toString()); + for (int j = 0; j < derivedFileNodes.getLength(); ++j) { + Element derivedFileElem = (Element) derivedFileNodes.item(j); + // Get the local path of the derived file. + String path = getChildElementContent(derivedFileElem, TagNames.LOCAL_PATH_ELEM.toString(), true); + if (path.isEmpty()) { + continue; + } + // Get the parent file of the derived file. + String parentFile = getChildElementContent((Element) derivedFileNodes.item(j), TagNames.PARENT_FILE_ELEM.toString(), true); + if (parentFile.isEmpty()) { + continue; + } + this.resultsData.addDerivedFile(path, parentFile); + } + } + } + + private void parseArtifacts(final Element root) { + // Get the artifact lists. + NodeList artifactsListNodes = root.getElementsByTagName(TagNames.ARTIFACTS_LIST_ELEM.toString()); + for (int i = 0; i < artifactsListNodes.getLength(); ++i) { + Element artifactsListElem = (Element) artifactsListNodes.item(i); + // Get the artifacts. + NodeList artifactNodes = artifactsListElem.getElementsByTagName(TagNames.ARTIFACT_ELEM.toString()); + for (int j = 0; j < artifactNodes.getLength(); ++j) { + Element artifactElem = (Element) artifactNodes.item(j); + // Get the artifact type. + final String type = getElementAttributeValue(artifactElem, AttributeNames.TYPE_ATTR.toString()); + if (!type.isEmpty()) { + // Get the source file of the artifact and the attributes, + // if any. + final String sourceFilePath = this.getChildElementContent((Element) artifactElem, TagNames.SOURCE_FILE_ELEM.toString(), true); + if (!sourceFilePath.isEmpty()) { + ExternalResults.Artifact artifact = this.resultsData.addArtifact(type, sourceFilePath); + parseArtifactAttributes(artifactElem, artifact); + } + } + } + } + } + + private void parseArtifactAttributes(final Element artifactElem, ExternalResults.Artifact artifact) { + // Get the artifact attributes. + NodeList attributeNodesList = artifactElem.getElementsByTagName(TagNames.ATTRIBUTE_ELEM.toString()); + for (int i = 0; i < attributeNodesList.getLength(); ++i) { + Element attributeElem = (Element) attributeNodesList.item(i); + final String type = getElementAttributeValue(attributeElem, AttributeNames.TYPE_ATTR.toString()); + if (type.isEmpty()) { + continue; + } + // Get the value of the artifact attribute. + Element valueElem = this.getChildElement(attributeElem, TagNames.VALUE_ELEM.toString()); + if (valueElem == null) { + continue; + } + final String value = valueElem.getTextContent(); + if (value.isEmpty()) { + String errorMessage = String.format("Found %s element that has no content in %s", + TagNames.VALUE_ELEM.toString(), this.resultsFilePath); + recordError(errorMessage); + continue; + } + // Get the value type. + String valueType = parseArtifactAttributeValueType(valueElem); + if (valueType.isEmpty()) { + continue; + } + // Get the optional source module. + String sourceModule = this.getChildElementContent(attributeElem, TagNames.SOURCE_MODULE_ELEM.toString(), false); + // Add the attribute to the artifact. + artifact.addAttribute(type, value, valueType, sourceModule); + } + } + + private String parseArtifactAttributeValueType(Element valueElem) { + String valueType = valueElem.getAttribute(AttributeNames.TYPE_ATTR.toString()); + if (valueType.isEmpty()) { + // Default to text. + valueType = AttributeValues.VALUE_TYPE_TEXT.toString(); + } else if (!valueType.equals(AttributeValues.VALUE_TYPE_TEXT.toString()) + && !valueType.equals(AttributeValues.VALUE_TYPE_DOUBLE.toString()) + && !valueType.equals(AttributeValues.VALUE_TYPE_INT32.toString()) + && !valueType.equals(AttributeValues.VALUE_TYPE_INT64.toString())) { + String errorMessage = String.format("Found unrecognized value %s for %s attribute of %s element", + valueType, + AttributeNames.TYPE_ATTR.toString(), + TagNames.VALUE_ELEM.toString()); + this.recordError(errorMessage); + valueType = ""; + } + return valueType; + } + + private void parseReports(Element root) { + // Get the report lists. + NodeList reportsListNodes = root.getElementsByTagName(TagNames.REPORTS_LIST_ELEM.toString()); + for (int i = 0; i < reportsListNodes.getLength(); ++i) { + Element reportsListElem = (Element) reportsListNodes.item(i); + // Get the reports. + NodeList reportNodes = reportsListElem.getElementsByTagName(TagNames.REPORT_ELEM.toString()); + for (int j = 0; j < reportNodes.getLength(); ++j) { + Element reportElem = (Element) reportNodes.item(j); + // Get the local path. + String path = getChildElementContent(reportElem, TagNames.LOCAL_PATH_ELEM.toString(), true); + if (path.isEmpty()) { + continue; + } + // 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); + } + } + } + + private String getElementAttributeValue(Element element, String attributeName) { + final String attributeValue = element.getAttribute(attributeName); + if (attributeValue.isEmpty()) { + logger.log(Level.SEVERE, "Found {0} element missing {1} attribute in {2}", new Object[]{ + element.getTagName(), + attributeName, + this.resultsFilePath}); + } + return attributeValue; + } + + private String getChildElementContent(Element parentElement, String childElementTagName, boolean required) { + String content = ""; + Element childElement = this.getChildElement(parentElement, childElementTagName); + if (childElement != null) { + content = childElement.getTextContent(); + if (content.isEmpty()) { + String errorMessage = String.format("Found %s element with %s child element that has no content in %s", + parentElement.getTagName(), + childElementTagName, + this.resultsFilePath); + this.recordError(errorMessage); + } + } else if (required) { + String errorMessage = String.format("Found %s element missing %s child element in %s", + parentElement.getTagName(), + childElementTagName, + this.resultsFilePath); + this.recordError(errorMessage); + } + return content; + } + + private Element getChildElement(Element parentElement, String childElementTagName) { + Element childElem = null; + NodeList childNodes = parentElement.getElementsByTagName(childElementTagName); + if (childNodes.getLength() > 0) { + childElem = (Element) childNodes.item(0); + if (childNodes.getLength() > 1) { + String errorMessage = String.format("Found multiple %s child elements for %s element in %s, ignoring all but first occurrence", + childElementTagName, + parentElement.getTagName(), + this.resultsFilePath); + this.recordError(errorMessage); + } + } + return childElem; + } + + private void recordError(String errorMessage) { + ExternalResultsXMLParser.logger.log(Level.SEVERE, errorMessage); + this.errors.add(new ErrorInfo(this.getClass().getSimpleName(), errorMessage)); + } + + private void recordError(String errorMessage, Exception ex) { + ExternalResultsXMLParser.logger.log(Level.SEVERE, errorMessage, ex); + this.errors.add(new ErrorInfo(this.getClass().getSimpleName(), errorMessage, ex)); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/externalresults/autopsy_external_results.xsd b/Core/src/org/sleuthkit/autopsy/externalresults/autopsy_external_results.xsd new file mode 100644 index 0000000000..1dfd931e47 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/externalresults/autopsy_external_results.xsd @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/images/report_16.png b/Core/src/org/sleuthkit/autopsy/images/report_16.png new file mode 100755 index 0000000000..433fb0447e Binary files /dev/null and b/Core/src/org/sleuthkit/autopsy/images/report_16.png differ diff --git a/Core/src/org/sleuthkit/autopsy/ingest/DataSourceIngestModule.java b/Core/src/org/sleuthkit/autopsy/ingest/DataSourceIngestModule.java index b94c037e08..a0eac84a66 100755 --- a/Core/src/org/sleuthkit/autopsy/ingest/DataSourceIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/DataSourceIngestModule.java @@ -31,9 +31,8 @@ public interface DataSourceIngestModule extends IngestModule { * and shutDown(). * * @param dataSource The data source to process. - * @param statusHelper A status helper to be used to report progress and - * detect ingest job cancellation. + * @param progressBar A progress bar to be used to report progress. * @return A result code indicating success or failure of the processing. */ - ProcessResult process(Content dataSource, DataSourceIngestModuleProgress statusHelper); + ProcessResult process(Content dataSource, DataSourceIngestModuleProgress progressBar); } \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestJobContext.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestJobContext.java index b23077856d..eb30235c62 100755 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestJobContext.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestJobContext.java @@ -60,7 +60,7 @@ public final class IngestJobContext { * * @param files The files to be processed by the file ingest pipeline. */ - public void addFiles(List files) { + public void scheduleFiles(List files) { for (AbstractFile file : files) { try { FileIngestTaskScheduler.getInstance().scheduleTask(ingestJob, file); diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleFactoryLoader.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleFactoryLoader.java index c40f9e3e4f..8e331598cb 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleFactoryLoader.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleFactoryLoader.java @@ -66,8 +66,9 @@ final class IngestModuleFactoryLoader { } } - // Kick out the sample modules factory. + // Kick out the sample module factories. moduleFactoriesByClass.remove("org.sleuthkit.autopsy.examples.SampleIngestModuleFactory"); + moduleFactoriesByClass.remove("org.sleuthkit.autopsy.examples.SampleExecutableIngestModuleFactory"); // Do the core ingest module ordering hack described above. ArrayList coreModuleOrdering = new ArrayList() { diff --git a/Core/src/org/sleuthkit/autopsy/modules/sevenzip/SevenZipIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/sevenzip/SevenZipIngestModule.java index 8d4491c458..35c5685ca0 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/sevenzip/SevenZipIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/sevenzip/SevenZipIngestModule.java @@ -70,9 +70,9 @@ public final class SevenZipIngestModule implements FileIngestModule { private static final Logger logger = Logger.getLogger(SevenZipIngestModule.class.getName()); private IngestServices services = IngestServices.getInstance(); static final String[] SUPPORTED_EXTENSIONS = {"zip", "rar", "arj", "7z", "7zip", "gzip", "gz", "bzip2", "tar", "tgz",}; // "iso"}; NON-NLS - private String unpackDir; //relative to the case, to store in db - private String unpackDirPath; //absolute, to extract to - private FileManager fileManager; + private String moduleDirRelative; //relative to the case, to store in db + private String moduleDirAbsolute; //absolute, to extract to + //encryption type strings private static final String ENCRYPTION_FILE_LEVEL = NbBundle.getMessage(SevenZipIngestModule.class, "SevenZipIngestModule.encryptionFileLevel"); @@ -103,22 +103,21 @@ public final class SevenZipIngestModule implements FileIngestModule { final Case currentCase = Case.getCurrentCase(); - unpackDir = Case.getModulesOutputDirRelPath() + File.separator + ArchiveFileExtractorModuleFactory.getModuleName(); - unpackDirPath = currentCase.getModulesOutputDirAbsPath() + File.separator + ArchiveFileExtractorModuleFactory.getModuleName(); + moduleDirRelative = Case.getModulesOutputDirRelPath() + File.separator + ArchiveFileExtractorModuleFactory.getModuleName(); + moduleDirAbsolute = currentCase.getModulesOutputDirAbsPath() + File.separator + ArchiveFileExtractorModuleFactory.getModuleName(); - fileManager = currentCase.getServices().getFileManager(); - - File unpackDirPathFile = new File(unpackDirPath); + + File unpackDirPathFile = new File(moduleDirAbsolute); if (!unpackDirPathFile.exists()) { try { unpackDirPathFile.mkdirs(); } catch (SecurityException e) { - logger.log(Level.SEVERE, "Error initializing output dir: " + unpackDirPath, e); //NON-NLS + logger.log(Level.SEVERE, "Error initializing output dir: " + moduleDirAbsolute, e); //NON-NLS String msg = NbBundle.getMessage(this.getClass(), "SevenZipIngestModule.init.errInitModule.msg", ArchiveFileExtractorModuleFactory.getModuleName()); String details = NbBundle.getMessage(this.getClass(), "SevenZipIngestModule.init.errInitModule.details", - unpackDirPath, e.getMessage()); + moduleDirAbsolute, e.getMessage()); services.postMessage(IngestMessage.createErrorMessage(ArchiveFileExtractorModuleFactory.getModuleName(), msg, details)); throw e; } @@ -177,8 +176,10 @@ public final class SevenZipIngestModule implements FileIngestModule { List unpackedFiles = unpack(abstractFile); if (!unpackedFiles.isEmpty()) { - sendNewFilesEvent(abstractFile, unpackedFiles); - context.addFiles(unpackedFiles); + //currently sending a single event for all new files + services.fireModuleContentEvent(new ModuleContentEvent(abstractFile)); + + context.scheduleFiles(unpackedFiles); } return ProcessResult.OK; @@ -190,10 +191,6 @@ public final class SevenZipIngestModule implements FileIngestModule { refCounter.decrementAndGet(jobId); } - private void sendNewFilesEvent(AbstractFile archive, List unpackedFiles) { - //currently sending a single event for all new files - services.fireModuleContentEvent(new ModuleContentEvent(archive)); - } /** * Get local relative path to the unpacked archive root @@ -212,7 +209,7 @@ public final class SevenZipIngestModule implements FileIngestModule { * @return */ private String getLocalRootAbsPath(String localRootRelPath) { - return unpackDirPath + File.separator + localRootRelPath; + return moduleDirAbsolute + File.separator + localRootRelPath; } /** @@ -315,8 +312,8 @@ public final class SevenZipIngestModule implements FileIngestModule { final ISimpleInArchive simpleInArchive = inArchive.getSimpleInterface(); //setup the archive local root folder - final String uniqueFileName = getUniqueName(archiveFile); - final String localRootAbsPath = getLocalRootAbsPath(uniqueFileName); + final String uniqueArchiveFileName = getUniqueName(archiveFile); + final String localRootAbsPath = getLocalRootAbsPath(uniqueArchiveFileName); final File localRoot = new File(localRootAbsPath); if (!localRoot.exists()) { try { @@ -329,15 +326,16 @@ public final class SevenZipIngestModule implements FileIngestModule { } //initialize tree hierarchy to keep track of unpacked file structure - UnpackedTree uTree = new UnpackedTree(unpackDir + "/" + uniqueFileName, archiveFile, fileManager); + UnpackedTree unpackedTree = new UnpackedTree(moduleDirRelative + "/" + uniqueArchiveFileName, archiveFile); long freeDiskSpace = services.getFreeDiskSpace(); //unpack and process every item in archive int itemNumber = 0; for (ISimpleInArchiveItem item : simpleInArchive.getArchiveItems()) { - String extractedPath = item.getPath(); - if (extractedPath == null || extractedPath.isEmpty()) { + String pathInArchive = item.getPath(); + + if (pathInArchive == null || pathInArchive.isEmpty()) { //some formats (.tar.gz) may not be handled correctly -- file in archive has no name/path //handle this for .tar.gz and tgz but assuming the child is tar, //otherwise, unpack using itemNumber as name @@ -360,18 +358,18 @@ public final class SevenZipIngestModule implements FileIngestModule { } if (useName == null) { - extractedPath = "/" + archName + "/" + Integer.toString(itemNumber); + pathInArchive = "/" + archName + "/" + Integer.toString(itemNumber); } else { - extractedPath = "/" + useName; + pathInArchive = "/" + useName; } String msg = NbBundle.getMessage(this.getClass(), "SevenZipIngestModule.unpack.unknownPath.msg", - archiveFile.getName(), extractedPath); + archiveFile.getName(), pathInArchive); logger.log(Level.WARNING, msg); } ++itemNumber; - logger.log(Level.INFO, "Extracted item path: {0}", extractedPath); //NON-NLS + logger.log(Level.INFO, "Extracted item path: {0}", pathInArchive); //NON-NLS //check if possible zip bomb if (isZipBombArchiveItemCheck(archiveFile.getName(), item)) { @@ -379,9 +377,9 @@ public final class SevenZipIngestModule implements FileIngestModule { } //find this node in the hierarchy, create if needed - UnpackedTree.Data uNode = uTree.find(extractedPath); + UnpackedTree.UnpackedNode unpackedNode = unpackedTree.addNode(pathInArchive); - String fileName = uNode.getFileName(); + String fileName = unpackedNode.getFileName(); //update progress bar progress.progress(archiveFile.getName() + ": " + fileName, processedItems); @@ -394,7 +392,7 @@ public final class SevenZipIngestModule implements FileIngestModule { final boolean isDir = item.isFolder(); if (isEncrypted) { - logger.log(Level.WARNING, "Skipping encrypted file in archive: {0}", extractedPath); //NON-NLS + logger.log(Level.WARNING, "Skipping encrypted file in archive: {0}", pathInArchive); //NON-NLS hasEncrypted = true; continue; } else { @@ -423,9 +421,11 @@ public final class SevenZipIngestModule implements FileIngestModule { } } - final String localFileRelPath = uniqueFileName + File.separator + extractedPath; + final String uniqueExtractedName = uniqueArchiveFileName + File.separator + (item.getItemIndex() / 1000) + File.separator + item.getItemIndex() + new File(pathInArchive).getName(); + //final String localRelPath = unpackDir + File.separator + localFileRelPath; - final String localAbsPath = unpackDirPath + File.separator + localFileRelPath; + final String localRelPath = moduleDirRelative + File.separator + uniqueExtractedName; + final String localAbsPath = moduleDirAbsolute + File.separator + uniqueExtractedName; //create local dirs and empty files before extracted File localFile = new java.io.File(localAbsPath); @@ -443,10 +443,15 @@ public final class SevenZipIngestModule implements FileIngestModule { } } } catch (SecurityException e) { - logger.log(Level.SEVERE, "Error setting up output path for unpacked file: {0}", extractedPath); //NON-NLS + logger.log(Level.SEVERE, "Error setting up output path for unpacked file: {0}", pathInArchive); //NON-NLS //TODO consider bail out / msg to the user } } + + // skip the rest of this loop if we couldn't create the file + if (localFile.exists() == false) { + continue; + } final Date createTime = item.getCreationTime(); final Date accessTime = item.getLastAccessTime(); @@ -456,8 +461,8 @@ public final class SevenZipIngestModule implements FileIngestModule { final long accesstime = accessTime == null ? 0L : accessTime.getTime() / 1000; //record derived data in unode, to be traversed later after unpacking the archive - uNode.addDerivedInfo(size, !isDir, - 0L, createtime, accesstime, modtime); + unpackedNode.addDerivedInfo(size, !isDir, + 0L, createtime, accesstime, modtime, localRelPath); //unpack locally if a file if (!isDir) { @@ -477,11 +482,13 @@ public final class SevenZipIngestModule implements FileIngestModule { //update units for progress bar ++processedItems; - } //for every item in archive - + } + + // add them to the DB. We wait until the end so that we have the metadata on all of the + // intermediate nodes since the order is not guaranteed try { - uTree.createDerivedFiles(); - unpackedFiles = uTree.getAllFileObjects(); + unpackedTree.addDerivedFilesToCase(); + unpackedFiles = unpackedTree.getAllFileObjects(); //check if children are archives, update archive depth tracking for (AbstractFile unpackedFile : unpackedFiles) { @@ -658,7 +665,7 @@ public final class SevenZipIngestModule implements FileIngestModule { } /** - * Representation of local directory tree of unpacked archive. Used to track + * Representation of the files in the archive. Used to track * of local tree file hierarchy, archive depth, and files created to easily * and reliably get parent AbstractFile for unpacked file. So that we don't * have to depend on type of traversal of unpacked files handed to us by @@ -666,27 +673,30 @@ public final class SevenZipIngestModule implements FileIngestModule { */ private class UnpackedTree { - final String localPathRoot; - final Data root; //dummy root to hold children - final FileManager fileManager; + final UnpackedNode rootNode; - UnpackedTree(String localPathRoot, AbstractFile archiveRoot, FileManager fileManager) { - this.localPathRoot = localPathRoot; - this.fileManager = fileManager; - this.root = new Data(); - this.root.setFile(archiveRoot); - this.root.setFileName(archiveRoot.getName()); - this.root.localRelPath = localPathRoot; + /** + * + * @param localPathRoot Path in module output folder that files will be saved to + * @param archiveFile Archive file being extracted + * @param fileManager + */ + UnpackedTree(String localPathRoot, AbstractFile archiveFile) { + this.rootNode = new UnpackedNode(); + this.rootNode.setFile(archiveFile); + this.rootNode.setFileName(archiveFile.getName()); + this.rootNode.localRelPath = localPathRoot; } /** - * Tokenizes filePath passed in and traverses the dir structure, - * creating data nodes on the path way as needed + * Creates a node in the tree at the given path. Makes intermediate + * nodes if needed. If a node already exists at that path, it is + * returned. * * @param filePath file path with 1 or more tokens separated by / * @return child node for the last file token in the filePath */ - Data find(String filePath) { + UnpackedNode addNode(String filePath) { String[] toks = filePath.split("[\\/\\\\]"); List tokens = new ArrayList<>(); for (int i = 0; i < toks.length; ++i) { @@ -694,28 +704,31 @@ public final class SevenZipIngestModule implements FileIngestModule { tokens.add(toks[i]); } } - return find(root, tokens); + return addNode(rootNode, tokens); } /** - * recursive method that traverses the path + * recursive method that traverses the path * * @param tokenPath * @return */ - private Data find(Data parent, List tokenPath) { - //base case + private UnpackedNode addNode(UnpackedNode parent, List tokenPath) { + // we found all of the tokens if (tokenPath.isEmpty()) { return parent; } - String childName = tokenPath.remove(0); //step towards base case - Data child = parent.getChild(childName); + // get the next name in the path and look it up + String childName = tokenPath.remove(0); + UnpackedNode child = parent.getChild(childName); + // create new node if (child == null) { - child = new Data(childName, parent); + child = new UnpackedNode(childName, parent); } - return find(child, tokenPath); - + + // go down one more level + return addNode(child, tokenPath); } /** @@ -726,7 +739,7 @@ public final class SevenZipIngestModule implements FileIngestModule { */ List getRootFileObjects() { List ret = new ArrayList<>(); - for (Data child : root.children) { + for (UnpackedNode child : rootNode.children) { ret.add(child.getFile()); } return ret; @@ -740,15 +753,15 @@ public final class SevenZipIngestModule implements FileIngestModule { */ List getAllFileObjects() { List ret = new ArrayList<>(); - for (Data child : root.children) { + for (UnpackedNode child : rootNode.children) { getAllFileObjectsRec(ret, child); } return ret; } - private void getAllFileObjectsRec(List list, Data parent) { + private void getAllFileObjectsRec(List list, UnpackedNode parent) { list.add(parent.getFile()); - for (Data child : parent.children) { + for (UnpackedNode child : parent.children) { getAllFileObjectsRec(list, child); } } @@ -757,27 +770,22 @@ public final class SevenZipIngestModule implements FileIngestModule { * Traverse the tree top-down after unzipping is done and create derived * files for the entire hierarchy */ - void createDerivedFiles() throws TskCoreException { - for (Data child : root.children) { - createDerivedFilesRec(child); + void addDerivedFilesToCase() throws TskCoreException { + final FileManager fileManager = Case.getCurrentCase().getServices().getFileManager(); + for (UnpackedNode child : rootNode.children) { + addDerivedFilesToCaseRec(child, fileManager); } - } - private void createDerivedFilesRec(Data node) throws TskCoreException { + private void addDerivedFilesToCaseRec(UnpackedNode node, FileManager fileManager) throws TskCoreException { final String fileName = node.getFileName(); - final String localRelPath = node.getLocalRelPath(); - final long size = node.getSize(); - final boolean isFile = node.isIsFile(); - final AbstractFile parent = node.getParent().getFile(); try { - DerivedFile df = fileManager.addDerivedFile(fileName, localRelPath, size, + DerivedFile df = fileManager.addDerivedFile(fileName, node.getLocalRelPath(), node.getSize(), node.getCtime(), node.getCrtime(), node.getAtime(), node.getMtime(), - isFile, parent, "", ArchiveFileExtractorModuleFactory.getModuleName(), "", ""); + node.isIsFile(), node.getParent().getFile(), "", ArchiveFileExtractorModuleFactory.getModuleName(), "", ""); node.setFile(df); - } catch (TskCoreException ex) { logger.log(Level.SEVERE, "Error adding a derived file to db:" + fileName, ex); //NON-NLS throw new TskCoreException( @@ -786,31 +794,35 @@ public final class SevenZipIngestModule implements FileIngestModule { } //recurse - for (Data child : node.children) { - createDerivedFilesRec(child); + for (UnpackedNode child : node.children) { + addDerivedFilesToCaseRec(child, fileManager); } } - private class Data { + + /** + * A node in the unpacked tree that represents a file or folder. + */ + private class UnpackedNode { private String fileName; private AbstractFile file; - private List children = new ArrayList<>(); - private String localRelPath; + private List children = new ArrayList<>(); + private String localRelPath = ""; private long size; private long ctime, crtime, atime, mtime; private boolean isFile; - private Data parent; + private UnpackedNode parent; //root constructor - Data() { + UnpackedNode() { } //child node constructor - Data(String fileName, Data parent) { + UnpackedNode(String fileName, UnpackedNode parent) { this.fileName = fileName; this.parent = parent; - this.localRelPath = parent.localRelPath + File.separator + fileName; + //this.localRelPath = parent.localRelPath + File.separator + fileName; //new child derived file will be set by unpack() method parent.children.add(this); @@ -836,19 +848,20 @@ public final class SevenZipIngestModule implements FileIngestModule { this.fileName = fileName; } - Data getParent() { + UnpackedNode getParent() { return parent; } void addDerivedInfo(long size, boolean isFile, - long ctime, long crtime, long atime, long mtime) { + long ctime, long crtime, long atime, long mtime, String relLocalPath) { this.size = size; this.isFile = isFile; this.ctime = ctime; this.crtime = crtime; this.atime = atime; this.mtime = mtime; + this.localRelPath = relLocalPath; } void setFile(AbstractFile file) { @@ -861,9 +874,9 @@ public final class SevenZipIngestModule implements FileIngestModule { * @param childFileName * @return */ - Data getChild(String childFileName) { - Data ret = null; - for (Data child : children) { + UnpackedNode getChild(String childFileName) { + UnpackedNode ret = null; + for (UnpackedNode child : children) { if (child.fileName.equals(childFileName)) { ret = child; break; 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 0b0dfd0507..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 @@ -32,6 +32,8 @@ import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.io.Writer; +import java.nio.file.Path; +import java.nio.file.Paths; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -801,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( @@ -818,8 +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()); + 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) { + 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 } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/Timeline.java b/Core/src/org/sleuthkit/autopsy/timeline/Timeline.java index fe9ad3dd65..25cc1d16f6 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/Timeline.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/Timeline.java @@ -1136,6 +1136,7 @@ public class Timeline extends CallableSystemAction implements Presenter.Toolbar, logger.log(Level.SEVERE, "Could not clsoe writer after creating mactime file, encountered error ", ex); //NON-NLS } } + execUtil.stop(); } return macfile; diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbIngestModule.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbIngestModule.java index ce69602e39..34c88de480 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbIngestModule.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbIngestModule.java @@ -36,7 +36,7 @@ import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; -import org.sleuthkit.datamodel.Hash; +import org.sleuthkit.datamodel.HashUtility; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; @@ -50,7 +50,6 @@ public class HashDbIngestModule implements FileIngestModule { private static final Logger logger = Logger.getLogger(HashDbIngestModule.class.getName()); private static final int MAX_COMMENT_SIZE = 500; private final IngestServices services = IngestServices.getInstance(); - private final Hash hasher = new Hash(); private final SleuthkitCase skCase = Case.getCurrentCase().getSleuthkitCase(); private final HashDbManager hashDbManager = HashDbManager.getInstance(); private final HashLookupModuleSettings settings; @@ -159,7 +158,7 @@ public class HashDbIngestModule implements FileIngestModule { if (md5Hash == null || md5Hash.isEmpty()) { try { long calcstart = System.currentTimeMillis(); - md5Hash = hasher.calculateMd5(file); + md5Hash = HashUtility.calculateMd5(file); long delta = (System.currentTimeMillis() - calcstart); totals.totalCalctime.addAndGet(delta); diff --git a/NEWS.txt b/NEWS.txt index 0cba2a59f1..b474dc1212 100644 --- a/NEWS.txt +++ b/NEWS.txt @@ -1,3 +1,6 @@ +---------------- VERSION 3.0.10 -------------- +Bug Fixes: +- Fixed 64-bit CRT dlls. No other logic changes. ---------------- VERSION 3.0.9 -------------- Bug Fixes: diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Extract.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Extract.java index 88812760bf..4d47d924c8 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Extract.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Extract.java @@ -56,8 +56,6 @@ abstract class Extract { void complete() { } - void stop() { - } /** * Returns a List of string error messages from the inheriting class diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractIE.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractIE.java index 7cd56c4b43..735ad9d92a 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractIE.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractIE.java @@ -65,7 +65,6 @@ class ExtractIE extends Extract { private String PASCO_LIB_PATH; private String JAVA_PATH; private static final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); - private ExecUtil execPasco; private Content dataSource; private IngestJobContext context; @@ -357,11 +356,11 @@ class ExtractIE extends Extract { boolean success = true; Writer writer = null; + ExecUtil execPasco = new ExecUtil(); try { final String outputFileFullPath = moduleTempResultsDir + File.separator + outputFileName; logger.log(Level.INFO, "Writing pasco results to: {0}", outputFileFullPath); //NON-NLS writer = new FileWriter(outputFileFullPath); - execPasco = new ExecUtil(); execPasco.execute(writer, JAVA_PATH, "-cp", PASCO_LIB_PATH, //NON-NLS "isi.pasco2.Main", "-T", "history", indexFilePath ); //NON-NLS @@ -382,6 +381,7 @@ class ExtractIE extends Extract { logger.log(Level.WARNING, "Error closing writer stream after for Pasco result", ex); //NON-NLS } } + execPasco.stop(); } return success; } @@ -515,12 +515,4 @@ class ExtractIE extends Extract { } fileScanner.close(); } - - @Override - public void stop() { - if (execPasco != null) { - execPasco.stop(); - execPasco = null; - } - } } diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java index 1b2e483500..3ba0ae8fa5 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java @@ -63,7 +63,7 @@ class ExtractRegistry extends Extract { private boolean rrFound = false; // true if we found the Autopsy-specific version of regripper private boolean rrFullFound = false; // true if we found the full version of regripper final private static String MODULE_VERSION = "1.0"; - private ExecUtil execRR; + private Content dataSource; private IngestJobContext context; @@ -300,6 +300,7 @@ class ExtractRegistry extends Extract { if (!autopsyType.isEmpty() && rrFound) { // TODO - add error messages Writer writer = null; + ExecUtil execRR = null; try { regOutputFiles.autopsyPlugins = outFilePathBase + "-autopsy.txt"; //NON-NLS logger.log(Level.INFO, "Writing RegRipper results to: " + regOutputFiles.autopsyPlugins); //NON-NLS @@ -325,12 +326,16 @@ class ExtractRegistry extends Extract { logger.log(Level.SEVERE, "Error closing output writer after running RegRipper", ex); //NON-NLS } } + if (execRR != null) { + execRR.stop(); + } } } // run the full set of rr modules if (!fullType.isEmpty() && rrFullFound) { Writer writer = null; + ExecUtil execRR = null; try { regOutputFiles.fullPlugins = outFilePathBase + "-full.txt"; //NON-NLS logger.log(Level.INFO, "Writing Full RegRipper results to: " + regOutputFiles.fullPlugins); //NON-NLS @@ -356,6 +361,9 @@ class ExtractRegistry extends Extract { logger.log(Level.SEVERE, "Error closing output writer after running RegRipper full", ex); //NON-NLS } } + if (execRR != null) { + execRR.stop(); + } } } @@ -578,11 +586,4 @@ class ExtractRegistry extends Extract { analyzeRegistryFiles(); } - @Override - public void stop() { - if (execRR != null) { - execRR.stop(); - execRR = null; - } - } } diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/RAImageIngestModule.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/RAImageIngestModule.java index 18cc82e2ab..7cbcbf4e23 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/RAImageIngestModule.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/RAImageIngestModule.java @@ -162,7 +162,6 @@ public final class RAImageIngestModule implements DataSourceIngestModule { services.postMessage(inboxMsg); if (context.isJobCancelled()) { - stop(); return ProcessResult.OK; } @@ -180,16 +179,7 @@ public final class RAImageIngestModule implements DataSourceIngestModule { return ProcessResult.OK; } - private void stop() { - for (Extract extracter : extracters) { - try { - extracter.stop(); - } catch (Exception ex) { - logger.log(Level.SEVERE, "Exception during stop() of " + extracter.getName(), ex); //NON-NLS - } - } - logger.log(Level.INFO, "Recent Activity processes has been shutdown."); //NON-NLS - } + /** * Get the temp path for a specific sub-module in recent activity. Will diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/SearchEngineURLQueryAnalyzer.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/SearchEngineURLQueryAnalyzer.java index b490c225ce..582240e371 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/SearchEngineURLQueryAnalyzer.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/SearchEngineURLQueryAnalyzer.java @@ -374,9 +374,4 @@ class SearchEngineURLQueryAnalyzer extends Extract { public void complete() { logger.info("Search Engine URL Query Analyzer has completed."); //NON-NLS } - - @Override - public void stop() { - logger.info("Attempted to stop Search Engine URL Query Analyzer, but operation is not supported; skipping..."); //NON-NLS - } } diff --git a/ScalpelCarver/src/org/sleuthkit/autopsy/scalpel/ScalpelCarverIngestModule.java b/ScalpelCarver/src/org/sleuthkit/autopsy/scalpel/ScalpelCarverIngestModule.java index 01e921cbb0..1e52dec332 100644 --- a/ScalpelCarver/src/org/sleuthkit/autopsy/scalpel/ScalpelCarverIngestModule.java +++ b/ScalpelCarver/src/org/sleuthkit/autopsy/scalpel/ScalpelCarverIngestModule.java @@ -223,7 +223,7 @@ class ScalpelCarverIngestModule implements FileIngestModule { } // reschedule carved files - context.addFiles(new ArrayList<AbstractFile>(carvedFiles)); + context.scheduleFiles(new ArrayList<AbstractFile>(carvedFiles)); return ProcessResult.OK; } diff --git a/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties b/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties index 76c162a96e..a4b426a229 100644 --- a/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties +++ b/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties @@ -1,5 +1,5 @@ #Updated by build script -#Sat, 03 May 2014 22:45:39 -0400 +#Mon, 28 Apr 2014 01:45:18 -0400 LBL_splash_window_title=Starting Autopsy SPLASH_HEIGHT=288 SPLASH_WIDTH=538 diff --git a/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties b/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties index 8d4a5daefb..24255ffb3f 100644 --- a/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties +++ b/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties @@ -1,5 +1,5 @@ #Updated by build script -#Sat, 03 May 2014 22:45:39 -0400 +#Mon, 28 Apr 2014 01:45:18 -0400 CTL_MainWindow_Title=Autopsy 3.1.0_Beta CTL_MainWindow_Title_No_Project=Autopsy 3.1.0_Beta diff --git a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java index c507b10fd8..7b92c80aa1 100644 --- a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java +++ b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java @@ -289,7 +289,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule { services.fireModuleContentEvent(new ModuleContentEvent(derived)); } } - context.addFiles(derivedFiles); + context.scheduleFiles(derivedFiles); services.fireModuleDataEvent(new ModuleDataEvent(EmailParserModuleFactory.getModuleName(), BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG)); }