From 58cdd58f988c751dced38653ad13335ed4cdbbbb Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Thu, 1 Dec 2016 14:51:29 -0500 Subject: [PATCH 01/16] 1917 cleaned up access levels and made EmptyNode more general purpose --- .../datamodel/AbstractContentChildren.java | 2 +- .../datamodel/DisplayableItemNodeVisitor.java | 5 +- .../autopsy/datamodel/EmptyNode.java | 86 +++ .../autopsy/datamodel/FileTypeByExtNode.java | 8 +- .../autopsy/datamodel/FileTypes.java | 22 +- .../autopsy/datamodel/FileTypesByExtNode.java | 8 +- .../datamodel/FileTypesByMimeType.java | 682 ++++++++---------- .../DirectoryTreeTopComponent.java | 11 +- 8 files changed, 418 insertions(+), 406 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/datamodel/EmptyNode.java diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java index 85e405d27c..a9aa3a7280 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java @@ -220,7 +220,7 @@ abstract class AbstractContentChildren extends Keys { @Override public AbstractNode visit(FileTypesByMimeType ftByMimeTypeItem) { - return ftByMimeTypeItem.new FileTypesByMimeTypeNode(ftByMimeTypeItem.getSleuthkitCase()); + return ftByMimeTypeItem.new FileTypesByMimeTypeNode(); } } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java b/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java index f7fdb96243..6d18d977ca 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java @@ -152,7 +152,8 @@ public interface DisplayableItemNodeVisitor { T visit(FileTypesByMimeType.MediaSubTypeNode ftByMimeTypeMediaSubType); - T visit(FileTypesByMimeType.EmptyNode.MessageNode aThis); + T visit(EmptyNode.MessageNode emptyNode); + /** @@ -233,7 +234,7 @@ public interface DisplayableItemNodeVisitor { } @Override - public T visit(FileTypesByMimeType.EmptyNode.MessageNode ftByMimeTypeEmptyNode) { + public T visit(EmptyNode.MessageNode ftByMimeTypeEmptyNode) { return defaultVisit(ftByMimeTypeEmptyNode); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/EmptyNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/EmptyNode.java new file mode 100644 index 0000000000..d77949e59f --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datamodel/EmptyNode.java @@ -0,0 +1,86 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.sleuthkit.autopsy.datamodel; + +import java.util.List; +import org.openide.nodes.AbstractNode; +import org.openide.nodes.ChildFactory; +import org.openide.nodes.Children; +import org.openide.nodes.Node; + +/** + * EmptyNode Class made for edge case where no mime exist in the database yet. + * Creates a node to display information on why the tree is empty. + * + * Swapped for the FileTypesByMimeType node in + * DirectoryTreeTopComponent.respondSelection + */ +public final class EmptyNode extends AbstractNode { + + public EmptyNode(String displayedMessage) { + super(Children.create(new EmptyChildFactory(displayedMessage), true)); + + } + + public static boolean isEmptyMimeTypeNode(Node originNode) { + boolean isEmptyMimeNode = false; + if (originNode instanceof FileTypesByMimeType.FileTypesByMimeTypeNode && ((FileTypesByMimeType.FileTypesByMimeTypeNode) originNode).isEmpty()) { + isEmptyMimeNode = true; + } + return isEmptyMimeNode; + } + + static class EmptyChildFactory extends ChildFactory { + + String fileIdMsg; //NON-NLS + + private EmptyChildFactory(String displayedMessage) { + fileIdMsg = displayedMessage; + } + + @Override + protected boolean createKeys(List list) { + list.add(fileIdMsg); + return true; + } + + @Override + protected Node createNodeForKey(String key) { + return new MessageNode(key); + } + + } + + /** + * MessageNode is is the info message that displays in the table view, by + * also extending a DisplayableItemNode type, rather than an AbstractNode + * type it doesn't throw an error when right clicked. + */ + static class MessageNode extends DisplayableItemNode { + + MessageNode(String name) { + super(Children.LEAF); + super.setName(name); + setName(name); + setDisplayName(name); + } + + @Override + public boolean isLeafTypeNode() { + return true; + } + + @Override + public T accept(DisplayableItemNodeVisitor v) { + return v.visit(this); + } + + @Override + public String getItemType() { + return getClass().getName(); + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypeByExtNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypeByExtNode.java index 6d94b02035..06a962a0c1 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypeByExtNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypeByExtNode.java @@ -48,7 +48,7 @@ import org.sleuthkit.datamodel.TskData; * Node for a specific file type / extension. Children of it will be the files * of that type. */ -public class FileTypeByExtNode extends DisplayableItemNode { +class FileTypeByExtNode extends DisplayableItemNode { FileTypeExtensionFilters.SearchFilterInterface filter; SleuthkitCase skCase; @@ -146,7 +146,7 @@ public class FileTypeByExtNode extends DisplayableItemNode { /** * Child node factory for a specific file type - does the database query. */ - public static class FileTypeChildFactory extends ChildFactory.Detachable { + private static class FileTypeChildFactory extends ChildFactory.Detachable { private final SleuthkitCase skCase; private final FileTypeExtensionFilters.SearchFilterInterface filter; @@ -169,7 +169,7 @@ public class FileTypeByExtNode extends DisplayableItemNode { * @param o Observable that will notify when there could be new * data to display */ - FileTypeChildFactory(FileTypeExtensionFilters.SearchFilterInterface filter, SleuthkitCase skCase, Observable o) { + private FileTypeChildFactory(FileTypeExtensionFilters.SearchFilterInterface filter, SleuthkitCase skCase, Observable o) { super(); this.filter = filter; this.skCase = skCase; @@ -205,7 +205,7 @@ public class FileTypeByExtNode extends DisplayableItemNode { * * @return */ - static long calculateItems(SleuthkitCase sleuthkitCase, FileTypeExtensionFilters.SearchFilterInterface filter) { + private static long calculateItems(SleuthkitCase sleuthkitCase, FileTypeExtensionFilters.SearchFilterInterface filter) { try { return sleuthkitCase.countFilesWhere(createQuery(filter)); } catch (TskCoreException ex) { diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java index 54a4c792c8..12a8b2b12c 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java @@ -28,7 +28,7 @@ import org.sleuthkit.datamodel.SleuthkitCase; /** * File Types node support */ -public class FileTypes implements AutopsyVisitableItem { +public final class FileTypes implements AutopsyVisitableItem { private SleuthkitCase skCase; @@ -48,12 +48,12 @@ public class FileTypes implements AutopsyVisitableItem { /** * Node which will contain By Mime Type and By Extension nodes. */ - public static class FileTypesNode extends DisplayableItemNode { + public static final class FileTypesNode extends DisplayableItemNode { - @NbBundle.Messages("FileTypesNew.name.text=File Types") - private static final String NAME = Bundle.FileTypesNew_name_text(); + @NbBundle.Messages("FileTypes.name.text=File Types") + private static final String NAME = Bundle.FileTypes_name_text(); - public FileTypesNode(SleuthkitCase sleuthkitCase) { + FileTypesNode(SleuthkitCase sleuthkitCase) { super(new RootContentChildren(Arrays.asList( new FileTypeExtensionFilters(sleuthkitCase), new FileTypesByMimeType(sleuthkitCase) @@ -75,9 +75,9 @@ public class FileTypes implements AutopsyVisitableItem { @Override @NbBundle.Messages({ - "FileTypesNew.createSheet.name.name=Name", - "FileTypesNew.createSheet.name.displayName=Name", - "FileTypesNew.createSheet.name.desc=no description"}) + "FileTypes.createSheet.name.name=Name", + "FileTypes.createSheet.name.displayName=Name", + "FileTypes.createSheet.name.desc=no description"}) protected Sheet createSheet() { Sheet s = super.createSheet(); Sheet.Set ss = s.get(Sheet.PROPERTIES); @@ -86,9 +86,9 @@ public class FileTypes implements AutopsyVisitableItem { s.put(ss); } - ss.put(new NodeProperty<>(Bundle.FileTypesNew_createSheet_name_name(), - Bundle.FileTypesNew_createSheet_name_displayName(), - Bundle.FileTypesNew_createSheet_name_desc(), + ss.put(new NodeProperty<>(Bundle.FileTypes_createSheet_name_name(), + Bundle.FileTypes_createSheet_name_displayName(), + Bundle.FileTypes_createSheet_name_desc(), NAME )); return s; diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtNode.java index 5afb64fef8..ef0fbd422c 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtNode.java @@ -37,7 +37,7 @@ import org.sleuthkit.datamodel.SleuthkitCase; /** * Node for root of file types view. Children are nodes for specific types. */ -public class FileTypesByExtNode extends DisplayableItemNode { +class FileTypesByExtNode extends DisplayableItemNode { private static final String FNAME = NbBundle.getMessage(FileTypesByExtNode.class, "FileTypesNode.fname.text"); private final FileTypeExtensionFilters.RootFilter filter; @@ -122,7 +122,7 @@ public class FileTypesByExtNode extends DisplayableItemNode { /** * */ - static class FileTypesByExtChildren extends ChildFactory { + private static class FileTypesByExtChildren extends ChildFactory { private SleuthkitCase skCase; private FileTypeExtensionFilters.RootFilter filter; @@ -135,7 +135,7 @@ public class FileTypesByExtNode extends DisplayableItemNode { * @param o Observable that provides updates based on events being * fired (or null if one needs to be created) */ - public FileTypesByExtChildren(SleuthkitCase skCase, FileTypeExtensionFilters.RootFilter filter, Observable o) { + private FileTypesByExtChildren(SleuthkitCase skCase, FileTypeExtensionFilters.RootFilter filter, Observable o) { super(); this.skCase = skCase; this.filter = filter; @@ -152,7 +152,7 @@ public class FileTypesByExtNode extends DisplayableItemNode { */ private final class FileTypesByExtChildrenObservable extends Observable { - FileTypesByExtChildrenObservable() { + private FileTypesByExtChildrenObservable() { IngestManager.getInstance().addIngestJobEventListener(pcl); IngestManager.getInstance().addIngestModuleEventListener(pcl); Case.addPropertyChangeListener(pcl); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java index f0fcb90901..19529a478a 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java @@ -57,9 +57,9 @@ import org.sleuthkit.datamodel.TskData; * Listener which is checking for changes in IngestJobEvent Completed or * Cancelled and IngestModuleEvent Content Changed. */ -public class FileTypesByMimeType extends Observable implements AutopsyVisitableItem { +class FileTypesByMimeType extends Observable implements AutopsyVisitableItem { - private static SleuthkitCase skCase; + private static SleuthkitCase SKCASE; /** * The nodes of this tree will be determined dynamically by the mimetypes * which exist in the database. This hashmap will store them with the media @@ -128,11 +128,11 @@ public class FileTypesByMimeType extends Observable implements AutopsyVisitableI existingMimeTypes.clear(); } - if (getSleuthkitCase() == null) { + if (SKCASE == null) { return; } - try (SleuthkitCase.CaseDbQuery dbQuery = getSleuthkitCase().executeQuery(allDistinctMimeTypesQuery.toString())) { + try (SleuthkitCase.CaseDbQuery dbQuery = SKCASE.executeQuery(allDistinctMimeTypesQuery.toString())) { ResultSet resultSet = dbQuery.getResultSet(); synchronized (existingMimeTypes) { while (resultSet.next()) { @@ -158,398 +158,324 @@ public class FileTypesByMimeType extends Observable implements AutopsyVisitableI FileTypesByMimeType(SleuthkitCase skCase) { IngestManager.getInstance().addIngestJobEventListener(pcl); IngestManager.getInstance().addIngestModuleEventListener(pcl); - FileTypesByMimeType.skCase = skCase; + SKCASE = skCase; populateHashMap(); } - /** - * @return skCase - the sluethkit case - */ - SleuthkitCase getSleuthkitCase() { - return skCase; - } - @Override public T accept(AutopsyItemVisitor v) { return v.visit(this); } - /** - * Class which represents the root node of the "By MIME Type" tree, will - * have children of each media type present in the database or no children - * when the file detection module has not been run and MIME type is - * currently unknown. - */ - public class FileTypesByMimeTypeNode extends DisplayableItemNode { +/** + * Class which represents the root node of the "By MIME Type" tree, will have + * children of each media type present in the database or no children when the + * file detection module has not been run and MIME type is currently unknown. + */ +class FileTypesByMimeTypeNode extends DisplayableItemNode { - @NbBundle.Messages("FileTypesByMimeType.name.text=By MIME Type") - final String NAME = Bundle.FileTypesByMimeType_name_text(); - - FileTypesByMimeTypeNode(SleuthkitCase sleuthkitCase) { - super(Children.create(new FileTypesByMimeTypeNodeChildren(), true)); - super.setName(NAME); - super.setDisplayName(NAME); - this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file_types.png"); - } - - @Override - public boolean isLeafTypeNode() { - return false; - } - - @Override - public T accept(DisplayableItemNodeVisitor v) { - return v.visit(this); - } - - @Override - public String getItemType() { - return getClass().getName(); - } - - public boolean isEmpty() { - return existingMimeTypes.isEmpty(); - } + @NbBundle.Messages("FileTypesByMimeType.name.text=By MIME Type") + final String NAME = Bundle.FileTypesByMimeType_name_text(); + FileTypesByMimeTypeNode() { + super(Children.create(new FileTypesByMimeTypeNodeChildren(), true)); + super.setName(NAME); + super.setDisplayName(NAME); + this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file_types.png"); } - /** - * Creates the children for the "By MIME Type" node these children will each - * represent a distinct media type present in the DB - */ - class FileTypesByMimeTypeNodeChildren extends ChildFactory implements Observer { - - public FileTypesByMimeTypeNodeChildren() { - super(); - addObserver(this); - } - - @Override - protected boolean createKeys(List mediaTypeNodes) { - if (!existingMimeTypes.isEmpty()) { - mediaTypeNodes.addAll(getMediaTypeList()); - } - return true; - } - - @Override - protected Node createNodeForKey(String key) { - return new MediaTypeNode(key); - } - - @Override - public void update(Observable o, Object arg) { - refresh(true); - } - + @Override + public boolean isLeafTypeNode() { + return false; } - /** - * The Media type node created by the FileTypesByMimeTypeNodeChildren and - * contains one of the unique media types present in the database for this - * case. - */ - class MediaTypeNode extends DisplayableItemNode { - - MediaTypeNode(String name) { - super(Children.create(new MediaTypeChildren(name), true)); - setName(name); - setDisplayName(name); - this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file_types.png"); - } - - @Override - public boolean isLeafTypeNode() { - return false; - } - - @Override - public T accept(DisplayableItemNodeVisitor v) { - return v.visit(this); - } - - @Override - public String getItemType() { - return getClass().getName(); - } - + @Override + public T accept(DisplayableItemNodeVisitor v) { + return v.visit(this); } - /** - * Creates children fro media type nodes, children will be MediaSubTypeNodes - * and represent one of the subtypes which are present in the database of - * their media type. - */ - class MediaTypeChildren extends ChildFactory implements Observer { - - String mediaType; - - MediaTypeChildren(String name) { - addObserver(this); - this.mediaType = name; - } - - @Override - protected boolean createKeys(List mediaTypeNodes) { - mediaTypeNodes.addAll(existingMimeTypes.get(mediaType)); - return true; - } - - @Override - protected Node createNodeForKey(String subtype) { - String mimeType = mediaType + "/" + subtype; - return new MediaSubTypeNode(mimeType); - } - - @Override - public void update(Observable o, Object arg) { - refresh(true); - } - + @Override + public String getItemType() { + return getClass().getName(); } - /** - * Node which represents the media sub type in the By MIME type tree, the - * media subtype is the portion of the MIME type following the /. - */ - class MediaSubTypeNode extends DisplayableItemNode implements Observer { - - private MediaSubTypeNode(String mimeType) { - super(Children.create(new MediaSubTypeNodeChildren(mimeType), true)); - addObserver(this); - init(mimeType); - } - - private void init(String mimeType) { - super.setName(mimeType); - updateDisplayName(mimeType); - this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file-filter-icon.png"); //NON-NLS - } - - /** - * Updates the display name of the mediaSubTypeNode to include the count - * of files which it represents. - * - * @param mimeType - the complete MimeType, needed for accurate query - * results - */ - private void updateDisplayName(String mimeType) { - - final long count = new MediaSubTypeNodeChildren(mimeType).calculateItems(getSleuthkitCase(), mimeType); - - super.setDisplayName(mimeType.split("/")[1] + " (" + count + ")"); - } - - /** - * This returns true because any MediaSubTypeNode that exists is going - * to be a bottom level node in the Tree view on the left of Autopsy. - * - * @return true - */ - @Override - public boolean isLeafTypeNode() { - return true; - } - - @Override - public T accept(DisplayableItemNodeVisitor v) { - return v.visit(this); - } - - @Override - public String getItemType() { - return getClass().getName(); - } - - @Override - public void update(Observable o, Object arg) { - updateDisplayName(getName()); - } - } - - /** - * Factory for populating the contents of the Media Sub Type Node with the - * files that match MimeType which is represented by this position in the - * tree. - */ - private class MediaSubTypeNodeChildren extends ChildFactory.Detachable implements Observer { - - private final String mimeType; - - MediaSubTypeNodeChildren(String mimeType) { - super(); - addObserver(this); - this.mimeType = mimeType; - } - - /** - * Get children count without actually loading all nodes - * - * @return count(*) - the number of items that will be shown in this - * items Directory Listing - */ - private long calculateItems(SleuthkitCase sleuthkitCase, String mime_type) { - try { - return sleuthkitCase.countFilesWhere(createQuery(mime_type)); - } catch (TskCoreException ex) { - LOGGER.log(Level.SEVERE, "Error getting file search view count", ex); //NON-NLS - return 0; - } - } - - /** - * Uses the createQuery method to complete the query, Select * from - * tsk_files WHERE. The results from the database will contain the files - * which match this mime type and their information. - * - * @param list - will contain all files and their attributes from the - * tsk_files table where mime_type matches the one specified - * @return true - */ - @Override - protected boolean createKeys(List list) { - try { - List files = skCase.findAllFilesWhere(createQuery(mimeType)); - list.addAll(files); - } catch (TskCoreException ex) { - LOGGER.log(Level.SEVERE, "Couldn't get search results", ex); //NON-NLS - } - return true; - } - - /** - * Create the portion of the query following WHERE for a query of the - * database for each file which matches the complete MIME type - * represented by this node. Matches against the mime_type column in - * tsk_files. - * - * @param mimeType - the complete mimetype of the file mediatype/subtype - * @return query.toString - portion of SQL query which will follow a - * WHERE clause. - */ - private String createQuery(String mime_type) { - StringBuilder query = new StringBuilder(); - query.append("(dir_type = ").append(TskData.TSK_FS_NAME_TYPE_ENUM.REG.getValue()).append(")"); //NON-NLS - query.append(" AND (type IN (").append(TskData.TSK_DB_FILES_TYPE_ENUM.FS.ordinal()).append(","); //NON-NLS - query.append(TskData.TSK_DB_FILES_TYPE_ENUM.CARVED.ordinal()).append(","); - query.append(TskData.TSK_DB_FILES_TYPE_ENUM.DERIVED.ordinal()).append(","); - query.append(TskData.TSK_DB_FILES_TYPE_ENUM.LOCAL.ordinal()).append("))"); - if (UserPreferences.hideKnownFilesInViewsTree()) { - query.append(" AND (known IS NULL OR known != ").append(TskData.FileKnown.KNOWN.getFileKnownValue()).append(")"); //NON-NLS - } - query.append(" AND mime_type = '").append(mime_type).append("'"); //NON-NLS - return query.toString(); - } - - @Override - public void update(Observable o, Object arg) { - refresh(true); - } - - - /** - * Creates the content to populate the Directory Listing Table view for - * each file - * - * @param key - * @return - */ - @Override - protected Node createNodeForKey(Content key) { - return key.accept(new ContentVisitor.Default() { - @Override - public FileNode visit(File f) { - return new FileNode(f, false); - } - - @Override - public DirectoryNode visit(Directory d) { - return new DirectoryNode(d); - } - - @Override - public LayoutFileNode visit(LayoutFile lf) { - return new LayoutFileNode(lf); - } - - @Override - public LocalFileNode visit(DerivedFile df) { - return new LocalFileNode(df); - } - - @Override - public LocalFileNode visit(LocalFile lf) { - return new LocalFileNode(lf); - } - - @Override - protected AbstractNode defaultVisit(Content di) { - throw new UnsupportedOperationException(NbBundle.getMessage(this.getClass(), "FileTypeChildren.exception.notSupported.msg", di.toString())); - } - }); - } - } - - /** - * EmptyNode Class made for edge case where no mime exist in the database - * yet. Creates a node to display information on why the tree is empty. - * - * Swapped for the FileTypesByMimeType node in - * DirectoryTreeTopComponent.respondSelection - */ - static public class EmptyNode extends AbstractNode { - - public EmptyNode() { - super(Children.create(new EmptyChildFactory(), true)); - - } - - static class EmptyChildFactory extends ChildFactory { - - String FILE_ID_MSG = "Data not available. Run file type identification module."; //NON-NLS - - @Override - protected boolean createKeys(List list) { - list.add(FILE_ID_MSG); - return true; - } - - @Override - protected Node createNodeForKey(String key) { - return new MessageNode(key); - } - - } - - /** - * MessageNode is is the info message that displays in the table view, - * by also extending a DisplayableItemNode type, rather than an - * AbstractNode type it doesn't throw an error when right clicked. - */ - static class MessageNode extends DisplayableItemNode { - - MessageNode(String name) { - super(Children.LEAF); - super.setName(name); - setName(name); - setDisplayName(name); - } - - @Override - public boolean isLeafTypeNode() { - return true; - } - - @Override - public T accept(DisplayableItemNodeVisitor v) { - return v.visit(this); - } - - @Override - public String getItemType() { - return getClass().getName(); - } - } + boolean isEmpty() { + return existingMimeTypes.isEmpty(); } } + +/** + * Creates the children for the "By MIME Type" node these children will each + * represent a distinct media type present in the DB + */ +private class FileTypesByMimeTypeNodeChildren extends ChildFactory implements Observer { + + private FileTypesByMimeTypeNodeChildren() { + super(); + addObserver(this); + } + + @Override + protected boolean createKeys(List mediaTypeNodes) { + if (!existingMimeTypes.isEmpty()) { + mediaTypeNodes.addAll(getMediaTypeList()); + } + return true; + } + + @Override + protected Node createNodeForKey(String key) { + return new MediaTypeNode(key); + } + + @Override + public void update(Observable o, Object arg) { + refresh(true); + } + +} + +/** + * The Media type node created by the FileTypesByMimeTypeNodeChildren and + * contains one of the unique media types present in the database for this case. + */ +class MediaTypeNode extends DisplayableItemNode { + + MediaTypeNode(String name) { + super(Children.create(new MediaTypeNodeChildren(name), true)); + setName(name); + setDisplayName(name); + this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file_types.png"); + } + + @Override + public boolean isLeafTypeNode() { + return false; + } + + @Override + public T accept(DisplayableItemNodeVisitor v) { + return v.visit(this); + } + + @Override + public String getItemType() { + return getClass().getName(); + } + +} + +/** + * Creates children for media type nodes, children will be MediaSubTypeNodes and + * represent one of the subtypes which are present in the database of their + * media type. + */ +private class MediaTypeNodeChildren extends ChildFactory implements Observer { + + String mediaType; + + MediaTypeNodeChildren(String name) { + addObserver(this); + this.mediaType = name; + } + + @Override + protected boolean createKeys(List mediaTypeNodes) { + mediaTypeNodes.addAll(existingMimeTypes.get(mediaType)); + return true; + } + + @Override + protected Node createNodeForKey(String subtype) { + String mimeType = mediaType + "/" + subtype; + return new MediaSubTypeNode(mimeType); + } + + @Override + public void update(Observable o, Object arg) { + refresh(true); + } + +} + +/** + * Node which represents the media sub type in the By MIME type tree, the media + * subtype is the portion of the MIME type following the /. + */ +class MediaSubTypeNode extends DisplayableItemNode implements Observer { + + private MediaSubTypeNode(String mimeType) { + super(Children.create(new MediaSubTypeNodeChildren(mimeType), true)); + addObserver(this); + init(mimeType); + } + + private void init(String mimeType) { + super.setName(mimeType); + updateDisplayName(mimeType); + this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file-filter-icon.png"); //NON-NLS + } + + /** + * Updates the display name of the mediaSubTypeNode to include the count of + * files which it represents. + * + * @param mimeType - the complete MimeType, needed for accurate query + * results + */ + private void updateDisplayName(String mimeType) { + + final long count = new MediaSubTypeNodeChildren(mimeType).calculateItems(SKCASE, mimeType); + + super.setDisplayName(mimeType.split("/")[1] + " (" + count + ")"); + } + + /** + * This returns true because any MediaSubTypeNode that exists is going to be + * a bottom level node in the Tree view on the left of Autopsy. + * + * @return true + */ + @Override + public boolean isLeafTypeNode() { + return true; + } + + @Override + public T accept(DisplayableItemNodeVisitor v) { + return v.visit(this); + } + + @Override + public String getItemType() { + return getClass().getName(); + } + + @Override + public void update(Observable o, Object arg) { + updateDisplayName(getName()); + } +} + +/** + * Factory for populating the contents of the Media Sub Type Node with the files + * that match MimeType which is represented by this position in the tree. + */ +private class MediaSubTypeNodeChildren extends ChildFactory.Detachable implements Observer { + + private final String mimeType; + + private MediaSubTypeNodeChildren(String mimeType) { + super(); + addObserver(this); + this.mimeType = mimeType; + } + + /** + * Get children count without actually loading all nodes + * + * @return count(*) - the number of items that will be shown in this items + * Directory Listing + */ + private long calculateItems(SleuthkitCase sleuthkitCase, String mime_type) { + try { + return sleuthkitCase.countFilesWhere(createQuery(mime_type)); + } catch (TskCoreException ex) { + LOGGER.log(Level.SEVERE, "Error getting file search view count", ex); //NON-NLS + return 0; + } + } + + /** + * Uses the createQuery method to complete the query, Select * from + * tsk_files WHERE. The results from the database will contain the files + * which match this mime type and their information. + * + * @param list - will contain all files and their attributes from the + * tsk_files table where mime_type matches the one specified + * @return true + */ + @Override + protected boolean createKeys(List list) { + try { + List files = SKCASE.findAllFilesWhere(createQuery(mimeType)); + list.addAll(files); + } catch (TskCoreException ex) { + LOGGER.log(Level.SEVERE, "Couldn't get search results", ex); //NON-NLS + } + return true; + } + + /** + * Create the portion of the query following WHERE for a query of the + * database for each file which matches the complete MIME type represented + * by this node. Matches against the mime_type column in tsk_files. + * + * @param mimeType - the complete mimetype of the file mediatype/subtype + * @return query.toString - portion of SQL query which will follow a WHERE + * clause. + */ + private String createQuery(String mime_type) { + StringBuilder query = new StringBuilder(); + query.append("(dir_type = ").append(TskData.TSK_FS_NAME_TYPE_ENUM.REG.getValue()).append(")"); //NON-NLS + query.append(" AND (type IN (").append(TskData.TSK_DB_FILES_TYPE_ENUM.FS.ordinal()).append(","); //NON-NLS + query.append(TskData.TSK_DB_FILES_TYPE_ENUM.CARVED.ordinal()).append(","); + query.append(TskData.TSK_DB_FILES_TYPE_ENUM.DERIVED.ordinal()).append(","); + query.append(TskData.TSK_DB_FILES_TYPE_ENUM.LOCAL.ordinal()).append("))"); + if (UserPreferences.hideKnownFilesInViewsTree()) { + query.append(" AND (known IS NULL OR known != ").append(TskData.FileKnown.KNOWN.getFileKnownValue()).append(")"); //NON-NLS + } + query.append(" AND mime_type = '").append(mime_type).append("'"); //NON-NLS + return query.toString(); + } + + @Override + public void update(Observable o, Object arg) { + refresh(true); + } + + /** + * Creates the content to populate the Directory Listing Table view for each + * file + * + * @param key + * @return + */ + @Override + protected Node createNodeForKey(Content key) { + return key.accept(new ContentVisitor.Default() { + @Override + public FileNode visit(File f) { + return new FileNode(f, false); + } + + @Override + public DirectoryNode visit(Directory d) { + return new DirectoryNode(d); + } + + @Override + public LayoutFileNode visit(LayoutFile lf) { + return new LayoutFileNode(lf); + } + + @Override + public LocalFileNode visit(DerivedFile df) { + return new LocalFileNode(df); + } + + @Override + public LocalFileNode visit(LocalFile lf) { + return new LocalFileNode(lf); + } + + @Override + protected AbstractNode defaultVisit(Content di) { + throw new UnsupportedOperationException(NbBundle.getMessage(this.getClass(), "FileTypeChildren.exception.notSupported.msg", di.toString())); + } + }); + } +} + +} diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java index 22485148ff..7fe87de524 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java @@ -18,6 +18,7 @@ */ package org.sleuthkit.autopsy.directorytree; +import org.sleuthkit.autopsy.datamodel.EmptyNode; import java.awt.Cursor; import java.awt.EventQueue; import java.beans.PropertyChangeEvent; @@ -61,9 +62,7 @@ import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode; import org.sleuthkit.autopsy.datamodel.DataSources; import org.sleuthkit.autopsy.datamodel.DataSourcesNode; import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; -import org.sleuthkit.autopsy.datamodel.FileTypesByMimeType.EmptyNode; import org.sleuthkit.autopsy.datamodel.ExtractedContent; -import org.sleuthkit.autopsy.datamodel.FileTypesByMimeType; import org.sleuthkit.autopsy.datamodel.KeywordHits; import org.sleuthkit.autopsy.datamodel.KnownFileFilterNode; import org.sleuthkit.autopsy.datamodel.Reports; @@ -603,7 +602,8 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat } } } - + + @NbBundle.Messages("DirectoryTree.emptyMimeNode.text=Data not available. Run file type identification module.") /** * Event handler to run when selection changed * @@ -650,9 +650,8 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat //Special case for when File Type Identification has not yet been run and //there are no mime types to populate Files by Mime Type Tree - if (originNode instanceof FileTypesByMimeType.FileTypesByMimeTypeNode - && ((FileTypesByMimeType.FileTypesByMimeTypeNode) originNode).isEmpty()) { - EmptyNode emptyNode = new EmptyNode(); + if (EmptyNode.isEmptyMimeTypeNode(originNode)) { + EmptyNode emptyNode = new EmptyNode(Bundle.DirectoryTree_emptyMimeNode_text()); Node emptyDrfn = new DataResultFilterNode(emptyNode, DirectoryTreeTopComponent.this.em); Node emptyKffn = new KnownFileFilterNode(emptyDrfn, KnownFileFilterNode.getSelectionContext(emptyNode)); Node emptySffn = new SlackFileFilterNode(emptyKffn, SlackFileFilterNode.getSelectionContext(originNode)); From 63dcd67493eb8f247c3ae9e81a315abef35afe0a Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Thu, 1 Dec 2016 15:10:52 -0500 Subject: [PATCH 02/16] 1917 instance of Sleuthkitcase now final --- Core/src/org/sleuthkit/autopsy/datamodel/EmptyNode.java | 6 ++++++ .../sleuthkit/autopsy/datamodel/FileTypesByMimeType.java | 4 ++-- .../autopsy/directorytree/DirectoryTreeTopComponent.java | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/EmptyNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/EmptyNode.java index d77949e59f..205e175e9f 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/EmptyNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/EmptyNode.java @@ -25,6 +25,12 @@ public final class EmptyNode extends AbstractNode { } + /** + * Method to check if the node in question is a FileTypesByMimeTypeNode which is empty. + * + * @param originNode the Node which you wish to check. + * @return True if originNode is an instance of FileTypesByMimeTypeNode and is empty, false otherwise. + */ public static boolean isEmptyMimeTypeNode(Node originNode) { boolean isEmptyMimeNode = false; if (originNode instanceof FileTypesByMimeType.FileTypesByMimeTypeNode && ((FileTypesByMimeType.FileTypesByMimeTypeNode) originNode).isEmpty()) { diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java index 19529a478a..c543a07209 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java @@ -59,7 +59,7 @@ import org.sleuthkit.datamodel.TskData; */ class FileTypesByMimeType extends Observable implements AutopsyVisitableItem { - private static SleuthkitCase SKCASE; + private final SleuthkitCase SKCASE; /** * The nodes of this tree will be determined dynamically by the mimetypes * which exist in the database. This hashmap will store them with the media @@ -158,7 +158,7 @@ class FileTypesByMimeType extends Observable implements AutopsyVisitableItem { FileTypesByMimeType(SleuthkitCase skCase) { IngestManager.getInstance().addIngestJobEventListener(pcl); IngestManager.getInstance().addIngestModuleEventListener(pcl); - SKCASE = skCase; + this.SKCASE = skCase; populateHashMap(); } diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java index 7fe87de524..1bcd302750 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java @@ -603,7 +603,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat } } - @NbBundle.Messages("DirectoryTree.emptyMimeNode.text=Data not available. Run file type identification module.") + @NbBundle.Messages("DirectoryTreeTopComponent.emptyMimeNode.text=Data not available. Run file type identification module.") /** * Event handler to run when selection changed * From 038ce32767f460759b650cc002c6cb4e04da6f30 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Thu, 1 Dec 2016 15:58:12 -0500 Subject: [PATCH 03/16] 1917 fixed to bundle messages, shortened inner class names --- .../datamodel/AbstractContentChildren.java | 2 +- .../autopsy/datamodel/Bundle.properties | 20 ++++++------ .../autopsy/datamodel/Bundle_ja.properties | 10 ------ .../datamodel/DisplayableItemNodeVisitor.java | 4 +-- .../autopsy/datamodel/EmptyNode.java | 6 ++-- .../autopsy/datamodel/FileTypeByExtNode.java | 20 ++++++------ .../autopsy/datamodel/FileTypes.java | 2 +- .../autopsy/datamodel/FileTypesByExtNode.java | 14 ++++---- .../datamodel/FileTypesByMimeType.java | 14 ++++---- .../DirectoryTreeTopComponent.java | 32 +++++++++---------- 10 files changed, 57 insertions(+), 67 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java index a9aa3a7280..4313be1f48 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java @@ -220,7 +220,7 @@ abstract class AbstractContentChildren extends Keys { @Override public AbstractNode visit(FileTypesByMimeType ftByMimeTypeItem) { - return ftByMimeTypeItem.new FileTypesByMimeTypeNode(); + return ftByMimeTypeItem.new ByMimeTypeNode(); } } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties index 4b9d0225a6..2230c23fe8 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties @@ -118,16 +118,16 @@ FileSize.createSheet.filterType.displayName=Filter Type FileSize.createSheet.filterType.desc=no description FileSize.exception.notSupported.msg=Not supported for this type of Displayable Item\: {0} FileTypeChildren.exception.notSupported.msg=Not supported for this type of Displayable Item\: {0} -FileTypeNode.createSheet.filterType.name=Filter Type -FileTypeNode.createSheet.filterType.displayName=Filter Type -FileTypeNode.createSheet.filterType.desc=no description -FileTypeNode.createSheet.fileExt.name=File Extensions -FileTypeNode.createSheet.fileExt.displayName=File Extensions -FileTypeNode.createSheet.fileExt.desc=no description -FileTypesNode.fname.text=By Extension -FileTypesNode.createSheet.name.name=Name -FileTypesNode.createSheet.name.displayName=Name -FileTypesNode.createSheet.name.desc=no description +FileTypeByExtNode.createSheet.filterType.name=Filter Type +FileTypeByExtNode.createSheet.filterType.displayName=Filter Type +FileTypeByExtNode.createSheet.filterType.desc=no description +FileTypeByExtNode.createSheet.fileExt.name=File Extensions +FileTypeByExtNode.createSheet.fileExt.displayName=File Extensions +FileTypeByExtNode.createSheet.fileExt.desc=no description +FileTypesByExtNode.fname.text=By Extension +FileTypesByExtNode.createSheet.name.name=Name +FileTypesByExtNode.createSheet.name.displayName=Name +FileTypesByExtNode.createSheet.name.desc=no description HashsetHits.createSheet.name.name=Name HashsetHits.createSheet.name.displayName=Name HashsetHits.createSheet.name.desc=no description diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle_ja.properties index 036ff29159..51970be2ec 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle_ja.properties @@ -112,16 +112,6 @@ FileTypeExtensionFilters.autDocOfficeFilter.text=\u30aa\u30d5\u30a3\u30b9 FileTypeExtensionFilters.autoDocPdfFilter.text=PDF FileTypeExtensionFilters.autDocTxtFilter.text=\u30d7\u30ec\u30fc\u30f3\u30c6\u30ad\u30b9\u30c8 FileTypeExtensionFilters.autDocRtfFilter.text=\u30ea\u30c3\u30c1\u30c6\u30ad\u30b9\u30c8 -FileTypeNode.createSheet.filterType.name=\u30d5\u30a3\u30eb\u30bf\u30fc\u30bf\u30a4\u30d7 -FileTypeNode.createSheet.filterType.displayName=\u30d5\u30a3\u30eb\u30bf\u30fc\u30bf\u30a4\u30d7 -FileTypeNode.createSheet.filterType.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093 -FileTypeNode.createSheet.fileExt.name=\u30d5\u30a1\u30a4\u30eb\u62e1\u5f35\u5b50 -FileTypeNode.createSheet.fileExt.displayName=\u30d5\u30a1\u30a4\u30eb\u62e1\u5f35\u5b50 -FileTypeNode.createSheet.fileExt.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093 -FileTypesNode.fname.text=\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7 -FileTypesNode.createSheet.name.name=\u540d\u524d -FileTypesNode.createSheet.name.displayName=\u540d\u524d -FileTypesNode.createSheet.name.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093 HashsetHits.createSheet.name.name=\u540d\u524d HashsetHits.createSheet.name.displayName=\u540d\u524d HashsetHits.createSheet.name.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093 diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java b/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java index 6d18d977ca..978933a7e1 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java @@ -146,7 +146,7 @@ public interface DisplayableItemNodeVisitor { T visit(FileTypes.FileTypesNode fileTypes); - T visit(FileTypesByMimeType.FileTypesByMimeTypeNode ftByMimeTypeNode); + T visit(FileTypesByMimeType.ByMimeTypeNode ftByMimeTypeNode); T visit(FileTypesByMimeType.MediaTypeNode ftByMimeTypeMediaType); @@ -219,7 +219,7 @@ public interface DisplayableItemNodeVisitor { } @Override - public T visit(FileTypesByMimeType.FileTypesByMimeTypeNode ftByMimeTypeNode) { + public T visit(FileTypesByMimeType.ByMimeTypeNode ftByMimeTypeNode) { return defaultVisit(ftByMimeTypeNode); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/EmptyNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/EmptyNode.java index 205e175e9f..a8eaf76cda 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/EmptyNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/EmptyNode.java @@ -26,14 +26,14 @@ public final class EmptyNode extends AbstractNode { } /** - * Method to check if the node in question is a FileTypesByMimeTypeNode which is empty. + * Method to check if the node in question is a ByMimeTypeNode which is empty. * * @param originNode the Node which you wish to check. - * @return True if originNode is an instance of FileTypesByMimeTypeNode and is empty, false otherwise. + * @return True if originNode is an instance of ByMimeTypeNode and is empty, false otherwise. */ public static boolean isEmptyMimeTypeNode(Node originNode) { boolean isEmptyMimeNode = false; - if (originNode instanceof FileTypesByMimeType.FileTypesByMimeTypeNode && ((FileTypesByMimeType.FileTypesByMimeTypeNode) originNode).isEmpty()) { + if (originNode instanceof FileTypesByMimeType.ByMimeTypeNode && ((FileTypesByMimeType.ByMimeTypeNode) originNode).isEmpty()) { isEmptyMimeNode = true; } return isEmptyMimeNode; diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypeByExtNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypeByExtNode.java index 06a962a0c1..f3d7eb28ba 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypeByExtNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypeByExtNode.java @@ -111,18 +111,18 @@ class FileTypeByExtNode extends DisplayableItemNode { s.put(ss); } - ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "FileTypeNode.createSheet.filterType.name"), - NbBundle.getMessage(this.getClass(), "FileTypeNode.createSheet.filterType.displayName"), - NbBundle.getMessage(this.getClass(), "FileTypeNode.createSheet.filterType.desc"), + ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "FileTypeByExtNode.createSheet.filterType.name"), + NbBundle.getMessage(this.getClass(), "FileTypeByExtNode.createSheet.filterType.displayName"), + NbBundle.getMessage(this.getClass(), "FileTypeByExtNode.createSheet.filterType.desc"), filter.getDisplayName())); String extensions = ""; for (String ext : filter.getFilter()) { extensions += "'" + ext + "', "; } extensions = extensions.substring(0, extensions.lastIndexOf(',')); - ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "FileTypeNode.createSheet.fileExt.name"), - NbBundle.getMessage(this.getClass(), "FileTypeNode.createSheet.fileExt.displayName"), - NbBundle.getMessage(this.getClass(), "FileTypeNode.createSheet.fileExt.desc"), + ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "FileTypeByExtNode.createSheet.fileExt.name"), + NbBundle.getMessage(this.getClass(), "FileTypeByExtNode.createSheet.fileExt.displayName"), + NbBundle.getMessage(this.getClass(), "FileTypeByExtNode.createSheet.fileExt.desc"), extensions)); return s; @@ -150,8 +150,8 @@ class FileTypeByExtNode extends DisplayableItemNode { private final SleuthkitCase skCase; private final FileTypeExtensionFilters.SearchFilterInterface filter; - private final static Logger logger = Logger.getLogger(FileTypeChildFactory.class.getName()); - private Observable notifier; + private final static Logger LOGGER = Logger.getLogger(FileTypeChildFactory.class.getName()); + private final Observable notifier; // use the constructor that gets an observable passed in for updates @Deprecated @@ -209,7 +209,7 @@ class FileTypeByExtNode extends DisplayableItemNode { try { return sleuthkitCase.countFilesWhere(createQuery(filter)); } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error getting file search view count", ex); //NON-NLS + LOGGER.log(Level.SEVERE, "Error getting file search view count", ex); //NON-NLS return 0; } } @@ -220,7 +220,7 @@ class FileTypeByExtNode extends DisplayableItemNode { List files = skCase.findAllFilesWhere(createQuery(filter)); list.addAll(files); } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Couldn't get search results", ex); //NON-NLS + LOGGER.log(Level.SEVERE, "Couldn't get search results", ex); //NON-NLS } return true; } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java index 12a8b2b12c..45db9395ba 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java @@ -30,7 +30,7 @@ import org.sleuthkit.datamodel.SleuthkitCase; */ public final class FileTypes implements AutopsyVisitableItem { - private SleuthkitCase skCase; + private final SleuthkitCase skCase; FileTypes(SleuthkitCase skCase) { this.skCase = skCase; diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtNode.java index ef0fbd422c..be76722876 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtNode.java @@ -39,7 +39,7 @@ import org.sleuthkit.datamodel.SleuthkitCase; */ class FileTypesByExtNode extends DisplayableItemNode { - private static final String FNAME = NbBundle.getMessage(FileTypesByExtNode.class, "FileTypesNode.fname.text"); + private static final String FNAME = NbBundle.getMessage(FileTypesByExtNode.class, "FileTypesByExtNode.fname.text"); private final FileTypeExtensionFilters.RootFilter filter; /** * @@ -98,9 +98,9 @@ class FileTypesByExtNode extends DisplayableItemNode { s.put(ss); } - ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "FileTypesNode.createSheet.name.name"), - NbBundle.getMessage(this.getClass(), "FileTypesNode.createSheet.name.displayName"), - NbBundle.getMessage(this.getClass(), "FileTypesNode.createSheet.name.desc"), + ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.name.name"), + NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.name.displayName"), + NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.name.desc"), getName())); return s; } @@ -124,9 +124,9 @@ class FileTypesByExtNode extends DisplayableItemNode { */ private static class FileTypesByExtChildren extends ChildFactory { - private SleuthkitCase skCase; - private FileTypeExtensionFilters.RootFilter filter; - private Observable notifier; + private final SleuthkitCase skCase; + private final FileTypeExtensionFilters.RootFilter filter; + private final Observable notifier; /** * diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java index c543a07209..8548904601 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java @@ -172,13 +172,13 @@ class FileTypesByMimeType extends Observable implements AutopsyVisitableItem { * children of each media type present in the database or no children when the * file detection module has not been run and MIME type is currently unknown. */ -class FileTypesByMimeTypeNode extends DisplayableItemNode { +class ByMimeTypeNode extends DisplayableItemNode { @NbBundle.Messages("FileTypesByMimeType.name.text=By MIME Type") final String NAME = Bundle.FileTypesByMimeType_name_text(); - FileTypesByMimeTypeNode() { - super(Children.create(new FileTypesByMimeTypeNodeChildren(), true)); + ByMimeTypeNode() { + super(Children.create(new ByMimeTypeNodeChildren(), true)); super.setName(NAME); super.setDisplayName(NAME); this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file_types.png"); @@ -209,9 +209,9 @@ class FileTypesByMimeTypeNode extends DisplayableItemNode { * Creates the children for the "By MIME Type" node these children will each * represent a distinct media type present in the DB */ -private class FileTypesByMimeTypeNodeChildren extends ChildFactory implements Observer { +private class ByMimeTypeNodeChildren extends ChildFactory implements Observer { - private FileTypesByMimeTypeNodeChildren() { + private ByMimeTypeNodeChildren() { super(); addObserver(this); } @@ -237,8 +237,8 @@ private class FileTypesByMimeTypeNodeChildren extends ChildFactory imple } /** - * The Media type node created by the FileTypesByMimeTypeNodeChildren and - * contains one of the unique media types present in the database for this case. + * The Media type node created by the ByMimeTypeNodeChildren and + contains one of the unique media types present in the database for this case. */ class MediaTypeNode extends DisplayableItemNode { diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java index 1bcd302750..a57f85fb68 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java @@ -95,7 +95,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat private final LinkedList backList; private final LinkedList forwardList; private static final String PREFERRED_ID = "DirectoryTreeTopComponent"; //NON-NLS - private static final Logger logger = Logger.getLogger(DirectoryTreeTopComponent.class.getName()); + private static final Logger LOGGER = Logger.getLogger(DirectoryTreeTopComponent.class.getName()); private RootContentChildren contentChildren; /** @@ -312,14 +312,14 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat WindowManager winManager = WindowManager.getDefault(); TopComponent win = winManager.findTopComponent(PREFERRED_ID); if (win == null) { - logger.warning( + LOGGER.warning( "Cannot find " + PREFERRED_ID + " component. It will not be located properly in the window system."); //NON-NLS return getDefault(); } if (win instanceof DirectoryTreeTopComponent) { return (DirectoryTreeTopComponent) win; } - logger.warning( + LOGGER.warning( "There seem to be multiple components with the '" + PREFERRED_ID //NON-NLS + "' ID. That is a potential source of errors and unexpected behavior."); //NON-NLS return getDefault(); @@ -434,7 +434,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat try { em.setSelectedNodes(new Node[]{childNodes.getNodeAt(0)}); } catch (Exception ex) { - logger.log(Level.SEVERE, "Error setting default selected node.", ex); //NON-NLS + LOGGER.log(Level.SEVERE, "Error setting default selected node.", ex); //NON-NLS } } @@ -651,11 +651,11 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat //Special case for when File Type Identification has not yet been run and //there are no mime types to populate Files by Mime Type Tree if (EmptyNode.isEmptyMimeTypeNode(originNode)) { - EmptyNode emptyNode = new EmptyNode(Bundle.DirectoryTree_emptyMimeNode_text()); + EmptyNode emptyNode = new EmptyNode(Bundle.DirectoryTreeTopComponent_emptyMimeNode_text()); Node emptyDrfn = new DataResultFilterNode(emptyNode, DirectoryTreeTopComponent.this.em); Node emptyKffn = new KnownFileFilterNode(emptyDrfn, KnownFileFilterNode.getSelectionContext(emptyNode)); Node emptySffn = new SlackFileFilterNode(emptyKffn, SlackFileFilterNode.getSelectionContext(originNode)); - dataResult.setNode(new TableFilterNode(emptySffn, true, "This Node Is Empty")); + dataResult.setNode(new TableFilterNode(emptySffn, true, "This Node Is Empty")); //NON-NLS } else if (originNode instanceof DisplayableItemNode) { dataResult.setNode(new TableFilterNode(sffn, true, ((DisplayableItemNode) originNode).getItemType())); } else { @@ -668,7 +668,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat try { displayName = content.getUniquePath(); } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Exception while calling Content.getUniquePath() for node: " + originNode); //NON-NLS + LOGGER.log(Level.SEVERE, "Exception while calling Content.getUniquePath() for node: " + originNode); //NON-NLS } } else if (originNode.getLookup().lookup(String.class) != null) { displayName = originNode.getLookup().lookup(String.class); @@ -775,13 +775,13 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat Children rootChildren = em.getRootContext().getChildren(); Node dataSourcesFilterNode = rootChildren.findChild(DataSourcesNode.NAME); if (dataSourcesFilterNode == null) { - logger.log(Level.SEVERE, "Cannot find data sources filter node, won't refresh the content tree"); //NON-NLS + LOGGER.log(Level.SEVERE, "Cannot find data sources filter node, won't refresh the content tree"); //NON-NLS return; } DirectoryTreeFilterNode.OriginalNode imagesNodeOrig = dataSourcesFilterNode.getLookup().lookup(DirectoryTreeFilterNode.OriginalNode.class); if (imagesNodeOrig == null) { - logger.log(Level.SEVERE, "Cannot find data sources node, won't refresh the content tree"); //NON-NLS + LOGGER.log(Level.SEVERE, "Cannot find data sources node, won't refresh the content tree"); //NON-NLS return; } @@ -824,7 +824,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat for (int i = 0; i < previouslySelectedNodePath.length; ++i) { nodePath.append(previouslySelectedNodePath[i]).append("/"); } - logger.log(Level.WARNING, "Failed to find any nodes to select on path " + nodePath.toString(), ex); //NON-NLS + LOGGER.log(Level.WARNING, "Failed to find any nodes to select on path " + nodePath.toString(), ex); //NON-NLS break; } } @@ -839,7 +839,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat try { em.setExploredContextAndSelection(selectedNode, new Node[]{selectedNode}); } catch (PropertyVetoException ex) { - logger.log(Level.WARNING, "Property veto from ExplorerManager setting selection to " + selectedNode.getName(), ex); //NON-NLS + LOGGER.log(Level.WARNING, "Property veto from ExplorerManager setting selection to " + selectedNode.getName(), ex); //NON-NLS } } } @@ -879,7 +879,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat } treeNode = hashsetRootChilds.findChild(setName); } catch (TskException ex) { - logger.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS + LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS } } else if (typeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()) { Node keywordRootNode = resultsChilds.findChild(typeName); @@ -906,7 +906,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat } treeNode = listChildren.findChild(keywordName); } catch (TskException ex) { - logger.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS + LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS } } else if (typeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID() || typeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()) { @@ -923,7 +923,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat } treeNode = interestingItemsRootChildren.findChild(setName); } catch (TskException ex) { - logger.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS + LOGGER.log(Level.WARNING, "Error retrieving attributes", ex); //NON-NLS } } else { Node extractedContent = resultsChilds.findChild(ExtractedContent.NAME); @@ -941,7 +941,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat try { em.setExploredContextAndSelection(treeNode, new Node[]{treeNode}); } catch (PropertyVetoException ex) { - logger.log(Level.WARNING, "Property Veto: ", ex); //NON-NLS + LOGGER.log(Level.WARNING, "Property Veto: ", ex); //NON-NLS } // Another thread is needed because we have to wait for dataResult to populate @@ -976,7 +976,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat try { firePropertyChange(BlackboardResultViewer.FINISHED_DISPLAY_EVT, 0, 1); } catch (Exception e) { - logger.log(Level.SEVERE, "DirectoryTreeTopComponent listener threw exception", e); //NON-NLS + LOGGER.log(Level.SEVERE, "DirectoryTreeTopComponent listener threw exception", e); //NON-NLS MessageNotifyUtil.Notify.show(NbBundle.getMessage(this.getClass(), "DirectoryTreeTopComponent.moduleErr"), NbBundle.getMessage(this.getClass(), "DirectoryTreeTopComponent.moduleErr.msg"), From d8c4ea9044b8b3a15881e63e659cb1573243c916 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Thu, 1 Dec 2016 16:51:13 -0500 Subject: [PATCH 04/16] 1917 made FileTypeByExtType a inner class of FileTypesByExtType --- .../autopsy/datamodel/Bundle.properties | 12 +- .../datamodel/DisplayableItemNodeVisitor.java | 24 +- .../autopsy/datamodel/EmptyNode.java | 14 - .../autopsy/datamodel/FileTypeByExtNode.java | 277 --------- .../autopsy/datamodel/FileTypesByExtNode.java | 278 ++++++++- .../datamodel/FileTypesByMimeType.java | 583 +++++++++--------- .../DirectoryTreeTopComponent.java | 9 +- 7 files changed, 583 insertions(+), 614 deletions(-) delete mode 100644 Core/src/org/sleuthkit/autopsy/datamodel/FileTypeByExtNode.java diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties index 2230c23fe8..b1cf4839c8 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties @@ -118,12 +118,12 @@ FileSize.createSheet.filterType.displayName=Filter Type FileSize.createSheet.filterType.desc=no description FileSize.exception.notSupported.msg=Not supported for this type of Displayable Item\: {0} FileTypeChildren.exception.notSupported.msg=Not supported for this type of Displayable Item\: {0} -FileTypeByExtNode.createSheet.filterType.name=Filter Type -FileTypeByExtNode.createSheet.filterType.displayName=Filter Type -FileTypeByExtNode.createSheet.filterType.desc=no description -FileTypeByExtNode.createSheet.fileExt.name=File Extensions -FileTypeByExtNode.createSheet.fileExt.displayName=File Extensions -FileTypeByExtNode.createSheet.fileExt.desc=no description +FileTypesByExtNode.createSheet.filterType.name=Filter Type +FileTypesByExtNode.createSheet.filterType.displayName=Filter Type +FileTypesByExtNode.createSheet.filterType.desc=no description +FileTypesByExtNode.createSheet.fileExt.name=File Extensions +FileTypesByExtNode.createSheet.fileExt.displayName=File Extensions +FileTypesByExtNode.createSheet.fileExt.desc=no description FileTypesByExtNode.fname.text=By Extension FileTypesByExtNode.createSheet.name.name=Name FileTypesByExtNode.createSheet.name.displayName=Name diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java b/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java index 978933a7e1..2c029806cd 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java @@ -49,16 +49,16 @@ public interface DisplayableItemNodeVisitor { T visit(ImageNode in); T visit(VolumeNode vn); - + T visit(SlackFileNode sfn); - + /* * Views Area */ T visit(ViewsNode vn); - T visit(FileTypeByExtNode fsfn); + T visit(FileTypesByExtNode.ByExtNode fsfn); T visit(DeletedContentNode dcn); @@ -154,8 +154,6 @@ public interface DisplayableItemNodeVisitor { T visit(EmptyNode.MessageNode emptyNode); - - /** * Visitor with an implementable default behavior for all types. Override * specific visit types to not use the default behavior. @@ -214,30 +212,30 @@ public interface DisplayableItemNodeVisitor { } @Override - public T visit(FileTypeByExtNode fsfn) { + public T visit(FileTypesByExtNode.ByExtNode fsfn) { return defaultVisit(fsfn); } - + @Override public T visit(FileTypesByMimeType.ByMimeTypeNode ftByMimeTypeNode) { return defaultVisit(ftByMimeTypeNode); } - + @Override public T visit(FileTypesByMimeType.MediaTypeNode ftByMimeTypeMediaTypeNode) { return defaultVisit(ftByMimeTypeMediaTypeNode); } - + @Override public T visit(FileTypesByMimeType.MediaSubTypeNode ftByMimeTypeMediaTypeNode) { return defaultVisit(ftByMimeTypeMediaTypeNode); } - + @Override public T visit(EmptyNode.MessageNode ftByMimeTypeEmptyNode) { return defaultVisit(ftByMimeTypeEmptyNode); } - + @Override public T visit(DeletedContentNode dcn) { return defaultVisit(dcn); @@ -297,11 +295,12 @@ public interface DisplayableItemNodeVisitor { public T visit(ResultsNode rn) { return defaultVisit(rn); } - + @Override public T visit(FileTypesNode ft) { return defaultVisit(ft); } + @Override public T visit(DataSourcesNode in) { return defaultVisit(in); @@ -426,6 +425,7 @@ public interface DisplayableItemNodeVisitor { public T visit(Accounts.BINNode node) { return defaultVisit(node); } + @Override public T visit(Accounts.DefaultAccountTypeNode node) { return defaultVisit(node); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/EmptyNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/EmptyNode.java index a8eaf76cda..39eb01b4e1 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/EmptyNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/EmptyNode.java @@ -25,20 +25,6 @@ public final class EmptyNode extends AbstractNode { } - /** - * Method to check if the node in question is a ByMimeTypeNode which is empty. - * - * @param originNode the Node which you wish to check. - * @return True if originNode is an instance of ByMimeTypeNode and is empty, false otherwise. - */ - public static boolean isEmptyMimeTypeNode(Node originNode) { - boolean isEmptyMimeNode = false; - if (originNode instanceof FileTypesByMimeType.ByMimeTypeNode && ((FileTypesByMimeType.ByMimeTypeNode) originNode).isEmpty()) { - isEmptyMimeNode = true; - } - return isEmptyMimeNode; - } - static class EmptyChildFactory extends ChildFactory { String fileIdMsg; //NON-NLS diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypeByExtNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypeByExtNode.java deleted file mode 100644 index f3d7eb28ba..0000000000 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypeByExtNode.java +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2011-2016 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 org.sleuthkit.autopsy.datamodel.accounts.FileTypeExtensionFilters; -import java.util.List; -import java.util.Observable; -import java.util.Observer; -import java.util.logging.Level; -import org.openide.nodes.AbstractNode; -import org.openide.nodes.ChildFactory; -import org.openide.nodes.Children; -import org.openide.nodes.Node; -import org.openide.nodes.Sheet; -import org.openide.util.NbBundle; -import org.openide.util.lookup.Lookups; -import org.sleuthkit.autopsy.core.UserPreferences; -import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.ContentVisitor; -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.SleuthkitCase; -import org.sleuthkit.datamodel.TskCoreException; -import org.sleuthkit.datamodel.TskData; - -/** - * Node for a specific file type / extension. Children of it will be the files - * of that type. - */ -class FileTypeByExtNode extends DisplayableItemNode { - - FileTypeExtensionFilters.SearchFilterInterface filter; - SleuthkitCase skCase; - - // deprecated in favor of the version that takes an observable to provide refresh updates - @Deprecated - FileTypeByExtNode(FileTypeExtensionFilters.SearchFilterInterface filter, SleuthkitCase skCase) { - super(Children.create(new FileTypeChildFactory(filter, skCase), true), Lookups.singleton(filter.getDisplayName())); - this.filter = filter; - this.skCase = skCase; - init(); - } - - /** - * - * @param filter Extensions that will be shown for this node - * @param skCase - * @param o Observable that sends updates when the child factories - * should refresh - */ - FileTypeByExtNode(FileTypeExtensionFilters.SearchFilterInterface filter, SleuthkitCase skCase, Observable o) { - super(Children.create(new FileTypeChildFactory(filter, skCase, o), true), Lookups.singleton(filter.getDisplayName())); - this.filter = filter; - this.skCase = skCase; - init(); - o.addObserver(new FileTypeNodeObserver()); - } - - private void init() { - super.setName(filter.getName()); - updateDisplayName(); - this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file-filter-icon.png"); //NON-NLS - } - - // update the display name when new events are fired - private class FileTypeNodeObserver implements Observer { - - @Override - public void update(Observable o, Object arg) { - updateDisplayName(); - } - } - - private void updateDisplayName() { - final long count = FileTypeChildFactory.calculateItems(skCase, filter); - super.setDisplayName(filter.getDisplayName() + " (" + count + ")"); - } - - @Override - public T accept(DisplayableItemNodeVisitor v) { - return v.visit(this); - } - - @Override - protected Sheet createSheet() { - Sheet s = super.createSheet(); - Sheet.Set ss = s.get(Sheet.PROPERTIES); - if (ss == null) { - ss = Sheet.createPropertiesSet(); - s.put(ss); - } - - ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "FileTypeByExtNode.createSheet.filterType.name"), - NbBundle.getMessage(this.getClass(), "FileTypeByExtNode.createSheet.filterType.displayName"), - NbBundle.getMessage(this.getClass(), "FileTypeByExtNode.createSheet.filterType.desc"), - filter.getDisplayName())); - String extensions = ""; - for (String ext : filter.getFilter()) { - extensions += "'" + ext + "', "; - } - extensions = extensions.substring(0, extensions.lastIndexOf(',')); - ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "FileTypeByExtNode.createSheet.fileExt.name"), - NbBundle.getMessage(this.getClass(), "FileTypeByExtNode.createSheet.fileExt.displayName"), - NbBundle.getMessage(this.getClass(), "FileTypeByExtNode.createSheet.fileExt.desc"), - extensions)); - - return s; - } - - @Override - public boolean isLeafTypeNode() { - return true; - } - - /** - * Consider allowing different configurations for Images, Videos, etc - * (in which case we'd return getClass().getName() + filter.getName() - * for all filters). - */ - @Override - public String getItemType() { - return DisplayableItemNode.FILE_PARENT_NODE_KEY; - } - - /** - * Child node factory for a specific file type - does the database query. - */ - private static class FileTypeChildFactory extends ChildFactory.Detachable { - - private final SleuthkitCase skCase; - private final FileTypeExtensionFilters.SearchFilterInterface filter; - private final static Logger LOGGER = Logger.getLogger(FileTypeChildFactory.class.getName()); - private final Observable notifier; - - // use the constructor that gets an observable passed in for updates - @Deprecated - FileTypeChildFactory(FileTypeExtensionFilters.SearchFilterInterface filter, SleuthkitCase skCase) { - super(); - this.filter = filter; - this.skCase = skCase; - notifier = null; - } - - /** - * - * @param filter Extensions to display - * @param skCase - * @param o Observable that will notify when there could be new - * data to display - */ - private FileTypeChildFactory(FileTypeExtensionFilters.SearchFilterInterface filter, SleuthkitCase skCase, Observable o) { - super(); - this.filter = filter; - this.skCase = skCase; - notifier = o; - } - - @Override - protected void addNotify() { - if (notifier != null) { - notifier.addObserver(observer); - } - } - - @Override - protected void removeNotify() { - if (notifier != null) { - notifier.deleteObserver(observer); - } - } - private final Observer observer = new FileTypeChildFactoryObserver(); - - // Cause refresh of children if there are changes - private class FileTypeChildFactoryObserver implements Observer { - - @Override - public void update(Observable o, Object arg) { - refresh(true); - } - } - - /** - * Get children count without actually loading all nodes - * - * @return - */ - private static long calculateItems(SleuthkitCase sleuthkitCase, FileTypeExtensionFilters.SearchFilterInterface filter) { - try { - return sleuthkitCase.countFilesWhere(createQuery(filter)); - } catch (TskCoreException ex) { - LOGGER.log(Level.SEVERE, "Error getting file search view count", ex); //NON-NLS - return 0; - } - } - - @Override - protected boolean createKeys(List list) { - try { - List files = skCase.findAllFilesWhere(createQuery(filter)); - list.addAll(files); - } catch (TskCoreException ex) { - LOGGER.log(Level.SEVERE, "Couldn't get search results", ex); //NON-NLS - } - return true; - } - - private static String createQuery(FileTypeExtensionFilters.SearchFilterInterface filter) { - StringBuilder query = new StringBuilder(); - query.append("(dir_type = ").append(TskData.TSK_FS_NAME_TYPE_ENUM.REG.getValue()).append(")"); //NON-NLS - if (UserPreferences.hideKnownFilesInViewsTree()) { - query.append(" AND (known IS NULL OR known != ").append(TskData.FileKnown.KNOWN.getFileKnownValue()).append(")"); //NON-NLS - } - query.append(" AND (NULL"); //NON-NLS - for (String s : filter.getFilter()) { - query.append(" OR LOWER(name) LIKE LOWER('%").append(s).append("')"); //NON-NLS - } - query.append(')'); - return query.toString(); - } - - @Override - protected Node createNodeForKey(Content key) { - return key.accept(new ContentVisitor.Default() { - @Override - public FileNode visit(File f) { - return new FileNode(f, false); - } - - @Override - public DirectoryNode visit(Directory d) { - return new DirectoryNode(d); - } - - @Override - public LayoutFileNode visit(LayoutFile lf) { - return new LayoutFileNode(lf); - } - - @Override - public LocalFileNode visit(DerivedFile df) { - return new LocalFileNode(df); - } - - @Override - public LocalFileNode visit(LocalFile lf) { - return new LocalFileNode(lf); - } - - @Override - protected AbstractNode defaultVisit(Content di) { - throw new UnsupportedOperationException(NbBundle.getMessage(this.getClass(), "FileTypeChildren.exception.notSupported.msg", di.toString())); - } - }); - } - } -} diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtNode.java index be76722876..4e40ab9bc4 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtNode.java @@ -24,6 +24,9 @@ import java.beans.PropertyChangeListener; import java.util.Arrays; import java.util.List; import java.util.Observable; +import java.util.Observer; +import java.util.logging.Level; +import org.openide.nodes.AbstractNode; import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; import org.openide.nodes.Node; @@ -31,8 +34,20 @@ import org.openide.nodes.Sheet; import org.openide.util.NbBundle; import org.openide.util.lookup.Lookups; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.core.UserPreferences; +import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.ingest.IngestManager; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.ContentVisitor; +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.SleuthkitCase; +import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.TskData; /** * Node for root of file types view. Children are nodes for specific types. @@ -41,14 +56,15 @@ class FileTypesByExtNode extends DisplayableItemNode { private static final String FNAME = NbBundle.getMessage(FileTypesByExtNode.class, "FileTypesByExtNode.fname.text"); private final FileTypeExtensionFilters.RootFilter filter; + /** * * @param skCase * @param filter null to display root node of file type tree, pass in - * something to provide a sub-node. + * something to provide a sub-node. */ FileTypesByExtNode(SleuthkitCase skCase, FileTypeExtensionFilters.RootFilter filter) { - super(Children.create(new FileTypesByExtChildren(skCase, filter, null), true), Lookups.singleton(filter == null ? FNAME : filter.getName())); + super(Children.create(new ByExtChildren(skCase, filter, null), true), Lookups.singleton(filter == null ? FNAME : filter.getName())); this.filter = filter; init(); } @@ -57,11 +73,11 @@ class FileTypesByExtNode extends DisplayableItemNode { * * @param skCase * @param filter - * @param o Observable that was created by a higher-level node that - * provides updates on events + * @param o Observable that was created by a higher-level node that provides + * updates on events */ private FileTypesByExtNode(SleuthkitCase skCase, FileTypeExtensionFilters.RootFilter filter, Observable o) { - super(Children.create(new FileTypesByExtChildren(skCase, filter, o), true), Lookups.singleton(filter == null ? FNAME : filter.getName())); + super(Children.create(new ByExtChildren(skCase, filter, o), true), Lookups.singleton(filter == null ? FNAME : filter.getName())); this.filter = filter; init(); } @@ -108,21 +124,23 @@ class FileTypesByExtNode extends DisplayableItemNode { @Override public String getItemType() { /** - * Because Documents and Executable are further expandable, their - * column order settings should be stored separately. + * Because Documents and Executable are further expandable, their column + * order settings should be stored separately. */ - if(filter == null) + if (filter == null) { return getClass().getName(); - if (filter.equals(FileTypeExtensionFilters.RootFilter.TSK_DOCUMENT_FILTER) || - filter.equals(FileTypeExtensionFilters.RootFilter.TSK_EXECUTABLE_FILTER)) + } + if (filter.equals(FileTypeExtensionFilters.RootFilter.TSK_DOCUMENT_FILTER) + || filter.equals(FileTypeExtensionFilters.RootFilter.TSK_EXECUTABLE_FILTER)) { return getClass().getName() + filter.getName(); + } return getClass().getName(); } /** * */ - private static class FileTypesByExtChildren extends ChildFactory { + private static class ByExtChildren extends ChildFactory { private final SleuthkitCase skCase; private final FileTypeExtensionFilters.RootFilter filter; @@ -132,15 +150,15 @@ class FileTypesByExtNode extends DisplayableItemNode { * * @param skCase * @param filter Is null for root node - * @param o Observable that provides updates based on events being - * fired (or null if one needs to be created) + * @param o Observable that provides updates based on events being fired + * (or null if one needs to be created) */ - private FileTypesByExtChildren(SleuthkitCase skCase, FileTypeExtensionFilters.RootFilter filter, Observable o) { + private ByExtChildren(SleuthkitCase skCase, FileTypeExtensionFilters.RootFilter filter, Observable o) { super(); this.skCase = skCase; this.filter = filter; if (o == null) { - this.notifier = new FileTypesByExtChildrenObservable(); + this.notifier = new ByExtChildrenObservable(); } else { this.notifier = o; } @@ -150,9 +168,9 @@ class FileTypesByExtNode extends DisplayableItemNode { * Listens for case and ingest invest. Updates observers when events are * fired. FileType and FileTypes nodes are all listening to this. */ - private final class FileTypesByExtChildrenObservable extends Observable { + private final class ByExtChildrenObservable extends Observable { - private FileTypesByExtChildrenObservable() { + private ByExtChildrenObservable() { IngestManager.getInstance().addIngestJobEventListener(pcl); IngestManager.getInstance().addIngestModuleEventListener(pcl); Case.addPropertyChangeListener(pcl); @@ -224,7 +242,231 @@ class FileTypesByExtNode extends DisplayableItemNode { } else if (key.getName().equals(FileTypeExtensionFilters.RootFilter.TSK_EXECUTABLE_FILTER.getName())) { return new FileTypesByExtNode(skCase, FileTypeExtensionFilters.RootFilter.TSK_EXECUTABLE_FILTER, notifier); } else { - return new FileTypeByExtNode(key, skCase, notifier); + return new ByExtNode(key, skCase, notifier); + } + } + } + + /** + * Node for a specific file type / extension. Children of it will be the + * files of that type. + */ + static class ByExtNode extends DisplayableItemNode { + + FileTypeExtensionFilters.SearchFilterInterface filter; + SleuthkitCase skCase; + + /** + * + * @param filter Extensions that will be shown for this node + * @param skCase + * @param o Observable that sends updates when the child factories + * should refresh + */ + ByExtNode(FileTypeExtensionFilters.SearchFilterInterface filter, SleuthkitCase skCase, Observable o) { + super(Children.create(new ByExtChildFactory(filter, skCase, o), true), Lookups.singleton(filter.getDisplayName())); + this.filter = filter; + this.skCase = skCase; + init(); + o.addObserver(new ByExtNodeObserver()); + } + + private void init() { + super.setName(filter.getName()); + updateDisplayName(); + this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file-filter-icon.png"); //NON-NLS + } + + // update the display name when new events are fired + private class ByExtNodeObserver implements Observer { + + @Override + public void update(Observable o, Object arg) { + updateDisplayName(); + } + } + + private void updateDisplayName() { + final long count = ByExtChildFactory.calculateItems(skCase, filter); + super.setDisplayName(filter.getDisplayName() + " (" + count + ")"); + } + + @Override + public T accept(DisplayableItemNodeVisitor v) { + return v.visit(this); + } + + @Override + protected Sheet createSheet() { + Sheet s = super.createSheet(); + Sheet.Set ss = s.get(Sheet.PROPERTIES); + if (ss == null) { + ss = Sheet.createPropertiesSet(); + s.put(ss); + } + + ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.filterType.name"), + NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.filterType.displayName"), + NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.filterType.desc"), + filter.getDisplayName())); + String extensions = ""; + for (String ext : filter.getFilter()) { + extensions += "'" + ext + "', "; + } + extensions = extensions.substring(0, extensions.lastIndexOf(',')); + ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.fileExt.name"), + NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.fileExt.displayName"), + NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.fileExt.desc"), + extensions)); + + return s; + } + + @Override + public boolean isLeafTypeNode() { + return true; + } + + /** + * Consider allowing different configurations for Images, Videos, etc + * (in which case we'd return getClass().getName() + filter.getName() + * for all filters). + */ + @Override + public String getItemType() { + return DisplayableItemNode.FILE_PARENT_NODE_KEY; + } + + /** + * Child node factory for a specific file type - does the database + * query. + */ + private static class ByExtChildFactory extends ChildFactory.Detachable { + + private final SleuthkitCase skCase; + private final FileTypeExtensionFilters.SearchFilterInterface filter; + private final static Logger LOGGER = Logger.getLogger(ByExtChildFactory.class.getName()); + private final Observable notifier; + + // use the constructor that gets an observable passed in for updates + @Deprecated + ByExtChildFactory(FileTypeExtensionFilters.SearchFilterInterface filter, SleuthkitCase skCase) { + super(); + this.filter = filter; + this.skCase = skCase; + notifier = null; + } + + /** + * + * @param filter Extensions to display + * @param skCase + * @param o Observable that will notify when there could be new data + * to display + */ + private ByExtChildFactory(FileTypeExtensionFilters.SearchFilterInterface filter, SleuthkitCase skCase, Observable o) { + super(); + this.filter = filter; + this.skCase = skCase; + notifier = o; + } + + @Override + protected void addNotify() { + if (notifier != null) { + notifier.addObserver(observer); + } + } + + @Override + protected void removeNotify() { + if (notifier != null) { + notifier.deleteObserver(observer); + } + } + private final Observer observer = new FileTypeChildFactoryObserver(); + + // Cause refresh of children if there are changes + private class FileTypeChildFactoryObserver implements Observer { + + @Override + public void update(Observable o, Object arg) { + refresh(true); + } + } + + /** + * Get children count without actually loading all nodes + * + * @return + */ + private static long calculateItems(SleuthkitCase sleuthkitCase, FileTypeExtensionFilters.SearchFilterInterface filter) { + try { + return sleuthkitCase.countFilesWhere(createQuery(filter)); + } catch (TskCoreException ex) { + LOGGER.log(Level.SEVERE, "Error getting file search view count", ex); //NON-NLS + return 0; + } + } + + @Override + protected boolean createKeys(List list) { + try { + List files = skCase.findAllFilesWhere(createQuery(filter)); + list.addAll(files); + } catch (TskCoreException ex) { + LOGGER.log(Level.SEVERE, "Couldn't get search results", ex); //NON-NLS + } + return true; + } + + private static String createQuery(FileTypeExtensionFilters.SearchFilterInterface filter) { + StringBuilder query = new StringBuilder(); + query.append("(dir_type = ").append(TskData.TSK_FS_NAME_TYPE_ENUM.REG.getValue()).append(")"); //NON-NLS + if (UserPreferences.hideKnownFilesInViewsTree()) { + query.append(" AND (known IS NULL OR known != ").append(TskData.FileKnown.KNOWN.getFileKnownValue()).append(")"); //NON-NLS + } + query.append(" AND (NULL"); //NON-NLS + for (String s : filter.getFilter()) { + query.append(" OR LOWER(name) LIKE LOWER('%").append(s).append("')"); //NON-NLS + } + query.append(')'); + return query.toString(); + } + + @Override + protected Node createNodeForKey(Content key) { + return key.accept(new ContentVisitor.Default() { + @Override + public FileNode visit(File f) { + return new FileNode(f, false); + } + + @Override + public DirectoryNode visit(Directory d) { + return new DirectoryNode(d); + } + + @Override + public LayoutFileNode visit(LayoutFile lf) { + return new LayoutFileNode(lf); + } + + @Override + public LocalFileNode visit(DerivedFile df) { + return new LocalFileNode(df); + } + + @Override + public LocalFileNode visit(LocalFile lf) { + return new LocalFileNode(lf); + } + + @Override + protected AbstractNode defaultVisit(Content di) { + throw new UnsupportedOperationException(NbBundle.getMessage(this.getClass(), "FileTypeChildren.exception.notSupported.msg", di.toString())); + } + }); } } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java index 8548904601..0bfc95660e 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java @@ -57,7 +57,7 @@ import org.sleuthkit.datamodel.TskData; * Listener which is checking for changes in IngestJobEvent Completed or * Cancelled and IngestModuleEvent Content Changed. */ -class FileTypesByMimeType extends Observable implements AutopsyVisitableItem { +public final class FileTypesByMimeType extends Observable implements AutopsyVisitableItem { private final SleuthkitCase SKCASE; /** @@ -167,315 +167,334 @@ class FileTypesByMimeType extends Observable implements AutopsyVisitableItem { return v.visit(this); } -/** - * Class which represents the root node of the "By MIME Type" tree, will have - * children of each media type present in the database or no children when the - * file detection module has not been run and MIME type is currently unknown. - */ -class ByMimeTypeNode extends DisplayableItemNode { - - @NbBundle.Messages("FileTypesByMimeType.name.text=By MIME Type") - final String NAME = Bundle.FileTypesByMimeType_name_text(); - - ByMimeTypeNode() { - super(Children.create(new ByMimeTypeNodeChildren(), true)); - super.setName(NAME); - super.setDisplayName(NAME); - this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file_types.png"); - } - - @Override - public boolean isLeafTypeNode() { - return false; - } - - @Override - public T accept(DisplayableItemNodeVisitor v) { - return v.visit(this); - } - - @Override - public String getItemType() { - return getClass().getName(); - } - - boolean isEmpty() { - return existingMimeTypes.isEmpty(); - } - -} - -/** - * Creates the children for the "By MIME Type" node these children will each - * represent a distinct media type present in the DB - */ -private class ByMimeTypeNodeChildren extends ChildFactory implements Observer { - - private ByMimeTypeNodeChildren() { - super(); - addObserver(this); - } - - @Override - protected boolean createKeys(List mediaTypeNodes) { - if (!existingMimeTypes.isEmpty()) { - mediaTypeNodes.addAll(getMediaTypeList()); + /** + * Method to check if the node in question is a ByMimeTypeNode which is + * empty. + * + * @param originNode the Node which you wish to check. + * @return True if originNode is an instance of ByMimeTypeNode and is empty, + * false otherwise. + */ + public static boolean isEmptyMimeTypeNode(Node originNode) { + boolean isEmptyMimeNode = false; + if (originNode instanceof FileTypesByMimeType.ByMimeTypeNode && ((FileTypesByMimeType.ByMimeTypeNode) originNode).isEmpty()) { + isEmptyMimeNode = true; } - return true; - } - - @Override - protected Node createNodeForKey(String key) { - return new MediaTypeNode(key); - } - - @Override - public void update(Observable o, Object arg) { - refresh(true); - } - -} - -/** - * The Media type node created by the ByMimeTypeNodeChildren and - contains one of the unique media types present in the database for this case. - */ -class MediaTypeNode extends DisplayableItemNode { - - MediaTypeNode(String name) { - super(Children.create(new MediaTypeNodeChildren(name), true)); - setName(name); - setDisplayName(name); - this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file_types.png"); - } - - @Override - public boolean isLeafTypeNode() { - return false; - } - - @Override - public T accept(DisplayableItemNodeVisitor v) { - return v.visit(this); - } - - @Override - public String getItemType() { - return getClass().getName(); - } - -} - -/** - * Creates children for media type nodes, children will be MediaSubTypeNodes and - * represent one of the subtypes which are present in the database of their - * media type. - */ -private class MediaTypeNodeChildren extends ChildFactory implements Observer { - - String mediaType; - - MediaTypeNodeChildren(String name) { - addObserver(this); - this.mediaType = name; - } - - @Override - protected boolean createKeys(List mediaTypeNodes) { - mediaTypeNodes.addAll(existingMimeTypes.get(mediaType)); - return true; - } - - @Override - protected Node createNodeForKey(String subtype) { - String mimeType = mediaType + "/" + subtype; - return new MediaSubTypeNode(mimeType); - } - - @Override - public void update(Observable o, Object arg) { - refresh(true); - } - -} - -/** - * Node which represents the media sub type in the By MIME type tree, the media - * subtype is the portion of the MIME type following the /. - */ -class MediaSubTypeNode extends DisplayableItemNode implements Observer { - - private MediaSubTypeNode(String mimeType) { - super(Children.create(new MediaSubTypeNodeChildren(mimeType), true)); - addObserver(this); - init(mimeType); - } - - private void init(String mimeType) { - super.setName(mimeType); - updateDisplayName(mimeType); - this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file-filter-icon.png"); //NON-NLS + return isEmptyMimeNode; } /** - * Updates the display name of the mediaSubTypeNode to include the count of - * files which it represents. - * - * @param mimeType - the complete MimeType, needed for accurate query - * results + * Class which represents the root node of the "By MIME Type" tree, will + * have children of each media type present in the database or no children + * when the file detection module has not been run and MIME type is + * currently unknown. */ - private void updateDisplayName(String mimeType) { + class ByMimeTypeNode extends DisplayableItemNode { - final long count = new MediaSubTypeNodeChildren(mimeType).calculateItems(SKCASE, mimeType); + @NbBundle.Messages("FileTypesByMimeType.name.text=By MIME Type") + final String NAME = Bundle.FileTypesByMimeType_name_text(); + + ByMimeTypeNode() { + super(Children.create(new ByMimeTypeNodeChildren(), true)); + super.setName(NAME); + super.setDisplayName(NAME); + this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file_types.png"); + } + + @Override + public boolean isLeafTypeNode() { + return false; + } + + @Override + public T accept(DisplayableItemNodeVisitor v) { + return v.visit(this); + } + + @Override + public String getItemType() { + return getClass().getName(); + } + + boolean isEmpty() { + return existingMimeTypes.isEmpty(); + } - super.setDisplayName(mimeType.split("/")[1] + " (" + count + ")"); } /** - * This returns true because any MediaSubTypeNode that exists is going to be - * a bottom level node in the Tree view on the left of Autopsy. - * - * @return true + * Creates the children for the "By MIME Type" node these children will each + * represent a distinct media type present in the DB */ - @Override - public boolean isLeafTypeNode() { - return true; - } + private class ByMimeTypeNodeChildren extends ChildFactory implements Observer { - @Override - public T accept(DisplayableItemNodeVisitor v) { - return v.visit(this); - } + private ByMimeTypeNodeChildren() { + super(); + addObserver(this); + } - @Override - public String getItemType() { - return getClass().getName(); - } + @Override + protected boolean createKeys(List mediaTypeNodes) { + if (!existingMimeTypes.isEmpty()) { + mediaTypeNodes.addAll(getMediaTypeList()); + } + return true; + } - @Override - public void update(Observable o, Object arg) { - updateDisplayName(getName()); - } -} + @Override + protected Node createNodeForKey(String key) { + return new MediaTypeNode(key); + } -/** - * Factory for populating the contents of the Media Sub Type Node with the files - * that match MimeType which is represented by this position in the tree. - */ -private class MediaSubTypeNodeChildren extends ChildFactory.Detachable implements Observer { + @Override + public void update(Observable o, Object arg) { + refresh(true); + } - private final String mimeType; - - private MediaSubTypeNodeChildren(String mimeType) { - super(); - addObserver(this); - this.mimeType = mimeType; } /** - * Get children count without actually loading all nodes - * - * @return count(*) - the number of items that will be shown in this items - * Directory Listing + * The Media type node created by the ByMimeTypeNodeChildren and contains + * one of the unique media types present in the database for this case. */ - private long calculateItems(SleuthkitCase sleuthkitCase, String mime_type) { - try { - return sleuthkitCase.countFilesWhere(createQuery(mime_type)); - } catch (TskCoreException ex) { - LOGGER.log(Level.SEVERE, "Error getting file search view count", ex); //NON-NLS - return 0; + class MediaTypeNode extends DisplayableItemNode { + + MediaTypeNode(String name) { + super(Children.create(new MediaTypeNodeChildren(name), true)); + setName(name); + setDisplayName(name); + this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file_types.png"); + } + + @Override + public boolean isLeafTypeNode() { + return false; + } + + @Override + public T accept(DisplayableItemNodeVisitor v) { + return v.visit(this); + } + + @Override + public String getItemType() { + return getClass().getName(); + } + + } + + /** + * Creates children for media type nodes, children will be MediaSubTypeNodes + * and represent one of the subtypes which are present in the database of + * their media type. + */ + private class MediaTypeNodeChildren extends ChildFactory implements Observer { + + String mediaType; + + MediaTypeNodeChildren(String name) { + addObserver(this); + this.mediaType = name; + } + + @Override + protected boolean createKeys(List mediaTypeNodes) { + mediaTypeNodes.addAll(existingMimeTypes.get(mediaType)); + return true; + } + + @Override + protected Node createNodeForKey(String subtype) { + String mimeType = mediaType + "/" + subtype; + return new MediaSubTypeNode(mimeType); + } + + @Override + public void update(Observable o, Object arg) { + refresh(true); + } + + } + + /** + * Node which represents the media sub type in the By MIME type tree, the + * media subtype is the portion of the MIME type following the /. + */ + class MediaSubTypeNode extends DisplayableItemNode implements Observer { + + private MediaSubTypeNode(String mimeType) { + super(Children.create(new MediaSubTypeNodeChildren(mimeType), true)); + addObserver(this); + init(mimeType); + } + + private void init(String mimeType) { + super.setName(mimeType); + updateDisplayName(mimeType); + this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file-filter-icon.png"); //NON-NLS + } + + /** + * Updates the display name of the mediaSubTypeNode to include the count + * of files which it represents. + * + * @param mimeType - the complete MimeType, needed for accurate query + * results + */ + private void updateDisplayName(String mimeType) { + + final long count = new MediaSubTypeNodeChildren(mimeType).calculateItems(SKCASE, mimeType); + + super.setDisplayName(mimeType.split("/")[1] + " (" + count + ")"); + } + + /** + * This returns true because any MediaSubTypeNode that exists is going + * to be a bottom level node in the Tree view on the left of Autopsy. + * + * @return true + */ + @Override + public boolean isLeafTypeNode() { + return true; + } + + @Override + public T accept(DisplayableItemNodeVisitor v) { + return v.visit(this); + } + + @Override + public String getItemType() { + return getClass().getName(); + } + + @Override + public void update(Observable o, Object arg) { + updateDisplayName(getName()); } } /** - * Uses the createQuery method to complete the query, Select * from - * tsk_files WHERE. The results from the database will contain the files - * which match this mime type and their information. - * - * @param list - will contain all files and their attributes from the - * tsk_files table where mime_type matches the one specified - * @return true + * Factory for populating the contents of the Media Sub Type Node with the + * files that match MimeType which is represented by this position in the + * tree. */ - @Override - protected boolean createKeys(List list) { - try { - List files = SKCASE.findAllFilesWhere(createQuery(mimeType)); - list.addAll(files); - } catch (TskCoreException ex) { - LOGGER.log(Level.SEVERE, "Couldn't get search results", ex); //NON-NLS + private class MediaSubTypeNodeChildren extends ChildFactory.Detachable implements Observer { + + private final String mimeType; + + private MediaSubTypeNodeChildren(String mimeType) { + super(); + addObserver(this); + this.mimeType = mimeType; } - return true; - } - /** - * Create the portion of the query following WHERE for a query of the - * database for each file which matches the complete MIME type represented - * by this node. Matches against the mime_type column in tsk_files. - * - * @param mimeType - the complete mimetype of the file mediatype/subtype - * @return query.toString - portion of SQL query which will follow a WHERE - * clause. - */ - private String createQuery(String mime_type) { - StringBuilder query = new StringBuilder(); - query.append("(dir_type = ").append(TskData.TSK_FS_NAME_TYPE_ENUM.REG.getValue()).append(")"); //NON-NLS - query.append(" AND (type IN (").append(TskData.TSK_DB_FILES_TYPE_ENUM.FS.ordinal()).append(","); //NON-NLS - query.append(TskData.TSK_DB_FILES_TYPE_ENUM.CARVED.ordinal()).append(","); - query.append(TskData.TSK_DB_FILES_TYPE_ENUM.DERIVED.ordinal()).append(","); - query.append(TskData.TSK_DB_FILES_TYPE_ENUM.LOCAL.ordinal()).append("))"); - if (UserPreferences.hideKnownFilesInViewsTree()) { - query.append(" AND (known IS NULL OR known != ").append(TskData.FileKnown.KNOWN.getFileKnownValue()).append(")"); //NON-NLS + /** + * Get children count without actually loading all nodes + * + * @return count(*) - the number of items that will be shown in this + * items Directory Listing + */ + private long calculateItems(SleuthkitCase sleuthkitCase, String mime_type) { + try { + return sleuthkitCase.countFilesWhere(createQuery(mime_type)); + } catch (TskCoreException ex) { + LOGGER.log(Level.SEVERE, "Error getting file search view count", ex); //NON-NLS + return 0; + } + } + + /** + * Uses the createQuery method to complete the query, Select * from + * tsk_files WHERE. The results from the database will contain the files + * which match this mime type and their information. + * + * @param list - will contain all files and their attributes from the + * tsk_files table where mime_type matches the one specified + * @return true + */ + @Override + protected boolean createKeys(List list) { + try { + List files = SKCASE.findAllFilesWhere(createQuery(mimeType)); + list.addAll(files); + } catch (TskCoreException ex) { + LOGGER.log(Level.SEVERE, "Couldn't get search results", ex); //NON-NLS + } + return true; + } + + /** + * Create the portion of the query following WHERE for a query of the + * database for each file which matches the complete MIME type + * represented by this node. Matches against the mime_type column in + * tsk_files. + * + * @param mimeType - the complete mimetype of the file mediatype/subtype + * @return query.toString - portion of SQL query which will follow a + * WHERE clause. + */ + private String createQuery(String mime_type) { + StringBuilder query = new StringBuilder(); + query.append("(dir_type = ").append(TskData.TSK_FS_NAME_TYPE_ENUM.REG.getValue()).append(")"); //NON-NLS + query.append(" AND (type IN (").append(TskData.TSK_DB_FILES_TYPE_ENUM.FS.ordinal()).append(","); //NON-NLS + query.append(TskData.TSK_DB_FILES_TYPE_ENUM.CARVED.ordinal()).append(","); + query.append(TskData.TSK_DB_FILES_TYPE_ENUM.DERIVED.ordinal()).append(","); + query.append(TskData.TSK_DB_FILES_TYPE_ENUM.LOCAL.ordinal()).append("))"); + if (UserPreferences.hideKnownFilesInViewsTree()) { + query.append(" AND (known IS NULL OR known != ").append(TskData.FileKnown.KNOWN.getFileKnownValue()).append(")"); //NON-NLS + } + query.append(" AND mime_type = '").append(mime_type).append("'"); //NON-NLS + return query.toString(); + } + + @Override + public void update(Observable o, Object arg) { + refresh(true); + } + + /** + * Creates the content to populate the Directory Listing Table view for + * each file + * + * @param key + * @return + */ + @Override + protected Node createNodeForKey(Content key) { + return key.accept(new ContentVisitor.Default() { + @Override + public FileNode visit(File f) { + return new FileNode(f, false); + } + + @Override + public DirectoryNode visit(Directory d) { + return new DirectoryNode(d); + } + + @Override + public LayoutFileNode visit(LayoutFile lf) { + return new LayoutFileNode(lf); + } + + @Override + public LocalFileNode visit(DerivedFile df) { + return new LocalFileNode(df); + } + + @Override + public LocalFileNode visit(LocalFile lf) { + return new LocalFileNode(lf); + } + + @Override + protected AbstractNode defaultVisit(Content di) { + throw new UnsupportedOperationException(NbBundle.getMessage(this.getClass(), "FileTypeChildren.exception.notSupported.msg", di.toString())); + } + }); } - query.append(" AND mime_type = '").append(mime_type).append("'"); //NON-NLS - return query.toString(); } - @Override - public void update(Observable o, Object arg) { - refresh(true); - } - - /** - * Creates the content to populate the Directory Listing Table view for each - * file - * - * @param key - * @return - */ - @Override - protected Node createNodeForKey(Content key) { - return key.accept(new ContentVisitor.Default() { - @Override - public FileNode visit(File f) { - return new FileNode(f, false); - } - - @Override - public DirectoryNode visit(Directory d) { - return new DirectoryNode(d); - } - - @Override - public LayoutFileNode visit(LayoutFile lf) { - return new LayoutFileNode(lf); - } - - @Override - public LocalFileNode visit(DerivedFile df) { - return new LocalFileNode(df); - } - - @Override - public LocalFileNode visit(LocalFile lf) { - return new LocalFileNode(lf); - } - - @Override - protected AbstractNode defaultVisit(Content di) { - throw new UnsupportedOperationException(NbBundle.getMessage(this.getClass(), "FileTypeChildren.exception.notSupported.msg", di.toString())); - } - }); - } -} - } diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java index a57f85fb68..496831fa88 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java @@ -63,6 +63,7 @@ import org.sleuthkit.autopsy.datamodel.DataSources; import org.sleuthkit.autopsy.datamodel.DataSourcesNode; import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; import org.sleuthkit.autopsy.datamodel.ExtractedContent; +import org.sleuthkit.autopsy.datamodel.FileTypesByMimeType; import org.sleuthkit.autopsy.datamodel.KeywordHits; import org.sleuthkit.autopsy.datamodel.KnownFileFilterNode; import org.sleuthkit.autopsy.datamodel.Reports; @@ -134,7 +135,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat refreshContentTreeSafe(); break; case UserPreferences.HIDE_KNOWN_FILES_IN_VIEWS_TREE: - case UserPreferences.HIDE_SLACK_FILES_IN_VIEWS_TREE: + case UserPreferences.HIDE_SLACK_FILES_IN_VIEWS_TREE: // TODO: Need a way to refresh the Views subtree break; } @@ -602,7 +603,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat } } } - + @NbBundle.Messages("DirectoryTreeTopComponent.emptyMimeNode.text=Data not available. Run file type identification module.") /** * Event handler to run when selection changed @@ -645,12 +646,10 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat Node kffn = new KnownFileFilterNode(drfn, KnownFileFilterNode.getSelectionContext(originNode)); Node sffn = new SlackFileFilterNode(kffn, SlackFileFilterNode.getSelectionContext(originNode)); - // Create a TableFilterNode with knowledge of the node's type to allow for column order settings - //Special case for when File Type Identification has not yet been run and //there are no mime types to populate Files by Mime Type Tree - if (EmptyNode.isEmptyMimeTypeNode(originNode)) { + if (FileTypesByMimeType.isEmptyMimeTypeNode(originNode)) { EmptyNode emptyNode = new EmptyNode(Bundle.DirectoryTreeTopComponent_emptyMimeNode_text()); Node emptyDrfn = new DataResultFilterNode(emptyNode, DirectoryTreeTopComponent.this.em); Node emptyKffn = new KnownFileFilterNode(emptyDrfn, KnownFileFilterNode.getSelectionContext(emptyNode)); From 155b1cead278f1cc82fdb261eecbc18e8c2cb811 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Fri, 2 Dec 2016 13:04:16 -0500 Subject: [PATCH 05/16] 1917 Modified documentation for EmptyNode class --- .../autopsy/datamodel/EmptyNode.java | 30 ++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/EmptyNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/EmptyNode.java index 39eb01b4e1..ee8de2a452 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/EmptyNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/EmptyNode.java @@ -1,7 +1,20 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * Autopsy Forensic Browser + * + * Copyright 2016 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; @@ -12,14 +25,17 @@ import org.openide.nodes.Children; import org.openide.nodes.Node; /** - * EmptyNode Class made for edge case where no mime exist in the database yet. - * Creates a node to display information on why the tree is empty. + * EmptyNode Class made for when you need to display a node with with text in the table view + * but no children in the tree view. * - * Swapped for the FileTypesByMimeType node in - * DirectoryTreeTopComponent.respondSelection */ public final class EmptyNode extends AbstractNode { + /** + * Creates an EmptyNode + * + * @param displayedMessage the text you would like displayed in the table view, this will not appear as a child node. + */ public EmptyNode(String displayedMessage) { super(Children.create(new EmptyChildFactory(displayedMessage), true)); From efee3816f6c25e28a4477459f7cd1d5e7ae98071 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Mon, 5 Dec 2016 17:57:30 -0500 Subject: [PATCH 06/16] 1917-Corrected variable name for skCase --- .../autopsy/datamodel/FileTypesByMimeType.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java index 0bfc95660e..b44d3c19d6 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java @@ -59,7 +59,7 @@ import org.sleuthkit.datamodel.TskData; */ public final class FileTypesByMimeType extends Observable implements AutopsyVisitableItem { - private final SleuthkitCase SKCASE; + private final SleuthkitCase skCase; /** * The nodes of this tree will be determined dynamically by the mimetypes * which exist in the database. This hashmap will store them with the media @@ -128,11 +128,11 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi existingMimeTypes.clear(); } - if (SKCASE == null) { + if (skCase == null) { return; } - try (SleuthkitCase.CaseDbQuery dbQuery = SKCASE.executeQuery(allDistinctMimeTypesQuery.toString())) { + try (SleuthkitCase.CaseDbQuery dbQuery = skCase.executeQuery(allDistinctMimeTypesQuery.toString())) { ResultSet resultSet = dbQuery.getResultSet(); synchronized (existingMimeTypes) { while (resultSet.next()) { @@ -158,7 +158,7 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi FileTypesByMimeType(SleuthkitCase skCase) { IngestManager.getInstance().addIngestJobEventListener(pcl); IngestManager.getInstance().addIngestModuleEventListener(pcl); - this.SKCASE = skCase; + this.skCase = skCase; populateHashMap(); } @@ -343,7 +343,7 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi */ private void updateDisplayName(String mimeType) { - final long count = new MediaSubTypeNodeChildren(mimeType).calculateItems(SKCASE, mimeType); + final long count = new MediaSubTypeNodeChildren(mimeType).calculateItems(skCase, mimeType); super.setDisplayName(mimeType.split("/")[1] + " (" + count + ")"); } @@ -417,7 +417,7 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi @Override protected boolean createKeys(List list) { try { - List files = SKCASE.findAllFilesWhere(createQuery(mimeType)); + List files = skCase.findAllFilesWhere(createQuery(mimeType)); list.addAll(files); } catch (TskCoreException ex) { LOGGER.log(Level.SEVERE, "Couldn't get search results", ex); //NON-NLS From 5290f1d211812b62fdcd6b7ce41b074c4a24e168 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Mon, 5 Dec 2016 18:14:22 -0500 Subject: [PATCH 07/16] 1917-Added back mistakenly removed japanese bundle properties --- .../org/sleuthkit/autopsy/datamodel/Bundle_ja.properties | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle_ja.properties index 51970be2ec..4dfb781a45 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle_ja.properties @@ -112,6 +112,15 @@ FileTypeExtensionFilters.autDocOfficeFilter.text=\u30aa\u30d5\u30a3\u30b9 FileTypeExtensionFilters.autoDocPdfFilter.text=PDF FileTypeExtensionFilters.autDocTxtFilter.text=\u30d7\u30ec\u30fc\u30f3\u30c6\u30ad\u30b9\u30c8 FileTypeExtensionFilters.autDocRtfFilter.text=\u30ea\u30c3\u30c1\u30c6\u30ad\u30b9\u30c8 +FileTypesByExtNode.createSheet.filterType.name=\u30d5\u30a3\u30eb\u30bf\u30fc\u30bf\u30a4\u30d7 +FileTypesByExtNode.createSheet.filterType.displayName=\u30d5\u30a3\u30eb\u30bf\u30fc\u30bf\u30a4\u30d7 +FileTypesByExtNode.createSheet.filterType.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093 +FileTypesByExtNode.createSheet.fileExt.name=\u30d5\u30a1\u30a4\u30eb\u62e1\u5f35\u5b50 +FileTypesByExtNode.createSheet.fileExt.displayName=\u30d5\u30a1\u30a4\u30eb\u62e1\u5f35\u5b50 +FileTypesByExtNode.createSheet.fileExt.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093 +FileTypesByExtNode.createSheet.name.name=\u540d\u524d +FileTypesByExtNode.createSheet.name.displayName=\u540d\u524d +FileTypesByExtNode.createSheet.name.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093 HashsetHits.createSheet.name.name=\u540d\u524d HashsetHits.createSheet.name.displayName=\u540d\u524d HashsetHits.createSheet.name.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093 From 8f216bf330af4cf98c0706582b4c3fba1441c53c Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Wed, 7 Dec 2016 10:11:40 -0500 Subject: [PATCH 08/16] 1917 Renamed child factory for consistency --- Core/src/org/sleuthkit/autopsy/datamodel/EmptyNode.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/EmptyNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/EmptyNode.java index ee8de2a452..3b552f8b70 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/EmptyNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/EmptyNode.java @@ -37,15 +37,15 @@ public final class EmptyNode extends AbstractNode { * @param displayedMessage the text you would like displayed in the table view, this will not appear as a child node. */ public EmptyNode(String displayedMessage) { - super(Children.create(new EmptyChildFactory(displayedMessage), true)); + super(Children.create(new EmptyNodeChildren(displayedMessage), true)); } - static class EmptyChildFactory extends ChildFactory { + static class EmptyNodeChildren extends ChildFactory { String fileIdMsg; //NON-NLS - private EmptyChildFactory(String displayedMessage) { + private EmptyNodeChildren(String displayedMessage) { fileIdMsg = displayedMessage; } From 83f8d575e941653efe94d88a0406cff5927a6c5f Mon Sep 17 00:00:00 2001 From: esaunders Date: Wed, 7 Dec 2016 16:14:00 -0500 Subject: [PATCH 09/16] Add quotes around the keyword when the search results are not available to make highlighting work correctly. --- .../org/sleuthkit/autopsy/keywordsearch/HighlightedText.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/HighlightedText.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/HighlightedText.java index fe87ac26cc..02b3391b5f 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/HighlightedText.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/HighlightedText.java @@ -84,7 +84,7 @@ class HighlightedText implements IndexedText, TextMarkupLookup { //when the results are not known and need to requery to get hits HighlightedText(long objectId, String solrQuery, boolean isRegex, String originalQuery) { - this(objectId, solrQuery, isRegex); + this(objectId, KeywordSearchUtil.quoteQuery(solrQuery), isRegex); this.originalQuery = originalQuery; } From a782e52f8041a5cc32ca6d9e8c81b10801806100 Mon Sep 17 00:00:00 2001 From: esaunders Date: Wed, 7 Dec 2016 16:17:24 -0500 Subject: [PATCH 10/16] Removed filterOneHitPerDocument() since (a) it's use prevents the display of hits across multiple pages/chunks and (b) QueryResults.writeAllHitsToBlackBoard() takes care of ensuring that only a single blackboard artifact is created per document. --- .../autopsy/keywordsearch/LuceneQuery.java | 36 +------------------ 1 file changed, 1 insertion(+), 35 deletions(-) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java index 0b7941747e..5cdf3ad438 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java @@ -206,7 +206,6 @@ class LuceneQuery implements KeywordSearchQuery { QueryResponse response; SolrDocumentList resultList; Map>> highlightResponse; - Set uniqueSolrDocumentsWithHits; response = solrServer.query(q, METHOD.POST); @@ -215,9 +214,6 @@ class LuceneQuery implements KeywordSearchQuery { // objectId_chunk -> "text" -> List of previews highlightResponse = response.getHighlighting(); - // get the unique set of files with hits - uniqueSolrDocumentsWithHits = filterOneHitPerDocument(resultList); - // cycle through results in sets of MAX_RESULTS for (int start = 0; !allMatchesFetched; start = start + MAX_RESULTS) { q.setStart(start); @@ -232,7 +228,7 @@ class LuceneQuery implements KeywordSearchQuery { return matches; } - for (SolrDocument resultDoc : uniqueSolrDocumentsWithHits) { + for (SolrDocument resultDoc : resultList) { KeywordHit contentHit; try { contentHit = createKeywordtHit(resultDoc, highlightResponse, sleuthkitCase); @@ -297,36 +293,6 @@ class LuceneQuery implements KeywordSearchQuery { return q; } - /** - * Create the minimum set of documents. Ignores chunk IDs. Only one hit per - * file in results. - * - * @param resultList - * - * @return - */ - private Set filterOneHitPerDocument(SolrDocumentList resultList) { - // sort the list so that we consistently pick the same chunk each time. - // note this sort is doing a string comparison and not an integer comparison, so - // chunk 10 will be smaller than chunk 9. - Collections.sort(resultList, new Comparator() { - @Override - public int compare(SolrDocument left, SolrDocument right) { - // ID is in the form of ObjectId_Chunk - String leftID = left.getFieldValue(Server.Schema.ID.toString()).toString(); - String rightID = right.getFieldValue(Server.Schema.ID.toString()).toString(); - return leftID.compareTo(rightID); - } - }); - - // NOTE: We could probably just iterate through the list and compare each ID with the - // previous ID to get the unique documents faster than using this set now that the list - // is sorted. - Set solrDocumentsWithMatches = new TreeSet<>(new SolrDocumentComparatorIgnoresChunkId()); - solrDocumentsWithMatches.addAll(resultList); - return solrDocumentsWithMatches; - } - private KeywordHit createKeywordtHit(SolrDocument solrDoc, Map>> highlightResponse, SleuthkitCase caseDb) throws TskException { /** * Get the first snippet from the document if keyword search is From 231e87187dfc2a1b005e7a413e22d406a5168aa1 Mon Sep 17 00:00:00 2001 From: Ann Priestman Date: Thu, 8 Dec 2016 09:58:31 -0500 Subject: [PATCH 11/16] Add dialog to allow the user to add multiple keywords at a time. --- .../keywordsearch/AddKeywordsDialog.form | 185 +++++++++++ .../keywordsearch/AddKeywordsDialog.java | 314 ++++++++++++++++++ .../autopsy/keywordsearch/Bundle.properties | 22 +- .../keywordsearch/GlobalEditListPanel.form | 30 +- .../keywordsearch/GlobalEditListPanel.java | 172 ++++++---- 5 files changed, 644 insertions(+), 79 deletions(-) create mode 100644 KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/AddKeywordsDialog.form create mode 100644 KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/AddKeywordsDialog.java diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/AddKeywordsDialog.form b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/AddKeywordsDialog.form new file mode 100644 index 0000000000..65cdd9f88d --- /dev/null +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/AddKeywordsDialog.form @@ -0,0 +1,185 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/AddKeywordsDialog.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/AddKeywordsDialog.java new file mode 100644 index 0000000000..0f5d42ca03 --- /dev/null +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/AddKeywordsDialog.java @@ -0,0 +1,314 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2011-2016 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.keywordsearch; + +import java.awt.Dimension; +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.List; +import java.util.ArrayList; +import java.util.Arrays; +import javax.swing.JFrame; +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; +import javax.swing.SwingUtilities; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import org.openide.util.NbBundle; +import org.openide.windows.WindowManager; + +/** + * Dialog to add one or more keywords to a list + */ +class AddKeywordsDialog extends javax.swing.JDialog { + + List newKeywords = new ArrayList<>(); + + /** + * Creates new form AddKeywordsDialog. + * Note that this does not display the dialog - call display() after creation. + * @param initialKeywords Keywords to populate the list with + * @param type Starting keyword type + */ + AddKeywordsDialog(){ + super((JFrame) WindowManager.getDefault().getMainWindow(), + NbBundle.getMessage(AddKeywordsDialog.class, "AddKeywordsDialog.addKeywordsTitle.text"), + true); + initComponents(); + + // Set the add button to only be active when there is text in the text area + addButton.setEnabled(false); + keywordTextArea.getDocument().addDocumentListener(new DocumentListener() { + @Override + public void changedUpdate(DocumentEvent e) { + fire(); + } + @Override + public void removeUpdate(DocumentEvent e) { + fire(); + } + @Override + public void insertUpdate(DocumentEvent e) { + fire(); + } + private void fire() { + enableButtons(); + } + }); + } + + /** + * Display the dialog + */ + void display() { + newKeywords.clear(); + Dimension screenDimension = Toolkit.getDefaultToolkit().getScreenSize(); + setLocation((screenDimension.width - getSize().width) / 2, (screenDimension.height - getSize().height) / 2); + setVisible(true); + } + + /** + * Set the initial contents of the text box. + * Intended to be used to redisplay any keywords that contained errors + * @param initialKeywords + */ + void setInitialKeywordList(String initialKeywords){ + keywordTextArea.setText(initialKeywords); + } + + + private void enableButtons(){ + addButton.setEnabled(! keywordTextArea.getText().isEmpty()); + } + + /** + * Get the list of keywords from the text area + * @return list of keywords + */ + List getKeywords(){ + return newKeywords; + } + + /** + * Get whether the regex option is selected + * @return true if the regex radio button is selected + */ + boolean isKeywordRegex(){ + return regexRadioButton.isSelected(); + } + + /** + * Get whether the exact match option is selected + * @return true if the exact match radio button is selected + */ + boolean isKeywordExact(){ + return exactRadioButton.isSelected(); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + keywordTypeButtonGroup = new javax.swing.ButtonGroup(); + exactRadioButton = new javax.swing.JRadioButton(); + substringRadioButton = new javax.swing.JRadioButton(); + regexRadioButton = new javax.swing.JRadioButton(); + jScrollPane1 = new javax.swing.JScrollPane(); + keywordTextArea = new javax.swing.JTextArea(); + enterKeywordsLabel = new javax.swing.JLabel(); + keywordTypeLabel = new javax.swing.JLabel(); + addButton = new javax.swing.JButton(); + cancelButton = new javax.swing.JButton(); + pasteButton = new javax.swing.JButton(); + + setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + + keywordTypeButtonGroup.add(exactRadioButton); + exactRadioButton.setSelected(true); + org.openide.awt.Mnemonics.setLocalizedText(exactRadioButton, org.openide.util.NbBundle.getMessage(AddKeywordsDialog.class, "AddKeywordsDialog.exactRadioButton.text")); // NOI18N + + keywordTypeButtonGroup.add(substringRadioButton); + org.openide.awt.Mnemonics.setLocalizedText(substringRadioButton, org.openide.util.NbBundle.getMessage(AddKeywordsDialog.class, "AddKeywordsDialog.substringRadioButton.text")); // NOI18N + + keywordTypeButtonGroup.add(regexRadioButton); + org.openide.awt.Mnemonics.setLocalizedText(regexRadioButton, org.openide.util.NbBundle.getMessage(AddKeywordsDialog.class, "AddKeywordsDialog.regexRadioButton.text")); // NOI18N + + keywordTextArea.setColumns(20); + keywordTextArea.setRows(5); + keywordTextArea.addMouseListener(new java.awt.event.MouseAdapter() { + public void mouseClicked(java.awt.event.MouseEvent evt) { + keywordTextAreaMouseClicked(evt); + } + }); + jScrollPane1.setViewportView(keywordTextArea); + + org.openide.awt.Mnemonics.setLocalizedText(enterKeywordsLabel, org.openide.util.NbBundle.getMessage(AddKeywordsDialog.class, "AddKeywordsDialog.enterKeywordsLabel.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(keywordTypeLabel, org.openide.util.NbBundle.getMessage(AddKeywordsDialog.class, "AddKeywordsDialog.keywordTypeLabel.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(addButton, org.openide.util.NbBundle.getMessage(AddKeywordsDialog.class, "AddKeywordsDialog.addButton.text")); // NOI18N + addButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + addButtonActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(cancelButton, org.openide.util.NbBundle.getMessage(AddKeywordsDialog.class, "AddKeywordsDialog.cancelButton.text")); // NOI18N + cancelButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + cancelButtonActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(pasteButton, org.openide.util.NbBundle.getMessage(AddKeywordsDialog.class, "AddKeywordsDialog.pasteButton.text")); // NOI18N + pasteButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + pasteButtonActionPerformed(evt); + } + }); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(enterKeywordsLabel) + .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 249, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(pasteButton)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(addButton, javax.swing.GroupLayout.PREFERRED_SIZE, 84, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(cancelButton, javax.swing.GroupLayout.PREFERRED_SIZE, 84, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(keywordTypeLabel) + .addGroup(layout.createSequentialGroup() + .addGap(10, 10, 10) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(substringRadioButton) + .addComponent(exactRadioButton) + .addComponent(regexRadioButton)))) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addContainerGap() + .addComponent(enterKeywordsLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(keywordTypeLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(exactRadioButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(substringRadioButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(regexRadioButton) + .addGap(194, 194, 194)) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addGap(0, 0, Short.MAX_VALUE) + .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 278, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(5, 5, 5))) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(addButton) + .addComponent(cancelButton)) + .addComponent(pasteButton)) + .addContainerGap()) + ); + + pack(); + }// //GEN-END:initComponents + + private void pasteButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pasteButtonActionPerformed + keywordTextArea.paste(); + }//GEN-LAST:event_pasteButtonActionPerformed + + private void addButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addButtonActionPerformed + // Save the values from the list + newKeywords.addAll(Arrays.asList(keywordTextArea.getText().split("\\r?\\n"))); + + setVisible(false); + dispose(); + }//GEN-LAST:event_addButtonActionPerformed + + private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed + setVisible(false); + dispose(); + }//GEN-LAST:event_cancelButtonActionPerformed + + private void keywordTextAreaMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_keywordTextAreaMouseClicked + if (SwingUtilities.isRightMouseButton(evt)) { + JPopupMenu popup = new JPopupMenu(); + + JMenuItem cutMenu = new JMenuItem("Cut"); // NON-NLS + cutMenu.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + keywordTextArea.cut(); + } + }); + + JMenuItem copyMenu = new JMenuItem("Copy"); // NON-NLS + copyMenu.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + keywordTextArea.copy(); + } + }); + + JMenuItem pasteMenu = new JMenuItem("Paste"); // NON-NLS + pasteMenu.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + keywordTextArea.paste(); + } + }); + + popup.add(cutMenu); + popup.add(copyMenu); + popup.add(pasteMenu); + popup.show(keywordTextArea, evt.getX(), evt.getY()); + } + }//GEN-LAST:event_keywordTextAreaMouseClicked + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton addButton; + private javax.swing.JButton cancelButton; + private javax.swing.JLabel enterKeywordsLabel; + private javax.swing.JRadioButton exactRadioButton; + private javax.swing.JScrollPane jScrollPane1; + private javax.swing.JTextArea keywordTextArea; + private javax.swing.ButtonGroup keywordTypeButtonGroup; + private javax.swing.JLabel keywordTypeLabel; + private javax.swing.JButton pasteButton; + private javax.swing.JRadioButton regexRadioButton; + private javax.swing.JRadioButton substringRadioButton; + // End of variables declaration//GEN-END:variables +} diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties index 2afdc83e03..43ba62c3c6 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties @@ -22,7 +22,7 @@ KeywordSearchEditListPanel.saveListButton.text=Copy List KeywordSearchEditListPanel.addWordField.text= KeywordSearchEditListPanel.addWordButton.text=New keyword KeywordSearchEditListPanel.chRegex.text=Regular Expression -KeywordSearchEditListPanel.deleteWordButton.text=Delete keyword +KeywordSearchEditListPanel.deleteWordButton.text=Delete keywords KeywordSearchEditListPanel.cutMenuItem.text=Cut KeywordSearchEditListPanel.selectAllMenuItem.text=Select All KeywordSearchEditListPanel.pasteMenuItem.text=Paste @@ -30,6 +30,7 @@ KeywordSearchEditListPanel.copyMenuItem.text=Copy KeywordSearchEditListPanel.exportButton.text=Export List KeywordSearchEditListPanel.deleteListButton.text=Delete List KeywordSearchEditListPanel.emptyKeyword.text=Empty keyword +KeywordSearchEditListPanel.errorAddingKeywords.text=Error adding keyword(s) KeywordSearchListsManagementPanel.newListButton.text=New List KeywordSearchListsManagementPanel.importButton.text=Import List KeywordSearchListsViewerPanel.searchAddButton.text=Search @@ -88,7 +89,7 @@ KeywordSearchConfigurationPanel1.customizeComponents.noOwDefaultMsg=Cannot overw KeywordSearchConfigurationPanel1.customizeComponents.kwListExistMsg=Keyword List <{0}> already exists, do you want to replace it? KeywordSearchConfigurationPanel1.customizeComponents.kwListSavedMsg=Keyword List <{0}> saved KeywordSearchEditListPanel.customizeComponents.kwReToolTip=Keyword is a regular expression -KeywordSearchEditListPanel.customizeComponents.addWordToolTip=Add a new word to the keyword search list +KeywordSearchEditListPanel.customizeComponents.addWordToolTip=Add new words to the keyword search list KeywordSearchEditListPanel.customizeComponents.enterNewWordToolTip=Enter a new word or regex to search KeywordSearchEditListPanel.customizeComponents.exportToFile=Export the current keyword list to a file KeywordSearchEditListPanel.customizeComponents.saveCurrentWIthNewNameToolTip=Save the current keyword list with a new name @@ -295,3 +296,20 @@ NewKeywordPanel.exactButton.text=Exact Match NewKeywordPanel.substringButton.text=Substring Match NewKeywordPanel.keywordTextField.text= NewKeywordPanel.newKeywordLabel.text=Enter a new keyword: +AddKeywordsDialog.exactRadioButton.text=Exact Match +AddKeywordsDialog.substringRadioButton.text=Substring Match +AddKeywordsDialog.regexRadioButton.text=Regular Expression +AddKeywordsDialog.keywordTypeLabel.text=Select type for keywords: +AddKeywordsDialog.enterKeywordsLabel.text=Enter keywords (one per line) below: +AddKeywordsDialog.pasteButton.text=Paste From Clipboard +AddKeywordsDialog.addButton.text=OK +AddKeywordsDialog.cancelButton.text=Cancel +AddKeywordsDialog.addKeywordsTitle.text=New keywords +GlobalEditListPanel.newKeywordsButton.text=New keywords +GlobalEditListPanel.addKeywordResults.text=Add keyword results +GlobalEditListPanel.keywordsAdded.text={0} keyword was successfully added. +GlobalEditListPanel.keywordsAddedPlural.text={0} keywords were successfully added. +GlobalEditListPanel.keywordDupesSkipped.text={0} keyword was already in the list. +GlobalEditListPanel.keywordDupesSkippedPlural.text={0} keywords were already in the list. +GlobalEditListPanel.keywordErrors.text={0} keyword could not be parsed. Please review and try again. +GlobalEditListPanel.keywordErrorsPlural.text={0} keywords could not be parsed. Please review and try again. diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalEditListPanel.form b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalEditListPanel.form index c2e3d6430c..c43b029272 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalEditListPanel.form +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalEditListPanel.form @@ -168,7 +168,7 @@ - + @@ -180,8 +180,8 @@ - + @@ -189,19 +189,6 @@ - - - - - - - - - - - - - @@ -215,6 +202,19 @@ + + + + + + + + + + + + + diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalEditListPanel.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalEditListPanel.java index 4b9ea3a7bd..8c8262ecdf 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalEditListPanel.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/GlobalEditListPanel.java @@ -42,7 +42,6 @@ import org.openide.util.NbBundle; import org.sleuthkit.autopsy.corecomponents.OptionsPanel; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.ingest.IngestManager; -import javax.swing.JOptionPane; /** * GlobalEditListPanel widget to manage keywords in lists @@ -64,7 +63,7 @@ class GlobalEditListPanel extends javax.swing.JPanel implements ListSelectionLis } private void customizeComponents() { - newWordButton.setToolTipText((NbBundle.getMessage(this.getClass(), "KeywordSearchEditListPanel.customizeComponents.addWordToolTip"))); + newKeywordsButton.setToolTipText((NbBundle.getMessage(this.getClass(), "KeywordSearchEditListPanel.customizeComponents.addWordToolTip"))); exportButton.setToolTipText(NbBundle.getMessage(this.getClass(), "KeywordSearchEditListPanel.customizeComponents.exportToFile")); saveListButton.setToolTipText(NbBundle.getMessage(this.getClass(), "KeywordSearchEditListPanel.customizeComponents.saveCurrentWIthNewNameToolTip")); deleteWordButton.setToolTipText(NbBundle.getMessage(this.getClass(), "KeywordSearchEditListPanel.customizeComponents.removeSelectedMsg")); @@ -125,7 +124,7 @@ class GlobalEditListPanel extends javax.swing.JPanel implements ListSelectionLis // items that need an unlocked list w/out ingest running boolean isListLocked = ((isListSelected == false) || (currentKeywordList.isEditable())); boolean canAddWord = isListSelected && !isIngestRunning && !isListLocked; - newWordButton.setEnabled(canAddWord); + newKeywordsButton.setEnabled(canAddWord); keywordOptionsLabel.setEnabled(canAddWord); keywordOptionsSeparator.setEnabled(canAddWord); deleteListButton.setEnabled(canAddWord); @@ -155,8 +154,8 @@ class GlobalEditListPanel extends javax.swing.JPanel implements ListSelectionLis jScrollPane1 = new javax.swing.JScrollPane(); keywordTable = new javax.swing.JTable(); addKeywordPanel = new javax.swing.JPanel(); - newWordButton = new javax.swing.JButton(); deleteWordButton = new javax.swing.JButton(); + newKeywordsButton = new javax.swing.JButton(); ingestMessagesCheckbox = new javax.swing.JCheckBox(); keywordsLabel = new javax.swing.JLabel(); keywordOptionsLabel = new javax.swing.JLabel(); @@ -180,14 +179,6 @@ class GlobalEditListPanel extends javax.swing.JPanel implements ListSelectionLis keywordTable.getTableHeader().setReorderingAllowed(false); jScrollPane1.setViewportView(keywordTable); - newWordButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/new16.png"))); // NOI18N - newWordButton.setText(org.openide.util.NbBundle.getMessage(GlobalEditListPanel.class, "KeywordSearchEditListPanel.addWordButton.text")); // NOI18N - newWordButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - newWordButtonActionPerformed(evt); - } - }); - deleteWordButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/delete16.png"))); // NOI18N deleteWordButton.setText(org.openide.util.NbBundle.getMessage(GlobalEditListPanel.class, "KeywordSearchEditListPanel.deleteWordButton.text")); // NOI18N deleteWordButton.addActionListener(new java.awt.event.ActionListener() { @@ -196,12 +187,20 @@ class GlobalEditListPanel extends javax.swing.JPanel implements ListSelectionLis } }); + newKeywordsButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/new16.png"))); // NOI18N + newKeywordsButton.setText(org.openide.util.NbBundle.getMessage(GlobalEditListPanel.class, "GlobalEditListPanel.newKeywordsButton.text")); // NOI18N + newKeywordsButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + newKeywordsButtonActionPerformed(evt); + } + }); + javax.swing.GroupLayout addKeywordPanelLayout = new javax.swing.GroupLayout(addKeywordPanel); addKeywordPanel.setLayout(addKeywordPanelLayout); addKeywordPanelLayout.setHorizontalGroup( addKeywordPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(addKeywordPanelLayout.createSequentialGroup() - .addComponent(newWordButton) + .addComponent(newKeywordsButton) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(deleteWordButton) .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) @@ -211,8 +210,8 @@ class GlobalEditListPanel extends javax.swing.JPanel implements ListSelectionLis .addGroup(addKeywordPanelLayout.createSequentialGroup() .addGap(0, 0, 0) .addGroup(addKeywordPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(newWordButton) - .addComponent(deleteWordButton)) + .addComponent(deleteWordButton) + .addComponent(newKeywordsButton)) .addGap(72, 72, 72)) ); @@ -333,51 +332,6 @@ class GlobalEditListPanel extends javax.swing.JPanel implements ListSelectionLis ); }// //GEN-END:initComponents - private void newWordButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_newWordButtonActionPerformed - NewKeywordPanel panel = new NewKeywordPanel(); - - int result = JOptionPane.showConfirmDialog(null, panel, - NbBundle.getMessage(this.getClass(), "KeywordSearchEditListPanel.addKeyword.title"), - JOptionPane.OK_CANCEL_OPTION); - - if (result == JOptionPane.OK_OPTION) { - String newWord = panel.getKeywordText(); - if (newWord.isEmpty()) { - KeywordSearchUtil.displayDialog(NbBundle.getMessage(this.getClass(), "KeywordSearchEditListPanel.newKwTitle"), - NbBundle.getMessage(this.getClass(), "KeywordSearchEditListPanel.emptyKeyword.text"), KeywordSearchUtil.DIALOG_MESSAGE_TYPE.INFO); - return; - } - final Keyword keyword = new Keyword(newWord, !panel.isKeywordRegex(), panel.isKeywordExact()); - if (currentKeywordList.hasKeyword(keyword)) { - KeywordSearchUtil.displayDialog(NbBundle.getMessage(this.getClass(), "KeywordSearchEditListPanel.newKwTitle"), - NbBundle.getMessage(this.getClass(), "KeywordSearchEditListPanel.addWordButtonAction.kwAlreadyExistsMsg"), KeywordSearchUtil.DIALOG_MESSAGE_TYPE.INFO); - return; - } - - //check if valid - boolean valid = true; - try { - Pattern.compile(newWord); - } catch (PatternSyntaxException ex1) { - valid = false; - } catch (IllegalArgumentException ex2) { - valid = false; - } - if (!valid) { - KeywordSearchUtil.displayDialog(NbBundle.getMessage(this.getClass(), "KeywordSearchEditListPanel.newKwTitle"), - NbBundle.getMessage(this.getClass(), "KeywordSearchEditListPanel.invalidKwMsg"), KeywordSearchUtil.DIALOG_MESSAGE_TYPE.ERROR); - return; - } - - //add & reset checkbox - tableModel.addKeyword(keyword); - XmlKeywordSearchList.getCurrent().addList(currentKeywordList); - firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); - setFocusOnKeywordTextBox(); - setButtonStates(); - } - }//GEN-LAST:event_newWordButtonActionPerformed - private void deleteWordButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteWordButtonActionPerformed if (KeywordSearchUtil.displayConfirmDialog(NbBundle.getMessage(this.getClass(), "KeywordSearchEditListPanel.removeKwMsg"), NbBundle.getMessage(this.getClass(), "KeywordSearchEditListPanel.deleteWordButtonActionPerformed.delConfirmMsg"), KeywordSearchUtil.DIALOG_MESSAGE_TYPE.WARN)) { @@ -450,6 +404,100 @@ class GlobalEditListPanel extends javax.swing.JPanel implements ListSelectionLis firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); }//GEN-LAST:event_deleteListButtonActionPerformed + private void newKeywordsButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_newKeywordsButtonActionPerformed + String keywordsToRedisplay = ""; + AddKeywordsDialog dialog = new AddKeywordsDialog(); + + int goodCount; + int dupeCount; + int badCount = 1; // Default to 1 so we enter the loop the first time + + while(badCount > 0){ + dialog.setInitialKeywordList(keywordsToRedisplay); + dialog.display(); + + goodCount = 0; + dupeCount = 0; + badCount = 0; + keywordsToRedisplay = ""; + + if(!dialog.getKeywords().isEmpty()){ + + + for(String newWord:dialog.getKeywords()){ + if (newWord.isEmpty()) { + continue; + } + + final Keyword keyword = new Keyword(newWord, !dialog.isKeywordRegex(), dialog.isKeywordExact()); + if (currentKeywordList.hasKeyword(keyword)) { + dupeCount++; + continue; + } + + //check if valid + boolean valid = true; + try { + Pattern.compile(newWord); + } catch (PatternSyntaxException ex1) { + valid = false; + } catch (IllegalArgumentException ex2) { + valid = false; + } + if (!valid) { + + // Invalid keywords will reappear in the UI + keywordsToRedisplay += newWord + "\n"; + badCount++; + continue; + } + + // Add the new keyword + tableModel.addKeyword(keyword); + goodCount++; + } + XmlKeywordSearchList.getCurrent().addList(currentKeywordList); + firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); + + if((badCount > 0) || (dupeCount > 0)){ + // Display the error counts to the user + // The add keywords dialog will pop up again if any were invalid with any + // invalid entries (valid entries and dupes will disappear) + + String summary = ""; + KeywordSearchUtil.DIALOG_MESSAGE_TYPE level = KeywordSearchUtil.DIALOG_MESSAGE_TYPE.INFO; + if(goodCount > 0){ + if(goodCount > 1){ + summary += NbBundle.getMessage(GlobalEditListPanel.class, "GlobalEditListPanel.keywordsAddedPlural.text", goodCount) + "\n"; + } else { + summary += NbBundle.getMessage(GlobalEditListPanel.class, "GlobalEditListPanel.keywordsAdded.text", goodCount) + "\n"; + } + } + if(dupeCount > 0){ + if(dupeCount > 1){ + summary += NbBundle.getMessage(GlobalEditListPanel.class, "GlobalEditListPanel.keywordDupesSkippedPlural.text", dupeCount) + "\n"; + } else { + summary += NbBundle.getMessage(GlobalEditListPanel.class, "GlobalEditListPanel.keywordDupesSkipped.text", dupeCount) + "\n"; + } + level = KeywordSearchUtil.DIALOG_MESSAGE_TYPE.WARN; + } + if(badCount > 0){ + if(badCount > 1){ + summary += NbBundle.getMessage(GlobalEditListPanel.class, "GlobalEditListPanel.keywordErrorsPlural.text", badCount) + "\n"; + } else { + summary += NbBundle.getMessage(GlobalEditListPanel.class, "GlobalEditListPanel.keywordErrors.text", badCount) + "\n"; + } + level = KeywordSearchUtil.DIALOG_MESSAGE_TYPE.ERROR; + } + KeywordSearchUtil.displayDialog(NbBundle.getMessage(this.getClass(), "GlobalEditListPanel.addKeywordResults.text"), + summary, level); + } + } + } + setFocusOnKeywordTextBox(); + setButtonStates(); + }//GEN-LAST:event_newKeywordsButtonActionPerformed + // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JPanel addKeywordPanel; private javax.swing.JButton deleteListButton; @@ -464,7 +512,7 @@ class GlobalEditListPanel extends javax.swing.JPanel implements ListSelectionLis private javax.swing.JPanel listEditorPanel; private javax.swing.JLabel listOptionsLabel; private javax.swing.JSeparator listOptionsSeparator; - private javax.swing.JButton newWordButton; + private javax.swing.JButton newKeywordsButton; private javax.swing.JButton saveListButton; // End of variables declaration//GEN-END:variables @@ -604,6 +652,6 @@ class GlobalEditListPanel extends javax.swing.JPanel implements ListSelectionLis * Set the keyboard focus to new keyword textbox. */ void setFocusOnKeywordTextBox() { - newWordButton.requestFocus(); + newKeywordsButton.requestFocus(); } } From d6067df967140c87d8938c373dca90be5c23b35d Mon Sep 17 00:00:00 2001 From: Sophie Mori Date: Fri, 9 Dec 2016 15:48:49 -0500 Subject: [PATCH 12/16] Fix typos in a message displayed by the UI --- .../autopsy/imagegallery/ImageGalleryController.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java index 98d8591b21..55fb260bbd 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java @@ -717,7 +717,7 @@ public final class ImageGalleryController implements Executor { } } - @NbBundle.Messages({"BulkTask.committingDb.status=commiting image/video database", + @NbBundle.Messages({"BulkTask.committingDb.status=committing image/video database", "BulkTask.stopCopy.status=Stopping copy to drawable db task.", "BulkTask.errPopulating.errMsg=There was an error populating Image Gallery database."}) abstract static private class BulkTransferTask extends BackgroundTask { @@ -821,7 +821,7 @@ public final class ImageGalleryController implements Executor { * adds them to the Drawable DB. Uses the presence of a mimetype as an * approximation to 'analyzed'. */ - @NbBundle.Messages({"CopyAnalyzedFiles.committingDb.status=commiting image/video database", + @NbBundle.Messages({"CopyAnalyzedFiles.committingDb.status=committing image/video database", "CopyAnalyzedFiles.stopCopy.status=Stopping copy to drawable db task.", "CopyAnalyzedFiles.errPopulating.errMsg=There was an error populating Image Gallery database."}) static private class CopyAnalyzedFiles extends BulkTransferTask { @@ -875,7 +875,7 @@ public final class ImageGalleryController implements Executor { * TODO: create methods to simplify progress value/text updates to both * netbeans and ImageGallery progress/status */ - @NbBundle.Messages({"PrePopulateDataSourceFiles.committingDb.status=commiting image/video database"}) + @NbBundle.Messages({"PrePopulateDataSourceFiles.committingDb.status=committing image/video database"}) static private class PrePopulateDataSourceFiles extends BulkTransferTask { private static final Logger LOGGER = Logger.getLogger(PrePopulateDataSourceFiles.class.getName()); From 377e91ebb00792c8bddc66cfabc35ade244e438e Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Sun, 11 Dec 2016 12:35:26 -0500 Subject: [PATCH 13/16] Tidy up EmptyNode class --- .../autopsy/datamodel/EmptyNode.java | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/EmptyNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/EmptyNode.java index 3b552f8b70..c822743c23 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/EmptyNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/EmptyNode.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2016 Basis Technology Corp. + * Copyright 2011-2016 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -25,33 +25,37 @@ import org.openide.nodes.Children; import org.openide.nodes.Node; /** - * EmptyNode Class made for when you need to display a node with with text in the table view - * but no children in the tree view. - * + * Provides a root node for the results views with a single child node that + * displays a message as the sole item in its property sheet, useful for + * displaying explanatory text in the result views when there is a node with no + * children in the tree view. */ public final class EmptyNode extends AbstractNode { /** - * Creates an EmptyNode - * - * @param displayedMessage the text you would like displayed in the table view, this will not appear as a child node. + * Provides a root node for the results views with a single child node that + * displays a message as the sole item in its property sheet, useful for + * displaying explanatory text in the result views when there is a node with + * no children in the tree view. + * + * @param displayedMessage The text for the property sheet of the child + * node. */ public EmptyNode(String displayedMessage) { super(Children.create(new EmptyNodeChildren(displayedMessage), true)); - } static class EmptyNodeChildren extends ChildFactory { - String fileIdMsg; //NON-NLS + String displayedMessage; private EmptyNodeChildren(String displayedMessage) { - fileIdMsg = displayedMessage; + this.displayedMessage = displayedMessage; } @Override - protected boolean createKeys(List list) { - list.add(fileIdMsg); + protected boolean createKeys(List keys) { + keys.add(displayedMessage); return true; } @@ -63,9 +67,8 @@ public final class EmptyNode extends AbstractNode { } /** - * MessageNode is is the info message that displays in the table view, by - * also extending a DisplayableItemNode type, rather than an AbstractNode - * type it doesn't throw an error when right clicked. + * The single child node of an EmptyNode, responsible for displaying a + * message as the sole item in its property sheet. */ static class MessageNode extends DisplayableItemNode { From ebe1a302382f5466f576bed4c9ad9e861f58e271 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Sun, 11 Dec 2016 12:38:16 -0500 Subject: [PATCH 14/16] Add private constructor to utility class FileTypeExtensions --- .../autopsy/datamodel/FileTypeExtensions.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypeExtensions.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypeExtensions.java index d9cd01b791..4b1146775c 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypeExtensions.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypeExtensions.java @@ -1,15 +1,15 @@ /* * Autopsy Forensic Browser - * + * * Copyright 2011-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. @@ -74,4 +74,8 @@ public class FileTypeExtensions { public static List getArchiveExtensions() { return ARCHIVE_EXTENSIONS; } + + private FileTypeExtensions() { + } + } From d574e1ba69d168bec17b3c43498be6b3a688a2b9 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Sun, 11 Dec 2016 13:41:26 -0500 Subject: [PATCH 15/16] Tidy up and refactor file types data model classes --- .../datamodel/AbstractContentChildren.java | 4 +- .../autopsy/datamodel/AutopsyItemVisitor.java | 18 ++- .../autopsy/datamodel/Bundle.properties | 13 +- .../datamodel/DisplayableItemNodeVisitor.java | 4 +- .../autopsy/datamodel/FileTypeExtensions.java | 2 +- .../autopsy/datamodel/FileTypes.java | 3 +- .../autopsy/datamodel/FileTypesByExtNode.java | 131 ++++++++---------- ...Filters.java => FileTypesByExtension.java} | 39 +++--- .../datamodel/FileTypesByMimeType.java | 43 +++--- .../datamodel/{accounts => }/RecentFiles.java | 2 +- .../datamodel/RecentFilesChildren.java | 1 - .../datamodel/RecentFilesFilterChildren.java | 2 +- .../datamodel/RecentFilesFilterNode.java | 2 +- .../autopsy/datamodel/ViewsNode.java | 1 - .../datamodel/accounts/Bundle.properties | 12 -- 15 files changed, 125 insertions(+), 152 deletions(-) rename Core/src/org/sleuthkit/autopsy/datamodel/{accounts/FileTypeExtensionFilters.java => FileTypesByExtension.java} (78%) rename Core/src/org/sleuthkit/autopsy/datamodel/{accounts => }/RecentFiles.java (98%) delete mode 100644 Core/src/org/sleuthkit/autopsy/datamodel/accounts/Bundle.properties diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java index 4313be1f48..c63cc81a76 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java @@ -23,8 +23,6 @@ import org.openide.nodes.Children.Keys; import org.openide.nodes.Node; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.datamodel.FileTypes.FileTypesNode; -import org.sleuthkit.autopsy.datamodel.accounts.FileTypeExtensionFilters; -import org.sleuthkit.autopsy.datamodel.accounts.RecentFiles; import org.sleuthkit.autopsy.datamodel.accounts.Accounts; import org.sleuthkit.autopsy.datamodel.accounts.Accounts.AccountsRootNode; import org.sleuthkit.datamodel.Content; @@ -137,7 +135,7 @@ abstract class AbstractContentChildren extends Keys { } @Override - public AbstractNode visit(FileTypeExtensionFilters sf) { + public AbstractNode visit(FileTypesByExtension sf) { return new FileTypesByExtNode(sf.getSleuthkitCase(), null); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AutopsyItemVisitor.java b/Core/src/org/sleuthkit/autopsy/datamodel/AutopsyItemVisitor.java index f0d2bcc253..2dcc5942db 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AutopsyItemVisitor.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AutopsyItemVisitor.java @@ -19,8 +19,6 @@ package org.sleuthkit.autopsy.datamodel; import org.sleuthkit.autopsy.datamodel.accounts.Accounts; -import org.sleuthkit.autopsy.datamodel.accounts.FileTypeExtensionFilters; -import org.sleuthkit.autopsy.datamodel.accounts.RecentFiles; /** * This visitor goes over the AutopsyVisitableItems, which are currently the @@ -33,13 +31,13 @@ public interface AutopsyItemVisitor { T visit(Views v); - T visit(FileTypeExtensionFilters sf); + T visit(FileTypesByExtension sf); - T visit(FileTypeExtensionFilters.RootFilter fsf); + T visit(FileTypesByExtension.RootFilter fsf); - T visit(FileTypeExtensionFilters.DocumentFilter df); + T visit(FileTypesByExtension.DocumentFilter df); - T visit(FileTypeExtensionFilters.ExecutableFilter ef); + T visit(FileTypesByExtension.ExecutableFilter ef); T visit(RecentFiles rf); @@ -86,22 +84,22 @@ public interface AutopsyItemVisitor { } @Override - public T visit(FileTypeExtensionFilters sf) { + public T visit(FileTypesByExtension sf) { return defaultVisit(sf); } @Override - public T visit(FileTypeExtensionFilters.RootFilter fsf) { + public T visit(FileTypesByExtension.RootFilter fsf) { return defaultVisit(fsf); } @Override - public T visit(FileTypeExtensionFilters.DocumentFilter df) { + public T visit(FileTypesByExtension.DocumentFilter df) { return defaultVisit(df); } @Override - public T visit(FileTypeExtensionFilters.ExecutableFilter ef) { + public T visit(FileTypesByExtension.ExecutableFilter ef) { return defaultVisit(ef); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties index b1cf4839c8..cbe816ffe7 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties @@ -274,4 +274,15 @@ AbstractAbstractFileNode.addFileProperty.desc=no description AbstractAbstractFileNode.addFileProperty.tags.name=Tags AbstractAbstractFileNode.addFileProperty.tags.displayName=Tags BlackboardArtifactNode.createSheet.tags.name=Tags -BlackboardArtifactNode.createSheet.tags.displayName=Tags \ No newline at end of file +BlackboardArtifactNode.createSheet.tags.displayName=Tags +FileTypeExtensionFilters.tskImgFilter.text=Images +FileTypeExtensionFilters.tskVideoFilter.text=Videos +FileTypeExtensionFilters.tskAudioFilter.text=Audio +FileTypeExtensionFilters.tskArchiveFilter.text=Archives +FileTypeExtensionFilters.tskDocumentFilter.text=Documents +FileTypeExtensionFilters.tskExecFilter.text=Executable +FileTypeExtensionFilters.autDocHtmlFilter.text=HTML +FileTypeExtensionFilters.autDocOfficeFilter.text=Office +FileTypeExtensionFilters.autoDocPdfFilter.text=PDF +FileTypeExtensionFilters.autDocTxtFilter.text=Plain Text +FileTypeExtensionFilters.autDocRtfFilter.text=Rich Text \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java b/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java index 2c029806cd..2ced0c870e 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java @@ -58,7 +58,7 @@ public interface DisplayableItemNodeVisitor { */ T visit(ViewsNode vn); - T visit(FileTypesByExtNode.ByExtNode fsfn); + T visit(FileTypesByExtNode.FileExtensionNode fsfn); T visit(DeletedContentNode dcn); @@ -212,7 +212,7 @@ public interface DisplayableItemNodeVisitor { } @Override - public T visit(FileTypesByExtNode.ByExtNode fsfn) { + public T visit(FileTypesByExtNode.FileExtensionNode fsfn) { return defaultVisit(fsfn); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypeExtensions.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypeExtensions.java index 4b1146775c..4195b33008 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypeExtensions.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypeExtensions.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2013 Basis Technology Corp. + * Copyright 2011-2016 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java index 45db9395ba..6998b2ef71 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java @@ -22,7 +22,6 @@ import java.util.Arrays; import org.openide.nodes.Sheet; import org.openide.util.NbBundle; import org.openide.util.lookup.Lookups; -import org.sleuthkit.autopsy.datamodel.accounts.FileTypeExtensionFilters; import org.sleuthkit.datamodel.SleuthkitCase; /** @@ -55,7 +54,7 @@ public final class FileTypes implements AutopsyVisitableItem { FileTypesNode(SleuthkitCase sleuthkitCase) { super(new RootContentChildren(Arrays.asList( - new FileTypeExtensionFilters(sleuthkitCase), + new FileTypesByExtension(sleuthkitCase), new FileTypesByMimeType(sleuthkitCase) )), Lookups.singleton(NAME)); setName(NAME); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtNode.java index 4e40ab9bc4..e055f49d74 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtNode.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2015 Basis Technology Corp. + * Copyright 2011-2016 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,7 +18,6 @@ */ package org.sleuthkit.autopsy.datamodel; -import org.sleuthkit.autopsy.datamodel.accounts.FileTypeExtensionFilters; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.Arrays; @@ -55,7 +54,7 @@ import org.sleuthkit.datamodel.TskData; class FileTypesByExtNode extends DisplayableItemNode { private static final String FNAME = NbBundle.getMessage(FileTypesByExtNode.class, "FileTypesByExtNode.fname.text"); - private final FileTypeExtensionFilters.RootFilter filter; + private final FileTypesByExtension.RootFilter filter; /** * @@ -63,8 +62,8 @@ class FileTypesByExtNode extends DisplayableItemNode { * @param filter null to display root node of file type tree, pass in * something to provide a sub-node. */ - FileTypesByExtNode(SleuthkitCase skCase, FileTypeExtensionFilters.RootFilter filter) { - super(Children.create(new ByExtChildren(skCase, filter, null), true), Lookups.singleton(filter == null ? FNAME : filter.getName())); + FileTypesByExtNode(SleuthkitCase skCase, FileTypesByExtension.RootFilter filter) { + super(Children.create(new FileTypesByExtNodeChildren(skCase, filter, null), true), Lookups.singleton(filter == null ? FNAME : filter.getName())); this.filter = filter; init(); } @@ -76,8 +75,8 @@ class FileTypesByExtNode extends DisplayableItemNode { * @param o Observable that was created by a higher-level node that provides * updates on events */ - private FileTypesByExtNode(SleuthkitCase skCase, FileTypeExtensionFilters.RootFilter filter, Observable o) { - super(Children.create(new ByExtChildren(skCase, filter, o), true), Lookups.singleton(filter == null ? FNAME : filter.getName())); + private FileTypesByExtNode(SleuthkitCase skCase, FileTypesByExtension.RootFilter filter, Observable o) { + super(Children.create(new FileTypesByExtNodeChildren(skCase, filter, o), true), Lookups.singleton(filter == null ? FNAME : filter.getName())); this.filter = filter; init(); } @@ -130,8 +129,8 @@ class FileTypesByExtNode extends DisplayableItemNode { if (filter == null) { return getClass().getName(); } - if (filter.equals(FileTypeExtensionFilters.RootFilter.TSK_DOCUMENT_FILTER) - || filter.equals(FileTypeExtensionFilters.RootFilter.TSK_EXECUTABLE_FILTER)) { + if (filter.equals(FileTypesByExtension.RootFilter.TSK_DOCUMENT_FILTER) + || filter.equals(FileTypesByExtension.RootFilter.TSK_EXECUTABLE_FILTER)) { return getClass().getName() + filter.getName(); } return getClass().getName(); @@ -140,10 +139,10 @@ class FileTypesByExtNode extends DisplayableItemNode { /** * */ - private static class ByExtChildren extends ChildFactory { + private static class FileTypesByExtNodeChildren extends ChildFactory { private final SleuthkitCase skCase; - private final FileTypeExtensionFilters.RootFilter filter; + private final FileTypesByExtension.RootFilter filter; private final Observable notifier; /** @@ -153,12 +152,12 @@ class FileTypesByExtNode extends DisplayableItemNode { * @param o Observable that provides updates based on events being fired * (or null if one needs to be created) */ - private ByExtChildren(SleuthkitCase skCase, FileTypeExtensionFilters.RootFilter filter, Observable o) { + private FileTypesByExtNodeChildren(SleuthkitCase skCase, FileTypesByExtension.RootFilter filter, Observable o) { super(); this.skCase = skCase; this.filter = filter; if (o == null) { - this.notifier = new ByExtChildrenObservable(); + this.notifier = new FileTypesByExtObservable(); } else { this.notifier = o; } @@ -168,9 +167,9 @@ class FileTypesByExtNode extends DisplayableItemNode { * Listens for case and ingest invest. Updates observers when events are * fired. FileType and FileTypes nodes are all listening to this. */ - private final class ByExtChildrenObservable extends Observable { + private final class FileTypesByExtObservable extends Observable { - private ByExtChildrenObservable() { + private FileTypesByExtObservable() { IngestManager.getInstance().addIngestJobEventListener(pcl); IngestManager.getInstance().addIngestModuleEventListener(pcl); Case.addPropertyChangeListener(pcl); @@ -183,33 +182,30 @@ class FileTypesByExtNode extends DisplayableItemNode { Case.removePropertyChangeListener(pcl); } - private final PropertyChangeListener pcl = new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - String eventType = evt.getPropertyName(); - if (eventType.equals(IngestManager.IngestModuleEvent.CONTENT_CHANGED.toString()) - || eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString()) - || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString()) - || eventType.equals(Case.Events.DATA_SOURCE_ADDED.toString())) { + private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> { + String eventType = evt.getPropertyName(); + if (eventType.equals(IngestManager.IngestModuleEvent.CONTENT_CHANGED.toString()) + || eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString()) + || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString()) + || eventType.equals(Case.Events.DATA_SOURCE_ADDED.toString())) { + /** + * Checking for a current case is a stop gap measure + * until a different way of handling the closing of + * cases is worked out. Currently, remote events may be + * received for a case that is already closed. + */ + try { + Case.getCurrentCase(); + update(); + } catch (IllegalStateException notUsed) { /** - * Checking for a current case is a stop gap measure - * until a different way of handling the closing of - * cases is worked out. Currently, remote events may be - * received for a case that is already closed. + * Case is closed, do nothing. */ - try { - Case.getCurrentCase(); - update(); - } catch (IllegalStateException notUsed) { - /** - * Case is closed, do nothing. - */ - } - } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) { - // case was closed. Remove listeners so that we don't get called with a stale case handle - if (evt.getNewValue() == null) { - removeListeners(); - } + } + } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) { + // case was closed. Remove listeners so that we don't get called with a stale case handle + if (evt.getNewValue() == null) { + removeListeners(); } } }; @@ -221,28 +217,28 @@ class FileTypesByExtNode extends DisplayableItemNode { } @Override - protected boolean createKeys(List list) { + protected boolean createKeys(List list) { // root node if (filter == null) { - list.addAll(Arrays.asList(FileTypeExtensionFilters.RootFilter.values())); + list.addAll(Arrays.asList(FileTypesByExtension.RootFilter.values())); } // document and executable has another level of nodes - else if (filter.equals(FileTypeExtensionFilters.RootFilter.TSK_DOCUMENT_FILTER)) { - list.addAll(Arrays.asList(FileTypeExtensionFilters.DocumentFilter.values())); - } else if (filter.equals(FileTypeExtensionFilters.RootFilter.TSK_EXECUTABLE_FILTER)) { - list.addAll(Arrays.asList(FileTypeExtensionFilters.ExecutableFilter.values())); + else if (filter.equals(FileTypesByExtension.RootFilter.TSK_DOCUMENT_FILTER)) { + list.addAll(Arrays.asList(FileTypesByExtension.DocumentFilter.values())); + } else if (filter.equals(FileTypesByExtension.RootFilter.TSK_EXECUTABLE_FILTER)) { + list.addAll(Arrays.asList(FileTypesByExtension.ExecutableFilter.values())); } return true; } @Override - protected Node createNodeForKey(FileTypeExtensionFilters.SearchFilterInterface key) { + protected Node createNodeForKey(FileTypesByExtension.SearchFilterInterface key) { // make new nodes for the sub-nodes - if (key.getName().equals(FileTypeExtensionFilters.RootFilter.TSK_DOCUMENT_FILTER.getName())) { - return new FileTypesByExtNode(skCase, FileTypeExtensionFilters.RootFilter.TSK_DOCUMENT_FILTER, notifier); - } else if (key.getName().equals(FileTypeExtensionFilters.RootFilter.TSK_EXECUTABLE_FILTER.getName())) { - return new FileTypesByExtNode(skCase, FileTypeExtensionFilters.RootFilter.TSK_EXECUTABLE_FILTER, notifier); + if (key.getName().equals(FileTypesByExtension.RootFilter.TSK_DOCUMENT_FILTER.getName())) { + return new FileTypesByExtNode(skCase, FileTypesByExtension.RootFilter.TSK_DOCUMENT_FILTER, notifier); + } else if (key.getName().equals(FileTypesByExtension.RootFilter.TSK_EXECUTABLE_FILTER.getName())) { + return new FileTypesByExtNode(skCase, FileTypesByExtension.RootFilter.TSK_EXECUTABLE_FILTER, notifier); } else { - return new ByExtNode(key, skCase, notifier); + return new FileExtensionNode(key, skCase, notifier); } } } @@ -251,9 +247,9 @@ class FileTypesByExtNode extends DisplayableItemNode { * Node for a specific file type / extension. Children of it will be the * files of that type. */ - static class ByExtNode extends DisplayableItemNode { + static class FileExtensionNode extends DisplayableItemNode { - FileTypeExtensionFilters.SearchFilterInterface filter; + FileTypesByExtension.SearchFilterInterface filter; SleuthkitCase skCase; /** @@ -263,8 +259,8 @@ class FileTypesByExtNode extends DisplayableItemNode { * @param o Observable that sends updates when the child factories * should refresh */ - ByExtNode(FileTypeExtensionFilters.SearchFilterInterface filter, SleuthkitCase skCase, Observable o) { - super(Children.create(new ByExtChildFactory(filter, skCase, o), true), Lookups.singleton(filter.getDisplayName())); + FileExtensionNode(FileTypesByExtension.SearchFilterInterface filter, SleuthkitCase skCase, Observable o) { + super(Children.create(new FileExtensionNodeChildren(filter, skCase, o), true), Lookups.singleton(filter.getDisplayName())); this.filter = filter; this.skCase = skCase; init(); @@ -287,7 +283,7 @@ class FileTypesByExtNode extends DisplayableItemNode { } private void updateDisplayName() { - final long count = ByExtChildFactory.calculateItems(skCase, filter); + final long count = FileExtensionNodeChildren.calculateItems(skCase, filter); super.setDisplayName(filter.getDisplayName() + " (" + count + ")"); } @@ -341,22 +337,13 @@ class FileTypesByExtNode extends DisplayableItemNode { * Child node factory for a specific file type - does the database * query. */ - private static class ByExtChildFactory extends ChildFactory.Detachable { + private static class FileExtensionNodeChildren extends ChildFactory.Detachable { private final SleuthkitCase skCase; - private final FileTypeExtensionFilters.SearchFilterInterface filter; - private final static Logger LOGGER = Logger.getLogger(ByExtChildFactory.class.getName()); + private final FileTypesByExtension.SearchFilterInterface filter; + private final static Logger LOGGER = Logger.getLogger(FileExtensionNodeChildren.class.getName()); private final Observable notifier; - // use the constructor that gets an observable passed in for updates - @Deprecated - ByExtChildFactory(FileTypeExtensionFilters.SearchFilterInterface filter, SleuthkitCase skCase) { - super(); - this.filter = filter; - this.skCase = skCase; - notifier = null; - } - /** * * @param filter Extensions to display @@ -364,7 +351,7 @@ class FileTypesByExtNode extends DisplayableItemNode { * @param o Observable that will notify when there could be new data * to display */ - private ByExtChildFactory(FileTypeExtensionFilters.SearchFilterInterface filter, SleuthkitCase skCase, Observable o) { + private FileExtensionNodeChildren(FileTypesByExtension.SearchFilterInterface filter, SleuthkitCase skCase, Observable o) { super(); this.filter = filter; this.skCase = skCase; @@ -400,7 +387,7 @@ class FileTypesByExtNode extends DisplayableItemNode { * * @return */ - private static long calculateItems(SleuthkitCase sleuthkitCase, FileTypeExtensionFilters.SearchFilterInterface filter) { + private static long calculateItems(SleuthkitCase sleuthkitCase, FileTypesByExtension.SearchFilterInterface filter) { try { return sleuthkitCase.countFilesWhere(createQuery(filter)); } catch (TskCoreException ex) { @@ -420,7 +407,7 @@ class FileTypesByExtNode extends DisplayableItemNode { return true; } - private static String createQuery(FileTypeExtensionFilters.SearchFilterInterface filter) { + private static String createQuery(FileTypesByExtension.SearchFilterInterface filter) { StringBuilder query = new StringBuilder(); query.append("(dir_type = ").append(TskData.TSK_FS_NAME_TYPE_ENUM.REG.getValue()).append(")"); //NON-NLS if (UserPreferences.hideKnownFilesInViewsTree()) { diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/accounts/FileTypeExtensionFilters.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java similarity index 78% rename from Core/src/org/sleuthkit/autopsy/datamodel/accounts/FileTypeExtensionFilters.java rename to Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java index 80680e97cc..2fcc399a77 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/accounts/FileTypeExtensionFilters.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java @@ -1,35 +1,32 @@ /* * 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.accounts; +package org.sleuthkit.autopsy.datamodel; -import org.sleuthkit.autopsy.datamodel.AutopsyItemVisitor; -import org.sleuthkit.autopsy.datamodel.AutopsyVisitableItem; import java.util.Arrays; import java.util.List; import org.openide.util.NbBundle; -import org.sleuthkit.autopsy.datamodel.FileTypeExtensions; import org.sleuthkit.datamodel.SleuthkitCase; /** * Filters database results by file extension. */ -public class FileTypeExtensionFilters implements AutopsyVisitableItem { +public class FileTypesByExtension implements AutopsyVisitableItem { private final SleuthkitCase skCase; @@ -37,22 +34,22 @@ public class FileTypeExtensionFilters implements AutopsyVisitableItem { public enum RootFilter implements AutopsyVisitableItem, SearchFilterInterface { TSK_IMAGE_FILTER(0, "TSK_IMAGE_FILTER", //NON-NLS - NbBundle.getMessage(FileTypeExtensionFilters.class, "FileTypeExtensionFilters.tskImgFilter.text"), + NbBundle.getMessage(FileTypesByExtension.class, "FileTypeExtensionFilters.tskImgFilter.text"), FileTypeExtensions.getImageExtensions()), TSK_VIDEO_FILTER(1, "TSK_VIDEO_FILTER", //NON-NLS - NbBundle.getMessage(FileTypeExtensionFilters.class, "FileTypeExtensionFilters.tskVideoFilter.text"), + NbBundle.getMessage(FileTypesByExtension.class, "FileTypeExtensionFilters.tskVideoFilter.text"), FileTypeExtensions.getVideoExtensions()), TSK_AUDIO_FILTER(2, "TSK_AUDIO_FILTER", //NON-NLS - NbBundle.getMessage(FileTypeExtensionFilters.class, "FileTypeExtensionFilters.tskAudioFilter.text"), + NbBundle.getMessage(FileTypesByExtension.class, "FileTypeExtensionFilters.tskAudioFilter.text"), FileTypeExtensions.getAudioExtensions()), TSK_ARCHIVE_FILTER(3, "TSK_ARCHIVE_FILTER", //NON-NLS - NbBundle.getMessage(FileTypeExtensionFilters.class, "FileTypeExtensionFilters.tskArchiveFilter.text"), + NbBundle.getMessage(FileTypesByExtension.class, "FileTypeExtensionFilters.tskArchiveFilter.text"), FileTypeExtensions.getArchiveExtensions()), TSK_DOCUMENT_FILTER(3, "TSK_DOCUMENT_FILTER", //NON-NLS - NbBundle.getMessage(FileTypeExtensionFilters.class, "FileTypeExtensionFilters.tskDocumentFilter.text"), + NbBundle.getMessage(FileTypesByExtension.class, "FileTypeExtensionFilters.tskDocumentFilter.text"), Arrays.asList(".doc", ".docx", ".pdf", ".xls", ".rtf", ".txt")), //NON-NLS TSK_EXECUTABLE_FILTER(3, "TSK_EXECUTABLE_FILTER", //NON-NLS - NbBundle.getMessage(FileTypeExtensionFilters.class, "FileTypeExtensionFilters.tskExecFilter.text"), + NbBundle.getMessage(FileTypesByExtension.class, "FileTypeExtensionFilters.tskExecFilter.text"), Arrays.asList(".exe", ".dll", ".bat", ".cmd", ".com")); //NON-NLS private final int id; @@ -97,19 +94,19 @@ public class FileTypeExtensionFilters implements AutopsyVisitableItem { public enum DocumentFilter implements AutopsyVisitableItem, SearchFilterInterface { AUT_DOC_HTML(0, "AUT_DOC_HTML", //NON-NLS - NbBundle.getMessage(FileTypeExtensionFilters.class, "FileTypeExtensionFilters.autDocHtmlFilter.text"), + NbBundle.getMessage(FileTypesByExtension.class, "FileTypeExtensionFilters.autDocHtmlFilter.text"), Arrays.asList(".htm", ".html")), //NON-NLS AUT_DOC_OFFICE(1, "AUT_DOC_OFFICE", //NON-NLS - NbBundle.getMessage(FileTypeExtensionFilters.class, "FileTypeExtensionFilters.autDocOfficeFilter.text"), + NbBundle.getMessage(FileTypesByExtension.class, "FileTypeExtensionFilters.autDocOfficeFilter.text"), Arrays.asList(".doc", ".docx", ".odt", ".xls", ".xlsx", ".ppt", ".pptx")), //NON-NLS AUT_DOC_PDF(2, "AUT_DOC_PDF", //NON-NLS - NbBundle.getMessage(FileTypeExtensionFilters.class, "FileTypeExtensionFilters.autoDocPdfFilter.text"), + NbBundle.getMessage(FileTypesByExtension.class, "FileTypeExtensionFilters.autoDocPdfFilter.text"), Arrays.asList(".pdf")), //NON-NLS AUT_DOC_TXT(3, "AUT_DOC_TXT", //NON-NLS - NbBundle.getMessage(FileTypeExtensionFilters.class, "FileTypeExtensionFilters.autDocTxtFilter.text"), + NbBundle.getMessage(FileTypesByExtension.class, "FileTypeExtensionFilters.autDocTxtFilter.text"), Arrays.asList(".txt")), //NON-NLS AUT_DOC_RTF(4, "AUT_DOC_RTF", //NON-NLS - NbBundle.getMessage(FileTypeExtensionFilters.class, "FileTypeExtensionFilters.autDocRtfFilter.text"), + NbBundle.getMessage(FileTypesByExtension.class, "FileTypeExtensionFilters.autDocRtfFilter.text"), Arrays.asList(".rtf")); //NON-NLS private final int id; @@ -197,7 +194,7 @@ public class FileTypeExtensionFilters implements AutopsyVisitableItem { } } - public FileTypeExtensionFilters(SleuthkitCase skCase) { + public FileTypesByExtension(SleuthkitCase skCase) { this.skCase = skCase; } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java index b44d3c19d6..4165553e2a 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java @@ -72,28 +72,25 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi * The pcl is in the class because it has the easiest mechanisms to add * and remove itself during its life cycles. */ - private final PropertyChangeListener pcl = new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - String eventType = evt.getPropertyName(); - if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString()) - || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString()) - // || eventType.equals(Case.Events.DATA_SOURCE_ADDED.toString()) - || eventType.equals(IngestManager.IngestModuleEvent.CONTENT_CHANGED.toString())) { + private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> { + String eventType = evt.getPropertyName(); + if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString()) + || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString()) + // || eventType.equals(Case.Events.DATA_SOURCE_ADDED.toString()) + || eventType.equals(IngestManager.IngestModuleEvent.CONTENT_CHANGED.toString())) { + /** + * Checking for a current case is a stop gap measure until a + * different way of handling the closing of cases is worked out. + * Currently, remote events may be received for a case that is + * already closed. + */ + try { + Case.getCurrentCase(); + populateHashMap(); + } catch (IllegalStateException notUsed) { /** - * Checking for a current case is a stop gap measure until a - * different way of handling the closing of cases is worked out. - * Currently, remote events may be received for a case that is - * already closed. + * Case is closed, do nothing. */ - try { - Case.getCurrentCase(); - populateHashMap(); - } catch (IllegalStateException notUsed) { - /** - * Case is closed, do nothing. - */ - } } } }; @@ -171,13 +168,13 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi * Method to check if the node in question is a ByMimeTypeNode which is * empty. * - * @param originNode the Node which you wish to check. + * @param node the Node which you wish to check. * @return True if originNode is an instance of ByMimeTypeNode and is empty, * false otherwise. */ - public static boolean isEmptyMimeTypeNode(Node originNode) { + public static boolean isEmptyMimeTypeNode(Node node) { boolean isEmptyMimeNode = false; - if (originNode instanceof FileTypesByMimeType.ByMimeTypeNode && ((FileTypesByMimeType.ByMimeTypeNode) originNode).isEmpty()) { + if (node instanceof FileTypesByMimeType.ByMimeTypeNode && ((FileTypesByMimeType.ByMimeTypeNode) node).isEmpty()) { isEmptyMimeNode = true; } return isEmptyMimeNode; diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/accounts/RecentFiles.java b/Core/src/org/sleuthkit/autopsy/datamodel/RecentFiles.java similarity index 98% rename from Core/src/org/sleuthkit/autopsy/datamodel/accounts/RecentFiles.java rename to Core/src/org/sleuthkit/autopsy/datamodel/RecentFiles.java index d1ad7cb47b..ab39b69890 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/accounts/RecentFiles.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/RecentFiles.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sleuthkit.autopsy.datamodel.accounts; +package org.sleuthkit.autopsy.datamodel; import org.sleuthkit.autopsy.datamodel.AutopsyVisitableItem; import org.sleuthkit.autopsy.datamodel.AutopsyItemVisitor; diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/RecentFilesChildren.java b/Core/src/org/sleuthkit/autopsy/datamodel/RecentFilesChildren.java index 6c39d582dd..391a49f757 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/RecentFilesChildren.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/RecentFilesChildren.java @@ -18,7 +18,6 @@ */ package org.sleuthkit.autopsy.datamodel; -import org.sleuthkit.autopsy.datamodel.accounts.RecentFiles; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Arrays; diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/RecentFilesFilterChildren.java b/Core/src/org/sleuthkit/autopsy/datamodel/RecentFilesFilterChildren.java index 2114166074..c6c9c72471 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/RecentFilesFilterChildren.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/RecentFilesFilterChildren.java @@ -28,7 +28,7 @@ import org.sleuthkit.autopsy.coreutils.Logger; import org.openide.nodes.AbstractNode; import org.openide.nodes.ChildFactory; import org.openide.nodes.Node; -import org.sleuthkit.autopsy.datamodel.accounts.RecentFiles.RecentFilesFilter; +import org.sleuthkit.autopsy.datamodel.RecentFiles.RecentFilesFilter; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.ContentVisitor; diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/RecentFilesFilterNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/RecentFilesFilterNode.java index d22775fce3..66cbc5fb68 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/RecentFilesFilterNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/RecentFilesFilterNode.java @@ -25,7 +25,7 @@ import org.sleuthkit.autopsy.coreutils.Logger; import org.openide.nodes.Children; import org.openide.nodes.Sheet; import org.openide.util.lookup.Lookups; -import org.sleuthkit.autopsy.datamodel.accounts.RecentFiles.RecentFilesFilter; +import org.sleuthkit.autopsy.datamodel.RecentFiles.RecentFilesFilter; import org.sleuthkit.datamodel.SleuthkitCase; /** diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ViewsNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/ViewsNode.java index a2c09299d1..9d1f0e0d63 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ViewsNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ViewsNode.java @@ -18,7 +18,6 @@ */ package org.sleuthkit.autopsy.datamodel; -import org.sleuthkit.autopsy.datamodel.accounts.FileTypeExtensionFilters; import java.util.Arrays; import org.openide.nodes.Sheet; import org.openide.util.NbBundle; diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Bundle.properties b/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Bundle.properties deleted file mode 100644 index dc778857c5..0000000000 --- a/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Bundle.properties +++ /dev/null @@ -1,12 +0,0 @@ -FileTypeExtensionFilters.tskImgFilter.text=Images -FileTypeExtensionFilters.tskVideoFilter.text=Videos -FileTypeExtensionFilters.tskAudioFilter.text=Audio -FileTypeExtensionFilters.tskArchiveFilter.text=Archives -FileTypeExtensionFilters.tskDocumentFilter.text=Documents -FileTypeExtensionFilters.tskExecFilter.text=Executable -FileTypeExtensionFilters.autDocHtmlFilter.text=HTML -FileTypeExtensionFilters.autDocOfficeFilter.text=Office -FileTypeExtensionFilters.autoDocPdfFilter.text=PDF -FileTypeExtensionFilters.autDocTxtFilter.text=Plain Text -FileTypeExtensionFilters.autDocRtfFilter.text=Rich Text - From 176e36581c39197f3c75dfc8b081cd87cf55a0f7 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Sun, 11 Dec 2016 14:26:00 -0500 Subject: [PATCH 16/16] Move new FileTypesByExtNode class into new FileTypesByExtension class --- .../datamodel/AbstractContentChildren.java | 6 +- .../datamodel/ArtifactStringContent.java | 5 +- .../datamodel/AutopsyVisitableItem.java | 2 +- .../datamodel/DisplayableItemNodeVisitor.java | 8 +- .../autopsy/datamodel/FileTypesByExtNode.java | 460 ------------------ .../datamodel/FileTypesByExtension.java | 455 ++++++++++++++++- 6 files changed, 447 insertions(+), 489 deletions(-) delete mode 100644 Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtNode.java diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java index c63cc81a76..3559bb79d7 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2014 Basis Technology Corp. + * Copyright 2011-2016 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -107,7 +107,7 @@ abstract class AbstractContentChildren extends Keys { public AbstractContentNode visit(VirtualDirectory ld) { return new VirtualDirectoryNode(ld); } - + @Override public AbstractContentNode visit(SlackFile sf) { return new SlackFileNode(sf); @@ -136,7 +136,7 @@ abstract class AbstractContentChildren extends Keys { @Override public AbstractNode visit(FileTypesByExtension sf) { - return new FileTypesByExtNode(sf.getSleuthkitCase(), null); + return new org.sleuthkit.autopsy.datamodel.FileTypesByExtension.FileTypesByExtNode(sf.getSleuthkitCase(), null); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ArtifactStringContent.java b/Core/src/org/sleuthkit/autopsy/datamodel/ArtifactStringContent.java index 0ea877d9a5..5df9f2756f 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ArtifactStringContent.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ArtifactStringContent.java @@ -19,7 +19,6 @@ package org.sleuthkit.autopsy.datamodel; import java.text.SimpleDateFormat; -import java.util.Arrays; import java.util.TimeZone; import java.util.logging.Level; @@ -42,7 +41,7 @@ public class ArtifactStringContent implements StringContent { BlackboardArtifact artifact; private String stringContent = ""; static final Logger logger = Logger.getLogger(ArtifactStringContent.class.getName()); - private static SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + private static final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); public ArtifactStringContent(BlackboardArtifact art) { artifact = art; @@ -74,7 +73,7 @@ public class ArtifactStringContent implements StringContent { buffer.append(""); //NON-NLS buffer.append(attr.getAttributeType().getDisplayName()); buffer.append(""); //NON-NLS - + // value column buffer.append(""); //NON-NLS switch (attr.getAttributeType().getValueType()) { diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AutopsyVisitableItem.java b/Core/src/org/sleuthkit/autopsy/datamodel/AutopsyVisitableItem.java index 90359bf055..6aaa05af94 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AutopsyVisitableItem.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AutopsyVisitableItem.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011 Basis Technology Corp. + * Copyright 2011-2016 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java b/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java index 2ced0c870e..2b9fb9f2b8 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java @@ -58,7 +58,7 @@ public interface DisplayableItemNodeVisitor { */ T visit(ViewsNode vn); - T visit(FileTypesByExtNode.FileExtensionNode fsfn); + T visit(org.sleuthkit.autopsy.datamodel.FileTypesByExtension.FileExtensionNode fsfn); T visit(DeletedContentNode dcn); @@ -68,7 +68,7 @@ public interface DisplayableItemNodeVisitor { T visit(FileSizeNode fsn); - T visit(FileTypesByExtNode sfn); + T visit(org.sleuthkit.autopsy.datamodel.FileTypesByExtension.FileTypesByExtNode sfn); T visit(RecentFilesNode rfn); @@ -212,7 +212,7 @@ public interface DisplayableItemNodeVisitor { } @Override - public T visit(FileTypesByExtNode.FileExtensionNode fsfn) { + public T visit(org.sleuthkit.autopsy.datamodel.FileTypesByExtension.FileExtensionNode fsfn) { return defaultVisit(fsfn); } @@ -257,7 +257,7 @@ public interface DisplayableItemNodeVisitor { } @Override - public T visit(FileTypesByExtNode sfn) { + public T visit(org.sleuthkit.autopsy.datamodel.FileTypesByExtension.FileTypesByExtNode sfn) { return defaultVisit(sfn); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtNode.java deleted file mode 100644 index e055f49d74..0000000000 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtNode.java +++ /dev/null @@ -1,460 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2011-2016 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.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.util.Arrays; -import java.util.List; -import java.util.Observable; -import java.util.Observer; -import java.util.logging.Level; -import org.openide.nodes.AbstractNode; -import org.openide.nodes.ChildFactory; -import org.openide.nodes.Children; -import org.openide.nodes.Node; -import org.openide.nodes.Sheet; -import org.openide.util.NbBundle; -import org.openide.util.lookup.Lookups; -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.core.UserPreferences; -import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.ingest.IngestManager; -import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.ContentVisitor; -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.SleuthkitCase; -import org.sleuthkit.datamodel.TskCoreException; -import org.sleuthkit.datamodel.TskData; - -/** - * Node for root of file types view. Children are nodes for specific types. - */ -class FileTypesByExtNode extends DisplayableItemNode { - - private static final String FNAME = NbBundle.getMessage(FileTypesByExtNode.class, "FileTypesByExtNode.fname.text"); - private final FileTypesByExtension.RootFilter filter; - - /** - * - * @param skCase - * @param filter null to display root node of file type tree, pass in - * something to provide a sub-node. - */ - FileTypesByExtNode(SleuthkitCase skCase, FileTypesByExtension.RootFilter filter) { - super(Children.create(new FileTypesByExtNodeChildren(skCase, filter, null), true), Lookups.singleton(filter == null ? FNAME : filter.getName())); - this.filter = filter; - init(); - } - - /** - * - * @param skCase - * @param filter - * @param o Observable that was created by a higher-level node that provides - * updates on events - */ - private FileTypesByExtNode(SleuthkitCase skCase, FileTypesByExtension.RootFilter filter, Observable o) { - super(Children.create(new FileTypesByExtNodeChildren(skCase, filter, o), true), Lookups.singleton(filter == null ? FNAME : filter.getName())); - this.filter = filter; - init(); - } - - private void init() { - // root node of tree - if (filter == null) { - super.setName(FNAME); - super.setDisplayName(FNAME); - } // sub-node in file tree (i.e. documents, exec, etc.) - else { - super.setName(filter.getName()); - super.setDisplayName(filter.getDisplayName()); - } - this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file_types.png"); //NON-NLS - } - - @Override - public boolean isLeafTypeNode() { - return false; - } - - @Override - public T accept(DisplayableItemNodeVisitor v) { - return v.visit(this); - } - - @Override - protected Sheet createSheet() { - Sheet s = super.createSheet(); - Sheet.Set ss = s.get(Sheet.PROPERTIES); - if (ss == null) { - ss = Sheet.createPropertiesSet(); - s.put(ss); - } - - ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.name.name"), - NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.name.displayName"), - NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.name.desc"), - getName())); - return s; - } - - @Override - public String getItemType() { - /** - * Because Documents and Executable are further expandable, their column - * order settings should be stored separately. - */ - if (filter == null) { - return getClass().getName(); - } - if (filter.equals(FileTypesByExtension.RootFilter.TSK_DOCUMENT_FILTER) - || filter.equals(FileTypesByExtension.RootFilter.TSK_EXECUTABLE_FILTER)) { - return getClass().getName() + filter.getName(); - } - return getClass().getName(); - } - - /** - * - */ - private static class FileTypesByExtNodeChildren extends ChildFactory { - - private final SleuthkitCase skCase; - private final FileTypesByExtension.RootFilter filter; - private final Observable notifier; - - /** - * - * @param skCase - * @param filter Is null for root node - * @param o Observable that provides updates based on events being fired - * (or null if one needs to be created) - */ - private FileTypesByExtNodeChildren(SleuthkitCase skCase, FileTypesByExtension.RootFilter filter, Observable o) { - super(); - this.skCase = skCase; - this.filter = filter; - if (o == null) { - this.notifier = new FileTypesByExtObservable(); - } else { - this.notifier = o; - } - } - - /** - * Listens for case and ingest invest. Updates observers when events are - * fired. FileType and FileTypes nodes are all listening to this. - */ - private final class FileTypesByExtObservable extends Observable { - - private FileTypesByExtObservable() { - IngestManager.getInstance().addIngestJobEventListener(pcl); - IngestManager.getInstance().addIngestModuleEventListener(pcl); - Case.addPropertyChangeListener(pcl); - } - - private void removeListeners() { - deleteObservers(); - IngestManager.getInstance().removeIngestJobEventListener(pcl); - IngestManager.getInstance().removeIngestModuleEventListener(pcl); - Case.removePropertyChangeListener(pcl); - } - - private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> { - String eventType = evt.getPropertyName(); - if (eventType.equals(IngestManager.IngestModuleEvent.CONTENT_CHANGED.toString()) - || eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString()) - || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString()) - || eventType.equals(Case.Events.DATA_SOURCE_ADDED.toString())) { - /** - * Checking for a current case is a stop gap measure - * until a different way of handling the closing of - * cases is worked out. Currently, remote events may be - * received for a case that is already closed. - */ - try { - Case.getCurrentCase(); - update(); - } catch (IllegalStateException notUsed) { - /** - * Case is closed, do nothing. - */ - } - } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) { - // case was closed. Remove listeners so that we don't get called with a stale case handle - if (evt.getNewValue() == null) { - removeListeners(); - } - } - }; - - private void update() { - setChanged(); - notifyObservers(); - } - } - - @Override - protected boolean createKeys(List list) { - // root node - if (filter == null) { - list.addAll(Arrays.asList(FileTypesByExtension.RootFilter.values())); - } // document and executable has another level of nodes - else if (filter.equals(FileTypesByExtension.RootFilter.TSK_DOCUMENT_FILTER)) { - list.addAll(Arrays.asList(FileTypesByExtension.DocumentFilter.values())); - } else if (filter.equals(FileTypesByExtension.RootFilter.TSK_EXECUTABLE_FILTER)) { - list.addAll(Arrays.asList(FileTypesByExtension.ExecutableFilter.values())); - } - return true; - } - - @Override - protected Node createNodeForKey(FileTypesByExtension.SearchFilterInterface key) { - // make new nodes for the sub-nodes - if (key.getName().equals(FileTypesByExtension.RootFilter.TSK_DOCUMENT_FILTER.getName())) { - return new FileTypesByExtNode(skCase, FileTypesByExtension.RootFilter.TSK_DOCUMENT_FILTER, notifier); - } else if (key.getName().equals(FileTypesByExtension.RootFilter.TSK_EXECUTABLE_FILTER.getName())) { - return new FileTypesByExtNode(skCase, FileTypesByExtension.RootFilter.TSK_EXECUTABLE_FILTER, notifier); - } else { - return new FileExtensionNode(key, skCase, notifier); - } - } - } - - /** - * Node for a specific file type / extension. Children of it will be the - * files of that type. - */ - static class FileExtensionNode extends DisplayableItemNode { - - FileTypesByExtension.SearchFilterInterface filter; - SleuthkitCase skCase; - - /** - * - * @param filter Extensions that will be shown for this node - * @param skCase - * @param o Observable that sends updates when the child factories - * should refresh - */ - FileExtensionNode(FileTypesByExtension.SearchFilterInterface filter, SleuthkitCase skCase, Observable o) { - super(Children.create(new FileExtensionNodeChildren(filter, skCase, o), true), Lookups.singleton(filter.getDisplayName())); - this.filter = filter; - this.skCase = skCase; - init(); - o.addObserver(new ByExtNodeObserver()); - } - - private void init() { - super.setName(filter.getName()); - updateDisplayName(); - this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file-filter-icon.png"); //NON-NLS - } - - // update the display name when new events are fired - private class ByExtNodeObserver implements Observer { - - @Override - public void update(Observable o, Object arg) { - updateDisplayName(); - } - } - - private void updateDisplayName() { - final long count = FileExtensionNodeChildren.calculateItems(skCase, filter); - super.setDisplayName(filter.getDisplayName() + " (" + count + ")"); - } - - @Override - public T accept(DisplayableItemNodeVisitor v) { - return v.visit(this); - } - - @Override - protected Sheet createSheet() { - Sheet s = super.createSheet(); - Sheet.Set ss = s.get(Sheet.PROPERTIES); - if (ss == null) { - ss = Sheet.createPropertiesSet(); - s.put(ss); - } - - ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.filterType.name"), - NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.filterType.displayName"), - NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.filterType.desc"), - filter.getDisplayName())); - String extensions = ""; - for (String ext : filter.getFilter()) { - extensions += "'" + ext + "', "; - } - extensions = extensions.substring(0, extensions.lastIndexOf(',')); - ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.fileExt.name"), - NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.fileExt.displayName"), - NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.fileExt.desc"), - extensions)); - - return s; - } - - @Override - public boolean isLeafTypeNode() { - return true; - } - - /** - * Consider allowing different configurations for Images, Videos, etc - * (in which case we'd return getClass().getName() + filter.getName() - * for all filters). - */ - @Override - public String getItemType() { - return DisplayableItemNode.FILE_PARENT_NODE_KEY; - } - - /** - * Child node factory for a specific file type - does the database - * query. - */ - private static class FileExtensionNodeChildren extends ChildFactory.Detachable { - - private final SleuthkitCase skCase; - private final FileTypesByExtension.SearchFilterInterface filter; - private final static Logger LOGGER = Logger.getLogger(FileExtensionNodeChildren.class.getName()); - private final Observable notifier; - - /** - * - * @param filter Extensions to display - * @param skCase - * @param o Observable that will notify when there could be new data - * to display - */ - private FileExtensionNodeChildren(FileTypesByExtension.SearchFilterInterface filter, SleuthkitCase skCase, Observable o) { - super(); - this.filter = filter; - this.skCase = skCase; - notifier = o; - } - - @Override - protected void addNotify() { - if (notifier != null) { - notifier.addObserver(observer); - } - } - - @Override - protected void removeNotify() { - if (notifier != null) { - notifier.deleteObserver(observer); - } - } - private final Observer observer = new FileTypeChildFactoryObserver(); - - // Cause refresh of children if there are changes - private class FileTypeChildFactoryObserver implements Observer { - - @Override - public void update(Observable o, Object arg) { - refresh(true); - } - } - - /** - * Get children count without actually loading all nodes - * - * @return - */ - private static long calculateItems(SleuthkitCase sleuthkitCase, FileTypesByExtension.SearchFilterInterface filter) { - try { - return sleuthkitCase.countFilesWhere(createQuery(filter)); - } catch (TskCoreException ex) { - LOGGER.log(Level.SEVERE, "Error getting file search view count", ex); //NON-NLS - return 0; - } - } - - @Override - protected boolean createKeys(List list) { - try { - List files = skCase.findAllFilesWhere(createQuery(filter)); - list.addAll(files); - } catch (TskCoreException ex) { - LOGGER.log(Level.SEVERE, "Couldn't get search results", ex); //NON-NLS - } - return true; - } - - private static String createQuery(FileTypesByExtension.SearchFilterInterface filter) { - StringBuilder query = new StringBuilder(); - query.append("(dir_type = ").append(TskData.TSK_FS_NAME_TYPE_ENUM.REG.getValue()).append(")"); //NON-NLS - if (UserPreferences.hideKnownFilesInViewsTree()) { - query.append(" AND (known IS NULL OR known != ").append(TskData.FileKnown.KNOWN.getFileKnownValue()).append(")"); //NON-NLS - } - query.append(" AND (NULL"); //NON-NLS - for (String s : filter.getFilter()) { - query.append(" OR LOWER(name) LIKE LOWER('%").append(s).append("')"); //NON-NLS - } - query.append(')'); - return query.toString(); - } - - @Override - protected Node createNodeForKey(Content key) { - return key.accept(new ContentVisitor.Default() { - @Override - public FileNode visit(File f) { - return new FileNode(f, false); - } - - @Override - public DirectoryNode visit(Directory d) { - return new DirectoryNode(d); - } - - @Override - public LayoutFileNode visit(LayoutFile lf) { - return new LayoutFileNode(lf); - } - - @Override - public LocalFileNode visit(DerivedFile df) { - return new LocalFileNode(df); - } - - @Override - public LocalFileNode visit(LocalFile lf) { - return new LocalFileNode(lf); - } - - @Override - protected AbstractNode defaultVisit(Content di) { - throw new UnsupportedOperationException(NbBundle.getMessage(this.getClass(), "FileTypeChildren.exception.notSupported.msg", di.toString())); - } - }); - } - } - } -} diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java index 2fcc399a77..6e776e6762 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java @@ -18,20 +18,452 @@ */ package org.sleuthkit.autopsy.datamodel; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import java.util.Arrays; import java.util.List; +import java.util.Observable; +import java.util.Observer; +import java.util.logging.Level; +import org.openide.nodes.AbstractNode; +import org.openide.nodes.ChildFactory; +import org.openide.nodes.Children; +import org.openide.nodes.Node; +import org.openide.nodes.Sheet; import org.openide.util.NbBundle; +import org.openide.util.lookup.Lookups; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.core.UserPreferences; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.ingest.IngestManager; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.ContentVisitor; +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.SleuthkitCase; +import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.TskData; /** * Filters database results by file extension. */ -public class FileTypesByExtension implements AutopsyVisitableItem { + final class FileTypesByExtension implements AutopsyVisitableItem { private final SleuthkitCase skCase; + public FileTypesByExtension(SleuthkitCase skCase) { + this.skCase = skCase; + } + + public SleuthkitCase getSleuthkitCase() { + return this.skCase; + } + + @Override + public T accept(AutopsyItemVisitor v) { + return v.visit(this); + } + + /** + * Listens for case and ingest invest. Updates observers when events are + * fired. FileType and FileTypes nodes are all listening to this. + */ + private static class FileTypesByExtObservable extends Observable { + + private FileTypesByExtObservable() { + super(); + IngestManager.getInstance().addIngestJobEventListener(pcl); + IngestManager.getInstance().addIngestModuleEventListener(pcl); + Case.addPropertyChangeListener(pcl); + } + + private void removeListeners() { + deleteObservers(); + IngestManager.getInstance().removeIngestJobEventListener(pcl); + IngestManager.getInstance().removeIngestModuleEventListener(pcl); + Case.removePropertyChangeListener(pcl); + } + + private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> { + String eventType = evt.getPropertyName(); + if (eventType.equals(IngestManager.IngestModuleEvent.CONTENT_CHANGED.toString()) || eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString()) || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString()) || eventType.equals(Case.Events.DATA_SOURCE_ADDED.toString())) { + /** + * Checking for a current case is a stop gap measure until a + * different way of handling the closing of cases is worked out. + * Currently, remote events may be received for a case that is + * already closed. + */ + try { + Case.getCurrentCase(); + update(); + } catch (IllegalStateException notUsed) { + /** + * Case is closed, do nothing. + */ + } + } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) { + // case was closed. Remove listeners so that we don't get called with a stale case handle + if (evt.getNewValue() == null) { + removeListeners(); + } + } + }; + + private void update() { + setChanged(); + notifyObservers(); + } + } + + /** + * Node for root of file types view. Children are nodes for specific types. + */ + static class FileTypesByExtNode extends DisplayableItemNode { + + private static final String FNAME = NbBundle.getMessage(FileTypesByExtNode.class, "FileTypesByExtNode.fname.text"); + private final FileTypesByExtension.RootFilter filter; + + /** + * + * @param skCase + * @param filter null to display root node of file type tree, pass in + * something to provide a sub-node. + */ + FileTypesByExtNode(SleuthkitCase skCase, FileTypesByExtension.RootFilter filter) { + super(Children.create(new FileTypesByExtNodeChildren(skCase, filter, null), true), Lookups.singleton(filter == null ? FNAME : filter.getName())); + this.filter = filter; + init(); + } + + /** + * + * @param skCase + * @param filter + * @param o Observable that was created by a higher-level node that + * provides updates on events + */ + private FileTypesByExtNode(SleuthkitCase skCase, FileTypesByExtension.RootFilter filter, Observable o) { + super(Children.create(new FileTypesByExtNodeChildren(skCase, filter, o), true), Lookups.singleton(filter == null ? FNAME : filter.getName())); + this.filter = filter; + init(); + } + + private void init() { + // root node of tree + if (filter == null) { + super.setName(FNAME); + super.setDisplayName(FNAME); + } // sub-node in file tree (i.e. documents, exec, etc.) + else { + super.setName(filter.getName()); + super.setDisplayName(filter.getDisplayName()); + } + this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file_types.png"); //NON-NLS + } + + @Override + public boolean isLeafTypeNode() { + return false; + } + + @Override + public T accept(DisplayableItemNodeVisitor v) { + return v.visit(this); + } + + @Override + protected Sheet createSheet() { + Sheet s = super.createSheet(); + Sheet.Set ss = s.get(Sheet.PROPERTIES); + if (ss == null) { + ss = Sheet.createPropertiesSet(); + s.put(ss); + } + ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.name.name"), NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.name.displayName"), NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.name.desc"), getName())); + return s; + } + + @Override + public String getItemType() { + /** + * Because Documents and Executable are further expandable, their + * column order settings should be stored separately. + */ + if (filter == null) { + return getClass().getName(); + } + if (filter.equals(FileTypesByExtension.RootFilter.TSK_DOCUMENT_FILTER) || filter.equals(FileTypesByExtension.RootFilter.TSK_EXECUTABLE_FILTER)) { + return getClass().getName() + filter.getName(); + } + return getClass().getName(); + } + + private static class FileTypesByExtNodeChildren extends ChildFactory { + + private final SleuthkitCase skCase; + private final FileTypesByExtension.RootFilter filter; + private final Observable notifier; + + /** + * + * @param skCase + * @param filter Is null for root node + * @param o Observable that provides updates based on events + * being fired (or null if one needs to be created) + */ + private FileTypesByExtNodeChildren(SleuthkitCase skCase, FileTypesByExtension.RootFilter filter, Observable o) { + super(); + this.skCase = skCase; + this.filter = filter; + if (o == null) { + this.notifier = new FileTypesByExtObservable(); + } else { + this.notifier = o; + } + } + + @Override + protected boolean createKeys(List list) { + // root node + if (filter == null) { + list.addAll(Arrays.asList(FileTypesByExtension.RootFilter.values())); + } // document and executable has another level of nodes + else if (filter.equals(FileTypesByExtension.RootFilter.TSK_DOCUMENT_FILTER)) { + list.addAll(Arrays.asList(FileTypesByExtension.DocumentFilter.values())); + } else if (filter.equals(FileTypesByExtension.RootFilter.TSK_EXECUTABLE_FILTER)) { + list.addAll(Arrays.asList(FileTypesByExtension.ExecutableFilter.values())); + } + return true; + } + + @Override + protected Node createNodeForKey(FileTypesByExtension.SearchFilterInterface key) { + // make new nodes for the sub-nodes + if (key.getName().equals(FileTypesByExtension.RootFilter.TSK_DOCUMENT_FILTER.getName())) { + return new FileTypesByExtNode(skCase, FileTypesByExtension.RootFilter.TSK_DOCUMENT_FILTER, notifier); + } else if (key.getName().equals(FileTypesByExtension.RootFilter.TSK_EXECUTABLE_FILTER.getName())) { + return new FileTypesByExtNode(skCase, FileTypesByExtension.RootFilter.TSK_EXECUTABLE_FILTER, notifier); + } else { + return new FileExtensionNode(key, skCase, notifier); + } + } + } + + } + + /** + * Node for a specific file type / extension. Children of it will be the + * files of that type. + */ + static class FileExtensionNode extends DisplayableItemNode { + + FileTypesByExtension.SearchFilterInterface filter; + SleuthkitCase skCase; + + /** + * + * @param filter Extensions that will be shown for this node + * @param skCase + * @param o Observable that sends updates when the child factories + * should refresh + */ + FileExtensionNode(FileTypesByExtension.SearchFilterInterface filter, SleuthkitCase skCase, Observable o) { + super(Children.create(new FileExtensionNodeChildren(filter, skCase, o), true), Lookups.singleton(filter.getDisplayName())); + this.filter = filter; + this.skCase = skCase; + init(); + o.addObserver(new ByExtNodeObserver()); + } + + private void init() { + super.setName(filter.getName()); + updateDisplayName(); + this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file-filter-icon.png"); //NON-NLS + } + + // update the display name when new events are fired + private class ByExtNodeObserver implements Observer { + + @Override + public void update(Observable o, Object arg) { + updateDisplayName(); + } + } + + private void updateDisplayName() { + final long count = FileExtensionNodeChildren.calculateItems(skCase, filter); + super.setDisplayName(filter.getDisplayName() + " (" + count + ")"); + } + + @Override + public T accept(DisplayableItemNodeVisitor v) { + return v.visit(this); + } + + @Override + protected Sheet createSheet() { + Sheet s = super.createSheet(); + Sheet.Set ss = s.get(Sheet.PROPERTIES); + if (ss == null) { + ss = Sheet.createPropertiesSet(); + s.put(ss); + } + ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.filterType.name"), NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.filterType.displayName"), NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.filterType.desc"), filter.getDisplayName())); + String extensions = ""; + for (String ext : filter.getFilter()) { + extensions += "'" + ext + "', "; + } + extensions = extensions.substring(0, extensions.lastIndexOf(',')); + ss.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.fileExt.name"), NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.fileExt.displayName"), NbBundle.getMessage(this.getClass(), "FileTypesByExtNode.createSheet.fileExt.desc"), extensions)); + return s; + } + + @Override + public boolean isLeafTypeNode() { + return true; + } + + /** + * Consider allowing different configurations for Images, Videos, etc + * (in which case we'd return getClass().getName() + filter.getName() + * for all filters). + */ + @Override + public String getItemType() { + return DisplayableItemNode.FILE_PARENT_NODE_KEY; + } + + /** + * Child node factory for a specific file type - does the database + * query. + */ + private static class FileExtensionNodeChildren extends ChildFactory.Detachable { + + private final SleuthkitCase skCase; + private final FileTypesByExtension.SearchFilterInterface filter; + private static final Logger LOGGER = Logger.getLogger(FileExtensionNodeChildren.class.getName()); + private final Observable notifier; + + /** + * + * @param filter Extensions to display + * @param skCase + * @param o Observable that will notify when there could be new + * data to display + */ + private FileExtensionNodeChildren(FileTypesByExtension.SearchFilterInterface filter, SleuthkitCase skCase, Observable o) { + super(); + this.filter = filter; + this.skCase = skCase; + notifier = o; + } + + @Override + protected void addNotify() { + if (notifier != null) { + notifier.addObserver(observer); + } + } + + @Override + protected void removeNotify() { + if (notifier != null) { + notifier.deleteObserver(observer); + } + } + private final Observer observer = new FileTypeChildFactoryObserver(); + + // Cause refresh of children if there are changes + private class FileTypeChildFactoryObserver implements Observer { + + @Override + public void update(Observable o, Object arg) { + refresh(true); + } + } + + /** + * Get children count without actually loading all nodes + * + * @return + */ + private static long calculateItems(SleuthkitCase sleuthkitCase, FileTypesByExtension.SearchFilterInterface filter) { + try { + return sleuthkitCase.countFilesWhere(createQuery(filter)); + } catch (TskCoreException ex) { + LOGGER.log(Level.SEVERE, "Error getting file search view count", ex); //NON-NLS + return 0; + } + } + + @Override + protected boolean createKeys(List list) { + try { + List files = skCase.findAllFilesWhere(createQuery(filter)); + list.addAll(files); + } catch (TskCoreException ex) { + LOGGER.log(Level.SEVERE, "Couldn't get search results", ex); //NON-NLS + } + return true; + } + + private static String createQuery(FileTypesByExtension.SearchFilterInterface filter) { + StringBuilder query = new StringBuilder(); + query.append("(dir_type = ").append(TskData.TSK_FS_NAME_TYPE_ENUM.REG.getValue()).append(")"); //NON-NLS + if (UserPreferences.hideKnownFilesInViewsTree()) { + query.append(" AND (known IS NULL OR known != ").append(TskData.FileKnown.KNOWN.getFileKnownValue()).append(")"); //NON-NLS + } + query.append(" AND (NULL"); //NON-NLS + for (String s : filter.getFilter()) { + query.append(" OR LOWER(name) LIKE LOWER('%").append(s).append("')"); //NON-NLS + } + query.append(')'); + return query.toString(); + } + + @Override + protected Node createNodeForKey(Content key) { + return key.accept(new ContentVisitor.Default() { + @Override + public FileNode visit(File f) { + return new FileNode(f, false); + } + + @Override + public DirectoryNode visit(Directory d) { + return new DirectoryNode(d); + } + + @Override + public LayoutFileNode visit(LayoutFile lf) { + return new LayoutFileNode(lf); + } + + @Override + public LocalFileNode visit(DerivedFile df) { + return new LocalFileNode(df); + } + + @Override + public LocalFileNode visit(LocalFile lf) { + return new LocalFileNode(lf); + } + + @Override + protected AbstractNode defaultVisit(Content di) { + throw new UnsupportedOperationException(NbBundle.getMessage(this.getClass(), "FileTypeChildren.exception.notSupported.msg", di.toString())); + } + }); + } + } + } + // root node filters - public enum RootFilter implements AutopsyVisitableItem, SearchFilterInterface { + enum RootFilter implements AutopsyVisitableItem, SearchFilterInterface { TSK_IMAGE_FILTER(0, "TSK_IMAGE_FILTER", //NON-NLS NbBundle.getMessage(FileTypesByExtension.class, "FileTypeExtensionFilters.tskImgFilter.text"), @@ -91,7 +523,7 @@ public class FileTypesByExtension implements AutopsyVisitableItem { } // document sub-node filters - public enum DocumentFilter implements AutopsyVisitableItem, SearchFilterInterface { + enum DocumentFilter implements AutopsyVisitableItem, SearchFilterInterface { AUT_DOC_HTML(0, "AUT_DOC_HTML", //NON-NLS NbBundle.getMessage(FileTypesByExtension.class, "FileTypeExtensionFilters.autDocHtmlFilter.text"), @@ -148,7 +580,7 @@ public class FileTypesByExtension implements AutopsyVisitableItem { } // executable sub-node filters - public enum ExecutableFilter implements AutopsyVisitableItem, SearchFilterInterface { + enum ExecutableFilter implements AutopsyVisitableItem, SearchFilterInterface { ExecutableFilter_EXE(0, "ExecutableFilter_EXE", ".exe", Arrays.asList(".exe")), //NON-NLS ExecutableFilter_DLL(1, "ExecutableFilter_DLL", ".dll", Arrays.asList(".dll")), //NON-NLS @@ -194,20 +626,7 @@ public class FileTypesByExtension implements AutopsyVisitableItem { } } - public FileTypesByExtension(SleuthkitCase skCase) { - this.skCase = skCase; - } - - @Override - public T accept(AutopsyItemVisitor v) { - return v.visit(this); - } - - public SleuthkitCase getSleuthkitCase() { - return this.skCase; - } - - public interface SearchFilterInterface { + interface SearchFilterInterface { public String getName();