Merge branch 'artifacts_are_content' of https://github.com/sleuthkit/autopsy into 2689_artifacts_are_content

# Conflicts:
#	Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java
This commit is contained in:
Raman 2017-07-31 12:35:26 -04:00
commit 4fdea244af
23 changed files with 576 additions and 338 deletions

View File

@ -861,7 +861,7 @@ public class EamDbSettingsDialog extends JDialog {
*
* @return true
*/
@Messages({"EamDbSettingsDialog.validation.mustTest=Once you are statisfied with the database settings, click the Test button to test the database connection, settings, and schema.",
@Messages({"EamDbSettingsDialog.validation.mustTest=Once you are statisfied with the database settings, click the Test button to test the database connection, settings, and schema. SQLite should only be used by one examiner at a time.",
"EamDbSettingsDialog.validation.failedConnection=The connection to the database failed. Update the settings and try the Test again."})
private boolean enableTestButton(boolean isValidInput) {
if (selectedPlatform != EamDbPlatformEnum.DISABLED && isValidInput) {

View File

@ -220,6 +220,7 @@ public class Metadata extends javax.swing.JPanel implements DataContentViewer {
@Override
public void resetComponent() {
setText("");
return;
}

View File

@ -493,18 +493,19 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
private final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
private String[][] rowData = null;
private final String artifactDisplayName;
private final Content content;
ResultsTableArtifact(BlackboardArtifact artifact) {
ResultsTableArtifact(BlackboardArtifact artifact, Content content) {
artifactDisplayName = artifact.getDisplayName();
this.content = content;
addRows(artifact);
}
ResultsTableArtifact(String errorMsg) {
artifactDisplayName = errorMsg;
rowData = new String[1][3];
rowData[0] = new String[]{"", errorMsg, ""};
content = null;
}
private String[][] getRows() {
@ -514,7 +515,6 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
private void addRows(BlackboardArtifact artifact) {
List<String[]> rowsToAdd = new ArrayList<>();
try {
Content content = artifact.getSleuthkitCase().getContentById(artifact.getObjectID());
/*
* Add rows for each attribute.
*/
@ -701,12 +701,15 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
if (contents.isEmpty()) {
return new ViewUpdate(getArtifactContents().size(), currentPage, ERROR_TEXT);
}
Content underlyingContent = null;
for (Content content : contents) {
if ( (content != null) && (!(content instanceof BlackboardArtifact)) ) {
// Get all of the blackboard artifacts associated with the content. These are what this
// viewer displays.
try {
artifacts = content.getAllArtifacts();
underlyingContent = content;
break;
} catch (TskException ex) {
logger.log(Level.SEVERE, "Couldn't get artifacts", ex); //NON-NLS
return new ViewUpdate(getArtifactContents().size(), currentPage, ERROR_TEXT);
@ -721,7 +724,7 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
// Build the new artifact contents cache.
ArrayList<ResultsTableArtifact> artifactContents = new ArrayList<>();
for (BlackboardArtifact artifact : artifacts) {
artifactContents.add(new ResultsTableArtifact(artifact));
artifactContents.add(new ResultsTableArtifact(artifact, underlyingContent));
}
// If the node has an underlying blackboard artifact, show it. If not,

View File

@ -28,6 +28,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
@ -104,11 +105,11 @@ class ThumbnailViewChildren extends Children.Keys<Integer> {
* children nodes (which might not be preloaded at this point).
*/
// get list of supported children sorted by persisted criteria
final List<Node> suppContent =
Stream.of(parent.getChildren().getNodes())
.filter(ThumbnailViewChildren::isSupported)
.sorted(getComparator())
.collect(Collectors.toList());
final List<Node> suppContent
= Stream.of(parent.getChildren().getNodes())
.filter(ThumbnailViewChildren::isSupported)
.sorted(getComparator())
.collect(Collectors.toList());
if (suppContent.isEmpty()) {
//if there are no images, there is nothing more to do
@ -314,7 +315,12 @@ class ThumbnailViewChildren extends Children.Keys<Integer> {
private boolean cancelled = false;
ThumbnailLoadTask() {
super(() -> ImageUtils.getThumbnail(content, thumbSize));
super(new Callable<Image>() { //Does not work as lambda expression in dependent projects in IDE
public Image call() {
return ImageUtils.getThumbnail(content, thumbSize);
}
});
//super(() -> ImageUtils.getThumbnail(content, thumbSize));
progressText = Bundle.ThumbnailViewNode_progressHandle_text(content.getName());
progressHandle = ProgressHandleFactory.createSystemHandle(progressText);

View File

@ -18,6 +18,8 @@
*/
package org.sleuthkit.autopsy.datamodel;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.MessageFormat;
@ -26,6 +28,8 @@ import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.stream.Collectors;
import javax.swing.Action;
@ -35,12 +39,12 @@ import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagAddedEvent;
import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagDeletedEvent;
import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent;
import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import static org.sleuthkit.autopsy.datamodel.DisplayableItemNode.findLinked;
import org.sleuthkit.autopsy.timeline.actions.ViewArtifactInTimelineAction;
import org.sleuthkit.autopsy.timeline.actions.ViewFileInTimelineAction;
@ -59,10 +63,15 @@ import org.sleuthkit.datamodel.TskCoreException;
*/
public class BlackboardArtifactNode extends DisplayableItemNode {
private static final Logger LOGGER = Logger.getLogger(BlackboardArtifactNode.class.getName());
private static Cache<Long, Content> contentCache = CacheBuilder.newBuilder()
.expireAfterWrite(1, TimeUnit.MINUTES).
build();
private final BlackboardArtifact artifact;
private Content associated = null;
private List<NodeProperty<? extends Object>> customProperties;
private static final Logger LOGGER = Logger.getLogger(BlackboardArtifactNode.class.getName());
/*
* Artifact types which should have the full unique path of the associated
* content as a property.
@ -107,6 +116,7 @@ public class BlackboardArtifactNode extends DisplayableItemNode {
if (evt.getNewValue() == null) {
// case was closed. Remove listeners so that we don't get called with a stale case handle
removeListeners();
contentCache.invalidateAll();
}
}
}
@ -297,7 +307,7 @@ public class BlackboardArtifactNode extends DisplayableItemNode {
// Do nothing since the display name will be set to the file name.
}
}
for (Map.Entry<String, Object> entry : map.entrySet()) {
ss.put(new NodeProperty<>(entry.getKey(),
entry.getKey(),
@ -491,46 +501,46 @@ public class BlackboardArtifactNode extends DisplayableItemNode {
LOGGER.log(Level.SEVERE, "Getting attributes failed", ex); //NON-NLS
}
}
/**
* Fill map with EmailMsg properties, not all attributes are filled
*
* @param map map with preserved ordering, where property names/values
* are put
* @param attribute attribute to check/fill as property
*/
* Fill map with EmailMsg properties, not all attributes are filled
*
* @param map map with preserved ordering, where property names/values
* are put
* @param attribute attribute to check/fill as property
*/
private void addEmailMsgProperty(Map<String, Object> map, BlackboardAttribute attribute ) {
final int attributeTypeID = attribute.getAttributeType().getTypeID();
// Skip certain Email msg attributes
if (attributeTypeID == ATTRIBUTE_TYPE.TSK_DATETIME_SENT.getTypeID()
|| attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_HTML.getTypeID()
|| attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_RTF.getTypeID()
|| attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_BCC.getTypeID()
|| attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CC.getTypeID()
final int attributeTypeID = attribute.getAttributeType().getTypeID();
// Skip certain Email msg attributes
if (attributeTypeID == ATTRIBUTE_TYPE.TSK_DATETIME_SENT.getTypeID()
|| attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_HTML.getTypeID()
|| attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_RTF.getTypeID()
|| attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_BCC.getTypeID()
|| attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CC.getTypeID()
|| attributeTypeID == ATTRIBUTE_TYPE.TSK_HEADERS.getTypeID()
) {
// do nothing
// do nothing
}
else if (attributeTypeID == ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_PLAIN.getTypeID()) {
String value = attribute.getDisplayString();
if (value.length() > 160) {
value = value.substring(0, 160) + "...";
}
map.put(attribute.getAttributeType().getDisplayName(), value);
String value = attribute.getDisplayString();
if (value.length() > 160) {
value = value.substring(0, 160) + "...";
}
map.put(attribute.getAttributeType().getDisplayName(), value);
}
else if (attribute.getAttributeType().getValueType() == BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME) {
map.put(attribute.getAttributeType().getDisplayName(), ContentUtils.getStringTime(attribute.getValueLong(), associated));
map.put(attribute.getAttributeType().getDisplayName(), ContentUtils.getStringTime(attribute.getValueLong(), associated));
}
else {
map.put(attribute.getAttributeType().getDisplayName(), attribute.getDisplayString());
map.put(attribute.getAttributeType().getDisplayName(), attribute.getDisplayString());
}
}
}
@Override
public <T> T accept(DisplayableItemNodeVisitor<T> v) {
return v.visit(this);
@ -539,31 +549,25 @@ public class BlackboardArtifactNode extends DisplayableItemNode {
/**
* Create a Lookup based on what is in the passed in artifact.
*
* @param artifact
* @param artifact The artifact to make a look up for.
*
* @return
* @return A lookup with the artifact and possibly any associated content in
* it.
*/
private static Lookup createLookup(BlackboardArtifact artifact) {
List<Object> forLookup = new ArrayList<>();
forLookup.add(artifact);
// Add the content the artifact is associated with
Content content = getAssociatedContent(artifact);
if (content != null) {
forLookup.add(content);
}
return Lookups.fixed(forLookup.toArray(new Object[forLookup.size()]));
}
private static Content getAssociatedContent(BlackboardArtifact artifact) {
final long objectID = artifact.getObjectID();
try {
return artifact.getSleuthkitCase().getContentById(artifact.getObjectID());
} catch (TskCoreException ex) {
LOGGER.log(Level.WARNING, "Getting file failed", ex); //NON-NLS
Content content = contentCache.get(objectID, () -> artifact.getSleuthkitCase().getContentById(objectID));
if (content == null) {
return Lookups.fixed(artifact);
} else {
return Lookups.fixed(artifact, content);
}
} catch (ExecutionException ex) {
LOGGER.log(Level.WARNING, "Getting associated content for artifact failed", ex); //NON-NLS
return Lookups.fixed(artifact);
}
throw new IllegalArgumentException(
NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.getAssocCont.exception.msg"));
}
@Override

View File

@ -42,7 +42,6 @@ BlackboardArtifactNode.createSheet.filePath.name=File Path
BlackboardArtifactNode.createSheet.filePath.displayName=File Path
BlackboardArtifactNode.createSheet.dataSrc.name=Data Source
BlackboardArtifactNode.createSheet.dataSrc.displayName=Data Source
BlackboardArtifactNode.getAssocCont.exception.msg=Could not get file from database
BlackboardArtifactTagNode.createSheet.srcFile.text=Source File
BlackboardArtifactTagNode.createSheet.unavail.text=Unavailable
BlackboardArtifactTagNode.createSheet.srcFilePath.text=Source File Path
@ -87,8 +86,8 @@ DeletedContent.deletedContentsNode.name=Deleted Files
DeletedContent.createSheet.name.name=Name
DeletedContent.createSheet.name.displayName=Name
DeletedContent.createSheet.name.desc=no description
DeletedContent.createSheet.filterType.name=Filter Type
DeletedContent.createSheet.filterType.displayName=Filter Type
DeletedContent.createSheet.filterType.name=Type
DeletedContent.createSheet.filterType.displayName=Type
DeletedContent.createSheet.filterType.desc=no description
DeletedContent.createKeys.maxObjects.msg=There are more Deleted Files than can be displayed. Only the first {0} Deleted Files will be shown.
DeletedContent.createNodeForKey.typeNotSupported.msg=Not supported for this type of Displayable Item\: {0}
@ -112,13 +111,13 @@ FileSize.fileSizeRootNode.name=File Size
FileSize.createSheet.name.name=Name
FileSize.createSheet.name.displayName=Name
FileSize.createSheet.name.desc=no description
FileSize.createSheet.filterType.name=Filter Type
FileSize.createSheet.filterType.displayName=Filter Type
FileSize.createSheet.filterType.name=Size Range
FileSize.createSheet.filterType.displayName=Size Range
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}
FileTypesByExtNode.createSheet.filterType.name=Filter Type
FileTypesByExtNode.createSheet.filterType.displayName=Filter Type
FileTypesByExtNode.createSheet.filterType.name=File Type
FileTypesByExtNode.createSheet.filterType.displayName=File Type
FileTypesByExtNode.createSheet.filterType.desc=no description
FileTypesByExtNode.createSheet.fileExt.name=File Extensions
FileTypesByExtNode.createSheet.fileExt.displayName=File Extensions

View File

@ -32,7 +32,6 @@ BlackboardArtifactNode.createSheet.filePath.name=\u30d5\u30a1\u30a4\u30eb\u30d1\
BlackboardArtifactNode.createSheet.filePath.displayName=\u30d5\u30a1\u30a4\u30eb\u30d1\u30b9
BlackboardArtifactNode.createSheet.dataSrc.name=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9
BlackboardArtifactNode.createSheet.dataSrc.displayName=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9
BlackboardArtifactNode.getAssocCont.exception.msg=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304b\u3089\u30d5\u30a1\u30a4\u30eb\u3092\u53d6\u5f97\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f
BlackboardArtifactTagNode.createSheet.srcFile.text=\u30bd\u30fc\u30b9\u30d5\u30a1\u30a4\u30eb
BlackboardArtifactTagNode.createSheet.unavail.text=\u4f7f\u7528\u3067\u304d\u307e\u305b\u3093
BlackboardArtifactTagNode.createSheet.srcFilePath.text=\u30bd\u30fc\u30b9\u30d5\u30a1\u30a4\u30eb\u30d1\u30b9
@ -64,8 +63,6 @@ DataSourcesNode.createSheet.name.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b
DeletedContent.fsDelFilter.text=\u30d5\u30a1\u30a4\u30eb\u30b7\u30b9\u30c6\u30e0
DeletedContent.allDelFilter.text=\u3059\u3079\u3066
DeletedContent.deletedContentsNode.name=\u524a\u9664\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb
DeletedContent.createSheet.name.name=\u540d\u524d
DeletedContent.createSheet.name.displayName=\u540d\u524d
DeletedContent.createSheet.name.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093
DeletedContent.createSheet.filterType.name=\u30d5\u30a3\u30eb\u30bf\u30fc\u30bf\u30a4\u30d7
DeletedContent.createSheet.filterType.displayName=\u30d5\u30a3\u30eb\u30bf\u30fc\u30bf\u30a4\u30d7
@ -92,8 +89,6 @@ FileNode.getActions.viewInNewWin.text=\u65b0\u898f\u30a6\u30a3\u30f3\u30c9\u30a6
FileNode.getActions.openInExtViewer.text=\u5916\u90e8\u30d3\u30e5\u30fc\u30a2\u3067\u958b\u304f
FileNode.getActions.searchFilesSameMD5.text=\u540c\u3058MD5\u30cf\u30c3\u30b7\u30e5\u3092\u6301\u3064\u30d5\u30a1\u30a4\u30eb\u3092\u691c\u7d22
FileSize.fileSizeRootNode.name=\u30d5\u30a1\u30a4\u30eb\u30b5\u30a4\u30ba
FileSize.createSheet.name.name=\u540d\u524d
FileSize.createSheet.name.displayName=\u540d\u524d
FileSize.createSheet.name.desc=\u8aac\u660e\u304c\u3042\u308a\u307e\u305b\u3093
FileSize.createSheet.filterType.name=\u30d5\u30a3\u30eb\u30bf\u30fc\u30bf\u30a4\u30d7
FileSize.createSheet.filterType.displayName=\u30d5\u30a3\u30eb\u30bf\u30fc\u30bf\u30a4\u30d7
@ -111,8 +106,6 @@ FileTypeExtensionFilters.autDocOfficeFilter.text=\u30aa\u30d5\u30a3\u30b9
FileTypeExtensionFilters.autoDocPdfFilter.text=PDF
FileTypeExtensionFilters.autDocTxtFilter.text=\u30d7\u30ec\u30fc\u30f3\u30c6\u30ad\u30b9\u30c8
FileTypeExtensionFilters.autDocRtfFilter.text=\u30ea\u30c3\u30c1\u30c6\u30ad\u30b9\u30c8
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

View File

@ -30,7 +30,6 @@ import org.openide.util.Utilities;
import org.sleuthkit.autopsy.actions.AddContentTagAction;
import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction;
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
import org.sleuthkit.autopsy.coreutils.ImageUtils;
import org.sleuthkit.autopsy.directorytree.ExternalViewerAction;
import org.sleuthkit.autopsy.directorytree.ExtractAction;
import org.sleuthkit.autopsy.directorytree.HashSearchAction;
@ -62,8 +61,7 @@ public class FileNode extends AbstractFsContentNode<AbstractFile> {
} else {
ext = "." + ext;
}
if (ImageUtils.isImageThumbnailSupported(file)
|| FileTypeExtensions.getImageExtensions().contains(ext)) {
if (FileTypeExtensions.getImageExtensions().contains(ext)) {
return "org/sleuthkit/autopsy/images/image-file.png"; //NON-NLS
}
if (FileTypeExtensions.getVideoExtensions().contains(ext)) {

View File

@ -45,6 +45,7 @@ import org.sleuthkit.datamodel.File;
import org.sleuthkit.datamodel.FsContent;
import org.sleuthkit.datamodel.LayoutFile;
import org.sleuthkit.datamodel.LocalFile;
import org.sleuthkit.datamodel.SlackFile;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
@ -483,6 +484,11 @@ public class FileSize implements AutopsyVisitableItem {
return new FileNode(f, false);
}
@Override
public FileNode visit(SlackFile f) {
return new FileNode(f, false);
}
@Override
protected AbstractNode defaultVisit(Content di) {
throw new UnsupportedOperationException(

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2016 Basis Technology Corp.
* Copyright 2011-2017 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -18,9 +18,12 @@
*/
package org.sleuthkit.autopsy.datamodel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import javax.swing.SwingWorker;
@ -31,6 +34,8 @@ import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.ContentVisitor;
import org.sleuthkit.datamodel.DerivedFile;
@ -40,6 +45,7 @@ import org.sleuthkit.datamodel.LayoutFile;
import org.sleuthkit.datamodel.LocalFile;
import org.sleuthkit.datamodel.SlackFile;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.SleuthkitItemVisitor;
import org.sleuthkit.datamodel.TskCoreException;
/**
@ -47,10 +53,24 @@ import org.sleuthkit.datamodel.TskCoreException;
*/
public final class FileTypes implements AutopsyVisitableItem {
private final static Logger logger = Logger.getLogger(FileTypes.class.getName());
private static final Logger logger = Logger.getLogger(FileTypes.class.getName());
@NbBundle.Messages("FileTypes.name.text=File Types")
private static final String NAME = Bundle.FileTypes_name_text();
/**
* Threshold used to limit db queries for child node counts. When the
* tsk_files table has more than this number of rows, we don't query for the
* child node counts, and since we don't have an accurate number we don't
* show the counts.
*/
private static final int NODE_COUNT_FILE_TABLE_THRESHOLD = 1_000_000;
/**
* Used to keep track of whether we have hit
* NODE_COUNT_FILE_TABLE_THRESHOLD. If we have, we stop querying for the
* number of rows in tsk_files, since it is already too large.
*/
private boolean showCounts = true;
private final SleuthkitCase skCase;
private boolean showCounts = true;
FileTypes(SleuthkitCase skCase) {
this.skCase = skCase;
@ -66,15 +86,16 @@ public final class FileTypes implements AutopsyVisitableItem {
}
/**
* Should the nodes show counts?
*
*
* @return True, unless the DB has more than 200k rows.
* Check the db to determine if the nodes should show child counts.
*/
boolean shouldShowCounts() {
void updateShowCounts() {
/*
* once we have passed the threshold, we don't need to keep checking the
* number of rows in tsk_files
*/
if (showCounts) {
try {
if (skCase.countFilesWhere("1=1") > 200000) { //NON-NLS
if (skCase.countFilesWhere("1=1") > NODE_COUNT_FILE_TABLE_THRESHOLD) { //NON-NLS
showCounts = false;
}
} catch (TskCoreException tskCoreException) {
@ -82,10 +103,7 @@ public final class FileTypes implements AutopsyVisitableItem {
logger.log(Level.SEVERE, "Error counting files.", tskCoreException); //NON-NLS
}
}
return showCounts;
}
@NbBundle.Messages("FileTypes.name.text=File Types")
static private final String NAME = Bundle.FileTypes_name_text();
/**
* Node which will contain By Mime Type and By Extension nodes.
@ -97,8 +115,8 @@ public final class FileTypes implements AutopsyVisitableItem {
new FileTypesByExtension(FileTypes.this),
new FileTypesByMimeType(FileTypes.this))),
Lookups.singleton(NAME));
setName(NAME);
setDisplayName(NAME);
this.setName(NAME);
this.setDisplayName(NAME);
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file_types.png"); //NON-NLS
}
@ -215,9 +233,9 @@ public final class FileTypes implements AutopsyVisitableItem {
* Updates the display name of the mediaSubTypeNode to include the count
* of files which it represents.
*/
@NbBundle.Messages("FileTypes.bgCounting.placeholder=(counting...)")
@NbBundle.Messages("FileTypes.bgCounting.placeholder= (counting...)")
void updateDisplayName() {
if (typesRoot.shouldShowCounts()) {
if (typesRoot.showCounts) {
//only show "(counting...)" the first time, otherwise it is distracting.
setDisplayName(getDisplayNameBase() + ((childCount < 0) ? Bundle.FileTypes_bgCounting_placeholder()
: ("(" + childCount + ")"))); //NON-NLS
@ -239,8 +257,186 @@ public final class FileTypes implements AutopsyVisitableItem {
}
}.execute();
} else {
setDisplayName(getDisplayNameBase() + ((childCount < 0) ? "" : ("(" + childCount + "+)"))); //NON-NLS
setDisplayName(getDisplayNameBase() + ((childCount < 0) ? "" : (" (" + childCount + "+)"))); //NON-NLS
}
}
}
/**
* Class that is used as a key by NetBeans for creating result nodes. This
* is a wrapper around a Content object and is being put in place as an
* optimization to avoid the Content.hashCode() implementation which issues
* a database query to get the number of children when determining whether 2
* Content objects represent the same thing. TODO: This is a temporary
* solution that can hopefully be removed once we address the issue of
* determining how many children a Content has (JIRA-2823).
*/
static class FileTypesKey implements Content {
private final Content content;
public FileTypesKey(Content content) {
this.content = content;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final FileTypesKey other = (FileTypesKey) obj;
return this.content.getId() == other.content.getId();
}
@Override
public int hashCode() {
int hash = 7;
hash = 101 * hash + (int)(this.content.getId() ^ (this.content.getId() >>> 32));
return hash;
}
@Override
public <T> T accept(SleuthkitItemVisitor<T> v) {
return content.accept(v);
}
@Override
public int read(byte[] buf, long offset, long len) throws TskCoreException {
return content.read(buf, offset, len);
}
@Override
public void close() {
content.close();
}
@Override
public long getSize() {
return content.getSize();
}
@Override
public <T> T accept(ContentVisitor<T> v) {
return content.accept(v);
}
@Override
public String getName() {
return content.getName();
}
@Override
public String getUniquePath() throws TskCoreException {
return content.getUniquePath();
}
@Override
public long getId() {
return content.getId();
}
@Override
public Content getDataSource() throws TskCoreException {
return content.getDataSource();
}
@Override
public List<Content> getChildren() throws TskCoreException {
return content.getChildren();
}
@Override
public boolean hasChildren() throws TskCoreException {
return content.hasChildren();
}
@Override
public int getChildrenCount() throws TskCoreException {
return content.getChildrenCount();
}
@Override
public Content getParent() throws TskCoreException {
return content.getParent();
}
@Override
public List<Long> getChildrenIds() throws TskCoreException {
return content.getChildrenIds();
}
@Override
public BlackboardArtifact newArtifact(int artifactTypeID) throws TskCoreException {
return content.newArtifact(artifactTypeID);
}
@Override
public BlackboardArtifact newArtifact(BlackboardArtifact.ARTIFACT_TYPE type) throws TskCoreException {
return content.newArtifact(type);
}
@Override
public ArrayList<BlackboardArtifact> getArtifacts(String artifactTypeName) throws TskCoreException {
return content.getArtifacts(artifactTypeName);
}
@Override
public BlackboardArtifact getGenInfoArtifact() throws TskCoreException {
return content.getGenInfoArtifact();
}
@Override
public BlackboardArtifact getGenInfoArtifact(boolean create) throws TskCoreException {
return content.getGenInfoArtifact(create);
}
@Override
public ArrayList<BlackboardAttribute> getGenInfoAttributes(BlackboardAttribute.ATTRIBUTE_TYPE attr_type) throws TskCoreException {
return content.getGenInfoAttributes(attr_type);
}
@Override
public ArrayList<BlackboardArtifact> getArtifacts(int artifactTypeID) throws TskCoreException {
return content.getArtifacts(artifactTypeID);
}
@Override
public ArrayList<BlackboardArtifact> getArtifacts(BlackboardArtifact.ARTIFACT_TYPE type) throws TskCoreException {
return content.getArtifacts(type);
}
@Override
public ArrayList<BlackboardArtifact> getAllArtifacts() throws TskCoreException {
return content.getAllArtifacts();
}
@Override
public Set<String> getHashSetNames() throws TskCoreException {
return content.getHashSetNames();
}
@Override
public long getArtifactsCount(String artifactTypeName) throws TskCoreException {
return content.getArtifactsCount(artifactTypeName);
}
@Override
public long getArtifactsCount(int artifactTypeID) throws TskCoreException {
return content.getArtifactsCount(artifactTypeID);
}
@Override
public long getArtifactsCount(BlackboardArtifact.ARTIFACT_TYPE type) throws TskCoreException {
return content.getArtifactsCount(type);
}
@Override
public long getAllArtifactsCount() throws TskCoreException {
return content.getAllArtifactsCount();
}
}
}

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-17 Basis Technology Corp.
* Copyright 2011-2017 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -24,7 +24,10 @@ import java.util.Arrays;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import java.util.function.Function;
import java.util.logging.Level;
import java.util.stream.Collectors;
import org.apache.commons.lang.StringUtils;
import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
@ -34,7 +37,9 @@ 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.datamodel.FileTypes.FileTypesKey;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
@ -87,7 +92,7 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
*/
try {
Case.getCurrentCase();
typesRoot.shouldShowCounts();
typesRoot.updateShowCounts();
update();
} catch (IllegalStateException notUsed) {
/**
@ -147,8 +152,9 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
* provides updates on events
*/
private FileTypesByExtNode(SleuthkitCase skCase, FileTypesByExtension.RootFilter filter, FileTypesByExtObservable o) {
super(Children.create(new FileTypesByExtNodeChildren(skCase, filter, o), true),
Lookups.singleton(filter == null ? FNAME : filter.getName()));
Lookups.singleton(filter == null ? FNAME : filter.getDisplayName()));
this.filter = filter;
// root node of tree
@ -157,7 +163,7 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
super.setDisplayName(FNAME);
} // sub-node in file tree (i.e. documents, exec, etc.)
else {
super.setName(filter.getName());
super.setName(filter.getDisplayName());
super.setDisplayName(filter.getDisplayName());
}
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file_types.png"); //NON-NLS
@ -181,7 +187,16 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
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()));
if (filter != null && (filter.equals(FileTypesByExtension.RootFilter.TSK_DOCUMENT_FILTER) || filter.equals(FileTypesByExtension.RootFilter.TSK_EXECUTABLE_FILTER))) {
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));
} else {
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"), getDisplayName()));
}
return s;
}
@ -272,7 +287,7 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
super(typesRoot, Children.create(new FileExtensionNodeChildren(filter, skCase, o), true),
Lookups.singleton(filter.getDisplayName()));
this.filter = filter;
setName(filter.getName());
super.setName(filter.getDisplayName());
updateDisplayName();
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file-filter-icon.png"); //NON-NLS
@ -330,24 +345,43 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
}
}
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
private String createQuery(FileTypesByExtension.SearchFilterInterface filter) {
if (filter.getFilter().isEmpty()) {
// We should never be given a search filter without extensions
// but if we are it is clearly a programming error so we throw
// an IllegalArgumentException.
throw new IllegalArgumentException("Empty filter list passed to createQuery()"); // 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
String query = "(dir_type = " + TskData.TSK_FS_NAME_TYPE_ENUM.REG.getValue() + ")"
+ (UserPreferences.hideKnownFilesInViewsTree() ? " AND (known IS NULL OR known != "
+ TskData.FileKnown.KNOWN.getFileKnownValue() + ")" : " ")
+ " AND (NULL "; //NON-NLS
if (skCase.getDatabaseType().equals(TskData.DbType.POSTGRESQL)) {
// For PostgreSQL we get a more efficient query by using builtin
// regular expression support and or'ing all extensions. We also
// escape the dot at the beginning of the extension.
// We will end up with a query that looks something like this:
// OR LOWER(name) ~ '(\.zip|\.rar|\.7zip|\.cab|\.jar|\.cpio|\.ar|\.gz|\.tgz|\.bz2)$')
query += "OR LOWER(name) ~ '(\\";
query += StringUtils.join(filter.getFilter().stream()
.map(String::toLowerCase).collect(Collectors.toList()), "|\\");
query += ")$'";
} else {
for (String s : filter.getFilter()) {
query += "OR LOWER(name) LIKE '%" + s.toLowerCase() + "'"; // NON-NLS
}
}
query.append(')');
return query.toString();
query += ')';
return query;
}
/**
* Child node factory for a specific file type - does the database query.
*/
private static class FileExtensionNodeChildren extends ChildFactory.Detachable<Content> implements Observer {
private class FileExtensionNodeChildren extends ChildFactory.Detachable<FileTypesKey> implements Observer {
private final SleuthkitCase skCase;
private final FileTypesByExtension.SearchFilterInterface filter;
@ -387,9 +421,10 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
}
@Override
protected boolean createKeys(List<Content> list) {
protected boolean createKeys(List<FileTypesKey> list) {
try {
list.addAll(skCase.findAllFilesWhere(createQuery(filter)));
list.addAll(skCase.findAllFilesWhere(createQuery(filter))
.stream().map(f -> new FileTypesKey(f)).collect(Collectors.toList()));
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Couldn't get search results", ex); //NON-NLS
}
@ -397,7 +432,7 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
}
@Override
protected Node createNodeForKey(Content key) {
protected Node createNodeForKey(FileTypesKey key) {
return key.accept(new FileTypes.FileNodeCreationVisitor());
}
}
@ -419,10 +454,10 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
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
Arrays.asList(".htm", ".html", ".doc", ".docx", ".odt", ".xls", ".xlsx", ".ppt", ".pptx", ".pdf", ".txt", ".rtf")), //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
FileTypeExtensions.getExecutableExtensions()); //NON-NLS
private final int id;
private final String name;

View File

@ -30,16 +30,20 @@ import java.util.Map;
import java.util.Observable;
import java.util.Observer;
import java.util.logging.Level;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
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 static org.sleuthkit.autopsy.core.UserPreferences.hideKnownFilesInViewsTree;
import static org.sleuthkit.autopsy.core.UserPreferences.hideSlackFilesInViewsTree;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.FileTypes.FileTypesKey;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
@ -55,6 +59,7 @@ import org.sleuthkit.datamodel.TskData;
public final class FileTypesByMimeType extends Observable implements AutopsyVisitableItem {
private final static Logger logger = Logger.getLogger(FileTypesByMimeType.class.getName());
private final SleuthkitCase skCase;
/**
* The nodes of this tree will be determined dynamically by the mimetypes
@ -62,36 +67,53 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
* type as the key and a Map, from media subtype to count, as the value.
*/
private final HashMap<String, Map<String, Long>> existingMimeTypeCounts = new HashMap<>();
private static final Logger LOGGER = Logger.getLogger(FileTypesByMimeType.class.getName());
/**
* Root of the File Types tree. Used to provide single answer to question:
* Should the child counts be shown next to the nodes?
*/
private final FileTypes typesRoot;
private void removeListeners() {
deleteObservers();
IngestManager.getInstance().removeIngestJobEventListener(pcl);
Case.removePropertyChangeListener(pcl);
}
/*
/**
* 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;
/**
* Performs the query on the database to get all distinct MIME types of
* files in it, and populate the hashmap with those results.
* Create the base expression used as the where clause in the queries for
* files by mime type. Filters out certain kinds of files and directories,
* and known/slack files based on user preferences.
*
* @return The base expression to be used in the where clause of queries for
* files by mime type.
*/
private void populateHashMap() {
String query = "SELECT mime_type,count(*) as count from tsk_files "
+ " where mime_type IS NOT null "
+ " AND dir_type = " + TskData.TSK_FS_NAME_TYPE_ENUM.REG.getValue()
static private String createBaseWhereExpr() {
return "(dir_type = " + TskData.TSK_FS_NAME_TYPE_ENUM.REG.getValue() + ")"
+ " AND (type IN ("
+ TskData.TSK_DB_FILES_TYPE_ENUM.FS.ordinal() + ","
+ TskData.TSK_DB_FILES_TYPE_ENUM.CARVED.ordinal() + ","
+ TskData.TSK_DB_FILES_TYPE_ENUM.DERIVED.ordinal() + ","
+ TskData.TSK_DB_FILES_TYPE_ENUM.LOCAL.ordinal()
+ ((UserPreferences.hideSlackFilesInViewsTree()) ? ""
: ("," + TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.ordinal()))
+ "))" + " GROUP BY mime_type";
+ (hideSlackFilesInViewsTree() ? "" : ("," + TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.ordinal()))
+ "))"
+ (hideKnownFilesInViewsTree() ? (" AND (known IS NULL OR known != " + TskData.FileKnown.KNOWN.getFileKnownValue() + ")") : "");
}
private void removeListeners() {
deleteObservers();
IngestManager.getInstance().removeIngestJobEventListener(pcl);
Case.removePropertyChangeListener(pcl);
}
/**
* Performs the query on the database to get all distinct MIME types of
* files in it, and populate the hashmap with those results.
*/
private void populateHashMap() {
String query = "SELECT mime_type, count(*) AS count FROM tsk_files "
+ " WHERE mime_type IS NOT null "
+ " AND " + createBaseWhereExpr()
+ " GROUP BY mime_type";
synchronized (existingMimeTypeCounts) {
existingMimeTypeCounts.clear();
@ -114,7 +136,7 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
}
}
} catch (TskCoreException | SQLException ex) {
LOGGER.log(Level.SEVERE, "Unable to populate File Types by MIME Type tree view from DB: ", ex); //NON-NLS
logger.log(Level.SEVERE, "Unable to populate File Types by MIME Type tree view from DB: ", ex); //NON-NLS
}
}
@ -138,7 +160,7 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
*/
try {
Case.getCurrentCase();
typesRoot.shouldShowCounts();
typesRoot.updateShowCounts();
populateHashMap();
} catch (IllegalStateException notUsed) {
/**
@ -187,11 +209,12 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
*/
class ByMimeTypeNode extends DisplayableItemNode {
@NbBundle.Messages("FileTypesByMimeType.name.text=By MIME Type")
@NbBundle.Messages({"FileTypesByMimeType.name.text=By MIME Type"})
final String NAME = Bundle.FileTypesByMimeType_name_text();
ByMimeTypeNode() {
super(Children.create(new ByMimeTypeNodeChildren(), true));
super(Children.create(new ByMimeTypeNodeChildren(), true), Lookups.singleton(Bundle.FileTypesByMimeType_name_text()));
super.setName(NAME);
super.setDisplayName(NAME);
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file_types.png");
@ -259,8 +282,12 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
*/
class MediaTypeNode extends DisplayableItemNode {
@NbBundle.Messages({"FileTypesByMimeTypeNode.createSheet.mediaType.name=Type",
"FileTypesByMimeTypeNode.createSheet.mediaType.displayName=Type",
"FileTypesByMimeTypeNode.createSheet.mediaType.desc=no description"})
MediaTypeNode(String name) {
super(Children.create(new MediaTypeNodeChildren(name), true));
super(Children.create(new MediaTypeNodeChildren(name), true), Lookups.singleton(name));
setName(name);
setDisplayName(name);
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file_types.png");
@ -276,6 +303,18 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
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(), "FileTypesByMimeTypeNode.createSheet.mediaType.name"), NbBundle.getMessage(this.getClass(), "FileTypesByMimeTypeNode.createSheet.mediaType.displayName"), NbBundle.getMessage(this.getClass(), "FileTypesByMimeTypeNode.createSheet.mediaType.desc"), getDisplayName()));
return s;
}
@Override
public String getItemType() {
return getClass().getName();
@ -316,19 +355,25 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
}
/**
* Node which represents the media sub type in the By MIME type tree, the
* media subtype is the portion of the MIME type following the /.
*/
class MediaSubTypeNode extends FileTypes.BGCountUpdatingNode {
@NbBundle.Messages({"FileTypesByMimeTypeNode.createSheet.mediaSubtype.name=Subtype",
"FileTypesByMimeTypeNode.createSheet.mediaSubtype.displayName=Subtype",
"FileTypesByMimeTypeNode.createSheet.mediaSubtype.desc=no description"})
private final String mimeType;
private final String subType;
private MediaSubTypeNode(String mimeType) {
super(typesRoot, Children.create(new MediaSubTypeNodeChildren(mimeType), true));
super(typesRoot, Children.create(new MediaSubTypeNodeChildren(mimeType), true), Lookups.singleton(mimeType));
this.mimeType = mimeType;
this.subType = StringUtils.substringAfter(mimeType, "/");
super.setName(mimeType);
super.setDisplayName(subType);
updateDisplayName();
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file-filter-icon.png"); //NON-NLS
addObserver(this);
}
@ -347,6 +392,17 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
public <T> T accept(DisplayableItemNodeVisitor< T> 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(), "FileTypesByMimeTypeNode.createSheet.mediaSubtype.name"), NbBundle.getMessage(this.getClass(), "FileTypesByMimeTypeNode.createSheet.mediaSubtype.displayName"), NbBundle.getMessage(this.getClass(), "FileTypesByMimeTypeNode.createSheet.mediaSubtype.desc"), getDisplayName()));
return s;
}
@Override
public String getItemType() {
@ -369,39 +425,12 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
}
}
/**
* Create the portion of the query following WHERE for a query of the
* database for each file which matches the complete MIME type represented
* by this node. Matches against the mime_type column in tsk_files.
*
* @param mimeType - the complete mimetype of the file mediatype/subtype
*
* @return query.toString - portion of SQL query which will follow a WHERE
* clause.
*/
static private String createQuery(String mimeType) {
StringBuilder query = new StringBuilder();
query.append("(dir_type = ").append(TskData.TSK_FS_NAME_TYPE_ENUM.REG.getValue()).append(")"); //NON-NLS
query.append(" AND (type IN (").append(TskData.TSK_DB_FILES_TYPE_ENUM.FS.ordinal()).append(","); //NON-NLS
query.append(TskData.TSK_DB_FILES_TYPE_ENUM.CARVED.ordinal()).append(",");
query.append(TskData.TSK_DB_FILES_TYPE_ENUM.DERIVED.ordinal()).append(",");
if (!UserPreferences.hideSlackFilesInViewsTree()) {
query.append(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.ordinal()).append(",");
}
query.append(TskData.TSK_DB_FILES_TYPE_ENUM.LOCAL.ordinal()).append("))");
if (UserPreferences.hideKnownFilesInViewsTree()) {
query.append(" AND (known IS NULL OR known != ").append(TskData.FileKnown.KNOWN.getFileKnownValue()).append(")"); //NON-NLS
}
query.append(" AND mime_type = '").append(mimeType).append("'"); //NON-NLS
return query.toString();
}
/**
* Factory for populating the contents of the Media Sub Type Node with the
* files that match MimeType which is represented by this position in the
* tree.
*/
private class MediaSubTypeNodeChildren extends ChildFactory.Detachable<Content> implements Observer {
private class MediaSubTypeNodeChildren extends ChildFactory.Detachable<FileTypesKey> implements Observer {
private final String mimeType;
@ -411,23 +440,13 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
this.mimeType = mimeType;
}
/**
* Uses the createQuery method to complete the query, Select * from
* tsk_files WHERE. The results from the database will contain the files
* which match this mime type and their information.
*
* @param list - will contain all files and their attributes from the
* tsk_files table where mime_type matches the one specified
*
* @return true
*/
@Override
protected boolean createKeys(List<Content> list) {
protected boolean createKeys(List<FileTypesKey> list) {
try {
List<AbstractFile> files = skCase.findAllFilesWhere(createQuery(mimeType));
list.addAll(files);
list.addAll(skCase.findAllFilesWhere(createBaseWhereExpr() + " AND mime_type = '" + mimeType + "'")
.stream().map(f -> new FileTypesKey(f)).collect(Collectors.toList())); //NON-NLS
} catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Couldn't get search results", ex); //NON-NLS
logger.log(Level.SEVERE, "Couldn't get search results", ex); //NON-NLS
}
return true;
}
@ -437,19 +456,9 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
refresh(true);
}
/**
* Creates the content to populate the Directory Listing Table view for
* each file
*
* @param key
*
* @return
*/
@Override
protected Node createNodeForKey(Content key) {
protected Node createNodeForKey(FileTypesKey key) {
return key.accept(new FileTypes.FileNodeCreationVisitor());
}
}
}

View File

@ -24,7 +24,6 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
@ -33,6 +32,7 @@ import java.util.Observable;
import java.util.Observer;
import java.util.Set;
import java.util.logging.Level;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children;
@ -95,6 +95,10 @@ public class KeywordHits implements AutopsyVisitableItem {
+ " OR attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP.getTypeID()//NON-NLS
+ ")"; //NON-NLS
static private boolean isOnlyDefaultInstance(List<String> instances) {
return (instances.size() == 1) && (instances.get(0).equals(DEFAULT_INSTANCE_NAME));
}
public KeywordHits(SleuthkitCase skCase) {
this.skCase = skCase;
keywordResults = new KeywordResults();
@ -265,7 +269,6 @@ public class KeywordHits implements AutopsyVisitableItem {
addRegExpToList(listMap, reg, word, id);
}
} else {//single term
if ("1".equals(kwType) || reg == null) { //literal, substring or exact
/*
* Substring, treated same as exact match. "1" is
@ -626,9 +629,8 @@ public class KeywordHits implements AutopsyVisitableItem {
@Override
public boolean isLeafTypeNode() {
List<String> instances = keywordResults.getKeywordInstances(setName, keyword);
// is this an exact/substring match (i.e. did we use the DEFAULT name)?
return instances.size() == 1 && instances.get(0).equals(DEFAULT_INSTANCE_NAME);
return isOnlyDefaultInstance(keywordResults.getKeywordInstances(setName, keyword));
}
@Override
@ -661,7 +663,6 @@ public class KeywordHits implements AutopsyVisitableItem {
return s;
}
}
/**
@ -706,7 +707,6 @@ public class KeywordHits implements AutopsyVisitableItem {
private final String keyword;
private final String setName;
private final Map<RegExpInstanceKey, DisplayableItemNode> nodesMap = new HashMap<>();
private RegExpInstancesFactory(String setName, String keyword) {
super();
@ -720,29 +720,20 @@ public class KeywordHits implements AutopsyVisitableItem {
// The keys are different depending on what we are displaying.
// regexp get another layer to show instances.
// Exact/substring matches don't.
if ((instances.size() == 1) && (instances.get(0).equals(DEFAULT_INSTANCE_NAME))) {
for (Long id : keywordResults.getArtifactIds(setName, keyword, DEFAULT_INSTANCE_NAME)) {
RegExpInstanceKey key = new RegExpInstanceKey(id);
nodesMap.computeIfAbsent(key, k -> createNode(k));
list.add(key);
}
if (isOnlyDefaultInstance(instances)) {
list.addAll(keywordResults.getArtifactIds(setName, keyword, DEFAULT_INSTANCE_NAME).stream()
.map(RegExpInstanceKey::new)
.collect(Collectors.toList()));
} else {
for (String instance : instances) {
RegExpInstanceKey key = new RegExpInstanceKey(instance);
nodesMap.computeIfAbsent(key, k -> createNode(k));
list.add(key);
}
list.addAll(instances.stream()
.map(RegExpInstanceKey::new)
.collect(Collectors.toList()));
}
return true;
}
@Override
protected Node createNodeForKey(RegExpInstanceKey key) {
return nodesMap.get(key);
}
private DisplayableItemNode createNode(RegExpInstanceKey key) {
if (key.isRegExp()) {
return new RegExpInstanceNode(setName, keyword, key.getRegExpKey());
} else {
@ -750,6 +741,7 @@ public class KeywordHits implements AutopsyVisitableItem {
return createBlackboardArtifactNode(key.getIdKey());
}
}
}
/**
@ -884,7 +876,6 @@ public class KeywordHits implements AutopsyVisitableItem {
private final String keyword;
private final String setName;
private final String instance;
private final Map<Long, BlackboardArtifactNode> nodesMap = new HashMap<>();
private HitsFactory(String setName, String keyword, String instance) {
super();
@ -895,16 +886,13 @@ public class KeywordHits implements AutopsyVisitableItem {
@Override
protected boolean createKeys(List<Long> list) {
for (Long id : keywordResults.getArtifactIds(setName, keyword, instance)) {
nodesMap.computeIfAbsent(id, i -> createBlackboardArtifactNode(i));
list.add(id);
}
list.addAll(keywordResults.getArtifactIds(setName, keyword, instance));
return true;
}
@Override
protected Node createNodeForKey(Long artifactId) {
return nodesMap.get(artifactId);
return createBlackboardArtifactNode(artifactId);
}
}
}

View File

@ -417,7 +417,9 @@ final class ExtractUnallocAction extends AbstractAction {
try {
List<LayoutFile> lflst = new ArrayList<>();
for (Content layout : vd.getChildren()) {
lflst.add((LayoutFile) layout);
if(layout instanceof LayoutFile){
lflst.add((LayoutFile) layout);
}
}
return lflst;
} catch (TskCoreException tce) {

View File

@ -51,6 +51,7 @@ final class GetFilesCountVisitor extends ContentVisitor.Default<Long> {
queryB.append(") )");
queryB.append(" AND ( (meta_type = ").append(TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG.getValue()); //NON-NLS
queryB.append(") OR (meta_type = ").append(TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_DIR.getValue()); //NON-NLS
queryB.append(") OR (meta_type = ").append(TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_VIRT_DIR.getValue()); //NON-NLS
queryB.append(") OR (meta_type = ").append(TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_VIRT.getValue()); //NON-NLS
queryB.append(" AND (name != '.') AND (name != '..')"); //NON-NLS
queryB.append(") )");

View File

@ -537,10 +537,12 @@ public final class FilesSet implements Serializable {
case FILES:
return file.isFile();
case DIRECTORIES:
return file.getMetaType() == TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_DIR;
return file.getMetaType() == TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_DIR
|| file.getMetaType() == TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_VIRT_DIR;
case FILES_AND_DIRECTORIES:
return file.getMetaType() == TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG
|| file.getMetaType() == TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_DIR;
|| file.getMetaType() == TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_DIR
|| file.getMetaType() == TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_VIRT_DIR;
case ALL:
return true; //Effectively ignores the metatype condition when All is selected.
default:

View File

@ -23,17 +23,13 @@ import java.io.FilenameFilter;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.CaseMetadata;
import org.sleuthkit.autopsy.casemodule.GeneralFilter;
final class PathUtils {
private static final List<String> CASE_METADATA_FILE_EXTS = Arrays.asList(new String[]{CaseMetadata.getFileExtension()});
private static final GeneralFilter caseMetadataFileFilter = new GeneralFilter(CASE_METADATA_FILE_EXTS, "Autopsy Case File");
private final static String CASE_METADATA_EXT = CaseMetadata.getFileExtension();
/**
* Searches a given folder for the most recently modified case folder for a
@ -83,34 +79,6 @@ final class PathUtils {
return caseFolderPaths;
}
/**
* Determines whether or not there is a case metadata file in a given
* folder.
*
* @param folderPath Path to the folder to search.
*
* @return True or false.
*/
static boolean hasCaseMetadataFile(Path folderPath) {
/**
* TODO: If need be, this can be rewritten without the FilenameFilter so
* that it does not necessarily visit every file in the folder.
*/
File folder = folderPath.toFile();
if (!folder.isDirectory()) {
return false;
}
String[] caseDataFiles = folder.list((File folder1, String fileName) -> {
File file = new File(folder1, fileName);
if (file.isFile()) {
return caseMetadataFileFilter.accept(file);
}
return false;
});
return caseDataFiles.length != 0;
}
/**
* Extracts the case name from a case folder path.
*
@ -141,6 +109,12 @@ final class PathUtils {
return Paths.get(caseFoldersPath.toString(), folderName);
}
/**
* Supress creation of instances of this class.
*/
private PathUtils() {
}
private static class CaseFolderFilter implements FilenameFilter {
private final String caseName;
@ -152,28 +126,37 @@ final class PathUtils {
@Override
public boolean accept(File folder, String fileName) {
File file = new File(folder, fileName);
if (file.isDirectory() && fileName.length() > TimeStampUtils.getTimeStampLength()) {
Path filePath = Paths.get(file.getPath());
if (fileName.length() > TimeStampUtils.getTimeStampLength() && file.isDirectory()) {
if (TimeStampUtils.endsWithTimeStamp(fileName)) {
if (null != caseName) {
String fileNamePrefix = fileName.substring(0, fileName.length() - TimeStampUtils.getTimeStampLength());
if (fileNamePrefix.equals(caseName)) {
return hasCaseMetadataFile(filePath);
return hasCaseMetadataFile(file);
}
} else {
return hasCaseMetadataFile(filePath);
return hasCaseMetadataFile(file);
}
}
}
return false;
}
}
/**
* Determines whether or not there is a case metadata file in a given
* folder.
*
* @param folder The file object representing the folder to search.
*
* @return True or false.
*/
private static boolean hasCaseMetadataFile(File folder) {
for (File file : folder.listFiles()) {
if (file.getName().toLowerCase().endsWith(CASE_METADATA_EXT) && file.isFile()) {
return true;
}
}
return false;
}
/**
* Supress creation of instances of this class.
*/
private PathUtils() {
}
}

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2015 Basis Technology Corp.
* Copyright 2015-2017 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -174,6 +174,7 @@ public class AutoIngestSettingsPanel extends javax.swing.JPanel {
}
validateSettings();
enableOptionsBasedOnMode(getModeFromRadioButtons());
}
/**
@ -306,10 +307,13 @@ public class AutoIngestSettingsPanel extends javax.swing.JPanel {
*/
boolean valid() {
if (!cbJoinAutoIngestCluster.isSelected()) {
if (!cbJoinAutoIngestCluster.isSelected()) { //hide the invalid field warnings when in stand alone mode
jLabelInvalidImageFolder.setVisible(false);
jLabelInvalidResultsFolder.setVisible(false);
sharedSettingsErrorTextField.setVisible(false);
configButtonErrorTextField.setText("");
return true;
}
boolean isValidNodePanel = true;
switch (getModeFromRadioButtons()) {
@ -326,6 +330,7 @@ public class AutoIngestSettingsPanel extends javax.swing.JPanel {
}
break;
case REVIEW:
jLabelInvalidImageFolder.setVisible(false);
if (!validateResultsPath()) {
isValidNodePanel = false;
}
@ -596,23 +601,24 @@ public class AutoIngestSettingsPanel extends javax.swing.JPanel {
private void enableOptionsBasedOnMode(OptionsUiMode mode) {
if (mode != OptionsUiMode.DOWNLOADING_CONFIGURATION) {
jRadioButtonAutomated.setEnabled(cbJoinAutoIngestCluster.isSelected());
jRadioButtonReview.setEnabled(cbJoinAutoIngestCluster.isSelected());
boolean nonMasterSharedConfig = !masterNodeCheckBox.isSelected() && sharedConfigCheckbox.isSelected();
jRadioButtonAutomated.setEnabled(cbJoinAutoIngestCluster.isSelected() && !sharedConfigCheckbox.isSelected());
jRadioButtonReview.setEnabled(cbJoinAutoIngestCluster.isSelected() && !sharedConfigCheckbox.isSelected());
jLabelSelectInputFolder.setEnabled(mode == OptionsUiMode.AIM);
inputPathTextField.setEnabled(mode == OptionsUiMode.AIM);
browseInputFolderButton.setEnabled(mode == OptionsUiMode.AIM);
jLabelSelectInputFolder.setEnabled(mode == OptionsUiMode.AIM && !nonMasterSharedConfig);
inputPathTextField.setEnabled(mode == OptionsUiMode.AIM && !nonMasterSharedConfig);
browseInputFolderButton.setEnabled(mode == OptionsUiMode.AIM && !nonMasterSharedConfig);
jLabelSelectOutputFolder.setEnabled(mode == OptionsUiMode.AIM || mode == OptionsUiMode.REVIEW);
outputPathTextField.setEnabled(mode == OptionsUiMode.AIM || mode == OptionsUiMode.REVIEW);
browseOutputFolderButton.setEnabled(mode == OptionsUiMode.AIM || mode == OptionsUiMode.REVIEW);
jLabelSelectOutputFolder.setEnabled((mode == OptionsUiMode.AIM && !nonMasterSharedConfig) || mode == OptionsUiMode.REVIEW);
outputPathTextField.setEnabled((mode == OptionsUiMode.AIM && !nonMasterSharedConfig) || mode == OptionsUiMode.REVIEW);
browseOutputFolderButton.setEnabled((mode == OptionsUiMode.AIM && !nonMasterSharedConfig) || mode == OptionsUiMode.REVIEW);
jPanelSharedConfig.setEnabled(mode == OptionsUiMode.AIM);
jPanelIngestSettings.setEnabled(mode == OptionsUiMode.AIM);
bnEditIngestSettings.setEnabled(mode == OptionsUiMode.AIM);
bnAdvancedSettings.setEnabled(mode == OptionsUiMode.AIM);
bnLogging.setEnabled(mode == OptionsUiMode.AIM);
jPanelIngestSettings.setEnabled(mode == OptionsUiMode.AIM && !nonMasterSharedConfig);
bnEditIngestSettings.setEnabled(mode == OptionsUiMode.AIM && !nonMasterSharedConfig);
bnAdvancedSettings.setEnabled(mode == OptionsUiMode.AIM && !nonMasterSharedConfig);
bnLogging.setEnabled(mode == OptionsUiMode.AIM && !nonMasterSharedConfig);
jPanelSharedConfig.setEnabled(mode == OptionsUiMode.AIM);
sharedConfigCheckbox.setEnabled(mode == OptionsUiMode.AIM);
masterNodeCheckBox.setEnabled(mode == OptionsUiMode.AIM && sharedConfigCheckbox.isSelected());
@ -1389,12 +1395,7 @@ public class AutoIngestSettingsPanel extends javax.swing.JPanel {
void setEnabledStateForSharedConfiguration() {
if (jRadioButtonAutomated.isSelected() && cbJoinAutoIngestCluster.isSelected()) {
if (sharedConfigCheckbox.isEnabled() && sharedConfigCheckbox.isSelected()) {
setEnabledState(masterNodeCheckBox.isSelected());
} else {
// If we are in AIM mode and shared config is not enabled, allow this
setEnabledState(true);
}
enableOptionsBasedOnMode(OptionsUiMode.AIM);
}
}

View File

@ -12,7 +12,6 @@ AdvancedAutoIngestSettingsPanel.lbSecondsBetweenJobs.toolTipText=A wait time use
AdvancedAutoIngestSettingsPanel.tbWarning.text=WARNING: Ensure you know what you are doing before modifying these values. Informed use can improve system performance. Misuse can cause system performance degradation and data loss. Please consult the user guide for details.
AdvancedAutoIngestSettingsPanel.threadCountLabel.text=For this computer, a maximum of {0} file ingest threads should be used.
AIMIngestSettingsPanel.browseGlobalSettingsButton.text=Browse
AIMIngestSettingsPanel.globalSettingsCheckbox.text=Use shared configuration in folder:
AIMIngestSettingsPanel.globalSettingsErrorTextField.text=
AIMIngestSettingsPanel.globalSettingsTextField.text=
AIMIngestSettingsPanel.jButton1.text=Download shared settings
@ -33,7 +32,6 @@ AutoIngestSettingsPanel.downloadButton.text=Download Config
AutoIngestSettingsPanel.EmptySettingsDirectory=Enter path to settings directory
AutoIngestSettingsPanel.ErrorSettingDefaultFolder=Error creating default folder
AutoIngestSettingsPanel.FileExportRules.text=File Export Rules
AutoIngestSettingsPanel.globalSettingsCheckbox.text=Use shared configuration in folder:
AutoIngestSettingsPanel.globalSettingsErrorTextField.text=
AutoIngestSettingsPanel.globalSettingsTextField.text=
AutoIngestSettingsPanel.ImageDirectoryUnspecified=Shared images folder must be set

View File

@ -101,7 +101,7 @@ class GlobalEditListPanel extends javax.swing.JPanel implements ListSelectionLis
}
});
}
/**
* Enables and disables buttons on this panel based on the current state.
*/
@ -125,12 +125,12 @@ class GlobalEditListPanel extends javax.swing.JPanel implements ListSelectionLis
}
}
@NbBundle.Messages("GlobalEditListPanel.editKeyword.title=Edit Keyword")
@NbBundle.Messages("GlobalEditListPanel.editKeyword.title=Edit Keyword")
/**
* Adds keywords to a keyword list, returns true if at least one keyword was successfully added and no
* duplicates were found.
*
* @return - true or false
* Adds keywords to a keyword list, returns true if at least one keyword was
* successfully added and no duplicates were found.
*
* @return - true or false
*/
private boolean addKeywordsAction(String existingKeywords, boolean isLiteral, boolean isWholeWord) {
String keywordsToRedisplay = existingKeywords;
@ -140,7 +140,7 @@ class GlobalEditListPanel extends javax.swing.JPanel implements ListSelectionLis
int dupeCount = 0;
int badCount = 1; // Default to 1 so we enter the loop the first time
if (!existingKeywords.isEmpty()){ //if there is an existing keyword then this action was called by the edit button
if (!existingKeywords.isEmpty()) { //if there is an existing keyword then this action was called by the edit button
dialog.setTitle(NbBundle.getMessage(GlobalEditListPanel.class, "GlobalEditListPanel.editKeyword.title"));
}
while (badCount > 0) {
@ -162,7 +162,7 @@ class GlobalEditListPanel extends javax.swing.JPanel implements ListSelectionLis
final Keyword keyword = new Keyword(newWord, !dialog.isKeywordRegex(), dialog.isKeywordExact(), currentKeywordList.getName(), newWord);
if (currentKeywordList.hasKeyword(keyword)) {
dupeCount++;
continue;
continue;
}
//check if valid
@ -231,8 +231,9 @@ class GlobalEditListPanel extends javax.swing.JPanel implements ListSelectionLis
/**
* Remove one or more keywords from a keyword list.
*
* @param selectedKeywords the indices of the keywords you would like to delete
*
* @param selectedKeywords the indices of the keywords you would like to
* delete
*/
private void deleteKeywordAction(int[] selectedKeywords) {
tableModel.deleteSelected(selectedKeywords);
@ -411,20 +412,15 @@ class GlobalEditListPanel extends javax.swing.JPanel implements ListSelectionLis
public void valueChanged(ListSelectionEvent e) {
//respond to list selection changes in KeywordSearchListManagementPanel
ListSelectionModel listSelectionModel = (ListSelectionModel) e.getSource();
currentKeywordList = null;
if (!listSelectionModel.isSelectionEmpty()) {
int index = listSelectionModel.getMinSelectionIndex();
listSelectionModel.setSelectionInterval(index, index);
XmlKeywordSearchList loader = XmlKeywordSearchList.getCurrent();
currentKeywordList = loader.getListsL(false).get(index);
tableModel.resync();
setButtonStates();
} else {
currentKeywordList = null;
tableModel.resync();
setButtonStates();
if (listSelectionModel.getMinSelectionIndex() == listSelectionModel.getMaxSelectionIndex()) {
currentKeywordList = loader.getListsL(false).get(listSelectionModel.getMinSelectionIndex());
}
}
tableModel.resync();
setButtonStates();
}
@Override

View File

@ -46,8 +46,7 @@ final class GlobalListSettingsPanel extends javax.swing.JPanel implements Option
@Override
public void actionPerformed(ActionEvent e) {
if (KeywordSearchUtil.displayConfirmDialog(NbBundle.getMessage(this.getClass(), "KeywordSearchConfigurationPanel1.customizeComponents.title"), NbBundle.getMessage(this.getClass(), "KeywordSearchConfigurationPanel1.customizeComponents.body"), KeywordSearchUtil.DIALOG_MESSAGE_TYPE.WARN)) {
String toDelete = editListPanel.getCurrentKeywordList().getName();
deleteAction(toDelete);
deleteAction();
listsManagementPanel.resync();
}
}
@ -56,9 +55,8 @@ final class GlobalListSettingsPanel extends javax.swing.JPanel implements Option
listsManagementPanel.addRenameButtonActionPerformed(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String toDelete = editListPanel.getCurrentKeywordList().getName();
if (copyAction()) {
deleteAction(toDelete);
deleteAction();
listsManagementPanel.resync();
}
}
@ -83,9 +81,8 @@ final class GlobalListSettingsPanel extends javax.swing.JPanel implements Option
*
* @param toDelete - the list to delete
*/
private void deleteAction(String toDelete) {
XmlKeywordSearchList deleter = XmlKeywordSearchList.getCurrent();
deleter.deleteList(toDelete);
private void deleteAction() {
listsManagementPanel.deleteSelected();
editListPanel.setCurrentKeywordList(null);
editListPanel.setButtonStates();
listsManagementPanel.setButtonStates();

View File

@ -102,12 +102,12 @@ class GlobalListsManagementPanel extends javax.swing.JPanel implements OptionsPa
}
/**
* Opens the dialogue for creating a new keyword list and adds it to the table.
* Opens the dialogue for creating a new keyword list and adds it to the
* table.
*/
private void newKeywordListAction() {
XmlKeywordSearchList writer = XmlKeywordSearchList.getCurrent();
String listName = "";
listName = (String) JOptionPane.showInputDialog(null, NbBundle.getMessage(this.getClass(), "KeywordSearch.newKwListTitle"),
NbBundle.getMessage(this.getClass(), "KeywordSearch.newKeywordListMsg"), JOptionPane.PLAIN_MESSAGE, null, null, listName);
@ -158,15 +158,19 @@ class GlobalListsManagementPanel extends javax.swing.JPanel implements OptionsPa
boolean isIngestRunning = IngestManager.getInstance().isIngestRunning();
boolean isListSelected = !listsTable.getSelectionModel().isSelectionEmpty();
boolean canEditList = isListSelected && !isIngestRunning;
boolean multiSelection = false; //can't rename or copy when multiple lists selected
if (isListSelected) {
multiSelection = (listsTable.getSelectionModel().getMaxSelectionIndex() != listsTable.getSelectionModel().getMinSelectionIndex());
}
// items that only need ingest to not be running
importButton.setEnabled(!isIngestRunning);
// items that need an unlocked list w/out ingest running
// items that need an unlocked list w/out ingest running
deleteListButton.setEnabled(canEditList);
renameListButton.setEnabled(canEditList);
renameListButton.setEnabled(canEditList && !multiSelection);
// items that only need a selected list
copyListButton.setEnabled(isListSelected);
copyListButton.setEnabled(isListSelected && !multiSelection);
exportButton.setEnabled(isListSelected);
}
@ -435,13 +439,9 @@ class GlobalListsManagementPanel extends javax.swing.JPanel implements OptionsPa
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}//GEN-LAST:event_importButtonActionPerformed
private void listsTableKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_listsTableKeyPressed
if (evt.getKeyCode() == KeyEvent.VK_DELETE) {
int[] selected = listsTable.getSelectedRows();
if (selected.length == 0) {
return;
} else if (KeywordSearchUtil.displayConfirmDialog(NbBundle.getMessage(this.getClass(), "KeywordSearchConfigurationPanel1.customizeComponents.title"), NbBundle.getMessage(this.getClass(), "KeywordSearchConfigurationPanel1.customizeComponents.body"), KeywordSearchUtil.DIALOG_MESSAGE_TYPE.WARN)) {
String listName = (String) listsTable.getModel().getValueAt(selected[0], 0);
XmlKeywordSearchList.getCurrent().deleteList(listName);
if (evt.getKeyCode() == KeyEvent.VK_DELETE && !IngestManager.getInstance().isIngestRunning() && !listsTable.getSelectionModel().isSelectionEmpty()) {
if (KeywordSearchUtil.displayConfirmDialog(NbBundle.getMessage(this.getClass(), "KeywordSearchConfigurationPanel1.customizeComponents.title"), NbBundle.getMessage(this.getClass(), "KeywordSearchConfigurationPanel1.customizeComponents.body"), KeywordSearchUtil.DIALOG_MESSAGE_TYPE.WARN)) {
deleteSelected();
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
} else {
return;
@ -492,7 +492,11 @@ class GlobalListsManagementPanel extends javax.swing.JPanel implements OptionsPa
XmlKeywordSearchList reader = XmlKeywordSearchList.getCurrent();
List<KeywordList> toWrite = new ArrayList<>();
toWrite.add(reader.getList(listName));
for (int index : listsTable.getSelectedRows()) {
toWrite.add(reader.getList(listsTable.getValueAt(index, 0).toString()));
}
final XmlKeywordSearchList exporter = new XmlKeywordSearchList(fileAbs);
boolean written = exporter.saveLists(toWrite);
if (written) {
@ -541,6 +545,14 @@ class GlobalListsManagementPanel extends javax.swing.JPanel implements OptionsPa
tableModel.resync();
}
void deleteSelected() {
int[] selected = listsTable.getSelectedRows();
if (selected.length == 0) {
return;
}
tableModel.deleteSelected(selected);
}
private class KeywordListTableModel extends AbstractTableModel {
private static final long serialVersionUID = 1L;
@ -586,8 +598,8 @@ class GlobalListsManagementPanel extends javax.swing.JPanel implements OptionsPa
//delete selected from handle, events are fired from the handle
void deleteSelected(int[] selected) {
List<String> toDel = new ArrayList<>();
for (int i = 0; i < selected.length; i++) {
toDel.add((String) getValueAt(0, selected[i]));
for (int i : selected) {
toDel.add(getValueAt(i, 0).toString());
}
for (String del : toDel) {
listsHandle.deleteList(del);

View File

@ -216,6 +216,14 @@ class TestRunner(object):
TestRunner._run_ant(test_data)
time.sleep(2) # Give everything a second to process
# exit if any build errors are found in antlog.txt
antlog = 'antlog.txt'
logs_path = test_data.logs_dir
for ant_line in codecs.open(os.path.join(logs_path, os.pardir, antlog)):
ant_ignoreCase = ant_line.lower()
if ant_line.startswith("BUILD FAILED") or "fatal error" in ant_ignoreCase or "crashed" in ant_ignoreCase:
Errors.print_error("Autopsy test failed. Please check the build log antlog.txt for details.")
sys.exit(1)
# exit if .db was not created
if not file_exists(test_data.get_db_path(DBType.OUTPUT)):
Errors.print_error("Autopsy did not run properly; No .db file was created")