From 88675499f69e855280fc9af6f70ccc7d866d92bd Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Mon, 5 Aug 2013 17:23:32 -0400 Subject: [PATCH 01/35] First draft of multi-select work --- .../corecomponents/DataResultViewerTable.java | 2 +- .../autopsy/datamodel/DirectoryNode.java | 194 +-- .../sleuthkit/autopsy/datamodel/FileNode.java | 361 +++--- .../autopsy/datamodel/LayoutFileNode.java | 9 +- .../autopsy/datamodel/LocalFileNode.java | 11 +- .../org/sleuthkit/autopsy/datamodel/Tags.java | 33 - .../datamodel/VirtualDirectoryNode.java | 11 +- .../directorytree/DataResultFilterNode.java | 1038 ++++++++--------- .../DirectoryTreeFilterNode.java | 317 +++-- .../ExplorerNodeActionVisitor.java | 764 ++++++------ .../autopsy/directorytree/ExtractAction.java | 118 +- .../directorytree/TagAbstractFileAction.java | 67 ++ .../autopsy/directorytree/TagAction.java | 141 +-- .../directorytree/TagAndCommentDialog.java | 75 +- .../TagBlackboardArtifactAction.java | 67 ++ .../autopsy/directorytree/TagMenu.java | 73 +- .../KeywordSearchFilterNode.java | 30 +- 17 files changed, 1679 insertions(+), 1632 deletions(-) mode change 100644 => 100755 Core/src/org/sleuthkit/autopsy/datamodel/DirectoryNode.java mode change 100644 => 100755 Core/src/org/sleuthkit/autopsy/datamodel/FileNode.java mode change 100644 => 100755 Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java mode change 100644 => 100755 Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeFilterNode.java mode change 100644 => 100755 Core/src/org/sleuthkit/autopsy/directorytree/ExplorerNodeActionVisitor.java create mode 100755 Core/src/org/sleuthkit/autopsy/directorytree/TagAbstractFileAction.java create mode 100755 Core/src/org/sleuthkit/autopsy/directorytree/TagBlackboardArtifactAction.java diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java index 9ab0d40dea..6f5368578b 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java @@ -62,7 +62,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer { ov.setAllowedDropActions(DnDConstants.ACTION_NONE); // only allow one item to be selected at a time - ov.getOutline().setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + ov.getOutline().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); // don't show the root node ov.getOutline().setRootVisible(false); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DirectoryNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/DirectoryNode.java old mode 100644 new mode 100755 index a364071ac7..3c4d33c253 --- a/Core/src/org/sleuthkit/autopsy/datamodel/DirectoryNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DirectoryNode.java @@ -1,97 +1,97 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2011 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.util.ArrayList; -import java.util.List; -import javax.swing.Action; -import org.sleuthkit.autopsy.directorytree.ExtractAction; -import org.sleuthkit.autopsy.directorytree.NewWindowViewAction; -import org.sleuthkit.autopsy.directorytree.TagAction; -import org.sleuthkit.autopsy.directorytree.ViewContextAction; -import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.Directory; -import org.sleuthkit.datamodel.TskData.TSK_FS_NAME_FLAG_ENUM; - -/** - * This class is used to represent the "Node" for the directory. Its children - * are more directories. - */ -public class DirectoryNode extends AbstractFsContentNode { - - public static final String DOTDOTDIR = "[parent folder]"; - public static final String DOTDIR = "[current folder]"; - - public DirectoryNode(Directory dir) { - this(dir, true); - - setIcon(dir); - } - - public DirectoryNode(AbstractFile dir, boolean directoryBrowseMode) { - super(dir, directoryBrowseMode); - - setIcon(dir); - } - - private void setIcon(AbstractFile dir) { - // set name, display name, and icon - if (dir.isDirNameFlagSet(TSK_FS_NAME_FLAG_ENUM.UNALLOC)) { - this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/folder-icon-deleted.png"); - } else { - this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/Folder-icon.png"); - } - } - - /** - * Right click action for this node - * - * @param popup - * @return - */ - @Override - public Action[] getActions(boolean popup) { - List actions = new ArrayList(); - if (!getDirectoryBrowseMode()) { - actions.add(new ViewContextAction("View File in Directory", this)); - actions.add(null); // creates a menu separator - } - actions.add(new NewWindowViewAction("View in New Window", this)); - actions.add(null); // creates a menu separator - actions.add(new ExtractAction("Extract Directory", this)); - actions.add(null); // creates a menu separator - actions.add(new TagAction(this)); - return actions.toArray(new Action[0]); - } - - @Override - public T accept(ContentNodeVisitor v) { - return v.visit(this); - } - - @Override - public T accept(DisplayableItemNodeVisitor v) { - return v.visit(this); - } - - @Override - public TYPE getDisplayableItemNodeType() { - return TYPE.CONTENT; - } -} +/* + * Autopsy Forensic Browser + * + * Copyright 2011 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.util.ArrayList; +import java.util.List; +import javax.swing.Action; +import org.sleuthkit.autopsy.directorytree.ExtractAction; +import org.sleuthkit.autopsy.directorytree.NewWindowViewAction; +import org.sleuthkit.autopsy.directorytree.TagAbstractFileAction; +import org.sleuthkit.autopsy.directorytree.ViewContextAction; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.Directory; +import org.sleuthkit.datamodel.TskData.TSK_FS_NAME_FLAG_ENUM; + +/** + * This class is used to represent the "Node" for the directory. Its children + * are more directories. + */ +public class DirectoryNode extends AbstractFsContentNode { + + public static final String DOTDOTDIR = "[parent folder]"; + public static final String DOTDIR = "[current folder]"; + + public DirectoryNode(Directory dir) { + this(dir, true); + + setIcon(dir); + } + + public DirectoryNode(AbstractFile dir, boolean directoryBrowseMode) { + super(dir, directoryBrowseMode); + + setIcon(dir); + } + + private void setIcon(AbstractFile dir) { + // set name, display name, and icon + if (dir.isDirNameFlagSet(TSK_FS_NAME_FLAG_ENUM.UNALLOC)) { + this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/folder-icon-deleted.png"); + } else { + this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/Folder-icon.png"); + } + } + + /** + * Right click action for this node + * + * @param popup + * @return + */ + @Override + public Action[] getActions(boolean popup) { + List actions = new ArrayList<>(); + if (!getDirectoryBrowseMode()) { + actions.add(new ViewContextAction("View File in Directory", this)); + actions.add(null); // creates a menu separator + } + actions.add(new NewWindowViewAction("View in New Window", this)); + actions.add(null); // creates a menu separator + actions.add(ExtractAction.getInstance()); + actions.add(null); // creates a menu separator + actions.add(TagAbstractFileAction.getInstance()); + return actions.toArray(new Action[0]); + } + + @Override + public T accept(ContentNodeVisitor v) { + return v.visit(this); + } + + @Override + public T accept(DisplayableItemNodeVisitor v) { + return v.visit(this); + } + + @Override + public TYPE getDisplayableItemNodeType() { + return TYPE.CONTENT; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileNode.java old mode 100644 new mode 100755 index 6c3f739624..9ca87fd8a7 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileNode.java @@ -1,181 +1,180 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2011 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.util.ArrayList; -import java.util.List; -import javax.swing.Action; -import org.sleuthkit.autopsy.directorytree.ExternalViewerAction; -import org.sleuthkit.autopsy.directorytree.ExtractAction; -import org.sleuthkit.autopsy.directorytree.HashSearchAction; -import org.sleuthkit.autopsy.directorytree.NewWindowViewAction; -import org.sleuthkit.autopsy.directorytree.TagAction; -import org.sleuthkit.autopsy.directorytree.ViewContextAction; -import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.FsContent; -import org.sleuthkit.datamodel.TskData.TSK_DB_FILES_TYPE_ENUM; -import org.sleuthkit.datamodel.TskData.TSK_FS_NAME_FLAG_ENUM; - -/** - * This class is used to represent the "Node" for the file. It may have derived - * files children. - */ -public class FileNode extends AbstractFsContentNode { - - /** - * @param file underlying Content - */ - public FileNode(AbstractFile file) { - this(file, true); - - setIcon(file); - } - - public FileNode(AbstractFile file, boolean directoryBrowseMode) { - super(file, directoryBrowseMode); - - setIcon(file); - } - - private void setIcon(AbstractFile file) { - // set name, display name, and icon - if (file.isDirNameFlagSet(TSK_FS_NAME_FLAG_ENUM.UNALLOC)) { - if (file.getType().equals(TSK_DB_FILES_TYPE_ENUM.CARVED)) { - this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/carved-file-icon-16.png"); - } else { - this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file-icon-deleted.png"); - } - } else { - this.setIconBaseWithExtension(getIconForFileType(file)); - } - } - - /** - * Right click action for this node - * - * @param popup - * @return - */ - @Override - public Action[] getActions(boolean popup) { - List actionsList = new ArrayList(); - if (!this.getDirectoryBrowseMode()) { - actionsList.add(new ViewContextAction("View File in Directory", this)); - actionsList.add(null); // creates a menu separator - } - actionsList.add(new NewWindowViewAction("View in New Window", this)); - actionsList.add(new ExternalViewerAction("Open in External Viewer", this)); - actionsList.add(null); // creates a menu separator - actionsList.add(new ExtractAction("Extract File", this)); - actionsList.add(new HashSearchAction("Search for files with the same MD5 hash", this)); - actionsList.add(null); // creates a menu separator - actionsList.add(new TagAction(this)); - return actionsList.toArray(new Action[0]); - } - - @Override - public T accept(ContentNodeVisitor< T> v) { - return v.visit(this); - } - - @Override - public T accept(DisplayableItemNodeVisitor< T> v) { - return v.visit(this); - } - - // Given a file, returns the correct icon for said - // file based off it's extension - static String getIconForFileType(AbstractFile file) { - // Get the name, extension - String name = file.getName(); - int dotIndex = name.lastIndexOf("."); - if (dotIndex == -1) { - return "org/sleuthkit/autopsy/images/file-icon.png"; - } - String ext = name.substring(dotIndex).toLowerCase(); - - // Images - for (String s : FileTypeExtensions.getImageExtensions()) { - if (ext.equals(s)) { - return "org/sleuthkit/autopsy/images/image-file.png"; - } - } - // Videos - for (String s : FileTypeExtensions.getVideoExtensions()) { - if (ext.equals(s)) { - return "org/sleuthkit/autopsy/images/video-file.png"; - } - } - // Audio Files - for (String s : FileTypeExtensions.getAudioExtensions()) { - if (ext.equals(s)) { - return "org/sleuthkit/autopsy/images/audio-file.png"; - } - } - // Documents - for (String s : FileTypeExtensions.getDocumentExtensions()) { - if (ext.equals(s)) { - return "org/sleuthkit/autopsy/images/doc-file.png"; - } - } - // Executables / System Files - for (String s : FileTypeExtensions.getExecutableExtensions()) { - if (ext.equals(s)) { - return "org/sleuthkit/autopsy/images/exe-file.png"; - } - } - // Text Files - for (String s : FileTypeExtensions.getTextExtensions()) { - if (ext.equals(s)) { - return "org/sleuthkit/autopsy/images/text-file.png"; - } - } - // Web Files - for (String s : FileTypeExtensions.getWebExtensions()) { - if (ext.equals(s)) { - return "org/sleuthkit/autopsy/images/web-file.png"; - } - } - // PDFs - for (String s : FileTypeExtensions.getPDFExtensions()) { - if (ext.equals(s)) { - return "org/sleuthkit/autopsy/images/pdf-file.png"; - } - } - // Archives - for (String s : FileTypeExtensions.getArchiveExtensions()) { - if (ext.equals(s)) { - return "org/sleuthkit/autopsy/images/archive-file.png"; - } - } - // Else return the default - return "org/sleuthkit/autopsy/images/file-icon.png"; - - } - - @Override - public TYPE getDisplayableItemNodeType() { - return TYPE.CONTENT; - } - - @Override - public boolean isLeafTypeNode() { - return true; //false; - } -} +/* + * Autopsy Forensic Browser + * + * Copyright 2011 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.util.ArrayList; +import java.util.List; +import javax.swing.Action; +import org.sleuthkit.autopsy.directorytree.ExternalViewerAction; +import org.sleuthkit.autopsy.directorytree.ExtractAction; +import org.sleuthkit.autopsy.directorytree.HashSearchAction; +import org.sleuthkit.autopsy.directorytree.NewWindowViewAction; +import org.sleuthkit.autopsy.directorytree.TagAbstractFileAction; +import org.sleuthkit.autopsy.directorytree.ViewContextAction; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.TskData.TSK_DB_FILES_TYPE_ENUM; +import org.sleuthkit.datamodel.TskData.TSK_FS_NAME_FLAG_ENUM; + +/** + * This class is used to represent the "Node" for the file. It may have derived + * files children. + */ +public class FileNode extends AbstractFsContentNode { + + /** + * @param file underlying Content + */ + public FileNode(AbstractFile file) { + this(file, true); + + setIcon(file); + } + + public FileNode(AbstractFile file, boolean directoryBrowseMode) { + super(file, directoryBrowseMode); + + setIcon(file); + } + + private void setIcon(AbstractFile file) { + // set name, display name, and icon + if (file.isDirNameFlagSet(TSK_FS_NAME_FLAG_ENUM.UNALLOC)) { + if (file.getType().equals(TSK_DB_FILES_TYPE_ENUM.CARVED)) { + this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/carved-file-icon-16.png"); + } else { + this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file-icon-deleted.png"); + } + } else { + this.setIconBaseWithExtension(getIconForFileType(file)); + } + } + + /** + * Right click action for this node + * + * @param popup + * @return + */ + @Override + public Action[] getActions(boolean popup) { + List actionsList = new ArrayList<>(); + if (!this.getDirectoryBrowseMode()) { + actionsList.add(new ViewContextAction("View File in Directory", this)); + actionsList.add(null); // creates a menu separator + } + actionsList.add(new NewWindowViewAction("View in New Window", this)); + actionsList.add(new ExternalViewerAction("Open in External Viewer", this)); + actionsList.add(null); // creates a menu separator + actionsList.add(ExtractAction.getInstance()); + actionsList.add(new HashSearchAction("Search for files with the same MD5 hash", this)); + actionsList.add(null); // creates a menu separator + actionsList.add(TagAbstractFileAction.getInstance()); + return actionsList.toArray(new Action[0]); + } + + @Override + public T accept(ContentNodeVisitor< T> v) { + return v.visit(this); + } + + @Override + public T accept(DisplayableItemNodeVisitor< T> v) { + return v.visit(this); + } + + // Given a file, returns the correct icon for said + // file based off it's extension + static String getIconForFileType(AbstractFile file) { + // Get the name, extension + String name = file.getName(); + int dotIndex = name.lastIndexOf("."); + if (dotIndex == -1) { + return "org/sleuthkit/autopsy/images/file-icon.png"; + } + String ext = name.substring(dotIndex).toLowerCase(); + + // Images + for (String s : FileTypeExtensions.getImageExtensions()) { + if (ext.equals(s)) { + return "org/sleuthkit/autopsy/images/image-file.png"; + } + } + // Videos + for (String s : FileTypeExtensions.getVideoExtensions()) { + if (ext.equals(s)) { + return "org/sleuthkit/autopsy/images/video-file.png"; + } + } + // Audio Files + for (String s : FileTypeExtensions.getAudioExtensions()) { + if (ext.equals(s)) { + return "org/sleuthkit/autopsy/images/audio-file.png"; + } + } + // Documents + for (String s : FileTypeExtensions.getDocumentExtensions()) { + if (ext.equals(s)) { + return "org/sleuthkit/autopsy/images/doc-file.png"; + } + } + // Executables / System Files + for (String s : FileTypeExtensions.getExecutableExtensions()) { + if (ext.equals(s)) { + return "org/sleuthkit/autopsy/images/exe-file.png"; + } + } + // Text Files + for (String s : FileTypeExtensions.getTextExtensions()) { + if (ext.equals(s)) { + return "org/sleuthkit/autopsy/images/text-file.png"; + } + } + // Web Files + for (String s : FileTypeExtensions.getWebExtensions()) { + if (ext.equals(s)) { + return "org/sleuthkit/autopsy/images/web-file.png"; + } + } + // PDFs + for (String s : FileTypeExtensions.getPDFExtensions()) { + if (ext.equals(s)) { + return "org/sleuthkit/autopsy/images/pdf-file.png"; + } + } + // Archives + for (String s : FileTypeExtensions.getArchiveExtensions()) { + if (ext.equals(s)) { + return "org/sleuthkit/autopsy/images/archive-file.png"; + } + } + // Else return the default + return "org/sleuthkit/autopsy/images/file-icon.png"; + + } + + @Override + public TYPE getDisplayableItemNodeType() { + return TYPE.CONTENT; + } + + @Override + public boolean isLeafTypeNode() { + return true; //false; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/LayoutFileNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/LayoutFileNode.java index 36f2fc8c02..9224131ad6 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/LayoutFileNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/LayoutFileNode.java @@ -24,11 +24,10 @@ import java.util.List; import java.util.Map; import javax.swing.Action; import org.openide.nodes.Sheet; -import org.sleuthkit.autopsy.directorytree.ExplorerNodeActionVisitor; import org.sleuthkit.autopsy.directorytree.ExternalViewerAction; import org.sleuthkit.autopsy.directorytree.ExtractAction; import org.sleuthkit.autopsy.directorytree.NewWindowViewAction; -import org.sleuthkit.autopsy.directorytree.TagAction; +import org.sleuthkit.autopsy.directorytree.TagAbstractFileAction; import org.sleuthkit.datamodel.LayoutFile; import org.sleuthkit.datamodel.TskData; @@ -104,14 +103,12 @@ public class LayoutFileNode extends AbstractAbstractFileNode { @Override public Action[] getActions(boolean context) { List actionsList = new ArrayList(); - actionsList.add(new NewWindowViewAction("View in New Window", this)); actionsList.add(new ExternalViewerAction("Open in External Viewer", this)); actionsList.add(null); // creates a menu separator - actionsList.add(new ExtractAction("Extract File", content)); + actionsList.add(ExtractAction.getInstance()); actionsList.add(null); // creates a menu separator - actionsList.add(new TagAction(content)); - + actionsList.add(TagAbstractFileAction.getInstance()); return actionsList.toArray(new Action[0]); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/LocalFileNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/LocalFileNode.java index f2dc17d9a0..78711736b9 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/LocalFileNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/LocalFileNode.java @@ -30,9 +30,8 @@ import org.sleuthkit.autopsy.directorytree.ExternalViewerAction; import org.sleuthkit.autopsy.directorytree.ExtractAction; import org.sleuthkit.autopsy.directorytree.HashSearchAction; import org.sleuthkit.autopsy.directorytree.NewWindowViewAction; -import org.sleuthkit.autopsy.directorytree.TagAction; +import org.sleuthkit.autopsy.directorytree.TagAbstractFileAction; import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.LocalFile; /** * A Node for a LocalFile or DerivedFile content object. @@ -86,16 +85,14 @@ public class LocalFileNode extends AbstractAbstractFileNode { @Override public Action[] getActions(boolean context) { - List actionsList = new ArrayList(); - + List actionsList = new ArrayList<>(); actionsList.add(new NewWindowViewAction("View in New Window", this)); actionsList.add(new ExternalViewerAction("Open in External Viewer", this)); actionsList.add(null); // creates a menu separator - actionsList.add(new ExtractAction("Extract", content)); //might not need this actions - already local file + actionsList.add(ExtractAction.getInstance()); actionsList.add(new HashSearchAction("Search for files with the same MD5 hash", this)); actionsList.add(null); // creates a menu separator - actionsList.add(new TagAction(content)); - + actionsList.add(TagAbstractFileAction.getInstance()); return actionsList.toArray(new Action[0]); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Tags.java b/Core/src/org/sleuthkit/autopsy/datamodel/Tags.java index 319d36e737..064f953660 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Tags.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Tags.java @@ -22,7 +22,6 @@ import java.awt.event.ActionEvent; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; -import java.util.Collections; import java.util.EnumMap; import java.util.HashMap; import java.util.HashSet; @@ -656,36 +655,4 @@ public class Tags implements AutopsyVisitableItem { return tagNames; } - - public interface Taggable { - void createTag(String name, String comment); - } - - public static class TaggableFile implements Taggable { - - private AbstractFile file; - - public TaggableFile(AbstractFile file) { - this.file = file; - } - - @Override - public void createTag(String name, String comment) { - Tags.createTag(file, name, comment); - } - } - - public static class TaggableBlackboardArtifact implements Taggable { - - private BlackboardArtifact bba; - - public TaggableBlackboardArtifact(BlackboardArtifact bba) { - this.bba = bba; - } - - @Override - public void createTag(String name, String comment) { - Tags.createTag(bba, name, comment); - } - } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/VirtualDirectoryNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/VirtualDirectoryNode.java index 59e523ff81..756a376d11 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/VirtualDirectoryNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/VirtualDirectoryNode.java @@ -27,7 +27,7 @@ import org.openide.nodes.Sheet; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.directorytree.ExtractAction; import org.sleuthkit.autopsy.directorytree.NewWindowViewAction; -import org.sleuthkit.autopsy.directorytree.TagAction; +import org.sleuthkit.autopsy.directorytree.TagAbstractFileAction; import org.sleuthkit.datamodel.VirtualDirectory; import org.sleuthkit.datamodel.TskData; @@ -76,16 +76,15 @@ public class VirtualDirectoryNode extends AbstractAbstractFileNode actions = new ArrayList(); - + List actions = new ArrayList<>(); actions.add(new NewWindowViewAction("View in New Window", this)); actions.add(null); // creates a menu separator - actions.add(new ExtractAction("Extract Directory", this)); + actions.add(ExtractAction.getInstance()); actions.add(null); // creates a menu separator - actions.add(new TagAction(this)); + actions.add(TagAbstractFileAction.getInstance()); return actions.toArray(new Action[0]); } - + @Override protected Sheet createSheet() { Sheet s = super.createSheet(); diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java old mode 100644 new mode 100755 index a439a066bf..e30e9b56b1 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DataResultFilterNode.java @@ -1,520 +1,520 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2011 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.directorytree; - -import java.awt.event.ActionEvent; -import java.beans.PropertyVetoException; -import java.util.ArrayList; -import java.util.List; -import org.sleuthkit.autopsy.datamodel.VolumeNode; -import org.sleuthkit.autopsy.datamodel.DirectoryNode; -import java.util.logging.Level; -import org.sleuthkit.autopsy.coreutils.Logger; -import javax.swing.AbstractAction; -import javax.swing.Action; -import org.openide.explorer.ExplorerManager; -import org.openide.nodes.AbstractNode; -import org.openide.nodes.FilterNode; -import org.openide.nodes.Node; -import org.openide.nodes.Sheet; -import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode.AbstractFilePropertyType; -import org.sleuthkit.autopsy.datamodel.AbstractFsContentNode; -import org.sleuthkit.autopsy.datamodel.ArtifactTypeNode; -import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode; -import org.sleuthkit.autopsy.datamodel.LocalFileNode; -import org.sleuthkit.autopsy.datamodel.DeletedContent.DeletedContentsChildren.DeletedContentNode; -import org.sleuthkit.autopsy.datamodel.DeletedContent.DeletedContentsNode; -import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; -import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor; -import org.sleuthkit.autopsy.datamodel.EmailExtracted.EmailExtractedAccountNode; -import org.sleuthkit.autopsy.datamodel.EmailExtracted.EmailExtractedFolderNode; -import org.sleuthkit.autopsy.datamodel.EmailExtracted.EmailExtractedRootNode; -import org.sleuthkit.autopsy.datamodel.ExtractedContentNode; -import org.sleuthkit.autopsy.datamodel.FileNode; -import org.sleuthkit.autopsy.datamodel.FileSearchFilterNode; -import org.sleuthkit.autopsy.datamodel.FileSize.FileSizeRootChildren.FileSizeNode; -import org.sleuthkit.autopsy.datamodel.FileSize.FileSizeRootNode; -import org.sleuthkit.autopsy.datamodel.HashsetHits.HashsetHitsRootNode; -import org.sleuthkit.autopsy.datamodel.HashsetHits.HashsetHitsSetNode; -import org.sleuthkit.autopsy.datamodel.ImageNode; -import org.sleuthkit.autopsy.datamodel.KeywordHits.KeywordHitsKeywordNode; -import org.sleuthkit.autopsy.datamodel.KeywordHits.KeywordHitsListNode; -import org.sleuthkit.autopsy.datamodel.KeywordHits.KeywordHitsRootNode; -import org.sleuthkit.autopsy.datamodel.VirtualDirectoryNode; -import org.sleuthkit.autopsy.datamodel.LayoutFileNode; -import org.sleuthkit.autopsy.datamodel.RecentFilesFilterNode; -import org.sleuthkit.autopsy.datamodel.RecentFilesNode; -import org.sleuthkit.autopsy.datamodel.SearchFiltersNode; -import org.sleuthkit.autopsy.datamodel.Tags.TagNodeRoot; -import org.sleuthkit.autopsy.datamodel.Tags.TagsNodeRoot; -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.Directory; -import org.sleuthkit.datamodel.File; -import org.sleuthkit.datamodel.LayoutFile; -import org.sleuthkit.datamodel.LocalFile; -import org.sleuthkit.datamodel.TskException; -import org.sleuthkit.datamodel.VirtualDirectory; - -/** - * This class wraps nodes as they are passed to the DataResult viewers. It - * defines the actions that the node should have. - */ -public class DataResultFilterNode extends FilterNode { - - private ExplorerManager sourceEm; - private final DisplayableItemNodeVisitor> getActionsDIV; - private final DisplayableItemNodeVisitor getPreferredActionsDIV; - - /** - * the constructor - */ - public DataResultFilterNode(Node node, ExplorerManager em) { - super(node, new DataResultFilterChildren(node, em)); - this.sourceEm = em; - getActionsDIV = new GetPopupActionsDisplayableItemNodeVisitor(); - getPreferredActionsDIV = new GetPreferredActionsDisplayableItemNodeVisitor(); - } - - /** - * Right click action for the nodes that we want to pass to the directory - * table and the output view. - * - * @param popup - * @return actions - */ - @Override - public Action[] getActions(boolean popup) { - - List actions = new ArrayList(); - - final DisplayableItemNode originalNode = (DisplayableItemNode) this.getOriginal(); - actions.addAll(originalNode.accept(getActionsDIV)); - - //actions.add(new IndexContentFilesAction(nodeContent, "Index")); - - return actions.toArray(new Action[actions.size()]); - } - - /** - * Double click action for the nodes that we want to pass to the directory - * table and the output view. - * - * @return action - */ - @Override - public Action getPreferredAction() { - // double click action(s) for volume node or directory node - - final DisplayableItemNode originalNode; - originalNode = (DisplayableItemNode) this.getOriginal(); - - return originalNode.accept(getPreferredActionsDIV); - } - - @Override - public Node.PropertySet[] getPropertySets() { - Node.PropertySet[] propertySets = super.getPropertySets(); - - for (int i = 0; i < propertySets.length; i++) { - Node.PropertySet ps = propertySets[i]; - - if (ps.getName().equals(Sheet.PROPERTIES)) { - Sheet.Set newPs = new Sheet.Set(); - newPs.setName(ps.getName()); - newPs.setDisplayName(ps.getDisplayName()); - newPs.setShortDescription(ps.getShortDescription()); - - newPs.put(ps.getProperties()); - if (newPs.remove(AbstractFsContentNode.HIDE_PARENT) != null) { - newPs.remove(AbstractFilePropertyType.LOCATION.toString()); - } - propertySets[i] = newPs; - } - } - - return propertySets; - } - - /** - * Uses the default nodes actions per node, adds some custom ones and - * returns them per visited node type - */ - private static class GetPopupActionsDisplayableItemNodeVisitor extends DisplayableItemNodeVisitor.Default> { - - @Override - public List visit(BlackboardArtifactNode ban) { - //set up actions for artifact node based on its Content object - //TODO all actions need to be consolidated in single place! - //they should be set in individual Node subclass and using a utility to get Actions per Content sub-type - - List actions = new ArrayList(); - - //merge predefined specific node actions if bban subclasses have their own - for (Action a : ban.getActions(true)) { - actions.add(a); - } - BlackboardArtifact ba = ban.getLookup().lookup(BlackboardArtifact.class); - final int artifactTypeID = ba.getArtifactTypeID(); - - if (artifactTypeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID() - || artifactTypeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()) { - actions.add(new ViewContextAction("View File in Directory", ban)); - } else { - Content c = findLinked(ban); - if (c != null) { - actions.add(new ViewContextAction("View File in Directory", c)); - } - actions.add(new ViewContextAction("View Source File in Directory", ban)); - } - File f = ban.getLookup().lookup(File.class); - LayoutFile lf = null; - AbstractFile locF = null; - Directory d = null; - VirtualDirectory vd = null; - if (f != null) { - final FileNode fn = new FileNode(f); - actions.add(null); // creates a menu separator - actions.add(new NewWindowViewAction("View in New Window", fn)); - actions.add(new ExternalViewerAction("Open in External Viewer", fn)); - actions.add(null); // creates a menu separator - actions.add(new ExtractAction("Extract File", new FileNode(f))); - actions.add(new HashSearchAction("Search for files with the same MD5 hash", fn)); - - //add file/result tag if itself is not a tag - if (artifactTypeID != BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE.getTypeID() - && artifactTypeID != BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_ARTIFACT.getTypeID()) { - actions.add(null); // creates a menu separator - actions.add(new TagAction(f)); - actions.add(new TagAction(ba)); - } - } - if ((d = ban.getLookup().lookup(Directory.class)) != null) { - DirectoryNode dn = new DirectoryNode(d); - actions.add(null); // creates a menu separator - actions.add(new NewWindowViewAction("View in New Window", dn)); - actions.add(new ExternalViewerAction("Open in External Viewer", dn)); - actions.add(null); // creates a menu separator - actions.add(new ExtractAction("Extract Directory", dn)); - - //add file/result tag if itself is not a tag - if (artifactTypeID != BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE.getTypeID() - && artifactTypeID != BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_ARTIFACT.getTypeID()) { - actions.add(null); // creates a menu separator - actions.add(new TagAction(d)); - actions.add(new TagAction(ba)); - } - } - if ((vd = ban.getLookup().lookup(VirtualDirectory.class)) != null) { - VirtualDirectoryNode dn = new VirtualDirectoryNode(vd); - actions.add(null); // creates a menu separator - actions.add(new NewWindowViewAction("View in New Window", dn)); - actions.add(new ExternalViewerAction("Open in External Viewer", dn)); - actions.add(null); // creates a menu separator - actions.add(new ExtractAction("Extract Directory", dn)); - - //add file/result tag if itself is not a tag - if (artifactTypeID != BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE.getTypeID() - && artifactTypeID != BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_ARTIFACT.getTypeID()) { - actions.add(null); // creates a menu separator - actions.add(new TagAction(d)); - actions.add(new TagAction(ba)); - } - } else if ((lf = ban.getLookup().lookup(LayoutFile.class)) != null) { - LayoutFileNode lfn = new LayoutFileNode(lf); - actions.add(null); // creates a menu separator - actions.add(new NewWindowViewAction("View in New Window", lfn)); - actions.add(new ExternalViewerAction("Open in External Viewer", lfn)); - actions.add(null); // creates a menu separator - actions.add(new ExtractAction("Extract File", lfn)); - - //add tag if itself is not a tag - if (artifactTypeID != BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE.getTypeID() - && artifactTypeID != BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_ARTIFACT.getTypeID()) { - actions.add(null); // creates a menu separator - actions.add(new TagAction(lf)); - actions.add(new TagAction(ba)); - } - } else if ((locF = ban.getLookup().lookup(LocalFile.class)) != null - || (locF = ban.getLookup().lookup(DerivedFile.class)) != null) { - final LocalFileNode locfn = new LocalFileNode(locF); - actions.add(null); // creates a menu separator - actions.add(new NewWindowViewAction("View in New Window", locfn)); - actions.add(new ExternalViewerAction("Open in External Viewer", locfn)); - actions.add(null); // creates a menu separator - actions.add(new ExtractAction("Extract File", locfn)); - - //add tag if itself is not a tag - if (artifactTypeID != BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE.getTypeID() - && artifactTypeID != BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_ARTIFACT.getTypeID()) { - actions.add(null); // creates a menu separator - actions.add(new TagAction(lf)); - actions.add(new TagAction(ba)); - } - } - - return actions; - } - - @Override - protected List defaultVisit(DisplayableItemNode ditem) { - //preserve the default node's actions - List actions = new ArrayList(); - - for (Action action : ditem.getActions(true)) { - actions.add(action); - } - - return actions; - } - - private Content findLinked(BlackboardArtifactNode ba) { - BlackboardArtifact art = ba.getLookup().lookup(BlackboardArtifact.class); - Content c = null; - try { - for (BlackboardAttribute attr : art.getAttributes()) { - if (attr.getAttributeTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID.getTypeID()) { - switch (attr.getValueType()) { - case INTEGER: - int i = attr.getValueInt(); - if (i != -1) { - c = art.getSleuthkitCase().getContentById(i); - } - break; - case LONG: - long l = attr.getValueLong(); - if (l != -1) { - c = art.getSleuthkitCase().getContentById(l); - } - break; - } - } - } - } catch (TskException ex) { - Logger.getLogger(this.getClass().getName()).log(Level.WARNING, "Error getting linked file", ex); - } - return c; - } - } - - private class GetPreferredActionsDisplayableItemNodeVisitor extends DisplayableItemNodeVisitor.Default { - - @Override - public AbstractAction visit(ImageNode in) { - return openChild(in); - } - - @Override - public AbstractAction visit(VolumeNode vn) { - return openChild(vn); - } - - @Override - public AbstractAction visit(ExtractedContentNode ecn) { - return openChild(ecn); - } - - @Override - public AbstractAction visit(KeywordHitsRootNode khrn) { - return openChild(khrn); - } - - @Override - public AbstractAction visit(HashsetHitsRootNode hhrn) { - return openChild(hhrn); - } - - @Override - public AbstractAction visit(HashsetHitsSetNode hhsn) { - return openChild(hhsn); - } - - @Override - public AbstractAction visit(EmailExtractedRootNode eern) { - return openChild(eern); - } - - @Override - public AbstractAction visit(EmailExtractedAccountNode eean) { - return openChild(eean); - } - - @Override - public AbstractAction visit(EmailExtractedFolderNode eefn) { - return openChild(eefn); - } - - @Override - public AbstractAction visit(RecentFilesNode rfn) { - return openChild(rfn); - } - - @Override - public AbstractAction visit(DeletedContentsNode dcn) { - return openChild(dcn); - } - - @Override - public AbstractAction visit(DeletedContentNode dcn) { - return openChild(dcn); - } - - @Override - public AbstractAction visit(FileSizeRootNode fsrn) { - return openChild(fsrn); - } - - @Override - public AbstractAction visit(FileSizeNode fsn) { - return openChild(fsn); - } - - @Override - public AbstractAction visit(BlackboardArtifactNode ban) { - return new ViewContextAction("View in Directory", ban); - } - - @Override - public AbstractAction visit(ArtifactTypeNode atn) { - return openChild(atn); - } - - @Override - public AbstractAction visit(TagNodeRoot tnr) { - return openChild(tnr); - } - - @Override - public AbstractAction visit(TagsNodeRoot tnr) { - return openChild(tnr); - } - - @Override - public AbstractAction visit(DirectoryNode dn) { - if (dn.getDisplayName().equals(DirectoryNode.DOTDOTDIR)) { - return openParent(dn); - } else if (!dn.getDisplayName().equals(DirectoryNode.DOTDIR)) { - return openChild(dn); - } else { - return null; - } - } - - @Override - public AbstractAction visit(VirtualDirectoryNode ldn) { - return openChild(ldn); - } - - @Override - public AbstractAction visit(FileNode fn) { - if (fn.hasContentChildren()) { - return openChild(fn); - } else { - return null; - } - } - - @Override - public AbstractAction visit(LocalFileNode dfn) { - if (dfn.hasContentChildren()) { - return openChild(dfn); - } else { - return null; - } - } - - @Override - public AbstractAction visit(FileSearchFilterNode fsfn) { - return openChild(fsfn); - } - - @Override - public AbstractAction visit(SearchFiltersNode sfn) { - return openChild(sfn); - } - - @Override - public AbstractAction visit(RecentFilesFilterNode rffn) { - return openChild(rffn); - } - - @Override - public AbstractAction visit(KeywordHitsListNode khsn) { - return openChild(khsn); - } - - @Override - public AbstractAction visit(KeywordHitsKeywordNode khmln) { - return openChild(khmln); - } - - @Override - protected AbstractAction defaultVisit(DisplayableItemNode c) { - return null; - } - - private AbstractAction openChild(AbstractNode node) { - final Node[] parentNode = sourceEm.getSelectedNodes(); - final Node parentContext = parentNode[0]; - final Node original = node; - - return new AbstractAction() { - @Override - public void actionPerformed(ActionEvent e) { - if (parentContext != null) { - final int childrenNodesCount = parentContext.getChildren().getNodesCount(); - for (int i = 0; i < childrenNodesCount; i++) { - Node selectedNode = parentContext.getChildren().getNodeAt(i); - if (selectedNode != null && selectedNode.getName().equals(original.getName())) { - try { - sourceEm.setExploredContextAndSelection(selectedNode, new Node[]{selectedNode}); - } catch (PropertyVetoException ex) { - // throw an error here - Logger logger = Logger.getLogger(DataResultFilterNode.class.getName()); - logger.log(Level.WARNING, "Error: can't open the selected directory.", ex); - } - } - } - } - } - }; - } - - private AbstractAction openParent(AbstractNode node) { - Node[] selectedNode = sourceEm.getSelectedNodes(); - Node selectedContext = selectedNode[0]; - final Node parentNode = selectedContext.getParentNode(); - - return new AbstractAction() { - @Override - public void actionPerformed(ActionEvent e) { - try { - sourceEm.setSelectedNodes(new Node[]{parentNode}); - } catch (PropertyVetoException ex) { - Logger logger = Logger.getLogger(DataResultFilterNode.class.getName()); - logger.log(Level.WARNING, "Error: can't open the parent directory.", ex); - } - } - }; - } - } +/* + * Autopsy Forensic Browser + * + * Copyright 2011 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.directorytree; + +import java.awt.event.ActionEvent; +import java.beans.PropertyVetoException; +import java.util.ArrayList; +import java.util.List; +import org.sleuthkit.autopsy.datamodel.VolumeNode; +import org.sleuthkit.autopsy.datamodel.DirectoryNode; +import java.util.logging.Level; +import org.sleuthkit.autopsy.coreutils.Logger; +import javax.swing.AbstractAction; +import javax.swing.Action; +import org.openide.explorer.ExplorerManager; +import org.openide.nodes.AbstractNode; +import org.openide.nodes.FilterNode; +import org.openide.nodes.Node; +import org.openide.nodes.Sheet; +import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode.AbstractFilePropertyType; +import org.sleuthkit.autopsy.datamodel.AbstractFsContentNode; +import org.sleuthkit.autopsy.datamodel.ArtifactTypeNode; +import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode; +import org.sleuthkit.autopsy.datamodel.LocalFileNode; +import org.sleuthkit.autopsy.datamodel.DeletedContent.DeletedContentsChildren.DeletedContentNode; +import org.sleuthkit.autopsy.datamodel.DeletedContent.DeletedContentsNode; +import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; +import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor; +import org.sleuthkit.autopsy.datamodel.EmailExtracted.EmailExtractedAccountNode; +import org.sleuthkit.autopsy.datamodel.EmailExtracted.EmailExtractedFolderNode; +import org.sleuthkit.autopsy.datamodel.EmailExtracted.EmailExtractedRootNode; +import org.sleuthkit.autopsy.datamodel.ExtractedContentNode; +import org.sleuthkit.autopsy.datamodel.FileNode; +import org.sleuthkit.autopsy.datamodel.FileSearchFilterNode; +import org.sleuthkit.autopsy.datamodel.FileSize.FileSizeRootChildren.FileSizeNode; +import org.sleuthkit.autopsy.datamodel.FileSize.FileSizeRootNode; +import org.sleuthkit.autopsy.datamodel.HashsetHits.HashsetHitsRootNode; +import org.sleuthkit.autopsy.datamodel.HashsetHits.HashsetHitsSetNode; +import org.sleuthkit.autopsy.datamodel.ImageNode; +import org.sleuthkit.autopsy.datamodel.KeywordHits.KeywordHitsKeywordNode; +import org.sleuthkit.autopsy.datamodel.KeywordHits.KeywordHitsListNode; +import org.sleuthkit.autopsy.datamodel.KeywordHits.KeywordHitsRootNode; +import org.sleuthkit.autopsy.datamodel.VirtualDirectoryNode; +import org.sleuthkit.autopsy.datamodel.LayoutFileNode; +import org.sleuthkit.autopsy.datamodel.RecentFilesFilterNode; +import org.sleuthkit.autopsy.datamodel.RecentFilesNode; +import org.sleuthkit.autopsy.datamodel.SearchFiltersNode; +import org.sleuthkit.autopsy.datamodel.Tags.TagNodeRoot; +import org.sleuthkit.autopsy.datamodel.Tags.TagsNodeRoot; +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.Directory; +import org.sleuthkit.datamodel.File; +import org.sleuthkit.datamodel.LayoutFile; +import org.sleuthkit.datamodel.LocalFile; +import org.sleuthkit.datamodel.TskException; +import org.sleuthkit.datamodel.VirtualDirectory; + +/** + * This class wraps nodes as they are passed to the DataResult viewers. It + * defines the actions that the node should have. + */ +public class DataResultFilterNode extends FilterNode { + + private ExplorerManager sourceEm; + private final DisplayableItemNodeVisitor> getActionsDIV; + private final DisplayableItemNodeVisitor getPreferredActionsDIV; + + /** + * the constructor + */ + public DataResultFilterNode(Node node, ExplorerManager em) { + super(node, new DataResultFilterChildren(node, em)); + this.sourceEm = em; + getActionsDIV = new GetPopupActionsDisplayableItemNodeVisitor(); + getPreferredActionsDIV = new GetPreferredActionsDisplayableItemNodeVisitor(); + } + + /** + * Right click action for the nodes that we want to pass to the directory + * table and the output view. + * + * @param popup + * @return actions + */ + @Override + public Action[] getActions(boolean popup) { + + List actions = new ArrayList<>(); + + final DisplayableItemNode originalNode = (DisplayableItemNode) this.getOriginal(); + actions.addAll(originalNode.accept(getActionsDIV)); + + //actions.add(new IndexContentFilesAction(nodeContent, "Index")); + + return actions.toArray(new Action[actions.size()]); + } + + /** + * Double click action for the nodes that we want to pass to the directory + * table and the output view. + * + * @return action + */ + @Override + public Action getPreferredAction() { + // double click action(s) for volume node or directory node + + final DisplayableItemNode originalNode; + originalNode = (DisplayableItemNode) this.getOriginal(); + + return originalNode.accept(getPreferredActionsDIV); + } + + @Override + public Node.PropertySet[] getPropertySets() { + Node.PropertySet[] propertySets = super.getPropertySets(); + + for (int i = 0; i < propertySets.length; i++) { + Node.PropertySet ps = propertySets[i]; + + if (ps.getName().equals(Sheet.PROPERTIES)) { + Sheet.Set newPs = new Sheet.Set(); + newPs.setName(ps.getName()); + newPs.setDisplayName(ps.getDisplayName()); + newPs.setShortDescription(ps.getShortDescription()); + + newPs.put(ps.getProperties()); + if (newPs.remove(AbstractFsContentNode.HIDE_PARENT) != null) { + newPs.remove(AbstractFilePropertyType.LOCATION.toString()); + } + propertySets[i] = newPs; + } + } + + return propertySets; + } + + /** + * Uses the default nodes actions per node, adds some custom ones and + * returns them per visited node type + */ + private static class GetPopupActionsDisplayableItemNodeVisitor extends DisplayableItemNodeVisitor.Default> { + + @Override + public List visit(BlackboardArtifactNode ban) { + //set up actions for artifact node based on its Content object + //TODO all actions need to be consolidated in single place! + //they should be set in individual Node subclass and using a utility to get Actions per Content sub-type + + List actions = new ArrayList<>(); + + //merge predefined specific node actions if bban subclasses have their own + for (Action a : ban.getActions(true)) { + actions.add(a); + } + BlackboardArtifact ba = ban.getLookup().lookup(BlackboardArtifact.class); + final int artifactTypeID = ba.getArtifactTypeID(); + + if (artifactTypeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID() + || artifactTypeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()) { + actions.add(new ViewContextAction("View File in Directory", ban)); + } else { + Content c = findLinked(ban); + if (c != null) { + actions.add(new ViewContextAction("View File in Directory", c)); + } + actions.add(new ViewContextAction("View Source File in Directory", ban)); + } + File f = ban.getLookup().lookup(File.class); + LayoutFile lf = null; + AbstractFile locF = null; + Directory d = null; + VirtualDirectory vd = null; + if (f != null) { + final FileNode fn = new FileNode(f); + actions.add(null); // creates a menu separator + actions.add(new NewWindowViewAction("View in New Window", fn)); + actions.add(new ExternalViewerAction("Open in External Viewer", fn)); + actions.add(null); // creates a menu separator + actions.add(ExtractAction.getInstance()); + actions.add(new HashSearchAction("Search for files with the same MD5 hash", fn)); + + //add file/result tag if itself is not a tag + if (artifactTypeID != BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE.getTypeID() + && artifactTypeID != BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_ARTIFACT.getTypeID()) { + actions.add(null); // creates a menu separator + actions.add(TagAbstractFileAction.getInstance()); + actions.add(TagBlackboardArtifactAction.getInstance()); + } + } + if ((d = ban.getLookup().lookup(Directory.class)) != null) { + DirectoryNode dn = new DirectoryNode(d); + actions.add(null); // creates a menu separator + actions.add(new NewWindowViewAction("View in New Window", dn)); + actions.add(new ExternalViewerAction("Open in External Viewer", dn)); + actions.add(null); // creates a menu separator + actions.add(ExtractAction.getInstance()); + + //add file/result tag if itself is not a tag + if (artifactTypeID != BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE.getTypeID() + && artifactTypeID != BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_ARTIFACT.getTypeID()) { + actions.add(null); // creates a menu separator + actions.add(TagAbstractFileAction.getInstance()); + actions.add(TagBlackboardArtifactAction.getInstance()); + } + } + if ((vd = ban.getLookup().lookup(VirtualDirectory.class)) != null) { + VirtualDirectoryNode dn = new VirtualDirectoryNode(vd); + actions.add(null); // creates a menu separator + actions.add(new NewWindowViewAction("View in New Window", dn)); + actions.add(new ExternalViewerAction("Open in External Viewer", dn)); + actions.add(null); // creates a menu separator + actions.add(ExtractAction.getInstance()); + + //add file/result tag if itself is not a tag + if (artifactTypeID != BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE.getTypeID() + && artifactTypeID != BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_ARTIFACT.getTypeID()) { + actions.add(null); // creates a menu separator + actions.add(TagAbstractFileAction.getInstance()); + actions.add(TagBlackboardArtifactAction.getInstance()); + } + } else if ((lf = ban.getLookup().lookup(LayoutFile.class)) != null) { + LayoutFileNode lfn = new LayoutFileNode(lf); + actions.add(null); // creates a menu separator + actions.add(new NewWindowViewAction("View in New Window", lfn)); + actions.add(new ExternalViewerAction("Open in External Viewer", lfn)); + actions.add(null); // creates a menu separator + actions.add(ExtractAction.getInstance()); + + //add tag if itself is not a tag + if (artifactTypeID != BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE.getTypeID() + && artifactTypeID != BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_ARTIFACT.getTypeID()) { + actions.add(null); // creates a menu separator + actions.add(TagAbstractFileAction.getInstance()); + actions.add(TagBlackboardArtifactAction.getInstance()); + } + } else if ((locF = ban.getLookup().lookup(LocalFile.class)) != null + || (locF = ban.getLookup().lookup(DerivedFile.class)) != null) { + final LocalFileNode locfn = new LocalFileNode(locF); + actions.add(null); // creates a menu separator + actions.add(new NewWindowViewAction("View in New Window", locfn)); + actions.add(new ExternalViewerAction("Open in External Viewer", locfn)); + actions.add(null); // creates a menu separator + actions.add(ExtractAction.getInstance()); + + //add tag if itself is not a tag + if (artifactTypeID != BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE.getTypeID() + && artifactTypeID != BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_ARTIFACT.getTypeID()) { + actions.add(null); // creates a menu separator + actions.add(TagAbstractFileAction.getInstance()); + actions.add(TagBlackboardArtifactAction.getInstance()); + } + } + + return actions; + } + + @Override + protected List defaultVisit(DisplayableItemNode ditem) { + //preserve the default node's actions + List actions = new ArrayList<>(); + + for (Action action : ditem.getActions(true)) { + actions.add(action); + } + + return actions; + } + + private Content findLinked(BlackboardArtifactNode ba) { + BlackboardArtifact art = ba.getLookup().lookup(BlackboardArtifact.class); + Content c = null; + try { + for (BlackboardAttribute attr : art.getAttributes()) { + if (attr.getAttributeTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID.getTypeID()) { + switch (attr.getValueType()) { + case INTEGER: + int i = attr.getValueInt(); + if (i != -1) { + c = art.getSleuthkitCase().getContentById(i); + } + break; + case LONG: + long l = attr.getValueLong(); + if (l != -1) { + c = art.getSleuthkitCase().getContentById(l); + } + break; + } + } + } + } catch (TskException ex) { + Logger.getLogger(this.getClass().getName()).log(Level.WARNING, "Error getting linked file", ex); + } + return c; + } + } + + private class GetPreferredActionsDisplayableItemNodeVisitor extends DisplayableItemNodeVisitor.Default { + + @Override + public AbstractAction visit(ImageNode in) { + return openChild(in); + } + + @Override + public AbstractAction visit(VolumeNode vn) { + return openChild(vn); + } + + @Override + public AbstractAction visit(ExtractedContentNode ecn) { + return openChild(ecn); + } + + @Override + public AbstractAction visit(KeywordHitsRootNode khrn) { + return openChild(khrn); + } + + @Override + public AbstractAction visit(HashsetHitsRootNode hhrn) { + return openChild(hhrn); + } + + @Override + public AbstractAction visit(HashsetHitsSetNode hhsn) { + return openChild(hhsn); + } + + @Override + public AbstractAction visit(EmailExtractedRootNode eern) { + return openChild(eern); + } + + @Override + public AbstractAction visit(EmailExtractedAccountNode eean) { + return openChild(eean); + } + + @Override + public AbstractAction visit(EmailExtractedFolderNode eefn) { + return openChild(eefn); + } + + @Override + public AbstractAction visit(RecentFilesNode rfn) { + return openChild(rfn); + } + + @Override + public AbstractAction visit(DeletedContentsNode dcn) { + return openChild(dcn); + } + + @Override + public AbstractAction visit(DeletedContentNode dcn) { + return openChild(dcn); + } + + @Override + public AbstractAction visit(FileSizeRootNode fsrn) { + return openChild(fsrn); + } + + @Override + public AbstractAction visit(FileSizeNode fsn) { + return openChild(fsn); + } + + @Override + public AbstractAction visit(BlackboardArtifactNode ban) { + return new ViewContextAction("View in Directory", ban); + } + + @Override + public AbstractAction visit(ArtifactTypeNode atn) { + return openChild(atn); + } + + @Override + public AbstractAction visit(TagNodeRoot tnr) { + return openChild(tnr); + } + + @Override + public AbstractAction visit(TagsNodeRoot tnr) { + return openChild(tnr); + } + + @Override + public AbstractAction visit(DirectoryNode dn) { + if (dn.getDisplayName().equals(DirectoryNode.DOTDOTDIR)) { + return openParent(dn); + } else if (!dn.getDisplayName().equals(DirectoryNode.DOTDIR)) { + return openChild(dn); + } else { + return null; + } + } + + @Override + public AbstractAction visit(VirtualDirectoryNode ldn) { + return openChild(ldn); + } + + @Override + public AbstractAction visit(FileNode fn) { + if (fn.hasContentChildren()) { + return openChild(fn); + } else { + return null; + } + } + + @Override + public AbstractAction visit(LocalFileNode dfn) { + if (dfn.hasContentChildren()) { + return openChild(dfn); + } else { + return null; + } + } + + @Override + public AbstractAction visit(FileSearchFilterNode fsfn) { + return openChild(fsfn); + } + + @Override + public AbstractAction visit(SearchFiltersNode sfn) { + return openChild(sfn); + } + + @Override + public AbstractAction visit(RecentFilesFilterNode rffn) { + return openChild(rffn); + } + + @Override + public AbstractAction visit(KeywordHitsListNode khsn) { + return openChild(khsn); + } + + @Override + public AbstractAction visit(KeywordHitsKeywordNode khmln) { + return openChild(khmln); + } + + @Override + protected AbstractAction defaultVisit(DisplayableItemNode c) { + return null; + } + + private AbstractAction openChild(AbstractNode node) { + final Node[] parentNode = sourceEm.getSelectedNodes(); + final Node parentContext = parentNode[0]; + final Node original = node; + + return new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + if (parentContext != null) { + final int childrenNodesCount = parentContext.getChildren().getNodesCount(); + for (int i = 0; i < childrenNodesCount; i++) { + Node selectedNode = parentContext.getChildren().getNodeAt(i); + if (selectedNode != null && selectedNode.getName().equals(original.getName())) { + try { + sourceEm.setExploredContextAndSelection(selectedNode, new Node[]{selectedNode}); + } catch (PropertyVetoException ex) { + // throw an error here + Logger logger = Logger.getLogger(DataResultFilterNode.class.getName()); + logger.log(Level.WARNING, "Error: can't open the selected directory.", ex); + } + } + } + } + } + }; + } + + private AbstractAction openParent(AbstractNode node) { + Node[] selectedNode = sourceEm.getSelectedNodes(); + Node selectedContext = selectedNode[0]; + final Node parentNode = selectedContext.getParentNode(); + + return new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + try { + sourceEm.setSelectedNodes(new Node[]{parentNode}); + } catch (PropertyVetoException ex) { + Logger logger = Logger.getLogger(DataResultFilterNode.class.getName()); + logger.log(Level.WARNING, "Error: can't open the parent directory.", ex); + } + } + }; + } + } } \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeFilterNode.java b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeFilterNode.java old mode 100644 new mode 100755 index 018679ae2c..d71ea25216 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeFilterNode.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeFilterNode.java @@ -1,160 +1,159 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2011 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.directorytree; - -import java.awt.event.ActionEvent; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.logging.Level; -import javax.swing.AbstractAction; -import javax.swing.Action; -import org.openide.nodes.FilterNode; -import org.openide.nodes.Node; -import org.openide.util.lookup.Lookups; -import org.openide.util.lookup.ProxyLookup; -import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.datamodel.AbstractContentNode; -import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; -import org.sleuthkit.autopsy.ingest.IngestDialog; -import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.Directory; -import org.sleuthkit.datamodel.Image; -import org.sleuthkit.datamodel.TskCoreException; - -/** - * This class sets the actions for the nodes in the directory tree and creates - * the children filter so that files and such are hidden from the tree. - * - */ -class DirectoryTreeFilterNode extends FilterNode { - - private static final Action collapseAll = new CollapseAction("Collapse All"); - private static final Logger logger = Logger.getLogger(DirectoryTreeFilterNode.class.getName()); - - /** - * the constructor - */ - DirectoryTreeFilterNode(Node arg, boolean createChildren) { - super(arg, DirectoryTreeFilterChildren.createInstance(arg, createChildren), - new ProxyLookup(Lookups.singleton(new OriginalNode(arg)), - arg.getLookup())); - } - - @Override - public String getDisplayName() { - final Node orig = getOriginal(); - - String name = orig.getDisplayName(); - - //do not show children counts for non content nodes - if (orig instanceof AbstractContentNode) { - //show only for file content nodes - AbstractFile file = getLookup().lookup(AbstractFile.class); - if (file != null) { - try { - final int numChildren = file.getChildrenCount(); - name = name + " (" + numChildren + ")"; - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error getting children count to display for file: " + file, ex); - } - - } - } - - return name; - } - - /** - * Right click action for the nodes in the directory tree. - * - * @param popup - * @return - */ - @Override - public Action[] getActions(boolean popup) { - List actions = new ArrayList(); - - final Content content = this.getLookup().lookup(Content.class); - if (content != null) { - actions.addAll(DirectoryTreeFilterNode.getDetailActions(content)); - - //extract dir action - Directory dir = this.getLookup().lookup(Directory.class); - if (dir != null) { - actions.add(new ExtractAction("Extract Directory", - getOriginal())); - } - - // file search action - final Image img = this.getLookup().lookup(Image.class); - if (img != null) { - actions.add(new FileSearchAction("Open File Search by Attributes")); - } - - //ingest action - actions.add(new AbstractAction("Run Ingest Modules") { - @Override - public void actionPerformed(ActionEvent e) { - final IngestDialog ingestDialog = new IngestDialog(); - ingestDialog.setContent(Collections.singletonList(content)); - ingestDialog.display(); - } - }); - } - - //check if delete actions should be added - final Node orig = getOriginal(); - //TODO add a mechanism to determine if DisplayableItemNode - if (orig instanceof DisplayableItemNode) { - actions.addAll(getDeleteActions((DisplayableItemNode) orig)); - } - - actions.add(collapseAll); - return actions.toArray(new Action[actions.size()]); - } - - private static List getDeleteActions(DisplayableItemNode original) { - List actions = new ArrayList(); - //actions.addAll(original.accept(getDeleteActionVisitor)); - return actions; - } - - private static List getDetailActions(Content c) { - List actions = new ArrayList(); - - actions.addAll(ExplorerNodeActionVisitor.getActions(c)); - - return actions; - } -} - -class OriginalNode { - - private Node original; - - OriginalNode(Node original) { - this.original = original; - } - - Node getNode() { - return original; - } +/* + * Autopsy Forensic Browser + * + * Copyright 2011 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.directorytree; + +import java.awt.event.ActionEvent; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.logging.Level; +import javax.swing.AbstractAction; +import javax.swing.Action; +import org.openide.nodes.FilterNode; +import org.openide.nodes.Node; +import org.openide.util.lookup.Lookups; +import org.openide.util.lookup.ProxyLookup; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.datamodel.AbstractContentNode; +import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; +import org.sleuthkit.autopsy.ingest.IngestDialog; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.Directory; +import org.sleuthkit.datamodel.Image; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * This class sets the actions for the nodes in the directory tree and creates + * the children filter so that files and such are hidden from the tree. + * + */ +class DirectoryTreeFilterNode extends FilterNode { + + private static final Action collapseAll = new CollapseAction("Collapse All"); + private static final Logger logger = Logger.getLogger(DirectoryTreeFilterNode.class.getName()); + + /** + * the constructor + */ + DirectoryTreeFilterNode(Node arg, boolean createChildren) { + super(arg, DirectoryTreeFilterChildren.createInstance(arg, createChildren), + new ProxyLookup(Lookups.singleton(new OriginalNode(arg)), + arg.getLookup())); + } + + @Override + public String getDisplayName() { + final Node orig = getOriginal(); + + String name = orig.getDisplayName(); + + //do not show children counts for non content nodes + if (orig instanceof AbstractContentNode) { + //show only for file content nodes + AbstractFile file = getLookup().lookup(AbstractFile.class); + if (file != null) { + try { + final int numChildren = file.getChildrenCount(); + name = name + " (" + numChildren + ")"; + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error getting children count to display for file: " + file, ex); + } + + } + } + + return name; + } + + /** + * Right click action for the nodes in the directory tree. + * + * @param popup + * @return + */ + @Override + public Action[] getActions(boolean popup) { + List actions = new ArrayList(); + + final Content content = this.getLookup().lookup(Content.class); + if (content != null) { + actions.addAll(DirectoryTreeFilterNode.getDetailActions(content)); + + //extract dir action + Directory dir = this.getLookup().lookup(Directory.class); + if (dir != null) { + actions.add(ExtractAction.getInstance()); + } + + // file search action + final Image img = this.getLookup().lookup(Image.class); + if (img != null) { + actions.add(new FileSearchAction("Open File Search by Attributes")); + } + + //ingest action + actions.add(new AbstractAction("Run Ingest Modules") { + @Override + public void actionPerformed(ActionEvent e) { + final IngestDialog ingestDialog = new IngestDialog(); + ingestDialog.setContent(Collections.singletonList(content)); + ingestDialog.display(); + } + }); + } + + //check if delete actions should be added + final Node orig = getOriginal(); + //TODO add a mechanism to determine if DisplayableItemNode + if (orig instanceof DisplayableItemNode) { + actions.addAll(getDeleteActions((DisplayableItemNode) orig)); + } + + actions.add(collapseAll); + return actions.toArray(new Action[actions.size()]); + } + + private static List getDeleteActions(DisplayableItemNode original) { + List actions = new ArrayList(); + //actions.addAll(original.accept(getDeleteActionVisitor)); + return actions; + } + + private static List getDetailActions(Content c) { + List actions = new ArrayList(); + + actions.addAll(ExplorerNodeActionVisitor.getActions(c)); + + return actions; + } +} + +class OriginalNode { + + private Node original; + + OriginalNode(Node original) { + this.original = original; + } + + Node getNode() { + return original; + } } \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/ExplorerNodeActionVisitor.java b/Core/src/org/sleuthkit/autopsy/directorytree/ExplorerNodeActionVisitor.java old mode 100644 new mode 100755 index 28479e83c3..90c1eded2e --- a/Core/src/org/sleuthkit/autopsy/directorytree/ExplorerNodeActionVisitor.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/ExplorerNodeActionVisitor.java @@ -1,383 +1,383 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2011 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.directorytree; - -import java.awt.Toolkit; -import java.awt.Dimension; -import java.awt.Font; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.logging.Level; -import javax.swing.AbstractAction; -import javax.swing.Action; -import javax.swing.JDialog; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JTable; -import javax.swing.table.DefaultTableModel; -import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.ContentVisitor; -import org.sleuthkit.datamodel.DerivedFile; -import org.sleuthkit.datamodel.Directory; -import org.sleuthkit.datamodel.FileSystem; -import org.sleuthkit.datamodel.Image; -import org.sleuthkit.datamodel.LocalFile; -import org.sleuthkit.datamodel.VirtualDirectory; -import org.sleuthkit.datamodel.Volume; - -public class ExplorerNodeActionVisitor extends ContentVisitor.Default> { - - private static ExplorerNodeActionVisitor instance = new ExplorerNodeActionVisitor(); - - public static List getActions(Content c) { - List actions = new ArrayList(); - - actions.addAll(c.accept(instance)); - //TODO: fix this - /* - while (c.isOnto()) { - try { - List children = c.getChildren(); - if (!children.isEmpty()) { - c = c.getChildren().get(0); - } else { - return actions; - } - } catch (TskException ex) { - Log.get(ExplorerNodeActionVisitor.class).log(Level.WARNING, "Error getting show detail actions.", ex); - return actions; - } - actions.addAll(c.accept(instance)); - }*/ - return actions; - } - - ExplorerNodeActionVisitor() { - } - - @Override - public List visit(final Image img) { - List lst = new ArrayList(); - lst.add(new ImageDetails("Image Details", img)); - //TODO lst.add(new ExtractAction("Extract Image", img)); - lst.add(new ExtractUnallocAction("Extract Unallocated Space to Single Files", img)); - return lst; - } - - @Override - public List visit(final FileSystem fs) { - return Collections.singletonList(new FileSystemDetails("File System Details", fs)); - } - - @Override - public List visit(final Volume vol) { - List lst = new ArrayList(); - lst.add(new VolumeDetails("Volume Details", vol)); - lst.add(new ExtractUnallocAction("Extract Unallocated Space to Single File", vol)); - return lst; - } - - @Override - public List visit(final Directory d) { - List actions = new ArrayList(); - actions.add(new TagAction(d)); - return actions; - } - - @Override - public List visit(final VirtualDirectory d) { - List actions = new ArrayList(); - actions.add(new TagAction(d)); - actions.add(new ExtractAction("Extract Directory", d)); - return actions; - } - - @Override - public List visit(final DerivedFile d) { - List actions = new ArrayList(); - actions.add(new ExtractAction("Extract File", d)); - actions.add(new TagAction(d)); - return actions; - } - - @Override - public List visit(final LocalFile d) { - List actions = new ArrayList(); - actions.add(new ExtractAction("Extract File", d)); - actions.add(new TagAction(d)); - return actions; - } - - @Override - public List visit(final org.sleuthkit.datamodel.File d) { - List actions = new ArrayList(); - actions.add(new ExtractAction("Extract File", d)); - actions.add(new TagAction(d)); - return actions; - } - - @Override - protected List defaultVisit(Content di) { - return Collections.emptyList(); - } - - //Below here are classes regarding node-specific actions - /** - * VolumeDetails class - */ - private class VolumeDetails extends AbstractAction { - - private final String title; - private final Volume vol; - - VolumeDetails(String title, Volume vol) { - super(title); - this.title = title; - this.vol = vol; - } - - @Override - public void actionPerformed(ActionEvent e) { - Logger.noteAction(ExplorerNodeActionVisitor.class); - - final JFrame frame = new JFrame(title); - final JDialog popUpWindow = new JDialog(frame, title, true); // to make the popUp Window to be modal - - - Dimension screenDimension = Toolkit.getDefaultToolkit().getScreenSize(); - - // set the popUp window / JFrame - popUpWindow.setSize(800, 400); - - int w = popUpWindow.getSize().width; - int h = popUpWindow.getSize().height; - - // set the location of the popUp Window on the center of the screen - popUpWindow.setLocation((screenDimension.width - w) / 2, (screenDimension.height - h) / 2); - - VolumeDetailsPanel volumeDetailPanel = new VolumeDetailsPanel(); - Boolean counter = false; - - volumeDetailPanel.setVolumeIDValue(Long.toString(vol.getAddr())); - volumeDetailPanel.setStartValue(Long.toString(vol.getStart())); - volumeDetailPanel.setLengthValue(Long.toString(vol.getLength())); - volumeDetailPanel.setDescValue(vol.getDescription()); - volumeDetailPanel.setFlagsValue(vol.getFlagsAsString()); - counter = true; - - if (counter) { - // add the volume detail panel to the popUp window - popUpWindow.add(volumeDetailPanel); - } else { - // error handler if no volume matches - JLabel error = new JLabel("Error: No Volume Matches."); - error.setFont(new Font("Arial", Font.BOLD, 24)); - popUpWindow.add(error); - } - - // add the command to close the window to the button on the Volume Detail Panel - volumeDetailPanel.setOKButtonActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - popUpWindow.dispose(); - } - }); - popUpWindow.pack(); - popUpWindow.setResizable(false); - popUpWindow.setVisible(true); - - } - } - - /** - * ImageDetails panel class - */ - private class ImageDetails extends AbstractAction { - - final String title; - final Image img; - - ImageDetails(String title, Image img) { - super(title); - this.title = title; - this.img = img; - } - - @Override - public void actionPerformed(ActionEvent e) { - Logger.noteAction(ExplorerNodeActionVisitor.class); - - final JFrame frame = new JFrame(title); - final JDialog popUpWindow = new JDialog(frame, title, true); // to make the popUp Window to be modal - // if we select the Image Details menu - - Dimension screenDimension = Toolkit.getDefaultToolkit().getScreenSize(); - - // set the popUp window / JFrame - popUpWindow.setSize(750, 400); - - int w = popUpWindow.getSize().width; - int h = popUpWindow.getSize().height; - - // set the location of the popUp Window on the center of the screen - popUpWindow.setLocation((screenDimension.width - w) / 2, (screenDimension.height - h) / 2); - - ImageDetailsPanel imgDetailPanel = new ImageDetailsPanel(); - Boolean counter = false; - - imgDetailPanel.setImgNameValue(img.getName()); - imgDetailPanel.setImgTypeValue(Image.imageTypeToString(img.getType())); - imgDetailPanel.setImgSectorSizeValue(Long.toString(img.getSsize())); - counter = true; - - if (counter) { - // add the volume detail panel to the popUp window - popUpWindow.add(imgDetailPanel); - } else { - // error handler if no volume matches - JLabel error = new JLabel("Error: No Volume Matches."); - error.setFont(new Font("Arial", Font.BOLD, 24)); - popUpWindow.add(error); - } - - // add the command to close the window to the button on the Volume Detail Panel - imgDetailPanel.setOKButtonActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - popUpWindow.dispose(); - } - }); - - - popUpWindow.pack(); - popUpWindow.setResizable(false); - popUpWindow.setVisible(true); - } - } - - /** - * FileSystemDetails class - */ - private class FileSystemDetails extends AbstractAction { - - private final FileSystem fs; - private final String title; - - FileSystemDetails(String title, FileSystem fs) { - super(title); - this.title = title; - this.fs = fs; - } - - @Override - public void actionPerformed(ActionEvent e) { - Dimension screenDimension = Toolkit.getDefaultToolkit().getScreenSize(); - - Logger.noteAction(ExplorerNodeActionVisitor.class); - - final JFrame frame = new JFrame(title); - final JDialog popUpWindow = new JDialog(frame, title, true); // to make the popUp Window to be modal - - // set the popUp window / JFrame - - popUpWindow.setSize(1000, 500); - - int w = popUpWindow.getSize().width; - int h = popUpWindow.getSize().height; - - // set the location of the popUp Window on the center of the screen - popUpWindow.setLocation((screenDimension.width - w) / 2, (screenDimension.height - h) / 2); - - String[] columnNames = new String[]{ - "fs_id", - "img_offset", - "par_id", - "fs_type", - "block_size", - "block_count", - "root_inum", - "first_inum", - "last_inum" - }; - - Object[][] rowValues = new Object[1][9]; - - Content parent = null; - try { - parent = fs.getParent(); - } catch (Exception ex) { - throw new RuntimeException("Problem getting parent from " + FileSystem.class.getName() + ": " + fs, ex); - } - long id = -1; - if (parent != null) { - id = parent.getId(); - } - - Arrays.fill(rowValues, 0, 1, new Object[]{ - fs.getId(), - fs.getImageOffset(), - id, - fs.getFsType(), - fs.getBlock_size(), - fs.getBlock_count(), - fs.getRoot_inum(), - fs.getFirst_inum(), - fs.getLastInum() - }); - - - JTable table = new JTable(new DefaultTableModel(rowValues, columnNames)); - - FileSystemDetailsPanel fsdPanel = new FileSystemDetailsPanel(); - - // add the command to close the window to the button on the Volume Detail Panel - fsdPanel.setOKButtonActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - popUpWindow.dispose(); - } - }); - - try { - fsdPanel.setFileSystemTypeValue(table.getValueAt(0, 3).toString()); - fsdPanel.setImageOffsetValue(table.getValueAt(0, 1).toString()); - fsdPanel.setVolumeIDValue(table.getValueAt(0, 2).toString()); //TODO: fix this to parent id, not vol id - fsdPanel.setBlockSizeValue(table.getValueAt(0, 4).toString()); - fsdPanel.setBlockCountValue(table.getValueAt(0, 5).toString()); - fsdPanel.setRootInumValue(table.getValueAt(0, 6).toString()); - fsdPanel.setFirstInumValue(table.getValueAt(0, 7).toString()); - fsdPanel.setLastInumValue(table.getValueAt(0, 8).toString()); - - popUpWindow.add(fsdPanel); - } catch (Exception ex) { - Logger.getLogger(ExplorerNodeActionVisitor.class.getName()).log(Level.WARNING, "Error setting up File System Details panel.", ex); - } - - popUpWindow.pack(); - popUpWindow.setResizable(false); - popUpWindow.setVisible(true); - - } - } +/* + * Autopsy Forensic Browser + * + * Copyright 2011 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.directorytree; + +import java.awt.Toolkit; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.logging.Level; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JTable; +import javax.swing.table.DefaultTableModel; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.ContentVisitor; +import org.sleuthkit.datamodel.DerivedFile; +import org.sleuthkit.datamodel.Directory; +import org.sleuthkit.datamodel.FileSystem; +import org.sleuthkit.datamodel.Image; +import org.sleuthkit.datamodel.LocalFile; +import org.sleuthkit.datamodel.VirtualDirectory; +import org.sleuthkit.datamodel.Volume; + +public class ExplorerNodeActionVisitor extends ContentVisitor.Default> { + + private static ExplorerNodeActionVisitor instance = new ExplorerNodeActionVisitor(); + + public static List getActions(Content c) { + List actions = new ArrayList(); + + actions.addAll(c.accept(instance)); + //TODO: fix this + /* + while (c.isOnto()) { + try { + List children = c.getChildren(); + if (!children.isEmpty()) { + c = c.getChildren().get(0); + } else { + return actions; + } + } catch (TskException ex) { + Log.get(ExplorerNodeActionVisitor.class).log(Level.WARNING, "Error getting show detail actions.", ex); + return actions; + } + actions.addAll(c.accept(instance)); + }*/ + return actions; + } + + ExplorerNodeActionVisitor() { + } + + @Override + public List visit(final Image img) { + List lst = new ArrayList(); + lst.add(new ImageDetails("Image Details", img)); + //TODO lst.add(new ExtractAction("Extract Image", img)); + lst.add(new ExtractUnallocAction("Extract Unallocated Space to Single Files", img)); + return lst; + } + + @Override + public List visit(final FileSystem fs) { + return Collections.singletonList(new FileSystemDetails("File System Details", fs)); + } + + @Override + public List visit(final Volume vol) { + List lst = new ArrayList(); + lst.add(new VolumeDetails("Volume Details", vol)); + lst.add(new ExtractUnallocAction("Extract Unallocated Space to Single File", vol)); + return lst; + } + + @Override + public List visit(final Directory d) { + List actions = new ArrayList(); + actions.add(TagAbstractFileAction.getInstance()); + return actions; + } + + @Override + public List visit(final VirtualDirectory d) { + List actions = new ArrayList(); + actions.add(ExtractAction.getInstance()); + actions.add(TagAbstractFileAction.getInstance()); + return actions; + } + + @Override + public List visit(final DerivedFile d) { + List actions = new ArrayList(); + actions.add(ExtractAction.getInstance()); + actions.add(TagAbstractFileAction.getInstance()); + return actions; + } + + @Override + public List visit(final LocalFile d) { + List actions = new ArrayList(); + actions.add(ExtractAction.getInstance()); + actions.add(TagAbstractFileAction.getInstance()); + return actions; + } + + @Override + public List visit(final org.sleuthkit.datamodel.File d) { + List actions = new ArrayList(); + actions.add(ExtractAction.getInstance()); + actions.add(TagAbstractFileAction.getInstance()); + return actions; + } + + @Override + protected List defaultVisit(Content di) { + return Collections.emptyList(); + } + + //Below here are classes regarding node-specific actions + /** + * VolumeDetails class + */ + private class VolumeDetails extends AbstractAction { + + private final String title; + private final Volume vol; + + VolumeDetails(String title, Volume vol) { + super(title); + this.title = title; + this.vol = vol; + } + + @Override + public void actionPerformed(ActionEvent e) { + Logger.noteAction(ExplorerNodeActionVisitor.class); + + final JFrame frame = new JFrame(title); + final JDialog popUpWindow = new JDialog(frame, title, true); // to make the popUp Window to be modal + + + Dimension screenDimension = Toolkit.getDefaultToolkit().getScreenSize(); + + // set the popUp window / JFrame + popUpWindow.setSize(800, 400); + + int w = popUpWindow.getSize().width; + int h = popUpWindow.getSize().height; + + // set the location of the popUp Window on the center of the screen + popUpWindow.setLocation((screenDimension.width - w) / 2, (screenDimension.height - h) / 2); + + VolumeDetailsPanel volumeDetailPanel = new VolumeDetailsPanel(); + Boolean counter = false; + + volumeDetailPanel.setVolumeIDValue(Long.toString(vol.getAddr())); + volumeDetailPanel.setStartValue(Long.toString(vol.getStart())); + volumeDetailPanel.setLengthValue(Long.toString(vol.getLength())); + volumeDetailPanel.setDescValue(vol.getDescription()); + volumeDetailPanel.setFlagsValue(vol.getFlagsAsString()); + counter = true; + + if (counter) { + // add the volume detail panel to the popUp window + popUpWindow.add(volumeDetailPanel); + } else { + // error handler if no volume matches + JLabel error = new JLabel("Error: No Volume Matches."); + error.setFont(new Font("Arial", Font.BOLD, 24)); + popUpWindow.add(error); + } + + // add the command to close the window to the button on the Volume Detail Panel + volumeDetailPanel.setOKButtonActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + popUpWindow.dispose(); + } + }); + popUpWindow.pack(); + popUpWindow.setResizable(false); + popUpWindow.setVisible(true); + + } + } + + /** + * ImageDetails panel class + */ + private class ImageDetails extends AbstractAction { + + final String title; + final Image img; + + ImageDetails(String title, Image img) { + super(title); + this.title = title; + this.img = img; + } + + @Override + public void actionPerformed(ActionEvent e) { + Logger.noteAction(ExplorerNodeActionVisitor.class); + + final JFrame frame = new JFrame(title); + final JDialog popUpWindow = new JDialog(frame, title, true); // to make the popUp Window to be modal + // if we select the Image Details menu + + Dimension screenDimension = Toolkit.getDefaultToolkit().getScreenSize(); + + // set the popUp window / JFrame + popUpWindow.setSize(750, 400); + + int w = popUpWindow.getSize().width; + int h = popUpWindow.getSize().height; + + // set the location of the popUp Window on the center of the screen + popUpWindow.setLocation((screenDimension.width - w) / 2, (screenDimension.height - h) / 2); + + ImageDetailsPanel imgDetailPanel = new ImageDetailsPanel(); + Boolean counter = false; + + imgDetailPanel.setImgNameValue(img.getName()); + imgDetailPanel.setImgTypeValue(Image.imageTypeToString(img.getType())); + imgDetailPanel.setImgSectorSizeValue(Long.toString(img.getSsize())); + counter = true; + + if (counter) { + // add the volume detail panel to the popUp window + popUpWindow.add(imgDetailPanel); + } else { + // error handler if no volume matches + JLabel error = new JLabel("Error: No Volume Matches."); + error.setFont(new Font("Arial", Font.BOLD, 24)); + popUpWindow.add(error); + } + + // add the command to close the window to the button on the Volume Detail Panel + imgDetailPanel.setOKButtonActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + popUpWindow.dispose(); + } + }); + + + popUpWindow.pack(); + popUpWindow.setResizable(false); + popUpWindow.setVisible(true); + } + } + + /** + * FileSystemDetails class + */ + private class FileSystemDetails extends AbstractAction { + + private final FileSystem fs; + private final String title; + + FileSystemDetails(String title, FileSystem fs) { + super(title); + this.title = title; + this.fs = fs; + } + + @Override + public void actionPerformed(ActionEvent e) { + Dimension screenDimension = Toolkit.getDefaultToolkit().getScreenSize(); + + Logger.noteAction(ExplorerNodeActionVisitor.class); + + final JFrame frame = new JFrame(title); + final JDialog popUpWindow = new JDialog(frame, title, true); // to make the popUp Window to be modal + + // set the popUp window / JFrame + + popUpWindow.setSize(1000, 500); + + int w = popUpWindow.getSize().width; + int h = popUpWindow.getSize().height; + + // set the location of the popUp Window on the center of the screen + popUpWindow.setLocation((screenDimension.width - w) / 2, (screenDimension.height - h) / 2); + + String[] columnNames = new String[]{ + "fs_id", + "img_offset", + "par_id", + "fs_type", + "block_size", + "block_count", + "root_inum", + "first_inum", + "last_inum" + }; + + Object[][] rowValues = new Object[1][9]; + + Content parent = null; + try { + parent = fs.getParent(); + } catch (Exception ex) { + throw new RuntimeException("Problem getting parent from " + FileSystem.class.getName() + ": " + fs, ex); + } + long id = -1; + if (parent != null) { + id = parent.getId(); + } + + Arrays.fill(rowValues, 0, 1, new Object[]{ + fs.getId(), + fs.getImageOffset(), + id, + fs.getFsType(), + fs.getBlock_size(), + fs.getBlock_count(), + fs.getRoot_inum(), + fs.getFirst_inum(), + fs.getLastInum() + }); + + + JTable table = new JTable(new DefaultTableModel(rowValues, columnNames)); + + FileSystemDetailsPanel fsdPanel = new FileSystemDetailsPanel(); + + // add the command to close the window to the button on the Volume Detail Panel + fsdPanel.setOKButtonActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + popUpWindow.dispose(); + } + }); + + try { + fsdPanel.setFileSystemTypeValue(table.getValueAt(0, 3).toString()); + fsdPanel.setImageOffsetValue(table.getValueAt(0, 1).toString()); + fsdPanel.setVolumeIDValue(table.getValueAt(0, 2).toString()); //TODO: fix this to parent id, not vol id + fsdPanel.setBlockSizeValue(table.getValueAt(0, 4).toString()); + fsdPanel.setBlockCountValue(table.getValueAt(0, 5).toString()); + fsdPanel.setRootInumValue(table.getValueAt(0, 6).toString()); + fsdPanel.setFirstInumValue(table.getValueAt(0, 7).toString()); + fsdPanel.setLastInumValue(table.getValueAt(0, 8).toString()); + + popUpWindow.add(fsdPanel); + } catch (Exception ex) { + Logger.getLogger(ExplorerNodeActionVisitor.class.getName()).log(Level.WARNING, "Error setting up File System Details panel.", ex); + } + + popUpWindow.pack(); + popUpWindow.setResizable(false); + popUpWindow.setVisible(true); + + } + } } \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/ExtractAction.java b/Core/src/org/sleuthkit/autopsy/directorytree/ExtractAction.java index 6b183644f1..42ee518fa2 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/ExtractAction.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/ExtractAction.java @@ -30,83 +30,42 @@ import javax.swing.JOptionPane; import javax.swing.SwingWorker; import org.netbeans.api.progress.ProgressHandle; import org.netbeans.api.progress.ProgressHandleFactory; +import org.openide.explorer.ExplorerManager; import org.openide.nodes.Node; import org.openide.util.Cancellable; +import org.openide.util.Lookup; +import org.openide.util.Utilities; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer; +import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable; import org.sleuthkit.autopsy.coreutils.FileUtil; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; -import org.sleuthkit.autopsy.datamodel.ContentUtils; import org.sleuthkit.autopsy.datamodel.ContentUtils.ExtractFscContentVisitor; import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.ContentVisitor; -import org.sleuthkit.datamodel.Directory; /** * Exports files and folders */ public final class ExtractAction extends AbstractAction { - - private static final InitializeContentVisitor initializeCV = new InitializeContentVisitor(); - private AbstractFile content; private Logger logger = Logger.getLogger(ExtractAction.class.getName()); - public ExtractAction(String title, Node contentNode) { - super(title); - Content tempContent = contentNode.getLookup().lookup(Content.class); + // This class is a singleton to support multi-selection of nodes, since + // org.openide.nodes.NodeOp.findActions(Node[] nodes) will only pick up an Action if every + // node in the array returns a reference to the same action object from Node.getActions(boolean). + private static ExtractAction instance; - this.content = tempContent.accept(initializeCV); - this.setEnabled(content != null); - } - - public ExtractAction(String title, Content content) { - super(title); + public static synchronized ExtractAction getInstance() { + if (null == instance) { + instance = new ExtractAction(); + } - this.content = content.accept(initializeCV); - this.setEnabled(this.content != null); + return instance; } - /** - * Returns the FsContent if it is supported, otherwise null - */ - private static class InitializeContentVisitor extends ContentVisitor.Default { - - @Override - public AbstractFile visit(org.sleuthkit.datamodel.File f) { - return f; - } - - @Override - public AbstractFile visit(org.sleuthkit.datamodel.LayoutFile lf) { - return lf; - } - - @Override - public AbstractFile visit(org.sleuthkit.datamodel.DerivedFile df) { - return df; - } - - @Override - public AbstractFile visit(org.sleuthkit.datamodel.LocalFile lf) { - return lf; - } - - @Override - public AbstractFile visit(org.sleuthkit.datamodel.VirtualDirectory vd) { - return vd; - } - - @Override - public AbstractFile visit(Directory dir) { - return ContentUtils.isDotDirectory(dir) ? null : dir; - } - - @Override - protected AbstractFile defaultVisit(Content cntnt) { - return null; - } + private ExtractAction() { + super("Extract"); } - + /** * Asks user to choose destination, then extracts content/directory to * destination (recursing on directories) @@ -114,10 +73,44 @@ public final class ExtractAction extends AbstractAction { */ @Override public void actionPerformed(ActionEvent e) { + Lookup.Result res = Utilities.actionsGlobalContext().lookupResult(ExplorerManager.Provider.class); + for (ExplorerManager.Provider dude : res.allInstances()) { + ExplorerManager ex = dude.getExplorerManager(); + Node[] selectedNodes = ex.getSelectedNodes(); + for (Node node : selectedNodes) { + String name = node.getDisplayName(); + } + } + + DataResultViewerTable resultViewer = (DataResultViewerTable)Lookup.getDefault().lookup(DataResultViewer.class); + if (null == resultViewer) { + Logger.getLogger(ExtractAction.class.getName()).log(Level.SEVERE, "Could not get DataResultViewerTable from Lookup"); + return; + } + + Node[] selectedNodes = resultViewer.getExplorerManager().getSelectedNodes(); + if (selectedNodes.length <= 0) { + Logger.getLogger(ExtractAction.class.getName()).log(Level.SEVERE, "Tried to perform tagging of Nodes with no Nodes selected"); + return; + } + + for (Node node : selectedNodes) { + AbstractFile file = node.getLookup().lookup(AbstractFile.class); + if (null != file) { + extractFile(e, file); + } + else { + // RJCTODO +// Logger.getLogger(org.sleuthkit.autopsy.directorytree.TagAbstractFileAction.TagAbstractFileMenu.class.getName()).log(Level.SEVERE, "Node not associated with an AbstractFile object"); + } + } + } + + private void extractFile(ActionEvent e, AbstractFile file) { // Get content and check that it's okay to overwrite existing content JFileChooser fc = new JFileChooser(); fc.setCurrentDirectory(new File(Case.getCurrentCase().getCaseDirectory())); - fc.setSelectedFile(new File(this.content.getName())); + fc.setSelectedFile(new File(file.getName())); int returnValue = fc.showSaveDialog((Component) e.getSource()); if (returnValue == JFileChooser.APPROVE_OPTION) { @@ -144,12 +137,12 @@ public final class ExtractAction extends AbstractAction { try { ExtractFileThread extract = new ExtractFileThread(); - extract.init(this.content, e, destination); + extract.init(file, e, destination); extract.execute(); } catch (Exception ex) { logger.log(Level.WARNING, "Unable to start background thread.", ex); } - } + } } private class ExtractFileThread extends SwingWorker { @@ -230,6 +223,5 @@ public final class ExtractAction extends AbstractAction { } } } - } - + } } diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/TagAbstractFileAction.java b/Core/src/org/sleuthkit/autopsy/directorytree/TagAbstractFileAction.java new file mode 100755 index 0000000000..bf5458b889 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/directorytree/TagAbstractFileAction.java @@ -0,0 +1,67 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2013 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.directorytree; + +import java.util.logging.Level; +import org.openide.nodes.Node; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.datamodel.Tags; +import org.sleuthkit.datamodel.AbstractFile; + +public class TagAbstractFileAction extends TagAction { + // This class is a singleton to support multi-selection of nodes, since + // org.openide.nodes.NodeOp.findActions(Node[] nodes) will only pick up an Action if every + // node in the array returns a reference to the same action object from Node.getActions(boolean). + private static TagAbstractFileAction instance; + + public static synchronized TagAbstractFileAction getInstance() { + if (null == instance) { + instance = new TagAbstractFileAction(); + } + + return instance; + } + + private TagAbstractFileAction() { + } + + @Override + protected TagMenu getTagMenu(Node[] selectedNodes) { + return new TagAbstractFileMenu(selectedNodes); + } + + private static class TagAbstractFileMenu extends TagMenu { + public TagAbstractFileMenu(Node[] nodes) { + super((nodes.length > 1 ? "Tag Files" : "Tag File"), nodes); + } + + @Override + protected void tagNodes(String tagName, String comment) { + for (Node node : getNodes()) { + AbstractFile file = node.getLookup().lookup(AbstractFile.class); + if (null != file) { + Tags.createTag(file, tagName, comment); + } + else { + Logger.getLogger(org.sleuthkit.autopsy.directorytree.TagAbstractFileAction.TagAbstractFileMenu.class.getName()).log(Level.SEVERE, "Node not associated with an AbstractFile object"); + } + } + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/TagAction.java b/Core/src/org/sleuthkit/autopsy/directorytree/TagAction.java index 35c755c0e2..e7bae82cb9 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/TagAction.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/TagAction.java @@ -19,107 +19,82 @@ package org.sleuthkit.autopsy.directorytree; import java.awt.event.ActionEvent; +import java.util.Collection; import java.util.logging.Level; import javax.swing.AbstractAction; -import javax.swing.JMenu; import javax.swing.JMenuItem; +import org.openide.explorer.ExplorerManager; import org.openide.nodes.Node; import org.openide.util.actions.Presenter; +import org.openide.util.Lookup; +import org.openide.util.Utilities; +import org.openide.windows.TopComponent; import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.datamodel.ContentUtils; -import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.BlackboardArtifact; -import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.ContentVisitor; -import org.sleuthkit.datamodel.Directory; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer; +import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable; /** - * Action on a file or artifact that adds a tag and - * reloads the directory tree. Supports tagging of AbstractFiles and - * BlackboardArtifacts. - * - * TODO add use enters description and hierarchy (TSK_TAG_NAME with slashes) + * Action on a file or artifact that adds a tag and reloads the directory tree. + * Supports tagging of AbstractFiles and BlackboardArtifacts. */ -public class TagAction extends AbstractAction implements Presenter.Popup { - - private static final Logger logger = Logger.getLogger(TagAction.class.getName()); - private JMenu tagMenu; - private final InitializeBookmarkFileV initializer = new InitializeBookmarkFileV(); - - public TagAction(Node contentNode) { - AbstractFile file = contentNode.getLookup().lookup(AbstractFile.class); - if (file != null) { - tagMenu = new TagMenu(file); - return; - } - - BlackboardArtifact bba = contentNode.getLookup().lookup(BlackboardArtifact.class); - if (bba != null) { - tagMenu = new TagMenu(bba); - return; - } - - logger.log(Level.SEVERE, "Tried to create a " + TagAction.class.getName() - + " using a Node whose lookup did not contain an AbstractFile or a BlackboardArtifact."); - } - - public TagAction(AbstractFile file) { - tagMenu = new TagMenu(file); - } - - public TagAction(BlackboardArtifact bba) { - tagMenu = new TagMenu(bba); - } - +public abstract class TagAction extends AbstractAction implements Presenter.Popup { @Override - public JMenuItem getPopupPresenter() { - return tagMenu; - } - - /** - * Returns the FsContent if it is supported, otherwise null - */ - private static class InitializeBookmarkFileV extends ContentVisitor.Default { - - @Override - public AbstractFile visit(org.sleuthkit.datamodel.File f) { - return f; - } - - @Override - public AbstractFile visit(org.sleuthkit.datamodel.LayoutFile lf) { - return lf; + public JMenuItem getPopupPresenter() { + Lookup.Result res = Utilities.actionsGlobalContext().lookupResult(ExplorerManager.Provider.class); + for (ExplorerManager.Provider dude : res.allInstances()) { + ExplorerManager ex = dude.getExplorerManager(); + Node[] selectedNodes = ex.getSelectedNodes(); + for (Node node : selectedNodes) { + String name = node.getDisplayName(); + } } - @Override - public AbstractFile visit(org.sleuthkit.datamodel.DerivedFile lf) { - return lf; + Collection tops = Lookup.getDefault().lookupAll(TopComponent.class); + for (TopComponent top : tops) { + int x = 0; } - @Override - public AbstractFile visit(org.sleuthkit.datamodel.LocalFile lf) { - return lf; - } - - @Override - public AbstractFile visit(org.sleuthkit.datamodel.VirtualDirectory ld) { - return ld; - } - - @Override - public AbstractFile visit(Directory dir) { - return ContentUtils.isDotDirectory(dir) ? null : dir; - } - - @Override - protected AbstractFile defaultVisit(Content cntnt) { + DirectoryTreeTopComponent directoryTree = Lookup.getDefault().lookup(DirectoryTreeTopComponent.class); + if (null == directoryTree) { + Logger.getLogger(TagAction.class.getName()).log(Level.SEVERE, "Could not get DataResultViewerTable from Lookup"); return null; } - } + Node[] activatedNodes = directoryTree.getActivatedNodes(); + for (Node node : activatedNodes) { + String name = node.getDisplayName(); + } + + Lookup.Result resultViewers = Lookup.getDefault().lookupResult(DataResultViewer.class); + for (DataResultViewer resultViewer : resultViewers.allInstances()) { + if (resultViewer instanceof DataResultViewerTable) { + Node[] selectedNodes = ((DataResultViewerTable)resultViewer).getExplorerManager().getSelectedNodes(); + for (Node node : selectedNodes) { + String name = node.getDisplayName(); + } + } + } + + DataResultViewerTable resultViewer = (DataResultViewerTable)Lookup.getDefault().lookup(DataResultViewer.class); + if (null == resultViewer) { + Logger.getLogger(TagAction.class.getName()).log(Level.SEVERE, "Could not get DataResultViewerTable from Lookup"); + return null; + } + + Node[] selectedNodes = resultViewer.getExplorerManager().getSelectedNodes(); + if (selectedNodes.length <= 0) { + Logger.getLogger(TagAction.class.getName()).log(Level.SEVERE, "Tried to perform tagging of Nodes with no Nodes selected"); + return null; + } + + return getTagMenu(selectedNodes); + } + + protected abstract TagMenu getTagMenu(Node[] selectedNodes); + @Override public void actionPerformed(ActionEvent e) { - // Do nothing - this action should never be performed - // Submenu actions are invoked instead + // Do nothing - this action should never be performed. + // Submenu actions are invoked instead. } } \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/TagAndCommentDialog.java b/Core/src/org/sleuthkit/autopsy/directorytree/TagAndCommentDialog.java index a36a78809b..6a66e55689 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/TagAndCommentDialog.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/TagAndCommentDialog.java @@ -30,28 +30,50 @@ import javax.swing.JFrame; import javax.swing.KeyStroke; import org.openide.windows.WindowManager; import org.sleuthkit.autopsy.datamodel.Tags; -import org.sleuthkit.autopsy.datamodel.Tags.Taggable; -import org.sleuthkit.datamodel.BlackboardArtifact; /** * Tag dialog for tagging files and results. User enters an optional comment. */ public class TagAndCommentDialog extends JDialog { - private static final String TAG_ICON_PATH = "org/sleuthkit/autopsy/images/tag-folder-blue-icon-16.png"; - private static final String BOOKMARK_ICON_PATH = "org/sleuthkit/autopsy/images/star-bookmark-icon-16.png"; - private static final String NO_TAG_MESSAGE = "No Tags"; - - private Taggable taggable; + private static final String NO_TAG_MESSAGE = "No Tags"; + private String tagName = ""; + private String comment = ""; + public static class CommentedTag { + private String name; + private String comment; + + CommentedTag(String name, String comment) { + this.name = name; + this.comment = comment; + } + + public String getName() { + return name; + } + + public String getComment() { + return comment; + } + } + + public static CommentedTag doDialog() { + TagAndCommentDialog dialog = new TagAndCommentDialog(); + if (!dialog.tagName.isEmpty()) { + return new CommentedTag(dialog.tagName, dialog.comment); + } + else { + return null; + } + } + /** * Creates new form TagDialog */ - public TagAndCommentDialog(Taggable taggable) { + private TagAndCommentDialog() { super((JFrame)WindowManager.getDefault().getMainWindow(), "Tag and Comment", true); - this.taggable = taggable; - initComponents(); // Close the dialog when Esc is pressed @@ -60,8 +82,8 @@ public class TagAndCommentDialog extends JDialog { inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), cancelName); ActionMap actionMap = getRootPane().getActionMap(); actionMap.put(cancelName, new AbstractAction() { + @Override public void actionPerformed(ActionEvent e) { - //doClose(RET_CANCEL); dispose(); } }); @@ -81,15 +103,10 @@ public class TagAndCommentDialog extends JDialog { //center it this.setLocationRelativeTo(WindowManager.getDefault().getMainWindow()); - - customizeComponent(); setVisible(true); // blocks } - - private void customizeComponent() { - } - + /** * This method is called from within the constructor to initialize the form. * WARNING: Do NOT modify this code. The content of this method is always @@ -195,22 +212,12 @@ public class TagAndCommentDialog extends JDialog { }// //GEN-END:initComponents private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed - //doClose(RET_OK); - - // get the selected tag and comment - String selectedTag = (String)tagCombo.getSelectedItem(); - String comment = commentText.getText(); - - // create the tag - taggable.createTag(selectedTag, comment); - - refreshDirectoryTree(); - + tagName = (String)tagCombo.getSelectedItem(); + comment = commentText.getText(); dispose(); }//GEN-LAST:event_okButtonActionPerformed private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed - //doClose(RET_CANCEL); dispose(); }//GEN-LAST:event_cancelButtonActionPerformed @@ -218,14 +225,12 @@ public class TagAndCommentDialog extends JDialog { * Closes the dialog */ private void closeDialog(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_closeDialog - //doClose(RET_CANCEL); dispose(); }//GEN-LAST:event_closeDialog private void newTagButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_newTagButtonActionPerformed String newTagName = CreateTagDialog.getNewTagNameDialog(null); if (newTagName != null) { - //tagsModel.addElement(newTagName); tagCombo.addItem(newTagName); tagCombo.setSelectedItem(newTagName); } @@ -240,12 +245,4 @@ public class TagAndCommentDialog extends JDialog { private javax.swing.JComboBox tagCombo; private javax.swing.JLabel tagLabel; // End of variables declaration//GEN-END:variables - //private int returnStatus = RET_CANCEL; - - private void refreshDirectoryTree() { - //TODO instead should send event to node children, which will call its refresh() / refreshKeys() - DirectoryTreeTopComponent viewer = DirectoryTreeTopComponent.findInstance(); - viewer.refreshTree(BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE); - viewer.refreshTree(BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_ARTIFACT); - } } diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/TagBlackboardArtifactAction.java b/Core/src/org/sleuthkit/autopsy/directorytree/TagBlackboardArtifactAction.java new file mode 100755 index 0000000000..7754b127ad --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/directorytree/TagBlackboardArtifactAction.java @@ -0,0 +1,67 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2013 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.directorytree; + +import java.util.logging.Level; +import org.openide.nodes.Node; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.datamodel.Tags; +import org.sleuthkit.datamodel.BlackboardArtifact; + +public class TagBlackboardArtifactAction extends TagAction { + // This class is a singleton to support multi-selection of nodes, since + // org.openide.nodes.NodeOp.findActions(Node[] nodes) will only pick up an Action if every + // node in the array returns a reference to the same action object from Node.getActions(boolean). + private static TagBlackboardArtifactAction instance; + + public static synchronized TagBlackboardArtifactAction getInstance() { + if (null == instance) { + instance = new TagBlackboardArtifactAction(); + } + + return instance; + } + + private TagBlackboardArtifactAction() { + } + + @Override + protected TagMenu getTagMenu(Node[] selectedNodes) { + return new TagBlackboardArtifactMenu(selectedNodes); + } + + private static class TagBlackboardArtifactMenu extends TagMenu { + public TagBlackboardArtifactMenu(Node[] nodes) { + super((nodes.length > 1 ? "Tag Results" : "Tag Result"), nodes); + } + + @Override + protected void tagNodes(String tagName, String comment) { + for (Node node : getNodes()) { + BlackboardArtifact artifact = node.getLookup().lookup(BlackboardArtifact.class); + if (null != artifact) { + Tags.createTag(artifact, tagName, comment); + } + else { + Logger.getLogger(org.sleuthkit.autopsy.directorytree.TagBlackboardArtifactAction.TagBlackboardArtifactMenu.class.getName()).log(Level.SEVERE, "Node not associated with a BlackboardArtifact object"); + } + } + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/TagMenu.java b/Core/src/org/sleuthkit/autopsy/directorytree/TagMenu.java index af432e199a..9d6709f39b 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/TagMenu.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/TagMenu.java @@ -23,39 +23,26 @@ import java.awt.event.ActionListener; import java.util.List; import javax.swing.JMenu; import javax.swing.JMenuItem; +import org.openide.nodes.Node; import org.sleuthkit.autopsy.datamodel.Tags; -import org.sleuthkit.autopsy.datamodel.Tags.Taggable; -import org.sleuthkit.autopsy.datamodel.Tags.TaggableBlackboardArtifact; -import org.sleuthkit.autopsy.datamodel.Tags.TaggableFile; -import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; /** * The menu that results when one right-clicks on a file or artifact. */ -public class TagMenu extends JMenu { +public abstract class TagMenu extends JMenu { - private Taggable tagCreator; - - public TagMenu(AbstractFile file) { - super("Tag File"); - tagCreator = new TaggableFile(file); - init(); - } - - public TagMenu(BlackboardArtifact bba) { - super("Tag Result"); - tagCreator = new TaggableBlackboardArtifact(bba); - init(); - } + private Node[] nodes; - private void init() { - - // create the 'Quick Tag' menu and add it to the 'Tag File' menu + public TagMenu(String menuItemText, Node[] selectedNodes) { + super(menuItemText); + this.nodes = selectedNodes; + + // Create the 'Quick Tag' sub-menu and add it to the tag menu. JMenu quickTagMenu = new JMenu("Quick Tag"); - add(quickTagMenu); - - // create the 'Quick Tag' sub-menu items and add them to the 'Quick Tag' menu + add(quickTagMenu); + + // Get the existing tag names. List tagNames = Tags.getTagNames(); if (tagNames.isEmpty()) { JMenuItem empty = new JMenuItem("No tags"); @@ -63,46 +50,56 @@ public class TagMenu extends JMenu { quickTagMenu.add(empty); } + // Add a menu item for each existing tag name to the 'Quick Tag' menu. for (final String tagName : tagNames) { - JMenuItem tagItem = new JMenuItem(tagName); - tagItem.addActionListener(new ActionListener() { + JMenuItem tagNameItem = new JMenuItem(tagName); + tagNameItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - tagCreator.createTag(tagName, ""); + tagNodes(tagName, ""); refreshDirectoryTree(); } }); - quickTagMenu.add(tagItem); + quickTagMenu.add(tagNameItem); } quickTagMenu.addSeparator(); - // create the 'New Tag' menu item + // Create the 'New Tag' menu item and add it to the 'Quick Tag' menu. JMenuItem newTagMenuItem = new JMenuItem("New Tag"); newTagMenuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - String newTagName = CreateTagDialog.getNewTagNameDialog(null); - if (newTagName != null) { - tagCreator.createTag(newTagName, ""); + String tagName = CreateTagDialog.getNewTagNameDialog(null); + if (tagName != null) { + tagNodes(tagName, ""); refreshDirectoryTree(); } } }); - - // add the 'New Tag' menu item to the 'Quick Tag' menu quickTagMenu.add(newTagMenuItem); - JMenuItem newTagItem = new JMenuItem("Tag and Comment"); - newTagItem.addActionListener(new ActionListener() { + // Create the 'Tag and Comment' menu item and add it to the tag menu. + JMenuItem tagAndCommentItem = new JMenuItem("Tag and Comment"); + tagAndCommentItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - new TagAndCommentDialog(tagCreator); + TagAndCommentDialog.CommentedTag commentedTag = TagAndCommentDialog.doDialog(); + if (null != commentedTag) { + tagNodes(commentedTag.getName(), commentedTag.getComment()); + refreshDirectoryTree(); + } } }); - add(newTagItem); + add(tagAndCommentItem); } + protected Node[] getNodes() { + return nodes; + } + + protected abstract void tagNodes(String tagName, String comment); + private void refreshDirectoryTree() { //TODO instead should send event to node children, which will call its refresh() / refreshKeys() DirectoryTreeTopComponent viewer = DirectoryTreeTopComponent.findInstance(); diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchFilterNode.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchFilterNode.java index bb9945dfa4..968631e27a 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchFilterNode.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchFilterNode.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011 Basis Technology Corp. + * Copyright 2013 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -28,9 +28,9 @@ import org.openide.nodes.PropertySupport; import org.openide.nodes.Sheet; import org.openide.util.lookup.Lookups; import org.openide.util.lookup.ProxyLookup; -import org.sleuthkit.autopsy.directorytree.TagAction; import org.sleuthkit.autopsy.directorytree.ExternalViewerAction; import org.sleuthkit.autopsy.directorytree.ExtractAction; +import org.sleuthkit.autopsy.directorytree.TagAbstractFileAction; import org.sleuthkit.autopsy.directorytree.HashSearchAction; import org.sleuthkit.autopsy.directorytree.NewWindowViewAction; import org.sleuthkit.datamodel.Content; @@ -128,8 +128,6 @@ class KeywordSearchFilterNode extends FilterNode { Content content = this.getOriginal().getLookup().lookup(Content.class); actions.addAll(content.accept(new GetPopupActionsContentVisitor())); - //actions.add(new IndexContentFilesAction(nodeContent, "Index")); - return actions.toArray(new Action[actions.size()]); } @@ -137,33 +135,29 @@ class KeywordSearchFilterNode extends FilterNode { @Override public List visit(File f) { - List actions = new ArrayList(); - actions.add(new NewWindowViewAction("View in New Window", KeywordSearchFilterNode.this)); - actions.add(new ExternalViewerAction("Open in External Viewer", getOriginal())); - actions.add(null); - actions.add(new ExtractAction("Extract File", getOriginal())); - actions.add(new HashSearchAction("Search for files with the same MD5 hash", getOriginal())); - actions.add(null); // creates a menu separator - actions.add(new TagAction(getOriginal())); - return actions; + return getFileActions(); } @Override public List visit(DerivedFile f) { - List actions = new ArrayList(); + return getFileActions(); + } + + private List getFileActions() { + List actions = new ArrayList<>(); actions.add(new NewWindowViewAction("View in New Window", KeywordSearchFilterNode.this)); actions.add(new ExternalViewerAction("Open in External Viewer", getOriginal())); actions.add(null); - actions.add(new ExtractAction("Extract File", getOriginal())); + actions.add(ExtractAction.getInstance()); actions.add(new HashSearchAction("Search for files with the same MD5 hash", getOriginal())); actions.add(null); // creates a menu separator - actions.add(new TagAction(getOriginal())); - return actions; + actions.add(TagAbstractFileAction.getInstance()); + return actions; } @Override protected List defaultVisit(Content c) { - return new ArrayList(); + return new ArrayList<>(); } } } From 9a26d45f19ac34733e649cdf1c8e4b378673876a Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Tue, 6 Aug 2013 17:08:01 -0400 Subject: [PATCH 02/35] Cherry-picked commit edc6ed58587ddb12c7abecf536ef8794ddc3bfad from ingest-config branch --- .../casemodule/GeneralIngestConfigurator.java | 153 ++++++++ .../autopsy/coreutils/ModuleSettings.java | 24 +- .../autopsy/ingest/IngestDialog.java | 35 +- .../autopsy/ingest/IngestDialogPanel.java | 362 ++++++------------ .../autopsy/ingest/IngestManager.java | 34 +- .../autopsy/ingest/IngestModuleAbstract.java | 55 ++- 6 files changed, 324 insertions(+), 339 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/casemodule/GeneralIngestConfigurator.java diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/GeneralIngestConfigurator.java b/Core/src/org/sleuthkit/autopsy/casemodule/GeneralIngestConfigurator.java new file mode 100644 index 0000000000..3026336b87 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/GeneralIngestConfigurator.java @@ -0,0 +1,153 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2013 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.casemodule; + +import java.util.ArrayList; +import java.util.List; +import javax.swing.JPanel; +import org.sleuthkit.autopsy.coreutils.ModuleSettings; +import org.sleuthkit.autopsy.ingest.IngestDialogPanel; +import static org.sleuthkit.autopsy.ingest.IngestDialogPanel.DISABLED_MOD; +import static org.sleuthkit.autopsy.ingest.IngestDialogPanel.PARSE_UNALLOC; +import org.sleuthkit.autopsy.ingest.IngestManager; +import org.sleuthkit.autopsy.ingest.IngestModuleAbstract; +import org.sleuthkit.datamodel.Content; + +/** + * + */ +public class GeneralIngestConfigurator implements IngestConfigurator { + + private List contentToIngest; + private IngestManager manager; + private IngestDialogPanel ingestDialogPanel; + private String moduleContext; + + public GeneralIngestConfigurator(String moduleContext) { + this.moduleContext = moduleContext; + ingestDialogPanel = new IngestDialogPanel(); + manager = IngestManager.getDefault(); + reload(); + } + + @Override + public JPanel getIngestConfigPanel() { + return ingestDialogPanel; + } + + @Override + public void setContent(List inputContent) { + this.contentToIngest = inputContent; + } + + @Override + public void start() { + + //pick the modules + List modulesToStart = ingestDialogPanel.getModulesToStart(); + + if (!modulesToStart.isEmpty()) { + manager.execute(modulesToStart, contentToIngest); + } + + //update ingest proc. unalloc space + manager.setProcessUnallocSpace(ingestDialogPanel.processUnallocSpaceEnabled()); + } + + @Override + public void save() { + + // Save the current module + IngestModuleAbstract currentModule = ingestDialogPanel.getCurrentIngestModule(); + if (currentModule != null && currentModule.hasSimpleConfiguration()) { + currentModule.saveSimpleConfiguration(); + } + + // create a list of disabled modules + List disabledModules = IngestManager.getDefault().enumerateAllModules(); + disabledModules.removeAll(ingestDialogPanel.getModulesToStart()); + + // create a csv list + String disabledModulesCsv = moduleListToCsv(disabledModules); + + ModuleSettings.setConfigSetting(moduleContext, DISABLED_MOD, disabledModulesCsv); + String processUnalloc = Boolean.toString(ingestDialogPanel.processUnallocSpaceEnabled()); + ModuleSettings.setConfigSetting(moduleContext, PARSE_UNALLOC, processUnalloc); + } + + public static String moduleListToCsv(List lst) { + + if (lst == null || lst.isEmpty()) { + return ""; + } + + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < lst.size() - 1; ++i) { + sb.append(lst.get(i).getName()).append(", "); + } + + // and the last one + sb.append(lst.get(lst.size() - 1).getName()); + + return sb.toString(); + } + + public static List csvToModuleList(String csv) { + List modules = new ArrayList<>(); + + if (csv == null || csv.isEmpty()) { + return modules; + } + + String[] moduleNames = csv.split(", "); + List allModules = IngestManager.getDefault().enumerateAllModules(); + for (String moduleName : moduleNames) { + for (IngestModuleAbstract module : allModules) { + if (moduleName.equals(module.getName())) { + modules.add(module); + break; + } + } + } + + return modules; + } + + @Override + public void reload() { + + // get the csv list of disabled modules + String disabledModulesCsv = ModuleSettings.getConfigSetting(moduleContext, DISABLED_MOD); + + // create a list of modules from it + List disabledModules = csvToModuleList(disabledModulesCsv); + + // tell th ingestDialogPanel to unselect these modules + ingestDialogPanel.setDisabledModules(disabledModules); + + boolean processUnalloc = Boolean.parseBoolean(ModuleSettings.getConfigSetting(moduleContext, PARSE_UNALLOC)); + ingestDialogPanel.setProcessUnallocSpaceEnabled(processUnalloc); + } + + @Override + public boolean isIngestRunning() { + return manager.isIngestRunning(); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/ModuleSettings.java b/Core/src/org/sleuthkit/autopsy/coreutils/ModuleSettings.java index 117c5eb4ab..4eff288380 100644 --- a/Core/src/org/sleuthkit/autopsy/coreutils/ModuleSettings.java +++ b/Core/src/org/sleuthkit/autopsy/coreutils/ModuleSettings.java @@ -19,12 +19,17 @@ package org.sleuthkit.autopsy.coreutils; -import java.io.*; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; import java.util.HashMap; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.logging.Level; + /** * This class contains the framework to read, add, update, and remove * from the property files located at %USERDIR%/Config/x.properties @@ -33,15 +38,11 @@ public class ModuleSettings { // The directory where the properties file is lcoated private final static String moduleDirPath = PlatformUtil.getUserConfigDirectory(); - public static final String MAIN_SETTINGS="Case"; + public static final String MAIN_SETTINGS = "Case"; - /** the constructor */ - private ModuleSettings() { - - } - - + private ModuleSettings() {} + /** * Makes a new config file of the specified name. Do not include the extension. * @param moduleName - The name of the config file to make @@ -161,9 +162,7 @@ public class ModuleSettings { return null; } } - - - + /** * Sets the given properties file to the given setting map. * @param moduleName - The name of the module to be written to. @@ -215,14 +214,12 @@ public class ModuleSettings { Logger.getLogger(ModuleSettings.class.getName()).log(Level.WARNING, "Property file exists for [" + moduleName + "] at [" + getPropertyPath(moduleName) + "] but could not be loaded.", e); } } - /** * Removes the given key from the given properties file. * @param moduleName - The name of the properties file to be modified. * @param key - the name of the key to remove. */ - public static synchronized void removeProperty(String moduleName, String key){ try{ if(getConfigSetting(moduleName, key) != null){ @@ -268,5 +265,4 @@ public class ModuleSettings { return new File(getPropertyPath(moduleName)); } } - } diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestDialog.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestDialog.java index 01cfba4239..970cd249dc 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestDialog.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestDialog.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011 Basis Technology Corp. + * Copyright 2013 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -26,36 +26,35 @@ import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.util.List; -import org.sleuthkit.autopsy.coreutils.Logger; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JPanel; import org.sleuthkit.datamodel.Content; +import org.sleuthkit.autopsy.casemodule.GeneralIngestConfigurator; +import org.sleuthkit.autopsy.casemodule.IngestConfigurator; /** * IngestDialog shown on Case.CASE_ADD_IMAGE property change */ public class IngestDialog extends JDialog { + private static final String MODULE_CONTEXT = "MainIngest"; private static final String TITLE = "Ingest Modules"; private static Dimension DIMENSIONS = new Dimension(500, 300); - private IngestDialogPanel panel = null; + private IngestConfigurator ingestConfigurator; - private static final Logger logger = Logger.getLogger(IngestDialog.class.getName()); - public IngestDialog(JFrame frame, String title, boolean modal) { super(frame, title, modal); - panel = new IngestDialogPanel(); + ingestConfigurator = new GeneralIngestConfigurator(MODULE_CONTEXT); + ingestConfigurator.reload(); } public IngestDialog(){ this(new JFrame(TITLE), TITLE, true); } - - /** * Shows the Ingest dialog. */ @@ -71,17 +70,14 @@ public class IngestDialog extends JDialog { // set the location of the popUp Window on the center of the screen setLocation((screenDimension.width - w) / 2, (screenDimension.height - h) / 2); - panel.reload(); // reload the simple panel - add(panel, BorderLayout.PAGE_START); + add(ingestConfigurator.getIngestConfigPanel(), BorderLayout.PAGE_START); JButton startButton = new JButton("Start"); JButton closeButton = new JButton("Close"); startButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - - panel.save(); - panel.start(); + ingestConfigurator.start(); close(); } }); @@ -89,7 +85,7 @@ public class IngestDialog extends JDialog { @Override public void actionPerformed(ActionEvent e) { - panel.save(); + ingestConfigurator.save(); close(); } }); @@ -97,7 +93,7 @@ public class IngestDialog extends JDialog { @Override public void windowClosing(WindowEvent e) { - panel.save(); + ingestConfigurator.save(); close(); } }); @@ -113,11 +109,10 @@ public class IngestDialog extends JDialog { setResizable(false); setVisible(true); } - + public void setContent(List inputContent) { - panel.setContent(inputContent); - } - + ingestConfigurator.setContent(inputContent); + } /** * Closes the Ingest dialog @@ -126,6 +121,4 @@ public class IngestDialog extends JDialog { setVisible(false); dispose(); } - - } diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestDialogPanel.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestDialogPanel.java index d6e65d2bac..98d7c197fe 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestDialogPanel.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestDialogPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011 Basis Technology Corp. + * Copyright 2013 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,24 +18,15 @@ */ package org.sleuthkit.autopsy.ingest; -import java.awt.Color; import java.awt.Component; -import java.awt.Graphics; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; +import java.util.AbstractMap; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.logging.Level; -import org.sleuthkit.autopsy.coreutils.Logger; -import javax.swing.JPanel; import javax.swing.JTable; import javax.swing.ListSelectionModel; import javax.swing.event.ListSelectionEvent; @@ -43,80 +34,47 @@ import javax.swing.event.ListSelectionListener; import javax.swing.table.AbstractTableModel; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.TableColumn; -import org.openide.util.lookup.ServiceProvider; -import org.sleuthkit.autopsy.casemodule.IngestConfigurator; import org.sleuthkit.autopsy.corecomponents.AdvancedConfigurationDialog; -import org.sleuthkit.autopsy.coreutils.ModuleSettings; -import org.sleuthkit.autopsy.ingest.IngestModuleAbstract.ModuleType; -import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.TskCoreException; /** * main configuration panel for all ingest modules, reusable JPanel component */ -@ServiceProvider(service = IngestConfigurator.class) -public class IngestDialogPanel extends javax.swing.JPanel implements IngestConfigurator { +public class IngestDialogPanel extends javax.swing.JPanel { - private IngestManager manager = null; - private List modules; private IngestModuleAbstract currentModule; - private Map moduleStates; private ModulesTableModel tableModel; - private static final Logger logger = Logger.getLogger(IngestDialogPanel.class.getName()); public static final String DISABLED_MOD = "Disabled_Ingest_Modules"; public static final String PARSE_UNALLOC = "Process_Unallocated_Space"; - // The input content that's just been added to the database - private List inputContent; - // private static IngestDialogPanel instance = null; /** * Creates new form IngestDialogPanel */ public IngestDialogPanel() { tableModel = new ModulesTableModel(); - modules = new ArrayList(); - moduleStates = new HashMap(); initComponents(); customizeComponents(); } - private void loadModules() { - this.modules.clear(); - //this.moduleStates.clear(); maintain the state - Collection imageModules = manager.enumerateDataSourceModules(); - for (final IngestModuleDataSource module : imageModules) { - addModule(module); - } - Collection fsModules = manager.enumerateAbstractFileModules(); - for (final IngestModuleAbstractFile module : fsModules) { - addModule(module); - } + public IngestModuleAbstract getCurrentIngestModule() { + return currentModule; + } + + public List getModulesToStart() { + return tableModel.getSelectedModules(); + } + + public boolean processUnallocSpaceEnabled() { + return processUnallocCheckbox.isSelected(); } private void customizeComponents() { modulesTable.setModel(tableModel); - this.manager = IngestManager.getDefault(); - - loadModules(); - try { - IngestModuleLoader.getDefault().addModulesReloadedListener(new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - if (evt.getPropertyName().equals(IngestModuleLoader.Event.ModulesReloaded.toString())) { - loadModules(); - } - } - }); - } catch (IngestModuleLoaderException ex) { - logger.log(Level.SEVERE, "Could not initialize ingest module loader to listen for module config changes", ex); - } - modulesTable.setTableHeader(null); modulesTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); //custom renderer for tooltips - ModulesTableRenderer renderer = new ModulesTableRenderer(); + //customize column witdhs final int width = modulesScrollPane.getPreferredSize().width; TableColumn column = null; @@ -135,40 +93,30 @@ public class IngestDialogPanel extends javax.swing.JPanel implements IngestConfi public void valueChanged(ListSelectionEvent e) { ListSelectionModel listSelectionModel = (ListSelectionModel) e.getSource(); if (!listSelectionModel.isSelectionEmpty()) { - save(); int index = listSelectionModel.getMinSelectionIndex(); - currentModule = modules.get(index); - reload(); + currentModule = tableModel.getModule(index); + + // add the module-specific configuration panel, if there is one + simplePanel.removeAll(); + if (currentModule.hasSimpleConfiguration()) { + simplePanel.add(currentModule.getSimpleConfiguration()); + } + simplePanel.revalidate(); + simplePanel.repaint(); advancedButton.setEnabled(currentModule.hasAdvancedConfiguration()); } else { currentModule = null; } } }); - - processUnallocCheckbox.setSelected(manager.getProcessUnallocSpace()); - } - private void setProcessUnallocSpaceEnabled(boolean enabled) { - processUnallocCheckbox.setEnabled(enabled); + public void setProcessUnallocSpaceEnabled(final boolean enabled) { + processUnallocCheckbox.setSelected(enabled); } - - @Override - public void paint(Graphics g) { - super.paint(g); - if (manager.isIngestRunning()) { - setProcessUnallocSpaceEnabled(false); - - } else { - setProcessUnallocSpaceEnabled(true); - } - } - - private void addModule(IngestModuleAbstract module) { - final String moduleName = module.getName(); - modules.add(module); - moduleStates.put(moduleName, true); + + public void setDisabledModules(List disabledModules) { + tableModel.setUnselectedModules(disabledModules); } /** @@ -311,22 +259,19 @@ public class IngestDialogPanel extends javax.swing.JPanel implements IngestConfi public void actionPerformed(ActionEvent e) { dialog.close(); currentModule.saveAdvancedConfiguration(); - reload(); } }); dialog.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { dialog.close(); - reload(); } }); - save(); // save the simple panel - dialog.display(currentModule.getAdvancedConfiguration(null)); + dialog.display(currentModule.getAdvancedConfiguration()); }//GEN-LAST:event_advancedButtonActionPerformed private void processUnallocCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_processUnallocCheckboxActionPerformed - // TODO add your handling code here: + // nothing to do here }//GEN-LAST:event_processUnallocCheckboxActionPerformed // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton advancedButton; @@ -342,10 +287,19 @@ public class IngestDialogPanel extends javax.swing.JPanel implements IngestConfi // End of variables declaration//GEN-END:variables private class ModulesTableModel extends AbstractTableModel { + + private List>moduleData = new ArrayList<>(); + + public ModulesTableModel() { + List modules = IngestManager.getDefault().enumerateAllModules(); + for (IngestModuleAbstract ingestModuleAbstract : modules) { + moduleData.add(new AbstractMap.SimpleEntry<>(ingestModuleAbstract, Boolean.TRUE)); + } + } @Override public int getRowCount() { - return modules.size(); + return moduleData.size(); } @Override @@ -355,11 +309,11 @@ public class IngestDialogPanel extends javax.swing.JPanel implements IngestConfi @Override public Object getValueAt(int rowIndex, int columnIndex) { - String name = modules.get(rowIndex).getName(); + Map.Entry entry = moduleData.get(rowIndex); if (columnIndex == 0) { - return moduleStates.get(name); + return entry.getValue(); } else { - return name; + return entry.getKey().getName(); } } @@ -371,8 +325,7 @@ public class IngestDialogPanel extends javax.swing.JPanel implements IngestConfi @Override public void setValueAt(Object aValue, int rowIndex, int columnIndex) { if (columnIndex == 0) { - moduleStates.put((String) getValueAt(rowIndex, 1), (Boolean) aValue); - + moduleData.get(rowIndex).setValue((Boolean)aValue); } } @@ -380,183 +333,96 @@ public class IngestDialogPanel extends javax.swing.JPanel implements IngestConfi public Class getColumnClass(int c) { return getValueAt(0, c).getClass(); } - } - - List getModulesToStart() { - List modulesToStart = new ArrayList(); - for (IngestModuleAbstract module : modules) { - boolean moduleEnabled = moduleStates.get(module.getName()); - if (moduleEnabled) { - modulesToStart.add(module); - } - } - return modulesToStart; - } - - private boolean processUnallocSpaceEnabled() { - return processUnallocCheckbox.isEnabled(); - } - - /** - * To be called whenever the next, close, or start buttons are pressed. - * - */ - @Override - public void save() { - // Save the current module - if (currentModule != null && currentModule.hasSimpleConfiguration()) { - currentModule.saveSimpleConfiguration(); - } - // Save this panel - List modulesDisabled = new ArrayList(); - for (int i = 0; i < modulesTable.getRowCount(); i++) { - // Column 0 is always the module's checkbox (which is retreived as a boolean) - Boolean enabled = (Boolean) modulesTable.getValueAt(i, 0); - if (!enabled) { - // Column 1 is always the module name - String moduleName = (String) modulesTable.getValueAt(i, 1); - modulesDisabled.add(moduleName); - } - } - // Add all the enabled modules to the properties separated by a coma - String list = ""; - for (int i = 0; i < modulesDisabled.size(); i++) { - list += modulesDisabled.get(i); - if (i + 1 < modulesDisabled.size()) { - list += ", "; - } - } - ModuleSettings.setConfigSetting(IngestManager.MODULE_PROPERTIES, DISABLED_MOD, list); - String processUnalloc = Boolean.toString(processUnallocCheckbox.isSelected()); - ModuleSettings.setConfigSetting(IngestManager.MODULE_PROPERTIES, PARSE_UNALLOC, processUnalloc); - - } - - /** - * Called when the dialog needs to be reloaded. Most commonly used to - * refresh the simple panel. - * - * Called every time this panel is displayed. - */ - @Override - public void reload() { - // Reload the simple panel - if (this.modulesTable.getSelectedRow() != -1) { - simplePanel.removeAll(); - if (currentModule.hasSimpleConfiguration()) { - simplePanel.add(currentModule.getSimpleConfiguration(null)); - } - simplePanel.revalidate(); - simplePanel.repaint(); - } - // Reload this panel - String list = ModuleSettings.getConfigSetting(IngestManager.MODULE_PROPERTIES, DISABLED_MOD); - if (list != null) { // if no property is found, list will be null - List modulesDisabled = new ArrayList(Arrays.asList(list.split(", "))); - // For every row, see if that module name is in the ArrayList - for (int i = 0; i < modulesTable.getRowCount(); i++) { - String moduleName = (String) modulesTable.getValueAt(i, 1); - if (modulesDisabled.contains(moduleName)) { - modulesTable.setValueAt(false, i, 0); // we found it, disable the module - } else { - modulesTable.setValueAt(true, i, 0); // not on disabled list, or a new module, enable it + + public List getSelectedModules() { + List selectedModules = new ArrayList<>(); + for (Map.Entry entry : moduleData) { + if (entry.getValue().booleanValue()) { + selectedModules.add(entry.getKey()); } } + return selectedModules; } - String processUnalloc = ModuleSettings.getConfigSetting(IngestManager.MODULE_PROPERTIES, PARSE_UNALLOC); - if (processUnalloc != null) { - processUnallocCheckbox.setSelected(Boolean.parseBoolean(processUnalloc)); + + /** + * Sets the given modules as selected in the modules table + * @param selectedModules + */ + public void setSelectedModules(List selectedModules) { + // unselect all modules + for (Map.Entry entry : moduleData) { + entry.setValue(Boolean.FALSE); + } + + // select only the given modules + for (IngestModuleAbstract selectedModule : selectedModules) { + getEntryForModule(selectedModule).setValue(Boolean.TRUE); + } + + // tell everyone about it + fireTableDataChanged(); } - } - - @Override - public JPanel getIngestConfigPanel() { - this.reload(); - return this; - } - - @Override - public void setContent(List inputContent) { - this.inputContent = inputContent; - } - - @Override - public void start() { - //pick the modules - List modulesToStart = getModulesToStart(); - - //update ingest proc. unalloc space - if (processUnallocSpaceEnabled()) { - manager.setProcessUnallocSpace(processUnallocCheckbox.isSelected()); + + /** + * Sets the given modules as NOT selected in the modules table + * @param selectedModules + */ + public void setUnselectedModules(List unselectedModules) { + // select all modules + for (Map.Entry entry : moduleData) { + entry.setValue(Boolean.TRUE); + } + + // unselect only the given modules + for (IngestModuleAbstract unselectedModule : unselectedModules) { + getEntryForModule(unselectedModule).setValue(Boolean.FALSE); + } + + // tell everyone about it + fireTableDataChanged(); } - - if (!modulesToStart.isEmpty()) { - manager.execute(modulesToStart, inputContent); + + public IngestModuleAbstract getModule(int row) { + return moduleData.get(row).getKey(); + } + + private Map.Entry getEntryForModule(IngestModuleAbstract module) { + Map.Entry entry = null; + for (Map.Entry anEntry : moduleData) { + if (anEntry.getKey().equals(module)) { + entry = anEntry; + break; + } + } + return entry; } - - } - - @Override - public boolean isIngestRunning() { - return manager.isIngestRunning(); } /** * Custom cell renderer for tooltips with module description */ private class ModulesTableRenderer extends DefaultTableCellRenderer { + + List tooltips = new ArrayList<>(); + + public ModulesTableRenderer() { + List modules = IngestManager.getDefault().enumerateAllModules(); + for (IngestModuleAbstract ingestModuleAbstract : modules) { + tooltips.add(ingestModuleAbstract.getDescription()); + } + } @Override public Component getTableCellRendererComponent( JTable table, Object value, boolean isSelected, boolean hasFocus, - int row, int column) { - - final Component cell = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + int row, int column) { + super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); if (column == 1) { - //String moduleName = (String) table.getModel().getValueAt(row, column); - IngestModuleAbstract module = modules.get(row); - String moduleDescr = module.getDescription(); - String toolTip = moduleDescr; - - if (inputContent != null && module.getType().equals(ModuleType.DataSource)) { - - //DataSource module accepts only data source, does not work on any child - //show warning to user and set fg red for that module - boolean isDataSource = true; - for (Content content : inputContent) { - try { - if (content.getParent() != null) { - isDataSource = false; - break; - } - } catch (TskCoreException e) { - logger.log(Level.SEVERE, "Error checking if module input content is parentless data source", e); - } - - } - - if (! isDataSource ) { - cell.setForeground(Color.red); - toolTip = toolTip + "
WARNING: this module will not run on current selection because it operates only on root-level data-source (such as Image, Filesets)."; - } - else { - cell.setForeground(Color.black); - } - - - - } //end data source - else { - cell.setForeground(Color.black); - } - // - - setToolTipText("" + toolTip+ ""); + setToolTipText(tooltips.get(row)); } - - return cell; + return this; } } } diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestManager.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestManager.java index 171cd5ca61..f801b421d8 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestManager.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestManager.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2012 Basis Technology Corp. + * Copyright 2013 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -154,8 +154,6 @@ public class IngestManager { } catch (IngestModuleLoaderException ex) { logger.log(Level.SEVERE, "Error getting module loader"); } - - } /** @@ -243,7 +241,6 @@ public class IngestManager { if (ui != null) { ui.restoreMessages(); } - } /** @@ -346,8 +343,6 @@ public class IngestManager { } } } - //} - //AbstractFile ingester boolean startAbstractFileIngester = false; @@ -421,7 +416,6 @@ public class IngestManager { List toStop = new ArrayList(); toStop.addAll(dataSourceIngesters); - for (IngestDataSourceThread dataSourceWorker : toStop) { IngestModuleDataSource s = dataSourceWorker.getModule(); @@ -440,7 +434,6 @@ public class IngestManager { logger.log(Level.WARNING, "Exception while stopping module: " + s.getName(), e); } } - } logger.log(Level.INFO, "stopped all"); @@ -545,7 +538,6 @@ public class IngestManager { return module.hasBackgroundJobsRunning(); } - } else { //data source module synchronized (this) { @@ -570,10 +562,7 @@ public class IngestManager { return false; } } - } - - } /** @@ -607,7 +596,7 @@ public class IngestManager { * * @param processUnallocSpace */ - void setProcessUnallocSpace(boolean processUnallocSpace) { + public void setProcessUnallocSpace(boolean processUnallocSpace) { this.processUnallocSpace = processUnallocSpace; } @@ -671,6 +660,13 @@ public class IngestManager { public List enumerateAbstractFileModules() { return moduleLoader.getAbstractFileIngestModules(); } + + public List enumerateAllModules() { + List modules = new ArrayList<>(); + modules.addAll(enumerateDataSourceModules()); + modules.addAll(enumerateAbstractFileModules()); + return modules; + } //data source worker to remove itself when complete or interrupted void removeDataSourceIngestWorker(IngestDataSourceThread worker) { @@ -697,7 +693,6 @@ public class IngestManager { IngestManagerStats() { errors = new HashMap(); - } /** @@ -775,19 +770,8 @@ public class IngestManager { public String toHtmlString() { StringBuilder sb = new StringBuilder(); sb.append(""); - sb.append("Ingest time: ").append(getTotalTimeString()).append("
"); sb.append("Total errors: ").append(errorsTotal).append("
"); - /* - if (errorsTotal > 0) { - sb.append("Errors per module:"); - for (IngestModuleAbstract module : errors.keySet()) { - final int errorsModule = errors.get(module); - sb.append("\t").append(module.getName()).append(": ").append(errorsModule).append("
"); - } - } - * */ - sb.append(""); return sb.toString(); } diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleAbstract.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleAbstract.java index d30e57f785..f8ff21bb82 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleAbstract.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleAbstract.java @@ -145,35 +145,28 @@ public abstract class IngestModuleAbstract { */ public void saveAdvancedConfiguration() {} - /** - * Returns a panel that displays the simple (run-time) configuration - * for the given configuration context (such as pipeline instance). - * This is presented to the user before ingest starts and only basic - * settings should be given here. Use the advanced (general) configuration - * panel for more in-depth interfaces. - * The module (or its configuration controller object) is responsible for preserving / saving its configuration state - * In addition, saveSimpleConfiguration() can be used as the trigger. - * - * @param context the configuration context to use in the panel - * @return JPanel containing basic configuration widgets or null if simple configuration is not available - */ - public javax.swing.JPanel getSimpleConfiguration(String context) { - return null; - } - /** - * Returns a panel that displays the advanced (run-time) configuration - * for the given configuration context (such as pipeline instance). - * Implements advanced module configuration exposed to the user before ingest starts. - * - * The module (or its configuration controller object) - * is responsible for preserving / saving its configuration state - * In addition, saveAdvancedConfiguration() can be used as the trigger. - * - * @param context the configuration context to use in the panel - * @return JPanel containing advanced configuration widgets or null if advanced configuration is not available - */ - public javax.swing.JPanel getAdvancedConfiguration(String context) { - return null; - }; -} + * Returns a panel that displays the simple (run-time) configuration. + * This is presented to the user before ingest starts and only basic + * settings should be given here. use the advanced (general) configuration + * panel for more in-depth interfaces. + * The module is responsible for preserving / saving its configuration state + * In addition, saveSimpleConfiguration() can be used + * + * @return JPanel containing basic configuration widgets or null if simple configuration is not available + */ + public javax.swing.JPanel getSimpleConfiguration() { + return null; + } + + /** + * Implements advanced module configuration exposed to the user before ingest starts + * The module is responsible for preserving / saving its configuration state + * In addition, saveAdvancedConfiguration() can be used + * + * @return JPanel containing basic configuration widgets or null if advanced configuration is not available + */ + public javax.swing.JPanel getAdvancedConfiguration() { + return null; + }; + } \ No newline at end of file From 1cf9195e74b90b388185430017e0dcc51118b4a9 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Tue, 6 Aug 2013 17:15:39 -0400 Subject: [PATCH 03/35] Cherry-picked commit 42ec4013ae5777200b37df69838bd85ea2253b2c --- .../autopsy/casemodule/GeneralIngestConfigurator.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/GeneralIngestConfigurator.java b/Core/src/org/sleuthkit/autopsy/casemodule/GeneralIngestConfigurator.java index 3026336b87..a3496c1765 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/GeneralIngestConfigurator.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/GeneralIngestConfigurator.java @@ -62,13 +62,13 @@ public class GeneralIngestConfigurator implements IngestConfigurator { //pick the modules List modulesToStart = ingestDialogPanel.getModulesToStart(); + + //update ingest proc. unalloc space + manager.setProcessUnallocSpace(ingestDialogPanel.processUnallocSpaceEnabled()); if (!modulesToStart.isEmpty()) { manager.execute(modulesToStart, contentToIngest); } - - //update ingest proc. unalloc space - manager.setProcessUnallocSpace(ingestDialogPanel.processUnallocSpaceEnabled()); } @Override From 9d2dcbcd4b864dda1872f7f0225adf01b58b7e3f Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Tue, 6 Aug 2013 17:53:30 -0400 Subject: [PATCH 04/35] Additional modifications for backing out changes to IngestModuleAbstract.java from commit 5d9bf3ff1f78dfc967128c02356613a3a980f328 --- .../sleuthkit/autopsy/hashdatabase/HashDbIngestModule.java | 4 ++-- .../autopsy/keywordsearch/KeywordSearchIngestModule.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbIngestModule.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbIngestModule.java index 8cf87ce28c..5cfc8d9454 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbIngestModule.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbIngestModule.java @@ -222,13 +222,13 @@ public class HashDbIngestModule extends IngestModuleAbstractFile { } @Override - public javax.swing.JPanel getSimpleConfiguration(String context) { + public javax.swing.JPanel getSimpleConfiguration() { HashDbXML.getCurrent().reload(); return new HashDbSimplePanel(); } @Override - public javax.swing.JPanel getAdvancedConfiguration(String context) { + public javax.swing.JPanel getAdvancedConfiguration() { //return HashDbManagementPanel.getDefault(); getPanel().load(); return getPanel(); diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java index cd7ef92825..a9ac21f9d7 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java @@ -430,13 +430,13 @@ public final class KeywordSearchIngestModule extends IngestModuleAbstractFile { } @Override - public javax.swing.JPanel getSimpleConfiguration(String context) { + public javax.swing.JPanel getSimpleConfiguration() { KeywordSearchListsXML.getCurrent().reload(); return new KeywordSearchIngestSimplePanel(); } @Override - public javax.swing.JPanel getAdvancedConfiguration(String context) { + public javax.swing.JPanel getAdvancedConfiguration() { //return KeywordSearchConfigurationPanel.getDefault(); getPanel().load(); return getPanel(); From 6aa5491b9adcbc72879915df9b7bb1c3c19e6721 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Fri, 9 Aug 2013 09:30:49 -0400 Subject: [PATCH 05/35] Merge in of GeneralIngestConfigurator, alternative 1 --- .../autopsy/casemodule/AddImageAction.java | 3 +- .../casemodule/GeneralIngestConfigurator.java | 71 +++++++++++-------- .../casemodule/IngestConfigurator.java | 12 ++++ .../autopsy/ingest/IngestDialog.java | 5 +- 4 files changed, 55 insertions(+), 36 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageAction.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageAction.java index 8b4ef96f1d..cdfb561ae0 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageAction.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageAction.java @@ -109,7 +109,7 @@ public final class AddImageAction extends CallableSystemAction implements Presen Logger.noteAction(AddImageAction.class); final IngestConfigurator ingestConfig = Lookup.getDefault().lookup(IngestConfigurator.class); - if (ingestConfig.isIngestRunning()) { + if (null != ingestConfig && ingestConfig.isIngestRunning()) { final String msg = "Ingest is ongoing on another data source. Adding a new source now might slow down the current ingest.
Do you want to proceed and add a new data source now?"; if (JOptionPane.showConfirmDialog(null, msg, "Ingest in progress", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.NO_OPTION) { return; @@ -128,7 +128,6 @@ public final class AddImageAction extends CallableSystemAction implements Presen dialog.setVisible(true); dialog.toFront(); - // Do any cleanup that needs to happen (potentially: stopping the //add-image process, reverting an image) runCleanupTasks(); diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/GeneralIngestConfigurator.java b/Core/src/org/sleuthkit/autopsy/casemodule/GeneralIngestConfigurator.java index a3496c1765..bfb6088978 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/GeneralIngestConfigurator.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/GeneralIngestConfigurator.java @@ -22,6 +22,7 @@ package org.sleuthkit.autopsy.casemodule; import java.util.ArrayList; import java.util.List; import javax.swing.JPanel; +import org.openide.util.lookup.ServiceProvider; import org.sleuthkit.autopsy.coreutils.ModuleSettings; import org.sleuthkit.autopsy.ingest.IngestDialogPanel; import static org.sleuthkit.autopsy.ingest.IngestDialogPanel.DISABLED_MOD; @@ -30,23 +31,31 @@ import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.IngestModuleAbstract; import org.sleuthkit.datamodel.Content; -/** - * - */ -public class GeneralIngestConfigurator implements IngestConfigurator { - +@ServiceProvider(service = IngestConfigurator.class) +public class GeneralIngestConfigurator implements IngestConfigurator { + private static final String DEFAULT_MODULE_CONTEXT = IngestManager.MODULE_PROPERTIES; private List contentToIngest; private IngestManager manager; private IngestDialogPanel ingestDialogPanel; private String moduleContext; - - public GeneralIngestConfigurator(String moduleContext) { - this.moduleContext = moduleContext; + + public GeneralIngestConfigurator() { + this.moduleContext = DEFAULT_MODULE_CONTEXT; ingestDialogPanel = new IngestDialogPanel(); manager = IngestManager.getDefault(); - reload(); + loadSettings(); } + @Override + public String getDefaultModuleContext() { + return DEFAULT_MODULE_CONTEXT; + } + + @Override + public void setModuleContext(String moduleContext) { + this.moduleContext = moduleContext; + } + @Override public JPanel getIngestConfigPanel() { return ingestDialogPanel; @@ -59,13 +68,13 @@ public class GeneralIngestConfigurator implements IngestConfigurator { @Override public void start() { - - //pick the modules + // Get the list of ingest modules selected by the user. List modulesToStart = ingestDialogPanel.getModulesToStart(); - //update ingest proc. unalloc space + // Get the user's selection of whether or not to process unallocated space. manager.setProcessUnallocSpace(ingestDialogPanel.processUnallocSpaceEnabled()); + // Start the ingest. if (!modulesToStart.isEmpty()) { manager.execute(modulesToStart, contentToIngest); } @@ -73,27 +82,34 @@ public class GeneralIngestConfigurator implements IngestConfigurator { @Override public void save() { - - // Save the current module + // Save the user's configuration of the currently selected module. IngestModuleAbstract currentModule = ingestDialogPanel.getCurrentIngestModule(); if (currentModule != null && currentModule.hasSimpleConfiguration()) { currentModule.saveSimpleConfiguration(); } - // create a list of disabled modules + // Create a list of the modules the user wants to be disabled. List disabledModules = IngestManager.getDefault().enumerateAllModules(); - disabledModules.removeAll(ingestDialogPanel.getModulesToStart()); - - // create a csv list + disabledModules.removeAll(ingestDialogPanel.getModulesToStart()); String disabledModulesCsv = moduleListToCsv(disabledModules); + // Save the user's general ingest configuration. ModuleSettings.setConfigSetting(moduleContext, DISABLED_MOD, disabledModulesCsv); String processUnalloc = Boolean.toString(ingestDialogPanel.processUnallocSpaceEnabled()); ModuleSettings.setConfigSetting(moduleContext, PARSE_UNALLOC, processUnalloc); } - - public static String moduleListToCsv(List lst) { + + @Override + public void reload() { + loadSettings(); + } + + @Override + public boolean isIngestRunning() { + return manager.isIngestRunning(); + } + private static String moduleListToCsv(List lst) { if (lst == null || lst.isEmpty()) { return ""; } @@ -109,7 +125,7 @@ public class GeneralIngestConfigurator implements IngestConfigurator { return sb.toString(); } - public static List csvToModuleList(String csv) { + private static List csvToModuleList(String csv) { List modules = new ArrayList<>(); if (csv == null || csv.isEmpty()) { @@ -130,24 +146,17 @@ public class GeneralIngestConfigurator implements IngestConfigurator { return modules; } - @Override - public void reload() { - + private void loadSettings() { // get the csv list of disabled modules String disabledModulesCsv = ModuleSettings.getConfigSetting(moduleContext, DISABLED_MOD); // create a list of modules from it List disabledModules = csvToModuleList(disabledModulesCsv); - // tell th ingestDialogPanel to unselect these modules + // tell the ingestDialogPanel to unselect these modules ingestDialogPanel.setDisabledModules(disabledModules); boolean processUnalloc = Boolean.parseBoolean(ModuleSettings.getConfigSetting(moduleContext, PARSE_UNALLOC)); - ingestDialogPanel.setProcessUnallocSpaceEnabled(processUnalloc); - } - - @Override - public boolean isIngestRunning() { - return manager.isIngestRunning(); + ingestDialogPanel.setProcessUnallocSpaceEnabled(processUnalloc); } } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/IngestConfigurator.java b/Core/src/org/sleuthkit/autopsy/casemodule/IngestConfigurator.java index cca3e2d064..9cfb31a98a 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/IngestConfigurator.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/IngestConfigurator.java @@ -26,6 +26,18 @@ import org.sleuthkit.datamodel.Content; * Lookup interface for ingest configuration dialog */ public interface IngestConfigurator { + /** + * Get the default context for running the ingest modules. + * @return The default context. + */ + String getDefaultModuleContext(); + + /** + * Set the context for running the ingest modules. + * @param moduleContext + */ + void setModuleContext(String moduleContext); + /** * get JPanel container with the configurator * @return diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestDialog.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestDialog.java index 970cd249dc..598d768e38 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestDialog.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestDialog.java @@ -31,8 +31,8 @@ import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JPanel; +import org.openide.util.Lookup; import org.sleuthkit.datamodel.Content; -import org.sleuthkit.autopsy.casemodule.GeneralIngestConfigurator; import org.sleuthkit.autopsy.casemodule.IngestConfigurator; /** @@ -40,14 +40,13 @@ import org.sleuthkit.autopsy.casemodule.IngestConfigurator; */ public class IngestDialog extends JDialog { - private static final String MODULE_CONTEXT = "MainIngest"; private static final String TITLE = "Ingest Modules"; private static Dimension DIMENSIONS = new Dimension(500, 300); private IngestConfigurator ingestConfigurator; public IngestDialog(JFrame frame, String title, boolean modal) { super(frame, title, modal); - ingestConfigurator = new GeneralIngestConfigurator(MODULE_CONTEXT); + ingestConfigurator = Lookup.getDefault().lookup(IngestConfigurator.class); ingestConfigurator.reload(); } From aaa6866a3bc386949311ca442978c17acc6e1b1a Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Fri, 9 Aug 2013 11:47:14 -0400 Subject: [PATCH 06/35] Merge in of GeneralIngestConfigurator, alternative 2 --- .../casemodule/GeneralIngestConfigurator.java | 13 +------------ .../autopsy/casemodule/IngestConfigurator.java | 16 ++-------------- 2 files changed, 3 insertions(+), 26 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/GeneralIngestConfigurator.java b/Core/src/org/sleuthkit/autopsy/casemodule/GeneralIngestConfigurator.java index bfb6088978..3ce1571150 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/GeneralIngestConfigurator.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/GeneralIngestConfigurator.java @@ -33,28 +33,17 @@ import org.sleuthkit.datamodel.Content; @ServiceProvider(service = IngestConfigurator.class) public class GeneralIngestConfigurator implements IngestConfigurator { - private static final String DEFAULT_MODULE_CONTEXT = IngestManager.MODULE_PROPERTIES; private List contentToIngest; private IngestManager manager; private IngestDialogPanel ingestDialogPanel; private String moduleContext; public GeneralIngestConfigurator() { - this.moduleContext = DEFAULT_MODULE_CONTEXT; + this.moduleContext = IngestManager.MODULE_PROPERTIES; // Hard-code this for now. ingestDialogPanel = new IngestDialogPanel(); manager = IngestManager.getDefault(); loadSettings(); } - - @Override - public String getDefaultModuleContext() { - return DEFAULT_MODULE_CONTEXT; - } - - @Override - public void setModuleContext(String moduleContext) { - this.moduleContext = moduleContext; - } @Override public JPanel getIngestConfigPanel() { diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/IngestConfigurator.java b/Core/src/org/sleuthkit/autopsy/casemodule/IngestConfigurator.java index 9cfb31a98a..45f6a8e80d 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/IngestConfigurator.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/IngestConfigurator.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011 Basis Technology Corp. + * Copyright 2013 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -26,18 +26,6 @@ import org.sleuthkit.datamodel.Content; * Lookup interface for ingest configuration dialog */ public interface IngestConfigurator { - /** - * Get the default context for running the ingest modules. - * @return The default context. - */ - String getDefaultModuleContext(); - - /** - * Set the context for running the ingest modules. - * @param moduleContext - */ - void setModuleContext(String moduleContext); - /** * get JPanel container with the configurator * @return @@ -71,5 +59,5 @@ public interface IngestConfigurator { * @return true if ingest process is running, false otherwise */ boolean isIngestRunning(); - + } From d68fcdb0cc9fdf381b6b607c6bbce549b8bc5a6a Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Fri, 9 Aug 2013 17:36:29 -0400 Subject: [PATCH 07/35] Added multi-select tagging and extraction support for data result tables --- .../AbstractDataResultViewer.java | 15 ++- .../DataResultTopComponent.java | 13 ++- .../autopsy/directorytree/ExtractAction.java | 41 +------ .../directorytree/TagAbstractFileAction.java | 43 ++++---- .../autopsy/directorytree/TagAction.java | 100 ------------------ .../TagBlackboardArtifactAction.java | 44 ++++---- .../autopsy/directorytree/TagMenu.java | 23 ++-- 7 files changed, 76 insertions(+), 203 deletions(-) delete mode 100644 Core/src/org/sleuthkit/autopsy/directorytree/TagAction.java diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/AbstractDataResultViewer.java b/Core/src/org/sleuthkit/autopsy/corecomponents/AbstractDataResultViewer.java index 3387065d1d..942bbbd242 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/AbstractDataResultViewer.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/AbstractDataResultViewer.java @@ -81,21 +81,17 @@ public abstract class AbstractDataResultViewer extends JPanel implements // change the cursor to "waiting cursor" for this operation setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); try { - Node selectedNode = getSelectedNode(); - - nodeSelected(selectedNode); - - - - if (selectedNode != null) { + Node[] selectedNodes = getExplorerManager().getSelectedNodes(); + if (selectedNodes.length == 1) { + nodeSelected(selectedNodes[0]); + // there's a new/changed node to display - Node newSelectedNode = selectedNode; // get the selected Node on the table // push the node to default "DataContent" //TODO only the active viewer should be calling setNode //not all of them, otherwise it results in multiple setNode() invocations //alternative is to use a single instance of the event listener //, per top component and not the tab perhaps - contentViewer.setNode(newSelectedNode); + contentViewer.setNode(selectedNodes[0]); } else { // clear the node viewer contentViewer.setNode(null); @@ -131,6 +127,7 @@ public abstract class AbstractDataResultViewer extends JPanel implements } } + @Deprecated public Node getSelectedNode() { Node result = null; Node[] selectedNodes = this.getExplorerManager().getSelectedNodes(); diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultTopComponent.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultTopComponent.java index ecdf121466..dc4fbb6d8f 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultTopComponent.java @@ -23,6 +23,7 @@ import java.util.Collections; import java.util.List; import org.sleuthkit.autopsy.corecomponentinterfaces.DataResult; import java.util.logging.Level; +import org.openide.explorer.ExplorerUtils; import org.openide.util.NbBundle; import org.openide.windows.TopComponent; import org.openide.nodes.Node; @@ -96,9 +97,9 @@ public class DataResultTopComponent extends TopComponent implements DataResult { initComponents(); customizeComponent(isMain, name);; - + } - + private void customizeComponent(boolean isMain, String title) { this.isMain = isMain; this.customModeName = null; @@ -260,6 +261,14 @@ public class DataResultTopComponent extends TopComponent implements DataResult { super.componentOpened(); this.dataResultPanel.open(); + + List resultViewers = this.dataResultPanel.getViewers(); + for (DataResultViewer viewer : resultViewers) { + if (viewer instanceof DataResultViewerTable) { + associateLookup(ExplorerUtils.createLookup(((DataResultViewerTable)viewer).getExplorerManager(), getActionMap())); + break; + } + } } diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/ExtractAction.java b/Core/src/org/sleuthkit/autopsy/directorytree/ExtractAction.java index 42ee518fa2..1442cb57cd 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/ExtractAction.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/ExtractAction.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011 Basis Technology Corp. + * Copyright 2013 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.directorytree; import java.awt.Component; import java.awt.event.ActionEvent; import java.io.File; +import java.util.Collection; import java.util.concurrent.CancellationException; import java.util.logging.Level; import org.sleuthkit.autopsy.coreutils.Logger; @@ -30,14 +31,9 @@ import javax.swing.JOptionPane; import javax.swing.SwingWorker; import org.netbeans.api.progress.ProgressHandle; import org.netbeans.api.progress.ProgressHandleFactory; -import org.openide.explorer.ExplorerManager; -import org.openide.nodes.Node; import org.openide.util.Cancellable; -import org.openide.util.Lookup; import org.openide.util.Utilities; import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer; -import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable; import org.sleuthkit.autopsy.coreutils.FileUtil; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.datamodel.ContentUtils.ExtractFscContentVisitor; @@ -73,37 +69,10 @@ public final class ExtractAction extends AbstractAction { */ @Override public void actionPerformed(ActionEvent e) { - Lookup.Result res = Utilities.actionsGlobalContext().lookupResult(ExplorerManager.Provider.class); - for (ExplorerManager.Provider dude : res.allInstances()) { - ExplorerManager ex = dude.getExplorerManager(); - Node[] selectedNodes = ex.getSelectedNodes(); - for (Node node : selectedNodes) { - String name = node.getDisplayName(); - } + Collection selectedFiles = Utilities.actionsGlobalContext().lookupAll(AbstractFile.class); + for (AbstractFile file : selectedFiles) { + extractFile(e, file); } - - DataResultViewerTable resultViewer = (DataResultViewerTable)Lookup.getDefault().lookup(DataResultViewer.class); - if (null == resultViewer) { - Logger.getLogger(ExtractAction.class.getName()).log(Level.SEVERE, "Could not get DataResultViewerTable from Lookup"); - return; - } - - Node[] selectedNodes = resultViewer.getExplorerManager().getSelectedNodes(); - if (selectedNodes.length <= 0) { - Logger.getLogger(ExtractAction.class.getName()).log(Level.SEVERE, "Tried to perform tagging of Nodes with no Nodes selected"); - return; - } - - for (Node node : selectedNodes) { - AbstractFile file = node.getLookup().lookup(AbstractFile.class); - if (null != file) { - extractFile(e, file); - } - else { - // RJCTODO -// Logger.getLogger(org.sleuthkit.autopsy.directorytree.TagAbstractFileAction.TagAbstractFileMenu.class.getName()).log(Level.SEVERE, "Node not associated with an AbstractFile object"); - } - } } private void extractFile(ActionEvent e, AbstractFile file) { diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/TagAbstractFileAction.java b/Core/src/org/sleuthkit/autopsy/directorytree/TagAbstractFileAction.java index bf5458b889..657673ac74 100755 --- a/Core/src/org/sleuthkit/autopsy/directorytree/TagAbstractFileAction.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/TagAbstractFileAction.java @@ -18,13 +18,16 @@ */ package org.sleuthkit.autopsy.directorytree; -import java.util.logging.Level; -import org.openide.nodes.Node; -import org.sleuthkit.autopsy.coreutils.Logger; +import java.awt.event.ActionEvent; +import java.util.Collection; +import javax.swing.AbstractAction; +import javax.swing.JMenuItem; +import org.openide.util.Utilities; +import org.openide.util.actions.Presenter; import org.sleuthkit.autopsy.datamodel.Tags; import org.sleuthkit.datamodel.AbstractFile; -public class TagAbstractFileAction extends TagAction { +public class TagAbstractFileAction extends AbstractAction implements Presenter.Popup { // This class is a singleton to support multi-selection of nodes, since // org.openide.nodes.NodeOp.findActions(Node[] nodes) will only pick up an Action if every // node in the array returns a reference to the same action object from Node.getActions(boolean). @@ -34,7 +37,6 @@ public class TagAbstractFileAction extends TagAction { if (null == instance) { instance = new TagAbstractFileAction(); } - return instance; } @@ -42,26 +44,27 @@ public class TagAbstractFileAction extends TagAction { } @Override - protected TagMenu getTagMenu(Node[] selectedNodes) { - return new TagAbstractFileMenu(selectedNodes); + public JMenuItem getPopupPresenter() { + return new TagAbstractFileMenu(); } - + + @Override + public void actionPerformed(ActionEvent e) { + // Do nothing - this action should never be performed. + // Submenu actions are invoked instead. + } + private static class TagAbstractFileMenu extends TagMenu { - public TagAbstractFileMenu(Node[] nodes) { - super((nodes.length > 1 ? "Tag Files" : "Tag File"), nodes); + public TagAbstractFileMenu() { + super(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class).size() > 1 ? "Tag Files" : "Tag File"); } @Override - protected void tagNodes(String tagName, String comment) { - for (Node node : getNodes()) { - AbstractFile file = node.getLookup().lookup(AbstractFile.class); - if (null != file) { - Tags.createTag(file, tagName, comment); - } - else { - Logger.getLogger(org.sleuthkit.autopsy.directorytree.TagAbstractFileAction.TagAbstractFileMenu.class.getName()).log(Level.SEVERE, "Node not associated with an AbstractFile object"); - } - } + protected void applyTag(String tagName, String comment) { + Collection selectedFiles = Utilities.actionsGlobalContext().lookupAll(AbstractFile.class); + for (AbstractFile file : selectedFiles) { + Tags.createTag(file, tagName, comment); + } } } } diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/TagAction.java b/Core/src/org/sleuthkit/autopsy/directorytree/TagAction.java deleted file mode 100644 index e7bae82cb9..0000000000 --- a/Core/src/org/sleuthkit/autopsy/directorytree/TagAction.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2013 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.directorytree; - -import java.awt.event.ActionEvent; -import java.util.Collection; -import java.util.logging.Level; -import javax.swing.AbstractAction; -import javax.swing.JMenuItem; -import org.openide.explorer.ExplorerManager; -import org.openide.nodes.Node; -import org.openide.util.actions.Presenter; -import org.openide.util.Lookup; -import org.openide.util.Utilities; -import org.openide.windows.TopComponent; -import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer; -import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable; - -/** - * Action on a file or artifact that adds a tag and reloads the directory tree. - * Supports tagging of AbstractFiles and BlackboardArtifacts. - */ -public abstract class TagAction extends AbstractAction implements Presenter.Popup { - @Override - public JMenuItem getPopupPresenter() { - Lookup.Result res = Utilities.actionsGlobalContext().lookupResult(ExplorerManager.Provider.class); - for (ExplorerManager.Provider dude : res.allInstances()) { - ExplorerManager ex = dude.getExplorerManager(); - Node[] selectedNodes = ex.getSelectedNodes(); - for (Node node : selectedNodes) { - String name = node.getDisplayName(); - } - } - - Collection tops = Lookup.getDefault().lookupAll(TopComponent.class); - for (TopComponent top : tops) { - int x = 0; - } - - DirectoryTreeTopComponent directoryTree = Lookup.getDefault().lookup(DirectoryTreeTopComponent.class); - if (null == directoryTree) { - Logger.getLogger(TagAction.class.getName()).log(Level.SEVERE, "Could not get DataResultViewerTable from Lookup"); - return null; - } - - Node[] activatedNodes = directoryTree.getActivatedNodes(); - for (Node node : activatedNodes) { - String name = node.getDisplayName(); - } - - Lookup.Result resultViewers = Lookup.getDefault().lookupResult(DataResultViewer.class); - for (DataResultViewer resultViewer : resultViewers.allInstances()) { - if (resultViewer instanceof DataResultViewerTable) { - Node[] selectedNodes = ((DataResultViewerTable)resultViewer).getExplorerManager().getSelectedNodes(); - for (Node node : selectedNodes) { - String name = node.getDisplayName(); - } - } - } - - DataResultViewerTable resultViewer = (DataResultViewerTable)Lookup.getDefault().lookup(DataResultViewer.class); - if (null == resultViewer) { - Logger.getLogger(TagAction.class.getName()).log(Level.SEVERE, "Could not get DataResultViewerTable from Lookup"); - return null; - } - - Node[] selectedNodes = resultViewer.getExplorerManager().getSelectedNodes(); - if (selectedNodes.length <= 0) { - Logger.getLogger(TagAction.class.getName()).log(Level.SEVERE, "Tried to perform tagging of Nodes with no Nodes selected"); - return null; - } - - return getTagMenu(selectedNodes); - } - - protected abstract TagMenu getTagMenu(Node[] selectedNodes); - - @Override - public void actionPerformed(ActionEvent e) { - // Do nothing - this action should never be performed. - // Submenu actions are invoked instead. - } -} \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/TagBlackboardArtifactAction.java b/Core/src/org/sleuthkit/autopsy/directorytree/TagBlackboardArtifactAction.java index 7754b127ad..3d1a9641b3 100755 --- a/Core/src/org/sleuthkit/autopsy/directorytree/TagBlackboardArtifactAction.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/TagBlackboardArtifactAction.java @@ -18,13 +18,16 @@ */ package org.sleuthkit.autopsy.directorytree; -import java.util.logging.Level; -import org.openide.nodes.Node; -import org.sleuthkit.autopsy.coreutils.Logger; +import java.awt.event.ActionEvent; +import java.util.Collection; +import javax.swing.AbstractAction; +import javax.swing.JMenuItem; +import org.openide.util.Utilities; +import org.openide.util.actions.Presenter; import org.sleuthkit.autopsy.datamodel.Tags; import org.sleuthkit.datamodel.BlackboardArtifact; -public class TagBlackboardArtifactAction extends TagAction { +public class TagBlackboardArtifactAction extends AbstractAction implements Presenter.Popup { // This class is a singleton to support multi-selection of nodes, since // org.openide.nodes.NodeOp.findActions(Node[] nodes) will only pick up an Action if every // node in the array returns a reference to the same action object from Node.getActions(boolean). @@ -34,7 +37,6 @@ public class TagBlackboardArtifactAction extends TagAction { if (null == instance) { instance = new TagBlackboardArtifactAction(); } - return instance; } @@ -42,26 +44,28 @@ public class TagBlackboardArtifactAction extends TagAction { } @Override - protected TagMenu getTagMenu(Node[] selectedNodes) { - return new TagBlackboardArtifactMenu(selectedNodes); - } + public JMenuItem getPopupPresenter() { + return new TagBlackboardArtifactMenu(); + } + + @Override + public void actionPerformed(ActionEvent e) { + // Do nothing - this action should never be performed. + // Submenu actions are invoked instead. + } + private static class TagBlackboardArtifactMenu extends TagMenu { - public TagBlackboardArtifactMenu(Node[] nodes) { - super((nodes.length > 1 ? "Tag Results" : "Tag Result"), nodes); + public TagBlackboardArtifactMenu() { + super(Utilities.actionsGlobalContext().lookupAll(BlackboardArtifact.class).size() > 1 ? "Tag Results" : "Tag Result"); } @Override - protected void tagNodes(String tagName, String comment) { - for (Node node : getNodes()) { - BlackboardArtifact artifact = node.getLookup().lookup(BlackboardArtifact.class); - if (null != artifact) { - Tags.createTag(artifact, tagName, comment); - } - else { - Logger.getLogger(org.sleuthkit.autopsy.directorytree.TagBlackboardArtifactAction.TagBlackboardArtifactMenu.class.getName()).log(Level.SEVERE, "Node not associated with a BlackboardArtifact object"); - } - } + protected void applyTag(String tagName, String comment) { + Collection selectedArtifacts = Utilities.actionsGlobalContext().lookupAll(BlackboardArtifact.class); + for (BlackboardArtifact artifact : selectedArtifacts) { + Tags.createTag(artifact, tagName, comment); + } } } } diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/TagMenu.java b/Core/src/org/sleuthkit/autopsy/directorytree/TagMenu.java index 9d6709f39b..e329dde10c 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/TagMenu.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/TagMenu.java @@ -23,7 +23,6 @@ import java.awt.event.ActionListener; import java.util.List; import javax.swing.JMenu; import javax.swing.JMenuItem; -import org.openide.nodes.Node; import org.sleuthkit.autopsy.datamodel.Tags; import org.sleuthkit.datamodel.BlackboardArtifact; @@ -31,12 +30,8 @@ import org.sleuthkit.datamodel.BlackboardArtifact; * The menu that results when one right-clicks on a file or artifact. */ public abstract class TagMenu extends JMenu { - - private Node[] nodes; - - public TagMenu(String menuItemText, Node[] selectedNodes) { + public TagMenu(String menuItemText) { super(menuItemText); - this.nodes = selectedNodes; // Create the 'Quick Tag' sub-menu and add it to the tag menu. JMenu quickTagMenu = new JMenu("Quick Tag"); @@ -56,7 +51,7 @@ public abstract class TagMenu extends JMenu { tagNameItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - tagNodes(tagName, ""); + applyTag(tagName, ""); refreshDirectoryTree(); } }); @@ -72,7 +67,7 @@ public abstract class TagMenu extends JMenu { public void actionPerformed(ActionEvent e) { String tagName = CreateTagDialog.getNewTagNameDialog(null); if (tagName != null) { - tagNodes(tagName, ""); + applyTag(tagName, ""); refreshDirectoryTree(); } } @@ -86,24 +81,20 @@ public abstract class TagMenu extends JMenu { public void actionPerformed(ActionEvent e) { TagAndCommentDialog.CommentedTag commentedTag = TagAndCommentDialog.doDialog(); if (null != commentedTag) { - tagNodes(commentedTag.getName(), commentedTag.getComment()); + applyTag(commentedTag.getName(), commentedTag.getComment()); refreshDirectoryTree(); } } }); add(tagAndCommentItem); } - - protected Node[] getNodes() { - return nodes; - } - - protected abstract void tagNodes(String tagName, String comment); - + private void refreshDirectoryTree() { //TODO instead should send event to node children, which will call its refresh() / refreshKeys() DirectoryTreeTopComponent viewer = DirectoryTreeTopComponent.findInstance(); viewer.refreshTree(BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_FILE); viewer.refreshTree(BlackboardArtifact.ARTIFACT_TYPE.TSK_TAG_ARTIFACT); } + + protected abstract void applyTag(String tagName, String comment); } From 948312fb331f9f4153955c6abc79257c028af79f Mon Sep 17 00:00:00 2001 From: Brian Carrier Date: Mon, 12 Aug 2013 11:04:05 -0400 Subject: [PATCH 08/35] Added basic file ingest module, 7ZIP and Exif skip more files --- .../examples/SampleFileIngestModule.java | 178 ++++++++++++++++++ .../ExifParserFileIngestModule.java | 6 + .../sevenzip/SevenZipIngestModule.java | 11 ++ 3 files changed, 195 insertions(+) create mode 100755 Core/src/org/sleuthkit/autopsy/examples/SampleFileIngestModule.java diff --git a/Core/src/org/sleuthkit/autopsy/examples/SampleFileIngestModule.java b/Core/src/org/sleuthkit/autopsy/examples/SampleFileIngestModule.java new file mode 100755 index 0000000000..8a24bd7c9b --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/examples/SampleFileIngestModule.java @@ -0,0 +1,178 @@ +/* +* 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 org.apache.log4j.Logger; +import org.openide.util.Exceptions; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.ingest.IngestModuleAbstractFile; +import org.sleuthkit.autopsy.ingest.IngestModuleInit; +import org.sleuthkit.autopsy.ingest.PipelineContext; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskData; + +/** + * This is a sample and simple module. It is a file-level ingest module, meaning + * that it will get called on each file in the disk image / logical file set. + * It does a stupid calculation of the number of null bytes in the beginning of the + * file in order to show the basic flow. + * + * Autopsy has been hard coded to ignore this module based on the name returned in + * getName(). Ensure that you change the module name when you use this as a template. + */ +public class SampleFileIngestModule extends org.sleuthkit.autopsy.ingest.IngestModuleAbstractFile { + private int attrId = -1; + private static SampleFileIngestModule defaultInstance = null; + + // Private to ensure Singleton status + private SampleFileIngestModule() { + } + + // File-level ingest modules are currently singleton -- this is required + public static synchronized SampleFileIngestModule getDefault() { + //defaultInstance is a private static class variable + if (defaultInstance == null) { + defaultInstance = new SampleFileIngestModule(); + } + return defaultInstance; + } + + + @Override + public String getName() { + // @@@ CHANGE THIS IN A REAL MODULE -- AUTOPSY IGNORES MODULES WITH THIS NAME + return "SampleFileIngestIgnore"; + } + + @Override + public void init(IngestModuleInit initContext) { + /* For this demo, we are going to make a private attribute to post our + * results to the blackbaord with. There are many standard blackboard artifact + * and attribute types and you should first consider using one of those before + * making private ones because other modules won't know about provate ones. + * Because our demo has results that have no real value, we do not have an + * official attribute for them. + */ + Case case1 = Case.getCurrentCase(); + SleuthkitCase sleuthkitCase = case1.getSleuthkitCase(); + + // see if the type already exists in the blackboard. + try { + attrId = sleuthkitCase.getAttrTypeID("ATTR_SAMPLE"); + } catch (TskCoreException ex) { + // create it if not + try { + attrId = sleuthkitCase.addAttrType("ATTR_SAMPLE", "Sample Attribute"); + } catch (TskCoreException ex1) { + Logger log = Logger.getLogger(SampleFileIngestModule.class); + log.fatal("Error adding attribute type: " + ex1.getLocalizedMessage()); + attrId = -1; + } + } + } + + @Override + public ProcessResult process(PipelineContext pipelineContext, AbstractFile abstractFile) { + // skip non-files + if ((abstractFile.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS) || + (abstractFile.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS)) { + return ProcessResult.OK; + } + + // skip NSRL / known files + if (abstractFile.getKnown() == TskData.FileKnown.KNOWN) { + return ProcessResult.OK; + } + + + /* Do a non-sensical calculation of the number of 0x00 bytes + * in the first 1024-bytes of the file. This is for demo + * purposes only. + */ + try { + byte buffer[] = new byte[1024]; + int len = abstractFile.read(buffer, 0, 1024); + int count = 0; + for (int i = 0; i < len; i++) { + if (buffer[i] == 0x00) { + count++; + } + } + + if (attrId != -1) { + // Make an attribute using the ID for the private type that we previously created. + BlackboardAttribute attr = new BlackboardAttribute(attrId, getName(), count); + + /* add it to the general info artifact. In real modules, you would likely have + * more complex data types and be making more specific artifacts. + */ + BlackboardArtifact art = abstractFile.getGenInfoArtifact(); + art.addAttribute(attr); + } + + return ProcessResult.OK; + } catch (TskCoreException ex) { + Exceptions.printStackTrace(ex); + return ProcessResult.ERROR; + } + } + + + @Override + public void complete() { + + } + + @Override + public void stop() { + + } + + @Override + public String getVersion() { + return "1.0"; + } + + @Override + public String getDescription() { + return "Doesn't do much"; + } + + @Override + public boolean hasBackgroundJobsRunning() { + // we're single threaded... + return false; + } +} diff --git a/ExifParser/src/org/sleuthkit/autopsy/exifparser/ExifParserFileIngestModule.java b/ExifParser/src/org/sleuthkit/autopsy/exifparser/ExifParserFileIngestModule.java index 663c7b007f..a6fb867052 100644 --- a/ExifParser/src/org/sleuthkit/autopsy/exifparser/ExifParserFileIngestModule.java +++ b/ExifParser/src/org/sleuthkit/autopsy/exifparser/ExifParserFileIngestModule.java @@ -46,6 +46,7 @@ import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; import org.sleuthkit.datamodel.ReadContentInputStream; import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.TskData.TSK_DB_FILES_TYPE_ENUM; /** @@ -92,6 +93,11 @@ public final class ExifParserFileIngestModule extends IngestModuleAbstractFile { return IngestModuleAbstractFile.ProcessResult.OK; } + // skip known + if (content.getKnown().equals(TskData.FileKnown.KNOWN)) { + return IngestModuleAbstractFile.ProcessResult.OK; + } + //skip unsupported if (! parsableFormat(content)) { return IngestModuleAbstractFile.ProcessResult.OK; diff --git a/SevenZip/src/org/sleuthkit/autopsy/sevenzip/SevenZipIngestModule.java b/SevenZip/src/org/sleuthkit/autopsy/sevenzip/SevenZipIngestModule.java index 26a863f4c7..ee96f7d4d5 100644 --- a/SevenZip/src/org/sleuthkit/autopsy/sevenzip/SevenZipIngestModule.java +++ b/SevenZip/src/org/sleuthkit/autopsy/sevenzip/SevenZipIngestModule.java @@ -58,6 +58,7 @@ import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; import org.sleuthkit.datamodel.DerivedFile; import org.sleuthkit.datamodel.ReadContentInputStream; import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.TskData; /** * 7Zip ingest module Extracts supported archives, adds extracted DerivedFiles, @@ -164,6 +165,16 @@ public final class SevenZipIngestModule extends IngestModuleAbstractFile { logger.log(Level.WARNING, "Skipping processing, module not initialized, file: " + abstractFile.getName()); return ProcessResult.OK; } + + //skip unalloc + if(abstractFile.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS)) { + return IngestModuleAbstractFile.ProcessResult.OK; + } + + // skip known + if (abstractFile.getKnown().equals(TskData.FileKnown.KNOWN)) { + return IngestModuleAbstractFile.ProcessResult.OK; + } if (abstractFile.isFile() == false || !isSupported(abstractFile)) { //do not process dirs and files that are not supported From e81615dbc55eb98c96b7c5939d3cb08e54a14acc Mon Sep 17 00:00:00 2001 From: Brian Carrier Date: Mon, 12 Aug 2013 11:04:32 -0400 Subject: [PATCH 09/35] Added a reference to MessageNOtifyUtil --- docs/doxygen/platformConcepts.dox | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/doxygen/platformConcepts.dox b/docs/doxygen/platformConcepts.dox index c8ae7cef1f..72701c5b5c 100644 --- a/docs/doxygen/platformConcepts.dox +++ b/docs/doxygen/platformConcepts.dox @@ -52,6 +52,7 @@ services are provided: - FileManager: the org.sleuthkit.autopsy.casemodule.services.FileManager service provides an API to access any file in the case. You can access FileManager by calling org.sleuthkit.autopsy.casemodule.services.Services.getFileManager(). Data Source-level Ingest modules and Report modules typically use this service because the other modules are passed in a reference to a specific file to do something with. - org.sleuthkit.autopsy.coreutils.Logger - for adding log messages to central logger - IngestModules also have a class that provides additional services. See \ref ingestmodule_services. +- MessageNotifyUtil.Notify.show() can be used to send messages to the user in the lower right-hand area. \subsection mod_dev_other_utilities Framework Utilities From 966da82416ce2092909c15585a4a09852bd84222 Mon Sep 17 00:00:00 2001 From: Brian Carrier Date: Mon, 12 Aug 2013 18:29:21 -0400 Subject: [PATCH 10/35] Updated API changes to keep it inline with 3.0.6, added context to uses of ingest panels. --- .../casemodule/AddImageWizardPanel3.java | 4 +- .../casemodule/GeneralIngestConfigurator.java | 8 +++ .../casemodule/IngestConfigurator.java | 8 ++- .../autopsy/coreutils/ModuleSettings.java | 1 + .../autopsy/ingest/IngestDialog.java | 8 ++- .../autopsy/ingest/IngestDialogPanel.java | 14 ++++- .../autopsy/ingest/IngestManager.java | 20 ++++--- .../autopsy/ingest/IngestModuleAbstract.java | 56 +++++++++++-------- .../hashdatabase/HashDbIngestModule.java | 4 +- .../KeywordSearchIngestModule.java | 4 +- 10 files changed, 84 insertions(+), 43 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardPanel3.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardPanel3.java index 7ec7090c03..98aa035b8a 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardPanel3.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardPanel3.java @@ -55,7 +55,7 @@ import org.sleuthkit.datamodel.TskException; class AddImageWizardPanel3 implements WizardDescriptor.Panel { private static final Logger logger = Logger.getLogger(AddImageWizardPanel3.class.getName()); - private IngestConfigurator ingestConfig = Lookup.getDefault().lookup(IngestConfigurator.class); + private IngestConfigurator ingestConfig; /** * The visual component that displays this panel. If you need to access the * component from this class, just use getComponent(). @@ -85,6 +85,8 @@ class AddImageWizardPanel3 implements WizardDescriptor.Panel { AddImageWizardPanel3(AddImageAction action, AddImageWizardPanel2 wizPanel) { this.action = action; this.wizPanel = wizPanel; + ingestConfig = Lookup.getDefault().lookup(IngestConfigurator.class); + ingestConfig.setContext(AddImageWizardPanel3.class.getCanonicalName()); } /** diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/GeneralIngestConfigurator.java b/Core/src/org/sleuthkit/autopsy/casemodule/GeneralIngestConfigurator.java index 3ce1571150..ba486f33a6 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/GeneralIngestConfigurator.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/GeneralIngestConfigurator.java @@ -41,10 +41,18 @@ public class GeneralIngestConfigurator implements IngestConfigurator { public GeneralIngestConfigurator() { this.moduleContext = IngestManager.MODULE_PROPERTIES; // Hard-code this for now. ingestDialogPanel = new IngestDialogPanel(); + ingestDialogPanel.setContext(moduleContext); manager = IngestManager.getDefault(); loadSettings(); } + @Override + public void setContext(String context) { + moduleContext = context; + ingestDialogPanel.setContext(moduleContext); + reload(); + } + @Override public JPanel getIngestConfigPanel() { return ingestDialogPanel; diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/IngestConfigurator.java b/Core/src/org/sleuthkit/autopsy/casemodule/IngestConfigurator.java index 45f6a8e80d..3b585b2167 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/IngestConfigurator.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/IngestConfigurator.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013 Basis Technology Corp. + * Copyright 2011-2013 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -59,5 +59,11 @@ public interface IngestConfigurator { * @return true if ingest process is running, false otherwise */ boolean isIngestRunning(); + + /** + * Set the context for the configuration. + * @param context + */ + public void setContext(String context); } diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/ModuleSettings.java b/Core/src/org/sleuthkit/autopsy/coreutils/ModuleSettings.java index 4eff288380..15d5e3d7a3 100644 --- a/Core/src/org/sleuthkit/autopsy/coreutils/ModuleSettings.java +++ b/Core/src/org/sleuthkit/autopsy/coreutils/ModuleSettings.java @@ -38,6 +38,7 @@ public class ModuleSettings { // The directory where the properties file is lcoated private final static String moduleDirPath = PlatformUtil.getUserConfigDirectory(); + public static final String DEFAULT_CONTEXT = "GeneralContext"; public static final String MAIN_SETTINGS = "Case"; /** the constructor */ diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestDialog.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestDialog.java index 598d768e38..5305700839 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestDialog.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestDialog.java @@ -32,11 +32,13 @@ import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JPanel; import org.openide.util.Lookup; +import org.sleuthkit.autopsy.casemodule.GeneralIngestConfigurator; import org.sleuthkit.datamodel.Content; import org.sleuthkit.autopsy.casemodule.IngestConfigurator; /** - * IngestDialog shown on Case.CASE_ADD_IMAGE property change + * Dialog box that allows ingest modules to be run on an image. + * Used outside of the wizards. */ public class IngestDialog extends JDialog { @@ -46,7 +48,8 @@ public class IngestDialog extends JDialog { public IngestDialog(JFrame frame, String title, boolean modal) { super(frame, title, modal); - ingestConfigurator = Lookup.getDefault().lookup(IngestConfigurator.class); + ingestConfigurator = new GeneralIngestConfigurator(); + ingestConfigurator.setContext(IngestDialog.class.getCanonicalName()); ingestConfigurator.reload(); } @@ -76,6 +79,7 @@ public class IngestDialog extends JDialog { @Override public void actionPerformed(ActionEvent e) { + ingestConfigurator.save(); ingestConfigurator.start(); close(); } diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestDialogPanel.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestDialogPanel.java index 98d7c197fe..b5308da4ce 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestDialogPanel.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestDialogPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013 Basis Technology Corp. + * Copyright 2011-2013 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -35,6 +35,7 @@ import javax.swing.table.AbstractTableModel; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.TableColumn; import org.sleuthkit.autopsy.corecomponents.AdvancedConfigurationDialog; +import org.sleuthkit.autopsy.coreutils.ModuleSettings; /** * main configuration panel for all ingest modules, reusable JPanel component @@ -45,15 +46,22 @@ public class IngestDialogPanel extends javax.swing.JPanel { private ModulesTableModel tableModel; public static final String DISABLED_MOD = "Disabled_Ingest_Modules"; public static final String PARSE_UNALLOC = "Process_Unallocated_Space"; + private String context; /** * Creates new form IngestDialogPanel */ public IngestDialogPanel() { tableModel = new ModulesTableModel(); + context = ModuleSettings.DEFAULT_CONTEXT; initComponents(); customizeComponents(); } + + public void setContext(String context) { + this.context = context; + } + public IngestModuleAbstract getCurrentIngestModule() { return currentModule; @@ -99,7 +107,7 @@ public class IngestDialogPanel extends javax.swing.JPanel { // add the module-specific configuration panel, if there is one simplePanel.removeAll(); if (currentModule.hasSimpleConfiguration()) { - simplePanel.add(currentModule.getSimpleConfiguration()); + simplePanel.add(currentModule.getSimpleConfiguration(context)); } simplePanel.revalidate(); simplePanel.repaint(); @@ -267,7 +275,7 @@ public class IngestDialogPanel extends javax.swing.JPanel { dialog.close(); } }); - dialog.display(currentModule.getAdvancedConfiguration()); + dialog.display(currentModule.getAdvancedConfiguration(context)); }//GEN-LAST:event_advancedButtonActionPerformed private void processUnallocCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_processUnallocCheckboxActionPerformed diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestManager.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestManager.java index 3d6e73ef3a..77df96496a 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestManager.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestManager.java @@ -347,11 +347,11 @@ public class IngestManager { //AbstractFile ingester boolean startAbstractFileIngester = false; if (fileScheduler.hasNext()) { - if (abstractFileIngester - == null) { + if (abstractFileIngester == null) { startAbstractFileIngester = true; logger.log(Level.INFO, "Starting initial AbstractFile ingester"); - } //if worker had completed, restart it in case data is still enqueued + } + //if worker had completed, restart it in case data is still enqueued else if (abstractFileIngester.isDone()) { startAbstractFileIngester = true; logger.log(Level.INFO, "Restarting AbstractFile ingester"); @@ -364,6 +364,9 @@ public class IngestManager { stats = new IngestManagerStats(); abstractFileIngester = new IngestAbstractFileProcessor(); //init all fs modules, everytime new worker starts + /* @@@ I don't understand why we do an init on each module. Should do only modules + * that we are going to be using in the pipeline + */ for (IngestModuleAbstractFile s : abstractFileModules) { IngestModuleInit moduleInit = new IngestModuleInit(); try { @@ -1066,10 +1069,7 @@ public class IngestManager { } private void queueAll(List modules, final List inputs) { - - final IngestScheduler.DataSourceScheduler dataSourceScheduler = scheduler.getDataSourceScheduler(); - final IngestScheduler.FileScheduler fileScheduler = scheduler.getFileScheduler(); - + int processed = 0; for (Content input : inputs) { final String inputName = input.getName(); @@ -1096,16 +1096,16 @@ public class IngestManager { logger.log(Level.INFO, "Error loading module and adding input " + inputName + " with module " + module.getName()); } - break; case AbstractFile: //enqueue the same singleton AbstractFile module logger.log(Level.INFO, "Adding input " + inputName - + " number of AbstractFile to module " + module.getName()); + + " for AbstractFileModule " + module.getName()); fileMods.add((IngestModuleAbstractFile) module); break; + default: logger.log(Level.SEVERE, "Unexpected module type: " + module.getType().name()); } @@ -1122,6 +1122,7 @@ public class IngestManager { new PipelineContext(dataSourceTask, processUnalloc); logger.log(Level.INFO, "Queing data source ingest task: " + dataSourceTask); progress.progress("DataSource Ingest" + " " + inputName, processed); + final IngestScheduler.DataSourceScheduler dataSourceScheduler = scheduler.getDataSourceScheduler(); dataSourceScheduler.schedule(dataSourcePipelineContext); progress.progress("DataSource Ingest" + " " + inputName, ++processed); @@ -1132,6 +1133,7 @@ public class IngestManager { = new PipelineContext(fTask, processUnalloc); logger.log(Level.INFO, "Queing file ingest task: " + fTask); progress.progress("File Ingest" + " " + inputName, processed); + final IngestScheduler.FileScheduler fileScheduler = scheduler.getFileScheduler(); fileScheduler.schedule(filepipelineContext); progress.progress("File Ingest" + " " + inputName, ++processed); diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleAbstract.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleAbstract.java index f8ff21bb82..7165640b75 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleAbstract.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleAbstract.java @@ -145,28 +145,38 @@ public abstract class IngestModuleAbstract { */ public void saveAdvancedConfiguration() {} - /** - * Returns a panel that displays the simple (run-time) configuration. - * This is presented to the user before ingest starts and only basic - * settings should be given here. use the advanced (general) configuration - * panel for more in-depth interfaces. - * The module is responsible for preserving / saving its configuration state - * In addition, saveSimpleConfiguration() can be used - * - * @return JPanel containing basic configuration widgets or null if simple configuration is not available - */ - public javax.swing.JPanel getSimpleConfiguration() { - return null; - } + + /** + * Returns a panel that displays the simple (run-time) configuration for the + * given configuration context (such as pipeline instance). This is + * presented to the user before ingest starts and only basic settings should + * be given here. Use the advanced (general) configuration panel for more + * in-depth interfaces. The module (or its configuration controller object) + * is responsible for preserving / saving its configuration state In + * addition, saveSimpleConfiguration() can be used as the trigger. + * + * @param context the configuration context to use in the panel + * @return JPanel containing basic configuration widgets or null if simple + * configuration is not available + */ + public javax.swing.JPanel getSimpleConfiguration(String context) { + return null; + } - /** - * Implements advanced module configuration exposed to the user before ingest starts - * The module is responsible for preserving / saving its configuration state - * In addition, saveAdvancedConfiguration() can be used - * - * @return JPanel containing basic configuration widgets or null if advanced configuration is not available - */ - public javax.swing.JPanel getAdvancedConfiguration() { - return null; - }; + /** + * Returns a panel that displays the advanced (run-time) configuration for + * the given configuration context (such as pipeline instance). Implements + * advanced module configuration exposed to the user before ingest starts. + * + * The module (or its configuration controller object) is responsible for + * preserving / saving its configuration state In addition, + * saveAdvancedConfiguration() can be used as the trigger. + * + * @param context the configuration context to use in the panel + * @return JPanel containing advanced configuration widgets or null if + * advanced configuration is not available + */ + public javax.swing.JPanel getAdvancedConfiguration(String context) { + return null; + } } \ No newline at end of file diff --git a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbIngestModule.java b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbIngestModule.java index ba7e621d7e..dd311ee2ed 100644 --- a/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbIngestModule.java +++ b/HashDatabase/src/org/sleuthkit/autopsy/hashdatabase/HashDbIngestModule.java @@ -216,13 +216,13 @@ public class HashDbIngestModule extends IngestModuleAbstractFile { } @Override - public javax.swing.JPanel getSimpleConfiguration() { + public javax.swing.JPanel getSimpleConfiguration(String context) { HashDbXML.getCurrent().reload(); return new HashDbSimplePanel(); } @Override - public javax.swing.JPanel getAdvancedConfiguration() { + public javax.swing.JPanel getAdvancedConfiguration(String context) { //return HashDbManagementPanel.getDefault(); getPanel().load(); return getPanel(); diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java index f447695a34..e6bf38c848 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java @@ -432,13 +432,13 @@ public final class KeywordSearchIngestModule extends IngestModuleAbstractFile { } @Override - public javax.swing.JPanel getSimpleConfiguration() { + public javax.swing.JPanel getSimpleConfiguration(String context) { KeywordSearchListsXML.getCurrent().reload(); return new KeywordSearchIngestSimplePanel(); } @Override - public javax.swing.JPanel getAdvancedConfiguration() { + public javax.swing.JPanel getAdvancedConfiguration(String context) { //return KeywordSearchConfigurationPanel.getDefault(); getPanel().load(); return getPanel(); From 84142a50c45becaa090cee03e510ed20ec94b56e Mon Sep 17 00:00:00 2001 From: Brian Carrier Date: Mon, 12 Aug 2013 19:16:04 -0400 Subject: [PATCH 11/35] Documented that we added a hack --- .../autopsy/corecomponents/DataResultTopComponent.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultTopComponent.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultTopComponent.java index dc4fbb6d8f..f2a70a7e51 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultTopComponent.java @@ -262,6 +262,9 @@ public class DataResultTopComponent extends TopComponent implements DataResult { this.dataResultPanel.open(); + /* @@@ Short-term hack to associate lookup with the table view so that we can do multi-select. + * Longer-term solution is to use same explorer Manager for all viewers. + */ List resultViewers = this.dataResultPanel.getViewers(); for (DataResultViewer viewer : resultViewers) { if (viewer instanceof DataResultViewerTable) { From e17b9f549f5d2f19b57b3c75134e5019c4e5062e Mon Sep 17 00:00:00 2001 From: Brian Carrier Date: Mon, 12 Aug 2013 20:18:45 -0400 Subject: [PATCH 12/35] Added some comments to keyword search module --- .../AbstractFileTikaTextExtract.java | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/AbstractFileTikaTextExtract.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/AbstractFileTikaTextExtract.java index 45bfa5035d..d015f65c17 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/AbstractFileTikaTextExtract.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/AbstractFileTikaTextExtract.java @@ -120,12 +120,11 @@ public class AbstractFileTikaTextExtract implements AbstractFileExtract { boolean success = false; Reader reader = null; - - final InputStream stream = new ReadContentInputStream(sourceFile); try { Metadata meta = new Metadata(); - //Tika parse request with timeout + + //Parse the file in a task Tika tika = new Tika(); //new tika instance for every file, to workaround tika memory issues ParseRequestTask parseTask = new ParseRequestTask(tika, stream, meta, sourceFile); final Future future = tikaParseExecutor.submit(parseTask); @@ -145,14 +144,16 @@ public class AbstractFileTikaTextExtract implements AbstractFileExtract { throw new IngesterException(msg); } + // get the reader with the results reader = parseTask.getReader(); - if (reader == null) { //likely due to exception in parse() logger.log(Level.WARNING, "No reader available from Tika parse"); return false; } + + // break the results into chunks and index success = true; long readSize; long totalRead = 0; @@ -180,8 +181,6 @@ public class AbstractFileTikaTextExtract implements AbstractFileExtract { //this is the last chunk eof = true; } - - } //logger.log(Level.INFO, "TOTAL READ SIZE: " + totalRead + " file: " + sourceFile.getName()); @@ -214,7 +213,7 @@ public class AbstractFileTikaTextExtract implements AbstractFileExtract { } extracted = sb.toString(); - + //converts BOM automatically to charSet encoding byte[] encodedBytes = extracted.getBytes(OUTPUT_CHARSET); AbstractFileChunk chunk = new AbstractFileChunk(this, this.numChunks + 1); @@ -293,8 +292,8 @@ public class AbstractFileTikaTextExtract implements AbstractFileExtract { } /** - * Runnable and timeable task that calls tika to parse the content using - * streaming + * Runnable task that calls tika to parse the content using + * the input stream. Provides reader for results. */ private static class ParseRequestTask implements Runnable { From 9a32f0be875a85283d8a0a20cdda5330ec0b1fca Mon Sep 17 00:00:00 2001 From: Brian Carrier Date: Tue, 13 Aug 2013 11:17:31 -0400 Subject: [PATCH 13/35] Added internal comments to keyword search code --- .../AbstractKeywordSearchPerformer.java | 6 +- .../autopsy/keywordsearch/Keyword.java | 36 ++++++--- .../KeywordSearchListsViewerPanel.java | 2 +- .../KeywordSearchPerformerInterface.java | 44 +++++++++-- .../keywordsearch/KeywordSearchQuery.java | 11 ++- .../KeywordSearchQueryManager.java | 75 +++++++++++++------ .../autopsy/keywordsearch/LuceneQuery.java | 36 ++++++--- .../keywordsearch/TermComponentQuery.java | 4 + 8 files changed, 156 insertions(+), 58 deletions(-) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/AbstractKeywordSearchPerformer.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/AbstractKeywordSearchPerformer.java index 105f011b2e..8fc05094df 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/AbstractKeywordSearchPerformer.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/AbstractKeywordSearchPerformer.java @@ -29,7 +29,8 @@ import org.sleuthkit.autopsy.keywordsearch.KeywordSearch.QueryType; import org.sleuthkit.autopsy.keywordsearch.KeywordSearchQueryManager.Presentation; /** - * Common functionality among keyword search performers / widgets + * Common functionality among keyword search performers / widgets. + * This is extended by the various panels and interfaces that perform the keyword searches. */ abstract class AbstractKeywordSearchPerformer extends javax.swing.JPanel implements KeywordSearchPerformerInterface { @@ -105,7 +106,8 @@ abstract class AbstractKeywordSearchPerformer extends javax.swing.JPanel impleme return; } man = new KeywordSearchQueryManager(keywords, Presentation.COLLAPSE); - } else { + } + else { QueryType queryType = null; if (isLuceneQuerySelected()) { queryType = QueryType.WORD; diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Keyword.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Keyword.java index 99524a700f..f715a97e87 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Keyword.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Keyword.java @@ -17,24 +17,34 @@ * limitations under the License. */ -/** - * Representation of keyword query as input by user - */ package org.sleuthkit.autopsy.keywordsearch; import org.sleuthkit.datamodel.BlackboardAttribute; +/** + * Representation of single keyword to search for + */ public class Keyword { - - private String query; - private boolean isLiteral; + private String keywordString; // keyword to search for + private boolean isLiteral; // false if reg exp private BlackboardAttribute.ATTRIBUTE_TYPE keywordType = null; + /** + * + * @param query Keyword to search for + * @param isLiteral false if reg exp + */ Keyword(String query, boolean isLiteral) { - this.query = query; + this.keywordString = query; this.isLiteral = isLiteral; } + /** + * + * @param query Keyword to search for + * @param isLiteral false if reg exp + * @param keywordType + */ Keyword(String query, boolean isLiteral, BlackboardAttribute.ATTRIBUTE_TYPE keywordType) { this(query, isLiteral); this.keywordType = keywordType; @@ -48,8 +58,12 @@ public class Keyword { return this.keywordType; } + /** + * + * @return Keyword to search for + */ String getQuery() { - return query; + return keywordString; } boolean isLiteral() { @@ -58,7 +72,7 @@ public class Keyword { @Override public String toString() { - return "Keyword{" + "query=" + query + ", isLiteral=" + isLiteral + ", keywordType=" + keywordType + '}'; + return "Keyword{" + "query=" + keywordString + ", isLiteral=" + isLiteral + ", keywordType=" + keywordType + '}'; } @@ -72,7 +86,7 @@ public class Keyword { return false; } final Keyword other = (Keyword) obj; - if ((this.query == null) ? (other.query != null) : !this.query.equals(other.query)) { + if ((this.keywordString == null) ? (other.keywordString != null) : !this.keywordString.equals(other.keywordString)) { return false; } if (this.isLiteral != other.isLiteral) { @@ -84,7 +98,7 @@ public class Keyword { @Override public int hashCode() { int hash = 7; - hash = 17 * hash + (this.query != null ? this.query.hashCode() : 0); + hash = 17 * hash + (this.keywordString != null ? this.keywordString.hashCode() : 0); hash = 17 * hash + (this.isLiteral ? 1 : 0); return hash; } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchListsViewerPanel.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchListsViewerPanel.java index e124ef44e2..3aec4613e3 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchListsViewerPanel.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchListsViewerPanel.java @@ -43,7 +43,7 @@ import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.IngestManager.IngestModuleEvent; /** - * Viewer panel widget for keyword lists + * Viewer panel widget for keyword lists that is used in the ingest config and options area. */ class KeywordSearchListsViewerPanel extends AbstractKeywordSearchPerformer { diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchPerformerInterface.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchPerformerInterface.java index 366dba7ef7..01c65a471b 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchPerformerInterface.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchPerformerInterface.java @@ -22,16 +22,46 @@ import java.util.List; /** - * common methods for the KeywordSearch performers - * + * KeywordSearchPerformers are perform different searches from + * different interfaces and places in the application. Its + * results are then passed to a KeywordSearchQuery implementation + * to perform the actual search. */ interface KeywordSearchPerformerInterface { + /** + * Does this interface support multi-word queries? + * @return + */ boolean isMultiwordQuery(); - boolean isLuceneQuerySelected(); - String getQueryText(); - List getQueryList(); - void setFilesIndexed(int filesIndexed); - void search(); + /** + * True if the user did not choose to do a regular expression search + * @return + */ + boolean isLuceneQuerySelected(); + + /** + * Returns the query/keyword string that the user entered/selected + * @return Keyword to search + */ + String getQueryText(); + + /** + * Returns the list of Keyword objects that the user entered/selected + * @return + */ + List getQueryList(); + + /** + * Set the number of files that have been indexed + * @param filesIndexed + */ + void setFilesIndexed(int filesIndexed); + + /** + * Performs the search using the selected keywords. + * Creates a DataResultTopComponent with the results. + */ + void search(); } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchQuery.java index a9c57dce36..29a1707a76 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchQuery.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchQuery.java @@ -24,6 +24,11 @@ import java.util.Map; import org.apache.solr.client.solrj.response.TermsResponse.Term; import org.sleuthkit.datamodel.AbstractFile; +/** + * Interface for a search query. Implemented by various + * engines or methods of using the same engine. One of these + * is created for each query. + */ public interface KeywordSearchQuery { /** @@ -51,7 +56,7 @@ public interface KeywordSearchQuery { public void addFilter(KeywordQueryFilter filter); /** - * Set an optional field to narrow down the search + * Set an optional SOLR field to narrow down the search * @param field field to set on the query */ public void setField(String field); @@ -75,13 +80,13 @@ public interface KeywordSearchQuery { public boolean isLiteral(); /** - * return original query string + * return original keyword/query string * @return the query String supplied originally */ public String getQueryString(); /** - * return escaped query string if escaping was done + * return escaped keyword/query string if escaping was done * @return the escaped query string, or original string if no escaping done */ public String getEscapedQueryString(); diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchQueryManager.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchQueryManager.java index 0ceb0cb306..f11838f90e 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchQueryManager.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchQueryManager.java @@ -34,73 +34,99 @@ import org.sleuthkit.autopsy.datamodel.KeyValue; import org.sleuthkit.autopsy.keywordsearch.KeywordSearch.QueryType; /** - * Query manager responsible for running appropriate queries and displaying - * results for single, multi keyword queries, with detailed or collapsed results + * Responsible for running a keyword search query and displaying + * the results. */ public class KeywordSearchQueryManager { + // how to display the results public enum Presentation { - COLLAPSE, DETAIL }; - private List queries; + + private List keywords; private Presentation presentation; private List queryDelegates; private QueryType queryType; private static int resultWindowCount = 0; //keep track of unique window ids to display private static Logger logger = Logger.getLogger(KeywordSearchQueryManager.class.getName()); + /** + * + * @param queries Keywords to search for + * @param presentation Presentation layout + */ public KeywordSearchQueryManager(List queries, Presentation presentation) { - this.queries = queries; + this.keywords = queries; this.presentation = presentation; queryType = QueryType.REGEX; init(); } + /** + * + * @param query Keyword to search for + * @param qt Query type + * @param presentation Presentation Layout + */ public KeywordSearchQueryManager(String query, QueryType qt, Presentation presentation) { - queries = new ArrayList(); - queries.add(new Keyword(query, qt == QueryType.REGEX ? false : true)); + keywords = new ArrayList(); + keywords.add(new Keyword(query, qt == QueryType.REGEX ? false : true)); this.presentation = presentation; queryType = qt; init(); } + /** + * + * @param query Keyword to search for + * @param isLiteral false if reg-exp + * @param presentation Presentation layout + */ public KeywordSearchQueryManager(String query, boolean isLiteral, Presentation presentation) { - queries = new ArrayList(); - queries.add(new Keyword(query, isLiteral)); + keywords = new ArrayList(); + keywords.add(new Keyword(query, isLiteral)); this.presentation = presentation; queryType = isLiteral ? QueryType.WORD : QueryType.REGEX; init(); } + /** + * Initialize internal settings based on constructor arguments. + * Create a list of queries to later run + */ private void init() { queryDelegates = new ArrayList(); - for (Keyword query : queries) { - KeywordSearchQuery del = null; + for (Keyword keyword : keywords) { + KeywordSearchQuery query = null; switch (queryType) { case WORD: - del = new LuceneQuery(query); + query = new LuceneQuery(keyword); break; case REGEX: - if (query.isLiteral()) { - del = new LuceneQuery(query); + if (keyword.isLiteral()) { + query = new LuceneQuery(keyword); } else { - del = new TermComponentQuery(query); + query = new TermComponentQuery(keyword); } break; default: ; } - if (query.isLiteral()) { - del.escape(); + if (query != null) { + if (keyword.isLiteral()) { + query.escape(); + } + queryDelegates.add(query); } - queryDelegates.add(del); } - //escape(); - } + /** + * Execute the keyword search based on keywords passed into constructor. + * Post results into a new DataResultViewer. + */ public void execute() { //execute and present the query //delegate query to query objects and presentation child factories @@ -109,10 +135,11 @@ public class KeywordSearchQueryManager { // q.execute(); // } // } else { + //Collapsed view Collection things = new ArrayList(); int queryID = 0; - StringBuilder queryConcat = new StringBuilder(); + StringBuilder queryConcat = new StringBuilder(); // concatenation of all query strings for (KeywordSearchQuery q : queryDelegates) { Map kvs = new LinkedHashMap(); final String queryStr = q.getQueryString(); @@ -129,7 +156,7 @@ public class KeywordSearchQueryManager { DataResultTopComponent searchResultWin = DataResultTopComponent.createInstance(windowTitle); if (things.size() > 0) { Children childThingNodes = - Children.create(new KeywordSearchResultFactory(queries, things, Presentation.COLLAPSE, searchResultWin), true); + Children.create(new KeywordSearchResultFactory(keywords, things, Presentation.COLLAPSE, searchResultWin), true); rootNode = new AbstractNode(childThingNodes); } else { @@ -144,6 +171,10 @@ public class KeywordSearchQueryManager { // } } + /** + * validate the queries before they are run + * @return false if any are invalid + */ public boolean validate() { boolean allValid = true; for (KeywordSearchQuery tcq : queryDelegates) { diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java index e62d983b1f..a2979b3404 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java @@ -42,11 +42,15 @@ import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskException; +/** + * Performs a normal string (i.e. non-regexp) query to SOLR/Lucene. + * By default, matches in all fields. + */ public class LuceneQuery implements KeywordSearchQuery { private static final Logger logger = Logger.getLogger(LuceneQuery.class.getName()); - private String query; //original unescaped query - private String queryEscaped; + private String keywordString; //original unescaped query + private String keywordStringEscaped; private boolean isEscaped; private Keyword keywordQuery = null; private final List filters = new ArrayList(); @@ -61,14 +65,22 @@ public class LuceneQuery implements KeywordSearchQuery { private static final boolean DEBUG = (Version.getBuildType() == Version.Type.DEVELOPMENT); + /** + * Constructor with query to process. + * @param keywordQuery + */ public LuceneQuery(Keyword keywordQuery) { this(keywordQuery.getQuery()); this.keywordQuery = keywordQuery; } + /** + * Constructor with keyword string to process + * @param queryStr Keyword to search for + */ public LuceneQuery(String queryStr) { - this.query = queryStr; - this.queryEscaped = queryStr; + this.keywordString = queryStr; + this.keywordStringEscaped = queryStr; isEscaped = false; } @@ -84,7 +96,7 @@ public class LuceneQuery implements KeywordSearchQuery { @Override public void escape() { - queryEscaped = KeywordSearchUtil.escapeLuceneQuery(query); + keywordStringEscaped = KeywordSearchUtil.escapeLuceneQuery(keywordString); isEscaped = true; } @@ -100,12 +112,12 @@ public class LuceneQuery implements KeywordSearchQuery { @Override public String getEscapedQueryString() { - return this.queryEscaped; + return this.keywordStringEscaped; } @Override public String getQueryString() { - return this.query; + return this.keywordString; } @Override @@ -117,7 +129,7 @@ public class LuceneQuery implements KeywordSearchQuery { public Map> performQuery() throws NoOpenCoreException { Map> results = new HashMap>(); //in case of single term literal query there is only 1 term - results.put(query, performLuceneQuery()); + results.put(keywordString, performLuceneQuery()); return results; } @@ -125,7 +137,7 @@ public class LuceneQuery implements KeywordSearchQuery { @Override public boolean validate() { - return query != null && !query.equals(""); + return keywordString != null && !keywordString.equals(""); } @Override @@ -192,7 +204,7 @@ public class LuceneQuery implements KeywordSearchQuery { q.setShowDebugInfo(DEBUG); //debug //set query, force quotes/grouping around all literal queries - final String groupedQuery = KeywordSearchUtil.quoteQuery(queryEscaped); + final String groupedQuery = KeywordSearchUtil.quoteQuery(keywordStringEscaped); String theQueryStr = groupedQuery; if (field != null) { //use the optional field @@ -262,10 +274,10 @@ public class LuceneQuery implements KeywordSearchQuery { } catch (NoOpenCoreException ex) { - logger.log(Level.WARNING, "Error executing Lucene Solr Query: " + query, ex); + logger.log(Level.WARNING, "Error executing Lucene Solr Query: " + keywordString, ex); throw ex; } catch (KeywordSearchModuleException ex) { - logger.log(Level.WARNING, "Error executing Lucene Solr Query: " + query, ex); + logger.log(Level.WARNING, "Error executing Lucene Solr Query: " + keywordString, ex); } } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermComponentQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermComponentQuery.java index cffa090d08..9dffbef2ed 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermComponentQuery.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermComponentQuery.java @@ -41,6 +41,10 @@ import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; import org.sleuthkit.datamodel.TskException; + +/** + * Performs a regular expression query to the SOLR/Lucene instance. + */ public class TermComponentQuery implements KeywordSearchQuery { private static final int TERMS_UNLIMITED = -1; From c6a7d47775d912b18cfa3d8c9ad5b9b27db2e2a2 Mon Sep 17 00:00:00 2001 From: Brian Carrier Date: Tue, 13 Aug 2013 15:53:02 -0400 Subject: [PATCH 14/35] Fixed bug where most relevant contentviewer was not being shown --- .../org/sleuthkit/autopsy/corecomponents/DataContentPanel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentPanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentPanel.java index e0778d537f..0bd1229a5f 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentPanel.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentPanel.java @@ -178,7 +178,7 @@ public class DataContentPanel extends javax.swing.JPanel implements DataContent, jTabbedPane1.setEnabledAt(i, true); // remember the viewer with the highest preference value - int currentPreferred = dcv.isPreferred(selectedNode, false); + int currentPreferred = dcv.isPreferred(selectedNode, true); if (currentPreferred > maxPreferred) { preferredViewerIndex = i; maxPreferred = currentPreferred; From f5d4d10aa29548c46c46170a06a6234d61a267ee Mon Sep 17 00:00:00 2001 From: Brian Carrier Date: Tue, 13 Aug 2013 16:39:10 -0400 Subject: [PATCH 15/35] removed some index fields in SOLR schema and added comments to code --- .../release/solr/solr/conf/schema.xml | 52 +++++++++++-------- .../autopsy/keywordsearch/Server.java | 18 +++++-- 2 files changed, 43 insertions(+), 27 deletions(-) diff --git a/KeywordSearch/release/solr/solr/conf/schema.xml b/KeywordSearch/release/solr/solr/conf/schema.xml index ecfb9e15d8..203820992f 100644 --- a/KeywordSearch/release/solr/solr/conf/schema.xml +++ b/KeywordSearch/release/solr/solr/conf/schema.xml @@ -504,37 +504,42 @@ when adding a document. --> + + - - + + + + - - - - - - - - - + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + @@ -545,10 +550,11 @@ - + + - - This page describes the basic concepts and setup that are needed regardless of the module type that you are building. \section mod_dev_setup Basic Setup @@ -66,11 +64,7 @@ You now have a NetBeans module that is using Autopsy as its build platform. Tha There are several optional things in the Properties section. You can add a description and specify the version. You can do all of this later though and it does not need to be done before you start development. A link about the NetBeans versioning scheme can be found here http://wiki.netbeans.org/VersioningPolicy. -Autopsy follows this scheme and we will make a wiki page about it. - -TODO: @@@ Add link to our wiki with Autopsy's versioning scheme - - +Autopsy follows this scheme and a link to the details can be found at http://wiki.sleuthkit.org/index.php?title=Autopsy_3_Module_Versions. \subsection mod_dev_mod_other Other Links From e6fe90719ae98668c10e81d3ac2fc226757715a4 Mon Sep 17 00:00:00 2001 From: Brian Carrier Date: Fri, 16 Aug 2013 22:16:28 -0400 Subject: [PATCH 23/35] Updated doxygen for dataresult viewers --- .../DataResultViewer.java | 5 +- .../AbstractDataResultViewer.java | 3 +- docs/doxygen/modResult.dox | 56 +++++++++++-------- 3 files changed, 38 insertions(+), 26 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponentinterfaces/DataResultViewer.java b/Core/src/org/sleuthkit/autopsy/corecomponentinterfaces/DataResultViewer.java index afc782da06..0ad466151e 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponentinterfaces/DataResultViewer.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponentinterfaces/DataResultViewer.java @@ -27,7 +27,6 @@ import org.openide.nodes.Node; * Interface for the different viewers that show a set of nodes in the DataResult area. * AbstractDataResultViewer has default implementations for the action handlers. * - * @author jantonius */ public interface DataResultViewer { /** @@ -47,7 +46,7 @@ public interface DataResultViewer { public DataResultViewer createInstance(); /** - * Get Component to display this DataResultViewer + * Get the Swing component (i.e. JPanel) for this viewer */ public Component getComponent(); @@ -74,7 +73,7 @@ public interface DataResultViewer { public void setSelectedNodes(Node[] selected); /** - * Checks whether the currently selected node + * Checks whether the currently selected root node * is supported by this viewer * @param selectedNode the selected node * @return True if supported, else false diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/AbstractDataResultViewer.java b/Core/src/org/sleuthkit/autopsy/corecomponents/AbstractDataResultViewer.java index 942bbbd242..dfff6f0e83 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/AbstractDataResultViewer.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/AbstractDataResultViewer.java @@ -36,7 +36,8 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer; import org.sleuthkit.autopsy.coreutils.Logger; /** - * Holds commonalities between all DataResultViewers + * Holds commonalities between all DataResultViewers, such as: + * - Pushes selection to DataContentViewers */ public abstract class AbstractDataResultViewer extends JPanel implements DataResultViewer, Provider { diff --git a/docs/doxygen/modResult.dox b/docs/doxygen/modResult.dox index b2cbae89f8..e4d93648fd 100644 --- a/docs/doxygen/modResult.dox +++ b/docs/doxygen/modResult.dox @@ -1,31 +1,43 @@ /*! \page mod_result_page Developing Result Viewer Modules -NOTE: This has been moved from a package-level description and needs cleanup and updating. +\section result_overview Overview +DataResultViewer modules exist in the upper-right area of the default Autopsy interface, as shown below. -

Creating a DataResultViewer

-

DataResultTopComponent is the high-level window in the DataResult area. The DataResult area is in the upper right of Autopsy and shows a set of nodes (i.e. in table form or thumbnail, by default). You will want to create a new module in this area if you have a new way to display a set of files or nodes. For example, in a graph form or different layout beyond the simple table. +\image html viewer_image.jpg "Module Viewer Areas" -

    -
  1. Create a module from within NetBeans. It must be dependent on these modules: -
      -
    • Case -
    • CoreComponentInterfaces -
    • CoreComponents -
    • DataModel -
    • DialogsAPI (if pop-ups and such are going to be used) -
    • Explorer & Property Sheet API -
    • Lookup -
    • Nodes API -
    • Setting API -
    • UI Utilities API -
    • Utilities API -
    • Window System API -
    +They display a set of files that are passed into the viewer from the tree on the left, keyword searching, or other searches. The main idea is that the same set of files can be viewed in table form, thumbnail form, or any other form that you can think of. Once a file is selected from the DataResult area, it is passed to the DataContent area for display. -
  2. Make a class that extends org.sleuthkit.autopsy.corecomponents.AbstractDataResultViewer and is registered as a service provider for the org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer class by specifying "@ServiceProvider(service = DataResultViewer.class)" or by using layer.xml. This class will extend JPanel.
  3. +\section result_dataflow Data Flow +This section provides some basics on DataResult viewers. DataResult viewers are created as needed. The directory tree on the left creates one when it loads and uses it for the life of the application. The keyword search module creates on each time it performs a keyword search. Data is explicitly passed into it. -
  4. See the previous sections on default actions. (note that this refers to the CoreComponentINterfaces package-level description).
  5. +By default, when a node is selected, it is then passed to the default DataContent viewer. There can be many data content viewers, but only one default one exists. -
+ +\section result_nb NetBeans Module Configuration +The rest of the document assumes that you have already created your NetBeans module, as outlined in \ref mod_dev_module. + +DataResultViewer modules will have additional NetBeans dependencies. Right click on the module, choose "Properties" -> "Libraries" -> "Module Dependencies". Add "Lookup API" and "Nodes API". + +\section result_mod Module Development + +You will need a class that extends org.sleuthkit.autopsy.corecomponents.AbstractDataResultViewer. You can use NetBeans to make a class, manually extend it, and then let NetBeans complain about missing methods. It will provide default implementations for them if you click on the error messages in the UI. +Refer to the documentation in org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer on what each method should do. Note that AbstractDataResultViewer extends JPanel. + +Autopsy will find your module using the NetBeans Lookup infrastructure. To be found, you will need to register as a service provider for DataResultViewer.class by annotating your class as follows: + +\code +@ServiceProvider(service = DataResultViewer.class) +public class DataResultViewerTable extends AbstractDataResultViewer { +\endcode + +If you get errors about not knowing about ServiceProviders and such, ensure that you configured your NetBeans module to depend on the Nodes and Lookup APIs as outlined in the previous section. + +\section result_examples Example Modules +You can refer to the org.sleuthkit.autopsy.corecomponents.DataResultViewerTable and org.sleuthkit.autopsy.corecomponents.DataResultViewerThumbnail modules to follow as examples. + +\section result_hint Hints +Note that these modules are currently the most challenging to develop because they require the most NetBeans knowledge about Nodes and ExplorerManagers. Make sure you read some of the tutorials (or books) first: +- NetBeans Nodes API Tutorial (https://platform.netbeans.org/tutorials/nbm-nodesapi2.html) +- NetBeans Nodes, Explorer Manager, and Component Palette Tutorial (https://platform.netbeans.org/tutorials/nbm-nodesapi3.html) */ From 63374ad0f39a72446953a3376ed78a5f915d693d Mon Sep 17 00:00:00 2001 From: Brian Carrier Date: Sun, 18 Aug 2013 21:58:20 -0400 Subject: [PATCH 24/35] Updated result viewer docs again --- docs/doxygen/modResult.dox | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/doxygen/modResult.dox b/docs/doxygen/modResult.dox index e4d93648fd..0daab56fa6 100644 --- a/docs/doxygen/modResult.dox +++ b/docs/doxygen/modResult.dox @@ -10,8 +10,9 @@ They display a set of files that are passed into the viewer from the tree on the \section result_dataflow Data Flow This section provides some basics on DataResult viewers. DataResult viewers are created as needed. The directory tree on the left creates one when it loads and uses it for the life of the application. The keyword search module creates on each time it performs a keyword search. Data is explicitly passed into it. -By default, when a node is selected, it is then passed to the default DataContent viewer. There can be many data content viewers, but only one default one exists. +By default, when a node is selected, it is then passed to the default DataContent viewer (this is done in org.sleuthkit.autopsy.corecomponents.AbstractDataResultViewer). There can be many data content viewers, but only one default one exists. +The org.sleuthkit.autopsy.corecomponents.DataResultViewerTopComponent class is the NetBeans TopComponent that encapsulates the various DataResult viewer modules. It creates tabs for each DataResult viewer module. \section result_nb NetBeans Module Configuration The rest of the document assumes that you have already created your NetBeans module, as outlined in \ref mod_dev_module. @@ -32,12 +33,19 @@ public class DataResultViewerTable extends AbstractDataResultViewer { If you get errors about not knowing about ServiceProviders and such, ensure that you configured your NetBeans module to depend on the Nodes and Lookup APIs as outlined in the previous section. +The current modules in this viewer area heavily use the NetBeans ExplorerManger and Node concepts. You do not need to use ExplorerManager concepts, but you will need to use Node concepts to identify the set of nodes to display and to extract the datamodel objects from each Node object. Refer to \ref content_hints_objects for hints on getting the datamodel objects from the Node. + \section result_examples Example Modules You can refer to the org.sleuthkit.autopsy.corecomponents.DataResultViewerTable and org.sleuthkit.autopsy.corecomponents.DataResultViewerThumbnail modules to follow as examples. \section result_hint Hints -Note that these modules are currently the most challenging to develop because they require the most NetBeans knowledge about Nodes and ExplorerManagers. Make sure you read some of the tutorials (or books) first: + +Note that we have made the least number of these types of modules, so some work could be done to make the framework and infrastuture for them better. + +These modules are currently the most challenging to develop because they require the most NetBeans knowledge about Nodes and ExplorerManagers. Make sure you read some of the tutorials (or books) first: - NetBeans Nodes API Tutorial (https://platform.netbeans.org/tutorials/nbm-nodesapi2.html) - NetBeans Nodes, Explorer Manager, and Component Palette Tutorial (https://platform.netbeans.org/tutorials/nbm-nodesapi3.html) +We have plans to change the design a bit in the future so that an ExplorerManager is created for each DataResultViewerTopComponent instance and each individual module does not need to make one. Instead, one will be given to it. + */ From 917398c59140715c8ee7211754e4a6a2e2fddf3a Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Mon, 19 Aug 2013 18:30:00 -0400 Subject: [PATCH 25/35] Changed access specifier of Case.closeCase() method --- Core/src/org/sleuthkit/autopsy/casemodule/Case.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 5dee9feb54..4f0545e494 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -372,7 +372,7 @@ public class Case implements SleuthkitCase.ErrorObserver { /** * Closes this case. This methods close the xml and clear all the fields. */ - void closeCase() throws CaseActionException { + public void closeCase() throws CaseActionException { changeCase(null); try { From ce306636a5c70a37ada7232c9ff17bd590211cdb Mon Sep 17 00:00:00 2001 From: Brian Carrier Date: Tue, 20 Aug 2013 13:16:24 -0400 Subject: [PATCH 26/35] fixed bug where images could not be resized and exceptions occured --- .../autopsy/corecomponents/MediaViewImagePanel.java | 4 ++++ .../autopsy/corecomponents/ThumbnailViewNode.java | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/MediaViewImagePanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/MediaViewImagePanel.java index 831cdb652e..8589b0ea7d 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/MediaViewImagePanel.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/MediaViewImagePanel.java @@ -144,6 +144,10 @@ public class MediaViewImagePanel extends javax.swing.JPanel { try { //original input stream BufferedImage bi = ImageIO.read(inputStream); + if (bi == null) { + logger.log(Level.WARNING, "Could image reader not found for file: " + fileName); + return; + } //scale image using Scalr BufferedImage biScaled = ScalrWrapper.resizeHighQuality(bi, (int) dims.getWidth(), (int) dims.getHeight()); //convert from awt imageto fx image diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/ThumbnailViewNode.java b/Core/src/org/sleuthkit/autopsy/corecomponents/ThumbnailViewNode.java index 92ee76109f..ef562ae0c4 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/ThumbnailViewNode.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/ThumbnailViewNode.java @@ -86,6 +86,9 @@ class ThumbnailViewNode extends FilterNode { if (getFile(content.getId()).exists()) { try { icon = ImageIO.read(getFile(content.getId())); + if (icon == null) { + icon = ThumbnailViewNode.defaultIcon; + } } catch (IOException ex) { icon = ThumbnailViewNode.defaultIcon; } @@ -120,6 +123,10 @@ class ThumbnailViewNode extends FilterNode { try { inputStream = new ReadContentInputStream(content); BufferedImage bi = ImageIO.read(inputStream); + if (bi == null) { + logger.log(Level.WARNING, "No image reader for file: " + content.getName()); + return null; + } BufferedImage biScaled = ScalrWrapper.resizeFast(bi, 100, 100); return biScaled; }catch (OutOfMemoryError e) { From 7ab52993a2cd6a6b238bfb5e93373f6b89a4299d Mon Sep 17 00:00:00 2001 From: Brian Carrier Date: Tue, 20 Aug 2013 13:35:38 -0400 Subject: [PATCH 27/35] removed call to update_version.py from ant and changed variable names --- build-windows.xml | 111 ++++++++++++++++++++++++---------------------- build.xml | 5 ++- 2 files changed, 63 insertions(+), 53 deletions(-) diff --git a/build-windows.xml b/build-windows.xml index bbc4e0e098..b5db6ae85a 100644 --- a/build-windows.xml +++ b/build-windows.xml @@ -54,19 +54,20 @@ - C:\Program Files (x86)\Caphyon\Advanced Installer 10.2\bin\x86\AdvancedInstaller.com + C:\Program Files (x86)\Caphyon\Advanced Installer 10.3\bin\x86\AdvancedInstaller.com - - + - + + @@ -78,87 +79,93 @@ - + - Product Code: ${guid1} - Product Code: ${guid1} + Product Version: ${app.version} + + + - Product Version: ${app.version} - + replace="ProductVersion" Value="${app.version}" /> + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + + diff --git a/build.xml b/build.xml index cbbefbee0b..8cd8a0d109 100644 --- a/build.xml +++ b/build.xml @@ -87,6 +87,7 @@ + @@ -230,8 +231,9 @@ - + + @@ -239,6 +241,7 @@ + From 1e67510c8a644541b6003cfd3ba23d2a48fdeebf Mon Sep 17 00:00:00 2001 From: raman-bt Date: Tue, 20 Aug 2013 14:09:59 -0400 Subject: [PATCH 28/35] i. Changed access specifier on casemodule.Case.addImage() to public, so other modules can access it to display a newly added image in the UI. ii. Fixed the names of icons in HTML report to go with the recent renaming of some Web artifacts. --- Core/src/org/sleuthkit/autopsy/casemodule/Case.java | 2 +- Core/src/org/sleuthkit/autopsy/report/ReportHTML.java | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 5dee9feb54..219cffb030 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -326,7 +326,7 @@ public class Case implements SleuthkitCase.ErrorObserver { * @param imgId the ID of the image that being added * @param timeZone the timeZone of the image where it's added */ - Image addImage(String imgPath, long imgId, String timeZone) throws CaseActionException { + public Image addImage(String imgPath, long imgId, String timeZone) throws CaseActionException { logger.log(Level.INFO, "Adding image to Case. imgPath: {0} ID: {1} TimeZone: {2}", new Object[]{imgPath, imgId, timeZone}); try { diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java b/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java index 60bb186c0a..d16511cc9d 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java +++ b/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java @@ -669,13 +669,13 @@ public class ReportHTML implements TableReportModule { output.close(); in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/bookmarks.png"); - output = new FileOutputStream(new File(path + File.separator + "Bookmarks.png")); + output = new FileOutputStream(new File(path + File.separator + "Web Bookmarks.png")); FileUtil.copy(in, output); in.close(); output.close(); in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/cookies.png"); - output = new FileOutputStream(new File(path + File.separator + "Cookies.png")); + output = new FileOutputStream(new File(path + File.separator + "Web Cookies.png")); FileUtil.copy(in, output); in.close(); output.close(); @@ -687,13 +687,13 @@ public class ReportHTML implements TableReportModule { output.close(); in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/downloads.png"); - output = new FileOutputStream(new File(path + File.separator + "Downloads.png")); + output = new FileOutputStream(new File(path + File.separator + "Web Downloads.png")); FileUtil.copy(in, output); in.close(); output.close(); in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/search.png"); - output = new FileOutputStream(new File(path + File.separator + "Web Search Engine Queries.png")); + output = new FileOutputStream(new File(path + File.separator + "Web Search.png")); FileUtil.copy(in, output); in.close(); output.close(); From 93e6abe5a672909e0d4be21a8293264a61b32e4e Mon Sep 17 00:00:00 2001 From: Brian Carrier Date: Wed, 21 Aug 2013 12:23:20 -0400 Subject: [PATCH 29/35] Updated build files for installer --- build-windows.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build-windows.xml b/build-windows.xml index b5db6ae85a..487da5568a 100644 --- a/build-windows.xml +++ b/build-windows.xml @@ -67,8 +67,8 @@ - - + + Product Code: ${guid1} Product Version: ${app.version} - + From 0cb46643e51aceea58f1affbab62ea7c20730ce5 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Wed, 21 Aug 2013 13:52:58 -0400 Subject: [PATCH 30/35] Added 24x24 version of btn_icon_create_new_case.png --- .../casemodule/btn_icon_create_new_case-24.png | Bin 0 -> 978 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100755 Core/src/org/sleuthkit/autopsy/casemodule/btn_icon_create_new_case-24.png diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/btn_icon_create_new_case-24.png b/Core/src/org/sleuthkit/autopsy/casemodule/btn_icon_create_new_case-24.png new file mode 100755 index 0000000000000000000000000000000000000000..b0e3e593c23b164007e307ac287a6a80bb82622d GIT binary patch literal 978 zcmV;@11+9>^t*op>WhzDrBeZp03W$&VIGDwDjTX>MA#zO|)9Aa$ooM_HOoiJ#k%EJkJx~_azKN3Byq0 zI38t^B$6bF37y!`pt42m630?(SMR;w^g z6S}TL*L5&6?Bn>Lbc>3z%Pcvy9n3Hc@R=P!HN+y9%BG0e%M1r$`{>@#f zMgJuxkum7NrmT;KbqM|p9<$B$b+9n#>@yKA4lF(r0Ed0c@Y{gP*5TW5+5pdU}!U_Xda*v)GBib6CXi5q&QYw95R)8U3@x}C|Je}D!73Di-(xG zTE$B~5J!x;>j{ELVQ`o{-@f~Jqe!5N?@Do#jui-MW*u|Z>v-DR!9YF3!nJuYYjC^+ zBpyYb2m9aL`F`gw0HAiejWkW`wr$_EEbESK+rtww0U!m7)G;Rk!nD2H!tTLOB!)vo zAc?D_dsm)ZjNX0c@(qeAm5M{{cAEgqr)jzn1i=!3OGH#BA`^g3M3tO{^%If+z) Date: Wed, 21 Aug 2013 13:59:54 -0400 Subject: [PATCH 31/35] Added improved version of btn_icon_create_new_case-24.png --- .../casemodule/btn_icon_create_new_case-24.png | Bin 978 -> 1144 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/btn_icon_create_new_case-24.png b/Core/src/org/sleuthkit/autopsy/casemodule/btn_icon_create_new_case-24.png index b0e3e593c23b164007e307ac287a6a80bb82622d..cdc98334cc921098d6b59b5339490ae78bc38bbe 100755 GIT binary patch delta 1074 zcmV-21kL->2lxn(Rev!s$fLJp000C9NklR?|?4 zX}W3JB-?IGcXRiC%#07|hTH9C+XDkPdYKS{ zR;xv$(MYy#-Ku+iD_0#UpO;dGQp(VAoCg}N>wa+f@L|~tK(!s;_dU<^D3wa9rxZdU zr9=p^y4JQRiio0UujhHKBS((x-??*V)RTeD=+YUyJ+gaRYs%$vp|7uR-_X#|$eulW zWKV#MRCkRwP=B|fwI&DxcVJ*(-^j?w`+(>P&>5_gMJFRESAGwIAU`-b_{HVRmp=kH zd-klnCkJhXx&_E;pZ%uMXb^^Bd#&jE{wL?oom)3NJpA!J46=cH1#cgwP$=Me9!e>c zQb;NBSL~gxRg{$|JMZSVbLG13UF@~ixUTE;Omb({tbfI#+kUpByE0g-YrC1&2}{fK zgwYbIj>)Sco?BkK(H_LCwFn_ts9#}x`Z%RxK*24d97VkuvfOGCMf#zh65Wr@Y;A%Q z7rx=K!HonJO`7|c+l(QL3#?yXp;Ga4-~IgF;{HP)ZXnwjlarI%f*|;z*xq_W-cR>-g`_&+!sIcw zJpCN?)&z-;u@Nv{b z(xqwARI_o@0C`v8=2MJKcH9GCj9Kl2?Db)cp_$x7njA_wjGn&4?6sRHr6{;A$Bv#R zNmAb2wVg^X0DYTO$IhA(8Do%AuFf5qCrt#YPKeVMyWV~mFIVQ`_+K3S=^&qfx)+^V zs(dU8-%l%0VBVe1zKERVsZ&j9Fo}`H3eP-u*hY_zwgUO$xyEYCUPS;;*u0 zYC2!D&PsaunYXA_p;lWWF$sxDXrwooO@B|48ckvnVjXjRW|5hx`o3QdU7QpEr%s&; zeBa+zDwU4vLZG z_LY}jtBTRlQ30&iT5k%&a0uuZLIgqx5AcN$xr~GXU>qfwnjPCdA5K&n@hq`vP%BjZ sR0@yt=(;UmZs>pN`zVTPN;$Lt1K}&>NK-q-AOHXW07*qoM6N<$f-Nf=r~m)} delta 907 zcmV;619bfO2+{|TRevo8;ckDR000ADNklfe2-kI!Mx&7e zK&#b4v)LT0<+9dGtgJ+3Dn<$<=SvYh%luuMrkI|duFlTRezml;^x^92DmR-= zv|6olU-$O*Zh!WAJ#k%EJkJx~_azKN3Byq0I38t^B$6bF37y!`pt42m630?(SMR;w^g6S}TL*MD^|GwkE|pmd9hvdb(vwH?eb z4C9pgG)-X`hE}>oWlAQ2Pa@B+^hAQQfd0*0sYU-KCXq4z$0&Ql^ht&1kw2Q*NuLVm zB*=rGMaIksBOgcJQ>5t-ws{d6Pn}JWmze>VkuWj->_3DPJch6jKyVb8IOs={XCNYD zS$4((ud_4EM z@aYlA2(a~F2S4Av2hl>LQ~lbNtCv51|Fdf|@4fl;WQ&wiVCHcGFpk_l4*f?!Eyn%r zKk(ab55u8CoFsT-`Bk8jTw1(-<85H~N4-oz_J3q#=TkLMP$CEtA1ZcWXfvp29-tD` zDsu=EA3;2%I96O7GL?B Date: Wed, 21 Aug 2013 15:35:12 -0400 Subject: [PATCH 32/35] Possible solution for IllegalStateException due to redundant associateLookup() calls --- .../corecomponents/DataResultTopComponent.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultTopComponent.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultTopComponent.java index f2a70a7e51..e9cf34eb77 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultTopComponent.java @@ -55,6 +55,7 @@ public class DataResultTopComponent extends TopComponent implements DataResult { private static final Logger logger = Logger.getLogger(DataResultTopComponent.class.getName()); private DataResultPanel dataResultPanel; //embedded component with all the logic private boolean isMain; + private boolean lookupSet = false; private String customModeName; //keep track of tcs openeded for menu presenters @@ -265,13 +266,17 @@ public class DataResultTopComponent extends TopComponent implements DataResult { /* @@@ Short-term hack to associate lookup with the table view so that we can do multi-select. * Longer-term solution is to use same explorer Manager for all viewers. */ - List resultViewers = this.dataResultPanel.getViewers(); - for (DataResultViewer viewer : resultViewers) { - if (viewer instanceof DataResultViewerTable) { - associateLookup(ExplorerUtils.createLookup(((DataResultViewerTable)viewer).getExplorerManager(), getActionMap())); - break; + if (!lookupSet) { + List resultViewers = this.dataResultPanel.getViewers(); + for (DataResultViewer viewer : resultViewers) { + if (viewer instanceof DataResultViewerTable) { + associateLookup(ExplorerUtils.createLookup(((DataResultViewerTable)viewer).getExplorerManager(), getActionMap())); + break; + } } - } + + lookupSet = true; + } } From 767e2dd6c4c74f6d1b9bcb78ea1d299d076d463a Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Wed, 21 Aug 2013 16:00:34 -0400 Subject: [PATCH 33/35] Eliminated redundant calls to assocaiteLookup() in DataResultTopComponent --- .../autopsy/corecomponents/DataResultTopComponent.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultTopComponent.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultTopComponent.java index e9cf34eb77..61560f9e99 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultTopComponent.java @@ -266,7 +266,7 @@ public class DataResultTopComponent extends TopComponent implements DataResult { /* @@@ Short-term hack to associate lookup with the table view so that we can do multi-select. * Longer-term solution is to use same explorer Manager for all viewers. */ - if (!lookupSet) { + if (!this.lookupSet) { List resultViewers = this.dataResultPanel.getViewers(); for (DataResultViewer viewer : resultViewers) { if (viewer instanceof DataResultViewerTable) { @@ -275,7 +275,7 @@ public class DataResultTopComponent extends TopComponent implements DataResult { } } - lookupSet = true; + this.lookupSet = true; } } From 96c5ba07799e974f065a6da557cfe23e061a39fe Mon Sep 17 00:00:00 2001 From: raman-bt Date: Wed, 21 Aug 2013 16:08:38 -0400 Subject: [PATCH 34/35] Make HTML report more robust with artifact names in the HTML and icon files. --- .../sleuthkit/autopsy/report/ReportHTML.java | 305 +++++++++--------- .../sleuthkit/autopsy/report/images/star.png | Bin 0 -> 674 bytes 2 files changed, 160 insertions(+), 145 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/report/images/star.png diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java b/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java index d16511cc9d..e535d645ec 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java +++ b/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java @@ -105,7 +105,160 @@ public class ReportHTML implements TableReportModule { } out = null; } + + /** + * Generate a file name for the given datatype, by replacing any + * undesirable chars, like /, or spaces + * @param dataType data type for which to generate a file name + */ + private String dataTypeToFileName(String dataType) { + + String fileName = org.sleuthkit.autopsy.coreutils.FileUtil.escapeFileName(dataType); + // replace all ' ' with '_' + fileName = fileName.replaceAll(" ", "_"); + + return fileName; + } + + /** + * Copies a suitable icon for the given data type in the output directory and + * returns the icon file name to use for the given data type. + */ + private String useDataTypeIcon(String dataType) + { + String iconFilePath; + String iconFileName; + InputStream in = null; + OutputStream output = null; + + logger.log(Level.INFO, "useDataTypeIcon: dataType = " + dataType); + + // find the artifact with matching display name + BlackboardArtifact.ARTIFACT_TYPE artifactType = null; + for (ARTIFACT_TYPE v : ARTIFACT_TYPE.values()) { + if (v.getDisplayName().equals(dataType)) { + artifactType = v; + } + } + + if (null != artifactType) + { + // set the icon file name + iconFileName = dataTypeToFileName(artifactType.getDisplayName()) + ".png"; + iconFilePath = path + File.separator + iconFileName; + + // determine the source image to use + switch (artifactType) { + case TSK_WEB_BOOKMARK: + in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/bookmarks.png"); + break; + case TSK_WEB_COOKIE: + in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/cookies.png"); + break; + case TSK_WEB_HISTORY: + in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/history.png"); + break; + case TSK_WEB_DOWNLOAD: + in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/downloads.png"); + break; + case TSK_RECENT_OBJECT: + in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/recent.png"); + break; + case TSK_INSTALLED_PROG: + in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/installed.png"); + break; + case TSK_KEYWORD_HIT: + in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/keywords.png"); + break; + case TSK_HASHSET_HIT: + in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/hash.png"); + break; + case TSK_DEVICE_ATTACHED: + in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/devices.png"); + break; + case TSK_WEB_SEARCH_QUERY: + in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/search.png"); + break; + case TSK_METADATA_EXIF: + in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/exif.png"); + break; + case TSK_TAG_FILE: + in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/userbookmarks.png"); + break; + case TSK_TAG_ARTIFACT: + in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/userbookmarks.png"); + break; + case TSK_SERVICE_ACCOUNT: + in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/account-icon-16.png"); + break; + case TSK_CONTACT: + in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/contact.png"); + break; + case TSK_MESSAGE: + in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/message.png"); + break; + case TSK_CALLLOG: + in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/calllog.png"); + break; + case TSK_CALENDAR_ENTRY: + in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/calendar.png"); + break; + case TSK_SPEED_DIAL_ENTRY: + in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/speeddialentry.png"); + break; + case TSK_BLUETOOTH_PAIRING: + in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/bluetooth.png"); + break; + case TSK_GPS_BOOKMARK: + in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/gpsfav.png"); + break; + case TSK_GPS_LAST_KNOWN_LOCATION: + in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/gps-lastlocation.png"); + break; + case TSK_GPS_SEARCH: + in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/gps-search.png"); + break; + + default: + logger.log(Level.WARNING, "useDataTypeIcon: unhandled artifact type = " + dataType); + in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/star.png"); + iconFileName = "star.png"; + iconFilePath = path + File.separator + iconFileName; + break; + } + } + else { // no defined artifact found for this dataType + logger.log(Level.WARNING, "useDataTypeIcon: no artifact found for data type = " + dataType); + in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/star.png"); + iconFileName = "star.png"; + iconFilePath = path + File.separator + iconFileName; + } + + try { + output = new FileOutputStream(iconFilePath); + FileUtil.copy(in, output); + in.close(); + output.close(); + } catch (IOException ex) { + logger.log(Level.SEVERE, "Failed to extract images for HTML report.", ex); + } finally { + if (output != null) { + try { + output.flush(); + output.close(); + } catch (IOException ex) { + } + } if (in != null) { + try { + in.close(); + } catch (IOException ex) { + } + } + } + + return iconFileName; + } /** * Start this report by setting the path, refreshing member variables, * and writing the skeleton for the HTML report. @@ -151,7 +304,7 @@ public class ReportHTML implements TableReportModule { */ @Override public void startDataType(String title) { - String fTitle = org.sleuthkit.autopsy.coreutils.FileUtil.escapeFileName(title); + String fTitle = dataTypeToFileName(title); // Make a new out for this page try { //escape out slashes tha that appear in title @@ -186,7 +339,7 @@ public class ReportHTML implements TableReportModule { * @param comment Comment on the data type, may be the empty string */ public void startDataType(String name, String comment) { - String title = org.sleuthkit.autopsy.coreutils.FileUtil.escapeFileName(name); + String title = dataTypeToFileName(name); try { out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(path + title + getExtension()), "UTF-8")); } catch (FileNotFoundException ex) { @@ -614,9 +767,10 @@ public class ReportHTML implements TableReportModule { nav.append("
  • Case Summary
  • \n"); for (String dataType : dataTypes.keySet()) { - String dataTypeEsc = org.sleuthkit.autopsy.coreutils.FileUtil.escapeFileName(dataType); - nav.append("
  • ") .append(dataType).append(" (").append(dataTypes.get(dataType)) .append(")
  • \n"); @@ -668,146 +822,7 @@ public class ReportHTML implements TableReportModule { in.close(); output.close(); - in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/bookmarks.png"); - output = new FileOutputStream(new File(path + File.separator + "Web Bookmarks.png")); - FileUtil.copy(in, output); - in.close(); - output.close(); - - in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/cookies.png"); - output = new FileOutputStream(new File(path + File.separator + "Web Cookies.png")); - FileUtil.copy(in, output); - in.close(); - output.close(); - - in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/history.png"); - output = new FileOutputStream(new File(path + File.separator + "Web History.png")); - FileUtil.copy(in, output); - in.close(); - output.close(); - - in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/downloads.png"); - output = new FileOutputStream(new File(path + File.separator + "Web Downloads.png")); - FileUtil.copy(in, output); - in.close(); - output.close(); - - in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/search.png"); - output = new FileOutputStream(new File(path + File.separator + "Web Search.png")); - FileUtil.copy(in, output); - in.close(); - output.close(); - - in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/recent.png"); - output = new FileOutputStream(new File(path + File.separator + "Recent Documents.png")); - FileUtil.copy(in, output); - in.close(); - output.close(); - - in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/installed.png"); - output = new FileOutputStream(new File(path + File.separator + "Installed Programs.png")); - FileUtil.copy(in, output); - in.close(); - output.close(); - - in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/keywords.png"); - output = new FileOutputStream(new File(path + File.separator + "Keyword Hits.png")); - FileUtil.copy(in, output); - in.close(); - output.close(); - - in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/devices.png"); - output = new FileOutputStream(new File(path + File.separator + "Devices Attached.png")); - FileUtil.copy(in, output); - in.close(); - output.close(); - - in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/exif.png"); - output = new FileOutputStream(new File(path + File.separator + "EXIF Metadata.png")); - FileUtil.copy(in, output); - in.close(); - output.close(); - - in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/userbookmarks.png"); - output = new FileOutputStream(new File(path + File.separator + "File Tags.png")); - FileUtil.copy(in, output); - in.close(); - output.close(); - - in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/userbookmarks.png"); - output = new FileOutputStream(new File(path + File.separator + "Result Tags.png")); - FileUtil.copy(in, output); - in.close(); - output.close(); - - in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/hash.png"); - output = new FileOutputStream(new File(path + File.separator + "Hashset Hits.png")); - FileUtil.copy(in, output); - in.close(); - output.close(); - - in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/contact.png"); - output = new FileOutputStream(new File(path + File.separator + "Contacts.png")); - FileUtil.copy(in, output); - in.close(); - output.close(); - - in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/message.png"); - output = new FileOutputStream(new File(path + File.separator + "Messages.png")); - FileUtil.copy(in, output); - in.close(); - output.close(); - - in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/calllog.png"); - output = new FileOutputStream(new File(path + File.separator + "Call Logs.png")); - FileUtil.copy(in, output); - in.close(); - output.close(); - - in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/calendar.png"); - output = new FileOutputStream(new File(path + File.separator + "Calendar Entries.png")); - FileUtil.copy(in, output); - in.close(); - output.close(); - - in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/speeddialentry.png"); - output = new FileOutputStream(new File(path + File.separator + "Speed Dial Entries.png")); - FileUtil.copy(in, output); - in.close(); - output.close(); - - in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/bluetooth.png"); - output = new FileOutputStream(new File(path + File.separator + "BlueTooth.png")); - FileUtil.copy(in, output); - in.close(); - output.close(); - - in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/gpsfav.png"); - output = new FileOutputStream(new File(path + File.separator + "GPS Bookmarks.png")); - FileUtil.copy(in, output); - in.close(); - output.close(); - - in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/gps-lastlocation.png"); - output = new FileOutputStream(new File(path + File.separator + "GPS Last Location.png")); - FileUtil.copy(in, output); - in.close(); - output.close(); - - in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/gps-search.png"); - output = new FileOutputStream(new File(path + File.separator + "GPS Search.png")); - FileUtil.copy(in, output); - in.close(); - output.close(); - - in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/account-icon-16.png"); - output = new FileOutputStream(new File(path + File.separator + "Accounts.png")); - FileUtil.copy(in, output); - in.close(); - output.close(); - - - + } catch (IOException ex) { logger.log(Level.SEVERE, "Failed to extract images for HTML report.", ex); } finally { diff --git a/Core/src/org/sleuthkit/autopsy/report/images/star.png b/Core/src/org/sleuthkit/autopsy/report/images/star.png new file mode 100644 index 0000000000000000000000000000000000000000..10169c061744c72365204043e0f63ebc977a51d9 GIT binary patch literal 674 zcmV;T0$u%yP)5e-c z#Lx-yxQ-z7fcPV&3QvD^oF}I-Q0@TbE7qJUZ|N0xS!~;NdkV)(wP;^YquBO4}>^yoC>eOAE)=y$my^Tn{1B!F> zOfivT7x#1A4R&4U1nbb@0@U1+%lPjh`BRsG@;t9{hQZU+M>q|obeHI_xkSr5JqDf@+7x0eVf Date: Wed, 21 Aug 2013 16:26:34 -0400 Subject: [PATCH 35/35] Removed 24x24 version of 'New Case' icon --- .../casemodule/btn_icon_create_new_case-24.png | Bin 1144 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100755 Core/src/org/sleuthkit/autopsy/casemodule/btn_icon_create_new_case-24.png diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/btn_icon_create_new_case-24.png b/Core/src/org/sleuthkit/autopsy/casemodule/btn_icon_create_new_case-24.png deleted file mode 100755 index cdc98334cc921098d6b59b5339490ae78bc38bbe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1144 zcmV-;1c&>HP)@7~>e*{+hrN@FWhH561*^+l>s5$sFx*$*t_MJo8E@}Y1$;)ZcKM`_kPTb59x;6?Pl8p17~LL zJ^%Ut&p9)KJ2o~pCS2Ebi^U?=T6&off>x_VqtQsVZr!STeJfWTDW8{8hEmGVahwMl zuIqkq`0!!b3qZ9U-}gPw^C*={tEUt~Af-eIvAWi_D2j-pXs_pats_T{?BBU_XVjB{ z&FIn@ygjmeT5HPXa-pxUZ{N_+(8!)Wdt^_5j8u1xHc+>rwI&DxcVJ*(-^j?w`+(>P z&>5_gMJFRESAGwIAU`-b_{HVRmp=kHd-klnCkJhXx&_E;pZ%uMXb^^Bd#&jE{wL?o zom)3NJpA!J46=cH1#cgwP$=Me9!e>cQb;NBSL~gxRg{$|JMZSVbLG13UF@~ixUTE; zOmb({ti__+ezv5$GFYo?yP4JrOUv_w(GsbS$*UrsTVA`-9>lD*2q9RgUtxUuIHh7h z!7ZX3MZFoa+-ed<`k|f@-H**|ZGsaQzTvUKjRX}v_ zM4B8*IgFmZ#O$@3D5WU4F2|0ZCP`A>+_jxbE&zR-RL9Pm5*cHVQm)P&nI}yIsZNN~ z7Q5bl7cW=l;`m=2{OKT{f4Uc)TB`LkBzl=tr$k9oTa$s5a@7YYx$Ce|3C$wbTCUd5 z0G4Zu3+NOk=8lu51}PLmK&xp9o5_JJfXL>dU8-%l%0VBVe1zKERVsZ&j9Fo}`H3eP z-u*hY_zwgUO$xyEYCUPS;;*u0YC2!D&PsaunYXA_p;lWWF$sxDXrwooO;3^