Merge branch 'develop' of https://github.com/sleuthkit/autopsy into live_autopsy_warning

This commit is contained in:
rishwanth1995 2018-08-10 09:50:15 -04:00
commit 1932d4381b
96 changed files with 2638 additions and 1472 deletions

View File

@ -86,7 +86,7 @@
<target name="getTestDataFiles">
<mkdir dir="${basedir}/test/qa-functional/data"/>
<get src="https://drive.google.com/uc?id=1dLYGctuvRQMmnzfXPppTM_9gB49eLc_g" dest="${test-input}/EmbeddedIM_img1_v1.vhd" skipexisting="true"/>
<get src="https://drive.google.com/uc?id=1FkinvA7EFqP4nOSOyTAOli5KefM67ufA" dest="${test-input}/EmbeddedIM_img1_v2.vhd" skipexisting="true"/>
<get src="https://drive.google.com/uc?id=1JACMDyH4y54ypGzFWl82ZzMQf3qbrioP" dest="${test-input}/BitlockerDetection_img1_v1.vhd" skipexisting="true"/>
<get src="https://drive.google.com/uc?id=17sGybvmBGsWWJYo1IWKmO04oG9hKpPi3" dest="${test-input}/SqlCipherDetection_img1_v1.vhd" skipexisting="true"/>
<get src="https://drive.google.com/uc?id=0BxdBkzm5VKGNT0dGY0dqcHVsU3M" dest="${test-input}/IngestFilters_img1_v1.img" skipexisting="true"/>

View File

@ -2,7 +2,7 @@ Manifest-Version: 1.0
OpenIDE-Module: org.sleuthkit.autopsy.core/10
OpenIDE-Module-Localizing-Bundle: org/sleuthkit/autopsy/core/Bundle.properties
OpenIDE-Module-Layer: org/sleuthkit/autopsy/core/layer.xml
OpenIDE-Module-Implementation-Version: 23
OpenIDE-Module-Implementation-Version: 24
OpenIDE-Module-Requires: org.openide.windows.WindowManager
AutoUpdate-Show-In-Client: true
AutoUpdate-Essential-Module: true

View File

@ -17,7 +17,6 @@ file.reference.sevenzipjbinding-AllPlatforms.jar=release/modules/ext/sevenzipjbi
file.reference.sevenzipjbinding.jar=release/modules/ext/sevenzipjbinding.jar
file.reference.sqlite-jdbc-3.8.11.jar=release/modules/ext/sqlite-jdbc-3.8.11.jar
file.reference.StixLib.jar=release/modules/ext/StixLib.jar
file.reference.sleuthkit-postgresql-4.6.1.jar=release/modules/ext/sleuthkit-postgresql-4.6.1.jar
file.reference.bcprov-jdk15on-1.54.jar=release/modules/ext/bcprov-jdk15on-1.54.jar
file.reference.jackcess-2.1.8.jar=release/modules/ext/jackcess-2.1.8.jar
file.reference.jackcess-encrypt-2.1.2.jar=release/modules/ext/jackcess-encrypt-2.1.2.jar
@ -30,7 +29,7 @@ file.reference.cxf-rt-transports-http-3.0.16.jar=release/modules/ext/cxf-rt-tran
file.reference.fontbox-2.0.8.jar=release/modules/ext/fontbox-2.0.8.jar
file.reference.pdfbox-2.0.8.jar=release/modules/ext/pdfbox-2.0.8.jar
file.reference.pdfbox-tools-2.0.8.jar=release/modules/ext/pdfbox-tools-2.0.8.jar
file.reference.sleuthkit-postgresql-4.6.1.jar=release/modules/ext/sleuthkit-postgresql-4.6.1.jar
file.reference.sleuthkit-postgresql-4.6.2.jar=release/modules/ext/sleuthkit-postgresql-4.6.2.jar
file.reference.tika-core-1.17.jar=release/modules/ext/tika-core-1.17.jar
file.reference.tika-parsers-1.17.jar=release/modules/ext/tika-parsers-1.17.jar
file.reference.curator-client-2.8.0.jar=release/modules/ext/curator-client-2.8.0.jar
@ -47,5 +46,5 @@ nbm.homepage=http://www.sleuthkit.org/
nbm.module.author=Brian Carrier
nbm.needs.restart=true
source.reference.curator-recipes-2.8.0.jar=release/modules/ext/curator-recipes-2.8.0-sources.jar
spec.version.base=10.11
spec.version.base=10.12

View File

@ -251,7 +251,7 @@
<compile-dependency/>
<run-dependency>
<release-version>3</release-version>
<specification-version>1.1</specification-version>
<specification-version>1.2</specification-version>
</run-dependency>
</dependency>
</module-dependencies>
@ -412,8 +412,8 @@
<binary-origin>release/modules/ext/metadata-extractor-2.10.1.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/sleuthkit-postgresql-4.6.1.jar</runtime-relative-path>
<binary-origin>release/modules/ext/sleuthkit-postgresql-4.6.1.jar</binary-origin>
<runtime-relative-path>ext/sleuthkit-postgresql-4.6.2.jar</runtime-relative-path>
<binary-origin>release/modules/ext/sleuthkit-postgresql-4.6.2.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/tika-core-1.17.jar</runtime-relative-path>
@ -499,7 +499,7 @@
<runtime-relative-path>ext/xmpcore-5.1.3.jar</runtime-relative-path>
<binary-origin>release/modules/ext/xmpcore-5.1.3.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/SparseBitSet-1.1.jar</runtime-relative-path>
<binary-origin>release/modules/ext/SparseBitSet-1.1.jar</binary-origin>
</class-path-extension>

View File

@ -24,10 +24,8 @@ import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
@ -57,7 +55,7 @@ public class GetTagNameAndCommentDialog extends JDialog {
private final List<TagName> tagNamesList = new ArrayList<>();
private final List<TagName> standardTagNamesList = new ArrayList<>();
private TagNameAndComment tagNameAndComment = null;
public static class TagNameAndComment {
private final TagName tagName;
@ -105,7 +103,16 @@ public class GetTagNameAndCommentDialog extends JDialog {
public static TagNameAndComment doDialog(Window owner) {
GetTagNameAndCommentDialog dialog = new GetTagNameAndCommentDialog(owner);
dialog.display();
return dialog.tagNameAndComment;
return dialog.getTagNameAndComment();
}
/**
* Get the TagNameAndComment.
*
* @return the tagNameAndComment
*/
private TagNameAndComment getTagNameAndComment() {
return tagNameAndComment;
}
private GetTagNameAndCommentDialog(Window owner) {
@ -114,14 +121,14 @@ public class GetTagNameAndCommentDialog extends JDialog {
ModalityType.APPLICATION_MODAL);
}
private void display() {
initComponents();
tagCombo.setRenderer(new DefaultListCellRenderer() {
private static final long serialVersionUID = 1L;
@Override
public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
String status = ((TagName) value).getKnownStatus() == TskData.FileKnown.BAD ?TagsManager.getNotableTagLabel() : "";
String status = ((TagName) value).getKnownStatus() == TskData.FileKnown.BAD ? TagsManager.getNotableTagLabel() : "";
String newValue = ((TagName) value).getDisplayName() + status;
return super.getListCellRendererComponent(list, newValue, index, isSelected, cellHasFocus);
}
@ -151,7 +158,7 @@ public class GetTagNameAndCommentDialog extends JDialog {
TagsManager tagsManager = Case.getCurrentCaseThrows().getServices().getTagsManager();
List<String> standardTagNames = TagsManager.getStandardTagNames();
Map<String, TagName> tagNamesMap = new TreeMap<>(tagsManager.getDisplayNamesToTagNamesMap());
tagNamesMap.entrySet().stream().map((entry) -> entry.getValue()).forEachOrdered((tagName) -> {
if (standardTagNames.contains(tagName.getDisplayName())) {
standardTagNamesList.add(tagName);
@ -159,7 +166,6 @@ public class GetTagNameAndCommentDialog extends JDialog {
tagNamesList.add(tagName);
}
});
} catch (TskCoreException | NoCurrentCaseException ex) {
Logger.getLogger(GetTagNameAndCommentDialog.class
@ -320,4 +326,5 @@ public class GetTagNameAndCommentDialog extends JDialog {
private javax.swing.JComboBox<TagName> tagCombo;
private javax.swing.JLabel tagLabel;
// End of variables declaration//GEN-END:variables
}

View File

@ -114,7 +114,6 @@ LocalFilesDSProcessor.toString.text=Logical Files
LocalFilesPanel.contentType.text=LOCAL
LocalFilesPanel.moduleErr=Module Error
LocalFilesPanel.moduleErr.msg=A module caused an error listening to LocalFilesPanel updates. See log to determine which module. Some data could be incomplete.
MissingImageDialog.allDesc.text=All Supported Types
MissingImageDialog.display.title=Search for Missing Image
MissingImageDialog.confDlg.noFileSel.msg=No image file has been selected. Are you sure you\nwould like to exit without finding the image?
MissingImageDialog.confDlg.noFileSel.title=Missing Image

View File

@ -101,7 +101,6 @@ LocalFilesDSProcessor.toString.text=\u30ed\u30b8\u30ab\u30eb\u30d5\u30a1\u30a4\u
LocalFilesPanel.contentType.text=\u30ed\u30fc\u30ab\u30eb
LocalFilesPanel.moduleErr=\u30e2\u30b8\u30e5\u30fc\u30eb\u30a8\u30e9\u30fc
LocalFilesPanel.moduleErr.msg=LocalFilesPanel\u30a2\u30c3\u30d7\u30c7\u30fc\u30c8\u3092\u78ba\u8a8d\u4e2d\u306b\u30e2\u30b8\u30e5\u30fc\u30eb\u304c\u30a8\u30e9\u30fc\u3092\u8d77\u3053\u3057\u307e\u3057\u305f\u3002\u3069\u306e\u30e2\u30b8\u30e5\u30fc\u30eb\u304b\u30ed\u30b0\u3092\u78ba\u8a8d\u3057\u3066\u4e0b\u3055\u3044\u3002\u4e00\u90e8\u306e\u30c7\u30fc\u30bf\u304c\u4e0d\u5b8c\u5168\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002
MissingImageDialog.allDesc.text=\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u308b\u5168\u3066\u306e\u30bf\u30a4\u30d7
MissingImageDialog.display.title=\u6b20\u843d\u30a4\u30e1\u30fc\u30b8\u3092\u691c\u7d22
MissingImageDialog.confDlg.noFileSel.msg=\u30a4\u30e1\u30fc\u30b8\u30d5\u30a1\u30a4\u30eb\u304c\u9078\u629e\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u30a4\u30e1\u30fc\u30b8\u3092\u898b\u3064\u3051\u308b\n\u524d\u306b\u672c\u5f53\u306b\u7d42\u4e86\u3057\u307e\u3059\u304b\uff1f
MissingImageDialog.confDlg.noFileSel.title=\u6b20\u843d\u30a4\u30e1\u30fc\u30b8

View File

@ -89,6 +89,15 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
public ImageDSProcessor() {
configPanel = ImageFilePanel.createInstance(ImageDSProcessor.class.getName(), filtersList);
}
/**
* Get the list of file filters supported by this DSP.
*
* @return A list of all supported file filters.
*/
static List<FileFilter> getFileFiltersList() {
return filtersList;
}
/**
* Gets a string that describes the type of data sources this processor is

View File

@ -28,6 +28,7 @@ import java.io.File;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.filechooser.FileFilter;
import org.openide.util.NbBundle;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.coreutils.DriveUtils;
@ -44,17 +45,8 @@ class MissingImageDialog extends javax.swing.JDialog {
private static final Logger logger = Logger.getLogger(MissingImageDialog.class.getName());
long obj_id;
SleuthkitCase db;
static final GeneralFilter rawFilter = new GeneralFilter(GeneralFilter.RAW_IMAGE_EXTS, GeneralFilter.RAW_IMAGE_DESC);
static final GeneralFilter encaseFilter = new GeneralFilter(GeneralFilter.ENCASE_IMAGE_EXTS, GeneralFilter.ENCASE_IMAGE_DESC);
static final List<String> allExt = new ArrayList<String>();
static {
allExt.addAll(GeneralFilter.RAW_IMAGE_EXTS);
allExt.addAll(GeneralFilter.ENCASE_IMAGE_EXTS);
}
static final String allDesc = NbBundle.getMessage(MissingImageDialog.class, "MissingImageDialog.allDesc.text");
static final GeneralFilter allFilter = new GeneralFilter(allExt, allDesc);
private final JFileChooser fc = new JFileChooser();
private final JFileChooser fileChooser = new JFileChooser();
/**
* Instantiate a MissingImageDialog.
@ -68,13 +60,15 @@ class MissingImageDialog extends javax.swing.JDialog {
this.db = db;
initComponents();
fc.setDragEnabled(false);
fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
fc.setMultiSelectionEnabled(false);
fileChooser.setDragEnabled(false);
fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
fileChooser.setMultiSelectionEnabled(false);
fc.addChoosableFileFilter(rawFilter);
fc.addChoosableFileFilter(encaseFilter);
fc.setFileFilter(allFilter);
List<FileFilter> fileFiltersList = ImageDSProcessor.getFileFiltersList();
for (FileFilter fileFilter : fileFiltersList) {
fileChooser.addChoosableFileFilter(fileFilter);
}
fileChooser.setFileFilter(fileFiltersList.get(0));
selectButton.setEnabled(false);
}
@ -286,12 +280,12 @@ class MissingImageDialog extends javax.swing.JDialog {
// set the current directory of the FileChooser if the ImagePath Field is valid
File currentDir = new File(oldText);
if (currentDir.exists()) {
fc.setCurrentDirectory(currentDir);
fileChooser.setCurrentDirectory(currentDir);
}
int retval = fc.showOpenDialog(this);
int retval = fileChooser.showOpenDialog(this);
if (retval == JFileChooser.APPROVE_OPTION) {
String path = fc.getSelectedFile().getPath();
String path = fileChooser.getSelectedFile().getPath();
pathNameTextField.setText(path);
}
//pcs.firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.FOCUS_NEXT.toString(), false, true);

View File

@ -859,13 +859,14 @@ public class SingleUserCaseConverter {
if (value > biggestPK) {
biggestPK = value;
}
outputStatement.executeUpdate("INSERT INTO content_tags (tag_id, obj_id, tag_name_id, comment, begin_byte_offset, end_byte_offset) VALUES (" //NON-NLS
outputStatement.executeUpdate("INSERT INTO content_tags (tag_id, obj_id, tag_name_id, comment, begin_byte_offset, end_byte_offset, user_name) VALUES (" //NON-NLS
+ value + ","
+ inputResultSet.getLong(2) + ","
+ inputResultSet.getLong(3) + ",'"
+ inputResultSet.getString(4) + "',"
+ inputResultSet.getLong(5) + ","
+ inputResultSet.getLong(6) + ")"); //NON-NLS
+ inputResultSet.getLong(6) + ",'"
+ inputResultSet.getString(7)+ "')"); //NON-NLS
} catch (SQLException ex) {
if (ex.getErrorCode() != 0) { // 0 if the entry already exists
@ -892,7 +893,8 @@ public class SingleUserCaseConverter {
+ value + ","
+ inputResultSet.getLong(2) + ","
+ inputResultSet.getLong(3) + ",'"
+ inputResultSet.getString(4) + "')"); //NON-NLS
+ inputResultSet.getString(4) + "','"
+ inputResultSet.getString(5) + "')"); //NON-NLS
} catch (SQLException ex) {
if (ex.getErrorCode() != 0) { // 0 if the entry already exists

View File

@ -21,7 +21,6 @@ package org.sleuthkit.autopsy.casemodule.services;
import java.io.Closeable;
import java.io.IOException;
import org.openide.util.Lookup;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchService;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
@ -38,7 +37,7 @@ import org.sleuthkit.datamodel.TskDataException;
public final class Blackboard implements Closeable {
private SleuthkitCase caseDb;
/**
* Constructs a representation of the blackboard, a place where artifacts
* and their attributes are posted.
@ -80,8 +79,8 @@ public final class Blackboard implements Closeable {
*
* @return A type object representing the artifact type.
*
* @throws BlackboardBlackboardException If there is a problem getting or
* adding the artifact type.
* @throws BlackboardException If there is a problem getting or adding the
* artifact type.
*/
public synchronized BlackboardArtifact.Type getOrAddArtifactType(String typeName, String displayName) throws BlackboardException {
if (null == caseDb) {
@ -110,8 +109,8 @@ public final class Blackboard implements Closeable {
*
* @return A type object representing the attribute type.
*
* @throws BlackboardBlackboardException If there is a problem getting or
* adding the attribute type.
* @throws BlackboardException If there is a problem getting or adding the
* attribute type.
*/
public synchronized BlackboardAttribute.Type getOrAddAttributeType(String typeName, BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE valueType, String displayName) throws BlackboardException {
if (null == caseDb) {
@ -140,7 +139,6 @@ public final class Blackboard implements Closeable {
caseDb = null;
}
/**
* A blackboard exception.
*/

View File

@ -47,7 +47,6 @@ import org.sleuthkit.datamodel.TskData;
public class TagsManager implements Closeable {
private static final Logger LOGGER = Logger.getLogger(TagsManager.class.getName());
private final SleuthkitCase caseDb;
/**
@ -71,13 +70,14 @@ public class TagsManager implements Closeable {
|| tagDisplayName.contains(";"));
}
@NbBundle.Messages({"TagsManager.notableTagEnding.text= (Notable)"})
/**
* Get String of text which is used to label tags as notable to the user.
*
* Get String of text which is used to label tags as notable to the user.
*
* @return Bundle message TagsManager.notableTagEnding.text
*/
public static String getNotableTagLabel(){
public static String getNotableTagLabel() {
return Bundle.TagsManager_notableTagEnding_text();
}
@ -123,13 +123,13 @@ public class TagsManager implements Closeable {
/**
* Returns a list of names of standard/predefined tags
*
*
* @return list of predefined tag names
*/
public static List<String> getStandardTagNames() {
return TagNameDefinition.getStandardTagNames();
}
/**
* Constructs a per case Autopsy service that manages the addition of
* content and artifact tags to the case database.
@ -166,21 +166,79 @@ public class TagsManager implements Closeable {
return caseDb.getTagNamesInUse();
}
/**
* Gets a list of all tag names currently in use in the case database for
* tagging content or artifacts by the specified user.
*
* @param userName - the user name that you want to get tags for
*
* @return A list, possibly empty, of TagName objects.
*
* @throws TskCoreException If there is an error querying the case database.
*/
public List<TagName> getTagNamesInUseForUser(String userName) throws TskCoreException {
Set<TagName> tagNameSet = new HashSet<>();
List<BlackboardArtifactTag> artifactTags = caseDb.getAllBlackboardArtifactTags();
for (BlackboardArtifactTag tag : artifactTags) {
if (tag.getUserName().equals(userName)) {
tagNameSet.add(tag.getName());
}
}
List<ContentTag> contentTags = caseDb.getAllContentTags();
for (ContentTag tag : contentTags) {
if (tag.getUserName().equals(userName)) {
tagNameSet.add(tag.getName());
}
}
return new ArrayList<>(tagNameSet);
}
/**
* Selects all of the rows from the tag_names table in the case database for
* which there is at least one matching row in the content_tags or
* blackboard_artifact_tags tables, for the given data source object id.
*
* @param dsObjId data source object id
*
*
* @return A list, possibly empty, of TagName data transfer objects (DTOs)
* for the rows.
* for the rows.
*
* @throws TskCoreException
*/
public List<TagName> getTagNamesInUse(long dsObjId) throws TskCoreException {
return caseDb.getTagNamesInUse(dsObjId);
}
/**
* Selects all of the rows from the tag_names table in the case database for
* which there is at least one matching row in the content_tags or
* blackboard_artifact_tags tables, for the given data source object id and user.
*
* @param dsObjId data source object id
* @param userName - the user name that you want to get tags for
*
* @return A list, possibly empty, of TagName data transfer objects (DTOs)
* for the rows.
*
* @throws TskCoreException
*/
public List<TagName> getTagNamesInUseForUser(long dsObjId, String userName) throws TskCoreException {
Set<TagName> tagNameSet = new HashSet<>();
List<BlackboardArtifactTag> artifactTags = caseDb.getAllBlackboardArtifactTags();
for (BlackboardArtifactTag tag : artifactTags) {
if (tag.getUserName().equals(userName) && tag.getArtifact().getDataSource().getId() == dsObjId) {
tagNameSet.add(tag.getName());
}
}
List<ContentTag> contentTags = caseDb.getAllContentTags();
for (ContentTag tag : contentTags) {
if (tag.getUserName().equals(userName) && tag.getContent().getDataSource().getId() == dsObjId) {
tagNameSet.add(tag.getName());
}
}
return new ArrayList<>(tagNameSet);
}
/**
* Gets a map of tag display names to tag name entries in the case database.
* It has keys for the display names of the standard tag types, the current
@ -416,24 +474,77 @@ public class TagsManager implements Closeable {
return caseDb.getContentTagsCountByTagName(tagName);
}
/**
* Gets content tags count by tag name for the specified user.
*
* @param tagName The representation of the desired tag type in the case
* database, which can be obtained by calling getTagNames
* and/or addTagName.
* @param userName - the user name that you want to get tags for
*
* @return A count of the content tags with the specified tag name for the
* specified user.
*
* @throws TskCoreException If there is an error getting the tags count from
* the case database.
*/
public long getContentTagsCountByTagNameForUser(TagName tagName, String userName) throws TskCoreException {
long count = 0;
List<ContentTag> contentTags = getContentTagsByTagName(tagName);
for (ContentTag tag : contentTags) {
if (userName.equals(tag.getUserName())) {
count++;
}
}
return count;
}
/**
* Gets content tags count by tag name, for the given data source
*
* @param tagName The representation of the desired tag type in the case
* database, which can be obtained by calling getTagNames and/or addTagName.
*
* database, which can be obtained by calling getTagNames
* and/or addTagName.
*
* @param dsObjId data source object id
*
* @return A count of the content tags with the specified tag name, and for
* the given data source
* the given data source
*
* @throws TskCoreException If there is an error getting the tags count from
* the case database.
* the case database.
*/
public long getContentTagsCountByTagName(TagName tagName, long dsObjId) throws TskCoreException {
return caseDb.getContentTagsCountByTagName(tagName, dsObjId);
}
/**
* Gets content tags count by tag name, for the given data source and user
*
* @param tagName The representation of the desired tag type in the case
* database, which can be obtained by calling getTagNames
* and/or addTagName.
*
* @param dsObjId data source object id
* @param userName - the user name that you want to get tags for
*
* @return A count of the content tags with the specified tag name, and for
* the given data source and user
*
* @throws TskCoreException If there is an error getting the tags count from
* the case database.
*/
public long getContentTagsCountByTagNameForUser(TagName tagName, long dsObjId, String userName) throws TskCoreException {
long count = 0;
List<ContentTag> contentTags = getContentTagsByTagName(tagName, dsObjId);
for (ContentTag tag : contentTags) {
if (userName.equals(tag.getUserName())) {
count++;
}
}
return count;
}
/**
* Gets a content tag by tag id.
*
@ -463,11 +574,11 @@ public class TagsManager implements Closeable {
return caseDb.getContentTagsByTagName(tagName);
}
/**
/**
* Gets content tags by tag name, for the given data source.
*
* @param tagName The tag name of interest.
*
*
* @param dsObjId data source object id
*
* @return A list, possibly empty, of the content tags with the specified
@ -479,7 +590,7 @@ public class TagsManager implements Closeable {
public List<ContentTag> getContentTagsByTagName(TagName tagName, long dsObjId) throws TskCoreException {
return caseDb.getContentTagsByTagName(tagName, dsObjId);
}
/**
* Gets content tags count by content.
*
@ -581,6 +692,31 @@ public class TagsManager implements Closeable {
return caseDb.getBlackboardArtifactTagsCountByTagName(tagName);
}
/**
* Gets an artifact tags count by tag name for a specific user.
*
* @param tagName The representation of the desired tag type in the case
* database, which can be obtained by calling getTagNames
* and/or addTagName.
* @param userName - the user name that you want to get tags for
*
* @return A count of the artifact tags with the specified tag name for the
* specified user.
*
* @throws TskCoreException If there is an error getting the tags count from
* the case database.
*/
public long getBlackboardArtifactTagsCountByTagNameForUser(TagName tagName, String userName) throws TskCoreException {
long count = 0;
List<BlackboardArtifactTag> artifactTags = getBlackboardArtifactTagsByTagName(tagName);
for (BlackboardArtifactTag tag : artifactTags) {
if (userName.equals(tag.getUserName())) {
count++;
}
}
return count;
}
/**
* Gets an artifact tags count by tag name, for the given data source.
*
@ -589,8 +725,8 @@ public class TagsManager implements Closeable {
* and/or addTagName.
* @param dsObjId data source object id
*
* @return A count of the artifact tags with the specified tag name,
* for the given data source.
* @return A count of the artifact tags with the specified tag name, for the
* given data source.
*
* @throws TskCoreException If there is an error getting the tags count from
* the case database.
@ -598,7 +734,34 @@ public class TagsManager implements Closeable {
public long getBlackboardArtifactTagsCountByTagName(TagName tagName, long dsObjId) throws TskCoreException {
return caseDb.getBlackboardArtifactTagsCountByTagName(tagName, dsObjId);
}
/**
* Gets an artifact tags count by tag name, for the given data source and
* user.
*
* @param tagName The representation of the desired tag type in the case
* database, which can be obtained by calling getTagNames
* and/or addTagName.
* @param dsObjId data source object id
* @param userName - the user name that you want to get tags for
*
* @return A count of the artifact tags with the specified tag name, for the
* given data source and user.
*
* @throws TskCoreException If there is an error getting the tags count from
* the case database.
*/
public long getBlackboardArtifactTagsCountByTagNameForUser(TagName tagName, long dsObjId, String userName) throws TskCoreException {
long count = 0;
List<BlackboardArtifactTag> artifactTags = getBlackboardArtifactTagsByTagName(tagName, dsObjId);
for (BlackboardArtifactTag tag : artifactTags) {
if (userName.equals(tag.getUserName())) {
count++;
}
}
return count;
}
/**
* Gets an artifact tag by tag id.
*
@ -647,7 +810,7 @@ public class TagsManager implements Closeable {
public List<BlackboardArtifactTag> getBlackboardArtifactTagsByTagName(TagName tagName, long dsObjId) throws TskCoreException {
return caseDb.getBlackboardArtifactTagsByTagName(tagName, dsObjId);
}
/**
* Gets artifact tags for a particular artifact.
*

View File

@ -21,51 +21,48 @@ package org.sleuthkit.autopsy.centralrepository;
import java.awt.event.ActionEvent;
import java.util.logging.Level;
import javax.swing.AbstractAction;
import org.openide.DialogDisplayer;
import org.openide.NotifyDescriptor;
import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamArtifactUtil;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.TskCoreException;
/**
* An AbstractAction to manage adding and modifying a Central Repository file
* instance comment.
*/
@Messages({"AddEditCentralRepoCommentAction.menuItemText.addEditCentralRepoComment=Add/Edit Central Repository Comment"})
public final class AddEditCentralRepoCommentAction extends AbstractAction {
private static final Logger logger = Logger.getLogger(AddEditCentralRepoCommentAction.class.getName());
private boolean addToDatabase;
private CorrelationAttribute correlationAttribute;
String title;
private String comment;
/**
* Private constructor to create an instance given a CorrelationAttribute.
* Constructor to create an instance given a CorrelationAttribute.
*
* @param correlationAttribute The correlation attribute to modify.
* @param title The text for the menu item.
*/
private AddEditCentralRepoCommentAction(CorrelationAttribute correlationAttribute, String title) {
super(title);
this.title = title;
public AddEditCentralRepoCommentAction(CorrelationAttribute correlationAttribute) {
super(Bundle.AddEditCentralRepoCommentAction_menuItemText_addEditCentralRepoComment());
this.correlationAttribute = correlationAttribute;
}
/**
* Private constructor to create an instance given an AbstractFile.
* Constructor to create an instance given an AbstractFile.
*
* @param file The file from which a correlation attribute to modify is
* derived.
* @param title The text for the menu item.
* @param file The file from which a correlation attribute to modify is
* derived.
*/
private AddEditCentralRepoCommentAction(AbstractFile file, String title) {
super(title);
this.title = title;
public AddEditCentralRepoCommentAction(AbstractFile file) {
super(Bundle.AddEditCentralRepoCommentAction_menuItemText_addEditCentralRepoComment());
correlationAttribute = EamArtifactUtil.getCorrelationAttributeFromContent(file);
if (correlationAttribute == null) {
addToDatabase = true;
@ -73,26 +70,24 @@ public final class AddEditCentralRepoCommentAction extends AbstractAction {
}
}
@Override
public void actionPerformed(ActionEvent event) {
addEditCentralRepoComment();
}
/**
* Create a Add/Edit dialog for the correlation attribute file instance
* comment. The comment will be updated in the database if the file instance
* exists there, or a new file instance will be added to the database with
* the comment attached otherwise.
*
* The current comment for this instance is returned in case it is needed to
* update the display.
*
* @return the current comment for this instance
* The current comment for this instance is saved in case it is needed to
* update the display. If the comment was not changed either due to the
* action being canceled or the occurrence of an error, the comment will be
* null.
*/
public String addEditCentralRepoComment() {
CentralRepoCommentDialog centralRepoCommentDialog = new CentralRepoCommentDialog(correlationAttribute, title);
@Override
public void actionPerformed(ActionEvent event) {
CentralRepoCommentDialog centralRepoCommentDialog = new CentralRepoCommentDialog(correlationAttribute);
centralRepoCommentDialog.display();
comment = null;
if (centralRepoCommentDialog.isCommentUpdated()) {
EamDb dbManager;
@ -104,45 +99,35 @@ public final class AddEditCentralRepoCommentAction extends AbstractAction {
} else {
dbManager.updateAttributeInstanceComment(correlationAttribute);
}
comment = centralRepoCommentDialog.getComment();
} catch (EamDbException ex) {
logger.log(Level.SEVERE, "Error adding comment", ex);
NotifyDescriptor notifyDescriptor = new NotifyDescriptor.Message(
"An error occurred while trying to save the comment to the central repository.",
NotifyDescriptor.ERROR_MESSAGE);
DialogDisplayer.getDefault().notify(notifyDescriptor);
}
}
return centralRepoCommentDialog.getComment();
}
/**
* Create an instance labeled "Add/Edit Central Repository Comment" given an
* AbstractFile. This is intended for the result view.
* Retrieve the comment that was last saved. If a comment update was
* canceled or an error occurred while attempting to save the comment, the
* comment will be null.
*
* @param file The file from which a correlation attribute to modify is
* derived.
*
* @return The instance.
*
* @throws EamDbException
* @throws NoCurrentCaseException
* @throws TskCoreException
* @return The comment.
*/
@Messages({"AddEditCentralRepoCommentAction.menuItemText.addEditCentralRepoComment=Add/Edit Central Repository Comment"})
public static AddEditCentralRepoCommentAction createAddEditCentralRepoCommentAction(AbstractFile file) {
return new AddEditCentralRepoCommentAction(file,
Bundle.AddEditCentralRepoCommentAction_menuItemText_addEditCentralRepoComment());
public String getComment() {
return comment;
}
/**
* Create an instance labeled "Add/Edit Comment" given a
* CorrelationAttribute. This is intended for the content view.
*
* @param correlationAttribute The correlation attribute to modify.
*
* @return The instance.
* Retrieve the associated correlation attribute.
*
* @return The correlation attribute.
*/
@Messages({"AddEditCentralRepoCommentAction.menuItemText.addEditComment=Add/Edit Comment"})
public static AddEditCentralRepoCommentAction createAddEditCommentAction(CorrelationAttribute correlationAttribute) {
return new AddEditCentralRepoCommentAction(correlationAttribute,
Bundle.AddEditCentralRepoCommentAction_menuItemText_addEditComment());
public CorrelationAttribute getCorrelationAttribute() {
return correlationAttribute;
}
}

View File

@ -5,8 +5,6 @@ OpenIDE-Module-Long-Description=\
Correlation Engine ingest module and central database. \n\n\
The Correlation Engine ingest module stores attributes of artifacts matching selected correlation types into a central database.\n\
Stored attributes are used in future cases to correlate and analyzes files and artifacts during ingest.
CentralRepoCommentDialog.fileLabel.text=File:
CentralRepoCommentDialog.commentLabel.text=Comment:
CentralRepoCommentDialog.pathLabel.text=
CentralRepoCommentDialog.okButton.text=&OK
CentralRepoCommentDialog.cancelButton.text=C&ancel

View File

@ -31,14 +31,7 @@
<Group type="103" groupAlignment="0" attributes="0">
<Component id="jScrollPane1" pref="500" max="32767" attributes="0"/>
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="fileLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="pathLabel" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="commentLabel" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="commentLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="451" max="32767" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
@ -55,21 +48,16 @@
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="fileLabel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="pathLabel" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="19" max="-2" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="commentLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="jScrollPane1" max="32767" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="okButton" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="cancelButton" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="cancelButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="okButton" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
@ -113,20 +101,6 @@
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cancelButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="fileLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/Bundle.properties" key="CentralRepoCommentDialog.fileLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="pathLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/Bundle.properties" key="CentralRepoCommentDialog.pathLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="commentLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">

View File

@ -18,6 +18,7 @@
*/
package org.sleuthkit.autopsy.centralrepository;
import org.openide.util.NbBundle.Messages;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
@ -26,6 +27,7 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeIns
* Dialog to allow Central Repository file instance comments to be added and
* modified.
*/
@Messages({"CentralRepoCommentDialog.title.addEditCentralRepoComment=Add/Edit Central Repository Comment"})
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
final class CentralRepoCommentDialog extends javax.swing.JDialog {
@ -37,10 +39,9 @@ final class CentralRepoCommentDialog extends javax.swing.JDialog {
* Create an instance.
*
* @param correlationAttribute The correlation attribute to be modified.
* @param title The title to assign the dialog.
*/
CentralRepoCommentDialog(CorrelationAttribute correlationAttribute, String title) {
super(WindowManager.getDefault().getMainWindow(), title);
CentralRepoCommentDialog(CorrelationAttribute correlationAttribute) {
super(WindowManager.getDefault().getMainWindow(), Bundle.CentralRepoCommentDialog_title_addEditCentralRepoComment());
initComponents();
@ -51,7 +52,6 @@ final class CentralRepoCommentDialog extends javax.swing.JDialog {
currentComment = instance.getComment();
}
pathLabel.setText(instance.getFilePath());
commentTextArea.setText(instance.getComment());
this.correlationAttribute = correlationAttribute;
@ -82,6 +82,7 @@ final class CentralRepoCommentDialog extends javax.swing.JDialog {
* Get the current comment.
* If the user hit OK, this will be the new comment.
* If the user canceled, this will be the original comment.
*
* @return the comment
*/
String getComment() {
@ -101,8 +102,6 @@ final class CentralRepoCommentDialog extends javax.swing.JDialog {
commentTextArea = new javax.swing.JTextArea();
okButton = new javax.swing.JButton();
cancelButton = new javax.swing.JButton();
fileLabel = new javax.swing.JLabel();
pathLabel = new javax.swing.JLabel();
commentLabel = new javax.swing.JLabel();
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
@ -129,10 +128,6 @@ final class CentralRepoCommentDialog extends javax.swing.JDialog {
}
});
org.openide.awt.Mnemonics.setLocalizedText(fileLabel, org.openide.util.NbBundle.getMessage(CentralRepoCommentDialog.class, "CentralRepoCommentDialog.fileLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(pathLabel, org.openide.util.NbBundle.getMessage(CentralRepoCommentDialog.class, "CentralRepoCommentDialog.pathLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(commentLabel, org.openide.util.NbBundle.getMessage(CentralRepoCommentDialog.class, "CentralRepoCommentDialog.commentLabel.text")); // NOI18N
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
@ -144,12 +139,7 @@ final class CentralRepoCommentDialog extends javax.swing.JDialog {
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 500, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(fileLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(pathLabel))
.addComponent(commentLabel))
.addComponent(commentLabel)
.addGap(0, 451, Short.MAX_VALUE))
.addGroup(layout.createSequentialGroup()
.addGap(0, 0, Short.MAX_VALUE)
@ -162,17 +152,13 @@ final class CentralRepoCommentDialog extends javax.swing.JDialog {
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(fileLabel)
.addComponent(pathLabel))
.addGap(19, 19, 19)
.addComponent(commentLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jScrollPane1)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(okButton)
.addComponent(cancelButton))
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(cancelButton)
.addComponent(okButton))
.addContainerGap())
);
@ -195,9 +181,7 @@ final class CentralRepoCommentDialog extends javax.swing.JDialog {
private javax.swing.JButton cancelButton;
private javax.swing.JLabel commentLabel;
private javax.swing.JTextArea commentTextArea;
private javax.swing.JLabel fileLabel;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JButton okButton;
private javax.swing.JLabel pathLabel;
// End of variables declaration//GEN-END:variables
}

View File

@ -38,19 +38,23 @@ public class CentralRepoContextMenuActionsProvider implements ContextMenuActions
@Override
public List<Action> getActions() {
ArrayList<Action> actions = new ArrayList<>();
ArrayList<Action> actionsList = new ArrayList<>();
Collection<? extends AbstractFile> selectedFiles = Utilities.actionsGlobalContext().lookupAll(AbstractFile.class);
if (selectedFiles.size() != 1) {
return actions;
return actionsList;
}
for (AbstractFile file : selectedFiles) {
if (EamDbUtil.useCentralRepo() && EamArtifactUtil.isSupportedAbstractFileType(file) && file.isFile()) {
actions.add(AddEditCentralRepoCommentAction.createAddEditCentralRepoCommentAction(file));
AddEditCentralRepoCommentAction action = new AddEditCentralRepoCommentAction(file);
if (action.getCorrelationAttribute() == null) {
action.setEnabled(false);
}
actionsList.add(action);
}
}
return actions;
return actionsList;
}
}

View File

@ -3,7 +3,7 @@ DataContentViewerOtherCases.showCaseDetailsMenuItem.text=Show Case Details
DataContentViewerOtherCases.table.toolTip.text=Click column name to sort. Right-click on the table for more options.
DataContentViewerOtherCases.exportToCSVMenuItem.text=Export Selected Rows to CSV
DataContentViewerOtherCases.showCommonalityMenuItem.text=Show Frequency
DataContentViewerOtherCases.addCommentMenuItem.text=Add/Edit Comment
DataContentViewerOtherCases.addCommentMenuItem.text=Add/Edit Central Repository Comment
DataContentViewerOtherCases.earliestCaseDate.text=Earliest Case Date
DataContentViewerOtherCases.earliestCaseLabel.toolTipText=
DataContentViewerOtherCases.earliestCaseLabel.text=Central Repository Starting Date:

View File

@ -80,7 +80,7 @@
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="otherCasesPanel" pref="483" max="32767" attributes="0"/>
<Component id="otherCasesPanel" pref="59" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
</Layout>
@ -106,7 +106,7 @@
<EmptySpace min="0" pref="483" max="32767" attributes="0"/>
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="tableContainerPanel" pref="483" max="32767" attributes="0"/>
<Component id="tableContainerPanel" pref="59" max="32767" attributes="0"/>
<EmptySpace min="-2" pref="0" max="-2" attributes="0"/>
</Group>
</Group>
@ -133,23 +133,18 @@
<Component id="earliestCaseLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="earliestCaseDate" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="tableStatusPanelLabel" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<Component id="tableScrollPane" pref="176" max="32767" attributes="0"/>
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
<Group type="103" groupAlignment="1" attributes="0">
<Group type="103" groupAlignment="3" attributes="0">
<Component id="earliestCaseLabel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="earliestCaseDate" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="tableStatusPanelLabel" min="-2" pref="16" max="-2" attributes="0"/>
<Component id="tableScrollPane" pref="27" max="32767" attributes="0"/>
<EmptySpace min="-2" pref="2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="earliestCaseLabel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="earliestCaseDate" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
<Component id="tableStatusPanel" min="-2" max="-2" attributes="0"/>
@ -230,13 +225,6 @@
</DimensionLayout>
</Layout>
</Container>
<Component class="javax.swing.JLabel" name="tableStatusPanelLabel">
<Properties>
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="33" green="0" red="ff" type="rgb"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
</SubComponents>

View File

@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.centralrepository.contentviewer;
import java.awt.Component;
import java.awt.FontMetrics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedWriter;
@ -38,7 +39,6 @@ import java.util.Map;
import java.util.Objects;
import java.util.logging.Level;
import org.sleuthkit.autopsy.coreutils.Logger;
import java.util.stream.Collectors;
import javax.swing.JFileChooser;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
@ -86,7 +86,10 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
private static final long serialVersionUID = -1L;
private final static Logger logger = Logger.getLogger(DataContentViewerOtherCases.class.getName());
private static final Logger logger = Logger.getLogger(DataContentViewerOtherCases.class.getName());
private static final int DEFAULT_MIN_CELL_WIDTH = 15;
private static final int CELL_TEXT_WIDTH_PADDING = 5;
private final DataContentViewerOtherCasesTableModel tableModel;
private final Collection<CorrelationAttribute> correlationAttributes;
@ -126,13 +129,16 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
showCommonalityDetails();
} else if (jmi.equals(addCommentMenuItem)) {
try {
OtherOccurrenceNodeData selectedNode = (OtherOccurrenceNodeData) tableModel.getRow(otherCasesTable.getSelectedRow());
AddEditCentralRepoCommentAction action = AddEditCentralRepoCommentAction.createAddEditCommentAction(selectedNode.createCorrelationAttribute());
String currentComment = action.addEditCentralRepoComment();
selectedNode.updateComment(currentComment);
otherCasesTable.repaint();
OtherOccurrenceNodeInstanceData selectedNode = (OtherOccurrenceNodeInstanceData) tableModel.getRow(otherCasesTable.getSelectedRow());
AddEditCentralRepoCommentAction action = new AddEditCentralRepoCommentAction(selectedNode.createCorrelationAttribute());
action.actionPerformed(null);
String currentComment = action.getComment();
if (currentComment != null) {
selectedNode.updateComment(action.getComment());
otherCasesTable.repaint();
}
} catch (EamDbException ex) {
logger.log(Level.SEVERE, "Error performing Add/Edit Comment action", ex);
logger.log(Level.SEVERE, "Error performing Add/Edit Central Repository Comment action", ex);
}
}
}
@ -147,7 +153,6 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
// Set background of every nth row as light grey.
TableCellRenderer renderer = new DataContentViewerOtherCasesTableCellRenderer();
otherCasesTable.setDefaultRenderer(Object.class, renderer);
tableStatusPanelLabel.setVisible(false);
}
@ -205,7 +210,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
if (-1 != selectedRowViewIdx) {
EamDb dbManager = EamDb.getInstance();
int selectedRowModelIdx = otherCasesTable.convertRowIndexToModel(selectedRowViewIdx);
OtherOccurrenceNodeData nodeData = (OtherOccurrenceNodeData) tableModel.getRow(selectedRowModelIdx);
OtherOccurrenceNodeInstanceData nodeData = (OtherOccurrenceNodeInstanceData) tableModel.getRow(selectedRowModelIdx);
CorrelationCase eamCasePartial = nodeData.getCorrelationAttributeInstance().getCorrelationCase();
if (eamCasePartial == null) {
JOptionPane.showConfirmDialog(showCaseDetailsMenuItem,
@ -460,12 +465,12 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
@Messages({"DataContentViewerOtherCases.earliestCaseNotAvailable= Not Enabled."})
/**
* Gets the list of Eam Cases and determines the earliest case creation date.
* Sets the label to display the earliest date string to the user.
* Gets the list of Eam Cases and determines the earliest case creation
* date. Sets the label to display the earliest date string to the user.
*/
private void setEarliestCaseDate() {
String dateStringDisplay = Bundle.DataContentViewerOtherCases_earliestCaseNotAvailable();
private void setEarliestCaseDate() {
String dateStringDisplay = Bundle.DataContentViewerOtherCases_earliestCaseNotAvailable();
if (EamDb.isEnabled()) {
LocalDateTime earliestDate = LocalDateTime.now(DateTimeZone.UTC);
DateFormat datetimeFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.US);
@ -473,15 +478,15 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
EamDb dbManager = EamDb.getInstance();
List<CorrelationCase> cases = dbManager.getCases();
for (CorrelationCase aCase : cases) {
LocalDateTime caseDate = LocalDateTime.fromDateFields(datetimeFormat.parse(aCase.getCreationDate()));
if (caseDate.isBefore(earliestDate)) {
LocalDateTime caseDate = LocalDateTime.fromDateFields(datetimeFormat.parse(aCase.getCreationDate()));
if (caseDate.isBefore(earliestDate)) {
earliestDate = caseDate;
dateStringDisplay = aCase.getCreationDate();
}
}
}
} catch (EamDbException ex) {
logger.log(Level.SEVERE, "Error getting list of cases from database.", ex); // NON-NLS
} catch (ParseException ex) {
@ -493,10 +498,10 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
}
/**
* Query the central repo database (if enabled) and the case database to find all
* artifact instances correlated to the given central repository artifact. If the
* central repo is not enabled, this will only return files from the current case
* with matching MD5 hashes.
* Query the central repo database (if enabled) and the case database to
* find all artifact instances correlated to the given central repository
* artifact. If the central repo is not enabled, this will only return files
* from the current case with matching MD5 hashes.
*
* @param corAttr CorrelationAttribute to query for
* @param dataSourceName Data source to filter results
@ -504,19 +509,19 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
*
* @return A collection of correlated artifact instances
*/
private Map<UniquePathKey,OtherOccurrenceNodeData> getCorrelatedInstances(CorrelationAttribute corAttr, String dataSourceName, String deviceId) {
private Map<UniquePathKey, OtherOccurrenceNodeInstanceData> getCorrelatedInstances(CorrelationAttribute corAttr, String dataSourceName, String deviceId) {
// @@@ Check exception
try {
final Case openCase = Case.getCurrentCase();
String caseUUID = openCase.getName();
HashMap<UniquePathKey,OtherOccurrenceNodeData> nodeDataMap = new HashMap<>();
HashMap<UniquePathKey, OtherOccurrenceNodeInstanceData> nodeDataMap = new HashMap<>();
if (EamDb.isEnabled()) {
List<CorrelationAttributeInstance> instances = EamDb.getInstance().getArtifactInstancesByTypeValue(corAttr.getCorrelationType(), corAttr.getCorrelationValue());
for (CorrelationAttributeInstance artifactInstance:instances) {
for (CorrelationAttributeInstance artifactInstance : instances) {
// Only add the attribute if it isn't the object the user selected.
// We consider it to be a different object if at least one of the following is true:
// - the case UUID is different
@ -528,14 +533,14 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|| !artifactInstance.getCorrelationDataSource().getDeviceID().equals(deviceId)
|| !artifactInstance.getFilePath().equalsIgnoreCase(file.getParentPath() + file.getName())) {
OtherOccurrenceNodeData newNode = new OtherOccurrenceNodeData(artifactInstance, corAttr.getCorrelationType(), corAttr.getCorrelationValue());
OtherOccurrenceNodeInstanceData newNode = new OtherOccurrenceNodeInstanceData(artifactInstance, corAttr.getCorrelationType(), corAttr.getCorrelationValue());
UniquePathKey uniquePathKey = new UniquePathKey(newNode);
nodeDataMap.put(uniquePathKey, newNode);
}
}
}
if (corAttr.getCorrelationType().getDisplayName().equals("Files")) {
if (corAttr.getCorrelationType().getDisplayName().equals("Files")) {
List<AbstractFile> caseDbFiles = getCaseDbMatches(corAttr, openCase);
for (AbstractFile caseDbFile : caseDbFiles) {
@ -558,13 +563,17 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
}
/**
* Get all other abstract files in the current case with the same MD5 as the selected node.
* Get all other abstract files in the current case with the same MD5 as the
* selected node.
*
* @param corAttr The CorrelationAttribute containing the MD5 to search for
* @param openCase The current case
*
* @return List of matching AbstractFile objects
*
* @throws NoCurrentCaseException
* @throws TskCoreException
* @throws EamDbException
* @throws EamDbException
*/
private List<AbstractFile> getCaseDbMatches(CorrelationAttribute corAttr, Case openCase) throws NoCurrentCaseException, TskCoreException, EamDbException {
String md5 = corAttr.getCorrelationValue();
@ -584,18 +593,18 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
/**
* Adds the file to the nodeDataMap map if it does not already exist
*
* @param autopsyCase
*
* @param autopsyCase
* @param nodeDataMap
* @param newFile
*
* @throws TskCoreException
* @throws EamDbException
*/
private void addOrUpdateNodeData(final Case autopsyCase, Map<UniquePathKey,OtherOccurrenceNodeData> nodeDataMap, AbstractFile newFile) throws TskCoreException, EamDbException {
OtherOccurrenceNodeData newNode = new OtherOccurrenceNodeData(newFile, autopsyCase);
private void addOrUpdateNodeData(final Case autopsyCase, Map<UniquePathKey, OtherOccurrenceNodeInstanceData> nodeDataMap, AbstractFile newFile) throws TskCoreException, EamDbException {
OtherOccurrenceNodeInstanceData newNode = new OtherOccurrenceNodeInstanceData(newFile, autopsyCase);
// If the caseDB object has a notable tag associated with it, update
// the known status to BAD
if (newNode.getKnown() != TskData.FileKnown.BAD) {
@ -611,13 +620,13 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
// Make a key to see if the file is already in the map
UniquePathKey uniquePathKey = new UniquePathKey(newNode);
// If this node is already in the list, the only thing we need to do is
// update the known status to BAD if the caseDB version had known status BAD.
// Otherwise this is a new node so add the new node to the map.
if (nodeDataMap.containsKey(uniquePathKey)) {
if (newNode.getKnown() == TskData.FileKnown.BAD) {
OtherOccurrenceNodeData prevInstance = nodeDataMap.get(uniquePathKey);
OtherOccurrenceNodeInstanceData prevInstance = nodeDataMap.get(uniquePathKey);
prevInstance.updateKnown(newNode.getKnown());
}
} else {
@ -640,7 +649,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
} else {
return this.file != null
&& this.file.getSize() > 0
&& ((this.file.getMd5Hash() != null) && ( ! this.file.getMd5Hash().isEmpty()));
&& ((this.file.getMd5Hash() != null) && (!this.file.getMd5Hash().isEmpty()));
}
}
@ -663,8 +672,10 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
*
* @param node The node being viewed.
*/
@Messages({"DataContentViewerOtherCases.table.isempty=There are no associated artifacts or files from other occurrences to display.",
"DataContentViewerOtherCases.table.noArtifacts=Correlation cannot be performed on the selected file."})
@Messages({
"DataContentViewerOtherCases.table.noArtifacts=Item has no attributes with which to search.",
"DataContentViewerOtherCases.table.noResultsFound=No results found."
})
private void populateTable(Node node) {
String dataSourceName = "";
String deviceId = "";
@ -682,7 +693,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
// get the attributes we can correlate on
correlationAttributes.addAll(getCorrelationAttributesFromNode(node));
for (CorrelationAttribute corAttr : correlationAttributes) {
Map<UniquePathKey,OtherOccurrenceNodeData> correlatedNodeDataMap = new HashMap<>(0);
Map<UniquePathKey, OtherOccurrenceNodeInstanceData> correlatedNodeDataMap = new HashMap<>(0);
// get correlation and reference set instances from DB
correlatedNodeDataMap.putAll(getCorrelatedInstances(corAttr, dataSourceName, deviceId));
@ -694,36 +705,45 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
}
if (correlationAttributes.isEmpty()) {
// @@@ BC: We should have a more descriptive message than this. Mention that the file didn't have a MD5, etc.
displayMessageOnTableStatusPanel(Bundle.DataContentViewerOtherCases_table_noArtifacts());
tableModel.addNodeData(new OtherOccurrenceNodeMessageData(Bundle.DataContentViewerOtherCases_table_noArtifacts()));
setColumnWidthToText(0, Bundle.DataContentViewerOtherCases_table_noArtifacts());
} else if (0 == tableModel.getRowCount()) {
displayMessageOnTableStatusPanel(Bundle.DataContentViewerOtherCases_table_isempty());
tableModel.addNodeData(new OtherOccurrenceNodeMessageData(Bundle.DataContentViewerOtherCases_table_noResultsFound()));
setColumnWidthToText(0, Bundle.DataContentViewerOtherCases_table_noResultsFound());
} else {
clearMessageOnTableStatusPanel();
setColumnWidths();
}
setEarliestCaseDate();
}
/**
* Adjust a given column for the text provided.
*
* @param columnIndex The index of the column to adjust.
* @param text The text whose length will be used to adjust the
* column width.
*/
private void setColumnWidthToText(int columnIndex, String text) {
TableColumn column = otherCasesTable.getColumnModel().getColumn(columnIndex);
FontMetrics fontMetrics = otherCasesTable.getFontMetrics(otherCasesTable.getFont());
int stringWidth = fontMetrics.stringWidth(text);
column.setMinWidth(stringWidth + CELL_TEXT_WIDTH_PADDING);
}
/**
* Adjust column widths to their preferred values.
*/
private void setColumnWidths() {
for (int idx = 0; idx < tableModel.getColumnCount(); idx++) {
TableColumn column = otherCasesTable.getColumnModel().getColumn(idx);
int colWidth = tableModel.getColumnPreferredWidth(idx);
if (0 < colWidth) {
column.setPreferredWidth(colWidth);
column.setMinWidth(DEFAULT_MIN_CELL_WIDTH);
int columnWidth = tableModel.getColumnPreferredWidth(idx);
if (columnWidth > 0) {
column.setPreferredWidth(columnWidth);
}
}
}
private void displayMessageOnTableStatusPanel(String message) {
tableStatusPanelLabel.setText(message);
tableStatusPanelLabel.setVisible(true);
}
private void clearMessageOnTableStatusPanel() {
tableStatusPanelLabel.setVisible(false);
}
/**
* 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
@ -747,7 +767,6 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
earliestCaseLabel = new javax.swing.JLabel();
earliestCaseDate = new javax.swing.JLabel();
tableStatusPanel = new javax.swing.JPanel();
tableStatusPanelLabel = new javax.swing.JLabel();
rightClickPopupMenu.addPopupMenuListener(new javax.swing.event.PopupMenuListener() {
public void popupMenuCanceled(javax.swing.event.PopupMenuEvent evt) {
@ -809,8 +828,6 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
.addGap(0, 16, Short.MAX_VALUE)
);
tableStatusPanelLabel.setForeground(new java.awt.Color(255, 0, 51));
javax.swing.GroupLayout tableContainerPanelLayout = new javax.swing.GroupLayout(tableContainerPanel);
tableContainerPanel.setLayout(tableContainerPanelLayout);
tableContainerPanelLayout.setHorizontalGroup(
@ -823,20 +840,16 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
.addComponent(earliestCaseLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(earliestCaseDate)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(tableStatusPanelLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addContainerGap())
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
tableContainerPanelLayout.setVerticalGroup(
tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, tableContainerPanelLayout.createSequentialGroup()
.addComponent(tableScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 176, Short.MAX_VALUE)
.addGap(0, 0, 0)
.addGroup(tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addGroup(tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(earliestCaseLabel)
.addComponent(earliestCaseDate))
.addComponent(tableStatusPanelLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE))
.addComponent(tableScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 27, Short.MAX_VALUE)
.addGap(2, 2, 2)
.addGroup(tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(earliestCaseLabel)
.addComponent(earliestCaseDate))
.addGap(0, 0, 0)
.addComponent(tableStatusPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 0, 0))
@ -855,7 +868,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
.addGap(0, 483, Short.MAX_VALUE)
.addGroup(otherCasesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(otherCasesPanelLayout.createSequentialGroup()
.addComponent(tableContainerPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 483, Short.MAX_VALUE)
.addComponent(tableContainerPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 59, Short.MAX_VALUE)
.addGap(0, 0, 0)))
);
@ -867,7 +880,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(otherCasesPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 483, Short.MAX_VALUE)
.addComponent(otherCasesPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 59, Short.MAX_VALUE)
);
}// </editor-fold>//GEN-END:initComponents
@ -877,8 +890,9 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
if (EamDbUtil.useCentralRepo() && otherCasesTable.getSelectedRowCount() == 1) {
int rowIndex = otherCasesTable.getSelectedRow();
OtherOccurrenceNodeData selectedNode = (OtherOccurrenceNodeData) tableModel.getRow(rowIndex);
if (selectedNode.isCentralRepoNode()) {
enableCentralRepoActions = true;
if (selectedNode instanceof OtherOccurrenceNodeInstanceData) {
OtherOccurrenceNodeInstanceData instanceData = (OtherOccurrenceNodeInstanceData) selectedNode;
enableCentralRepoActions = instanceData.isCentralRepoNode();
}
}
@ -902,20 +916,19 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
private javax.swing.JPanel tableContainerPanel;
private javax.swing.JScrollPane tableScrollPane;
private javax.swing.JPanel tableStatusPanel;
private javax.swing.JLabel tableStatusPanelLabel;
// End of variables declaration//GEN-END:variables
/**
* Used as a key to ensure we eliminate duplicates from the result set by
* not overwriting CR correlation instances.
*/
static final class UniquePathKey {
private static final class UniquePathKey {
private final String dataSourceID;
private final String filePath;
private final String type;
UniquePathKey(OtherOccurrenceNodeData nodeData) {
UniquePathKey(OtherOccurrenceNodeInstanceData nodeData) {
super();
dataSourceID = nodeData.getDeviceID();
if (nodeData.getFilePath() != null) {
@ -929,10 +942,10 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
@Override
public boolean equals(Object other) {
if (other instanceof UniquePathKey) {
UniquePathKey otherKey = (UniquePathKey)(other);
return ( Objects.equals(otherKey.dataSourceID, this.dataSourceID)
&& Objects.equals(otherKey.filePath, this.filePath)
&& Objects.equals(otherKey.type, this.type));
UniquePathKey otherKey = (UniquePathKey) (other);
return (Objects.equals(otherKey.getDataSourceID(), this.getDataSourceID())
&& Objects.equals(otherKey.getFilePath(), this.getFilePath())
&& Objects.equals(otherKey.getType(), this.getType()));
}
return false;
}
@ -942,7 +955,34 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
//int hash = 7;
//hash = 67 * hash + this.dataSourceID.hashCode();
//hash = 67 * hash + this.filePath.hashCode();
return Objects.hash(dataSourceID, filePath, type);
return Objects.hash(getDataSourceID(), getFilePath(), getType());
}
/**
* Get the type of this UniquePathKey.
*
* @return the type
*/
String getType() {
return type;
}
/**
* Get the file path for the UniquePathKey.
*
* @return the filePath
*/
String getFilePath() {
return filePath;
}
/**
* Get the data source id for the UniquePathKey.
*
* @return the dataSourceID
*/
String getDataSourceID() {
return dataSourceID;
}
}

View File

@ -1,7 +1,7 @@
/*
* Central Repository
*
* Copyright 2015-2017 Basis Technology Corp.
* Copyright 2015-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -22,20 +22,20 @@ import java.util.ArrayList;
import java.util.List;
import javax.swing.table.AbstractTableModel;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
/**
* Model for cells in data content viewer table
*/
public class DataContentViewerOtherCasesTableModel extends AbstractTableModel {
private static final long serialVersionUID = 1L;
@Messages({"DataContentViewerOtherCasesTableModel.case=Case",
"DataContentViewerOtherCasesTableModel.device=Device",
"DataContentViewerOtherCasesTableModel.dataSource=Data Source",
"DataContentViewerOtherCasesTableModel.path=Path",
"DataContentViewerOtherCasesTableModel.type=Correlation Type",
"DataContentViewerOtherCasesTableModel.value=Correlation Value",
"DataContentViewerOtherCasesTableModel.attribute=Matched Attribute",
"DataContentViewerOtherCasesTableModel.value=Attribute Value",
"DataContentViewerOtherCasesTableModel.known=Known",
"DataContentViewerOtherCasesTableModel.comment=Comment",
"DataContentViewerOtherCasesTableModel.noData=No Data.",})
@ -44,7 +44,7 @@ public class DataContentViewerOtherCasesTableModel extends AbstractTableModel {
// If order is changed, update the CellRenderer to ensure correct row coloring.
CASE_NAME(Bundle.DataContentViewerOtherCasesTableModel_case(), 100),
DATA_SOURCE(Bundle.DataContentViewerOtherCasesTableModel_dataSource(), 100),
TYPE(Bundle.DataContentViewerOtherCasesTableModel_type(), 100),
ATTRIBUTE(Bundle.DataContentViewerOtherCasesTableModel_attribute(), 125),
VALUE(Bundle.DataContentViewerOtherCasesTableModel_value(), 200),
KNOWN(Bundle.DataContentViewerOtherCasesTableModel_known(), 50),
FILE_PATH(Bundle.DataContentViewerOtherCasesTableModel_path(), 450),
@ -68,7 +68,7 @@ public class DataContentViewerOtherCasesTableModel extends AbstractTableModel {
}
};
List<OtherOccurrenceNodeData> nodeDataList;
private final List<OtherOccurrenceNodeData> nodeDataList;
DataContentViewerOtherCasesTableModel() {
nodeDataList = new ArrayList<>();
@ -109,26 +109,41 @@ public class DataContentViewerOtherCasesTableModel extends AbstractTableModel {
return Bundle.DataContentViewerOtherCasesTableModel_noData();
}
return mapValueById(rowIdx, TableColumns.values()[colIdx]);
}
Object getRow(int rowIdx) {
return nodeDataList.get(rowIdx);
OtherOccurrenceNodeData nodeData = nodeDataList.get(rowIdx);
TableColumns columnId = TableColumns.values()[colIdx];
if (nodeData instanceof OtherOccurrenceNodeMessageData) {
return mapNodeMessageData((OtherOccurrenceNodeMessageData) nodeData, columnId);
}
return mapNodeInstanceData((OtherOccurrenceNodeInstanceData) nodeData, columnId);
}
/**
* Map a rowIdx and colId to the value in that cell.
* Map a column ID to the value in that cell for node message data.
*
* @param rowIdx Index of row to search
* @param colId ID of column to search
* @param nodeData The node message data.
* @param columnId The ID of the cell column.
*
* @return value in the cell
* @return The value in the cell.
*/
private Object mapValueById(int rowIdx, TableColumns colId) {
OtherOccurrenceNodeData nodeData = nodeDataList.get(rowIdx);
private Object mapNodeMessageData(OtherOccurrenceNodeMessageData nodeData, TableColumns columnId) {
if (columnId == TableColumns.CASE_NAME) {
return nodeData.getDisplayMessage();
}
return "";
}
/**
* Map a column ID to the value in that cell for node instance data.
*
* @param nodeData The node instance data.
* @param columnId The ID of the cell column.
*
* @return The value in the cell.
*/
private Object mapNodeInstanceData(OtherOccurrenceNodeInstanceData nodeData, TableColumns columnId) {
String value = Bundle.DataContentViewerOtherCasesTableModel_noData();
switch (colId) {
switch (columnId) {
case CASE_NAME:
if (null != nodeData.getCaseName()) {
value = nodeData.getCaseName();
@ -147,7 +162,7 @@ public class DataContentViewerOtherCasesTableModel extends AbstractTableModel {
case FILE_PATH:
value = nodeData.getFilePath();
break;
case TYPE:
case ATTRIBUTE:
value = nodeData.getType();
break;
case VALUE:
@ -159,10 +174,16 @@ public class DataContentViewerOtherCasesTableModel extends AbstractTableModel {
case COMMENT:
value = nodeData.getComment();
break;
default: // This shouldn't occur! Use default "No data" value.
break;
}
return value;
}
Object getRow(int rowIdx) {
return nodeDataList.get(rowIdx);
}
@Override
public Class<String> getColumnClass(int colIdx) {
return String.class;
@ -178,6 +199,9 @@ public class DataContentViewerOtherCasesTableModel extends AbstractTableModel {
fireTableDataChanged();
}
/**
* Clear the node data table.
*/
void clearTable() {
nodeDataList.clear();
fireTableDataChanged();

View File

@ -1,5 +1,5 @@
/*
* Central Repository
* Autopsy Forensic Browser
*
* Copyright 2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
@ -18,216 +18,9 @@
*/
package org.sleuthkit.autopsy.centralrepository.contentviewer;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.datamodel.TskDataException;
/**
* Class for populating the Other Occurrences tab
* Marker interface for Other Occurrences nodes.
*/
class OtherOccurrenceNodeData {
interface OtherOccurrenceNodeData {
// For now hard code the string for the central repo files type, since
// getting it dynamically can fail.
private static final String FILE_TYPE_STR = "Files";
private final String caseName;
private String deviceID;
private String dataSourceName;
private final String filePath;
private final String typeStr;
private final CorrelationAttribute.Type type;
private final String value;
private TskData.FileKnown known;
private String comment;
private AbstractFile originalAbstractFile = null;
private CorrelationAttributeInstance originalCorrelationInstance = null;
/**
* Create a node from a central repo instance.
* @param instance The central repo instance
* @param type The type of the instance
* @param value The value of the instance
*/
OtherOccurrenceNodeData(CorrelationAttributeInstance instance, CorrelationAttribute.Type type, String value) {
caseName = instance.getCorrelationCase().getDisplayName();
deviceID = instance.getCorrelationDataSource().getDeviceID();
dataSourceName = instance.getCorrelationDataSource().getName();
filePath = instance.getFilePath();
this.typeStr = type.getDisplayName();
this.type = type;
this.value = value;
known = instance.getKnownStatus();
comment = instance.getComment();
originalCorrelationInstance = instance;
}
/**
* Create a node from an abstract file.
* @param newFile The abstract file
* @param autopsyCase The current case
* @throws EamDbException
*/
OtherOccurrenceNodeData(AbstractFile newFile, Case autopsyCase) throws EamDbException {
caseName = autopsyCase.getDisplayName();
try {
DataSource dataSource = autopsyCase.getSleuthkitCase().getDataSource(newFile.getDataSource().getId());
deviceID = dataSource.getDeviceId();
dataSourceName = dataSource.getName();
} catch (TskDataException | TskCoreException ex) {
throw new EamDbException("Error loading data source for abstract file ID " + newFile.getId(), ex);
}
filePath = newFile.getParentPath() + newFile.getName();
typeStr = FILE_TYPE_STR;
this.type = null;
value = newFile.getMd5Hash();
known = newFile.getKnown();
comment = "";
originalAbstractFile = newFile;
}
/**
* Check if this node is a "file" type
* @return true if it is a file type
*/
boolean isFileType() {
return FILE_TYPE_STR.equals(typeStr);
}
/**
* Update the known status for this node
* @param newKnownStatus The new known status
*/
void updateKnown(TskData.FileKnown newKnownStatus) {
known = newKnownStatus;
}
/**
* Update the comment for this node
* @param newComment The new comment
*/
void updateComment(String newComment) {
comment = newComment;
}
/**
* Check if this is a central repo node.
* @return true if this node was created from a central repo instance, false otherwise
*/
boolean isCentralRepoNode() {
return (originalCorrelationInstance != null);
}
/**
* Uses the saved instance plus type and value to make a new CorrelationAttribute.
* Should only be called if isCentralRepoNode() is true.
* @return the newly created CorrelationAttribute
*/
CorrelationAttribute createCorrelationAttribute() throws EamDbException {
if (! isCentralRepoNode() ) {
throw new EamDbException("Can not create CorrelationAttribute for non central repo node");
}
CorrelationAttribute attr = new CorrelationAttribute(type, value);
attr.addInstance(originalCorrelationInstance);
return attr;
}
/**
* Get the case name
* @return the case name
*/
String getCaseName() {
return caseName;
}
/**
* Get the device ID
* @return the device ID
*/
String getDeviceID() {
return deviceID;
}
/**
* Get the data source name
* @return the data source name
*/
String getDataSourceName() {
return dataSourceName;
}
/**
* Get the file path
* @return the file path
*/
String getFilePath() {
return filePath;
}
/**
* Get the type (as a string)
* @return the type
*/
String getType() {
return typeStr;
}
/**
* Get the value (MD5 hash for files)
* @return the value
*/
String getValue() {
return value;
}
/**
* Get the known status
* @return the known status
*/
TskData.FileKnown getKnown() {
return known;
}
/**
* Get the comment
* @return the comment
*/
String getComment() {
return comment;
}
/**
* Get the backing abstract file.
* Should only be called if isCentralRepoNode() is false
* @return the original abstract file
*/
AbstractFile getAbstractFile() throws EamDbException {
if (originalCorrelationInstance == null) {
throw new EamDbException("AbstractFile is null");
}
return originalAbstractFile;
}
/**
* Get the backing CorrelationAttributeInstance.
* Should only be called if isCentralRepoNode() is true
* @return the original CorrelationAttributeInstance
* @throws EamDbException
*/
CorrelationAttributeInstance getCorrelationAttributeInstance() throws EamDbException {
if (originalCorrelationInstance == null) {
throw new EamDbException("CorrelationAttributeInstance is null");
}
return originalCorrelationInstance;
}
}

View File

@ -0,0 +1,233 @@
/*
* Central Repository
*
* Copyright 2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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.centralrepository.contentviewer;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.datamodel.TskDataException;
/**
* Class for populating the Other Occurrences tab
*/
class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData {
// For now hard code the string for the central repo files type, since
// getting it dynamically can fail.
private static final String FILE_TYPE_STR = "Files";
private final String caseName;
private String deviceID;
private String dataSourceName;
private final String filePath;
private final String typeStr;
private final CorrelationAttribute.Type type;
private final String value;
private TskData.FileKnown known;
private String comment;
private AbstractFile originalAbstractFile = null;
private CorrelationAttributeInstance originalCorrelationInstance = null;
/**
* Create a node from a central repo instance.
* @param instance The central repo instance
* @param type The type of the instance
* @param value The value of the instance
*/
OtherOccurrenceNodeInstanceData(CorrelationAttributeInstance instance, CorrelationAttribute.Type type, String value) {
caseName = instance.getCorrelationCase().getDisplayName();
deviceID = instance.getCorrelationDataSource().getDeviceID();
dataSourceName = instance.getCorrelationDataSource().getName();
filePath = instance.getFilePath();
this.typeStr = type.getDisplayName();
this.type = type;
this.value = value;
known = instance.getKnownStatus();
comment = instance.getComment();
originalCorrelationInstance = instance;
}
/**
* Create a node from an abstract file.
* @param newFile The abstract file
* @param autopsyCase The current case
* @throws EamDbException
*/
OtherOccurrenceNodeInstanceData(AbstractFile newFile, Case autopsyCase) throws EamDbException {
caseName = autopsyCase.getDisplayName();
try {
DataSource dataSource = autopsyCase.getSleuthkitCase().getDataSource(newFile.getDataSource().getId());
deviceID = dataSource.getDeviceId();
dataSourceName = dataSource.getName();
} catch (TskDataException | TskCoreException ex) {
throw new EamDbException("Error loading data source for abstract file ID " + newFile.getId(), ex);
}
filePath = newFile.getParentPath() + newFile.getName();
typeStr = FILE_TYPE_STR;
this.type = null;
value = newFile.getMd5Hash();
known = newFile.getKnown();
comment = "";
originalAbstractFile = newFile;
}
/**
* Check if this node is a "file" type
* @return true if it is a file type
*/
boolean isFileType() {
return FILE_TYPE_STR.equals(typeStr);
}
/**
* Update the known status for this node
* @param newKnownStatus The new known status
*/
void updateKnown(TskData.FileKnown newKnownStatus) {
known = newKnownStatus;
}
/**
* Update the comment for this node
* @param newComment The new comment
*/
void updateComment(String newComment) {
comment = newComment;
}
/**
* Check if this is a central repo node.
* @return true if this node was created from a central repo instance, false otherwise
*/
boolean isCentralRepoNode() {
return (originalCorrelationInstance != null);
}
/**
* Uses the saved instance plus type and value to make a new CorrelationAttribute.
* Should only be called if isCentralRepoNode() is true.
* @return the newly created CorrelationAttribute
*/
CorrelationAttribute createCorrelationAttribute() throws EamDbException {
if (! isCentralRepoNode() ) {
throw new EamDbException("Can not create CorrelationAttribute for non central repo node");
}
CorrelationAttribute attr = new CorrelationAttribute(type, value);
attr.addInstance(originalCorrelationInstance);
return attr;
}
/**
* Get the case name
* @return the case name
*/
String getCaseName() {
return caseName;
}
/**
* Get the device ID
* @return the device ID
*/
String getDeviceID() {
return deviceID;
}
/**
* Get the data source name
* @return the data source name
*/
String getDataSourceName() {
return dataSourceName;
}
/**
* Get the file path
* @return the file path
*/
String getFilePath() {
return filePath;
}
/**
* Get the type (as a string)
* @return the type
*/
String getType() {
return typeStr;
}
/**
* Get the value (MD5 hash for files)
* @return the value
*/
String getValue() {
return value;
}
/**
* Get the known status
* @return the known status
*/
TskData.FileKnown getKnown() {
return known;
}
/**
* Get the comment
* @return the comment
*/
String getComment() {
return comment;
}
/**
* Get the backing abstract file.
* Should only be called if isCentralRepoNode() is false
* @return the original abstract file
*/
AbstractFile getAbstractFile() throws EamDbException {
if (originalCorrelationInstance == null) {
throw new EamDbException("AbstractFile is null");
}
return originalAbstractFile;
}
/**
* Get the backing CorrelationAttributeInstance.
* Should only be called if isCentralRepoNode() is true
* @return the original CorrelationAttributeInstance
* @throws EamDbException
*/
CorrelationAttributeInstance getCorrelationAttributeInstance() throws EamDbException {
if (originalCorrelationInstance == null) {
throw new EamDbException("CorrelationAttributeInstance is null");
}
return originalCorrelationInstance;
}
}

View File

@ -0,0 +1,34 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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.centralrepository.contentviewer;
/**
* Class for populating the Other Occurrences tab with a single message.
*/
final class OtherOccurrenceNodeMessageData implements OtherOccurrenceNodeData {
private final String displayMessage;
OtherOccurrenceNodeMessageData(String displayMessage) {
this.displayMessage = displayMessage;
}
String getDisplayMessage() {
return displayMessage;
}
}

View File

@ -235,21 +235,37 @@ public class EamArtifactUtil {
return null;
}
CorrelationAttribute correlationAttribute = null;
CorrelationAttribute correlationAttribute;
CorrelationAttribute.Type type;
CorrelationCase correlationCase;
CorrelationDataSource correlationDataSource;
String value;
String filePath;
try {
CorrelationAttribute.Type type = EamDb.getInstance().getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID);
CorrelationCase correlationCase = EamDb.getInstance().getCase(Case.getCurrentCaseThrows());
type = EamDb.getInstance().getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID);
correlationCase = EamDb.getInstance().getCase(Case.getCurrentCaseThrows());
if (null == correlationCase) {
correlationCase = EamDb.getInstance().newCase(Case.getCurrentCaseThrows());
}
CorrelationDataSource correlationDataSource = CorrelationDataSource.fromTSKDataSource(correlationCase, file.getDataSource());
String value = file.getMd5Hash();
String filePath = (file.getParentPath() + file.getName()).toLowerCase();
correlationAttribute = EamDb.getInstance().getCorrelationAttribute(type, correlationCase, correlationDataSource, value, filePath);
} catch (TskCoreException | EamDbException | NoCurrentCaseException ex) {
correlationDataSource = CorrelationDataSource.fromTSKDataSource(correlationCase, file.getDataSource());
value = file.getMd5Hash();
filePath = (file.getParentPath() + file.getName()).toLowerCase();
} catch (TskCoreException | EamDbException ex) {
logger.log(Level.SEVERE, "Error retrieving correlation attribute.", ex);
return null;
} catch (NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "Case is closed.", ex);
return null;
}
try {
correlationAttribute = EamDb.getInstance().getCorrelationAttribute(type, correlationCase, correlationDataSource, value, filePath);
} catch (EamDbException ex) {
logger.log(Level.WARNING, String.format(
"Correlation attribute could not be retrieved for '%s' (id=%d): %s",
content.getName(), content.getId(), ex.getMessage()));
return null;
}
return correlationAttribute;
@ -300,9 +316,12 @@ public class EamArtifactUtil {
af.getParentPath() + af.getName());
eamArtifact.addInstance(cei);
return eamArtifact;
} catch (TskCoreException | EamDbException | NoCurrentCaseException ex) {
} catch (TskCoreException | EamDbException ex) {
logger.log(Level.SEVERE, "Error making correlation attribute.", ex);
return null;
} catch (NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "Case is closed.", ex);
return null;
}
}

View File

@ -77,7 +77,7 @@ public class FileInstanceNode extends FileNode {
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), Bundle.CommonFilesSearchResultsViewerTable_filesColLbl(), NO_DESCR, this.getContent().getName()));
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), Bundle.CommonFilesSearchResultsViewerTable_pathColLbl(), NO_DESCR, this.getContent().getParentPath()));
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), NO_DESCR, getHashSetHitsForFile(this.getContent())));
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), Bundle.CommonFilesSearchResultsViewerTable_hashsetHitsColLbl(), NO_DESCR, getHashSetHitsCsvList(this.getContent())));
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), Bundle.CommonFilesSearchResultsViewerTable_dataSourceColLbl(), NO_DESCR, this.getDataSource()));
sheetSet.put(new NodeProperty<>(Bundle.CommonFilesSearchResultsViewerTable_mimeTypeColLbl(), Bundle.CommonFilesSearchResultsViewerTable_mimeTypeColLbl(), NO_DESCR, StringUtils.defaultString(this.getContent().getMIMEType())));

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2017-18 Basis Technology Corp.
* Copyright 2017-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -46,7 +46,7 @@ import org.sleuthkit.datamodel.TskCoreException;
final class RelationshipNode extends BlackboardArtifactNode {
private static final Logger logger = Logger.getLogger(RelationshipNode.class.getName());
RelationshipNode(BlackboardArtifact artifact) {
super(artifact);
final String stripEnd = StringUtils.stripEnd(artifact.getDisplayName(), "s");
@ -115,7 +115,7 @@ final class RelationshipNode extends BlackboardArtifactNode {
}
addTagProperty(sheetSet);
return sheet;
}

View File

@ -117,8 +117,8 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont
drp.open();
drpExplorerManager = drp.getExplorerManager();
drpExplorerManager.addPropertyChangeListener(evt ->
viewInNewWindowButton.setEnabled(drpExplorerManager.getSelectedNodes().length == 1));
drpExplorerManager.addPropertyChangeListener(evt
-> viewInNewWindowButton.setEnabled(drpExplorerManager.getSelectedNodes().length == 1));
}
/**

View File

@ -22,23 +22,17 @@ import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Cursor;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.stream.Collectors;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
@ -48,14 +42,11 @@ import org.openide.util.NbBundle;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.casemodule.services.FileManager;
import org.sleuthkit.autopsy.casemodule.services.Services;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.sqlitereader.SQLiteReader;
/**
* A file content viewer for SQLite database files.
@ -70,7 +61,7 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
private final SQLiteTableView selectedTableView = new SQLiteTableView();
private AbstractFile sqliteDbFile;
private File tmpDbFile;
private Connection connection;
private SQLiteReader sqliteReader;
private int numRows; // num of rows in the selected table
private int currPage = 0; // curr page of rows being displayed
@ -347,10 +338,10 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
numEntriesField.setText("");
// close DB connection to file
if (null != connection) {
if (null != sqliteReader) {
try {
connection.close();
connection = null;
sqliteReader.close();
sqliteReader = null;
} catch (SQLException ex) {
logger.log(Level.SEVERE, "Failed to close DB connection to file.", ex); //NON-NLS
}
@ -370,41 +361,15 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
"SQLiteViewer.errorMessage.failedToQueryDatabase=The database tables in the file could not be read.",
"SQLiteViewer.errorMessage.failedToinitJDBCDriver=The JDBC driver for SQLite could not be loaded.",
"# {0} - exception message", "SQLiteViewer.errorMessage.unexpectedError=An unexpected error occurred:\n{0).",})
private void processSQLiteFile() {
private void processSQLiteFile() {
tablesDropdownList.removeAllItems();
// Copy the file to temp folder
String tmpDBPathName;
try {
tmpDBPathName = Case.getCurrentCaseThrows().getTempDirectory() + File.separator + sqliteDbFile.getName();
} catch (NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "Current case has been closed", ex); //NON-NLS
MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_errorMessage_noCurrentCase());
return;
}
tmpDbFile = new File(tmpDBPathName);
if (! tmpDbFile.exists()) {
try {
ContentUtils.writeToFile(sqliteDbFile, tmpDbFile);
// Look for any meta files associated with this DB - WAL, SHM, etc.
findAndCopySQLiteMetaFile(sqliteDbFile, sqliteDbFile.getName() + "-wal");
findAndCopySQLiteMetaFile(sqliteDbFile, sqliteDbFile.getName() + "-shm");
} catch (IOException | NoCurrentCaseException | TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Failed to create temp copy of DB file '%s' (objId=%d)", sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS
MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_errorMessage_failedToExtractFile());
return;
}
}
try {
// Load the SQLite JDBC driver, if necessary.
Class.forName("org.sqlite.JDBC"); //NON-NLS
connection = DriverManager.getConnection("jdbc:sqlite:" + tmpDBPathName); //NON-NLS
Map<String, String> dbTablesMap = getTables();
String localDiskPath = Case.getCurrentCaseThrows().getTempDirectory() +
File.separator + sqliteDbFile.getName();
sqliteReader = new SQLiteReader(sqliteDbFile, localDiskPath);
Map<String, String> dbTablesMap = sqliteReader.getTableSchemas();
if (dbTablesMap.isEmpty()) {
tablesDropdownList.addItem(Bundle.SQLiteViewer_comboBox_noTableEntry());
tablesDropdownList.setEnabled(false);
@ -413,78 +378,36 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
tablesDropdownList.addItem(tableName);
});
}
} catch (NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "Current case has been closed", ex); //NON-NLS
MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_errorMessage_noCurrentCase());
} catch (IOException | TskCoreException ex) {
logger.log(Level.SEVERE, String.format(
"Failed to create temp copy of DB file '%s' (objId=%d)", //NON-NLS
sqliteDbFile.getName(), sqliteDbFile.getId()), ex);
MessageNotifyUtil.Message.error(
Bundle.SQLiteViewer_errorMessage_failedToExtractFile());
} catch (ClassNotFoundException ex) {
logger.log(Level.SEVERE, String.format("Failed to initialize JDBC SQLite '%s' (objId=%d)", sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS
MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_errorMessage_failedToinitJDBCDriver());
logger.log(Level.SEVERE, String.format(
"Failed to initialize JDBC SQLite '%s' (objId=%d)", //NON-NLS
sqliteDbFile.getName(), sqliteDbFile.getId()), ex);
MessageNotifyUtil.Message.error(
Bundle.SQLiteViewer_errorMessage_failedToinitJDBCDriver());
} catch (SQLException ex) {
logger.log(Level.SEVERE, String.format("Failed to get tables from DB file '%s' (objId=%d)", sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS
MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_errorMessage_failedToQueryDatabase());
logger.log(Level.SEVERE, String.format(
"Failed to get tables from DB file '%s' (objId=%d)", //NON-NLS
sqliteDbFile.getName(), sqliteDbFile.getId()), ex);
MessageNotifyUtil.Message.error(
Bundle.SQLiteViewer_errorMessage_failedToQueryDatabase());
}
}
/**
* Searches for a meta file associated with the give SQLite db If found,
* copies the file to the temp folder
*
* @param sqliteFile - SQLIte db file being processed
* @param metaFileName name of meta file to look for
*/
private void findAndCopySQLiteMetaFile(AbstractFile sqliteFile, String metaFileName) throws NoCurrentCaseException, TskCoreException, IOException {
Case openCase = Case.getCurrentCaseThrows();
SleuthkitCase sleuthkitCase = openCase.getSleuthkitCase();
Services services = new Services(sleuthkitCase);
FileManager fileManager = services.getFileManager();
List<AbstractFile> metaFiles = fileManager.findFiles(sqliteFile.getDataSource(), metaFileName, sqliteFile.getParent().getName());
if (metaFiles != null) {
for (AbstractFile metaFile : metaFiles) {
String tmpMetafilePathName = openCase.getTempDirectory() + File.separator + metaFile.getName();
File tmpMetafile = new File(tmpMetafilePathName);
ContentUtils.writeToFile(metaFile, tmpMetafile);
}
}
}
/**
* Gets the table names and schemas from the SQLite database file.
*
* @return A mapping of table names to SQL CREATE TABLE statements.
*/
private Map<String, String> getTables() throws SQLException {
Map<String, String> dbTablesMap = new TreeMap<>();
Statement statement = null;
ResultSet resultSet = null;
try {
statement = connection.createStatement();
resultSet = statement.executeQuery(
"SELECT name, sql FROM sqlite_master "
+ " WHERE type= 'table' "
+ " ORDER BY name;"); //NON-NLS
while (resultSet.next()) {
String tableName = resultSet.getString("name"); //NON-NLS
String tableSQL = resultSet.getString("sql"); //NON-NLS
dbTablesMap.put(tableName, tableSQL);
}
} finally {
if (null != resultSet) {
resultSet.close();
}
if (null != statement) {
statement.close();
}
}
return dbTablesMap;
}
@NbBundle.Messages({"# {0} - tableName",
"SQLiteViewer.selectTable.errorText=Error getting row count for table: {0}"
})
private void selectTable(String tableName) {
try (Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(
"SELECT count (*) as count FROM " + tableName)) { //NON-NLS{
numRows = resultSet.getInt("count");
try {
numRows = sqliteReader.getTableRowCount(tableName);
numEntriesField.setText(numRows + " entries");
currPage = 1;
@ -504,8 +427,11 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
}
} catch (SQLException ex) {
logger.log(Level.SEVERE, String.format("Failed to load table %s from DB file '%s' (objId=%d)", tableName, sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS
MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_selectTable_errorText(tableName));
logger.log(Level.SEVERE, String.format(
"Failed to load table %s from DB file '%s' (objId=%d)", tableName, //NON-NLS
sqliteDbFile.getName(), sqliteDbFile.getId()), ex);
MessageNotifyUtil.Message.error(
Bundle.SQLiteViewer_selectTable_errorText(tableName));
}
}
@ -513,109 +439,108 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
"SQLiteViewer.readTable.errorText=Error getting rows for table: {0}"})
private void readTable(String tableName, int startRow, int numRowsToRead) {
try (
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(
"SELECT * FROM " + tableName
+ " LIMIT " + Integer.toString(numRowsToRead)
+ " OFFSET " + Integer.toString(startRow - 1))) {
ArrayList<Map<String, Object>> rows = resultSetToArrayList(resultSet);
try {
List<Map<String, Object>> rows = sqliteReader.getRowsFromTable(
tableName, startRow, numRowsToRead);
if (Objects.nonNull(rows)) {
selectedTableView.setupTable(rows);
} else {
selectedTableView.setupTable(Collections.emptyList());
}
} catch (SQLException ex) {
logger.log(Level.SEVERE, String.format("Failed to read table %s from DB file '%s' (objId=%d)", tableName, sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS
MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_readTable_errorText(tableName));
logger.log(Level.SEVERE, String.format(
"Failed to read table %s from DB file '%s' (objId=%d)", tableName, //NON-NLS
sqliteDbFile.getName(), sqliteDbFile.getId()), ex);
MessageNotifyUtil.Message.error(
Bundle.SQLiteViewer_readTable_errorText(tableName));
}
}
@NbBundle.Messages("SQLiteViewer.BlobNotShown.message=BLOB Data not shown")
private ArrayList<Map<String, Object>> resultSetToArrayList(ResultSet rs) throws SQLException {
ResultSetMetaData metaData = rs.getMetaData();
int columns = metaData.getColumnCount();
ArrayList<Map<String, Object>> rowlist = new ArrayList<>();
while (rs.next()) {
Map<String, Object> row = new LinkedHashMap<>(columns);
for (int i = 1; i <= columns; ++i) {
if (rs.getObject(i) == null) {
row.put(metaData.getColumnName(i), "");
} else {
if (metaData.getColumnTypeName(i).compareToIgnoreCase("blob") == 0) {
row.put(metaData.getColumnName(i), Bundle.SQLiteViewer_BlobNotShown_message());
} else {
row.put(metaData.getColumnName(i), rs.getObject(i));
}
}
}
rowlist.add(row);
}
return rowlist;
}
@NbBundle.Messages({"SQLiteViewer.exportTableToCsv.write.errText=Failed to export table content to csv file.",
"SQLiteViewer.exportTableToCsv.FileName=File name: ",
"SQLiteViewer.exportTableToCsv.TableName=Table name: "
/**
* Converts a sqlite table into a CSV file.
*
* @param file
* @param tableName
* @param rowMap -- A list of rows in the table, where each row is represented as a column-value
* map.
* @throws FileNotFoundException
* @throws IOException
*/
@NbBundle.Messages({
"SQLiteViewer.exportTableToCsv.FileName=File name: ",
"SQLiteViewer.exportTableToCsv.TableName=Table name: "
})
public void exportTableToCSV(File file, String tableName,
List<Map<String, Object>> rowMap) throws FileNotFoundException, IOException{
File csvFile;
String fileName = file.getName();
if (FilenameUtils.getExtension(fileName).equalsIgnoreCase("csv")) {
csvFile = file;
} else {
csvFile = new File(file.toString() + ".csv");
}
try (FileOutputStream out = new FileOutputStream(csvFile, false)) {
out.write((Bundle.SQLiteViewer_exportTableToCsv_FileName() + csvFile.getName() + "\n").getBytes());
out.write((Bundle.SQLiteViewer_exportTableToCsv_TableName() + tableName + "\n").getBytes());
String header = createColumnHeader(rowMap.get(0)).concat("\n");
out.write(header.getBytes());
for (Map<String, Object> maps : rowMap) {
String row = maps.values()
.stream()
.map(Object::toString)
.collect(Collectors.joining(","))
.concat("\n");
out.write(row.getBytes());
}
}
}
@NbBundle.Messages({
"SQLiteViewer.exportTableToCsv.write.errText=Failed to export table content to csv file.",
})
private void exportTableToCsv(File file) {
String tableName = (String) this.tablesDropdownList.getSelectedItem();
try (
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("SELECT * FROM " + tableName)) {
List<Map<String, Object>> currentTableRows = resultSetToArrayList(resultSet);
try {
List<Map<String, Object>> currentTableRows =
sqliteReader.getRowsFromTable(tableName);
if (Objects.isNull(currentTableRows) || currentTableRows.isEmpty()) {
logger.log(Level.INFO, String.format("The table %s is empty. (objId=%d)", tableName, sqliteDbFile.getId())); //NON-NLS
logger.log(Level.INFO, String.format(
"The table %s is empty. (objId=%d)", tableName, //NON-NLS
sqliteDbFile.getId()));
} else {
File csvFile;
String fileName = file.getName();
if (FilenameUtils.getExtension(fileName).equalsIgnoreCase("csv")) {
csvFile = file;
} else {
csvFile = new File(file.toString() + ".csv");
}
try (FileOutputStream out = new FileOutputStream(csvFile, false)) {
out.write((Bundle.SQLiteViewer_exportTableToCsv_FileName() + csvFile.getName() + "\n").getBytes());
out.write((Bundle.SQLiteViewer_exportTableToCsv_TableName() + tableName + "\n").getBytes());
// Set up the column names
Map<String, Object> row = currentTableRows.get(0);
StringBuffer header = new StringBuffer();
for (Map.Entry<String, Object> col : row.entrySet()) {
String colName = col.getKey();
if (header.length() > 0) {
header.append(',').append(colName);
} else {
header.append(colName);
}
}
out.write(header.append('\n').toString().getBytes());
for (Map<String, Object> maps : currentTableRows) {
StringBuffer valueLine = new StringBuffer();
maps.values().forEach((value) -> {
if (valueLine.length() > 0) {
valueLine.append(',').append(value.toString());
} else {
valueLine.append(value.toString());
}
});
out.write(valueLine.append('\n').toString().getBytes());
}
}
exportTableToCSV(file, tableName, currentTableRows);
}
} catch (SQLException ex) {
logger.log(Level.SEVERE, String.format("Failed to read table %s from DB file '%s' (objId=%d)", tableName, sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS
MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_readTable_errorText(tableName));
logger.log(Level.SEVERE, String.format(
"Failed to read table %s from DB file '%s' (objId=%d)", //NON-NLS
tableName, sqliteDbFile.getName(), sqliteDbFile.getId()), ex);
MessageNotifyUtil.Message.error(
Bundle.SQLiteViewer_readTable_errorText(tableName));
} catch (IOException ex) {
logger.log(Level.SEVERE, String.format("Failed to export table %s to file '%s'", tableName, file.getName()), ex); //NON-NLS
MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_exportTableToCsv_write_errText());
logger.log(Level.SEVERE, String.format(
"Failed to export table %s to file '%s'", tableName, file.getName()), ex); //NON-NLS
MessageNotifyUtil.Message.error(
Bundle.SQLiteViewer_exportTableToCsv_write_errText());
}
}
/**
* Returns a comma seperated header string from the keys of the column
* row map.
*
* @param row -- column header row map
* @return -- comma seperated header string
*/
private String createColumnHeader(Map<String, Object> row) {
return row.entrySet()
.stream()
.map(Map.Entry::getKey)
.collect(Collectors.joining(","));
}
}

View File

@ -70,7 +70,8 @@ public final class UserPreferences {
private static final String MAX_NUM_OF_LOG_FILE = "MaximumNumberOfLogFiles";
private static final int LOG_FILE_NUM_INT = 10;
public static final String GROUP_ITEMS_IN_TREE_BY_DATASOURCE = "GroupItemsInTreeByDataSource"; //NON-NLS
public static final String SHOW_ONLY_CURRENT_USER_TAGS = "ShowOnlyCurrentUserTags";
// Prevent instantiation.
private UserPreferences() {
}
@ -196,6 +197,27 @@ public final class UserPreferences {
preferences.putBoolean(GROUP_ITEMS_IN_TREE_BY_DATASOURCE, value);
}
/**
* Get the user preference which identifies whether tags should be shown for
* only the current user or all users.
*
* @return true for just the current user, false for all users
*/
public static boolean showOnlyCurrentUserTags() {
return preferences.getBoolean(SHOW_ONLY_CURRENT_USER_TAGS, false);
}
/**
* Set the user preference which identifies whether tags should be shown for
* only the current user or all users.
*
* @param value - true for just the current user, false for all users
*/
public static void setShowOnlyCurrentUserTags(boolean value) {
preferences.putBoolean(SHOW_ONLY_CURRENT_USER_TAGS, value);
}
/**
* Reads persisted case database connection info.
*
@ -379,21 +401,25 @@ public final class UserPreferences {
/**
* get the maximum number of log files to save
*
* @return Number of log files
*/
public static int getLogFileCount() {
return preferences.getInt(MAX_NUM_OF_LOG_FILE, LOG_FILE_NUM_INT);
}
/**
* get the default number of log files to save
*
* @return LOG_FILE_COUNT
*/
public static int getDefaultLogFileCount() {
return LOG_FILE_NUM_INT;
}
/**
* Set the maximum number of log files to save
*
* @param count number of log files
*/
public static void setLogFileCount(int count) {

View File

@ -26,7 +26,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
import javax.swing.JTabbedPane;
import javax.swing.SwingWorker;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.openide.explorer.ExplorerManager;
@ -579,29 +579,6 @@ public class DataResultPanel extends javax.swing.JPanel implements DataResult, C
}
}
/**
* Worker for RootNodeListener childrenAdded.
*/
class SetupTabsChildrenWorker extends SwingWorker<Void, Void> {
private final Node childNode;
SetupTabsChildrenWorker(Node aChildNode) {
childNode = aChildNode;
}
@Override
protected Void doInBackground() throws Exception {
setupTabs(childNode);
return null;
}
@Override
protected void done() {
setupTabs(childNode);
}
}
/**
* Responds to changes in the root node due to asynchronous child node
* creation.
@ -628,8 +605,13 @@ public class DataResultPanel extends javax.swing.JPanel implements DataResult, C
*/
if (waitingForData && containsReal(delta)) {
waitingForData = false;
Node childNode = nme.getNode();
new SetupTabsChildrenWorker(childNode).execute();
if (SwingUtilities.isEventDispatchThread()) {
setupTabs(nme.getNode());
} else {
SwingUtilities.invokeLater(() -> {
setupTabs(nme.getNode());
});
}
}
}

View File

@ -141,7 +141,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
* Configure the child OutlineView (explorer view) component.
*/
outlineView.setAllowedDragActions(DnDConstants.ACTION_NONE);
outline = outlineView.getOutline();
outline.setRowSelectionAllowed(true);
outline.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
@ -208,6 +208,11 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
@Override
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
public void setNode(Node rootNode) {
if (! SwingUtilities.isEventDispatchThread()) {
LOGGER.log(Level.SEVERE, "Attempting to run setNode() from non-EDT thread");
return;
}
/*
* The quick filter must be reset because when determining column width,
* ETable.getRowCount is called, and the documentation states that quick
@ -281,7 +286,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
* let the table resize itself.
*/
outline.setAutoResizeMode((props.isEmpty()) ? JTable.AUTO_RESIZE_ALL_COLUMNS : JTable.AUTO_RESIZE_OFF);
assignColumns(props); // assign columns to match the properties
if (firstProp != null) {
((DefaultOutlineModel) outline.getOutlineModel()).setNodesColumnLabel(firstProp.getDisplayName());

View File

@ -169,7 +169,7 @@ public class FileUtil {
public static String escapeFileName(String fileName) {
//for now escaping /:"*?<>| (not valid in file name, at least on Windows)
//with underscores. We are only keeping \ as it could be part of the path.
return fileName.replaceAll("[/:\"*?<>|]+", "_");
return fileName.replaceAll("[\\p{Cntrl}/:\"*?<>|]+", "_");
}
/**

View File

@ -21,14 +21,12 @@ package org.sleuthkit.autopsy.datamodel;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.stream.Collectors;
import javax.swing.Action;
import org.apache.commons.lang3.StringUtils;
import org.openide.nodes.Children;
import org.openide.nodes.Sheet;
@ -38,9 +36,6 @@ import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent;
import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent;
import org.sleuthkit.autopsy.centralrepository.AddEditCentralRepoCommentAction;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamArtifactUtil;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbUtil;
import org.sleuthkit.autopsy.coreutils.Logger;
import static org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode.AbstractFilePropertyType.*;
import static org.sleuthkit.autopsy.datamodel.Bundle.*;
@ -251,7 +246,7 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
map.put(TYPE_DIR.toString(), content.getDirType().getLabel());
map.put(TYPE_META.toString(), content.getMetaType().toString());
map.put(KNOWN.toString(), content.getKnown().getName());
map.put(HASHSETS.toString(), getHashSetHitsForFile(content));
map.put(HASHSETS.toString(), getHashSetHitsCsvList(content));
map.put(MD5HASH.toString(), StringUtils.defaultString(content.getMd5Hash()));
map.put(ObjectID.toString(), content.getId());
map.put(MIMETYPE.toString(), StringUtils.defaultString(content.getMIMEType()));
@ -263,7 +258,7 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
* to their sheets.
*
* @param sheetSet the modifiable Sheet.Set returned by
* Sheet.get(Sheet.PROPERTIES)
* Sheet.get(Sheet.PROPERTIES)
*/
@NbBundle.Messages("AbstractAbstractFileNode.tagsProperty.displayName=Tags")
protected void addTagProperty(Sheet.Set sheetSet) {
@ -301,7 +296,15 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
}
}
public static String getHashSetHitsForFile(AbstractFile file) {
/**
* Gets a comma-separated values list of the names of the hash sets
* currently identified as including a given file.
*
* @param file The file.
*
* @return The CSV list of hash set names.
*/
protected static String getHashSetHitsCsvList(AbstractFile file) {
try {
return StringUtils.join(file.getHashSetNames(), ", ");
} catch (TskCoreException tskCoreException) {

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011 Basis Technology Corp.
* Copyright 2011-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -29,6 +29,7 @@ import org.sleuthkit.datamodel.AbstractFile;
* Abstract class that implements the commonality between File and Directory
* Nodes (same properties).
*
* @param <T> extends AbstractFile
*/
public abstract class AbstractFsContentNode<T extends AbstractFile> extends AbstractAbstractFileNode<T> {

View File

@ -41,9 +41,9 @@ import org.sleuthkit.datamodel.TskCoreException;
* Child factory to create the top level children of the autopsy tree
*
*/
public class AutopsyTreeChildrenFactory extends ChildFactory.Detachable<Object> {
public final class AutopsyTreeChildFactory extends ChildFactory.Detachable<Object> {
private static final Logger logger = Logger.getLogger(AutopsyTreeChildrenFactory.class.getName());
private static final Logger logger = Logger.getLogger(AutopsyTreeChildFactory.class.getName());
/**
* Listener for handling DATA_SOURCE_ADDED events.

View File

@ -48,8 +48,6 @@ 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.centralrepository.AddEditCentralRepoCommentAction;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbUtil;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import static org.sleuthkit.autopsy.datamodel.DisplayableItemNode.findLinked;
@ -456,10 +454,10 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
dataSourceStr));
}
}
// If EXIF, add props for file size and path
if (artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF.getTypeID()) {
long size = 0;
String path = ""; //NON-NLS
if (associated instanceof AbstractFile) {

View File

@ -27,6 +27,7 @@ import javax.swing.Action;
import org.openide.nodes.Children;
import org.openide.nodes.Sheet;
import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
@ -59,6 +60,7 @@ public class BlackboardArtifactTagNode extends DisplayableItemNode {
this.tag = tag;
}
@Messages({"BlackboardArtifactTagNode.createSheet.userName.text=User Name"})
@Override
protected Sheet createSheet() {
Sheet propertySheet = super.createSheet();
@ -80,6 +82,7 @@ public class BlackboardArtifactTagNode extends DisplayableItemNode {
Logger.getLogger(ContentTagNode.class.getName()).log(Level.SEVERE, "Failed to get path for content (id = " + tag.getContent().getId() + ")", ex); //NON-NLS
contentPath = NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.unavail.text");
}
properties.put(new NodeProperty<>(
NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.srcFilePath.text"),
NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.srcFilePath.text"),
@ -95,7 +98,11 @@ public class BlackboardArtifactTagNode extends DisplayableItemNode {
NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.comment.text"),
"",
tag.getComment()));
properties.put(new NodeProperty<>(
NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.userName.text"),
NbBundle.getMessage(this.getClass(), "BlackboardArtifactTagNode.createSheet.userName.text"),
"",
tag.getUserName()));
return propertySheet;
}
@ -132,7 +139,7 @@ public class BlackboardArtifactTagNode extends DisplayableItemNode {
}
actions.add(new ViewTaggedArtifactAction(BlackboardArtifactTagNode_viewSourceArtifact_text(), artifact));
actions.addAll(DataModelActionsFactory.getActions(tag, true));
return actions.toArray(new Action[0]);
}

View File

@ -59,8 +59,8 @@ class ContentTagNode extends DisplayableItemNode {
@Messages({
"ContentTagNode.createSheet.artifactMD5.displayName=MD5 Hash",
"ContentTagNode.createSheet.artifactMD5.name=MD5 Hash"
})
"ContentTagNode.createSheet.artifactMD5.name=MD5 Hash",
"ContentTagNode.createSheet.userName.text=User Name"})
@Override
protected Sheet createSheet() {
Content content = tag.getContent();
@ -115,6 +115,11 @@ class ContentTagNode extends DisplayableItemNode {
Bundle.ContentTagNode_createSheet_artifactMD5_displayName(),
"",
file != null ? StringUtils.defaultString(file.getMd5Hash()) : ""));
properties.put(new NodeProperty<>(
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.userName.text"),
NbBundle.getMessage(this.getClass(), "ContentTagNode.createSheet.userName.text"),
"",
tag.getUserName()));
return propertySheet;
}
@ -123,13 +128,14 @@ class ContentTagNode extends DisplayableItemNode {
List<Action> actions = new ArrayList<>();
actions.addAll(Arrays.asList(super.getActions(context)));
AbstractFile file = getLookup().lookup(AbstractFile.class);
AbstractFile file = getLookup().lookup(AbstractFile.class
);
if (file != null) {
actions.add(ViewFileInTimelineAction.createViewFileAction(file));
}
actions.addAll(DataModelActionsFactory.getActions(tag, false));
return actions.toArray(new Action[actions.size()]);
}

View File

@ -1,15 +1,15 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2017 Basis Technology Corp.
*
* Copyright 2011-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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.
@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.datamodel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashMap;
@ -46,11 +47,11 @@ public class LayoutFileNode extends AbstractAbstractFileNode<LayoutFile> {
public static enum LayoutContentPropertyType {
PARTS {
@Override
public String toString() {
return NbBundle.getMessage(this.getClass(), "LayoutFileNode.propertyType.parts");
}
}
@Override
public String toString() {
return NbBundle.getMessage(this.getClass(), "LayoutFileNode.propertyType.parts");
}
}
}
public static String nameForLayoutFile(LayoutFile lf) {
@ -115,9 +116,7 @@ public class LayoutFileNode extends AbstractAbstractFileNode<LayoutFile> {
@Override
public Action[] getActions(boolean context) {
List<Action> actionsList = new ArrayList<>();
for (Action a : super.getActions(true)) {
actionsList.add(a);
}
actionsList.addAll(Arrays.asList(super.getActions(true)));
actionsList.add(new NewWindowViewAction(
NbBundle.getMessage(this.getClass(), "LayoutFileNode.getActions.viewInNewWin.text"), this));
actionsList.add(new ExternalViewerAction(
@ -126,19 +125,18 @@ public class LayoutFileNode extends AbstractAbstractFileNode<LayoutFile> {
actionsList.add(ExtractAction.getInstance());
actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance());
final Collection<AbstractFile> selectedFilesList =
new HashSet<>(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class));
if(selectedFilesList.size() == 1) {
final Collection<AbstractFile> selectedFilesList
= new HashSet<>(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class));
if (selectedFilesList.size() == 1) {
actionsList.add(DeleteFileContentTagAction.getInstance());
}
actionsList.addAll(ContextMenuExtensionPoint.getActions());
return actionsList.toArray(new Action[actionsList.size()]);
}
void fillPropertyMap(Map<String, Object> map) {
void fillPropertyMap(Map<String, Object> map) {
AbstractAbstractFileNode.fillPropertyMap(map, getContent());
map.put(LayoutContentPropertyType.PARTS.toString(), content.getNumParts());
}

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2017 Basis Technology Corp.
* Copyright 2011-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -40,7 +40,7 @@ public class LocalDirectoryNode extends SpecialDirectoryNode {
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/Folder-icon.png"); //NON-NLS
}
@Override
@NbBundle.Messages({
"LocalDirectoryNode.createSheet.name.name=Name",

View File

@ -96,7 +96,7 @@ public class LocalFileNode extends AbstractAbstractFileNode<AbstractFile> {
public Action[] getActions(boolean context) {
List<Action> actionsList = new ArrayList<>();
actionsList.addAll(Arrays.asList(super.getActions(true)));
actionsList.add(new ViewContextAction(NbBundle.getMessage(this.getClass(), "LocalFileNode.viewFileInDir.text"), this.content));
actionsList.add(null); // creates a menu separator
actionsList.add(new NewWindowViewAction(
@ -125,7 +125,7 @@ public class LocalFileNode extends AbstractAbstractFileNode<AbstractFile> {
logger.log(Level.WARNING, "Unable to add unzip with password action to context menus", ex);
}
}
return actionsList.toArray(new Action[0]);
return actionsList.toArray(new Action[actionsList.size()]);
}
@Override

View File

@ -1,15 +1,15 @@
/*
* Autopsy Forensic Browser
*
*
* Copyright 2011-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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.
@ -55,23 +55,33 @@ public class Tags implements AutopsyVisitableItem {
// override of Children.Keys<T>.createNodes().
private final TagResults tagResults = new TagResults();
private final String DISPLAY_NAME = NbBundle.getMessage(RootNode.class, "TagsNode.displayName.text");
private final static String DISPLAY_NAME = NbBundle.getMessage(RootNode.class, "TagsNode.displayName.text");
private static final String USER_NAME_PROPERTY = "user.name"; //NON-NLS
private final String ICON_PATH = "org/sleuthkit/autopsy/images/tag-folder-blue-icon-16.png"; //NON-NLS
private final long datasourceObjId;
Tags() {
this(0);
this(0);
}
Tags(long dsObjId) {
this.datasourceObjId = dsObjId;
}
/**
* Return the display name used by the tags node in the tree.
*
* @return - DISPLAY_NAME
*/
public static String getTagsDisplayName() {
return DISPLAY_NAME;
}
long filteringDataSourceObjId() {
return this.datasourceObjId;
}
@Override
public <T> T accept(AutopsyItemVisitor<T> visitor) {
return visitor.visit(this);
@ -98,13 +108,11 @@ public class Tags implements AutopsyVisitableItem {
*/
public class RootNode extends DisplayableItemNode {
public RootNode(long objId) {
super(Children.create(new TagNameNodeFactory(objId), true), Lookups.singleton(DISPLAY_NAME));
super.setName(DISPLAY_NAME);
super.setDisplayName(DISPLAY_NAME);
this.setIconBaseWithExtension(ICON_PATH);
}
@Override
@ -134,12 +142,20 @@ public class Tags implements AutopsyVisitableItem {
public String getItemType() {
return getClass().getName();
}
/**
* Cause the contents of the RootNode and its children to be updated.
*/
public void refresh() {
tagResults.update();
}
}
private class TagNameNodeFactory extends ChildFactory.Detachable<TagName> implements Observer {
private final long datasourceObjId;
private final Set<Case.Events> CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED,
Case.Events.BLACKBOARD_ARTIFACT_TAG_DELETED,
Case.Events.CONTENT_TAG_ADDED,
@ -197,13 +213,14 @@ public class Tags implements AutopsyVisitableItem {
/**
* Constructor
*
* @param objId data source object id
*/
TagNameNodeFactory(long objId) {
this.datasourceObjId = objId;
}
@Override
protected void addNotify() {
IngestManager.getInstance().addIngestJobEventListener(pcl);
@ -224,11 +241,17 @@ public class Tags implements AutopsyVisitableItem {
@Override
protected boolean createKeys(List<TagName> keys) {
try {
List<TagName> tagNamesInUse = UserPreferences.groupItemsInTreeByDatasource() ?
Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUse(datasourceObjId) :
Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUse()
;
List<TagName> tagNamesInUse;
if (UserPreferences.showOnlyCurrentUserTags()) {
String userName = System.getProperty(USER_NAME_PROPERTY);
tagNamesInUse = UserPreferences.groupItemsInTreeByDatasource()
? Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUseForUser(datasourceObjId, userName)
: Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUseForUser(userName);
} else {
tagNamesInUse = UserPreferences.groupItemsInTreeByDatasource()
? Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUse(datasourceObjId)
: Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUse();
}
Collections.sort(tagNamesInUse);
keys.addAll(tagNamesInUse);
} catch (TskCoreException | NoCurrentCaseException ex) {
@ -276,15 +299,24 @@ public class Tags implements AutopsyVisitableItem {
long tagsCount = 0;
try {
TagsManager tm = Case.getCurrentCaseThrows().getServices().getTagsManager();
if (UserPreferences.groupItemsInTreeByDatasource()) {
tagsCount = tm.getContentTagsCountByTagName(tagName, datasourceObjId);
tagsCount += tm.getBlackboardArtifactTagsCountByTagName(tagName, datasourceObjId);
if (UserPreferences.showOnlyCurrentUserTags()) {
String userName = System.getProperty(USER_NAME_PROPERTY);
if (UserPreferences.groupItemsInTreeByDatasource()) {
tagsCount = tm.getContentTagsCountByTagNameForUser(tagName, datasourceObjId, userName);
tagsCount += tm.getBlackboardArtifactTagsCountByTagNameForUser(tagName, datasourceObjId, userName);
} else {
tagsCount = tm.getContentTagsCountByTagNameForUser(tagName, userName);
tagsCount += tm.getBlackboardArtifactTagsCountByTagNameForUser(tagName, userName);
}
} else {
if (UserPreferences.groupItemsInTreeByDatasource()) {
tagsCount = tm.getContentTagsCountByTagName(tagName, datasourceObjId);
tagsCount += tm.getBlackboardArtifactTagsCountByTagName(tagName, datasourceObjId);
} else {
tagsCount = tm.getContentTagsCountByTagName(tagName);
tagsCount += tm.getBlackboardArtifactTagsCountByTagName(tagName);
}
}
else {
tagsCount = tm.getContentTagsCountByTagName(tagName);
tagsCount += tm.getBlackboardArtifactTagsCountByTagName(tagName);
}
} catch (TskCoreException | NoCurrentCaseException ex) {
Logger.getLogger(TagNameNode.class.getName()).log(Level.SEVERE, "Failed to get tags count for " + tagName.getDisplayName() + " tag name", ex); //NON-NLS
}
@ -387,9 +419,17 @@ public class Tags implements AutopsyVisitableItem {
private void updateDisplayName() {
long tagsCount = 0;
try {
tagsCount = UserPreferences.groupItemsInTreeByDatasource() ?
Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsCountByTagName(tagName, datasourceObjId) :
Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsCountByTagName(tagName);
if (UserPreferences.showOnlyCurrentUserTags()) {
String userName = System.getProperty(USER_NAME_PROPERTY);
tagsCount = UserPreferences.groupItemsInTreeByDatasource()
? Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsCountByTagNameForUser(tagName, datasourceObjId, userName)
: Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsCountByTagNameForUser(tagName, userName);
} else {
tagsCount = UserPreferences.groupItemsInTreeByDatasource()
? Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsCountByTagName(tagName, datasourceObjId)
: Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsCountByTagName(tagName);
}
} catch (TskCoreException | NoCurrentCaseException ex) {
Logger.getLogger(ContentTagTypeNode.class.getName()).log(Level.SEVERE, "Failed to get content tags count for " + tagName.getDisplayName() + " tag name", ex); //NON-NLS
}
@ -444,11 +484,19 @@ public class Tags implements AutopsyVisitableItem {
protected boolean createKeys(List<ContentTag> keys) {
// Use the content tags bearing the specified tag name as the keys.
try {
List<ContentTag> contentTags = UserPreferences.groupItemsInTreeByDatasource() ?
Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByTagName(tagName, datasourceObjId) :
Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByTagName(tagName);
keys.addAll(contentTags);
List<ContentTag> contentTags = UserPreferences.groupItemsInTreeByDatasource()
? Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByTagName(tagName, datasourceObjId)
: Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByTagName(tagName);
if (UserPreferences.showOnlyCurrentUserTags()) {
String userName = System.getProperty(USER_NAME_PROPERTY);
for (ContentTag tag : contentTags) {
if (userName.equals(tag.getUserName())) {
keys.add(tag);
}
}
} else {
keys.addAll(contentTags);
}
} catch (TskCoreException | NoCurrentCaseException ex) {
Logger.getLogger(ContentTagNodeFactory.class.getName()).log(Level.SEVERE, "Failed to get tag names", ex); //NON-NLS
}
@ -492,9 +540,16 @@ public class Tags implements AutopsyVisitableItem {
private void updateDisplayName() {
long tagsCount = 0;
try {
tagsCount = UserPreferences.groupItemsInTreeByDatasource() ?
Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagName(tagName, datasourceObjId) :
Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagName(tagName);
if (UserPreferences.showOnlyCurrentUserTags()) {
String userName = System.getProperty(USER_NAME_PROPERTY);
tagsCount = UserPreferences.groupItemsInTreeByDatasource()
? Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagNameForUser(tagName, datasourceObjId, userName)
: Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagNameForUser(tagName, userName);
} else {
tagsCount = UserPreferences.groupItemsInTreeByDatasource()
? Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagName(tagName, datasourceObjId)
: Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagName(tagName);
}
} catch (TskCoreException | NoCurrentCaseException ex) {
Logger.getLogger(BlackboardArtifactTagTypeNode.class.getName()).log(Level.SEVERE, "Failed to get blackboard artifact tags count for " + tagName.getDisplayName() + " tag name", ex); //NON-NLS
}
@ -549,10 +604,19 @@ public class Tags implements AutopsyVisitableItem {
protected boolean createKeys(List<BlackboardArtifactTag> keys) {
try {
// Use the blackboard artifact tags bearing the specified tag name as the keys.
List<BlackboardArtifactTag> artifactTags = UserPreferences.groupItemsInTreeByDatasource() ?
Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsByTagName(tagName, datasourceObjId) :
Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsByTagName(tagName);
keys.addAll(artifactTags);
List<BlackboardArtifactTag> artifactTags = UserPreferences.groupItemsInTreeByDatasource()
? Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsByTagName(tagName, datasourceObjId)
: Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsByTagName(tagName);
if (UserPreferences.showOnlyCurrentUserTags()) {
String userName = System.getProperty(USER_NAME_PROPERTY);
for (BlackboardArtifactTag tag : artifactTags) {
if (userName.equals(tag.getUserName())) {
keys.add(tag);
}
}
} else {
keys.addAll(artifactTags);
}
} catch (TskCoreException | NoCurrentCaseException ex) {
Logger.getLogger(BlackboardArtifactTagNodeFactory.class.getName()).log(Level.SEVERE, "Failed to get tag names", ex); //NON-NLS
}

View File

@ -50,8 +50,6 @@ public class VirtualDirectoryNode extends SpecialDirectoryNode {
this.setDisplayName(nameForVirtualDirectory(ld));
String name = ld.getName();
//set icon for name, special case for logical file set
if (ld.isDataSource()) {
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/fileset-icon-16.png"); //NON-NLS
@ -59,7 +57,7 @@ public class VirtualDirectoryNode extends SpecialDirectoryNode {
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/folder-icon-virtual.png"); //TODO NON-NLS
}
}
@Override
@NbBundle.Messages({"VirtualDirectoryNode.createSheet.size.name=Size (Bytes)",
"VirtualDirectoryNode.createSheet.size.displayName=Size (Bytes)",

View File

@ -124,4 +124,5 @@ GroupDataSourcesDialog.dataSourceCountLabel.text=jLabel1
GroupDataSourcesDialog.queryLabel.text=Would you like to group by data source for faster loading?
GroupDataSourcesDialog.yesButton.text=Yes
GroupDataSourcesDialog.noButton.text=No
GroupDataSourcesDialog.title=Group by Data Source?
GroupDataSourcesDialog.title=Group by Data Source?
DirectoryTreeTopComponent.showOnlyCurrentUserTagsCheckbox.text=Hide Other User's Tags

View File

@ -21,7 +21,9 @@
<Component id="backButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="forwardButton" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="51" max="32767" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="showOnlyCurrentUserTagsCheckbox" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="showRejectedCheckBox" min="-2" max="-2" attributes="0"/>
<Component id="groupByDatasourceCheckBox" min="-2" max="-2" attributes="0"/>
@ -36,7 +38,10 @@
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace min="-2" pref="5" max="-2" attributes="0"/>
<Component id="showRejectedCheckBox" min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="showRejectedCheckBox" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="showOnlyCurrentUserTagsCheckbox" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="groupByDatasourceCheckBox" min="-2" max="-2" attributes="0"/>
</Group>
@ -151,5 +156,15 @@
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="groupByDatasourceCheckBoxActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JCheckBox" name="showOnlyCurrentUserTagsCheckbox">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/directorytree/Bundle.properties" key="DirectoryTreeTopComponent.showOnlyCurrentUserTagsCheckbox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="showOnlyCurrentUserTagsCheckboxActionPerformed"/>
</Events>
</Component>
</SubComponents>
</Form>

View File

@ -80,7 +80,8 @@ import org.sleuthkit.autopsy.datamodel.FileTypesByMimeType;
import org.sleuthkit.autopsy.datamodel.InterestingHits;
import org.sleuthkit.autopsy.datamodel.KeywordHits;
import org.sleuthkit.autopsy.datamodel.ResultsNode;
import org.sleuthkit.autopsy.datamodel.AutopsyTreeChildrenFactory;
import org.sleuthkit.autopsy.datamodel.AutopsyTreeChildFactory;
import org.sleuthkit.autopsy.datamodel.Tags;
import org.sleuthkit.autopsy.datamodel.ViewsNode;
import org.sleuthkit.autopsy.datamodel.accounts.Accounts;
import org.sleuthkit.autopsy.datamodel.accounts.BINRange;
@ -108,7 +109,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
private final LinkedList<String[]> forwardList;
private static final String PREFERRED_ID = "DirectoryTreeTopComponent"; //NON-NLS
private static final Logger LOGGER = Logger.getLogger(DirectoryTreeTopComponent.class.getName());
private AutopsyTreeChildrenFactory autopsyTreeChildrenFactory;
private AutopsyTreeChildFactory autopsyTreeChildFactory;
private Children autopsyTreeChildren;
private static final long DEFAULT_DATASOURCE_GROUPING_THRESHOLD = 5; // Threshold for prompting the user about grouping by data source
private static final String GROUPING_THRESHOLD_NAME = "GroupDataSourceThreshold";
@ -137,6 +138,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
forwardButton.setEnabled(false);
groupByDatasourceCheckBox.setSelected(UserPreferences.groupItemsInTreeByDatasource());
showOnlyCurrentUserTagsCheckbox.setSelected(UserPreferences.showOnlyCurrentUserTags());
}
/**
@ -152,6 +154,9 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
case UserPreferences.GROUP_ITEMS_IN_TREE_BY_DATASOURCE:
refreshContentTreeSafe();
break;
case UserPreferences.SHOW_ONLY_CURRENT_USER_TAGS:
refreshTagsTree();
break;
case UserPreferences.HIDE_KNOWN_FILES_IN_VIEWS_TREE:
case UserPreferences.HIDE_SLACK_FILES_IN_VIEWS_TREE:
// TODO: Need a way to refresh the Views subtree
@ -191,6 +196,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
forwardButton = new javax.swing.JButton();
showRejectedCheckBox = new javax.swing.JCheckBox();
groupByDatasourceCheckBox = new javax.swing.JCheckBox();
showOnlyCurrentUserTagsCheckbox = new javax.swing.JCheckBox();
treeView.setBorder(null);
@ -235,6 +241,13 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
}
});
org.openide.awt.Mnemonics.setLocalizedText(showOnlyCurrentUserTagsCheckbox, org.openide.util.NbBundle.getMessage(DirectoryTreeTopComponent.class, "DirectoryTreeTopComponent.showOnlyCurrentUserTagsCheckbox.text")); // NOI18N
showOnlyCurrentUserTagsCheckbox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
showOnlyCurrentUserTagsCheckboxActionPerformed(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
@ -244,7 +257,9 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
.addComponent(backButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(forwardButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 51, Short.MAX_VALUE)
.addGap(18, 18, 18)
.addComponent(showOnlyCurrentUserTagsCheckbox, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(showRejectedCheckBox)
.addComponent(groupByDatasourceCheckBox))
@ -256,7 +271,9 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(5, 5, 5)
.addComponent(showRejectedCheckBox)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(showRejectedCheckBox)
.addComponent(showOnlyCurrentUserTagsCheckbox))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(groupByDatasourceCheckBox))
.addGroup(layout.createSequentialGroup()
@ -323,10 +340,15 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
UserPreferences.setGroupItemsInTreeByDatasource(this.groupByDatasourceCheckBox.isSelected());
}//GEN-LAST:event_groupByDatasourceCheckBoxActionPerformed
private void showOnlyCurrentUserTagsCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_showOnlyCurrentUserTagsCheckboxActionPerformed
UserPreferences.setShowOnlyCurrentUserTags(this.showOnlyCurrentUserTagsCheckbox.isSelected());
}//GEN-LAST:event_showOnlyCurrentUserTagsCheckboxActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton backButton;
private javax.swing.JButton forwardButton;
private javax.swing.JCheckBox groupByDatasourceCheckBox;
private javax.swing.JCheckBox showOnlyCurrentUserTagsCheckbox;
private javax.swing.JCheckBox showRejectedCheckBox;
private javax.swing.JScrollPane treeView;
// End of variables declaration//GEN-END:variables
@ -476,8 +498,8 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
}
// if there's at least one image, load the image and open the top componen
autopsyTreeChildrenFactory = new AutopsyTreeChildrenFactory();
autopsyTreeChildren = Children.create(autopsyTreeChildrenFactory, true);
autopsyTreeChildFactory = new AutopsyTreeChildFactory();
autopsyTreeChildren = Children.create(autopsyTreeChildFactory, true);
Node root = new AbstractNode(autopsyTreeChildren) {
//JIRA-2807: What is the point of these overrides?
/**
@ -735,9 +757,11 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
* responsible for opening core windows. Consider moving
* this elsewhere.
*/
if (!this.isOpened()) {
SwingUtilities.invokeLater(CoreComponentControl::openCoreWindows);
}
SwingUtilities.invokeLater(() -> {
if (! DirectoryTreeTopComponent.this.isOpened()) {
CoreComponentControl.openCoreWindows();
}
});
} catch (NoCurrentCaseException notUsed) {
/**
* Case is closed, do nothing.
@ -888,6 +912,29 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
SwingUtilities.invokeLater(this::rebuildTree);
}
/**
* Refresh only the tags subtree(s) of the tree view.
*/
private void refreshTagsTree() {
SwingUtilities.invokeLater(() -> {
// if no open case or has no data then there is no tree to rebuild
if (UserPreferences.groupItemsInTreeByDatasource()) {
for (Node dataSource : autopsyTreeChildren.getNodes()) {
Node tagsNode = dataSource.getChildren().findChild(Tags.getTagsDisplayName());
if (tagsNode != null) {
//Reports is at the same level as the data sources so we want to ignore it
((Tags.RootNode)tagsNode).refresh();
}
}
} else {
Node tagsNode = autopsyTreeChildren.findChild(Tags.getTagsDisplayName());
if (tagsNode != null) {
((Tags.RootNode)tagsNode).refresh();
}
}
});
}
/**
* Rebuilds the autopsy tree.
*
@ -907,7 +954,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
}
// refresh all children of the root.
autopsyTreeChildrenFactory.refreshChildren();
autopsyTreeChildFactory.refreshChildren();
// Select the first node and reset the selection history
// This should happen on the EDT once the tree has been rebuilt.

View File

@ -25,7 +25,9 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import javax.swing.AbstractAction;
import javax.swing.JFileChooser;
@ -33,7 +35,6 @@ import javax.swing.JOptionPane;
import javax.swing.SwingWorker;
import org.netbeans.api.progress.ProgressHandle;
import org.openide.util.Cancellable;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle;
import org.openide.util.Utilities;
import org.sleuthkit.autopsy.casemodule.Case;
@ -44,8 +45,6 @@ import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.autopsy.datamodel.ContentUtils.ExtractFscContentVisitor;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Extracts AbstractFiles to a location selected by the user.
@ -54,6 +53,8 @@ public final class ExtractAction extends AbstractAction {
private Logger logger = Logger.getLogger(ExtractAction.class.getName());
private String userDefinedExportPath;
// This class is a singleton to support multi-selection of nodes, since
// org.openide.nodes.NodeOp.findActions(Node[] nodes) will only pick up an Action if every
// node in the array returns a reference to the same action object from Node.getActions(boolean).
@ -66,6 +67,9 @@ public final class ExtractAction extends AbstractAction {
return instance;
}
/**
* Private constructor for the action.
*/
private ExtractAction() {
super(NbBundle.getMessage(ExtractAction.class, "ExtractAction.title.extractFiles.text"));
}
@ -94,77 +98,129 @@ public final class ExtractAction extends AbstractAction {
/**
* Called when user has selected a single file to extract
*
* @param e
* @param event
* @param selectedFile Selected file
*/
@NbBundle.Messages ({"ExtractAction.noOpenCase.errMsg=No open case available."})
private void extractFile(ActionEvent e, AbstractFile selectedFile) {
@NbBundle.Messages({"ExtractAction.noOpenCase.errMsg=No open case available."})
private void extractFile(ActionEvent event, AbstractFile selectedFile) {
Case openCase;
try {
openCase = Case.getCurrentCaseThrows();
} catch (NoCurrentCaseException ex) {
JOptionPane.showMessageDialog((Component) e.getSource(), Bundle.ExtractAction_noOpenCase_errMsg());
JOptionPane.showMessageDialog((Component) event.getSource(), Bundle.ExtractAction_noOpenCase_errMsg());
logger.log(Level.INFO, "Exception while getting open case.", ex); //NON-NLS
return;
}
JFileChooser fileChooser = new JFileChooser();
fileChooser.setCurrentDirectory(new File(openCase.getExportDirectory()));
fileChooser.setCurrentDirectory(new File(getExportDirectory(openCase)));
// If there is an attribute name, change the ":". Otherwise the extracted file will be hidden
fileChooser.setSelectedFile(new File(FileUtil.escapeFileName(selectedFile.getName())));
if (fileChooser.showSaveDialog((Component) e.getSource()) == JFileChooser.APPROVE_OPTION) {
if (fileChooser.showSaveDialog((Component) event.getSource()) == JFileChooser.APPROVE_OPTION) {
updateExportDirectory(fileChooser.getSelectedFile().getParent(), openCase);
ArrayList<FileExtractionTask> fileExtractionTasks = new ArrayList<>();
fileExtractionTasks.add(new FileExtractionTask(selectedFile, fileChooser.getSelectedFile()));
runExtractionTasks(e, fileExtractionTasks);
runExtractionTasks(event, fileExtractionTasks);
}
}
/**
* Called when a user has selected multiple files to extract
*
* @param e
* @param event
* @param selectedFiles Selected files
*/
private void extractFiles(ActionEvent e, Collection<? extends AbstractFile> selectedFiles) {
private void extractFiles(ActionEvent event, Collection<? extends AbstractFile> selectedFiles) {
Case openCase;
try {
openCase = Case.getCurrentCaseThrows();
} catch (NoCurrentCaseException ex) {
JOptionPane.showMessageDialog((Component) e.getSource(), Bundle.ExtractAction_noOpenCase_errMsg());
JOptionPane.showMessageDialog((Component) event.getSource(), Bundle.ExtractAction_noOpenCase_errMsg());
logger.log(Level.INFO, "Exception while getting open case.", ex); //NON-NLS
return;
}
JFileChooser folderChooser = new JFileChooser();
folderChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
folderChooser.setCurrentDirectory(new File(openCase.getExportDirectory()));
if (folderChooser.showSaveDialog((Component) e.getSource()) == JFileChooser.APPROVE_OPTION) {
folderChooser.setCurrentDirectory(new File(getExportDirectory(openCase)));
if (folderChooser.showSaveDialog((Component) event.getSource()) == JFileChooser.APPROVE_OPTION) {
File destinationFolder = folderChooser.getSelectedFile();
if (!destinationFolder.exists()) {
try {
destinationFolder.mkdirs();
} catch (Exception ex) {
JOptionPane.showMessageDialog((Component) e.getSource(), NbBundle.getMessage(this.getClass(),
JOptionPane.showMessageDialog((Component) event.getSource(), NbBundle.getMessage(this.getClass(),
"ExtractAction.extractFiles.cantCreateFolderErr.msg"));
logger.log(Level.INFO, "Unable to create folder(s) for user " + destinationFolder.getAbsolutePath(), ex); //NON-NLS
return;
}
}
updateExportDirectory(destinationFolder.getPath(), openCase);
/* get the unique set of files from the list. A user once reported extraction taking
* days because it was extracting the same PST file 20k times. They selected 20k
* email messages in the tree and chose to extract them. */
/*
* get the unique set of files from the list. A user once reported
* extraction taking days because it was extracting the same PST
* file 20k times. They selected 20k email messages in the tree and
* chose to extract them.
*/
Set<AbstractFile> uniqueFiles = new HashSet<>(selectedFiles);
// make a task for each file
ArrayList<FileExtractionTask> fileExtractionTasks = new ArrayList<>();
for (AbstractFile source : uniqueFiles) {
// If there is an attribute name, change the ":". Otherwise the extracted file will be hidden
fileExtractionTasks.add(new FileExtractionTask(source, new File(destinationFolder, source.getId() + "-" + FileUtil.escapeFileName(source.getName()))));
}
runExtractionTasks(e, fileExtractionTasks);
runExtractionTasks(event, fileExtractionTasks);
}
}
private void runExtractionTasks(ActionEvent e, ArrayList<FileExtractionTask> fileExtractionTasks) {
/**
* Get the export directory path.
*
* @param openCase The current case.
*
* @return The export directory path.
*/
private String getExportDirectory(Case openCase) {
String caseExportPath = openCase.getExportDirectory();
if (userDefinedExportPath == null) {
return caseExportPath;
}
File file = new File(userDefinedExportPath);
if (file.exists() == false || file.isDirectory() == false) {
return caseExportPath;
}
return userDefinedExportPath;
}
/**
* Update the default export directory. If the directory path matches the
* case export directory, then the directory used will always match the
* export directory of any given case. Otherwise, the path last used will be
* saved.
*
* @param exportPath The export path.
* @param openCase The current case.
*/
private void updateExportDirectory(String exportPath, Case openCase) {
if (exportPath.equalsIgnoreCase(openCase.getExportDirectory())) {
userDefinedExportPath = null;
} else {
userDefinedExportPath = exportPath;
}
}
/**
* Execute a series of file extraction tasks.
*
* @param event ActionEvent whose source will be used for
* centering popup dialogs.
* @param fileExtractionTasks List of file extraction tasks.
*/
private void runExtractionTasks(ActionEvent event, List<FileExtractionTask> fileExtractionTasks) {
// verify all of the sources and destinations are OK
for (Iterator<FileExtractionTask> it = fileExtractionTasks.iterator(); it.hasNext();) {
@ -177,16 +233,16 @@ public final class ExtractAction extends AbstractAction {
}
/*
* This code assumes that each destination is unique. We previously satisfied
* that by adding the unique ID.
* This code assumes that each destination is unique. We previously
* satisfied that by adding the unique ID.
*/
if (task.destination.exists()) {
if (JOptionPane.showConfirmDialog((Component) e.getSource(),
if (JOptionPane.showConfirmDialog((Component) event.getSource(),
NbBundle.getMessage(this.getClass(), "ExtractAction.confDlg.destFileExist.msg", task.destination.getAbsolutePath()),
NbBundle.getMessage(this.getClass(), "ExtractAction.confDlg.destFileExist.title"),
JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
if (!FileUtil.deleteFileDir(task.destination)) {
JOptionPane.showMessageDialog((Component) e.getSource(),
JOptionPane.showMessageDialog((Component) event.getSource(),
NbBundle.getMessage(this.getClass(), "ExtractAction.msgDlg.cantOverwriteFile.msg", task.destination.getAbsolutePath()));
it.remove();
}
@ -210,11 +266,20 @@ public final class ExtractAction extends AbstractAction {
}
}
/**
* Stores source and destination for file extraction.
*/
private class FileExtractionTask {
AbstractFile source;
File destination;
/**
* Create an instance of the FileExtractionTask.
*
* @param source The file to be extracted.
* @param destination The destination for the extraction.
*/
FileExtractionTask(AbstractFile source, File destination) {
this.source = source;
this.destination = destination;
@ -226,11 +291,16 @@ public final class ExtractAction extends AbstractAction {
*/
private class FileExtracter extends SwingWorker<Object, Void> {
private Logger logger = Logger.getLogger(FileExtracter.class.getName());
private final Logger logger = Logger.getLogger(FileExtracter.class.getName());
private ProgressHandle progress;
private ArrayList<FileExtractionTask> extractionTasks;
private final List<FileExtractionTask> extractionTasks;
FileExtracter(ArrayList<FileExtractionTask> extractionTasks) {
/**
* Create an instance of the FileExtracter.
*
* @param extractionTasks List of file extraction tasks.
*/
FileExtracter(List<FileExtractionTask> extractionTasks) {
this.extractionTasks = extractionTasks;
}
@ -275,7 +345,7 @@ public final class ExtractAction extends AbstractAction {
boolean msgDisplayed = false;
try {
super.get();
} catch (Exception ex) {
} catch (InterruptedException | ExecutionException ex) {
logger.log(Level.SEVERE, "Fatal error during file extraction", ex); //NON-NLS
MessageNotifyUtil.Message.info(
NbBundle.getMessage(this.getClass(), "ExtractAction.done.notifyMsg.extractErr", ex.getMessage()));
@ -289,22 +359,23 @@ public final class ExtractAction extends AbstractAction {
}
}
private int calculateProgressBarWorkUnits(AbstractFile file) {
int workUnits = 0;
if (file.isFile()) {
workUnits += file.getSize();
} else {
try {
for (Content child : file.getChildren()) {
if (child instanceof AbstractFile) {
workUnits += calculateProgressBarWorkUnits((AbstractFile) child);
}
}
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Could not get children of content", ex); //NON-NLS
}
}
return workUnits;
/**
* Calculate the number of work units for the progress bar.
*
* @param file File whose children will be reviewed to get the number of
* work units.
*
* @return The number of work units.
*/
/*
* private int calculateProgressBarWorkUnits(AbstractFile file) { int
* workUnits = 0; if (file.isFile()) { workUnits += file.getSize(); }
* else { try { for (Content child : file.getChildren()) { if (child
* instanceof AbstractFile) { workUnits +=
* calculateProgressBarWorkUnits((AbstractFile) child); } } } catch
* (TskCoreException ex) { logger.log(Level.SEVERE, "Could not get
* children of content", ex); //NON-NLS } } return workUnits;
}
*/
}
}

View File

@ -61,27 +61,42 @@ import org.sleuthkit.datamodel.VolumeSystem;
* Extracts all the unallocated space as a single file
*/
final class ExtractUnallocAction extends AbstractAction {
private static final Logger logger = Logger.getLogger(ExtractUnallocAction.class.getName());
private final List<OutputFileData> filesToExtract = new ArrayList<>();
private static final Set<String> volumesInProgress = new HashSet<>();
private static final Set<Long> imagesInProgress = new HashSet<>();
private static String userDefinedExportPath;
private long currentImage = 0L;
private final boolean isImage;
public ExtractUnallocAction(String title, Volume volume){
/**
* Create an instance of ExtractUnallocAction with a volume.
*
* @param title The title
* @param volume The volume set for extraction.
*/
public ExtractUnallocAction(String title, Volume volume) {
super(title);
isImage = false;
try {
OutputFileData outputFileData = new OutputFileData(volume);
filesToExtract.add(outputFileData);
} catch (NoCurrentCaseException ex) {
} catch (NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "Exception while getting open case.", ex);
setEnabled(false);
}
}
/**
* Create an instance of ExtractUnallocAction with an image.
*
* @param title The title.
* @param image The image set for extraction.
* @throws NoCurrentCaseException If no case is open.
*/
public ExtractUnallocAction(String title, Image image) throws NoCurrentCaseException {
super(title);
isImage = true;
@ -101,17 +116,17 @@ final class ExtractUnallocAction extends AbstractAction {
* Writes the unallocated files to
* $CaseDir/Export/ImgName-Unalloc-ImgObjectID-VolumeID.dat
*
* @param e
* @param event
*/
@NbBundle.Messages({"# {0} - fileName",
"ExtractUnallocAction.volumeInProgress=Already extracting unallocated space into {0} - will skip this volume",
"ExtractUnallocAction.volumeError=Error extracting unallocated space from volume",
"ExtractUnallocAction.noFiles=No unallocated files found on volume",
"ExtractUnallocAction.imageError=Error extracting unallocated space from image",
"ExtractUnallocAction.noOpenCase.errMsg=No open case available."})
"ExtractUnallocAction.volumeInProgress=Already extracting unallocated space into {0} - will skip this volume",
"ExtractUnallocAction.volumeError=Error extracting unallocated space from volume",
"ExtractUnallocAction.noFiles=No unallocated files found on volume",
"ExtractUnallocAction.imageError=Error extracting unallocated space from image",
"ExtractUnallocAction.noOpenCase.errMsg=No open case available."})
@Override
public void actionPerformed(ActionEvent e) {
if (filesToExtract != null && filesToExtract.size() > 0) {
public void actionPerformed(ActionEvent event) {
if (filesToExtract != null && filesToExtract.isEmpty() == false) {
// This check doesn't absolutely guarantee that the image won't be in progress when we make the worker,
// but in general it will suffice.
if (isImage && isImageInProgress(currentImage)) {
@ -145,20 +160,23 @@ final class ExtractUnallocAction extends AbstractAction {
}
};
fileChooser.setCurrentDirectory(new File(openCase.getExportDirectory()));
fileChooser.setCurrentDirectory(new File(getExportDirectory(openCase)));
fileChooser.setDialogTitle(
NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.dlgTitle.selectDirToSaveTo.msg"));
fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
fileChooser.setAcceptAllFileFilterUsed(false);
int returnValue = fileChooser.showSaveDialog((Component) e.getSource());
int returnValue = fileChooser.showSaveDialog((Component) event.getSource());
if (returnValue == JFileChooser.APPROVE_OPTION) {
String destination = fileChooser.getSelectedFile().getPath();
updateExportDirectory(destination, openCase);
for (OutputFileData outputFileData : filesToExtract) {
outputFileData.setPath(destination);
if (outputFileData.layoutFiles != null && outputFileData.layoutFiles.size() > 0 && (! isVolumeInProgress(outputFileData.getFileName()))) {
if (outputFileData.layoutFiles != null && outputFileData.layoutFiles.size() > 0 && (!isVolumeInProgress(outputFileData.getFileName()))) {
//Format for single Unalloc File is ImgName-Unalloc-ImgObjectID-VolumeID.dat
// Check if there is already a file with this name
if (outputFileData.fileInstance.exists()) {
int res = JOptionPane.showConfirmDialog(new Frame(), NbBundle.getMessage(this.getClass(),
@ -172,22 +190,22 @@ final class ExtractUnallocAction extends AbstractAction {
copyList.remove(outputFileData);
}
}
if (!isImage & !copyList.isEmpty()) {
try{
try {
ExtractUnallocWorker worker = new ExtractUnallocWorker(outputFileData);
worker.execute();
} catch (Exception ex){
logger.log(Level.WARNING, "Already extracting unallocated space into " + outputFileData.getFileName());
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Already extracting unallocated space into {0}", outputFileData.getFileName());
MessageNotifyUtil.Message.info(NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.volumeInProgress", outputFileData.getFileName()));
}
}
} else {
// The output file for this volume could not be created for one of the following reasons
if (outputFileData.layoutFiles == null){
if (outputFileData.layoutFiles == null) {
MessageNotifyUtil.Message.info(NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.volumeError"));
logger.log(Level.SEVERE, "Tried to get unallocated content but the list of unallocated files was null"); //NON-NLS
} else if (outputFileData.layoutFiles.isEmpty()){
} else if (outputFileData.layoutFiles.isEmpty()) {
MessageNotifyUtil.Message.info(NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.noFiles"));
logger.log(Level.WARNING, "No unallocated files found in volume"); //NON-NLS
copyList.remove(outputFileData);
@ -198,38 +216,76 @@ final class ExtractUnallocAction extends AbstractAction {
}
}
}
// This needs refactoring. The idea seems to be that we'll take advantage of the loop above to
// check whether each output file exists but wait until this point to make a worker
// to extract everything (the worker in the above loop doesn't get created because isImage is true)
// It's also unclear to me why we need the two separate worker types.
if (isImage && !copyList.isEmpty()) {
try{
try {
ExtractUnallocWorker worker = new ExtractUnallocWorker(copyList);
worker.execute();
} catch (Exception ex){
} catch (Exception ex) {
logger.log(Level.WARNING, "Error creating ExtractUnallocWorker", ex);
MessageNotifyUtil.Message.info(NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.imageError"));
}
}
}
}
}
/**
* Get the export directory path.
*
* @param openCase The current case.
*
* @return The export directory path.
*/
private String getExportDirectory(Case openCase) {
String caseExportPath = openCase.getExportDirectory();
if (userDefinedExportPath == null) {
return caseExportPath;
}
File file = new File(userDefinedExportPath);
if (file.exists() == false || file.isDirectory() == false) {
return caseExportPath;
}
return userDefinedExportPath;
}
/**
* Update the default export directory. If the directory path matches the
* case export directory, then the directory used will always match the
* export directory of any given case. Otherwise, the path last used will be
* saved.
*
* @param exportPath The export path.
* @param openCase The current case.
*/
private void updateExportDirectory(String exportPath, Case openCase) {
if (exportPath.equalsIgnoreCase(openCase.getExportDirectory())) {
userDefinedExportPath = null;
} else {
userDefinedExportPath = exportPath;
}
}
/**
* Gets all the unallocated files in a given Content.
*
* @param c Content to get Unallocated Files from
* @param content Content to get Unallocated Files from
*
* @return A list<LayoutFile> if it didn't crash List may be empty.
*/
private List<LayoutFile> getUnallocFiles(Content c) {
UnallocVisitor uv = new UnallocVisitor();
private List<LayoutFile> getUnallocFiles(Content content) {
UnallocVisitor unallocVisitor = new UnallocVisitor();
try {
for (Content contentChild : c.getChildren()) {
for (Content contentChild : content.getChildren()) {
if (contentChild instanceof AbstractContent) {
return contentChild.accept(uv); //call on first non-artifact child added to database
return contentChild.accept(unallocVisitor); //call on first non-artifact child added to database
}
}
} catch (TskCoreException tce) {
@ -237,38 +293,37 @@ final class ExtractUnallocAction extends AbstractAction {
}
return Collections.emptyList();
}
synchronized static private void addVolumeInProgress(String volumeOutputFileName) throws TskCoreException {
if(volumesInProgress.contains(volumeOutputFileName)){
if (volumesInProgress.contains(volumeOutputFileName)) {
throw new TskCoreException("Already writing unallocated space to " + volumeOutputFileName);
}
}
volumesInProgress.add(volumeOutputFileName);
}
synchronized static private void removeVolumeInProgress(String volumeOutputFileName){
synchronized static private void removeVolumeInProgress(String volumeOutputFileName) {
volumesInProgress.remove(volumeOutputFileName);
}
synchronized static private boolean isVolumeInProgress(String volumeOutputFileName){
synchronized static private boolean isVolumeInProgress(String volumeOutputFileName) {
return volumesInProgress.contains(volumeOutputFileName);
}
synchronized static private void addImageInProgress(Long id) throws TskCoreException {
if(imagesInProgress.contains(id)){
throw new TskCoreException("Image " + id + " is in use");
}
imagesInProgress.add(id);
synchronized static private void addImageInProgress(Long objId) throws TskCoreException {
if (imagesInProgress.contains(objId)) {
throw new TskCoreException("Image " + objId + " is in use");
}
imagesInProgress.add(objId);
}
synchronized static private void removeImageInProgress(Long id){
imagesInProgress.remove(id);
}
synchronized static private boolean isImageInProgress(Long id){
return imagesInProgress.contains(id);
synchronized static private void removeImageInProgress(Long objId) {
imagesInProgress.remove(objId);
}
synchronized static private boolean isImageInProgress(Long objId) {
return imagesInProgress.contains(objId);
}
/**
* Private class for dispatching the file IO in a background thread.
*/
@ -276,9 +331,9 @@ final class ExtractUnallocAction extends AbstractAction {
private ProgressHandle progress;
private boolean canceled = false;
private List<OutputFileData> outputFileDataList = new ArrayList<>();
private final List<OutputFileData> outputFileDataList = new ArrayList<>();
private File currentlyProcessing;
private int totalSizeinMegs;
private final int totalSizeinMegs;
long totalBytes = 0;
ExtractUnallocWorker(OutputFileData outputFileData) throws TskCoreException {
@ -291,32 +346,32 @@ final class ExtractUnallocAction extends AbstractAction {
ExtractUnallocWorker(List<OutputFileData> outputFileDataList) throws TskCoreException {
addImageInProgress(currentImage);
//Getting the total megs this worker is going to be doing
for (OutputFileData outputFileData : outputFileDataList) {
try{
try {
// If a volume is locked, skip it but continue trying to process any other requested volumes
addVolumeInProgress(outputFileData.getFileName());
totalBytes += outputFileData.getSizeInBytes();
this.outputFileDataList.add(outputFileData);
} catch (TskCoreException ex){
logger.log(Level.WARNING, "Already extracting data into " + outputFileData.getFileName());
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Already extracting data into {0}", outputFileData.getFileName());
}
}
// If we don't have anything to output (because of locking), throw an exception
if(this.outputFileDataList.isEmpty()){
if (this.outputFileDataList.isEmpty()) {
throw new TskCoreException("No unallocated files can be extracted");
}
totalSizeinMegs = toMb(totalBytes);
}
private int toMb(long bytes) {
if (bytes > 1024 && (bytes / 1024.0) <= Double.MAX_VALUE) {
double Mb = ((bytes / 1024.0) / 1024.0);//Bytes -> Megabytes
if (Mb <= Integer.MAX_VALUE) {
return (int) Math.ceil(Mb);
double megabytes = ((bytes / 1024.0) / 1024.0);//Bytes -> Megabytes
if (megabytes <= Integer.MAX_VALUE) {
return (int) Math.ceil(megabytes);
}
}
return 0;
@ -347,7 +402,7 @@ final class ExtractUnallocAction extends AbstractAction {
int mbs = 0; //Increments every 128th tick of kbs
for (OutputFileData outputFileData : this.outputFileDataList) {
currentlyProcessing = outputFileData.getFile();
logger.log(Level.INFO, "Writing Unalloc file to " + currentlyProcessing.getPath()); //NON-NLS
logger.log(Level.INFO, "Writing Unalloc file to {0}", currentlyProcessing.getPath()); //NON-NLS
OutputStream outputStream = new FileOutputStream(currentlyProcessing);
long bytes = 0;
int i = 0;
@ -374,9 +429,9 @@ final class ExtractUnallocAction extends AbstractAction {
if (canceled) {
outputFileData.getFile().delete();
logger.log(Level.INFO, "Canceled extraction of " + outputFileData.getFileName() + " and deleted file"); //NON-NLS
logger.log(Level.INFO, "Canceled extraction of {0} and deleted file", outputFileData.getFileName()); //NON-NLS
} else {
logger.log(Level.INFO, "Finished writing unalloc file " + outputFileData.getFile().getPath()); //NON-NLS
logger.log(Level.INFO, "Finished writing unalloc file {0}", outputFileData.getFile().getPath()); //NON-NLS
}
}
progress.finish();
@ -589,7 +644,7 @@ final class ExtractUnallocAction extends AbstractAction {
*/
private class OutputFileData {
private List<LayoutFile> layoutFiles;
private final List<LayoutFile> layoutFiles;
private final long sizeInBytes;
private long volumeId;
private long imageId;
@ -601,7 +656,7 @@ final class ExtractUnallocAction extends AbstractAction {
* Contingency constructor in event no VolumeSystem exists on an Image.
*
* @param img Image file to be analyzed
*
*
* @throws NoCurrentCaseException if there is no open case.
*/
OutputFileData(Image img) throws NoCurrentCaseException {
@ -619,7 +674,7 @@ final class ExtractUnallocAction extends AbstractAction {
* Default constructor for extracting info from Volumes.
*
* @param volume Volume file to be analyzed
*
*
* @throws NoCurrentCaseException if there is no open case.
*/
OutputFileData(Volume volume) throws NoCurrentCaseException {

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2015-2017 Basis Technology Corp.
* Copyright 2015-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -19,16 +19,19 @@
package org.sleuthkit.autopsy.guiutils;
import java.awt.Component;
import java.lang.reflect.InvocationTargetException;
import javax.swing.ImageIcon;
import javax.swing.JTable;
import static javax.swing.SwingConstants.CENTER;
import org.openide.nodes.Node;
import org.openide.util.ImageUtilities;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.datamodel.NodeProperty;
/**
* A JTable cell renderer that represents a status as a center-aligned icon, and
* grays out the cell if the table is disabled. The statuses represented are OK,
* WARNING, and ERROR.
* A JTable and outline view cell renderer that represents a status as a
* center-aligned icon, and grays out the cell if the table is disabled. The
* statuses represented are OK, WARNING, and ERROR.
*/
public class StatusIconCellRenderer extends GrayableCellRenderer {
@ -45,8 +48,20 @@ public class StatusIconCellRenderer extends GrayableCellRenderer {
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
setHorizontalAlignment(CENTER);
if ((value instanceof Status)) {
switch((Status) value) {
Object switchValue = null;
if ((value instanceof NodeProperty)) {
//The Outline view has properties in the cell, the value contained in the property is what we want
try {
switchValue = ((Node.Property) value).getValue();
} catch (IllegalAccessException | InvocationTargetException ex) {
//Unable to get the value from the NodeProperty no Icon will be displayed
}
} else {
//JTables contain the value we want directly in the cell
switchValue = value;
}
if ((switchValue instanceof Status)) {
switch ((Status) switchValue) {
case OK:
setIcon(OK_ICON);
setToolTipText(org.openide.util.NbBundle.getMessage(StatusIconCellRenderer.class, "StatusIconCellRenderer.tooltiptext.ok"));
@ -60,8 +75,7 @@ public class StatusIconCellRenderer extends GrayableCellRenderer {
setToolTipText(org.openide.util.NbBundle.getMessage(StatusIconCellRenderer.class, "StatusIconCellRenderer.tooltiptext.error"));
break;
}
}
else {
} else {
setIcon(null);
setText("");
}
@ -69,7 +83,7 @@ public class StatusIconCellRenderer extends GrayableCellRenderer {
return this;
}
public enum Status {
OK,
WARNING,

View File

@ -134,7 +134,7 @@ class ImageWriter implements PropertyChangeListener{
@Messages({
"# {0} - data source name",
"ImageWriter.progressBar.message=Finishing acquisition of {0}"
"ImageWriter.progressBar.message=Finishing acquisition of {0} (unplug device to cancel)"
})
private void startFinishImage(String dataSourceName){

View File

@ -25,22 +25,27 @@ import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import net.sf.sevenzipjbinding.ArchiveFormat;
import static net.sf.sevenzipjbinding.ArchiveFormat.RAR;
import net.sf.sevenzipjbinding.ExtractAskMode;
import net.sf.sevenzipjbinding.ISequentialOutStream;
import net.sf.sevenzipjbinding.ISevenZipInArchive;
import net.sf.sevenzipjbinding.SevenZip;
import net.sf.sevenzipjbinding.SevenZipException;
import net.sf.sevenzipjbinding.SevenZipNativeInitializationException;
import net.sf.sevenzipjbinding.simple.ISimpleInArchive;
import net.sf.sevenzipjbinding.simple.ISimpleInArchiveItem;
import net.sf.sevenzipjbinding.ExtractOperationResult;
import net.sf.sevenzipjbinding.IArchiveExtractCallback;
import net.sf.sevenzipjbinding.ICryptoGetTextPassword;
import net.sf.sevenzipjbinding.PropID;
import org.netbeans.api.progress.ProgressHandle;
import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
@ -158,8 +163,12 @@ class SevenZipExtractor {
*
* @param archiveFile the AbstractFile for the parent archive which
* which we are checking
* @param archiveFileItem the current item being extracted from the parent
* archive
* @param inArchive The SevenZip archive currently open for extraction
*
* @param inArchiveItemIndex Index of item inside the SevenZip archive. Each
* file inside an archive is associated with a unique
* integer
*
* @param depthMap a concurrent hashmap which keeps track of the
* depth of all nested archives, key of objectID
* @param escapedFilePath the path to the archiveFileItem which has been
@ -167,19 +176,22 @@ class SevenZipExtractor {
*
* @return true if potential zip bomb, false otherwise
*/
private boolean isZipBombArchiveItemCheck(AbstractFile archiveFile, ISimpleInArchiveItem archiveFileItem, ConcurrentHashMap<Long, Archive> depthMap, String escapedFilePath) {
private boolean isZipBombArchiveItemCheck(AbstractFile archiveFile, ISevenZipInArchive inArchive, int inArchiveItemIndex, ConcurrentHashMap<Long, Archive> depthMap, String escapedFilePath) {
try {
final Long archiveItemSize = archiveFileItem.getSize();
final Long archiveItemSize = (Long) inArchive.getProperty(
inArchiveItemIndex, PropID.SIZE);
//skip the check for small files
if (archiveItemSize == null || archiveItemSize < MIN_COMPRESSION_RATIO_SIZE) {
return false;
}
final Long archiveItemPackedSize = archiveFileItem.getPackedSize();
final Long archiveItemPackedSize = (Long) inArchive.getProperty(
inArchiveItemIndex, PropID.PACKED_SIZE);
if (archiveItemPackedSize == null || archiveItemPackedSize <= 0) {
logger.log(Level.WARNING, "Cannot getting compression ratio, cannot detect if zipbomb: {0}, item: {1}", new Object[]{archiveFile.getName(), archiveFileItem.getPath()}); //NON-NLS
logger.log(Level.WARNING, "Cannot getting compression ratio, cannot detect if zipbomb: {0}, item: {1}", //NON-NLS
new Object[]{archiveFile.getName(), (String) inArchive.getProperty(inArchiveItemIndex, PropID.PATH)}); //NON-NLS
return false;
}
@ -366,8 +378,9 @@ class SevenZipExtractor {
*
* @throws SevenZipException
*/
private String getPathInArchive(ISimpleInArchiveItem item, int itemNumber, AbstractFile archiveFile) throws SevenZipException {
String pathInArchive = item.getPath();
private String getPathInArchive(ISevenZipInArchive archive, int inArchiveItemIndex, AbstractFile archiveFile) throws SevenZipException {
String pathInArchive = (String) archive.getProperty(
inArchiveItemIndex, PropID.PATH);
if (pathInArchive == null || pathInArchive.isEmpty()) {
//some formats (.tar.gz) may not be handled correctly -- file in archive has no name/path
@ -400,7 +413,7 @@ class SevenZipExtractor {
}
}
if (useName == null) {
pathInArchive = "/" + archName + "/" + Integer.toString(itemNumber);
pathInArchive = "/" + archName + "/" + Integer.toString(inArchiveItemIndex);
} else {
pathInArchive = "/" + useName;
}
@ -428,69 +441,6 @@ class SevenZipExtractor {
return node == null ? null : archiveFilePath + "/" + node.getFileName();
}
/**
* Unpack an archive item to the disk using a password if specified.
*
* @param item - the archive item to unpack
* @param unpackedNode - the unpackedNode to add derivedInfo to
* @param password - the password for the archive, null if not
* used
* @param freeDiskSpace - the amount of free disk space
* @param uniqueExtractedName - the name of the file to extract the item to
*
* @return unpackedNode - the updated unpackedNode
*
* @throws SevenZipException
*/
private SevenZipExtractor.UnpackedTree.UnpackedNode unpackNode(ISimpleInArchiveItem item, SevenZipExtractor.UnpackedTree.UnpackedNode unpackedNode, String password, long freeDiskSpace, String uniqueExtractedName) throws SevenZipException {
//unpack locally if a file
final String localAbsPath = moduleDirAbsolute + File.separator + uniqueExtractedName;
final String localRelPath = moduleDirRelative + File.separator + uniqueExtractedName;
final Date createTime = item.getCreationTime();
final Date accessTime = item.getLastAccessTime();
final Date writeTime = item.getLastWriteTime();
final long createtime = createTime == null ? 0L : createTime.getTime() / 1000;
final long modtime = writeTime == null ? 0L : writeTime.getTime() / 1000;
final long accesstime = accessTime == null ? 0L : accessTime.getTime() / 1000;
SevenZipExtractor.UnpackStream unpackStream = null;
boolean isDir = item.isFolder();
if (!isDir) {
try {
// NOTE: item.getSize() may return null in case of certain
// archiving formats. Eg: BZ2
if (item.getSize() != null) {
unpackStream = new SevenZipExtractor.KnownSizeUnpackStream(localAbsPath, item.getSize());
} else {
unpackStream = new SevenZipExtractor.UnknownSizeUnpackStream(localAbsPath, freeDiskSpace);
}
ExtractOperationResult result;
if (password == null) {
result = item.extractSlow(unpackStream);
} else {
result = item.extractSlow(unpackStream, password);
}
if (result != ExtractOperationResult.OK) {
logger.log(Level.WARNING, "Extraction of : {0} encountered error {1}", new Object[]{localAbsPath, result}); //NON-NLS
return null;
}
} catch (SevenZipException e) {
//could be something unexpected with this file, move on
logger.log(Level.WARNING, "Could not extract file from archive: " + localAbsPath, e); //NON-NLS
} finally {
if (unpackStream != null) {
//record derived data in unode, to be traversed later after unpacking the archive
unpackedNode.addDerivedInfo(unpackStream.getSize(), !isDir,
0L, createtime, accesstime, modtime, localRelPath);
unpackStream.close();
}
}
} else { // this is a directory, size is always 0
unpackedNode.addDerivedInfo(0, !isDir, 0L, createtime, accesstime, modtime, localRelPath);
}
return unpackedNode;
}
/**
* Unpack the file to local folder and return a list of derived files
*
@ -523,7 +473,6 @@ class SevenZipExtractor {
boolean hasEncrypted = false;
boolean fullEncryption = true;
boolean progressStarted = false;
int processedItems = 0;
final String archiveFilePath = getArchiveFilePath(archiveFile);
final String escapedArchiveFilePath = FileUtil.escapeFileName(archiveFilePath);
HashMap<String, ZipFileStatusWrapper> statusMap = new HashMap<>();
@ -590,7 +539,7 @@ class SevenZipExtractor {
logger.log(Level.INFO, "Count of items in archive: {0}: {1}", new Object[]{escapedArchiveFilePath, numItems}); //NON-NLS
progress.start(numItems);
progressStarted = true;
final ISimpleInArchive simpleInArchive = inArchive.getSimpleInterface();
progress.progress(archiveFile.getName() + ": Analyzing archive metadata and creating local files");
//setup the archive local root folder
final String uniqueArchiveFileName = FileUtil.escapeFileName(EmbeddedFileExtractorIngestModule.getUniqueName(archiveFile));
@ -614,25 +563,18 @@ class SevenZipExtractor {
//currently getFreeDiskSpace always returns DISK_FREE_SPACE_UNKNOWN
freeDiskSpace = IngestMonitor.DISK_FREE_SPACE_UNKNOWN;
}
//unpack and process every item in archive
int itemNumber = 0;
for (ISimpleInArchiveItem item : simpleInArchive.getArchiveItems()) {
String pathInArchive = getPathInArchive(item, itemNumber, archiveFile);
//query for path in db
++itemNumber;
//check if possible zip bomb
if (isZipBombArchiveItemCheck(archiveFile, item, depthMap, escapedArchiveFilePath)) {
Map<Integer, InArchiveItemDetails> archiveDetailsMap = new LinkedHashMap<>();
for (int inArchiveItemIndex = 0; inArchiveItemIndex < numItems; inArchiveItemIndex++) {
if (isZipBombArchiveItemCheck(archiveFile, inArchive, inArchiveItemIndex, depthMap, escapedArchiveFilePath)) {
unpackSuccessful = false;
return unpackSuccessful;
}
SevenZipExtractor.UnpackedTree.UnpackedNode unpackedNode = unpackedTree.addNode(pathInArchive);
//update progress bar
progress.progress(archiveFile.getName() + ": " + item.getPath(), processedItems);
final boolean isEncrypted = item.isEncrypted();
String pathInArchive = getPathInArchive(inArchive, inArchiveItemIndex, archiveFile);
SevenZipExtractor.UnpackedTree.UnpackedNode unpackedNode = unpackedTree.addNode(pathInArchive);
final boolean isEncrypted = (Boolean) inArchive.getProperty(inArchiveItemIndex, PropID.ENCRYPTED);
if (isEncrypted && password == null) {
logger.log(Level.WARNING, "Skipping encrypted file in archive: {0}", pathInArchive); //NON-NLS
@ -642,20 +584,25 @@ class SevenZipExtractor {
} else {
fullEncryption = false;
}
// NOTE: item.getSize() may return null in case of certain
// NOTE: item size may return null in case of certain
// archiving formats. Eg: BZ2
//check if unpacking this file will result in out of disk space
//this is additional to zip bomb prevention mechanism
if (freeDiskSpace != IngestMonitor.DISK_FREE_SPACE_UNKNOWN && item.getSize() != null && item.getSize() > 0) { //if free space is known and file is not empty.
long newDiskSpace = freeDiskSpace - item.getSize();
Long archiveItemSize = (Long) inArchive.getProperty(
inArchiveItemIndex, PropID.SIZE);
if (freeDiskSpace != IngestMonitor.DISK_FREE_SPACE_UNKNOWN && archiveItemSize != null && archiveItemSize > 0) { //if free space is known and file is not empty.
String archiveItemPath = (String) inArchive.getProperty(
inArchiveItemIndex, PropID.PATH);
long newDiskSpace = freeDiskSpace - archiveItemSize;
if (newDiskSpace < MIN_FREE_DISK_SPACE) {
String msg = NbBundle.getMessage(SevenZipExtractor.class,
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.notEnoughDiskSpace.msg",
escapedArchiveFilePath, item.getPath());
escapedArchiveFilePath, archiveItemPath);
String details = NbBundle.getMessage(SevenZipExtractor.class,
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.unpack.notEnoughDiskSpace.details");
services.postMessage(IngestMessage.createErrorMessage(EmbeddedFileExtractorModuleFactory.getModuleName(), msg, details));
logger.log(Level.INFO, "Skipping archive item due to insufficient disk space: {0}, {1}", new String[]{escapedArchiveFilePath, item.getPath()}); //NON-NLS
logger.log(Level.INFO, "Skipping archive item due to insufficient disk space: {0}, {1}", new String[]{escapedArchiveFilePath, archiveItemPath}); //NON-NLS
logger.log(Level.INFO, "Available disk space: {0}", new Object[]{freeDiskSpace}); //NON-NLS
unpackSuccessful = false;
continue; //skip this file
@ -664,42 +611,61 @@ class SevenZipExtractor {
freeDiskSpace = newDiskSpace;
}
}
final String uniqueExtractedName = FileUtil.escapeFileName(uniqueArchiveFileName + File.separator + (item.getItemIndex() / 1000) + File.separator + item.getItemIndex() + "_" + new File(pathInArchive).getName());
final String uniqueExtractedName = FileUtil.escapeFileName(uniqueArchiveFileName + File.separator + (inArchiveItemIndex / 1000) + File.separator + inArchiveItemIndex + "_" + new File(pathInArchive).getName());
final String localAbsPath = moduleDirAbsolute + File.separator + uniqueExtractedName;
final String localRelPath = moduleDirRelative + File.separator + uniqueExtractedName;
//create local dirs and empty files before extracted
File localFile = new java.io.File(moduleDirAbsolute + File.separator + uniqueExtractedName);
File localFile = new java.io.File(localAbsPath);
//cannot rely on files in top-bottom order
if (!localFile.exists()) {
try {
if (item.isFolder()) {
if ((Boolean) inArchive.getProperty(
inArchiveItemIndex, PropID.IS_FOLDER)) {
localFile.mkdirs();
} else {
localFile.getParentFile().mkdirs();
try {
localFile.createNewFile();
} catch (IOException e) {
logger.log(Level.SEVERE, "Error creating extracted file: " + localFile.getAbsolutePath(), e); //NON-NLS
logger.log(Level.SEVERE, "Error creating extracted file: "//NON-NLS
+ localFile.getAbsolutePath(), e);
}
}
} catch (SecurityException e) {
logger.log(Level.SEVERE, "Error setting up output path for unpacked file: {0}", pathInArchive); //NON-NLS
logger.log(Level.SEVERE, "Error setting up output path for unpacked file: {0}", //NON-NLS
pathInArchive); //NON-NLS
//TODO consider bail out / msg to the user
}
}
// skip the rest of this loop if we couldn't create the file
//continue will skip details from being added to the map
if (localFile.exists() == false) {
continue;
}
//find this node in the hierarchy, create if neede;
unpackedNode = unpackNode(item, unpackedNode, password,
freeDiskSpace, uniqueExtractedName);
if (unpackedNode == null) {
unpackSuccessful = false;
}
//update units for progress bar
++processedItems;
//Store archiveItemIndex with local paths and unpackedNode reference.
//Necessary for the extract call back to write the current archive
//file to the correct disk location and to correctly update it's
//corresponding unpackedNode
archiveDetailsMap.put(inArchiveItemIndex, new InArchiveItemDetails(
unpackedNode, localAbsPath, localRelPath));
}
int[] extractionIndices = getExtractableFilesFromDetailsMap(archiveDetailsMap);
StandardIArchiveExtractCallback archiveCallBack
= new StandardIArchiveExtractCallback(
inArchive, archiveFile, progress,
archiveDetailsMap, password, freeDiskSpace);
//According to the documentation, indices in sorted order are optimal
//for efficiency. Hence, the LinkedHashMap and linear processing of
//inArchiveItemIndex. False indicates non-test mode
inArchive.extract(extractionIndices, false, archiveCallBack);
unpackSuccessful = unpackSuccessful & archiveCallBack.wasSuccessful();
// add them to the DB. We wait until the end so that we have the metadata on all of the
// intermediate nodes since the order is not guaranteed
try {
@ -799,6 +765,21 @@ class SevenZipExtractor {
return unpackSuccessful;
}
/**
* Produce a list of archive indices needed for the call to extract, which
* will open the archive and begin unpacking the files.
*/
private int[] getExtractableFilesFromDetailsMap(
Map<Integer, InArchiveItemDetails> archiveDetailsMap) {
Integer[] wrappedExtractionIndices = archiveDetailsMap.keySet()
.toArray(new Integer[archiveDetailsMap.size()]);
return Arrays.stream(wrappedExtractionIndices)
.mapToInt(Integer::intValue)
.toArray();
}
/**
* Stream used to unpack the archive to local file
*/
@ -933,6 +914,207 @@ class SevenZipExtractor {
}
}
/**
* Wrapper for necessary details used in StandardIArchiveExtractCallback
*/
private static class InArchiveItemDetails {
private final SevenZipExtractor.UnpackedTree.UnpackedNode unpackedNode;
private final String localAbsPath;
private final String localRelPath;
public InArchiveItemDetails(
SevenZipExtractor.UnpackedTree.UnpackedNode unpackedNode,
String localAbsPath, String localRelPath) {
this.unpackedNode = unpackedNode;
this.localAbsPath = localAbsPath;
this.localRelPath = localRelPath;
}
public SevenZipExtractor.UnpackedTree.UnpackedNode getUnpackedNode() {
return unpackedNode;
}
public String getLocalAbsPath() {
return localAbsPath;
}
public String getLocalRelPath() {
return localRelPath;
}
}
/**
* Call back class used by extract to expand archive files. This is the most
* efficient way to process according to the sevenzip binding documentation.
*/
private static class StandardIArchiveExtractCallback
implements IArchiveExtractCallback, ICryptoGetTextPassword {
private final AbstractFile archiveFile;
private final ISevenZipInArchive inArchive;
private SevenZipExtractor.UnpackStream unpackStream = null;
private final Map<Integer, InArchiveItemDetails> archiveDetailsMap;
private final ProgressHandle progressHandle;
private int inArchiveItemIndex;
private final long freeDiskSpace;
private long createTimeInSeconds;
private long modTimeInSeconds;
private long accessTimeInSeconds;
private boolean isFolder;
private final String password;
private boolean unpackSuccessful = true;
public StandardIArchiveExtractCallback(ISevenZipInArchive inArchive,
AbstractFile archiveFile, ProgressHandle progressHandle,
Map<Integer, InArchiveItemDetails> archiveDetailsMap,
String password, long freeDiskSpace) {
this.inArchive = inArchive;
this.freeDiskSpace = freeDiskSpace;
this.progressHandle = progressHandle;
this.archiveFile = archiveFile;
this.archiveDetailsMap = archiveDetailsMap;
this.password = password;
}
/**
* Get stream is called by the internal framework as it traverses
* the archive structure. The ISequentialOutStream is where the
* archive file contents will be expanded and written to the local disk.
*
* Skips folders, as there is nothing to extract.
*
* @param inArchiveItemIndex current location of the
* @param mode Will always be EXTRACT
* @return
* @throws SevenZipException
*/
@Override
public ISequentialOutStream getStream(int inArchiveItemIndex,
ExtractAskMode mode) throws SevenZipException {
this.inArchiveItemIndex = inArchiveItemIndex;
isFolder = (Boolean) inArchive
.getProperty(inArchiveItemIndex, PropID.IS_FOLDER);
if (isFolder || mode != ExtractAskMode.EXTRACT) {
return null;
}
final Long archiveItemSize = (Long) inArchive.getProperty(
inArchiveItemIndex, PropID.SIZE);
final String localAbsPath = archiveDetailsMap.get(
inArchiveItemIndex).getLocalAbsPath();
if (archiveItemSize != null) {
unpackStream = new SevenZipExtractor.KnownSizeUnpackStream(
localAbsPath, archiveItemSize);
} else {
unpackStream = new SevenZipExtractor.UnknownSizeUnpackStream(
localAbsPath, freeDiskSpace);
}
return unpackStream;
}
/**
* Retrieves the file metadata from the archive before extraction.
* Called after getStream.
*
* @param mode Will always be EXTRACT.
* @throws SevenZipException
*/
@Override
public void prepareOperation(ExtractAskMode mode) throws SevenZipException {
final Date createTime = (Date) inArchive.getProperty(
inArchiveItemIndex, PropID.CREATION_TIME);
final Date accessTime = (Date) inArchive.getProperty(
inArchiveItemIndex, PropID.LAST_ACCESS_TIME);
final Date writeTime = (Date) inArchive.getProperty(
inArchiveItemIndex, PropID.LAST_WRITE_TIME);
createTimeInSeconds = createTime == null ? 0L
: createTime.getTime() / 1000;
modTimeInSeconds = writeTime == null ? 0L
: writeTime.getTime() / 1000;
accessTimeInSeconds = accessTime == null ? 0L
: accessTime.getTime() / 1000;
}
/**
* Updates the unpackedNode data in the tree after the archive has been
* expanded to local disk.
*
* @param EOR - ExtractOperationResult
*
* @throws SevenZipException
*/
@Override
public void setOperationResult(ExtractOperationResult result) throws SevenZipException {
progressHandle.progress(archiveFile.getName() + ": "
+ (String) inArchive.getProperty(inArchiveItemIndex, PropID.PATH),
inArchiveItemIndex);
final SevenZipExtractor.UnpackedTree.UnpackedNode unpackedNode
= archiveDetailsMap.get(inArchiveItemIndex).getUnpackedNode();
final String localRelPath = archiveDetailsMap.get(
inArchiveItemIndex).getLocalRelPath();
if (isFolder) {
unpackedNode.addDerivedInfo(0,
!(Boolean) inArchive.getProperty(inArchiveItemIndex, PropID.IS_FOLDER),
0L, createTimeInSeconds, accessTimeInSeconds, modTimeInSeconds,
localRelPath);
return;
}
final String localAbsPath = archiveDetailsMap.get(
inArchiveItemIndex).getLocalAbsPath();
if (result != ExtractOperationResult.OK) {
logger.log(Level.WARNING, "Extraction of : {0} encountered error {1}", //NON-NLS
new Object[]{localAbsPath, result});
unpackSuccessful = false;
}
//record derived data in unode, to be traversed later after unpacking the archive
unpackedNode.addDerivedInfo(unpackStream.getSize(),
!(Boolean) inArchive.getProperty(inArchiveItemIndex, PropID.IS_FOLDER),
0L, createTimeInSeconds, accessTimeInSeconds, modTimeInSeconds, localRelPath);
unpackStream.close();
}
@Override
public void setTotal(long value) throws SevenZipException {
//Not necessary for extract, left intenionally blank
}
@Override
public void setCompleted(long value) throws SevenZipException {
//Not necessary for extract, left intenionally blank
}
/**
* Called when opening encrypted archive files.
*
* @return - Password supplied by user
*
* @throws SevenZipException
*/
@Override
public String cryptoGetTextPassword() throws SevenZipException {
return password;
}
public boolean wasSuccessful() {
return unpackSuccessful;
}
}
/**
* Representation of the files in the archive. Used to track of local tree
* file hierarchy, archive depth, and files created to easily and reliably
@ -1290,7 +1472,8 @@ class SevenZipExtractor {
}
/**
* Set the flag which identifies whether this file has been determined to be a zip bomb to true.
* Set the flag which identifies whether this file has been determined
* to be a zip bomb to true.
*/
synchronized void flagAsZipBomb() {
flaggedAsZipBomb = true;

View File

@ -26,20 +26,21 @@
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Component id="jScrollPane1" max="32767" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Component id="instructionLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
<Component id="jScrollPane1" max="32767" attributes="0"/>
</Group>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="AddValuesToHashDatabaseButton" alignment="1" min="-2" max="-2" attributes="0"/>
<Component id="cancelButton" alignment="1" min="-2" max="-2" attributes="0"/>
<Component id="pasteFromClipboardButton" alignment="1" min="-2" max="-2" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Component id="pasteFromClipboardButton" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="121" max="32767" attributes="0"/>
<Component id="okButton" min="-2" pref="84" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="cancelButton" min="-2" pref="84" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
@ -47,21 +48,18 @@
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="102" alignment="1" attributes="0">
<EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="instructionLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Component id="AddValuesToHashDatabaseButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="cancelButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Component id="pasteFromClipboardButton" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="jScrollPane1" pref="274" max="32767" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="jScrollPane1" pref="279" max="32767" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="pasteFromClipboardButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="okButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="cancelButton" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
@ -102,14 +100,14 @@
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="pasteFromClipboardButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="AddValuesToHashDatabaseButton">
<Component class="javax.swing.JButton" name="okButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/hashdatabase/Bundle.properties" key="AddHashValuesToDatabaseDialog.AddValuesToHashDatabaseButton.text_2" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/modules/hashdatabase/Bundle.properties" key="AddHashValuesToDatabaseDialog.okButton.text_2" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="AddValuesToHashDatabaseButtonActionPerformed"/>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="okButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="cancelButton">

View File

@ -73,7 +73,7 @@ public class AddHashValuesToDatabaseDialog extends javax.swing.JDialog {
} else {
setDefaultCloseOperation(0);
}
AddValuesToHashDatabaseButton.setEnabled(enable);
okButton.setEnabled(enable);
cancelButton.setEnabled(enable);
pasteFromClipboardButton.setEnabled(enable);
}
@ -91,7 +91,7 @@ public class AddHashValuesToDatabaseDialog extends javax.swing.JDialog {
jScrollPane1 = new javax.swing.JScrollPane();
hashValuesTextArea = new javax.swing.JTextArea();
pasteFromClipboardButton = new javax.swing.JButton();
AddValuesToHashDatabaseButton = new javax.swing.JButton();
okButton = new javax.swing.JButton();
cancelButton = new javax.swing.JButton();
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
@ -115,10 +115,10 @@ public class AddHashValuesToDatabaseDialog extends javax.swing.JDialog {
}
});
org.openide.awt.Mnemonics.setLocalizedText(AddValuesToHashDatabaseButton, org.openide.util.NbBundle.getMessage(AddHashValuesToDatabaseDialog.class, "AddHashValuesToDatabaseDialog.AddValuesToHashDatabaseButton.text_2")); // NOI18N
AddValuesToHashDatabaseButton.addActionListener(new java.awt.event.ActionListener() {
org.openide.awt.Mnemonics.setLocalizedText(okButton, org.openide.util.NbBundle.getMessage(AddHashValuesToDatabaseDialog.class, "AddHashValuesToDatabaseDialog.okButton.text_2")); // NOI18N
okButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
AddValuesToHashDatabaseButtonActionPerformed(evt);
okButtonActionPerformed(evt);
}
});
@ -136,31 +136,30 @@ public class AddHashValuesToDatabaseDialog extends javax.swing.JDialog {
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jScrollPane1)
.addGroup(layout.createSequentialGroup()
.addComponent(instructionLabel)
.addGap(0, 0, Short.MAX_VALUE))
.addComponent(jScrollPane1))
.addGap(18, 18, 18)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(AddValuesToHashDatabaseButton, javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(cancelButton, javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(pasteFromClipboardButton, javax.swing.GroupLayout.Alignment.TRAILING))
.addGroup(layout.createSequentialGroup()
.addComponent(pasteFromClipboardButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 121, Short.MAX_VALUE)
.addComponent(okButton, 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)))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap()
.addComponent(instructionLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(AddValuesToHashDatabaseButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(cancelButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(pasteFromClipboardButton))
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 274, Short.MAX_VALUE))
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 279, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(pasteFromClipboardButton)
.addComponent(okButton)
.addComponent(cancelButton))
.addContainerGap())
);
@ -213,17 +212,17 @@ public class AddHashValuesToDatabaseDialog extends javax.swing.JDialog {
}
}//GEN-LAST:event_hashValuesTextAreaMouseClicked
private void AddValuesToHashDatabaseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_AddValuesToHashDatabaseButtonActionPerformed
private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed
AddHashValuesToDatabaseProgressDialog progressDialog = new AddHashValuesToDatabaseProgressDialog(this, hashDb, hashValuesTextArea.getText());
progressDialog.addHashValuesToDatabase();
}//GEN-LAST:event_AddValuesToHashDatabaseButtonActionPerformed
}//GEN-LAST:event_okButtonActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton AddValuesToHashDatabaseButton;
private javax.swing.JButton cancelButton;
private javax.swing.JTextArea hashValuesTextArea;
private javax.swing.JLabel instructionLabel;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JButton okButton;
private javax.swing.JButton pasteFromClipboardButton;
// End of variables declaration//GEN-END:variables
}

View File

@ -87,8 +87,8 @@ HashDbImportDatabaseDialog.importHashDbErr=Import Hash Set Error
HashDbImportDatabaseDialog.mustSelectHashDbFilePathMsg=A hash set file path must be selected.
HashDbImportDatabaseDialog.hashDbDoesNotExistMsg=The selected hash set does not exist.
HashDbImportDatabaseDialog.errorMessage.failedToOpenHashDbMsg=Failed to open hash set at {0}.
HashDbIngestModule.moduleName=Hash Lookup
HashDbIngestModule.moduleDescription=Identifies known and notable files using supplied hash sets, such as a standard NSRL hash set.
HashLookupModuleFactory.moduleName.text=Hash Lookup
HashLookupModuleFactory.moduleDescription.text=Identifies known and notable files using supplied hash sets, such as a standard NSRL hash set.
HashDbIngestModule.fileReadErrorMsg=Read Error\: {0}
HashDbIngestModule.calcHashValueErr=Error encountered while calculating the hash value for {0}.
HashDbIngestModule.hashLookupErrorMsg=Hash Lookup Error\: {0}
@ -172,7 +172,6 @@ HashDbSearchPanel.hashTable.defaultModel.title.text=MD5 Hashes
AddHashValuesToDatabaseDialog.JDialog.Title=Add Hashes to Hash Set
AddHashValuesToDatabaseDialog.instructionLabel.text_1=Paste MD5 hash values (one per line) below:
AddHashValuesToDatabaseDialog.cancelButton.text_2=Cancel
AddHashValuesToDatabaseDialog.AddValuesToHashDatabaseButton.text_2=Add Hashes to Hash Set
AddHashValuesToDatabaseDialog.pasteFromClipboardButton.text_2=Paste From Clipboard
AddHashValuesToDatabaseProgressDialog.okButton.text=OK
AddHashValuesToDatabaseProgressDialog.statusLabel.text=status
@ -237,3 +236,4 @@ HashDbCreateDatabaseDialog.centralRepoRadioButton.text=Remote (Central Repositor
HashDbCreateDatabaseDialog.lbOrg.text=Source Organization:
HashDbCreateDatabaseDialog.orgButton.text=Manage Organizations
HashDbCreateDatabaseDialog.databasePathLabel.text=Hash Set Path:
AddHashValuesToDatabaseDialog.okButton.text_2=OK

View File

@ -76,8 +76,8 @@ HashDbImportDatabaseDialog.importHashDbErr=\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\
HashDbImportDatabaseDialog.mustSelectHashDbFilePathMsg=\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306e\u30d5\u30a1\u30a4\u30eb\u30d1\u30b9\u306e\u9078\u629e\u304c\u5fc5\u8981\u3067\u3059\u3002
HashDbImportDatabaseDialog.hashDbDoesNotExistMsg=\u9078\u629e\u3055\u308c\u305f\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306f\u5b58\u5728\u3057\u307e\u305b\u3093\u3002
HashDbImportDatabaseDialog.errorMessage.failedToOpenHashDbMsg={0}\u3067\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u958b\u304f\u306e\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002
HashDbIngestModule.moduleName=\u30cf\u30c3\u30b7\u30e5\u30eb\u30c3\u30af\u30a2\u30c3\u30d7
HashDbIngestModule.moduleDescription=\u6a19\u6e96\u306eNSRL\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306a\u3069\u3001\u63d0\u4f9b\u3055\u308c\u305f\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u5229\u7528\u3057\u3066\u3001\u65e2\u77e5\u307e\u305f\u306f\u7591\u308f\u3057\u3044\u3082\u306e\u3092\u691c\u77e5\u3057\u307e\u3059\u3002
HashLookupModuleFactory.moduleName.text=\u30cf\u30c3\u30b7\u30e5\u30eb\u30c3\u30af\u30a2\u30c3\u30d7
HashLookupModuleFactory.moduleDescription.text=\u6a19\u6e96\u306eNSRL\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306a\u3069\u3001\u63d0\u4f9b\u3055\u308c\u305f\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u5229\u7528\u3057\u3066\u3001\u65e2\u77e5\u307e\u305f\u306f\u7591\u308f\u3057\u3044\u3082\u306e\u3092\u691c\u77e5\u3057\u307e\u3059\u3002
HashDbIngestModule.noKnownHashDbSetMsg=\u65e2\u77e5\u306e\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304c\u5b58\u5728\u3057\u307e\u305b\u3093\u3002
HashDbIngestModule.knownFileSearchWillNotExecuteWarn=\u65e2\u77e5\u306e\u30d5\u30a1\u30a4\u30eb\u691c\u7d22\u304c\u5b9f\u884c\u3055\u308c\u307e\u305b\u3093\u3002
HashDbIngestModule.noKnownBadHashDbSetMsg=\u65e2\u77e5\u306e\u60aa\u8cea\u306a\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30bb\u30c3\u30c8\u306f\u3042\u308a\u307e\u305b\u3093\u3002
@ -86,7 +86,6 @@ HashDbIngestModule.knownBadFileSearchWillNotExecuteWarn=\u65e2\u77e5\u306e\u60aa
HashDbIngestModule.fileReadErrorMsg=\u8aad\u307f\u8fbc\u307f\u30a8\u30e9\u30fc\uff1a {0}
HashDbIngestModule.calcHashValueErr={0}\u306e\u30cf\u30c3\u30b7\u30e5\u5024\u3092\u8a08\u7b97\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002
HashDbIngestModule.hashLookupErrorMsg=\u30cf\u30c3\u30b7\u30e5\u30eb\u30c3\u30af\u30a2\u30c3\u30d7\u30a8\u30e9\u30fc\uff1a {0}
HashDbIngestModule.settingKnownBadStateErr={0}\u306e\u65e2\u77e5\u306e\u60aa\u8cea\u30b9\u30c6\u30fc\u30bf\u30b9\u3092\u8a2d\u5b9a\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002
HashDbIngestModule.lookingUpKnownBadHashValueErr={0}\u306e\u65e2\u77e5\u306e\u60aa\u8cea\u30cf\u30c3\u30b7\u30e5\u5024\u3092\u30eb\u30c3\u30af\u30a2\u30c3\u30d7\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002
HashDbIngestModule.lookingUpKnownHashValueErr={0}\u306e\u65e2\u77e5\u306e\u30cf\u30c3\u30b7\u30e5\u5024\u3092\u30eb\u30c3\u30af\u30a2\u30c3\u30d7\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002
HashDbIngestModule.postToBB.fileName=\u30d5\u30a1\u30a4\u30eb\u540d
@ -172,7 +171,6 @@ AddHashValuesToDatabaseDialog.JDialog.Title=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9
HashLookupSettingsPanel.addHashesToDatabaseButton.text=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306b\u30cf\u30c3\u30b7\u30e5\u3092\u8ffd\u52a0
AddHashValuesToDatabaseDialog.instructionLabel.text_1=\u4e0b\u8a18\u306bMD5\u306e\u30cf\u30c3\u30b7\u30e5\u5024\u3092\u8cbc\u308a\u4ed8\u3051\u308b\uff08\u30e9\u30a4\u30f3\u3054\u3068\u306b\u4e00\u3064\u305a\u3064\uff09\uff1a
AddHashValuesToDatabaseDialog.cancelButton.text_2=\u30ad\u30e3\u30f3\u30bb\u30eb
AddHashValuesToDatabaseDialog.AddValuesToHashDatabaseButton.text_2=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306b\u30cf\u30c3\u30b7\u30e5\u3092\u8ffd\u52a0
AddHashValuesToDatabaseDialog.pasteFromClipboardButton.text_2=\u30af\u30ea\u30c3\u30d7\u30dc\u30fc\u30c9\u304b\u3089\u8cbc\u308a\u4ed8\u3051\u308b
AddHashValuesToDatabaseProgressDialog.okButton.text=OK
AddHashValuesToDatabaseProgressDialog.statusLabel.text=\u30b9\u30c6\u30fc\u30bf\u30b9
@ -207,3 +205,4 @@ HashLookupSettingsPanel.nameLabel.text=\u30cf\u30c3\u30b7\u30e5\u30bb\u30c3\u30c
HashLookupSettingsPanel.hashDatabasesLabel.text=\u30cf\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\uff1a
HashLookupSettingsPanel.importDatabaseButton.text=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u30a4\u30f3\u30dd\u30fc\u30c8
HashDbCreateDatabaseDialog.databasePathLabel.text=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30d1\u30b9\uff1a
AddHashValuesToDatabaseDialog.okButton.text_2=OK

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011 - 2013 Basis Technology Corp.
* Copyright 2011-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -53,7 +53,10 @@ import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.datamodel.TskException;
@NbBundle.Messages({
/**
* File ingest module to mark files based on hash values.
*/
@Messages({
"HashDbIngestModule.noKnownBadHashDbSetMsg=No notable hash set.",
"HashDbIngestModule.knownBadFileSearchWillNotExecuteWarn=Notable file search will not be executed.",
"HashDbIngestModule.noKnownHashDbSetMsg=No known hash set.",
@ -67,18 +70,21 @@ public class HashDbIngestModule implements FileIngestModule {
private final SleuthkitCase skCase;
private final HashDbManager hashDbManager = HashDbManager.getInstance();
private final HashLookupModuleSettings settings;
private List<HashDb> knownBadHashSets = new ArrayList<>();
private List<HashDb> knownHashSets = new ArrayList<>();
private final List<HashDb> knownBadHashSets = new ArrayList<>();
private final List<HashDb> knownHashSets = new ArrayList<>();
private long jobId;
private static final HashMap<Long, IngestJobTotals> totalsForIngestJobs = new HashMap<>();
private static final IngestModuleReferenceCounter refCounter = new IngestModuleReferenceCounter();
private Blackboard blackboard;
/**
* A container of values for storing ingest metrics for the job.
*/
private static class IngestJobTotals {
private AtomicLong totalKnownBadCount = new AtomicLong(0);
private AtomicLong totalCalctime = new AtomicLong(0);
private AtomicLong totalLookuptime = new AtomicLong(0);
private final AtomicLong totalKnownBadCount = new AtomicLong(0);
private final AtomicLong totalCalctime = new AtomicLong(0);
private final AtomicLong totalLookuptime = new AtomicLong(0);
}
private static synchronized IngestJobTotals getTotalsForIngestJobs(long ingestJobId) {
@ -90,6 +96,15 @@ public class HashDbIngestModule implements FileIngestModule {
return totals;
}
/**
* Create a HashDbIngestModule object that will mark files based on a
* supplied list of hash values. The supplied HashLookupModuleSettings
* object is used to configure the module.
*
* @param settings The module settings.
*
* @throws NoCurrentCaseException If there is no open case.
*/
HashDbIngestModule(HashLookupModuleSettings settings) throws NoCurrentCaseException {
this.settings = settings;
skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
@ -140,12 +155,18 @@ public class HashDbIngestModule implements FileIngestModule {
enabledHashSets.add(db);
}
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Error getting index status for " + db.getDisplayName()+ " hash set", ex); //NON-NLS
logger.log(Level.WARNING, "Error getting index status for " + db.getDisplayName() + " hash set", ex); //NON-NLS
}
}
}
}
@Messages({
"# {0} - File name",
"HashDbIngestModule.dialogTitle.errorFindingArtifacts=Error Finding Artifacts: {0}",
"# {0} - File name",
"HashDbIngestModule.errorMessage.lookingForFileArtifacts=Error encountered while looking for existing artifacts for {0}."
})
@Override
public ProcessResult process(AbstractFile file) {
try {
@ -156,8 +177,8 @@ public class HashDbIngestModule implements FileIngestModule {
}
// Skip unallocated space files.
if ((file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS) ||
file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK))) {
if ((file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS)
|| file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK))) {
return ProcessResult.OK;
}
@ -181,6 +202,7 @@ public class HashDbIngestModule implements FileIngestModule {
// calc hash value
String name = file.getName();
long fileId = file.getId();
String md5Hash = file.getMd5Hash();
if (md5Hash == null || md5Hash.isEmpty()) {
try {
@ -203,15 +225,11 @@ public class HashDbIngestModule implements FileIngestModule {
totals.totalCalctime.addAndGet(delta);
} catch (IOException ex) {
logger.log(Level.WARNING, "Error calculating hash of file " + name, ex); //NON-NLS
logger.log(Level.WARNING, String.format("Error calculating hash of file '%s' (id=%d).", name, fileId), ex); //NON-NLS
services.postMessage(IngestMessage.createErrorMessage(
HashLookupModuleFactory.getModuleName(),
NbBundle.getMessage(this.getClass(),
"HashDbIngestModule.fileReadErrorMsg",
name),
NbBundle.getMessage(this.getClass(),
"HashDbIngestModule.calcHashValueErr",
name)));
NbBundle.getMessage(this.getClass(), "HashDbIngestModule.fileReadErrorMsg", name),
NbBundle.getMessage(this.getClass(), "HashDbIngestModule.calcHashValueErr", name)));
return ProcessResult.ERROR;
}
}
@ -245,21 +263,37 @@ public class HashDbIngestModule implements FileIngestModule {
}
}
postHashSetHitToBlackboard(file, md5Hash, hashSetName, comment, db.getSendIngestMessages());
/*
* We have a match. Now create an artifact if it is
* determined that one hasn't been created yet.
*/
List<BlackboardAttribute> attributesList = new ArrayList<>();
attributesList.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, HashLookupModuleFactory.getModuleName(), hashSetName));
try {
org.sleuthkit.datamodel.Blackboard tskBlackboard = skCase.getBlackboard();
if (tskBlackboard.artifactExists(file, BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT, attributesList) == false) {
postHashSetHitToBlackboard(file, md5Hash, hashSetName, comment, db.getSendIngestMessages());
}
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, String.format(
"A problem occurred while checking for existing artifacts for file '%s' (id=%d).", name, fileId), ex); //NON-NLS
services.postMessage(IngestMessage.createErrorMessage(
HashLookupModuleFactory.getModuleName(),
Bundle.HashDbIngestModule_dialogTitle_errorFindingArtifacts(name),
Bundle.HashDbIngestModule_errorMessage_lookingForFileArtifacts(name)));
ret = ProcessResult.ERROR;
}
}
long delta = (System.currentTimeMillis() - lookupstart);
totals.totalLookuptime.addAndGet(delta);
} catch (TskException ex) {
logger.log(Level.WARNING, "Couldn't lookup notable hash for file " + name + " - see sleuthkit log for details", ex); //NON-NLS
logger.log(Level.WARNING, String.format(
"Couldn't lookup notable hash for file '%s' (id=%d) - see sleuthkit log for details", name, fileId), ex); //NON-NLS
services.postMessage(IngestMessage.createErrorMessage(
HashLookupModuleFactory.getModuleName(),
NbBundle.getMessage(this.getClass(),
"HashDbIngestModule.hashLookupErrorMsg",
name),
NbBundle.getMessage(this.getClass(),
"HashDbIngestModule.lookingUpKnownBadHashValueErr",
name)));
NbBundle.getMessage(this.getClass(), "HashDbIngestModule.hashLookupErrorMsg", name),
NbBundle.getMessage(this.getClass(), "HashDbIngestModule.lookingUpKnownBadHashValueErr", name)));
ret = ProcessResult.ERROR;
}
}
@ -279,15 +313,12 @@ public class HashDbIngestModule implements FileIngestModule {
totals.totalLookuptime.addAndGet(delta);
} catch (TskException ex) {
logger.log(Level.WARNING, "Couldn't lookup known hash for file " + name + " - see sleuthkit log for details", ex); //NON-NLS
logger.log(Level.WARNING, String.format(
"Couldn't lookup known hash for file '%s' (id=%d) - see sleuthkit log for details", name, fileId), ex); //NON-NLS
services.postMessage(IngestMessage.createErrorMessage(
HashLookupModuleFactory.getModuleName(),
NbBundle.getMessage(this.getClass(),
"HashDbIngestModule.hashLookupErrorMsg",
name),
NbBundle.getMessage(this.getClass(),
"HashDbIngestModule.lookingUpKnownHashValueErr",
name)));
NbBundle.getMessage(this.getClass(), "HashDbIngestModule.hashLookupErrorMsg", name),
NbBundle.getMessage(this.getClass(), "HashDbIngestModule.lookingUpKnownHashValueErr", name)));
ret = ProcessResult.ERROR;
}
}
@ -296,18 +327,29 @@ public class HashDbIngestModule implements FileIngestModule {
return ret;
}
@Messages({"HashDbIngestModule.indexError.message=Failed to index hashset hit artifact for keyword search."})
/**
* Post a hash set hit to the blackboard.
*
* @param abstractFile The file to be processed.
* @param md5Hash The MD5 hash value of the file.
* @param hashSetName The name of the hash set with which to associate
* the hit.
* @param comment A comment to be attached to the artifact.
* @param showInboxMessage Show a message in the inbox?
*/
@Messages({
"HashDbIngestModule.indexError.message=Failed to index hashset hit artifact for keyword search."
})
private void postHashSetHitToBlackboard(AbstractFile abstractFile, String md5Hash, String hashSetName, String comment, boolean showInboxMessage) {
try {
String MODULE_NAME = NbBundle.getMessage(HashDbIngestModule.class, "HashDbIngestModule.moduleName");
String moduleName = HashLookupModuleFactory.getModuleName();
BlackboardArtifact badFile = abstractFile.newArtifact(ARTIFACT_TYPE.TSK_HASHSET_HIT);
Collection<BlackboardAttribute> attributes = new ArrayList<>();
//TODO Revisit usage of deprecated constructor as per TSK-583
//BlackboardAttribute att2 = new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID(), MODULE_NAME, "Known Bad", hashSetName);
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, hashSetName));
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_HASH_MD5, MODULE_NAME, md5Hash));
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COMMENT, MODULE_NAME, comment));
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, moduleName, hashSetName));
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_HASH_MD5, moduleName, md5Hash));
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COMMENT, moduleName, comment));
badFile.addAttributes(attributes);
@ -351,19 +393,24 @@ public class HashDbIngestModule implements FileIngestModule {
detailsSb.append("</table>"); //NON-NLS
services.postMessage(IngestMessage.createDataMessage(HashLookupModuleFactory.getModuleName(),
NbBundle.getMessage(this.getClass(),
"HashDbIngestModule.postToBB.knownBadMsg",
abstractFile.getName()),
NbBundle.getMessage(this.getClass(), "HashDbIngestModule.postToBB.knownBadMsg", abstractFile.getName()),
detailsSb.toString(),
abstractFile.getName() + md5Hash,
badFile));
}
services.fireModuleDataEvent(new ModuleDataEvent(MODULE_NAME, ARTIFACT_TYPE.TSK_HASHSET_HIT, Collections.singletonList(badFile)));
services.fireModuleDataEvent(new ModuleDataEvent(moduleName, ARTIFACT_TYPE.TSK_HASHSET_HIT, Collections.singletonList(badFile)));
} catch (TskException ex) {
logger.log(Level.WARNING, "Error creating blackboard artifact", ex); //NON-NLS
}
}
/**
* Post a message summarizing the results of the ingest.
*
* @param jobId The ID of the job.
* @param knownBadHashSets The list of hash sets for "known bad" files.
* @param knownHashSets The list of hash sets for "known" files.
*/
private static synchronized void postSummary(long jobId,
List<HashDb> knownBadHashSets, List<HashDb> knownHashSets) {
IngestJobTotals jobTotals = getTotalsForIngestJobs(jobId);
@ -399,8 +446,7 @@ public class HashDbIngestModule implements FileIngestModule {
IngestServices.getInstance().postMessage(IngestMessage.createMessage(
IngestMessage.MessageType.INFO,
HashLookupModuleFactory.getModuleName(),
NbBundle.getMessage(HashDbIngestModule.class,
"HashDbIngestModule.complete.hashLookupResults"),
NbBundle.getMessage(HashDbIngestModule.class, "HashDbIngestModule.complete.hashLookupResults"),
detailsSb.toString()));
}
}

View File

@ -42,13 +42,18 @@ public class HashLookupModuleFactory extends IngestModuleFactoryAdapter {
return getModuleName();
}
/**
* Get the name of the module.
*
* @return The module name.
*/
static String getModuleName() {
return NbBundle.getMessage(HashLookupModuleFactory.class, "HashDbIngestModule.moduleName");
return NbBundle.getMessage(HashLookupModuleFactory.class, "HashLookupModuleFactory.moduleName.text");
}
@Override
public String getModuleDescription() {
return NbBundle.getMessage(HashLookupModuleFactory.class, "HashDbIngestModule.moduleDescription");
return NbBundle.getMessage(HashLookupModuleFactory.class, "HashLookupModuleFactory.moduleDescription.text");
}
@Override
@ -101,8 +106,8 @@ public class HashLookupModuleFactory extends IngestModuleFactoryAdapter {
@Override
public FileIngestModule createFileIngestModule(IngestModuleIngestJobSettings settings) {
if (!(settings instanceof HashLookupModuleSettings)) {
throw new IllegalArgumentException(
NbBundle.getMessage(this.getClass(), "HashLookupModuleFactory.createFileIngestModule.exception.msg"));
throw new IllegalArgumentException(NbBundle.getMessage(this.getClass(),
"HashLookupModuleFactory.createFileIngestModule.exception.msg"));
}
try {
return new HashDbIngestModule((HashLookupModuleSettings) settings);

View File

@ -80,7 +80,6 @@ class ReportHTML implements TableReportModule {
private static final int MAX_THUMBS_PER_PAGE = 1000;
private static final String HTML_SUBDIR = "content";
private Case currentCase;
private SleuthkitCase skCase;
static Integer THUMBNAIL_COLUMNS = 5;
private Map<String, Integer> dataTypes;
@ -109,7 +108,6 @@ class ReportHTML implements TableReportModule {
// Refesh the member variables
private void refresh() throws NoCurrentCaseException {
currentCase = Case.getCurrentCaseThrows();
skCase = currentCase.getSleuthkitCase();
dataTypes = new TreeMap<>();
@ -274,7 +272,7 @@ class ReportHTML implements TableReportModule {
in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/accounts.png"); //NON-NLS
break;
default:
logger.log(Level.WARNING, "useDataTypeIcon: unhandled artifact type = " + dataType); //NON-NLS
logger.log(Level.WARNING, "useDataTypeIcon: unhandled artifact type = {0}", dataType); //NON-NLS
in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/star.png"); //NON-NLS
iconFileName = "star.png"; //NON-NLS
iconFilePath = subPath + File.separator + iconFileName;
@ -627,12 +625,19 @@ class ReportHTML implements TableReportModule {
int positionCounter = 0;
for (String cell : row) {
// position-dependent code used to format this report. Not great, but understandable for formatting.
if (positionCounter == 1) { // Convert the file name to a hyperlink and left-align it
builder.append("\t\t<td class=\"left_align_cell\">").append(localFileLink.toString()).append(cell).append("</a></td>\n"); //NON-NLS
} else if (positionCounter == 7) { // Right-align the bytes column.
builder.append("\t\t<td class=\"right_align_cell\">").append(cell).append("</td>\n"); //NON-NLS
} else { // Regular case, not a file name nor a byte count
builder.append("\t\t<td>").append(cell).append("</td>\n"); //NON-NLS
switch (positionCounter) {
case 1:
// Convert the file name to a hyperlink and left-align it
builder.append("\t\t<td class=\"left_align_cell\">").append(localFileLink.toString()).append(cell).append("</a></td>\n"); //NON-NLS
break;
case 7:
// Right-align the bytes column.
builder.append("\t\t<td class=\"right_align_cell\">").append(cell).append("</td>\n"); //NON-NLS
break;
default:
// Regular case, not a file name nor a byte count
builder.append("\t\t<td>").append(cell).append("</td>\n"); //NON-NLS
break;
}
++positionCounter;
}
@ -719,7 +724,7 @@ class ReportHTML implements TableReportModule {
for (int i = 0; i < tags.size(); i++) {
ContentTag tag = tags.get(i);
String notableString = tag.getName().getKnownStatus() == TskData.FileKnown.BAD ? TagsManager.getNotableTagLabel() : "";
linkToThumbnail.append(tag.getName().getDisplayName() + notableString);
linkToThumbnail.append(tag.getName().getDisplayName()).append(notableString);
if (i != tags.size() - 1) {
linkToThumbnail.append(", ");
}
@ -751,12 +756,9 @@ class ReportHTML implements TableReportModule {
return true;
}
AbstractFile file = (AbstractFile) c;
if (file.isDir()
return file.isDir()
|| file.getType() == TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS
|| file.getType() == TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS) {
return true;
}
return false;
|| file.getType() == TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS;
}
/**
@ -782,8 +784,14 @@ class ReportHTML implements TableReportModule {
localFileFolder.mkdirs();
}
// Construct a file tagName for the local file that incorporates the file id to ensure uniqueness.
String fileName = file.getName();
/*
* Construct a file tagName for the local file that incorporates the
* file ID to ensure uniqueness.
*
* Note: File name is normalized to account for possible attribute name
* which will be separated by a ':' character.
*/
String fileName = org.sleuthkit.autopsy.coreutils.FileUtil.escapeFileName(file.getName());
String objectIdSuffix = "_" + file.getId();
int lastDotIndex = fileName.lastIndexOf(".");
if (lastDotIndex != -1 && lastDotIndex != 0) {
@ -1052,9 +1060,9 @@ class ReportHTML implements TableReportModule {
* Write the summary of the current case for this report.
*/
private void writeSummary() {
Writer out = null;
Writer output = null;
try {
out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(subPath + "summary.html"), "UTF-8")); //NON-NLS
output = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(subPath + "summary.html"), "UTF-8")); //NON-NLS
StringBuilder head = new StringBuilder();
head.append("<html>\n<head>\n<title>").append( //NON-NLS
NbBundle.getMessage(this.getClass(), "ReportHTML.writeSum.title")).append("</title>\n"); //NON-NLS
@ -1080,7 +1088,7 @@ class ReportHTML implements TableReportModule {
head.append("li {padding-bottom: 5px;}");
head.append("</style>\n"); //NON-NLS
head.append("</head>\n<body>\n"); //NON-NLS
out.write(head.toString());
output.write(head.toString());
DateFormat datetimeFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
Date date = new Date();
@ -1119,7 +1127,7 @@ class ReportHTML implements TableReportModule {
}
summary.append("</div>\n"); //NON-NLS
summary.append("</body></html>"); //NON-NLS
out.write(summary.toString());
output.write(summary.toString());
} catch (FileNotFoundException ex) {
logger.log(Level.SEVERE, "Could not find summary.html file to write to."); //NON-NLS
} catch (UnsupportedEncodingException ex) {
@ -1130,9 +1138,9 @@ class ReportHTML implements TableReportModule {
logger.log(Level.WARNING, "Unable to get current sleuthkit Case for the HTML report.");
} finally {
try {
if (out != null) {
out.flush();
out.close();
if (output != null) {
output.flush();
output.close();
}
} catch (IOException ex) {
}
@ -1292,9 +1300,24 @@ class ReportHTML implements TableReportModule {
return summary;
}
/**
* Create a thumbnail of a given file.
*
* @param file The file from which to create the thumbnail.
*
* @return The path to the thumbnail file, or null if a thumbnail couldn't
* be created.
*/
private String prepareThumbnail(AbstractFile file) {
BufferedImage bufferedThumb = ImageUtils.getThumbnail(file, ImageUtils.ICON_SIZE_MEDIUM);
File thumbFile = Paths.get(thumbsPath, file.getName() + ".png").toFile();
/*
* File name is normalized to account for possible attribute name which
* will be separated by a ':' character.
*/
String fileName = org.sleuthkit.autopsy.coreutils.FileUtil.escapeFileName(file.getName());
File thumbFile = Paths.get(thumbsPath, fileName + ".png").toFile();
if (bufferedThumb == null) {
return null;
}

View File

@ -97,6 +97,9 @@
<ResourceString bundle="org/sleuthkit/autopsy/report/Bundle.properties" key="ReportVisualPanel2.allResultsRadioButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="allResultsRadioButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="dataLabel">
<Properties>

View File

@ -109,7 +109,7 @@ final class ReportVisualPanel2 extends JPanel {
}
for (TagName tagName : tagNamesInUse) {
String notableString = tagName.getKnownStatus() == TskData.FileKnown.BAD ? TagsManager.getNotableTagLabel() : "";
String notableString = tagName.getKnownStatus() == TskData.FileKnown.BAD ? TagsManager.getNotableTagLabel() : "";
tagStates.put(tagName.getDisplayName() + notableString, Boolean.FALSE);
}
tags.addAll(tagStates.keySet());
@ -124,7 +124,9 @@ final class ReportVisualPanel2 extends JPanel {
tagsList.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent evt) {
if (!taggedResultsRadioButton.isSelected()) {
return;
}
int index = tagsList.locationToIndex(evt.getPoint());
if (index < tagsModel.getSize() && index >= 0) {
String value = tagsModel.getElementAt(index);
@ -184,16 +186,26 @@ final class ReportVisualPanel2 extends JPanel {
return tagStates;
}
/**
* Are any tags selected?
*
* @return True if any tags are selected; otherwise false.
*/
private boolean areTagsSelected() {
boolean result = false;
for (Entry<String, Boolean> entry : tagStates.entrySet()) {
if (entry.getValue()) {
result = true;
break;
}
}
return result;
}
/**
* Set the Finish button as either enabled or disabled depending on the UI
* component selections.
*/
private void updateFinishButton() {
if (taggedResultsRadioButton.isSelected()) {
wizPanel.setFinish(areTagsSelected());
@ -209,6 +221,19 @@ final class ReportVisualPanel2 extends JPanel {
return taggedResultsRadioButton.isSelected();
}
/**
* Set all tagged results as either selected or unselected.
*
* @param selected Should all tagged results be selected?
*/
void setAllTaggedResultsSelected(boolean selected) {
for (String tag : tags) {
tagStates.put(tag, (selected ? Boolean.TRUE : Boolean.FALSE));
}
tagsList.repaint();
wizPanel.setFinish(selected);
}
/**
* 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
@ -234,6 +259,11 @@ final class ReportVisualPanel2 extends JPanel {
optionsButtonGroup.add(allResultsRadioButton);
org.openide.awt.Mnemonics.setLocalizedText(allResultsRadioButton, org.openide.util.NbBundle.getMessage(ReportVisualPanel2.class, "ReportVisualPanel2.allResultsRadioButton.text")); // NOI18N
allResultsRadioButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
allResultsRadioButtonActionPerformed(evt);
}
});
org.openide.awt.Mnemonics.setLocalizedText(dataLabel, org.openide.util.NbBundle.getMessage(ReportVisualPanel2.class, "ReportVisualPanel2.dataLabel.text")); // NOI18N
@ -312,25 +342,21 @@ final class ReportVisualPanel2 extends JPanel {
}// </editor-fold>//GEN-END:initComponents
private void selectAllButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_selectAllButtonActionPerformed
for (String tag : tags) {
tagStates.put(tag, Boolean.TRUE);
}
tagsList.repaint();
wizPanel.setFinish(true);
setAllTaggedResultsSelected(true);
}//GEN-LAST:event_selectAllButtonActionPerformed
private void deselectAllButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deselectAllButtonActionPerformed
for (String tag : tags) {
tagStates.put(tag, Boolean.FALSE);
}
tagsList.repaint();
wizPanel.setFinish(false);
setAllTaggedResultsSelected(false);
}//GEN-LAST:event_deselectAllButtonActionPerformed
private void advancedButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_advancedButtonActionPerformed
artifactStates = dialog.display();
}//GEN-LAST:event_advancedButtonActionPerformed
private void allResultsRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_allResultsRadioButtonActionPerformed
setAllTaggedResultsSelected(false);
}//GEN-LAST:event_allResultsRadioButtonActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton advancedButton;
private javax.swing.JRadioButton allResultsRadioButton;

View File

@ -38,6 +38,7 @@ import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Level;
import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.casemodule.services.TagsManager;
@ -58,7 +59,7 @@ import org.sleuthkit.datamodel.TagName;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
class TableReportGenerator {
class TableReportGenerator {
private final List<BlackboardArtifact.Type> artifactTypes = new ArrayList<>();
private final HashSet<String> tagNamesFilter = new HashSet<>();
@ -264,6 +265,7 @@ class TableReportGenerator {
/**
* Make table for tagged files
*/
@Messages({"ReportGenerator.tagTable.header.userName=User Name"})
@SuppressWarnings("deprecation")
private void makeContentTagsTables() {
@ -286,7 +288,8 @@ class TableReportGenerator {
ArrayList<String> columnHeaders = new ArrayList<>(Arrays.asList(
NbBundle.getMessage(this.getClass(), "ReportGenerator.htmlOutput.header.tag"),
NbBundle.getMessage(this.getClass(), "ReportGenerator.htmlOutput.header.file"),
NbBundle.getMessage(this.getClass(), "ReportGenerator.htmlOutput.header.comment"),
NbBundle.getMessage(this.getClass(), "ReportGenerator.htmlOutput.header.comment"),
NbBundle.getMessage(this.getClass(), "ReportGenerator.tagTable.header.userName"),
NbBundle.getMessage(this.getClass(), "ReportGenerator.htmlOutput.header.timeModified"),
NbBundle.getMessage(this.getClass(), "ReportGenerator.htmlOutput.header.timeChanged"),
NbBundle.getMessage(this.getClass(), "ReportGenerator.htmlOutput.header.timeAccessed"),
@ -324,7 +327,7 @@ class TableReportGenerator {
fileName = tag.getContent().getName();
}
ArrayList<String> rowData = new ArrayList<>(Arrays.asList(tag.getName().getDisplayName() + notableString, fileName, tag.getComment()));
ArrayList<String> rowData = new ArrayList<>(Arrays.asList(tag.getName().getDisplayName() + notableString, fileName, tag.getComment(), tag.getUserName()));
Content content = tag.getContent();
if (content instanceof AbstractFile) {
AbstractFile file = (AbstractFile) content;
@ -386,7 +389,8 @@ class TableReportGenerator {
NbBundle.getMessage(this.getClass(), "ReportGenerator.tagTable.header.resultType"),
NbBundle.getMessage(this.getClass(), "ReportGenerator.tagTable.header.tag"),
NbBundle.getMessage(this.getClass(), "ReportGenerator.tagTable.header.comment"),
NbBundle.getMessage(this.getClass(), "ReportGenerator.tagTable.header.srcFile"))));
NbBundle.getMessage(this.getClass(), "ReportGenerator.tagTable.header.srcFile"),
NbBundle.getMessage(this.getClass(), "ReportGenerator.tagTable.header.userName"))));
// Give the modules the rows for the content tags.
for (BlackboardArtifactTag tag : tags) {
@ -396,7 +400,8 @@ class TableReportGenerator {
}
List<String> row;
row = new ArrayList<>(Arrays.asList(tag.getArtifact().getArtifactTypeName(), tag.getName().getDisplayName() + notableString, tag.getComment(), tag.getContent().getName()));
row = new ArrayList<>(Arrays.asList(tag.getArtifact().getArtifactTypeName(), tag.getName().getDisplayName() + notableString,
tag.getComment(), tag.getContent().getName(), tag.getUserName()));
tableReport.addRow(row);
// check if the tag is an image that we should later make a thumbnail for
@ -582,7 +587,7 @@ class TableReportGenerator {
adHocCountQuery += " AND (art.artifact_id = tag.artifact_id) AND (tag.tag_name_id IN (" + tagIDList + ")) "; //NON-NLS
}
adHocCountQuery += "EXCEPT " + // NON-NLS
"SELECT art.artifact_id FROM blackboard_artifacts AS art, blackboard_attributes AS att1 WHERE (att1.artifact_id = art.artifact_id) AND (art.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID() + ") AND (att1.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID() + ")) "; //NON-NLS
"SELECT art.artifact_id FROM blackboard_artifacts AS art, blackboard_attributes AS att1 WHERE (att1.artifact_id = art.artifact_id) AND (art.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID() + ") AND (att1.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID() + ")) AS adHocHits"; //NON-NLS
int adHocCount = 0;
try (SleuthkitCase.CaseDbQuery dbQuery = openCase.getSleuthkitCase().executeQuery(adHocCountQuery)) {
@ -600,7 +605,7 @@ class TableReportGenerator {
// Create the query to get the keyword list names
if (openCase.getCaseType() == Case.CaseType.MULTI_USER_CASE) {
orderByClause = "ORDER BY convert_to(att.value_text, 'SQL_ASCII') ASC NULLS FIRST"; //NON-NLS
orderByClause = "ORDER BY convert_to(list, 'SQL_ASCII') ASC NULLS FIRST"; //NON-NLS
} else {
orderByClause = "ORDER BY list ASC"; //NON-NLS
}
@ -621,8 +626,9 @@ class TableReportGenerator {
"AND (tag.tag_name_id IN (" + tagIDList + ")) "; //NON-NLS
}
if (adHocCount > 0) {
keywordListQuery += " UNION SELECT \"\" AS list ";
keywordListQuery += " UNION SELECT \'\' AS list ";
}
keywordListQuery = "SELECT * FROM ( " + keywordListQuery + " ) kwListNames ";
keywordListQuery += "GROUP BY list " + orderByClause; //NON-NLS
// Make the table of contents links for each list type
@ -645,17 +651,17 @@ class TableReportGenerator {
BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getDisplayName()));
} catch (TskCoreException | SQLException ex) {
errorList.add(NbBundle.getMessage(this.getClass(), "ReportGenerator.errList.failedQueryKWLists"));
logger.log(Level.SEVERE, "Failed to query keyword lists: ", ex); //NON-NLS
logger.log(Level.SEVERE, "Failed to query keyword lists with query " + keywordListQuery, ex); //NON-NLS
return;
}
// Query for keywords, grouped by list
if (openCase.getCaseType() == Case.CaseType.MULTI_USER_CASE) {
orderByClause = "ORDER BY convert_to(att3.value_text, 'SQL_ASCII') ASC NULLS FIRST, " //NON-NLS
+ "convert_to(att1.value_text, 'SQL_ASCII') ASC NULLS FIRST, " //NON-NLS
+ "convert_to(f.parent_path, 'SQL_ASCII') ASC NULLS FIRST, " //NON-NLS
+ "convert_to(f.name, 'SQL_ASCII') ASC NULLS FIRST, " //NON-NLS
+ "convert_to(att2.value_text, 'SQL_ASCII') ASC NULLS FIRST"; //NON-NLS
orderByClause = "ORDER BY convert_to(list, 'SQL_ASCII') ASC NULLS FIRST, " //NON-NLS
+ "convert_to(keyword, 'SQL_ASCII') ASC NULLS FIRST, " //NON-NLS
+ "convert_to(parent_path, 'SQL_ASCII') ASC NULLS FIRST, " //NON-NLS
+ "convert_to(name, 'SQL_ASCII') ASC NULLS FIRST, " //NON-NLS
+ "convert_to(preview, 'SQL_ASCII') ASC NULLS FIRST"; //NON-NLS
} else {
orderByClause = "ORDER BY list ASC, keyword ASC, parent_path ASC, name ASC, preview ASC"; //NON-NLS
}
@ -684,7 +690,7 @@ class TableReportGenerator {
// Query for keywords that are not part of a list
String keywordAdHocQuery =
"SELECT art.artifact_id AS artifact_id, art.obj_id AS obj_id, att1.value_text AS keyword, att2.value_text AS preview, \"\" AS list, f.name AS name, f.parent_path AS parent_path " + // NON-NLS
"SELECT art.artifact_id AS artifact_id, art.obj_id AS obj_id, att1.value_text AS keyword, att2.value_text AS preview, \'\' AS list, f.name AS name, f.parent_path AS parent_path " + // NON-NLS
"FROM blackboard_artifacts AS art, blackboard_attributes AS att1, blackboard_attributes AS att2, tsk_files AS f " + // NON-NLS
"WHERE " + // NON-NLS
" (art.artifact_id IN (SELECT art.artifact_id FROM blackboard_artifacts AS art, blackboard_attributes AS att1 WHERE (att1.artifact_id = art.artifact_id) AND (art.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID() + ") " + // NON-NLS
@ -697,7 +703,7 @@ class TableReportGenerator {
"AND (att2.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW.getTypeID() + ") " + // NON-NLS
"AND (art.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID() + ") "; // NON-NLS
String keywordsQuery = keywordListsQuery + " UNION " + keywordAdHocQuery + orderByClause;
String keywordsQuery = "SELECT * FROM ( " + keywordListsQuery + " UNION " + keywordAdHocQuery + " ) kwHits " + orderByClause;
try (SleuthkitCase.CaseDbQuery dbQuery = openCase.getSleuthkitCase().executeQuery(keywordsQuery)) {
ResultSet resultSet = dbQuery.getResultSet();
@ -770,7 +776,7 @@ class TableReportGenerator {
tableModule.endDataType();
} catch (TskCoreException | SQLException ex) {
errorList.add(NbBundle.getMessage(this.getClass(), "ReportGenerator.errList.failedQueryKWs"));
logger.log(Level.SEVERE, "Failed to query keywords: ", ex); //NON-NLS
logger.log(Level.SEVERE, "Failed to query keywords with query " + keywordsQuery, ex); //NON-NLS
}
}

View File

@ -0,0 +1,272 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2018-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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.sqlitereader;
import java.io.File;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.casemodule.services.FileManager;
import org.sleuthkit.autopsy.casemodule.services.Services;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Reads rows from SQLite tables and returns results in a list collection.
*/
public class SQLiteReader implements AutoCloseable {
private final Connection connection;
/**
* Writes data source file contents to local disk and opens a sqlite JDBC
* connection.
*
* @param sqliteDbFile Data source abstract file
* @param localDiskPath Location for database contents to be copied to
* @throws ClassNotFoundException missing SQLite JDBC class
* @throws SQLException Exception opening JDBC connection
* @throws IOException Exception writing file contents
* @throws NoCurrentCaseException Current case closed during file copying
* @throws TskCoreException Exception finding files from abstract file
*/
public SQLiteReader(AbstractFile sqliteDbFile, String localDiskPath) throws ClassNotFoundException,
SQLException, IOException, NoCurrentCaseException, TskCoreException{
writeDataSourceToLocalDisk(sqliteDbFile, localDiskPath);
connection = getDatabaseConnection(localDiskPath);
}
/**
* Copies the data source file contents to local drive for processing.
*
* @param file AbstractFile from the data source
* @param localDiskPath Local drive path to copy AbstractFile contents
* @throws IOException Exception writing file contents
* @throws NoCurrentCaseException Current case closed during file copying
* @throws TskCoreException Exception finding files from abstract file
*/
private void writeDataSourceToLocalDisk(AbstractFile file, String localDiskPath)
throws IOException, NoCurrentCaseException, TskCoreException {
File localDatabaseFile = new File(localDiskPath);
if (!localDatabaseFile.exists()) {
ContentUtils.writeToFile(file, localDatabaseFile);
// Look for any meta files associated with this DB - WAL, SHM, etc.
findAndCopySQLiteMetaFile(file, file.getName() + "-wal");
findAndCopySQLiteMetaFile(file, file.getName() + "-shm");
}
}
/**
* Searches for a meta file associated with the give SQLite database. If found,
* copies the file to the local disk folder
*
* @param sqliteFile SQLIte db file being processed
* @param metaFileName name of meta file to look for
* @throws NoCurrentCaseException Case has been closed.
* @throws TskCoreException fileManager cannot find AbstractFile files.
* @throws IOException Issue during writing to file.
*/
private void findAndCopySQLiteMetaFile(AbstractFile sqliteFile,
String metaFileName) throws NoCurrentCaseException, TskCoreException, IOException {
Case openCase = Case.getCurrentCaseThrows();
SleuthkitCase sleuthkitCase = openCase.getSleuthkitCase();
Services services = new Services(sleuthkitCase);
FileManager fileManager = services.getFileManager();
List<AbstractFile> metaFiles = fileManager.findFiles(
sqliteFile.getDataSource(), metaFileName,
sqliteFile.getParent().getName());
if (metaFiles != null) {
for (AbstractFile metaFile : metaFiles) {
String tmpMetafilePathName = openCase.getTempDirectory() +
File.separator + metaFile.getName();
File tmpMetafile = new File(tmpMetafilePathName);
ContentUtils.writeToFile(metaFile, tmpMetafile);
}
}
}
/**
* Opens a JDBC connection to the sqlite database specified by the path
* parameter.
*
* @param databasePath Local path of sqlite database
* @return Connection JDBC connection, to be maintained and closed by the reader
* @throws ClassNotFoundException missing SQLite JDBC class
* @throws SQLException Exception during opening database connection
*/
private Connection getDatabaseConnection(String databasePath)
throws ClassNotFoundException, SQLException {
// Load the SQLite JDBC driver, if necessary.
Class.forName("org.sqlite.JDBC"); //NON-NLS
return DriverManager.getConnection(
"jdbc:sqlite:" + databasePath); //NON-NLS
}
/**
* Retrieves a map view of table names to table schemas (in the form of
* CREATE TABLE statments).
*
* @return A map of table names to table schemas
* @throws SQLException
*/
public Map<String, String> getTableSchemas()
throws SQLException {
Map<String, String> dbTablesMap = new TreeMap<>();
try (Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(
"SELECT name, sql FROM sqlite_master " //NON-NLS
+ " WHERE type= 'table' " //NON-NLS
+ " ORDER BY name;")){ //NON-NLS
while (resultSet.next()) {
String tableName = resultSet.getString("name"); //NON-NLS
String tableSQL = resultSet.getString("sql"); //NON-NLS
dbTablesMap.put(tableName, tableSQL);
}
}
return dbTablesMap;
}
/**
* Retrieves the total number of rows from a table in the SQLite database.
*
* @param tableName
* @return Row count from tableName
* @throws SQLException
*/
public Integer getTableRowCount(String tableName) throws SQLException {
try (Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(
"SELECT count (*) as count FROM " + tableName)){ //NON-NLS
return resultSet.getInt("count"); //NON-NLS
}
}
/**
* Retrieves all rows from a given table in the SQLite database. If only a
* subset of rows are desired, see the overloaded function below.
*
* @param tableName
* @return List of rows, where each row is
* represented as a column-value map.
* @throws SQLException
*/
public List<Map<String, Object>> getRowsFromTable(String tableName) throws SQLException {
//This method does not directly call its overloaded counterpart
//since the second parameter would need to be retreived from a call to
//getTableRowCount().
try(Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(
"SELECT * FROM " + tableName)) { //NON-NLS
return resultSetToList(resultSet);
}
}
/**
* Retrieves a subset of the rows from a given table in the SQLite database.
*
* @param tableName
* @param startRow Desired start index (rows begin at 1)
* @param numRowsToRead Number of rows past the start index
* @return List of rows, where each row is
* represented as a column-value map.
* @throws SQLException
*/
public List<Map<String, Object>> getRowsFromTable(String tableName,
int startRow, int numRowsToRead) throws SQLException{
try(Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(
"SELECT * FROM " + tableName //NON-NLS
+ " LIMIT " + Integer.toString(numRowsToRead) //NON-NLS
+ " OFFSET " + Integer.toString(startRow - 1))) { //NON-NLS
return resultSetToList(resultSet);
}
}
/**
* Converts a ResultSet (row results from a table read) into a list.
*
* @param resultSet row results from a table read
* @return List of rows, where each row is
* represented as a column-value map.
* @throws SQLException occurs if ResultSet is closed while attempting to
* access it's data.
*/
@NbBundle.Messages("SQLiteReader.BlobNotShown.message=BLOB Data not shown")
private List<Map<String, Object>> resultSetToList(ResultSet resultSet) throws SQLException {
ResultSetMetaData metaData = resultSet.getMetaData();
int columns = metaData.getColumnCount();
List<Map<String, Object>> rowMap = new ArrayList<>();
while (resultSet.next()) {
Map<String, Object> row = new LinkedHashMap<>(columns);
for (int i = 1; i <= columns; ++i) {
if (resultSet.getObject(i) == null) {
row.put(metaData.getColumnName(i), "");
} else {
if (metaData.getColumnTypeName(i).compareToIgnoreCase("blob") == 0) {
row.put(metaData.getColumnName(i), Bundle.SQLiteReader_BlobNotShown_message());
} else {
row.put(metaData.getColumnName(i), resultSet.getObject(i));
}
}
}
rowMap.add(row);
}
return rowMap;
}
/**
* Closes underlying JDBC connection.
*
* @throws SQLException
*/
@Override
public void close() throws SQLException {
connection.close();
}
}

View File

@ -567,8 +567,11 @@ class ListTimeline extends BorderPane {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
setTooltip(null);
} else {
setText(textSupplier.apply(getEvent()));
String text = textSupplier.apply(getEvent());
setText(text);
setTooltip(new Tooltip(text));
}
}
}

View File

@ -23,6 +23,7 @@ import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import junit.framework.Test;
import org.netbeans.junit.NbModuleSuite;
import org.netbeans.junit.NbTestCase;
@ -37,6 +38,7 @@ import org.sleuthkit.autopsy.testutils.CaseUtils;
import org.sleuthkit.autopsy.testutils.IngestUtils;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.TskCoreException;
/**
@ -45,7 +47,7 @@ import org.sleuthkit.datamodel.TskCoreException;
public class EmbeddedFileTest extends NbTestCase {
private static final String CASE_NAME = "EmbeddedFileTest";
private final Path IMAGE_PATH = Paths.get(this.getDataDir().toString(), "EmbeddedIM_img1_v1.vhd");
private final Path IMAGE_PATH = Paths.get(this.getDataDir().toString(), "EmbeddedIM_img1_v2.vhd");
public static final String HASH_VALUE = "098f6bcd4621d373cade4e832627b4f6";
private static final int DEEP_FOLDER_COUNT = 25;
private Case openCase;
@ -92,31 +94,47 @@ public class EmbeddedFileTest extends NbTestCase {
CaseUtils.closeCurrentCase(testSucceeded);
}
public void testEncryption() {
public void testEncryptionAndZipBomb() {
try {
List<AbstractFile> results = openCase.getSleuthkitCase().findAllFilesWhere("name LIKE '%%'");
String protectedName1 = "password_protected.zip";
String protectedName2 = "level1_protected.zip";
String protectedName3 = "42.zip";
assertEquals(2207, results.size());
List<AbstractFile> results = openCase.getSleuthkitCase().findAllFilesWhere("name LIKE '%%'");
final String zipBombSetName = "Possible Zip Bomb";
final String protectedName1 = "password_protected.zip";
final String protectedName2 = "level1_protected.zip";
final String protectedName3 = "42.zip";
final String depthZipBomb = "DepthTriggerZipBomb.zip";
final String ratioZipBomb = "RatioTriggerZipBomb.zip";
int zipBombs = 0;
assertEquals("The number of files in the test image has changed", 2221, results.size());
int passwdProtectedZips = 0;
for (AbstractFile file : results) {
//.zip file has artifact TSK_ENCRYPTION_DETECTED
if (file.getName().equalsIgnoreCase(protectedName1) || file.getName().equalsIgnoreCase(protectedName2) || file.getName().equalsIgnoreCase(protectedName3)){
ArrayList<BlackboardArtifact> artifacts = file.getAllArtifacts();
assertEquals(1, artifacts.size());
assertEquals("Password protected zip file " + file.getName() + " has incorrect number of artifacts", 1, artifacts.size());
for (BlackboardArtifact artifact : artifacts) {
assertEquals(artifact.getArtifactTypeID(), BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED.getTypeID());
assertEquals("Artifact for password protected zip file " + file.getName() + " has incorrect type ID", artifact.getArtifactTypeID(), BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED.getTypeID());
passwdProtectedZips++;
}
} else if (file.getName().equalsIgnoreCase(depthZipBomb) || file.getName().equalsIgnoreCase(ratioZipBomb)){
ArrayList<BlackboardArtifact> artifacts = file.getAllArtifacts();
assertEquals("Zip bomb " + file.getName() + " has incorrect number of artifacts", 1, artifacts.size());
for (BlackboardArtifact artifact : artifacts) {
assertEquals("Artifact for Zip bomb " + file.getName() + " has incorrect type ID", artifact.getArtifactTypeID(), BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID());
BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME));
assertNotNull("No attribute found for artifact on zip bomb " + file.getName(), attribute);
assertEquals("Interesting artifact on file, " + file.getName() + ", does not reflect it being a zip bomb", zipBombSetName, attribute.getDisplayString());
zipBombs++;
}
} else {//No other files have artifact defined
assertEquals(0, file.getAllArtifacts().size());
assertEquals("Unexpected file, " + file.getName() + ", has artifacts", 0, file.getAllArtifacts().size());
}
}
//Make sure 3 password protected zip files have been tested: password_protected.zip, level1_protected.zip and 42.zip that we download for bomb testing.
assertEquals(3, passwdProtectedZips);
assertEquals("Unexpected number of artifacts reflecting password protected zip files found", 3, passwdProtectedZips);
//Make sure 2 zip bomb files have been tested: DepthTriggerZipBomb.zip and RatioTriggerZipBomb.zip.
assertEquals("Unexpected number of artifacts reflecting zip bombs found", 2, zipBombs);
} catch (TskCoreException ex) {
Exceptions.printStackTrace(ex);
Assert.fail(ex);

View File

@ -1,8 +1,8 @@
Manifest-Version: 1.0
OpenIDE-Module: org.sleuthkit.autopsy.corelibs/3
OpenIDE-Module-Implementation-Version: 4
OpenIDE-Module-Implementation-Version: 5
OpenIDE-Module-Localizing-Bundle: org/sleuthkit/autopsy/corelibs/Bundle.properties
OpenIDE-Module-Specification-Version: 1.1
OpenIDE-Module-Specification-Version: 1.2
AutoUpdate-Show-In-Client: true
AutoUpdate-Essential-Module: true

View File

@ -135,7 +135,7 @@
<compile-dependency/>
<run-dependency>
<release-version>10</release-version>
<specification-version>10.11</specification-version>
<specification-version>10.12</specification-version>
</run-dependency>
</dependency>
<dependency>
@ -144,7 +144,7 @@
<compile-dependency/>
<run-dependency>
<release-version>3</release-version>
<specification-version>1.1</specification-version>
<specification-version>1.2</specification-version>
</run-dependency>
</dependency>
<dependency>

View File

@ -326,7 +326,7 @@ final class AutoIngestJobsNode extends AbstractNode {
ss.put(new NodeProperty<>(Bundle.AutoIngestJobsNode_jobCreated_text(), Bundle.AutoIngestJobsNode_jobCreated_text(), Bundle.AutoIngestJobsNode_jobCreated_text(),
jobWrapper.getManifest().getDateFileCreated()));
ss.put(new NodeProperty<>(Bundle.AutoIngestJobsNode_priority_text(), Bundle.AutoIngestJobsNode_priority_text(), Bundle.AutoIngestJobsNode_priority_text(),
jobWrapper.getPriority() > 0 ? Bundle.AutoIngestJobsNode_prioritized_true() : Bundle.AutoIngestJobsNode_prioritized_false()));
jobWrapper.getPriority()));
break;
case RUNNING_JOB:
AutoIngestJob.StageDetails status = jobWrapper.getProcessingStageDetails();

View File

@ -31,6 +31,7 @@ import org.sleuthkit.autopsy.datamodel.EmptyNode;
import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestJobsNode.AutoIngestJobStatus;
import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestJobsNode.JobNode;
import org.sleuthkit.autopsy.experimental.autoingest.AutoIngestNodeRefreshEvents.AutoIngestRefreshEvent;
import org.sleuthkit.autopsy.guiutils.StatusIconCellRenderer;
/**
* A panel which displays an outline view with all jobs for a specified status.
@ -84,6 +85,7 @@ final class AutoIngestJobsPanel extends javax.swing.JPanel implements ExplorerMa
indexOfColumn = getColumnIndexByName(Bundle.AutoIngestJobsNode_priority_text());
if (indexOfColumn != INVALID_INDEX) {
outline.getColumnModel().getColumn(indexOfColumn).setPreferredWidth(INITIAL_PRIORITIZED_WIDTH);
outline.getColumnModel().getColumn(indexOfColumn).setCellRenderer(new PrioritizedIconCellRenderer());
}
break;
case RUNNING_JOB:
@ -108,6 +110,7 @@ final class AutoIngestJobsPanel extends javax.swing.JPanel implements ExplorerMa
indexOfColumn = getColumnIndexByName(Bundle.AutoIngestJobsNode_status_text());
if (indexOfColumn != INVALID_INDEX) {
outline.getColumnModel().getColumn(indexOfColumn).setPreferredWidth(INITIAL_STATUS_WIDTH);
outline.getColumnModel().getColumn(indexOfColumn).setCellRenderer(new StatusIconCellRenderer());
}
break;
default:

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2017 Basis Technology Corp.
* Copyright 2017-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -19,21 +19,22 @@
package org.sleuthkit.autopsy.experimental.autoingest;
import java.awt.Component;
import java.lang.reflect.InvocationTargetException;
import javax.swing.ImageIcon;
import javax.swing.JTable;
import static javax.swing.SwingConstants.CENTER;
import org.openide.nodes.Node;
import org.openide.util.ImageUtilities;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.guiutils.GrayableCellRenderer;
import org.sleuthkit.autopsy.guiutils.StatusIconCellRenderer;
import org.sleuthkit.autopsy.datamodel.NodeProperty;
/**
* A JTable cell renderer that represents whether the priority value of a job
* has ever been increased, tick if prioritized nothing if not.
* A JTable and Outline view cell renderer that represents whether the priority
* value of a job has ever been increased, tick if prioritized nothing if not.
*/
class PrioritizedIconCellRenderer extends GrayableCellRenderer {
@Messages({
"PrioritizedIconCellRenderer.prioritized.tooltiptext=This job has been prioritized. The most recently prioritized job should be processed next.",
"PrioritizedIconCellRenderer.notPrioritized.tooltiptext=This job has not been prioritized."
@ -44,13 +45,25 @@ class PrioritizedIconCellRenderer extends GrayableCellRenderer {
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
setHorizontalAlignment(CENTER);
if ((value instanceof Integer)) {
if ((int) value == 0) {
setIcon(null);
Object switchValue = null;
if ((value instanceof NodeProperty)) {
//The Outline view has properties in the cell, the value contained in the property is what we want
try {
switchValue = ((Node.Property) value).getValue();
} catch (IllegalAccessException | InvocationTargetException ignored) {
//Unable to get the value from the NodeProperty no Icon will be displayed
}
} else {
//JTables contain the value we want directly in the cell
switchValue = value;
}
if (switchValue instanceof Integer && (int) switchValue != 0) {
setIcon(checkedIcon);
setToolTipText(org.openide.util.NbBundle.getMessage(PrioritizedIconCellRenderer.class, "PrioritizedIconCellRenderer.prioritized.tooltiptext"));
} else {
setIcon(null);
if (switchValue instanceof Integer) {
setToolTipText(org.openide.util.NbBundle.getMessage(PrioritizedIconCellRenderer.class, "PrioritizedIconCellRenderer.notPrioritized.tooltiptext"));
} else {
setIcon(checkedIcon);
setToolTipText(org.openide.util.NbBundle.getMessage(PrioritizedIconCellRenderer.class, "PrioritizedIconCellRenderer.prioritized.tooltiptext"));
}
}
grayCellIfTableNotEnabled(table, isSelected);

View File

@ -19,14 +19,11 @@
package org.sleuthkit.autopsy.experimental.objectdetection;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.opencv.core.CvException;
import org.opencv.core.Mat;
import org.opencv.core.MatOfByte;
@ -52,7 +49,6 @@ import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_OBJECT_DETECTED;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.ReadContentInputStream;
import org.sleuthkit.datamodel.TskCoreException;
/**
@ -61,6 +57,7 @@ import org.sleuthkit.datamodel.TskCoreException;
public class ObjectDetectectionFileIngestModule extends FileIngestModuleAdapter {
private final static Logger logger = Logger.getLogger(ObjectDetectectionFileIngestModule.class.getName());
private final static int MAX_FILE_SIZE = 100000000; //Max size of pictures to perform object detection on
private static final IngestModuleReferenceCounter refCounter = new IngestModuleReferenceCounter();
private long jobId;
private Map<String, CascadeClassifier> classifiers;
@ -100,15 +97,24 @@ public class ObjectDetectectionFileIngestModule extends FileIngestModuleAdapter
@Override
public ProcessResult process(AbstractFile file) {
if (!classifiers.isEmpty() && ImageUtils.isImageThumbnailSupported(file)) {
//Any image we can create a thumbnail for is one we should apply the classifiers to
InputStream inputStream = new ReadContentInputStream(file);
byte[] imageInMemory;
//Any image we can create a thumbnail for is one we should apply the classifiers to
if (file.getSize() > MAX_FILE_SIZE) {
//prevent it from allocating gigabytes of memory for extremely large files
logger.log(Level.INFO, "Encountered file " + file.getParentPath() + file.getName() + " with object id of "
+ file.getId() + " which exceeds max file size of " + MAX_FILE_SIZE + " bytes, with a size of " + file.getSize());
return IngestModule.ProcessResult.OK;
}
byte[] imageInMemory = new byte[(int) file.getSize()];
try {
imageInMemory = IOUtils.toByteArray(inputStream);
} catch (IOException ex) {
file.read(imageInMemory, 0, file.getSize());
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Unable to read image to byte array for performing object detection on " + file.getParentPath() + file.getName() + " with object id of " + file.getId(), ex);
return IngestModule.ProcessResult.ERROR;
}
Mat originalImage;
try {
originalImage = Highgui.imdecode(new MatOfByte(imageInMemory), Highgui.IMREAD_GRAYSCALE);
@ -121,7 +127,6 @@ public class ObjectDetectectionFileIngestModule extends FileIngestModuleAdapter
logger.log(Level.SEVERE, "Unexpected Exception encountered attempting to use OpenCV to decode picture: " + file.getParentPath() + file.getName() + " with object id of " + file.getId(), unexpectedException);
return IngestModule.ProcessResult.ERROR;
}
MatOfRect detectionRectangles = new MatOfRect(); //the rectangles which reprent the coordinates on the image for where objects were detected
for (String classifierKey : classifiers.keySet()) {
//apply each classifier to the file
@ -130,9 +135,9 @@ public class ObjectDetectectionFileIngestModule extends FileIngestModuleAdapter
} catch (CvException ignored) {
//The image was likely an image which we are unable to generate a thumbnail for, and the classifier was likely one where that is not acceptable
continue;
} catch (Exception unexpectedException) {
} catch (Exception unexpectedException) {
//hopefully an unnecessary generic exception catch but currently present to catch any exceptions OpenCv throws which may not be documented
logger.log(Level.SEVERE, "Unexpected Exception encountered for image " + file.getParentPath() + file.getName() + " with object id of " + file.getId() +" while trying to apply classifier " + classifierKey, unexpectedException);
logger.log(Level.SEVERE, "Unexpected Exception encountered for image " + file.getParentPath() + file.getName() + " with object id of " + file.getId() + " while trying to apply classifier " + classifierKey, unexpectedException);
continue;
}
@ -163,10 +168,14 @@ public class ObjectDetectectionFileIngestModule extends FileIngestModuleAdapter
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Failed to create blackboard artifact for '%s'.", file.getParentPath() + file.getName()), ex); //NON-NLS
detectionRectangles.release();
originalImage.release();
return IngestModule.ProcessResult.ERROR;
}
}
}
detectionRectangles.release();
originalImage.release();
}
return IngestModule.ProcessResult.OK;

View File

@ -127,7 +127,7 @@
<compile-dependency/>
<run-dependency>
<release-version>10</release-version>
<specification-version>10.11</specification-version>
<specification-version>10.12</specification-version>
</run-dependency>
</dependency>
<dependency>
@ -136,7 +136,7 @@
<compile-dependency/>
<run-dependency>
<release-version>3</release-version>
<specification-version>1.1</specification-version>
<specification-version>1.2</specification-version>
</run-dependency>
</dependency>
</module-dependencies>

View File

@ -1,7 +1,7 @@
Manifest-Version: 1.0
AutoUpdate-Show-In-Client: true
OpenIDE-Module: org.sleuthkit.autopsy.keywordsearch/6
OpenIDE-Module-Implementation-Version: 19
OpenIDE-Module-Implementation-Version: 20
OpenIDE-Module-Install: org/sleuthkit/autopsy/keywordsearch/Installer.class
OpenIDE-Module-Layer: org/sleuthkit/autopsy/keywordsearch/layer.xml
OpenIDE-Module-Localizing-Bundle: org/sleuthkit/autopsy/keywordsearch/Bundle.properties

View File

@ -119,7 +119,7 @@
<compile-dependency/>
<run-dependency>
<release-version>10</release-version>
<specification-version>10.11</specification-version>
<specification-version>10.12</specification-version>
</run-dependency>
</dependency>
<dependency>
@ -128,7 +128,7 @@
<compile-dependency/>
<run-dependency>
<release-version>3</release-version>
<specification-version>1.1</specification-version>
<specification-version>1.2</specification-version>
</run-dependency>
</dependency>
</module-dependencies>

View File

@ -83,9 +83,17 @@ class AdHocSearchChildFactory extends ChildFactory<KeyValue> {
.collect(Collectors.toList());
private final Collection<AdHocQueryRequest> queryRequests;
private final boolean saveResults;
AdHocSearchChildFactory(Collection<AdHocQueryRequest> queryRequests) {
/**
* Constructor
*
* @param queryRequests Query results
* @param saveResults Flag whether to save search results as KWS artifacts.
*/
AdHocSearchChildFactory(Collection<AdHocQueryRequest> queryRequests, boolean saveResults) {
this.queryRequests = queryRequests;
this.saveResults = saveResults;
}
/**
@ -223,7 +231,7 @@ class AdHocSearchChildFactory extends ChildFactory<KeyValue> {
//cannot reuse snippet in BlackboardResultWriter
//because for regex searches in UI we compress results by showing a content per regex once (even if multiple term hits)
//whereas in bb we write every hit per content separately
new BlackboardResultWriter(queryResults, queryRequest.getKeywordList().getName()).execute();
new BlackboardResultWriter(queryResults, queryRequest.getKeywordList().getName(), saveResults).execute();
return true;
}
@ -396,10 +404,12 @@ class AdHocSearchChildFactory extends ChildFactory<KeyValue> {
private final KeywordSearchQuery query;
private final QueryResults hits;
private static final int QUERY_DISPLAY_LEN = 40;
private final boolean saveResults;
BlackboardResultWriter(QueryResults hits, String listName) {
BlackboardResultWriter(QueryResults hits, String listName, boolean saveResults) {
this.hits = hits;
this.query = hits.getQuery();
this.saveResults = saveResults;
}
protected void finalizeWorker() {
@ -414,7 +424,7 @@ class AdHocSearchChildFactory extends ChildFactory<KeyValue> {
final String queryDisp = queryStr.length() > QUERY_DISPLAY_LEN ? queryStr.substring(0, QUERY_DISPLAY_LEN - 1) + " ..." : queryStr;
try {
progress = ProgressHandle.createHandle(NbBundle.getMessage(this.getClass(), "KeywordSearchResultFactory.progress.saving", queryDisp), () -> BlackboardResultWriter.this.cancel(true));
hits.process(progress, null, this, false);
hits.process(progress, null, this, false, saveResults);
} finally {
finalizeWorker();
}

View File

@ -74,8 +74,10 @@ class AdHocSearchDelegator {
/**
* Execute the keyword search based on keywords passed into constructor.
* Post results into a new DataResultViewer.
*
* @param saveResults Flag whether to save search results as KWS artifacts.
*/
public void execute() {
public void execute(boolean saveResults) {
Collection<AdHocQueryRequest> queryRequests = new ArrayList<>();
int queryID = 0;
StringBuilder queryConcat = new StringBuilder(); // concatenation of all query strings
@ -95,7 +97,7 @@ class AdHocSearchDelegator {
Node rootNode;
if (queryRequests.size() > 0) {
Children childNodes =
Children.create(new AdHocSearchChildFactory(queryRequests), true);
Children.create(new AdHocSearchChildFactory(queryRequests, saveResults), true);
rootNode = new AbstractNode(childNodes);
} else {

View File

@ -98,8 +98,10 @@ abstract class AdHocSearchPanel extends javax.swing.JPanel {
/**
* Performs the search using the selected keywords. Creates a
* DataResultTopComponent with the results.
*
* @param saveResults Flag whether to save search results as KWS artifacts.
*/
public void search() {
public void search(boolean saveResults) {
boolean isIngestRunning = IngestManager.getInstance().isIngestRunning();
if (filesIndexed == 0) {
@ -138,7 +140,7 @@ abstract class AdHocSearchPanel extends javax.swing.JPanel {
AdHocSearchDelegator man = new AdHocSearchDelegator(keywordLists, getDataSourcesSelected());
if (man.validate()) {
man.execute();
man.execute(saveResults);
} else {
KeywordSearchUtil.displayDialog(keywordSearchErrorDialogHeader, NbBundle.getMessage(this.getClass(),
"AbstractKeywordSearchPerformer.search.invalidSyntaxHeader"), KeywordSearchUtil.DIALOG_MESSAGE_TYPE.ERROR);

View File

@ -313,3 +313,7 @@ ExtractedContentPanel.pagesLabel.text=Page:
DropdownSingleTermSearchPanel.dataSourceCheckBox.text=Restrict search to the selected data sources:
DropdownListSearchPanel.dataSourceCheckBox.text=Restrict search to the selected data sources:
DropdownSingleTermSearchPanel.ingestIndexLabel.text=Files Indexed:
DropdownSingleTermSearchPanel.jSaveSearchResults.toolTipText=Perform keyword search without saving the results in the form of keyword hit artifacts
DropdownSingleTermSearchPanel.jSaveSearchResults.text=Save search results
DropdownListSearchPanel.jSaveSearchResults.toolTipText=Perform keyword search without saving the results in the form of keyword hit artifacts
DropdownListSearchPanel.jSaveSearchResults.text=Save search results

View File

@ -23,38 +23,41 @@
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="jSplitPane1" alignment="0" pref="400" max="32767" attributes="0"/>
<Group type="102" attributes="0">
<EmptySpace min="-2" pref="4" max="-2" attributes="0"/>
<Component id="searchAddButton" linkSize="1" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="manageListsButton" linkSize="1" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="ingestIndexLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<Component id="dataSourceCheckBox" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
<Component id="jSplitPane1" alignment="0" pref="0" max="32767" attributes="0"/>
<Component id="jScrollPane1" max="32767" attributes="0"/>
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="dataSourceCheckBox" min="-2" max="-2" attributes="0"/>
<Component id="jSaveSearchResults" alignment="0" min="-2" max="-2" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Component id="searchAddButton" linkSize="1" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="manageListsButton" linkSize="1" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="ingestIndexLabel" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace min="0" pref="120" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="jSplitPane1" min="-2" pref="183" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="dataSourceCheckBox" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="jScrollPane1" min="-2" pref="65" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Component id="jSaveSearchResults" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="manageListsButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="searchAddButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="ingestIndexLabel" alignment="3" min="-2" pref="13" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<EmptySpace min="-2" pref="23" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
@ -226,5 +229,15 @@
</Component>
</SubComponents>
</Container>
<Component class="javax.swing.JCheckBox" name="jSaveSearchResults">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="DropdownListSearchPanel.jSaveSearchResults.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="DropdownListSearchPanel.jSaveSearchResults.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Form>

View File

@ -164,6 +164,8 @@ class DropdownListSearchPanel extends AdHocSearchPanel {
}
listsTableModel.resync();
updateIngestIndexLabel();
jSaveSearchResults.setSelected(true);
}
private void updateIngestIndexLabel() {
@ -206,6 +208,7 @@ class DropdownListSearchPanel extends AdHocSearchPanel {
dataSourceCheckBox = new javax.swing.JCheckBox();
jScrollPane1 = new javax.swing.JScrollPane();
dataSourceList = new javax.swing.JList<>();
jSaveSearchResults = new javax.swing.JCheckBox();
setFont(getFont().deriveFont(getFont().getStyle() & ~java.awt.Font.BOLD, 11));
@ -265,23 +268,26 @@ class DropdownListSearchPanel extends AdHocSearchPanel {
dataSourceList.setMinimumSize(new java.awt.Dimension(0, 200));
jScrollPane1.setViewportView(dataSourceList);
jSaveSearchResults.setText(org.openide.util.NbBundle.getMessage(DropdownListSearchPanel.class, "DropdownListSearchPanel.jSaveSearchResults.text")); // NOI18N
jSaveSearchResults.setToolTipText(org.openide.util.NbBundle.getMessage(DropdownListSearchPanel.class, "DropdownListSearchPanel.jSaveSearchResults.toolTipText")); // NOI18N
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jSplitPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup()
.addGap(4, 4, 4)
.addComponent(searchAddButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(manageListsButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(ingestIndexLabel)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addGroup(layout.createSequentialGroup()
.addComponent(dataSourceCheckBox)
.addGap(0, 0, Short.MAX_VALUE))
.addComponent(jSplitPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)
.addComponent(jScrollPane1)
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(dataSourceCheckBox)
.addComponent(jSaveSearchResults)
.addGroup(layout.createSequentialGroup()
.addComponent(searchAddButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(manageListsButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(ingestIndexLabel)))
.addGap(0, 120, Short.MAX_VALUE))
);
layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {manageListsButton, searchAddButton});
@ -290,16 +296,18 @@ class DropdownListSearchPanel extends AdHocSearchPanel {
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(jSplitPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 183, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(dataSourceCheckBox)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 65, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(jSaveSearchResults)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(manageListsButton)
.addComponent(searchAddButton)
.addComponent(ingestIndexLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 13, javax.swing.GroupLayout.PREFERRED_SIZE))
.addContainerGap())
.addGap(23, 23, 23))
);
}// </editor-fold>//GEN-END:initComponents
@ -319,6 +327,7 @@ class DropdownListSearchPanel extends AdHocSearchPanel {
private javax.swing.JCheckBox dataSourceCheckBox;
private javax.swing.JList<String> dataSourceList;
private javax.swing.JLabel ingestIndexLabel;
private javax.swing.JCheckBox jSaveSearchResults;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JSplitPane jSplitPane1;
private javax.swing.JTable keywordsTable;
@ -333,7 +342,7 @@ class DropdownListSearchPanel extends AdHocSearchPanel {
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
try {
search();
search(jSaveSearchResults.isSelected());
} finally {
setCursor(null);
}

View File

@ -59,21 +59,22 @@
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="5" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Group type="102" attributes="0">
<Component id="exactRadioButton" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="substringRadioButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="regexRadioButton" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="dataSourceCheckBox" min="-2" max="-2" attributes="0"/>
<Component id="jScrollPane1" min="-2" pref="297" max="-2" attributes="0"/>
<Component id="jSaveSearchResults" min="-2" max="-2" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Component id="searchButton" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="ingestIndexLabel" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="keywordTextField" alignment="0" min="-2" pref="296" max="-2" attributes="0"/>
<Component id="dataSourceCheckBox" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="32767" attributes="0"/>
</Group>
@ -83,23 +84,25 @@
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="keywordTextField" max="32767" attributes="0"/>
<Component id="keywordTextField" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="exactRadioButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="substringRadioButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="regexRadioButton" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="dataSourceCheckBox" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="jScrollPane1" min="-2" pref="61" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="jSaveSearchResults" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="searchButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="ingestIndexLabel" alignment="3" min="-2" pref="13" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<EmptySpace pref="22" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
@ -217,5 +220,15 @@
</Property>
</Properties>
</Component>
<Component class="javax.swing.JCheckBox" name="jSaveSearchResults">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="DropdownSingleTermSearchPanel.jSaveSearchResults.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="DropdownSingleTermSearchPanel.jSaveSearchResults.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Form>

View File

@ -120,6 +120,8 @@ public class DropdownSingleTermSearchPanel extends AdHocSearchPanel {
};
ingestRunning = IngestManager.getInstance().isIngestRunning();
updateIngestIndexLabel();
jSaveSearchResults.setSelected(true);
IngestManager.getInstance().addIngestJobEventListener(new PropertyChangeListener() {
@Override
@ -221,6 +223,7 @@ public class DropdownSingleTermSearchPanel extends AdHocSearchPanel {
jScrollPane1 = new javax.swing.JScrollPane();
dataSourceList = new javax.swing.JList<>();
ingestIndexLabel = new javax.swing.JLabel();
jSaveSearchResults = new javax.swing.JCheckBox();
org.openide.awt.Mnemonics.setLocalizedText(cutMenuItem, org.openide.util.NbBundle.getMessage(DropdownSingleTermSearchPanel.class, "DropdownSearchPanel.cutMenuItem.text")); // NOI18N
rightClickMenu.add(cutMenuItem);
@ -280,6 +283,9 @@ public class DropdownSingleTermSearchPanel extends AdHocSearchPanel {
ingestIndexLabel.setFont(ingestIndexLabel.getFont().deriveFont(ingestIndexLabel.getFont().getStyle() & ~java.awt.Font.BOLD, 10));
org.openide.awt.Mnemonics.setLocalizedText(ingestIndexLabel, org.openide.util.NbBundle.getMessage(DropdownSingleTermSearchPanel.class, "DropdownSingleTermSearchPanel.ingestIndexLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(jSaveSearchResults, org.openide.util.NbBundle.getMessage(DropdownSingleTermSearchPanel.class, "DropdownSingleTermSearchPanel.jSaveSearchResults.text")); // NOI18N
jSaveSearchResults.setToolTipText(org.openide.util.NbBundle.getMessage(DropdownSingleTermSearchPanel.class, "DropdownSingleTermSearchPanel.jSaveSearchResults.toolTipText")); // NOI18N
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
@ -293,34 +299,37 @@ public class DropdownSingleTermSearchPanel extends AdHocSearchPanel {
.addComponent(substringRadioButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(regexRadioButton))
.addComponent(dataSourceCheckBox)
.addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 297, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(jSaveSearchResults)
.addGroup(layout.createSequentialGroup()
.addComponent(searchButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGap(18, 18, 18)
.addComponent(ingestIndexLabel))
.addComponent(keywordTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 296, javax.swing.GroupLayout.PREFERRED_SIZE))
.addComponent(keywordTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 296, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(dataSourceCheckBox))
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(keywordTextField, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(keywordTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(exactRadioButton)
.addComponent(substringRadioButton)
.addComponent(regexRadioButton))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(dataSourceCheckBox)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 61, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(jSaveSearchResults)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(searchButton)
.addComponent(ingestIndexLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 13, javax.swing.GroupLayout.PREFERRED_SIZE))
.addContainerGap())
.addContainerGap(22, Short.MAX_VALUE))
);
}// </editor-fold>//GEN-END:initComponents
@ -340,7 +349,7 @@ public class DropdownSingleTermSearchPanel extends AdHocSearchPanel {
*/
private void keywordTextFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_keywordTextFieldActionPerformed
try {
search();
search(jSaveSearchResults.isSelected());
} catch (Exception e) {
LOGGER.log(Level.SEVERE, "Error performing ad hoc single keyword search", e); //NON-NLS
}
@ -431,6 +440,7 @@ public class DropdownSingleTermSearchPanel extends AdHocSearchPanel {
private javax.swing.JList<String> dataSourceList;
private javax.swing.JRadioButton exactRadioButton;
private javax.swing.JLabel ingestIndexLabel;
private javax.swing.JCheckBox jSaveSearchResults;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JTextField keywordTextField;
private javax.swing.JMenuItem pasteMenuItem;

View File

@ -563,7 +563,7 @@ final class IngestSearchRunner {
subProgresses[keywordsSearched].progress(keywordList.getName() + ": " + queryDisplayStr, unitProgress);
// Create blackboard artifacts
newResults.process(null, subProgresses[keywordsSearched], this, keywordList.getIngestMessages());
newResults.process(null, subProgresses[keywordsSearched], this, keywordList.getIngestMessages(), true);
} //if has results

View File

@ -570,7 +570,9 @@ public final class KeywordSearchIngestModule implements FileIngestModule {
putIngestStatus(jobId, aFile.getId(), IngestStatus.SKIPPED_ERROR_TEXTEXTRACT);
}
if ((wasTextAdded == false) && (aFile.getNameExtension().equalsIgnoreCase("txt"))) {
if ((wasTextAdded == false) && (aFile.getNameExtension().equalsIgnoreCase("txt") && !(aFile.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.CARVED)))) {
//Carved Files should be the only type of unallocated files capable of a txt extension and
//should be ignored by the TextFileExtractor because they may contain more than one text encoding
try {
if (Ingester.getDefault().indexText(txtFileExtractor, aFile, context)) {
putIngestStatus(jobId, aFile.getId(), IngestStatus.TEXT_INGESTED);

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2017 Basis Technology Corp.
* Copyright 2011-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -32,14 +32,18 @@ import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.params.CursorMarkParams;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.EscapeUtil;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.Version;
import org.sleuthkit.datamodel.Blackboard;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskException;
@ -203,15 +207,44 @@ class LuceneQuery implements KeywordSearchQuery {
* @param listName The name of the keyword list that contained the
* keyword for which the hit was found.
*
*
* @return The newly created artifact or null if there was a problem
* creating it.
* @return The newly created artifact, or null if one wasn't created due to
* either the artifact already existing or an error while trying to
* create it.
*/
@Override
public BlackboardArtifact postKeywordHitToBlackboard(Content content, Keyword foundKeyword, KeywordHit hit, String snippet, String listName) {
final String MODULE_NAME = KeywordSearchModuleFactory.getModuleName();
Collection<BlackboardAttribute> attributes = new ArrayList<>();
List<BlackboardAttribute> attributesList = new ArrayList<>();
attributesList.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD, MODULE_NAME, foundKeyword.getSearchTerm()));
if (originalKeyword != null) {
BlackboardAttribute.ATTRIBUTE_TYPE selType = originalKeyword.getArtifactAttributeType();
if (selType != null) {
attributesList.add(new BlackboardAttribute(selType, MODULE_NAME, foundKeyword.getSearchTerm()));
}
if (originalKeyword.searchTermIsWholeWord()) {
attributesList.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE, MODULE_NAME, KeywordSearch.QueryType.LITERAL.ordinal()));
} else {
attributesList.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE, MODULE_NAME, KeywordSearch.QueryType.SUBSTRING.ordinal()));
}
}
if (StringUtils.isNotBlank(listName)) {
attributesList.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, listName));
}
try {
SleuthkitCase tskCase = Case.getCurrentCaseThrows().getSleuthkitCase();
Blackboard blackboard = tskCase.getBlackboard();
if (blackboard.artifactExists(content, BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT, attributesList)) {
return null;
}
} catch (NoCurrentCaseException | TskCoreException ex) {
logger.log(Level.SEVERE, String.format(
"A problem occurred while checking for existing artifacts for file '%s' (id=%d).",
content.getName(), content.getId()), ex); //NON-NLS
}
BlackboardArtifact bba;
try {
bba = content.newArtifact(ARTIFACT_TYPE.TSK_KEYWORD_HIT);
@ -221,32 +254,15 @@ class LuceneQuery implements KeywordSearchQuery {
}
if (snippet != null) {
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW, MODULE_NAME, snippet));
}
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD, MODULE_NAME, foundKeyword.getSearchTerm()));
if (StringUtils.isNotBlank(listName)) {
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, listName));
}
if (originalKeyword != null) {
BlackboardAttribute.ATTRIBUTE_TYPE selType = originalKeyword.getArtifactAttributeType();
if (selType != null) {
attributes.add(new BlackboardAttribute(selType, MODULE_NAME, foundKeyword.getSearchTerm()));
}
if (originalKeyword.searchTermIsWholeWord()) {
attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE, MODULE_NAME, KeywordSearch.QueryType.LITERAL.ordinal()));
} else {
attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE, MODULE_NAME, KeywordSearch.QueryType.SUBSTRING.ordinal()));
}
attributesList.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW, MODULE_NAME, snippet));
}
hit.getArtifactID().ifPresent(artifactID
-> attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, artifactID))
-> attributesList.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, artifactID))
);
try {
bba.addAttributes(attributes); //write out to bb
bba.addAttributes(attributesList); //write out to bb
return bba;
} catch (TskCoreException e) {
logger.log(Level.WARNING, "Error adding bb attributes to artifact", e); //NON-NLS

View File

@ -141,9 +141,10 @@ class QueryResults {
* @param notifyInbox Whether or not to write a message to the ingest
* messages inbox if there is a keyword hit in the text
* exrtacted from the text source object.
* @param saveResults Flag whether to save search results as KWS artifacts.
*
*/
void process(ProgressHandle progress, ProgressContributor subProgress, SwingWorker<?, ?> worker, boolean notifyInbox) {
void process(ProgressHandle progress, ProgressContributor subProgress, SwingWorker<?, ?> worker, boolean notifyInbox, boolean saveResults) {
/*
* Initialize the progress indicator to the number of keywords that will
* be processed.
@ -218,22 +219,24 @@ class QueryResults {
} catch (TskCoreException | NoCurrentCaseException tskCoreException) {
logger.log(Level.SEVERE, "Failed to get text source object for ", tskCoreException); //NON-NLS
}
if (saveResults) {
/*
* Post an artifact for the hit to the blackboard.
*/
BlackboardArtifact artifact = query.postKeywordHitToBlackboard(content, keyword, hit, snippet, query.getKeywordList().getName());
/*
* Post an artifact for the hit to the blackboard.
*/
BlackboardArtifact artifact = query.postKeywordHitToBlackboard(content, keyword, hit, snippet, query.getKeywordList().getName());
/*
* Send an ingest inbox message for the hit.
*/
if (null != artifact) {
hitArtifacts.add(artifact);
if (notifyInbox) {
try {
writeSingleFileInboxMessage(artifact, content);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error sending message to ingest messages inbox", ex); //NON-NLS
/*
* Send an ingest inbox message for the hit.
*/
if (null != artifact) {
hitArtifacts.add(artifact);
if (notifyInbox) {
try {
writeSingleFileInboxMessage(artifact, content);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error sending message to ingest messages inbox", ex); //NON-NLS
}
}
}
}

View File

@ -36,7 +36,6 @@ import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.params.CursorMarkParams;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
@ -50,11 +49,13 @@ import static org.sleuthkit.autopsy.keywordsearch.TermsComponentQuery.KEYWORD_SE
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Account;
import org.sleuthkit.datamodel.AccountFileInstance;
import org.sleuthkit.datamodel.Blackboard;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
@ -74,7 +75,7 @@ import org.sleuthkit.datamodel.TskData;
*/
final class RegexQuery implements KeywordSearchQuery {
public static final Logger LOGGER = Logger.getLogger(RegexQuery.class.getName());
public static final Logger logger = Logger.getLogger(RegexQuery.class.getName());
/**
* Lucene regular expressions do not support the following Java predefined
@ -213,7 +214,7 @@ final class RegexQuery implements KeywordSearchQuery {
hitsForKeyword.add(hit);
}
} catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Error creating keyword hits", ex); //NON-NLS
logger.log(Level.SEVERE, "Error creating keyword hits", ex); //NON-NLS
}
}
@ -223,7 +224,7 @@ final class RegexQuery implements KeywordSearchQuery {
}
cursorMark = nextCursorMark;
} catch (KeywordSearchModuleException ex) {
LOGGER.log(Level.SEVERE, "Error executing Regex Solr Query: " + keywordString, ex); //NON-NLS
logger.log(Level.SEVERE, "Error executing Regex Solr Query: " + keywordString, ex); //NON-NLS
MessageNotifyUtil.Notify.error(NbBundle.getMessage(Server.class, "Server.query.exception.msg", keywordString), ex.getCause().getMessage());
}
}
@ -437,16 +438,16 @@ final class RegexQuery implements KeywordSearchQuery {
* @param listName The name of the keyword list that contained the
* keyword for which the hit was found.
*
*
* @return The newly created artifact or null if there was a problem
* creating it.
* @return The newly created artifact, or null if one wasn't created due to
* either the artifact already existing or an error while trying to
* create it.
*/
@Override
public BlackboardArtifact postKeywordHitToBlackboard(Content content, Keyword foundKeyword, KeywordHit hit, String snippet, String listName) {
final String MODULE_NAME = KeywordSearchModuleFactory.getModuleName();
if (content == null) {
LOGGER.log(Level.WARNING, "Error adding artifact for keyword hit to blackboard"); //NON-NLS
logger.log(Level.WARNING, "Error adding artifact for keyword hit to blackboard"); //NON-NLS
return null;
}
@ -458,41 +459,52 @@ final class RegexQuery implements KeywordSearchQuery {
return null;
}
List<BlackboardAttribute> attributesList = new ArrayList<>();
attributesList.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD, MODULE_NAME, foundKeyword.getSearchTerm()));
attributesList.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE, MODULE_NAME, KeywordSearch.QueryType.REGEX.ordinal()));
attributesList.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP, MODULE_NAME, getQueryString()));
if (StringUtils.isNotBlank(listName)) {
attributesList.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, listName));
}
try {
SleuthkitCase tskCase = Case.getCurrentCaseThrows().getSleuthkitCase();
Blackboard blackboard = tskCase.getBlackboard();
if (blackboard.artifactExists(content, BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT, attributesList)) {
return null;
}
} catch (NoCurrentCaseException | TskCoreException ex) {
logger.log(Level.SEVERE, String.format(
"A problem occurred while checking for existing artifacts for file '%s' (id=%d).",
content.getName(), content.getId()), ex); //NON-NLS
}
/*
* Create a "plain vanilla" keyword hit artifact with keyword and
* regex attributes
*/
BlackboardArtifact newArtifact;
Collection<BlackboardAttribute> attributes = new ArrayList<>();
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD, MODULE_NAME, foundKeyword.getSearchTerm()));
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP, MODULE_NAME, getQueryString()));
try {
newArtifact = content.newArtifact(ARTIFACT_TYPE.TSK_KEYWORD_HIT);
} catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Error adding artifact for keyword hit to blackboard", ex); //NON-NLS
logger.log(Level.SEVERE, "Error adding artifact for keyword hit to blackboard", ex); //NON-NLS
return null;
}
if (StringUtils.isNotBlank(listName)) {
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, listName));
}
if (snippet != null) {
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW, MODULE_NAME, snippet));
attributesList.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW, MODULE_NAME, snippet));
}
hit.getArtifactID().ifPresent(artifactID
-> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, artifactID))
-> attributesList.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, artifactID))
);
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE, MODULE_NAME, KeywordSearch.QueryType.REGEX.ordinal()));
try {
newArtifact.addAttributes(attributes);
newArtifact.addAttributes(attributesList);
return newArtifact;
} catch (TskCoreException e) {
LOGGER.log(Level.SEVERE, "Error adding bb attributes for terms search artifact", e); //NON-NLS
logger.log(Level.SEVERE, "Error adding bb attributes for terms search artifact", e); //NON-NLS
return null;
}
}
@ -502,7 +514,7 @@ final class RegexQuery implements KeywordSearchQuery {
final String MODULE_NAME = KeywordSearchModuleFactory.getModuleName();
if (originalKeyword.getArtifactAttributeType() != ATTRIBUTE_TYPE.TSK_CARD_NUMBER) {
LOGGER.log(Level.SEVERE, "Keyword hit is not a credit card number"); //NON-NLS
logger.log(Level.SEVERE, "Keyword hit is not a credit card number"); //NON-NLS
return;
}
/*
@ -525,13 +537,13 @@ final class RegexQuery implements KeywordSearchQuery {
if (ccnAttribute == null || StringUtils.isBlank(ccnAttribute.getValueString())) {
if (hit.isArtifactHit()) {
LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for artifact keyword hit: term = %s, snippet = '%s', artifact id = %d", foundKeyword.getSearchTerm(), hit.getSnippet(), hit.getArtifactID().get())); //NON-NLS
logger.log(Level.SEVERE, String.format("Failed to parse credit card account number for artifact keyword hit: term = %s, snippet = '%s', artifact id = %d", foundKeyword.getSearchTerm(), hit.getSnippet(), hit.getArtifactID().get())); //NON-NLS
} else {
try {
LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for content keyword hit: term = %s, snippet = '%s', object id = %d", foundKeyword.getSearchTerm(), hit.getSnippet(), hit.getContentID())); //NON-NLS
logger.log(Level.SEVERE, String.format("Failed to parse credit card account number for content keyword hit: term = %s, snippet = '%s', object id = %d", foundKeyword.getSearchTerm(), hit.getSnippet(), hit.getContentID())); //NON-NLS
} catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for content keyword hit: term = %s, snippet = '%s' ", foundKeyword.getSearchTerm(), hit.getSnippet())); //NON-NLS
LOGGER.log(Level.SEVERE, "There was a error getting contentID for keyword hit.", ex); //NON-NLS
logger.log(Level.SEVERE, String.format("Failed to parse credit card account number for content keyword hit: term = %s, snippet = '%s' ", foundKeyword.getSearchTerm(), hit.getSnippet())); //NON-NLS
logger.log(Level.SEVERE, "There was a error getting contentID for keyword hit.", ex); //NON-NLS
}
}
return;
@ -599,7 +611,7 @@ final class RegexQuery implements KeywordSearchQuery {
ccAccountInstance.addAttributes(attributes);
} catch (TskCoreException | NoCurrentCaseException ex) {
LOGGER.log(Level.SEVERE, "Error creating CCN account instance", ex); //NON-NLS
logger.log(Level.SEVERE, "Error creating CCN account instance", ex); //NON-NLS
}

View File

@ -17,8 +17,9 @@
* limitations under the License.
*/
package org.sleuthkit.autopsy.keywordsearch;
import java.io.IOException;
import java.io.InputStream;
import java.io.BufferedInputStream;
import java.io.Reader;
import java.util.logging.Level;
import org.apache.tika.parser.txt.CharsetDetector;
@ -53,15 +54,16 @@ final class TextFileExtractor extends ContentTextExtractor {
@Override
public Reader getReader(Content source) throws TextExtractorException {
CharsetDetector detector = new CharsetDetector();
ReadContentInputStream stream = new ReadContentInputStream(source);
//wrap stream in a BufferedInputStream so that it supports the mark/reset methods necessary for the CharsetDetector
InputStream stream = new BufferedInputStream(new ReadContentInputStream(source));
try {
detector.setText(stream);
} catch (IOException ex) {
throw new TextExtractorException("Unable to get string from detected text in UnicodeTextExtractor", ex);
throw new TextExtractorException("Unable to get string from detected text in TextFileExtractor", ex);
}
CharsetMatch match = detector.detect();
if (match.getConfidence() < MIN_MATCH_CONFIDENCE) {
throw new TextExtractorException("Text does not match any character set with a high enough confidence for UnicodeTextExtractor");
throw new TextExtractorException("Text does not match any character set with a high enough confidence for TextFileExtractor");
}
return match.getReader();

View File

@ -1,29 +1,25 @@
---------------- VERSION 4.8.0 --------------
New Features:
- The case tree view can now be grouped by data source.
- Added a common files search tool that finds all instances of a file in a case.
- Text extraction optionally includes optical character recognition (OCR).
- Data source(s) filter added to ad hoc keyword search and file search by
attributes.
- SQLite tables can be now be exported to CSV files.
- User defined tags now appear first in tagging menus.
- Eliminated one tagging sub menu layer for faster tagging.
- Added Replace Tag item to tagging menus (shortcut for delete tag, add tag).
- The Other Occurrences content viewer now shows matches in the current case.
- A listing of cases in the central repository is displayed by the
central repository options panel.
- An interesting file artifact is now created when a "zip bomb" is detected.
- Text and queries sent to Solr are now normalized to handle diacritics,
ligatures, narrow and wide width Japanese characters, etc.
- An object detection ingest module that uses OpenCV and user-supplied
classifiers has been added to the "experimental" Net Beans Module (NBM).
- A data source processor that runs Volatility on a memory image has been
added to the "experimental" NBM.
- Comments can be added to all files (file correlation properties) recorded
in the central repository using a results view context menu item.
- Comments can be added to all correlation properties recorded
in the central repository using an Other Occurrences results content viewer
context menu item.
- Data Source Grouping:
-- The case tree view can now be grouped by data source.
-- Keyword and file search can now be restricted to a data source.
- Central Repository / Corrrelation:
-- New common files search feature that finds files that exist in multiple devices in the same case.
-- The Other Occurrences content viewer now shows matches in the current case (in addition to central repository).
-- Central repository options panel now shows cases that are in repo.
- A comment about a file can be created and saved in the central repository so that future cases and see it.
- Keyword Search:
-- Can enable OCR text extraction of PDF and JPG files using Tesseract.
-- Keyword search module normalizes Unicode text.
-- Keyword search module uses ICU to convert text files that do not have a BOM.
- Tagging:
-- Tagging menu changed to have user defined tags at top and "quick tag" removed one level of menus.
-- New "Replace Tag" feature to change the tag on an item.
- Other:
-- SQLite tables can be now be exported to CSV files.
-- An interesting file artifact is now created when a "zip bomb" is detected.
-- An object detection ingest module was added to the Experimental module. It requires an OpenCV trained model.
Bug Fixes:
- Expanding the case tree is more efficient.

View File

@ -1,6 +1,6 @@
Manifest-Version: 1.0
OpenIDE-Module: org.sleuthkit.autopsy.recentactivity/6
OpenIDE-Module-Implementation-Version: 15
OpenIDE-Module-Implementation-Version: 16
OpenIDE-Module-Layer: org/sleuthkit/autopsy/recentactivity/layer.xml
OpenIDE-Module-Localizing-Bundle: org/sleuthkit/autopsy/recentactivity/Bundle.properties
OpenIDE-Module-Requires:

View File

@ -60,7 +60,7 @@
<compile-dependency/>
<run-dependency>
<release-version>10</release-version>
<specification-version>10.11</specification-version>
<specification-version>10.12</specification-version>
</run-dependency>
</dependency>
</module-dependencies>

View File

@ -1,3 +1,3 @@
<project name="TSK_VERSION">
<property name="TSK_VERSION" value="4.6.1"/>
<property name="TSK_VERSION" value="4.6.2"/>
</project>

View File

@ -6,8 +6,8 @@ app.name=${branding.token}
### if left unset, version will default to today's date
app.version=4.8.0
### build.type must be one of: DEVELOPMENT, RELEASE
build.type=RELEASE
#build.type=DEVELOPMENT
#build.type=RELEASE
build.type=DEVELOPMENT
project.org.netbeans.progress=org-netbeans-api-progress
project.org.sleuthkit.autopsy.experimental=Experimental

View File

@ -36,7 +36,7 @@
<compile-dependency/>
<run-dependency>
<release-version>10</release-version>
<specification-version>10.11</specification-version>
<specification-version>10.12</specification-version>
</run-dependency>
</dependency>
<dependency>

View File

@ -24,7 +24,7 @@ errorLog () {
exit 1
}
Verify we can find the script
#Verify we can find the script
if [[ -x "$AUTOPSY_BIN" ]]; then
infoLog "Autopsy found"
else
@ -66,7 +66,11 @@ showAndReadOptions () {
echo [$x] "${word}"
x=$((x + 1))
done
read option
read -n 1 option
if [[ $option = "" ]] || ! [[ "$option" =~ ^[0-9]+$ ]]; then
echo "Please choose a valid option"
showAndReadOptions
fi
}

View File

@ -2,7 +2,7 @@
# Verifies programs are installed and copies native code into the Autopsy folder structure
TSK_VERSION=4.6.1
TSK_VERSION=4.6.2
# Verify PhotoRec was installed
photorec_filepath=/usr/bin/photorec