Merge pull request #4221 from esaunders/develop

Merge release-4.9.0 into develop
This commit is contained in:
esaunders
2018-10-15 16:26:19 -04:00
committed by GitHub
43 changed files with 878 additions and 286 deletions
+1 -1
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: 24
OpenIDE-Module-Implementation-Version: 25
OpenIDE-Module-Requires: org.openide.windows.WindowManager
AutoUpdate-Show-In-Client: true
AutoUpdate-Essential-Module: true
+1 -1
View File
@@ -47,5 +47,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.12
spec.version.base=10.13
@@ -127,7 +127,10 @@ public class EamArtifactUtil {
|| BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID() == artifactTypeID)) {
// Lower-case this to normalize domains
value = bbArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN)).getValueString();
BlackboardAttribute attribute = bbArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN));
if (attribute != null) {
value = attribute.getValueString();
}
} else if (correlationType.getId() == CorrelationAttributeInstance.PHONE_TYPE_ID
&& (BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT.getTypeID() == artifactTypeID
|| BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG.getTypeID() == artifactTypeID
@@ -171,7 +174,7 @@ public class EamArtifactUtil {
return null;
}
if (null != value) {
if ((null != value) && (value.isEmpty() == false)) {
return makeCorrelationAttributeInstanceUsingTypeValue(bbArtifact, correlationType, value);
} else {
return null;
@@ -1,16 +1,16 @@
/*
*
*
* 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.
@@ -29,14 +29,13 @@ import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
import org.sleuthkit.autopsy.corecomponents.DelayedLoadChildNodesOnTreeExpansion;
/**
* <code>DataResultViewerTable</code> which overrides the default column
* header width calculations. The <code>CommonAttributesSearchResultsViewerTable</code>
* presents multiple tiers of data which are not always present and it may not
* make sense to try to calculate the column widths for such tables by sampling
* rows and looking for wide cells. Rather, we just pick some reasonable values.
* <code>DataResultViewerTable</code> which overrides the default column header
* width calculations. The <code>CommonAttributesSearchResultsViewerTable</code>
* presents multiple tiers of data which are not always present and it may not
* make sense to try to calculate the column widths for such tables by sampling
* rows and looking for wide cells. Rather, we just pick some reasonable values.
*/
public class CommonAttributesSearchResultsViewerTable extends DataResultViewerTable {
@@ -44,7 +43,7 @@ public class CommonAttributesSearchResultsViewerTable extends DataResultViewerTa
private static final long serialVersionUID = 1L;
private static final Logger LOGGER = Logger.getLogger(CommonAttributesSearchResultsViewerTable.class.getName());
private static final int DEFAULT_WIDTH = 100;
static {
@@ -60,19 +59,20 @@ public class CommonAttributesSearchResultsViewerTable extends DataResultViewerTa
COLUMN_WIDTHS = Collections.unmodifiableMap(map);
}
/**
* Implements a DataResultViewerTable which constructs a tabular result viewer that
* displays the children of the given root node using an OutlineView. The explorer
* manager will be discovered at runtime.
*
* Adds a TreeExpansionsListener to the outlineView to receive tree expansion events
* which dynamically loads children nodes when requested.
* Implements a DataResultViewerTable which constructs a tabular result
* viewer that displays the children of the given root node using an
* OutlineView. The explorer manager will be discovered at runtime.
*
* Adds a TreeExpansionsListener to the outlineView to receive tree
* expansion events which dynamically loads children nodes when requested.
*/
public CommonAttributesSearchResultsViewerTable() {
super();
outlineView.addTreeExpansionListener(new DelayedLoadChildNodesOnTreeExpansion());
addTreeExpansionListener(new InstanceCountNodeTreeExpansionListener());
}
@NbBundle.Messages({
"CommonFilesSearchResultsViewerTable.noDescText= ",
"CommonFilesSearchResultsViewerTable.filesColLbl=Files",
@@ -96,8 +96,8 @@ public class CommonAttributesSearchResultsViewerTable extends DataResultViewerTa
final String headerValue = column.getHeaderValue().toString();
final Integer defaultWidth = COLUMN_WIDTHS.get(headerValue);
if(defaultWidth == null){
if (defaultWidth == null) {
column.setPreferredWidth(DEFAULT_WIDTH);
LOGGER.log(Level.SEVERE, String.format("Tried to set width on a column not supported by the CommonFilesSearchResultsViewerTable: %s", headerValue));
} else {
@@ -1,16 +1,16 @@
/*
*
*
* 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.
@@ -37,7 +37,7 @@ import org.sleuthkit.autopsy.datamodel.NodeProperty;
* Node used to indicate the number of matches found with the MD5 children of
* this Node.
*/
final public class InstanceCountNode extends DisplayableItemNode {
public final class InstanceCountNode extends DisplayableItemNode {
private static final Logger logger = Logger.getLogger(InstanceCountNode.class.getName());
@@ -74,11 +74,10 @@ final public class InstanceCountNode extends DisplayableItemNode {
}
/**
* Refresh the node, by dynamically loading in the children when called, and
* calling the CommonAttributeValueNodeFactory to generate nodes for the
* children in attributeValues.
* Creates the Children of this node. By doing this here instead of in the
* constructor, lazy creation of the Children is made possible.
*/
public void refresh() {
void createChildren() {
attributeValues.displayDelayedMetadata();
setChildren(Children.create(new CommonAttributeValueNodeFactory(attributeValues.getMetadataList()), true));
}
@@ -1,54 +1,54 @@
/*
*
*
* 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.corecomponents;
package org.sleuthkit.autopsy.commonfilesearch;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeExpansionListener;
import org.openide.explorer.view.Visualizer;
import org.openide.nodes.Node;
import org.sleuthkit.autopsy.corecomponents.TableFilterNode;
import org.sleuthkit.autopsy.directorytree.DataResultFilterNode;
/**
* A tree expansion listener that will trigger a recreation of childs through
* its child factory on re-expansion of a node (causes to recreate the
* ChildFactory for this purpose.).
* A tree expansion listener used to do lazy creation of the Childfren of an
* InstanceCountNode when the node is expanded.
*/
public final class DelayedLoadChildNodesOnTreeExpansion implements TreeExpansionListener {
/**
* A flag for avoiding endless recursion inside the expansion listener that
* could trigger collapsing and (re-)expanding nodes again.
* @param event
*/
final class InstanceCountNodeTreeExpansionListener implements TreeExpansionListener {
@Override
public synchronized void treeCollapsed(final TreeExpansionEvent event) {
// Do nothing on collapse. Netbeans should manage nodes falling out of scope and GC.
}
@Override
public synchronized void treeExpanded(final TreeExpansionEvent event) {
Node eventNode = Visualizer.findNode(event.getPath().getLastPathComponent());
final Node eventNode = Visualizer.findNode(event.getPath().getLastPathComponent());
if (eventNode instanceof TableFilterNode) {
final TableFilterNode node = (TableFilterNode) eventNode;
node.refresh();
final TableFilterNode tableFilterNode = (TableFilterNode) eventNode;
final DataResultFilterNode dataResultFilterNode = tableFilterNode.getLookup().lookup(DataResultFilterNode.class);
if (dataResultFilterNode != null) {
final InstanceCountNode instanceCountNode = dataResultFilterNode.getLookup().lookup(InstanceCountNode.class);
if (instanceCountNode != null) {
instanceCountNode.createChildren();
}
}
}
}
}
@@ -53,7 +53,6 @@ import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.coreutils.SqliteUtil;
/**
* A file content viewer for SQLite database files.
@@ -16,7 +16,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.coreutils;
package org.sleuthkit.autopsy.contentviewers;
import java.io.File;
import java.io.IOException;
@@ -34,7 +34,7 @@ import org.sleuthkit.datamodel.TskCoreException;
* Sqlite utility class. Find and copy metafiles, write sqlite abstract files to
* temp directory, and generate unique temp directory paths.
*/
public final class SqliteUtil {
final class SqliteUtil {
private SqliteUtil() {
@@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2013-2017 Basis Technology Corp.
* Copyright 2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -190,3 +190,5 @@ ViewPreferencesPanel.hideRejectedResultsCheckbox.text=Hide rejected results
ViewPreferencesPanel.hideOtherUsersTagsLabel.text=Hide other users' tags in the:
ViewPreferencesPanel.centralRepoLabel.text=Do not use Central Repository for:
ViewPreferencesPanel.commentsOccurencesColumnsCheckbox.text=C(omments) and O(ccurences) columns to reduce loading times
ViewPreferencesPanel.deletedFilesLimitCheckbox.text=Limit to 10,000
ViewPreferencesPanel.deletedFilesLimitLabel.text=Limit number of deleted files displayed:
@@ -29,7 +29,6 @@
<Container class="org.openide.explorer.view.OutlineView" name="outlineView">
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new OutlineView(DataResultViewerTable.FIRST_COLUMN_LABEL);"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="4"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
@@ -46,6 +46,7 @@ import javax.swing.event.ChangeEvent;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.TableColumnModelEvent;
import javax.swing.event.TableColumnModelListener;
import javax.swing.event.TreeExpansionListener;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
@@ -265,6 +266,16 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
}
}
/**
* Adds a tree expansion listener to the OutlineView of this tabular results
* viewer.
*
* @param listener The listener
*/
protected void addTreeExpansionListener(TreeExpansionListener listener) {
outlineView.addTreeExpansionListener(listener);
}
/**
* Sets up the Outline view of this tabular result viewer by creating column
* headers based on the children of the current root node. The persisted
@@ -1036,7 +1047,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
);
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
protected org.openide.explorer.view.OutlineView outlineView;
private org.openide.explorer.view.OutlineView outlineView;
// End of variables declaration//GEN-END:variables
}
@@ -128,16 +128,6 @@ public class TableFilterNode extends FilterNode {
return null;
}
}
/**
* Refreshes the inner node, which depending on the actual node type that was wrapped
* could trigger a dynamic refresh of the children, if supported.
*/
void refresh() {
DataResultFilterNode innerNode = getLookup().lookup(DataResultFilterNode.class);
innerNode.refresh();
}
/**
* @return the column order key, which allows custom column ordering to be
@@ -79,65 +79,72 @@
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="hideKnownFilesLabel" alignment="0" min="-2" max="-2" attributes="0"/>
<Group type="103" alignment="0" groupAlignment="1" attributes="0">
<Group type="103" alignment="1" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="10" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="dataSourcesHideSlackCheckbox" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="viewsHideSlackCheckbox" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<Component id="hideSlackFilesLabel" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="1" attributes="0">
<EmptySpace min="-2" pref="10" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="dataSourcesHideKnownCheckbox" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="viewsHideKnownCheckbox" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</Group>
</Group>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="displayTimeLabel" alignment="0" min="-2" max="-2" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="10" pref="10" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="keepCurrentViewerRadioButton" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="useBestViewerRadioButton" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="useGMTTimeRadioButton" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="useLocalTimeRadioButton" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<Component id="selectFileLabel" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<Component id="hideOtherUsersTagsLabel" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="centralRepoLabel" alignment="0" min="-2" max="-2" attributes="0"/>
<Group type="102" attributes="0">
<EmptySpace min="10" pref="10" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="commentsOccurencesColumnsCheckbox" min="-2" max="-2" attributes="0"/>
<Component id="hideOtherUsersTagsCheckbox" min="-2" max="-2" attributes="0"/>
<Component id="deletedFilesLimitCheckbox" alignment="0" max="32767" attributes="0"/>
</Group>
</Group>
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="hideKnownFilesLabel" alignment="0" min="-2" max="-2" attributes="0"/>
<Group type="103" alignment="0" groupAlignment="1" attributes="0">
<Group type="103" alignment="1" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="10" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="dataSourcesHideSlackCheckbox" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="viewsHideSlackCheckbox" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<Component id="hideSlackFilesLabel" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="1" attributes="0">
<EmptySpace min="-2" pref="10" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="dataSourcesHideKnownCheckbox" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="viewsHideKnownCheckbox" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</Group>
</Group>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="displayTimeLabel" alignment="0" min="-2" max="-2" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="10" pref="10" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="keepCurrentViewerRadioButton" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="useBestViewerRadioButton" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="useGMTTimeRadioButton" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="useLocalTimeRadioButton" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<Component id="selectFileLabel" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<Component id="hideOtherUsersTagsLabel" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="centralRepoLabel" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="deletedFilesLimitLabel" alignment="0" min="-2" pref="215" max="-2" attributes="0"/>
</Group>
<EmptySpace min="0" pref="10" max="32767" attributes="0"/>
</Group>
</Group>
<EmptySpace pref="16" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="hideKnownFilesLabel" min="-2" max="-2" attributes="0"/>
@@ -174,6 +181,11 @@
<Component id="centralRepoLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="commentsOccurencesColumnsCheckbox" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="deletedFilesLimitLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="deletedFilesLimitCheckbox" min="-2" pref="33" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
@@ -327,6 +339,23 @@
</Property>
</Properties>
</Component>
<Component class="javax.swing.JCheckBox" name="deletedFilesLimitCheckbox">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="ViewPreferencesPanel.deletedFilesLimitCheckbox.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="deletedFilesLimitCheckboxActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="deletedFilesLimitLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="ViewPreferencesPanel.deletedFilesLimitLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="currentCaseSettingsPanel">
@@ -388,7 +417,7 @@
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="hideRejectedResultsCheckbox" min="-2" max="-2" attributes="0"/>
<Component id="hideRejectedResultsCheckbox" min="-2" pref="259" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
@@ -25,6 +25,7 @@ import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.CasePreferences;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbUtil;
import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.deletedFiles.DeletedFilePreferences;
import org.sleuthkit.autopsy.directorytree.DirectoryTreeTopComponent;
/**
@@ -61,10 +62,12 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
dataSourcesHideSlackCheckbox.setSelected(UserPreferences.hideSlackFilesInDataSourcesTree());
viewsHideSlackCheckbox.setSelected(UserPreferences.hideSlackFilesInViewsTree());
commentsOccurencesColumnsCheckbox.setEnabled(EamDbUtil.useCentralRepo());
commentsOccurencesColumnsCheckbox.setSelected(UserPreferences.hideCentralRepoCommentsAndOccurrences());
deletedFilesLimitCheckbox.setSelected(DeletedFilePreferences.getDefault().getShouldLimitDeletedFiles());
// Current Case Settings
boolean caseIsOpen = Case.isCaseOpen();
currentCaseSettingsPanel.setEnabled(caseIsOpen);
@@ -91,6 +94,8 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
storeGroupItemsInTreeByDataSource();
DirectoryTreeTopComponent.getDefault().setShowRejectedResults(hideRejectedResultsCheckbox.isSelected() == false);
DeletedFilePreferences.getDefault().setShouldLimitDeletedFiles(deletedFilesLimitCheckbox.isSelected());
}
/**
@@ -135,6 +140,8 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
hideOtherUsersTagsLabel = new javax.swing.JLabel();
commentsOccurencesColumnsCheckbox = new javax.swing.JCheckBox();
centralRepoLabel = new javax.swing.JLabel();
deletedFilesLimitCheckbox = new javax.swing.JCheckBox();
deletedFilesLimitLabel = new javax.swing.JLabel();
currentCaseSettingsPanel = new javax.swing.JPanel();
groupByDataSourceCheckbox = new javax.swing.JCheckBox();
currentSessionSettingsPanel = new javax.swing.JPanel();
@@ -228,6 +235,15 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
org.openide.awt.Mnemonics.setLocalizedText(centralRepoLabel, org.openide.util.NbBundle.getMessage(ViewPreferencesPanel.class, "ViewPreferencesPanel.centralRepoLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(deletedFilesLimitCheckbox, org.openide.util.NbBundle.getMessage(ViewPreferencesPanel.class, "ViewPreferencesPanel.deletedFilesLimitCheckbox.text")); // NOI18N
deletedFilesLimitCheckbox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
deletedFilesLimitCheckboxActionPerformed(evt);
}
});
org.openide.awt.Mnemonics.setLocalizedText(deletedFilesLimitLabel, org.openide.util.NbBundle.getMessage(ViewPreferencesPanel.class, "ViewPreferencesPanel.deletedFilesLimitLabel.text")); // NOI18N
javax.swing.GroupLayout globalSettingsPanelLayout = new javax.swing.GroupLayout(globalSettingsPanel);
globalSettingsPanel.setLayout(globalSettingsPanelLayout);
globalSettingsPanelLayout.setHorizontalGroup(
@@ -235,46 +251,51 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
.addGroup(globalSettingsPanelLayout.createSequentialGroup()
.addContainerGap()
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(globalSettingsPanelLayout.createSequentialGroup()
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(hideKnownFilesLabel)
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(globalSettingsPanelLayout.createSequentialGroup()
.addGap(10, 10, 10)
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(dataSourcesHideSlackCheckbox)
.addComponent(viewsHideSlackCheckbox)))
.addComponent(hideSlackFilesLabel))
.addGroup(globalSettingsPanelLayout.createSequentialGroup()
.addGap(10, 10, 10)
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(dataSourcesHideKnownCheckbox)
.addComponent(viewsHideKnownCheckbox)))))
.addGap(18, 18, 18)
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(displayTimeLabel)
.addGroup(globalSettingsPanelLayout.createSequentialGroup()
.addGap(10, 10, 10)
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(keepCurrentViewerRadioButton)
.addComponent(useBestViewerRadioButton)
.addComponent(useGMTTimeRadioButton)
.addComponent(useLocalTimeRadioButton)))
.addComponent(selectFileLabel)))
.addComponent(hideOtherUsersTagsLabel)
.addComponent(centralRepoLabel)
.addGroup(globalSettingsPanelLayout.createSequentialGroup()
.addGap(10, 10, 10)
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(commentsOccurencesColumnsCheckbox)
.addComponent(hideOtherUsersTagsCheckbox))))
.addContainerGap(16, Short.MAX_VALUE))
.addComponent(hideOtherUsersTagsCheckbox)
.addComponent(deletedFilesLimitCheckbox, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
.addGroup(globalSettingsPanelLayout.createSequentialGroup()
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(globalSettingsPanelLayout.createSequentialGroup()
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(hideKnownFilesLabel)
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(globalSettingsPanelLayout.createSequentialGroup()
.addGap(10, 10, 10)
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(dataSourcesHideSlackCheckbox)
.addComponent(viewsHideSlackCheckbox)))
.addComponent(hideSlackFilesLabel))
.addGroup(globalSettingsPanelLayout.createSequentialGroup()
.addGap(10, 10, 10)
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(dataSourcesHideKnownCheckbox)
.addComponent(viewsHideKnownCheckbox)))))
.addGap(18, 18, 18)
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(displayTimeLabel)
.addGroup(globalSettingsPanelLayout.createSequentialGroup()
.addGap(10, 10, 10)
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(keepCurrentViewerRadioButton)
.addComponent(useBestViewerRadioButton)
.addComponent(useGMTTimeRadioButton)
.addComponent(useLocalTimeRadioButton)))
.addComponent(selectFileLabel)))
.addComponent(hideOtherUsersTagsLabel)
.addComponent(centralRepoLabel)
.addComponent(deletedFilesLimitLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 215, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGap(0, 10, Short.MAX_VALUE)))
.addContainerGap())
);
globalSettingsPanelLayout.setVerticalGroup(
globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(globalSettingsPanelLayout.createSequentialGroup()
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addContainerGap()
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(globalSettingsPanelLayout.createSequentialGroup()
.addComponent(hideKnownFilesLabel)
@@ -307,7 +328,12 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(centralRepoLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(commentsOccurencesColumnsCheckbox))
.addComponent(commentsOccurencesColumnsCheckbox)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(deletedFilesLimitLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(deletedFilesLimitCheckbox, javax.swing.GroupLayout.PREFERRED_SIZE, 33, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 0, 0))
);
currentCaseSettingsPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(ViewPreferencesPanel.class, "ViewPreferencesPanel.currentCaseSettingsPanel.border.title"))); // NOI18N
@@ -350,7 +376,7 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
currentSessionSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(currentSessionSettingsPanelLayout.createSequentialGroup()
.addContainerGap()
.addComponent(hideRejectedResultsCheckbox)
.addComponent(hideRejectedResultsCheckbox, javax.swing.GroupLayout.PREFERRED_SIZE, 259, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
currentSessionSettingsPanelLayout.setVerticalGroup(
@@ -501,6 +527,14 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
}
}//GEN-LAST:event_commentsOccurencesColumnsCheckboxActionPerformed
private void deletedFilesLimitCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deletedFilesLimitCheckboxActionPerformed
if (immediateUpdates) {
DeletedFilePreferences.getDefault().setShouldLimitDeletedFiles(deletedFilesLimitCheckbox.isSelected());
} else {
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}
}//GEN-LAST:event_deletedFilesLimitCheckboxActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel centralRepoLabel;
@@ -509,6 +543,8 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
private javax.swing.JPanel currentSessionSettingsPanel;
private javax.swing.JCheckBox dataSourcesHideKnownCheckbox;
private javax.swing.JCheckBox dataSourcesHideSlackCheckbox;
private javax.swing.JCheckBox deletedFilesLimitCheckbox;
private javax.swing.JLabel deletedFilesLimitLabel;
private javax.swing.JLabel displayTimeLabel;
private javax.swing.JPanel globalSettingsPanel;
private javax.swing.JCheckBox groupByDataSourceCheckbox;
@@ -527,4 +563,4 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
private javax.swing.JCheckBox viewsHideKnownCheckbox;
private javax.swing.JCheckBox viewsHideSlackCheckbox;
// End of variables declaration//GEN-END:variables
}
}
@@ -43,18 +43,23 @@ import javax.annotation.concurrent.ThreadSafe;
@ThreadSafe
public class History<T> {
// Stack of things that were previously shown before an 'advance' was done
@GuardedBy("this")
private final ObservableStack<T> historyStack = new ObservableStack<>();
// stack of things that were previously shown before a 'retreat' (i.e. a back) was done
@GuardedBy("this")
private final ObservableStack<T> forwardStack = new ObservableStack<>();
// what is currently being shown
@GuardedBy("this")
private final ReadOnlyObjectWrapper<T> currentState = new ReadOnlyObjectWrapper<>();
// Is the forward stack empty?
@GuardedBy("this")
private final ReadOnlyBooleanWrapper canAdvance = new ReadOnlyBooleanWrapper();
// is the historyStack empty?
@GuardedBy("this")
private final ReadOnlyBooleanWrapper canRetreat = new ReadOnlyBooleanWrapper();
@@ -45,6 +45,7 @@ import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.coreutils.Logger;
import static org.sleuthkit.autopsy.datamodel.Bundle.*;
import org.sleuthkit.autopsy.deletedFiles.DeletedFilePreferences;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Content;
@@ -111,11 +112,11 @@ public class DeletedContent implements AutopsyVisitableItem {
this.skCase = skCase;
this.datasourceObjId = dsObjId;
}
long filteringDataSourceObjId() {
return this.datasourceObjId;
}
@Override
public <T> T accept(AutopsyItemVisitor<T> visitor) {
return visitor.visit(this);
@@ -191,9 +192,10 @@ public class DeletedContent implements AutopsyVisitableItem {
* fired. Other nodes are listening to this for changes.
*/
private static final class DeletedContentsChildrenObservable extends Observable {
private static final Set<Case.Events> CASE_EVENTS_OF_INTEREST = EnumSet.of(
Case.Events.DATA_SOURCE_ADDED,
Case.Events.CURRENT_CASE
Case.Events.DATA_SOURCE_ADDED,
Case.Events.CURRENT_CASE
);
DeletedContentsChildrenObservable() {
@@ -213,12 +215,11 @@ public class DeletedContent implements AutopsyVisitableItem {
String eventType = evt.getPropertyName();
if (eventType.equals(IngestManager.IngestModuleEvent.CONTENT_CHANGED.toString())) {
/**
* + // @@@ COULD CHECK If the new file is deleted
* before notifying... Checking for a current case is a
* stop gap measure + update(); until a different way of
* handling the closing of cases is worked out.
* Currently, remote events may be received for a case
* that is already closed.
* + // @@@ COULD CHECK If the new file is deleted before
* notifying... Checking for a current case is a stop gap
* measure + update(); until a different way of handling the
* closing of cases is worked out. Currently, remote events
* may be received for a case that is already closed.
*/
try {
Case.getCurrentCaseThrows();
@@ -234,10 +235,10 @@ public class DeletedContent implements AutopsyVisitableItem {
|| eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())
|| eventType.equals(Case.Events.DATA_SOURCE_ADDED.toString())) {
/**
* Checking for a current case is a stop gap measure
* until a different way of handling the closing of
* cases is worked out. Currently, remote events may be
* received for a case that is already closed.
* Checking for a current case is a stop gap measure until a
* different way of handling the closing of cases is worked
* out. Currently, remote events may be received for a case
* that is already closed.
*/
try {
Case.getCurrentCaseThrows();
@@ -282,7 +283,7 @@ public class DeletedContent implements AutopsyVisitableItem {
// Use version that has observer for updates
@Deprecated
DeletedContentNode(SleuthkitCase skCase, DeletedContent.DeletedContentFilter filter, long dsObjId) {
super(Children.create(new DeletedContentChildren(filter, skCase, null, dsObjId ), true), Lookups.singleton(filter.getDisplayName()));
super(Children.create(new DeletedContentChildren(filter, skCase, null, dsObjId), true), Lookups.singleton(filter.getDisplayName()));
this.filter = filter;
this.datasourceObjId = dsObjId;
init();
@@ -366,7 +367,7 @@ public class DeletedContent implements AutopsyVisitableItem {
private final SleuthkitCase skCase;
private final DeletedContent.DeletedContentFilter filter;
private static final Logger logger = Logger.getLogger(DeletedContentChildren.class.getName());
private static final int MAX_OBJECTS = 10001;
private final Observable notifier;
private final long datasourceObjId;
@@ -385,7 +386,7 @@ public class DeletedContent implements AutopsyVisitableItem {
@Override
public void update(Observable o, Object arg) {
refresh(true);
}
}
}
@Override
@@ -408,18 +409,19 @@ public class DeletedContent implements AutopsyVisitableItem {
+ "There are more Deleted Files than can be displayed."
+ " Only the first {0} Deleted Files will be shown."})
protected boolean createKeys(List<AbstractFile> list) {
DeletedFilePreferences deletedPreferences = DeletedFilePreferences.getDefault();
List<AbstractFile> queryList = runFsQuery();
if (queryList.size() == MAX_OBJECTS) {
if (deletedPreferences.getShouldLimitDeletedFiles() && queryList.size() == deletedPreferences.getDeletedFilesLimit()) {
queryList.remove(queryList.size() - 1);
// only show the dialog once - not each time we refresh
if (maxFilesDialogShown == false) {
maxFilesDialogShown = true;
SwingUtilities.invokeLater(()
-> JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
DeletedContent_createKeys_maxObjects_msg(MAX_OBJECTS - 1))
DeletedContent_createKeys_maxObjects_msg(deletedPreferences.getDeletedFilesLimit() - 1))
);
}
}
}
list.addAll(queryList);
return true;
}
@@ -463,10 +465,12 @@ public class DeletedContent implements AutopsyVisitableItem {
}
if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) {
query += " AND data_source_obj_id = " + filteringDSObjId;
query += " AND data_source_obj_id = " + filteringDSObjId;
}
DeletedFilePreferences deletedPreferences = DeletedFilePreferences.getDefault();
if (deletedPreferences.getShouldLimitDeletedFiles()) {
query += " LIMIT " + deletedPreferences.getDeletedFilesLimit(); //NON-NLS
}
query += " LIMIT " + MAX_OBJECTS; //NON-NLS
return query;
}
@@ -44,7 +44,6 @@ import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.CasePreferences;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
@@ -476,11 +475,13 @@ public class InterestingHits implements AutopsyVisitableItem {
BlackboardArtifact art = skCase.getBlackboardArtifact(id);
artifactHits.put(id, art);
}
list.add(id);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "TSK Exception occurred", ex); //NON-NLS
}
});
list.addAll(artifactHits.keySet());
return true;
}
@@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.datamodel;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.Action;
import org.openide.util.NbBundle;
@@ -27,6 +28,7 @@ import org.sleuthkit.autopsy.directorytree.ExtractAction;
import org.sleuthkit.autopsy.directorytree.FileSearchAction;
import org.sleuthkit.autopsy.directorytree.NewWindowViewAction;
import org.sleuthkit.autopsy.ingest.runIngestModuleWizard.RunIngestModulesAction;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.SpecialDirectory;
/**
@@ -60,7 +62,11 @@ public abstract class SpecialDirectoryNode extends AbstractAbstractFileNode<Spec
actions.add(ExtractAction.getInstance());
actions.add(null); // creates a menu separator
actions.add(new FileSearchAction(Bundle.ImageNode_getActions_openFileSearchByAttr_text()));
actions.add(new RunIngestModulesAction(content));
if (content.isDataSource()) {
actions.add(new RunIngestModulesAction(Collections.<Content>singletonList(content)));
} else {
actions.add(new RunIngestModulesAction(content));
}
actions.addAll(ContextMenuExtensionPoint.getActions());
return actions.toArray(new Action[0]);
}
@@ -0,0 +1,171 @@
/*
* 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.deletedFiles;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Properties;
import java.util.logging.Level;
import org.sleuthkit.autopsy.casemodule.CasePreferences;
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.directorytree.DirectoryTreeTopComponent;
/**
* Class to store settings related to the display of deleted files.
*/
public class DeletedFilePreferences {
private static final String SETTINGS_FILE = "DeletedFilePreferences.properties"; //NON-NLS
private static final String KEY_LIMIT_DELETED_FILES = "limitDeletedFiles"; //NON-NLS
private static final String KEY_LIMIT_VALUE = "limitValue";
private static final String VALUE_TRUE = "true"; //NON-NLS
private static final String VALUE_FALSE = "false"; //NON-NLS
private static final int DEFAULT_MAX_OBJECTS = 10001;
private static final Logger logger = Logger.getLogger(CasePreferences.class.getName());
private static DeletedFilePreferences defaultInstance;
private static boolean limitDeletedFiles = true;
private static int deletedFilesLimit = DEFAULT_MAX_OBJECTS;
/**
* Get the settings for the display of deleted files.
*
* @return defaultInstance with freshly loaded
*/
public static synchronized DeletedFilePreferences getDefault() {
if (defaultInstance == null) {
defaultInstance = new DeletedFilePreferences();
}
defaultInstance.loadFromStorage();
return defaultInstance;
}
/**
* Prevent instantiation.
*/
private DeletedFilePreferences() {
}
/**
* Get the 'limitDeletedFiles' value. This can be true or false. It will
* default to true if it was not saved correctly previously.s
*
* @return true if the number of deleted files displayed should be limied,
* false if it should not be limited.
*/
public boolean getShouldLimitDeletedFiles() {
return limitDeletedFiles;
}
/**
* Set the 'limitDeletedFiles' value to true or false.
*
* @param value true if the number of deleted files displayed should be
* limied, false if it should not be limited.
*/
public void setShouldLimitDeletedFiles(boolean value) {
limitDeletedFiles = value;
saveToStorage();
DirectoryTreeTopComponent.getDefault().refreshContentTreeSafe();
}
/**
* Get the 'limitValue' value. This is an interger value and will default to
* DEFAULT_MAX_OBJECTS if it was not previously saved correctly.
*
* @return an integer representing the max number of deleted files to display.
*/
public int getDeletedFilesLimit() {
return deletedFilesLimit;
}
/**
* Set the 'limitValue' for max number of deleted files to display.
*
* @param value an integer representing the max number of deleted files to display.
*/
public void setDeletedFilesLimit(int value) {
deletedFilesLimit = value;
saveToStorage();
DirectoryTreeTopComponent.getDefault().refreshContentTreeSafe();
}
/**
* Load deleted file preferences from the settings file.
*/
private void loadFromStorage() {
Path settingsFile = Paths.get(PlatformUtil.getUserConfigDirectory(), SETTINGS_FILE); //NON-NLS
if (settingsFile.toFile().exists()) {
// Read the settings
try (InputStream inputStream = Files.newInputStream(settingsFile)) {
Properties props = new Properties();
props.load(inputStream);
String limitDeletedFilesValue = props.getProperty(KEY_LIMIT_DELETED_FILES);
if (limitDeletedFilesValue != null) {
switch (limitDeletedFilesValue) {
case VALUE_TRUE:
limitDeletedFiles = true;
break;
case VALUE_FALSE:
limitDeletedFiles = false;
break;
default:
logger.log(Level.WARNING, String.format("Unexpected value '%s' for limit deleted files using value of true instead",
limitDeletedFilesValue));
limitDeletedFiles = true;
break;
}
}
String limitValue = props.getProperty(KEY_LIMIT_VALUE);
try {
if (limitValue != null) {
deletedFilesLimit = Integer.valueOf(limitValue);
}
} catch (NumberFormatException ex) {
logger.log(Level.INFO, String.format("Unexpected value '%s' for limit, expected an integer using default of 10,001 instead",
limitValue));
deletedFilesLimit = DEFAULT_MAX_OBJECTS;
}
} catch (IOException ex) {
logger.log(Level.SEVERE, "Error reading deletedFilesPreferences file", ex);
}
}
}
/**
* Store deleted file preferences in the settings file.
*/
private void saveToStorage() {
Path settingsFile = Paths.get(PlatformUtil.getUserConfigDirectory(), SETTINGS_FILE); //NON-NLS
Properties props = new Properties();
props.setProperty(KEY_LIMIT_DELETED_FILES, (limitDeletedFiles ? VALUE_TRUE : VALUE_FALSE));
props.setProperty(KEY_LIMIT_VALUE, String.valueOf(deletedFilesLimit));
try (OutputStream fos = Files.newOutputStream(settingsFile)) {
props.store(fos, ""); //NON-NLS
} catch (IOException ex) {
logger.log(Level.SEVERE, "Error writing deletedFilesPreferences file", ex);
}
}
}
@@ -140,18 +140,6 @@ public class DataResultFilterNode extends FilterNode {
this.sourceEm = em;
}
/**
* Refreshes the inner node. If the actual underlying node is an InstanceCountNode,
* refresh() that node, which refreshes the children.
*
*/
public void refresh() {
if (getOriginal() instanceof InstanceCountNode) {
InstanceCountNode innerNode = getLookup().lookup(InstanceCountNode.class);
innerNode.refresh();
}
}
/**
* Constructs a node used to wrap another node before passing it to the
* result viewers. The wrapper node defines the actions associated with the
+1 -1
View File
@@ -135,7 +135,7 @@
<compile-dependency/>
<run-dependency>
<release-version>10</release-version>
<specification-version>10.12</specification-version>
<specification-version>10.13</specification-version>
</run-dependency>
</dependency>
<dependency>
+1 -1
View File
@@ -1,6 +1,6 @@
Manifest-Version: 1.0
OpenIDE-Module: org.sleuthkit.autopsy.imagegallery/2
OpenIDE-Module-Implementation-Version: 3
OpenIDE-Module-Implementation-Version: 4
OpenIDE-Module-Layer: org/sleuthkit/autopsy/imagegallery/layer.xml
OpenIDE-Module-Localizing-Bundle: org/sleuthkit/autopsy/imagegallery/Bundle.properties
+1 -1
View File
@@ -127,7 +127,7 @@
<compile-dependency/>
<run-dependency>
<release-version>10</release-version>
<specification-version>10.12</specification-version>
<specification-version>10.13</specification-version>
</run-dependency>
</dependency>
<dependency>
@@ -136,18 +136,10 @@ public final class ImageGalleryController {
return thumbnailSizeProp;
}
public GroupViewState getViewState() {
return historyManager.getCurrentState();
}
public ReadOnlyBooleanProperty regroupDisabledProperty() {
return regroupDisabled.getReadOnlyProperty();
}
public ReadOnlyObjectProperty<GroupViewState> viewStateProperty() {
return historyManager.currentState();
}
public FileIDSelectionModel getSelectionModel() {
return selectionModel;
}
@@ -240,24 +232,66 @@ public final class ImageGalleryController {
dbTaskQueueSize.addListener(obs -> this.updateRegroupDisabled());
}
/**
* @return Currently displayed group or null if nothing is being displayed
*/
public GroupViewState getViewState() {
return historyManager.getCurrentState();
}
/**
* Get observable property of the current group. The UI currently changes
* based on this property changing, which happens when other actions and
* threads call advance().
*
* @return Currently displayed group (as a property that can be observed)
*/
public ReadOnlyObjectProperty<GroupViewState> viewStateProperty() {
return historyManager.currentState();
}
/**
* Should the "forward" button on the history be enabled?
* @return
*/
public ReadOnlyBooleanProperty getCanAdvance() {
return historyManager.getCanAdvance();
}
/**
* Should the "Back" button on the history be enabled?
* @return
*/
public ReadOnlyBooleanProperty getCanRetreat() {
return historyManager.getCanRetreat();
}
/**
* Display the passed in group. Causes this group to
* get recorded in the history queue and observers of the
* current state will be notified and update their panels/widgets
* appropriately.
*
* @param newState
*/
@ThreadConfined(type = ThreadConfined.ThreadType.ANY)
public void advance(GroupViewState newState) {
historyManager.advance(newState);
}
/**
* Display the next group in the "forward" history stack
* @return
*/
public GroupViewState advance() {
return historyManager.advance();
}
/**
* Display the previous group in the "back" history stack
* @return
*/
public GroupViewState retreat() {
return historyManager.retreat();
}
@@ -18,6 +18,7 @@
*/
package org.sleuthkit.autopsy.imagegallery.actions;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import java.util.Optional;
import javafx.application.Platform;
@@ -32,6 +33,7 @@ import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.DrawableGroup;
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupManager;
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupViewState;
import org.sleuthkit.autopsy.imagegallery.utils.TaskUtils;
/**
* Marks the currently displayed group as "seen" and advances to the next unseen
@@ -56,6 +58,11 @@ public class NextUnseenGroup extends Action {
private final ImageGalleryController controller;
private final ObservableList<DrawableGroup> unSeenGroups;
private final GroupManager groupManager;
private boolean isLoading = false; // set to true when we are marking current group as seen and loading new
private final ListeningExecutorService exec = TaskUtils.getExecutorForClass(NextUnseenGroup.class);
public NextUnseenGroup(ImageGalleryController controller) {
super(NEXT_UNSEEN_GROUP);
@@ -63,56 +70,98 @@ public class NextUnseenGroup extends Action {
this.controller = controller;
groupManager = controller.getGroupManager();
// Get reference to the list of unseen groups, that GroupManager will continue to manage
unSeenGroups = groupManager.getUnSeenGroups();
unSeenGroups.addListener((Observable observable) -> updateButton());
unSeenGroups.addListener((Observable observable) -> unSeenGroupListener());
controller.viewStateProperty().addListener((Observable observable) -> updateButton());
setEventHandler(event -> { //on fx-thread
//if there is a group assigned to the view, mark it as seen
Optional.ofNullable(controller.getViewState())
.flatMap(GroupViewState::getGroup)
.ifPresent(group -> {
setDisabled(true);
groupManager.markGroupSeen(group, true)
.addListener(this::advanceToNextUnseenGroup, MoreExecutors.newDirectExecutorService());
});
isLoading = true; // make sure button stays disabled until we are done loading
setDisabled(true);
//if there is a group assigned to the view, mark it as seen and move on to the next one
GroupViewState viewState = controller.getViewState();
if (viewState != null) {
Optional<DrawableGroup> group = viewState.getGroup();
if (group.isPresent()) {
// NOTE: We need to wait for current group to be marked as seen because the 'advance'
// method grabs the top of the unseen list
groupManager.markGroupSeen(group.get(), true)
.addListener(this::advanceToNextUnseenGroup, MoreExecutors.newDirectExecutorService());
return;
}
}
// otherwise, just move on to the next one
exec.submit(this::advanceToNextUnseenGroup);
});
// initial button state
updateButton();
}
/**
* Listener that updates UI based on changes to the unseen group list
*/
private void unSeenGroupListener() {
// set the group if there is no visible group.
// NOTE: it could be argued that this should be done in another listner
if (controller.getViewState() == null) {
advanceToNextUnseenGroup();
// do not update the button if it is supposed to be disabled during loading of the next group
} else if (isLoading == false) {
// NOTE: should we get a lock on groupManager here like advanceToNextUnseenGroup does?
updateButton();
}
}
// update UI based on button being pressed
private void advanceToNextUnseenGroup() {
synchronized (groupManager) {
if (CollectionUtils.isNotEmpty(unSeenGroups)) {
controller.advance(GroupViewState.tile(unSeenGroups.get(0)));
// NOTE: We keep the group in the unSeenGroup list until the user presses the
// button again mark it as seen
controller.advance(GroupViewState.createTile(unSeenGroups.get(0)));
}
updateButton();
}
}
/**
* Update button based on currently displayed group and queues.
*/
private void updateButton() {
int size = unSeenGroups.size();
if (size < 1) {
//there are no unseen groups.
isLoading = false;
int unSeenSize = unSeenGroups.size();
// NOTE: The currently displayed group is still in the unSeenGroups list until the user presses
// the button again and then we'll mark it as seen.
// disable button if no unseen groups
if (unSeenSize < 1) {
Platform.runLater(() -> {
setDisabled(true);
setText(ALL_GROUPS_SEEN);
setGraphic(null);
});
} else {
DrawableGroup get = unSeenGroups.get(0);
DrawableGroup orElse = Optional.ofNullable(controller.getViewState()).flatMap(GroupViewState::getGroup).orElse(null);
boolean equals = get.equals(orElse);
if (size == 1 & equals) {
//The only unseen group is the one that is being viewed.
DrawableGroup groupOnList = unSeenGroups.get(0);
DrawableGroup groupInView = Optional.ofNullable(controller.getViewState()).flatMap(GroupViewState::getGroup).orElse(null);
//The only unseen group is the one that is being viewed.
if (unSeenSize == 1 & groupOnList.equals(groupInView)) {
Platform.runLater(() -> {
setDisabled(false);
setDisabled(true);
setText(MARK_GROUP_SEEN);
setGraphic(new ImageView(END_IMAGE));
});
} else {
//there are more unseen groups.
//there are more unseen groups after this one
Platform.runLater(() -> {
setDisabled(false);
setText(NEXT_UNSEEN_GROUP);
@@ -110,6 +110,8 @@ public final class DrawableDB {
private final PreparedStatement insertHashHitStmt;
private final PreparedStatement removeHashHitStmt;
private final PreparedStatement updateDataSourceStmt;
private final PreparedStatement updateFileStmt;
@@ -263,6 +265,7 @@ public final class DrawableDB {
selectHashSetStmt = prepareStatement("SELECT hash_set_id FROM hash_sets WHERE hash_set_name = ?"); //NON-NLS
insertHashHitStmt = prepareStatement("INSERT OR IGNORE INTO hash_set_hits (hash_set_id, obj_id) VALUES (?,?)"); //NON-NLS
removeHashHitStmt = prepareStatement("DELETE FROM hash_set_hits WHERE obj_id = ?"); //NON-NLS
CaseDbTransaction caseDbTransaction = null;
try {
@@ -1408,7 +1411,7 @@ public final class DrawableDB {
ds_obj_id, value, groupBy.attrName.toString());
if (DbType.POSTGRESQL == tskCase.getDatabaseType()) {
insertSQL += "ON CONFLICT DO NOTHING";
insertSQL += " ON CONFLICT DO NOTHING";
}
tskCase.getCaseDbAccessManager().insert(GROUPS_TABLENAME, insertSQL, caseDbTransaction);
groupCache.put(cacheKey, Boolean.TRUE);
@@ -1517,12 +1520,15 @@ public final class DrawableDB {
// Update the list of file IDs in memory
removeImageFileFromList(id);
//"delete from hash_set_hits where (obj_id = " + id + ")"
removeHashHitStmt.setLong(1, id);
removeHashHitStmt.executeUpdate();
//"delete from drawable_files where (obj_id = " + id + ")"
removeFileStmt.setLong(1, id);
removeFileStmt.executeUpdate();
tr.addRemovedFile(id);
//TODO: delete from hash_set_hits table also...
} catch (SQLException ex) {
logger.log(Level.WARNING, "failed to delete row for obj_id = " + id, ex); //NON-NLS
} finally {
@@ -99,7 +99,17 @@ public class GroupKey<T extends Comparable<T>> implements Comparable<GroupKey<T>
if (!Objects.equals(this.attr, other.attr)) {
return false;
}
return this.dataSource.getId() == other.dataSource.getId();
// Check datasource, if available
if (this.dataSource != null && other.dataSource != null) {
return this.dataSource.getId() == other.dataSource.getId();
} else if (this.dataSource == null && other.dataSource == null) {
// neither group has a datasource
return true;
} else {
// one group has a datasource, other doesn't
return false;
}
}
@Override
@@ -449,7 +449,7 @@ public class GroupManager {
*
* @param dataSource Data source to display or null to display all of them
*/
synchronized void setDataSource(DataSource dataSource) {
public synchronized void setDataSource(DataSource dataSource) {
dataSourceProp.set(dataSource);
}
@@ -785,12 +785,12 @@ public class GroupManager {
//the current group should not be visible so ...
if (isNotEmpty(unSeenGroups)) {
// show then next unseen group
controller.advance(GroupViewState.tile(unSeenGroups.get(0)));
controller.advance(GroupViewState.createTile(unSeenGroups.get(0)));
} else if (isNotEmpty(analyzedGroups)) {
//show the first analyzed group.
controller.advance(GroupViewState.tile(analyzedGroups.get(0)));
controller.advance(GroupViewState.createTile(analyzedGroups.get(0)));
} else { //there are no groups, clear the group area.
controller.advance(GroupViewState.tile(null));
controller.advance(GroupViewState.createTile(null));
}
}
} finally {
@@ -26,12 +26,28 @@ import java.util.Optional;
*/
public final class GroupViewState {
// what group is being represented
private final DrawableGroup group;
// Tile, Slide show, etc.
private final GroupViewMode mode;
private final Optional<Long> slideShowfileID;
private GroupViewState(DrawableGroup group, GroupViewMode mode, Long slideShowfileID) {
this.group = group;
this.mode = mode;
this.slideShowfileID = Optional.ofNullable(slideShowfileID);
}
public static GroupViewState createTile(DrawableGroup group) {
return new GroupViewState(group, GroupViewMode.TILE, null);
}
public static GroupViewState createSlideShow(DrawableGroup group, Long fileID) {
return new GroupViewState(group, GroupViewMode.SLIDE_SHOW, fileID);
}
public Optional<DrawableGroup> getGroup() {
return Optional.ofNullable(group);
}
@@ -44,19 +60,7 @@ public final class GroupViewState {
return slideShowfileID;
}
private GroupViewState(DrawableGroup group, GroupViewMode mode, Long slideShowfileID) {
this.group = group;
this.mode = mode;
this.slideShowfileID = Optional.ofNullable(slideShowfileID);
}
public static GroupViewState tile(DrawableGroup group) {
return new GroupViewState(group, GroupViewMode.TILE, null);
}
public static GroupViewState slideShow(DrawableGroup group, Long fileID) {
return new GroupViewState(group, GroupViewMode.SLIDE_SHOW, fileID);
}
@Override
public int hashCode() {
@@ -185,6 +185,9 @@ public class Toolbar extends ToolBar {
alert.initOwner(getScene().getWindow());
GuiUtils.setDialogIcons(alert);
if (alert.showAndWait().orElse(ButtonType.CANCEL) == ButtonType.OK) {
// Set the datasource selection to 'All', before switching group
controller.getGroupManager().setDataSource(null);
queryInvalidationListener.invalidated(observable);
} else {
Platform.runLater(() -> groupByBox.getSelectionModel().select(DrawableAttribute.PATH));
@@ -107,7 +107,7 @@ abstract class NavPanel<X> extends Tab {
.addListener((observable, oldItem, newSelectedItem) -> {
Optional.ofNullable(newSelectedItem)
.map(getDataItemMapper())
.ifPresent(group -> controller.advance(GroupViewState.tile(group)));
.ifPresent(group -> controller.advance(GroupViewState.createTile(group)));
});
}
+1 -1
View File
@@ -119,7 +119,7 @@
<compile-dependency/>
<run-dependency>
<release-version>10</release-version>
<specification-version>10.12</specification-version>
<specification-version>10.13</specification-version>
</run-dependency>
</dependency>
<dependency>
@@ -33,7 +33,6 @@ import java.util.LinkedList;
import java.util.logging.Level;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.SqliteUtil;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.TskCoreException;
@@ -0,0 +1,130 @@
/*
* 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.keywordsearch;
import java.io.File;
import java.io.IOException;
import java.util.List;
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;
/**
* Sqlite utility class. Find and copy metafiles, write sqlite abstract files to
* temp directory, and generate unique temp directory paths.
*/
final class SqliteUtil {
private SqliteUtil() {
}
/**
* Overloaded implementation of
* {@link #findAndCopySQLiteMetaFile(AbstractFile, String) findAndCopySQLiteMetaFile}
* , automatically tries to copy -wal and -shm files without needing to know
* their existence.
*
* @param sqliteFile file which has -wal and -shm meta files
*
* @throws NoCurrentCaseException Case has been closed.
* @throws TskCoreException fileManager cannot find AbstractFile
* files.
* @throws IOException Issue during writing to file.
*/
public static void findAndCopySQLiteMetaFile(AbstractFile sqliteFile)
throws NoCurrentCaseException, TskCoreException, IOException {
findAndCopySQLiteMetaFile(sqliteFile, sqliteFile.getName() + "-wal");
findAndCopySQLiteMetaFile(sqliteFile, sqliteFile.getName() + "-shm");
}
/**
* Searches for a meta file associated with the give SQLite database. If
* found, it copies this file into the temp directory of the current case.
*
* @param sqliteFile 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.
*/
public static 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) {
writeAbstractFileToLocalDisk(metaFile);
}
}
}
/**
* Copies the file contents into a unique path in the current case temp
* directory.
*
* @param file AbstractFile from the data source
*
* @return The path of the file on disk
*
* @throws IOException Exception writing file contents
* @throws NoCurrentCaseException Current case closed during file copying
*/
public static String writeAbstractFileToLocalDisk(AbstractFile file)
throws IOException, NoCurrentCaseException {
String localDiskPath = getUniqueTempDirectoryPath(file);
File localDatabaseFile = new File(localDiskPath);
if (!localDatabaseFile.exists()) {
ContentUtils.writeToFile(file, localDatabaseFile);
}
return localDiskPath;
}
/**
* Generates a unique local disk path that resides in the temp directory of
* the current case.
*
* @param file The database abstract file
*
* @return Unique local disk path living in the temp directory of the case
*
* @throws org.sleuthkit.autopsy.casemodule.NoCurrentCaseException
*/
public static String getUniqueTempDirectoryPath(AbstractFile file) throws NoCurrentCaseException {
return Case.getCurrentCaseThrows().getTempDirectory()
+ File.separator + file.getId() + file.getName();
}
}
+32 -2
View File
@@ -1,9 +1,39 @@
---------------- VERSION 4.9.0 --------------
New Features:
- Removed data from table that are time intensive and can be found in content viewers (such as hash set hits)
- Added ability to find common items (files, emails, etc.) between current case and past cases using the Central Repository.
- Added ability to ignore common items that exist in a large number of cases by using Central Repository data.
- Data is validated and normalized before being entered into the Central Repository.
- Allow users to specify that an ad-hoc keyword search should not be saved to database
- New “Annotations” content viewer that shows all tags and comments associated with an item
- Added 2 icons to the table to show the item’s score (if it is notable or suspicious) and if it has a comment.
- Added column to the table to show previous number of occurrences.
- Tags are now associated with the user (in a multi-user environment) and you can hide other people’s tags
- New Display options area that unifies various new settings.
- Hash sets can be copied into the user’s config folder (AppData), which makes it easier to run Autopsy from a Live Triage USB and not care about what drive letter it gets.
- Image Gallery stores its groups and seen status in Case DB instead of its own.
- Image Gallery works better in multi-user setups and reloads the database when other nodes add data sources.
- Image Gallery saves which user saw a group and gives user option of seeing only their unseen groups or all unseen groups.
- Saves last export location and pre-populates that in the file picker
- Provide feedback about why some right click options are disabled (ingest is running, not file content, etc.)
Bug Fixes:
- Substring keyword search is more accurate (now uses regular expression)
- New text extractor for SQLite that better deals with full text search tables
- Better deal with Unicode text files that do not have Byte Order Marker
- Embedded file extractor module is now faster because it uses a different 7ZIP API.
- Fixed various HTML report bugs
- Duplicate hash set hits are not created when you run the Hash Ingest Module twice.
- Auto ingest (in Experimental) scan times of input folders is faster.
---------------- VERSION 4.8.0 --------------
New Features:
- 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:
- Central Repository / Correlation:
-- 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.
@@ -31,7 +61,7 @@ New Features:
- A graph visualization was added to the Communications tool to make it easier to find messages and relationships.
- A new "Application" content viewer (lower right) that will contain file-type specific viewers (to reduce number of tabs).
- New viewer for SQLite databases (in Application content viewer)
- New viewer for binary PLists (in Appilcation content viewer)
- New viewer for binary PLists (in Application content viewer)
- L01 files can be imported as data sources.
- Ingest filters can now use date range conditions for triage.
- Passwords to open password protected archive files can be entered (by right clicking on the file).
+1 -1
View File
@@ -60,7 +60,7 @@
<compile-dependency/>
<run-dependency>
<release-version>10</release-version>
<specification-version>10.12</specification-version>
<specification-version>10.13</specification-version>
</run-dependency>
</dependency>
</module-dependencies>
@@ -69,6 +69,7 @@ class ExtractIE extends Extract {
private final String moduleTempResultsDir;
private String PASCO_LIB_PATH;
private final String JAVA_PATH;
private static final String RESOURCE_URL_PREFIX = "res://";
private static final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
private Content dataSource;
private IngestJobContext context;
@@ -473,8 +474,8 @@ class ExtractIE extends Extract {
String actime = lineBuff[3];
Long ftime = (long) 0;
String user;
String realurl;
String user = null;
String realurl = null;
String domain;
/*
@@ -494,6 +495,9 @@ class ExtractIE extends Extract {
realurl = realurl.replace(":Host:", ""); //NON-NLS
realurl = realurl.trim();
} else {
/*
* Use the entire input for the URL.
*/
user = "";
realurl = lineBuff[1].trim();
}
@@ -532,9 +536,12 @@ class ExtractIE extends Extract {
"ExtractIE.parentModuleName.noSpace"),
NbBundle.getMessage(this.getClass(),
"ExtractIE.moduleName.text")));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
NbBundle.getMessage(this.getClass(),
"ExtractIE.parentModuleName.noSpace"), domain));
if (isIgnoredUrl(lineBuff[1]) == false) {
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
NbBundle.getMessage(this.getClass(),
"ExtractIE.parentModuleName.noSpace"), domain));
}
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME,
NbBundle.getMessage(this.getClass(),
"ExtractIE.parentModuleName.noSpace"), user));
@@ -562,4 +569,26 @@ class ExtractIE extends Extract {
fileScanner.close();
return bbartifacts;
}
/**
* Determine if the URL should be ignored.
*
* @param url The URL to test.
*
* @return True if the URL should be ignored; otherwise false.
*/
private boolean isIgnoredUrl(String url) {
if (url == null || url.isEmpty()) {
return true;
}
if (url.toLowerCase().startsWith(RESOURCE_URL_PREFIX)) {
/*
* Ignore URLs that begin with the matched text.
*/
return true;
}
return false;
}
}
@@ -54,6 +54,7 @@ import org.sleuthkit.datamodel.TskCoreException;
class Firefox extends Extract {
private static final Logger logger = Logger.getLogger(Firefox.class.getName());
private static final String PLACE_URL_PREFIX = "place:";
private static final String HISTORY_QUERY = "SELECT moz_historyvisits.id,url,title,visit_count,(visit_date/1000000) AS visit_date,from_visit,(SELECT url FROM moz_places WHERE id=moz_historyvisits.from_visit) as ref FROM moz_places, moz_historyvisits WHERE moz_places.id = moz_historyvisits.place_id AND hidden = 0"; //NON-NLS
private static final String COOKIE_QUERY = "SELECT name,value,host,expiry,(lastAccessed/1000000) AS lastAccessed,(creationTime/1000000) AS creationTime FROM moz_cookies"; //NON-NLS
private static final String COOKIE_QUERY_V3 = "SELECT name,value,host,expiry,(lastAccessed/1000000) AS lastAccessed FROM moz_cookies"; //NON-NLS
@@ -132,11 +133,13 @@ class Firefox extends Extract {
List<HashMap<String, Object>> tempList = this.dbConnect(temps, HISTORY_QUERY);
logger.log(Level.INFO, "{0} - Now getting history from {1} with {2} artifacts identified.", new Object[]{moduleName, temps, tempList.size()}); //NON-NLS
for (HashMap<String, Object> result : tempList) {
String url = result.get("url").toString();
Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
NbBundle.getMessage(this.getClass(),
"Firefox.parentModuleName.noSpace"),
((result.get("url").toString() != null) ? result.get("url").toString() : ""))); //NON-NLS
((url != null) ? url : ""))); //NON-NLS
//bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL_DECODED.getTypeID(), "RecentActivity", ((result.get("url").toString() != null) ? EscapeUtil.decodeURL(result.get("url").toString()) : "")));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
NbBundle.getMessage(this.getClass(),
@@ -154,10 +157,12 @@ class Firefox extends Extract {
NbBundle.getMessage(this.getClass(),
"Firefox.parentModuleName.noSpace"),
NbBundle.getMessage(this.getClass(), "Firefox.moduleName")));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
NbBundle.getMessage(this.getClass(),
"Firefox.parentModuleName.noSpace"), (Util.extractDomain((result.get("url").toString() != null) ? result.get("url").toString() : "")))); //NON-NLS
if (isIgnoredUrl(url) == false) {
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
NbBundle.getMessage(this.getClass(),
"Firefox.parentModuleName.noSpace"), Util.extractDomain(url))); //NON-NLS
}
BlackboardArtifact bbart = this.addArtifact(ARTIFACT_TYPE.TSK_WEB_HISTORY, historyFile, bbattributes);
if (bbart != null) {
bbartifacts.add(bbart);
@@ -226,12 +231,13 @@ class Firefox extends Extract {
List<HashMap<String, Object>> tempList = this.dbConnect(temps, BOOKMARK_QUERY);
logger.log(Level.INFO, "{0} - Now getting bookmarks from {1} with {2} artifacts identified.", new Object[]{moduleName, temps, tempList.size()}); //NON-NLS
for (HashMap<String, Object> result : tempList) {
String url = result.get("url").toString();
Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
NbBundle.getMessage(this.getClass(),
"Firefox.parentModuleName.noSpace"),
((result.get("url").toString() != null) ? result.get("url").toString() : ""))); //NON-NLS
((url != null) ? url : ""))); //NON-NLS
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_TITLE,
NbBundle.getMessage(this.getClass(),
"Firefox.parentModuleName.noSpace"),
@@ -246,10 +252,12 @@ class Firefox extends Extract {
NbBundle.getMessage(this.getClass(),
"Firefox.parentModuleName.noSpace"),
NbBundle.getMessage(this.getClass(), "Firefox.moduleName")));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
NbBundle.getMessage(this.getClass(),
"Firefox.parentModuleName.noSpace"),
(Util.extractDomain((result.get("url").toString() != null) ? result.get("url").toString() : "")))); //NON-NLS
if (isIgnoredUrl(url) == false) {
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
NbBundle.getMessage(this.getClass(),
"Firefox.parentModuleName.noSpace"),
Util.extractDomain(url))); //NON-NLS
}
BlackboardArtifact bbart = this.addArtifact(ARTIFACT_TYPE.TSK_WEB_BOOKMARK, bookmarkFile, bbattributes);
if (bbart != null) {
@@ -327,12 +335,13 @@ class Firefox extends Extract {
List<HashMap<String, Object>> tempList = this.dbConnect(temps, query);
logger.log(Level.INFO, "{0} - Now getting cookies from {1} with {2} artifacts identified.", new Object[]{moduleName, temps, tempList.size()}); //NON-NLS
for (HashMap<String, Object> result : tempList) {
String host = result.get("host").toString();
Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
NbBundle.getMessage(this.getClass(),
"Firefox.parentModuleName.noSpace"),
((result.get("host").toString() != null) ? result.get("host").toString() : ""))); //NON-NLS
((host != null) ? host : ""))); //NON-NLS
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME,
NbBundle.getMessage(this.getClass(),
"Firefox.parentModuleName.noSpace"),
@@ -356,11 +365,13 @@ class Firefox extends Extract {
"Firefox.parentModuleName.noSpace"),
(Long.valueOf(result.get("creationTime").toString())))); //NON-NLS
}
String domain = Util.extractDomain(result.get("host").toString()); //NON-NLS
domain = domain.replaceFirst("^\\.+(?!$)", "");
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
NbBundle.getMessage(this.getClass(),
"Firefox.parentModuleName.noSpace"), domain));
if (isIgnoredUrl(host) == false) {
String domain = Util.extractDomain(host); //NON-NLS
domain = domain.replaceFirst("^\\.+(?!$)", "");
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
NbBundle.getMessage(this.getClass(),
"Firefox.parentModuleName.noSpace"), domain));
}
BlackboardArtifact bbart = this.addArtifact(ARTIFACT_TYPE.TSK_WEB_COOKIE, cookiesFile, bbattributes);
if (bbart != null) {
@@ -442,13 +453,14 @@ class Firefox extends Extract {
List<HashMap<String, Object>> tempList = this.dbConnect(temps, DOWNLOAD_QUERY);
logger.log(Level.INFO, "{0}- Now getting downloads from {1} with {2} artifacts identified.", new Object[]{moduleName, temps, tempList.size()}); //NON-NLS
for (HashMap<String, Object> result : tempList) {
String source = result.get("source").toString();
Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
NbBundle.getMessage(this.getClass(),
"Firefox.parentModuleName.noSpace"),
((result.get("source").toString() != null) ? result.get("source").toString() : ""))); //NON-NLS
source)); //NON-NLS
//bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL_DECODED.getTypeID(), "RecentActivity", ((result.get("source").toString() != null) ? EscapeUtil.decodeURL(result.get("source").toString()) : "")));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
NbBundle.getMessage(this.getClass(),
@@ -481,10 +493,12 @@ class Firefox extends Extract {
NbBundle.getMessage(this.getClass(),
"Firefox.parentModuleName.noSpace"),
NbBundle.getMessage(this.getClass(), "Firefox.moduleName")));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
NbBundle.getMessage(this.getClass(),
"Firefox.parentModuleName.noSpace"),
(Util.extractDomain((result.get("source").toString() != null) ? result.get("source").toString() : "")))); //NON-NLS
if (isIgnoredUrl(source) == false) {
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
NbBundle.getMessage(this.getClass(),
"Firefox.parentModuleName.noSpace"),
Util.extractDomain(source))); //NON-NLS
}
BlackboardArtifact bbart = this.addArtifact(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadsFile, bbattributes);
if (bbart != null) {
@@ -565,13 +579,14 @@ class Firefox extends Extract {
logger.log(Level.INFO, "{0} - Now getting downloads from {1} with {2} artifacts identified.", new Object[]{moduleName, temps, tempList.size()}); //NON-NLS
for (HashMap<String, Object> result : tempList) {
String url = result.get("url").toString();
Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
NbBundle.getMessage(this.getClass(),
"Firefox.parentModuleName.noSpace"),
((result.get("url").toString() != null) ? result.get("url").toString() : ""))); //NON-NLS
url)); //NON-NLS
//bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL_DECODED.getTypeID(), "RecentActivity", ((result.get("source").toString() != null) ? EscapeUtil.decodeURL(result.get("source").toString()) : "")));
//TODO Revisit usage of deprecated constructor as per TSK-583
//bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_LAST_ACCESSED.getTypeID(), "RecentActivity", "Last Visited", (Long.valueOf(result.get("startTime").toString()))));
@@ -604,10 +619,12 @@ class Firefox extends Extract {
NbBundle.getMessage(this.getClass(),
"Firefox.parentModuleName.noSpace"),
NbBundle.getMessage(this.getClass(), "Firefox.moduleName")));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
NbBundle.getMessage(this.getClass(),
"Firefox.parentModuleName.noSpace"),
(Util.extractDomain((result.get("url").toString() != null) ? result.get("url").toString() : "")))); //NON-NLS
if (isIgnoredUrl(url) == false) {
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
NbBundle.getMessage(this.getClass(),
"Firefox.parentModuleName.noSpace"),
Util.extractDomain(url))); //NON-NLS
}
BlackboardArtifact bbart = this.addArtifact(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadsFile, bbattributes);
if (bbart != null) {
@@ -627,4 +644,26 @@ class Firefox extends Extract {
NbBundle.getMessage(this.getClass(), "Firefox.parentModuleName"),
BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, bbartifacts));
}
/**
* Determine if the URL should be ignored.
*
* @param url The URL to test.
*
* @return True if the URL should be ignored; otherwise false.
*/
private boolean isIgnoredUrl(String url) {
if (url == null || url.isEmpty()) {
return true;
}
if (url.toLowerCase().startsWith(PLACE_URL_PREFIX)) {
/*
* Ignore URLs that begin with the matched text.
*/
return true;
}
return false;
}
}
@@ -84,7 +84,12 @@ class Util {
}
}
public static String getBaseDomain(String url) {
/**
*
* @param url
* @return empty string if no domain could be found
*/
private static String getBaseDomain(String url) {
String host = null;
//strip protocol
@@ -113,10 +118,21 @@ class Util {
hostB.append(".");
}
}
return hostB.toString();
String base = hostB.toString();
// verify there are no special characters in there
if (base.matches(".*[~`!@#$%^&\\*\\(\\)\\+={}\\[\\];:\\?<>,/ ].*")) {
return "";
}
return base;
}
/**
*
* @param value
* @return empty string if no domain name was found
*/
public static String extractDomain(String value) {
if (value == null) {
return "";
@@ -1,5 +1,5 @@
#Updated by build script
#Fri, 05 Oct 2018 09:58:28 -0400
#Sat, 13 Oct 2018 21:02:18 -0400
LBL_splash_window_title=Starting Autopsy
SPLASH_HEIGHT=314
SPLASH_WIDTH=538
@@ -1,4 +1,4 @@
#Updated by build script
#Fri, 05 Oct 2018 09:58:28 -0400
#Sat, 13 Oct 2018 21:02:18 -0400
CTL_MainWindow_Title=Autopsy 4.9.0
CTL_MainWindow_Title_No_Project=Autopsy 4.9.0
+1 -1
View File
@@ -36,7 +36,7 @@
<compile-dependency/>
<run-dependency>
<release-version>10</release-version>
<specification-version>10.12</specification-version>
<specification-version>10.13</specification-version>
</run-dependency>
</dependency>
<dependency>