diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentChildren.java index 85e405d27c..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"); @@ -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; @@ -109,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); @@ -137,8 +135,8 @@ abstract class AbstractContentChildren extends Keys { } @Override - public AbstractNode visit(FileTypeExtensionFilters sf) { - return new FileTypesByExtNode(sf.getSleuthkitCase(), null); + public AbstractNode visit(FileTypesByExtension sf) { + return new org.sleuthkit.autopsy.datamodel.FileTypesByExtension.FileTypesByExtNode(sf.getSleuthkitCase(), null); } @Override @@ -220,7 +218,7 @@ abstract class AbstractContentChildren extends Keys { @Override public AbstractNode visit(FileTypesByMimeType ftByMimeTypeItem) { - return ftByMimeTypeItem.new FileTypesByMimeTypeNode(ftByMimeTypeItem.getSleuthkitCase()); + return ftByMimeTypeItem.new ByMimeTypeNode(); } } } 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/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/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/Bundle.properties b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties index 4b9d0225a6..cbe816ffe7 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 +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 +FileTypesByExtNode.createSheet.name.desc=no description HashsetHits.createSheet.name.name=Name HashsetHits.createSheet.name.displayName=Name HashsetHits.createSheet.name.desc=no description @@ -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/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle_ja.properties index 036ff29159..4dfb781a45 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle_ja.properties @@ -112,16 +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 -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 +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 diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java b/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java index f7fdb96243..2b9fb9f2b8 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(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); @@ -146,14 +146,13 @@ public interface DisplayableItemNodeVisitor { T visit(FileTypes.FileTypesNode fileTypes); - T visit(FileTypesByMimeType.FileTypesByMimeTypeNode ftByMimeTypeNode); + T visit(FileTypesByMimeType.ByMimeTypeNode ftByMimeTypeNode); T visit(FileTypesByMimeType.MediaTypeNode ftByMimeTypeMediaType); T visit(FileTypesByMimeType.MediaSubTypeNode ftByMimeTypeMediaSubType); - T visit(FileTypesByMimeType.EmptyNode.MessageNode aThis); - + T visit(EmptyNode.MessageNode emptyNode); /** * Visitor with an implementable default behavior for all types. Override @@ -213,30 +212,30 @@ public interface DisplayableItemNodeVisitor { } @Override - public T visit(FileTypeByExtNode fsfn) { + public T visit(org.sleuthkit.autopsy.datamodel.FileTypesByExtension.FileExtensionNode fsfn) { return defaultVisit(fsfn); } - + @Override - public T visit(FileTypesByMimeType.FileTypesByMimeTypeNode ftByMimeTypeNode) { + 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(FileTypesByMimeType.EmptyNode.MessageNode ftByMimeTypeEmptyNode) { + public T visit(EmptyNode.MessageNode ftByMimeTypeEmptyNode) { return defaultVisit(ftByMimeTypeEmptyNode); } - + @Override public T visit(DeletedContentNode dcn) { return defaultVisit(dcn); @@ -258,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); } @@ -296,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); @@ -425,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 new file mode 100644 index 0000000000..c822743c23 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datamodel/EmptyNode.java @@ -0,0 +1,97 @@ +/* + * 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.util.List; +import org.openide.nodes.AbstractNode; +import org.openide.nodes.ChildFactory; +import org.openide.nodes.Children; +import org.openide.nodes.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. + */ +public final class EmptyNode extends AbstractNode { + + /** + * 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 displayedMessage; + + private EmptyNodeChildren(String displayedMessage) { + this.displayedMessage = displayedMessage; + } + + @Override + protected boolean createKeys(List keys) { + keys.add(displayedMessage); + return true; + } + + @Override + protected Node createNodeForKey(String key) { + return new MessageNode(key); + } + + } + + /** + * 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 { + + 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 deleted file mode 100644 index 6d94b02035..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. - */ -public 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(), "FileTypeNode.createSheet.filterType.name"), - NbBundle.getMessage(this.getClass(), "FileTypeNode.createSheet.filterType.displayName"), - NbBundle.getMessage(this.getClass(), "FileTypeNode.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"), - 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. - */ - public 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 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 - */ - 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 - */ - 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/FileTypeExtensions.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypeExtensions.java index d9cd01b791..4195b33008 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. + * + * 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. @@ -74,4 +74,8 @@ public class FileTypeExtensions { public static List getArchiveExtensions() { return ARCHIVE_EXTENSIONS; } + + private FileTypeExtensions() { + } + } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java index 54a4c792c8..6998b2ef71 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypes.java @@ -22,15 +22,14 @@ 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; /** * File Types node support */ -public class FileTypes implements AutopsyVisitableItem { +public final class FileTypes implements AutopsyVisitableItem { - private SleuthkitCase skCase; + private final SleuthkitCase skCase; FileTypes(SleuthkitCase skCase) { this.skCase = skCase; @@ -48,14 +47,14 @@ 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 FileTypesByExtension(sleuthkitCase), new FileTypesByMimeType(sleuthkitCase) )), Lookups.singleton(NAME)); setName(NAME); @@ -75,9 +74,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 +85,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 deleted file mode 100644 index 5afb64fef8..0000000000 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtNode.java +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2011-2015 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.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.util.Arrays; -import java.util.List; -import java.util.Observable; -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.ingest.IngestManager; -import org.sleuthkit.datamodel.SleuthkitCase; - -/** - * Node for root of file types view. Children are nodes for specific types. - */ -public class FileTypesByExtNode extends DisplayableItemNode { - - private static final String FNAME = NbBundle.getMessage(FileTypesByExtNode.class, "FileTypesNode.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. - */ - FileTypesByExtNode(SleuthkitCase skCase, FileTypeExtensionFilters.RootFilter filter) { - super(Children.create(new FileTypesByExtChildren(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, FileTypeExtensionFilters.RootFilter filter, Observable o) { - super(Children.create(new FileTypesByExtChildren(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(), "FileTypesNode.createSheet.name.name"), - NbBundle.getMessage(this.getClass(), "FileTypesNode.createSheet.name.displayName"), - NbBundle.getMessage(this.getClass(), "FileTypesNode.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(FileTypeExtensionFilters.RootFilter.TSK_DOCUMENT_FILTER) || - filter.equals(FileTypeExtensionFilters.RootFilter.TSK_EXECUTABLE_FILTER)) - return getClass().getName() + filter.getName(); - return getClass().getName(); - } - - /** - * - */ - static class FileTypesByExtChildren extends ChildFactory { - - private SleuthkitCase skCase; - private FileTypeExtensionFilters.RootFilter filter; - private 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) - */ - public FileTypesByExtChildren(SleuthkitCase skCase, FileTypeExtensionFilters.RootFilter filter, Observable o) { - super(); - this.skCase = skCase; - this.filter = filter; - if (o == null) { - this.notifier = new FileTypesByExtChildrenObservable(); - } 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 FileTypesByExtChildrenObservable extends Observable { - - FileTypesByExtChildrenObservable() { - 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 = 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())) { - /** - * 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(FileTypeExtensionFilters.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())); - } - return true; - } - - @Override - protected Node createNodeForKey(FileTypeExtensionFilters.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); - } else { - return new FileTypeByExtNode(key, skCase, notifier); - } - } - } -} diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java new file mode 100644 index 0000000000..6e776e6762 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java @@ -0,0 +1,639 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2011 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.datamodel; + +import java.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. + */ + 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 + enum RootFilter implements AutopsyVisitableItem, SearchFilterInterface { + + TSK_IMAGE_FILTER(0, "TSK_IMAGE_FILTER", //NON-NLS + NbBundle.getMessage(FileTypesByExtension.class, "FileTypeExtensionFilters.tskImgFilter.text"), + FileTypeExtensions.getImageExtensions()), + TSK_VIDEO_FILTER(1, "TSK_VIDEO_FILTER", //NON-NLS + NbBundle.getMessage(FileTypesByExtension.class, "FileTypeExtensionFilters.tskVideoFilter.text"), + FileTypeExtensions.getVideoExtensions()), + TSK_AUDIO_FILTER(2, "TSK_AUDIO_FILTER", //NON-NLS + NbBundle.getMessage(FileTypesByExtension.class, "FileTypeExtensionFilters.tskAudioFilter.text"), + FileTypeExtensions.getAudioExtensions()), + TSK_ARCHIVE_FILTER(3, "TSK_ARCHIVE_FILTER", //NON-NLS + NbBundle.getMessage(FileTypesByExtension.class, "FileTypeExtensionFilters.tskArchiveFilter.text"), + FileTypeExtensions.getArchiveExtensions()), + TSK_DOCUMENT_FILTER(3, "TSK_DOCUMENT_FILTER", //NON-NLS + 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(FileTypesByExtension.class, "FileTypeExtensionFilters.tskExecFilter.text"), + Arrays.asList(".exe", ".dll", ".bat", ".cmd", ".com")); //NON-NLS + + private final int id; + private final String name; + private final String displayName; + private final List filter; + + private RootFilter(int id, String name, String displayName, List filter) { + this.id = id; + this.name = name; + this.displayName = displayName; + this.filter = filter; + } + + @Override + public T accept(AutopsyItemVisitor v) { + return v.visit(this); + } + + @Override + public String getName() { + return this.name; + } + + @Override + public int getId() { + return this.id; + } + + @Override + public String getDisplayName() { + return this.displayName; + } + + @Override + public List getFilter() { + return this.filter; + } + } + + // document sub-node filters + enum DocumentFilter implements AutopsyVisitableItem, SearchFilterInterface { + + AUT_DOC_HTML(0, "AUT_DOC_HTML", //NON-NLS + NbBundle.getMessage(FileTypesByExtension.class, "FileTypeExtensionFilters.autDocHtmlFilter.text"), + Arrays.asList(".htm", ".html")), //NON-NLS + AUT_DOC_OFFICE(1, "AUT_DOC_OFFICE", //NON-NLS + 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(FileTypesByExtension.class, "FileTypeExtensionFilters.autoDocPdfFilter.text"), + Arrays.asList(".pdf")), //NON-NLS + AUT_DOC_TXT(3, "AUT_DOC_TXT", //NON-NLS + NbBundle.getMessage(FileTypesByExtension.class, "FileTypeExtensionFilters.autDocTxtFilter.text"), + Arrays.asList(".txt")), //NON-NLS + AUT_DOC_RTF(4, "AUT_DOC_RTF", //NON-NLS + NbBundle.getMessage(FileTypesByExtension.class, "FileTypeExtensionFilters.autDocRtfFilter.text"), + Arrays.asList(".rtf")); //NON-NLS + + private final int id; + private final String name; + private final String displayName; + private final List filter; + + private DocumentFilter(int id, String name, String displayName, List filter) { + this.id = id; + this.name = name; + this.displayName = displayName; + this.filter = filter; + } + + @Override + public T accept(AutopsyItemVisitor v) { + return v.visit(this); + } + + @Override + public String getName() { + return this.name; + } + + @Override + public int getId() { + return this.id; + } + + @Override + public String getDisplayName() { + return this.displayName; + } + + @Override + public List getFilter() { + return this.filter; + } + } + + // executable sub-node filters + 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 + ExecutableFilter_BAT(2, "ExecutableFilter_BAT", ".bat", Arrays.asList(".bat")), //NON-NLS + ExecutableFilter_CMD(3, "ExecutableFilter_CMD", ".cmd", Arrays.asList(".cmd")), //NON-NLS + ExecutableFilter_COM(4, "ExecutableFilter_COM", ".com", Arrays.asList(".com")); //NON-NLS + + private final int id; + private final String name; + private final String displayName; + private final List filter; + + private ExecutableFilter(int id, String name, String displayName, List filter) { + this.id = id; + this.name = name; + this.displayName = displayName; + this.filter = filter; + } + + @Override + public T accept(AutopsyItemVisitor v) { + return v.visit(this); + } + + @Override + public String getName() { + return this.name; + } + + @Override + public int getId() { + return this.id; + } + + @Override + public String getDisplayName() { + return this.displayName; + } + + @Override + public List getFilter() { + return this.filter; + } + } + + interface SearchFilterInterface { + + public String getName(); + + public int getId(); + + public String getDisplayName(); + + public List getFilter(); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java index f0fcb90901..4165553e2a 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 { +public final 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 @@ -72,28 +72,25 @@ public class FileTypesByMimeType extends Observable implements AutopsyVisitableI * 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. - */ - } } } }; @@ -128,11 +125,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,35 +155,44 @@ public class FileTypesByMimeType extends Observable implements AutopsyVisitableI FileTypesByMimeType(SleuthkitCase skCase) { IngestManager.getInstance().addIngestJobEventListener(pcl); IngestManager.getInstance().addIngestModuleEventListener(pcl); - FileTypesByMimeType.skCase = skCase; + this.skCase = skCase; populateHashMap(); } - /** - * @return skCase - the sluethkit case - */ - SleuthkitCase getSleuthkitCase() { - return skCase; - } - @Override public T accept(AutopsyItemVisitor v) { return v.visit(this); } + /** + * Method to check if the node in question is a ByMimeTypeNode which is + * empty. + * + * @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 node) { + boolean isEmptyMimeNode = false; + if (node instanceof FileTypesByMimeType.ByMimeTypeNode && ((FileTypesByMimeType.ByMimeTypeNode) node).isEmpty()) { + isEmptyMimeNode = true; + } + return isEmptyMimeNode; + } + /** * 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 ByMimeTypeNode 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)); + ByMimeTypeNode() { + super(Children.create(new ByMimeTypeNodeChildren(), true)); super.setName(NAME); super.setDisplayName(NAME); this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file_types.png"); @@ -207,7 +213,7 @@ public class FileTypesByMimeType extends Observable implements AutopsyVisitableI return getClass().getName(); } - public boolean isEmpty() { + boolean isEmpty() { return existingMimeTypes.isEmpty(); } @@ -217,9 +223,9 @@ public class FileTypesByMimeType extends Observable implements AutopsyVisitableI * 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 { + private class ByMimeTypeNodeChildren extends ChildFactory implements Observer { - public FileTypesByMimeTypeNodeChildren() { + private ByMimeTypeNodeChildren() { super(); addObserver(this); } @@ -245,14 +251,13 @@ public class FileTypesByMimeType extends Observable implements AutopsyVisitableI } /** - * 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 { MediaTypeNode(String name) { - super(Children.create(new MediaTypeChildren(name), true)); + super(Children.create(new MediaTypeNodeChildren(name), true)); setName(name); setDisplayName(name); this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file_types.png"); @@ -276,15 +281,15 @@ public class FileTypesByMimeType extends Observable implements AutopsyVisitableI } /** - * Creates children fro media type nodes, children will be MediaSubTypeNodes + * 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. */ - class MediaTypeChildren extends ChildFactory implements Observer { + private class MediaTypeNodeChildren extends ChildFactory implements Observer { String mediaType; - MediaTypeChildren(String name) { + MediaTypeNodeChildren(String name) { addObserver(this); this.mediaType = name; } @@ -335,7 +340,7 @@ public class FileTypesByMimeType extends Observable implements AutopsyVisitableI */ private void updateDisplayName(String mimeType) { - final long count = new MediaSubTypeNodeChildren(mimeType).calculateItems(getSleuthkitCase(), mimeType); + final long count = new MediaSubTypeNodeChildren(mimeType).calculateItems(skCase, mimeType); super.setDisplayName(mimeType.split("/")[1] + " (" + count + ")"); } @@ -376,7 +381,7 @@ public class FileTypesByMimeType extends Observable implements AutopsyVisitableI private final String mimeType; - MediaSubTypeNodeChildren(String mimeType) { + private MediaSubTypeNodeChildren(String mimeType) { super(); addObserver(this); this.mimeType = mimeType; @@ -388,7 +393,7 @@ public class FileTypesByMimeType extends Observable implements AutopsyVisitableI * @return count(*) - the number of items that will be shown in this * items Directory Listing */ - private long calculateItems(SleuthkitCase sleuthkitCase, String mime_type) { + private long calculateItems(SleuthkitCase sleuthkitCase, String mime_type) { try { return sleuthkitCase.countFilesWhere(createQuery(mime_type)); } catch (TskCoreException ex) { @@ -440,13 +445,12 @@ public class FileTypesByMimeType extends Observable implements AutopsyVisitableI 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 @@ -490,66 +494,4 @@ public class FileTypesByMimeType extends Observable implements AutopsyVisitableI } } - /** - * 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(); - } - } - } - } 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 - diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/accounts/FileTypeExtensionFilters.java b/Core/src/org/sleuthkit/autopsy/datamodel/accounts/FileTypeExtensionFilters.java deleted file mode 100644 index 80680e97cc..0000000000 --- a/Core/src/org/sleuthkit/autopsy/datamodel/accounts/FileTypeExtensionFilters.java +++ /dev/null @@ -1,223 +0,0 @@ -/* - * 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; - -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 { - - private final SleuthkitCase skCase; - - // root node filters - public enum RootFilter implements AutopsyVisitableItem, SearchFilterInterface { - - TSK_IMAGE_FILTER(0, "TSK_IMAGE_FILTER", //NON-NLS - NbBundle.getMessage(FileTypeExtensionFilters.class, "FileTypeExtensionFilters.tskImgFilter.text"), - FileTypeExtensions.getImageExtensions()), - TSK_VIDEO_FILTER(1, "TSK_VIDEO_FILTER", //NON-NLS - NbBundle.getMessage(FileTypeExtensionFilters.class, "FileTypeExtensionFilters.tskVideoFilter.text"), - FileTypeExtensions.getVideoExtensions()), - TSK_AUDIO_FILTER(2, "TSK_AUDIO_FILTER", //NON-NLS - NbBundle.getMessage(FileTypeExtensionFilters.class, "FileTypeExtensionFilters.tskAudioFilter.text"), - FileTypeExtensions.getAudioExtensions()), - TSK_ARCHIVE_FILTER(3, "TSK_ARCHIVE_FILTER", //NON-NLS - NbBundle.getMessage(FileTypeExtensionFilters.class, "FileTypeExtensionFilters.tskArchiveFilter.text"), - FileTypeExtensions.getArchiveExtensions()), - TSK_DOCUMENT_FILTER(3, "TSK_DOCUMENT_FILTER", //NON-NLS - NbBundle.getMessage(FileTypeExtensionFilters.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"), - Arrays.asList(".exe", ".dll", ".bat", ".cmd", ".com")); //NON-NLS - - private final int id; - private final String name; - private final String displayName; - private final List filter; - - private RootFilter(int id, String name, String displayName, List filter) { - this.id = id; - this.name = name; - this.displayName = displayName; - this.filter = filter; - } - - @Override - public T accept(AutopsyItemVisitor v) { - return v.visit(this); - } - - @Override - public String getName() { - return this.name; - } - - @Override - public int getId() { - return this.id; - } - - @Override - public String getDisplayName() { - return this.displayName; - } - - @Override - public List getFilter() { - return this.filter; - } - } - - // document sub-node filters - public enum DocumentFilter implements AutopsyVisitableItem, SearchFilterInterface { - - AUT_DOC_HTML(0, "AUT_DOC_HTML", //NON-NLS - NbBundle.getMessage(FileTypeExtensionFilters.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"), - 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"), - Arrays.asList(".pdf")), //NON-NLS - AUT_DOC_TXT(3, "AUT_DOC_TXT", //NON-NLS - NbBundle.getMessage(FileTypeExtensionFilters.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"), - Arrays.asList(".rtf")); //NON-NLS - - private final int id; - private final String name; - private final String displayName; - private final List filter; - - private DocumentFilter(int id, String name, String displayName, List filter) { - this.id = id; - this.name = name; - this.displayName = displayName; - this.filter = filter; - } - - @Override - public T accept(AutopsyItemVisitor v) { - return v.visit(this); - } - - @Override - public String getName() { - return this.name; - } - - @Override - public int getId() { - return this.id; - } - - @Override - public String getDisplayName() { - return this.displayName; - } - - @Override - public List getFilter() { - return this.filter; - } - } - - // executable sub-node filters - public 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 - ExecutableFilter_BAT(2, "ExecutableFilter_BAT", ".bat", Arrays.asList(".bat")), //NON-NLS - ExecutableFilter_CMD(3, "ExecutableFilter_CMD", ".cmd", Arrays.asList(".cmd")), //NON-NLS - ExecutableFilter_COM(4, "ExecutableFilter_COM", ".com", Arrays.asList(".com")); //NON-NLS - - private final int id; - private final String name; - private final String displayName; - private final List filter; - - private ExecutableFilter(int id, String name, String displayName, List filter) { - this.id = id; - this.name = name; - this.displayName = displayName; - this.filter = filter; - } - - @Override - public T accept(AutopsyItemVisitor v) { - return v.visit(this); - } - - @Override - public String getName() { - return this.name; - } - - @Override - public int getId() { - return this.id; - } - - @Override - public String getDisplayName() { - return this.displayName; - } - - @Override - public List getFilter() { - return this.filter; - } - } - - public FileTypeExtensionFilters(SleuthkitCase skCase) { - this.skCase = skCase; - } - - @Override - public T accept(AutopsyItemVisitor v) { - return v.visit(this); - } - - public SleuthkitCase getSleuthkitCase() { - return this.skCase; - } - - public interface SearchFilterInterface { - - public String getName(); - - public int getId(); - - public String getDisplayName(); - - public List getFilter(); - } -} diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java index 22485148ff..496831fa88 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,7 +62,6 @@ 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; @@ -96,7 +96,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; /** @@ -135,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; } @@ -313,14 +313,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(); @@ -435,7 +435,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 } } @@ -604,6 +604,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,18 +646,15 @@ 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 (originNode instanceof FileTypesByMimeType.FileTypesByMimeTypeNode - && ((FileTypesByMimeType.FileTypesByMimeTypeNode) originNode).isEmpty()) { - EmptyNode emptyNode = new EmptyNode(); + 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)); 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 { @@ -669,7 +667,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); @@ -776,13 +774,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; } @@ -825,7 +823,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; } } @@ -840,7 +838,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 } } } @@ -880,7 +878,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); @@ -907,7 +905,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()) { @@ -924,7 +922,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); @@ -942,7 +940,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 @@ -977,7 +975,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"), 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()); 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(); } } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/LuceneQuery.java index d4fa4b1716..ae45eb949d 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); @@ -297,52 +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) { - /** - * Filtering down to one hit per document is being performed so that we - * only create a single keyword hit blackboard artifact per file. This - * only makes sense for the case where we are performing an exact match - * query. It does not make sense for (a) the TermsComponentQuery which - * creates blackboard artifacts for the individual terms that are matched - * by the regular expression or (b) HighlightedText.loadPageInfo() which - * (i) doesn't create blackboard artifacts and (ii) needs to know about - * all hits so that it can correctly set up the paging infrastructure. - * Additionally, keyword hit artifact is being performed by - * writeSingleFileHitsToBlackboard() above which is being called by - * QueryResults.writeAllHitsToBlackboard() which appears to be taking care - * of filtering results down to a single hit per document in the - * QueryResults.getOneHitPerObject() method. - */ - - // 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