Merge branch 'release-4.12.0' of https://github.com/sleuthkit/autopsy into 5187-LogicalImagerGUITweaks

This commit is contained in:
William Schaefer 2019-06-24 09:41:15 -04:00
commit 78e8ecfbb3
34 changed files with 1130 additions and 254 deletions

View File

@ -75,7 +75,7 @@ file.reference.javax.ws.rs-api-2.0.1.jar=release/modules/ext/javax.ws.rs-api-2.0
file.reference.cxf-core-3.0.16.jar=release/modules/ext/cxf-core-3.0.16.jar
file.reference.cxf-rt-frontend-jaxrs-3.0.16.jar=release/modules/ext/cxf-rt-frontend-jaxrs-3.0.16.jar
file.reference.cxf-rt-transports-http-3.0.16.jar=release/modules/ext/cxf-rt-transports-http-3.0.16.jar
file.reference.sleuthkit-postgresql-4.6.6.jar=release/modules/ext/sleuthkit-postgresql-4.6.6.jar
file.reference.sleuthkit-postgresql-4.6.7.jar=release/modules/ext/sleuthkit-postgresql-4.6.7.jar
file.reference.curator-client-2.8.0.jar=release/modules/ext/curator-client-2.8.0.jar
file.reference.curator-framework-2.8.0.jar=release/modules/ext/curator-framework-2.8.0.jar
file.reference.curator-recipes-2.8.0.jar=release/modules/ext/curator-recipes-2.8.0.jar

View File

@ -482,8 +482,8 @@
<binary-origin>release\modules\ext\jmatio-1.5.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/sleuthkit-postgresql-4.6.6.jar</runtime-relative-path>
<binary-origin>release/modules/ext/sleuthkit-postgresql-4.6.6.jar</binary-origin>
<runtime-relative-path>ext/sleuthkit-postgresql-4.6.7.jar</runtime-relative-path>
<binary-origin>release/modules/ext/sleuthkit-postgresql-4.6.7.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/tika-parsers-1.20.jar</runtime-relative-path>

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 B

View File

@ -11,5 +11,13 @@ SummaryViewer.emailDataLabel.text=emails
SummaryViewer.attachmentsDataLabel.text=attachments
SummaryViewer.messagesLabel.text=Messages:
SummaryViewer.callLogsLabel.text=Call Logs:
ThreadRootMessagePanel.showAllCheckBox.text=Show All Messages
ThreadPane.backButton.text=<---
SummaryViewer.caseReferencesPanel.border.title=Other Occurrences
SummaryViewer.fileReferencesPanel.border.title=File References in Current Case
MessageViewer.threadsLabel.text=Select a Thread to View
MessageViewer.threadNameLabel.text=<threadName>
MessageViewer.showingMessagesLabel.text=Showing Messages for Thread:
MessageViewer.backButton.AccessibleContext.accessibleDescription=
MessageViewer.backButton.text=Threads
MessageViewer.showAllButton.text=All Messages

View File

@ -19,12 +19,16 @@ MessageNode_Node_Property_Subject=Subject
MessageNode_Node_Property_To=To
MessageNode_Node_Property_Type=Type
MessageViewer_columnHeader_Attms=Attachments
MessageViewer_columnHeader_Date=Date
MessageViewer_columnHeader_Date=Data
MessageViewer_columnHeader_EarlyDate=Earliest Message
MessageViewer_columnHeader_From=From
MessageViewer_columnHeader_Subject=Subject
MessageViewer_columnHeader_To=To
MessageViewer_no_messages=<No messages found for selected account>
MessageViewer_tabTitle=Messages
MessageViewer_viewMessage_all=All
MessageViewer_viewMessage_selected=Selected
MessageViewer_viewMessage_unthreaded=Unthreaded
SummaryViewer.countsPanel.border.title=Counts
SummaryViewer.emailLabel.text=Emails:
SummaryViewer.contactsLabel.text=Contacts:
@ -37,11 +41,19 @@ SummaryViewer.emailDataLabel.text=emails
SummaryViewer.attachmentsDataLabel.text=attachments
SummaryViewer.messagesLabel.text=Messages:
SummaryViewer.callLogsLabel.text=Call Logs:
SummaryViewer.caseReferencesPanel.border.title=Other Occurrences
SummaryViewer.fileReferencesPanel.border.title=File References in Current Case
SummaryViewer_CaseRefNameColumn_Title=Case Name
SummaryViewer_CentralRepository_Message=<Enable Central Resposity to see Other Occurrences>
SummaryViewer_Creation_Date_Title=Creation Date
SummaryViewer_FileRefNameColumn_Title=Path
SummaryViewer_TabTitle=Summary
SummeryViewer_FileRef_Message=<Select one Accout to see File References>
ThreadRootMessagePanel.showAllCheckBox.text=Show All Messages
ThreadPane.backButton.text=<---
SummaryViewer.caseReferencesPanel.border.title=Other Occurrences
SummaryViewer.fileReferencesPanel.border.title=File References in Current Case
MessageViewer.threadsLabel.text=Select a Thread to View
MessageViewer.threadNameLabel.text=<threadName>
MessageViewer.showingMessagesLabel.text=Showing Messages for Thread:
MessageViewer.backButton.AccessibleContext.accessibleDescription=
MessageViewer.backButton.text=Threads
MessageViewer.showAllButton.text=All Messages

View File

@ -17,34 +17,7 @@
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="propertySheet" max="32767" attributes="0"/>
<Group type="102" attributes="0">
<Component id="nameLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
</Group>
<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="-2" attributes="0"/>
<Component id="nameLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="propertySheet" pref="283" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<SubComponents>
<Component class="javax.swing.JLabel" name="nameLabel">
<Properties>
@ -55,11 +28,21 @@
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="ContactDetailsPane.nameLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="16" insetsLeft="15" insetsBottom="15" insetsRight="15" anchor="23" weightX="1.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="org.openide.explorer.propertysheet.PropertySheet" name="propertySheet">
<Properties>
<Property name="descriptionAreaVisible" type="boolean" value="false"/>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="1" gridWidth="2" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="9" insetsLeft="15" insetsBottom="16" insetsRight="15" anchor="18" weightX="1.0" weightY="1.0"/>
</Constraint>
</Constraints>
</Component>
</SubComponents>
</Form>

View File

@ -73,38 +73,36 @@ public final class ContactDetailsPane extends javax.swing.JPanel implements Expl
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
java.awt.GridBagConstraints gridBagConstraints;
messageContentViewer1 = new org.sleuthkit.autopsy.contentviewers.MessageContentViewer();
nameLabel = new javax.swing.JLabel();
propertySheet = new org.openide.explorer.propertysheet.PropertySheet();
setLayout(new java.awt.GridBagLayout());
nameLabel.setFont(new java.awt.Font("Tahoma", 0, 24)); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(nameLabel, org.openide.util.NbBundle.getMessage(ContactDetailsPane.class, "ContactDetailsPane.nameLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.insets = new java.awt.Insets(16, 15, 15, 15);
add(nameLabel, gridBagConstraints);
propertySheet.setDescriptionAreaVisible(false);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(propertySheet, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup()
.addComponent(nameLabel)
.addGap(0, 0, Short.MAX_VALUE)))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(nameLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(propertySheet, javax.swing.GroupLayout.DEFAULT_SIZE, 283, Short.MAX_VALUE)
.addContainerGap())
);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 1;
gridBagConstraints.gridwidth = 2;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.weighty = 1.0;
gridBagConstraints.insets = new java.awt.Insets(9, 15, 16, 15);
add(propertySheet, gridBagConstraints);
}// </editor-fold>//GEN-END:initComponents

View File

@ -36,7 +36,7 @@ import org.sleuthkit.datamodel.TskCoreException;
* ChildFactory for ContactNodes.
*/
final class ContactsChildNodeFactory extends ChildFactory<BlackboardArtifact>{
private static final Logger logger = Logger.getLogger(MessagesChildNodeFactory.class.getName());
private static final Logger logger = Logger.getLogger(ContactsChildNodeFactory.class.getName());
private SelectionInfo selectionInfo;

View File

@ -20,11 +20,11 @@ package org.sleuthkit.autopsy.communications.relationships;
import java.util.TimeZone;
import java.util.logging.Level;
import javax.swing.Action;
import org.apache.commons.lang3.StringUtils;
import org.openide.nodes.Sheet;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode;
import org.sleuthkit.autopsy.datamodel.NodeProperty;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
@ -40,20 +40,31 @@ import static org.sleuthkit.datamodel.BlackboardAttribute.TSK_BLACKBOARD_ATTRIBU
import org.sleuthkit.datamodel.TimeUtilities;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.autopsy.communications.Utils;
import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode;
/**
* Wraps a BlackboardArtifact as an AbstractNode for use in an OutlookView
*/
final class MessageNode extends BlackboardArtifactNode {
class MessageNode extends BlackboardArtifactNode {
public static final String UNTHREADED_ID = "<UNTHREADED>";
private static final Logger logger = Logger.getLogger(MessageNode.class.getName());
MessageNode(BlackboardArtifact artifact) {
private final String threadID;
private final Action preferredAction;
MessageNode(BlackboardArtifact artifact, String threadID, Action preferredAction) {
super(artifact);
this.preferredAction = preferredAction;
final String stripEnd = StringUtils.stripEnd(artifact.getDisplayName(), "s"); // NON-NLS
String removeEndIgnoreCase = StringUtils.removeEndIgnoreCase(stripEnd, "message"); // NON-NLS
setDisplayName(removeEndIgnoreCase.isEmpty() ? stripEnd : removeEndIgnoreCase);
this.threadID = threadID;
}
@Messages({
@ -76,6 +87,8 @@ final class MessageNode extends BlackboardArtifactNode {
sheetSet.put(new NodeProperty<>("Type", Bundle.MessageNode_Node_Property_Type(), "", getDisplayName())); //NON-NLS
sheetSet.put(new NodeProperty<>("ThreadID", "ThreadID","",threadID == null ? UNTHREADED_ID : threadID)); //NON-NLS
final BlackboardArtifact artifact = getArtifact();
BlackboardArtifact.ARTIFACT_TYPE fromID = BlackboardArtifact.ARTIFACT_TYPE.fromID(artifact.getArtifactTypeID());
@ -167,4 +180,13 @@ final class MessageNode extends BlackboardArtifactNode {
public String getSourceName() {
return getDisplayName();
}
String getThreadID() {
return threadID;
}
@Override
public Action getPreferredAction() {
return preferredAction;
}
}

View File

@ -0,0 +1,140 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,3,-126,0,0,2,-76"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignCardLayout"/>
<SubComponents>
<Container class="javax.swing.JPanel" name="rootMessagesPane">
<Properties>
<Property name="opaque" type="boolean" value="false"/>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignCardLayout" value="org.netbeans.modules.form.compat2.layouts.DesignCardLayout$CardConstraintsDescription">
<CardConstraints cardName="threads"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<SubComponents>
<Component class="javax.swing.JLabel" name="threadsLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/relationships/Bundle.properties" key="MessageViewer.threadsLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="15" insetsLeft="15" insetsBottom="9" insetsRight="0" anchor="18" weightX="1.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JButton" name="showAllButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/relationships/Bundle.properties" key="MessageViewer.showAllButton.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="showAllButtonActionPerformed"/>
</Events>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="2" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="15" insetsBottom="15" insetsRight="0" anchor="17" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel" name="rootTablePane">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.LineBorderInfo">
<LineBorder/>
</Border>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="1" gridWidth="2" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="15" insetsBottom="9" insetsRight="15" anchor="18" weightX="1.0" weightY="1.0"/>
</Constraint>
</Constraints>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="messagePanel">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignCardLayout" value="org.netbeans.modules.form.compat2.layouts.DesignCardLayout$CardConstraintsDescription">
<CardConstraints cardName="messages"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<SubComponents>
<Component class="org.sleuthkit.autopsy.communications.relationships.MessagesPanel" name="threadMessagesPanel">
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new MessagesPanel()"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="3" gridWidth="3" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="15" insetsBottom="0" insetsRight="15" anchor="18" weightX="1.0" weightY="1.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JButton" name="backButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/relationships/Bundle.properties" key="MessageViewer.backButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<AccessibilityProperties>
<Property name="AccessibleContext.accessibleDescription" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/relationships/Bundle.properties" key="MessageViewer.backButton.AccessibleContext.accessibleDescription" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</AccessibilityProperties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="backButtonActionPerformed"/>
</Events>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="2" gridY="0" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="9" insetsLeft="0" insetsBottom="9" insetsRight="15" anchor="13" weightX="1.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JLabel" name="showingMessagesLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/relationships/Bundle.properties" key="MessageViewer.showingMessagesLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="9" insetsLeft="15" insetsBottom="5" insetsRight="0" anchor="17" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JLabel" name="threadNameLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/relationships/Bundle.properties" key="MessageViewer.threadNameLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="1" gridY="0" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="9" insetsLeft="5" insetsBottom="5" insetsRight="15" anchor="17" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Form>

View File

@ -0,0 +1,439 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 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.communications.relationships;
import java.awt.CardLayout;
import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.KeyboardFocusManager;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyVetoException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.logging.Level;
import javax.swing.AbstractAction;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import static javax.swing.SwingUtilities.isDescendingFrom;
import org.netbeans.swing.outline.DefaultOutlineModel;
import org.netbeans.swing.outline.Outline;
import org.openide.explorer.ExplorerManager;
import static org.openide.explorer.ExplorerUtils.createLookup;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.nodes.Node.Property;
import org.openide.nodes.Node.PropertySet;
import org.openide.util.Exceptions;
import org.openide.util.Lookup;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.communications.ModifiableProxyLookup;
import org.sleuthkit.autopsy.coreutils.Logger;
/**
* The main panel for the messages tab of the RelationshipViewer
*
*/
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
public class MessageViewer extends JPanel implements RelationshipsViewer {
private static final Logger logger = Logger.getLogger(MessageViewer.class.getName());
private final ModifiableProxyLookup proxyLookup;
private final PropertyChangeListener focusPropertyListener;
private final ThreadChildNodeFactory rootMessageFactory;
private final MessagesChildNodeFactory threadMessageNodeFactory;
private SelectionInfo currentSelectionInfo = null;
OutlineViewPanel currentPanel;
@Messages({
"MessageViewer_tabTitle=Messages",
"MessageViewer_columnHeader_From=From",
"MessageViewer_columnHeader_Date=Data",
"MessageViewer_columnHeader_To=To",
"MessageViewer_columnHeader_EarlyDate=Earliest Message",
"MessageViewer_columnHeader_Subject=Subject",
"MessageViewer_columnHeader_Attms=Attachments",
"MessageViewer_no_messages=<No messages found for selected account>",
"MessageViewer_viewMessage_all=All",
"MessageViewer_viewMessage_selected=Selected",
"MessageViewer_viewMessage_unthreaded=Unthreaded",})
/**
* Creates new form MessageViewer
*/
public MessageViewer() {
initComponents();
currentPanel = rootTablePane;
proxyLookup = new ModifiableProxyLookup(createLookup(rootTablePane.getExplorerManager(), getActionMap()));
rootMessageFactory = new ThreadChildNodeFactory(new ShowThreadMessagesAction());
threadMessageNodeFactory = new MessagesChildNodeFactory();
// See org.sleuthkit.autopsy.timeline.TimeLineTopComponent for a detailed
// explaination of focusPropertyListener
focusPropertyListener = (final PropertyChangeEvent focusEvent) -> {
if (focusEvent.getPropertyName().equalsIgnoreCase("focusOwner")) {
final Component newFocusOwner = (Component) focusEvent.getNewValue();
if (newFocusOwner == null) {
return;
}
if (isDescendingFrom(newFocusOwner, rootTablePane)) {
proxyLookup.setNewLookups(createLookup(rootTablePane.getExplorerManager(), getActionMap()));
} else if (isDescendingFrom(newFocusOwner, MessageViewer.this)) {
proxyLookup.setNewLookups(createLookup(currentPanel.getExplorerManager(), getActionMap()));
}
}
};
rootTablePane.getExplorerManager().setRootContext(
new AbstractNode(Children.create(rootMessageFactory, true)));
rootTablePane.getOutlineView().setPopupAllowed(false);
Outline outline = rootTablePane.getOutlineView().getOutline();
rootTablePane.getOutlineView().setPropertyColumns(
"Date", Bundle.MessageViewer_columnHeader_EarlyDate(),
"Subject", Bundle.MessageViewer_columnHeader_Subject()
);
outline.setRootVisible(false);
((DefaultOutlineModel) outline.getOutlineModel()).setNodesColumnLabel("Type");
outline.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
rootTablePane.getExplorerManager().addPropertyChangeListener((PropertyChangeEvent evt) -> {
if (evt.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) {
showSelectedThread();
}
});
threadMessagesPanel.setChildFactory(threadMessageNodeFactory);
rootTablePane.setTableColumnsWidth(10, 20, 70);
Image image = getScaledImage((new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/timeline/images/arrow-180.png"))).getImage(), 16, 16);
backButton.setIcon(new ImageIcon(image) );
}
@Override
public String getDisplayName() {
return Bundle.MessageViewer_tabTitle();
}
@Override
public JPanel getPanel() {
return this;
}
@Override
public void setSelectionInfo(SelectionInfo info) {
currentSelectionInfo = info;
currentPanel = rootTablePane;
CardLayout layout = (CardLayout) this.getLayout();
layout.show(this, "threads");
rootMessageFactory.refresh(info);
}
@Override
public Lookup getLookup() {
return proxyLookup;
}
@Override
public void addNotify() {
super.addNotify();
//add listener that maintains correct selection in the Global Actions Context
KeyboardFocusManager.getCurrentKeyboardFocusManager()
.addPropertyChangeListener("focusOwner", focusPropertyListener);
}
@Override
public void removeNotify() {
super.removeNotify();
KeyboardFocusManager.getCurrentKeyboardFocusManager()
.removePropertyChangeListener("focusOwner", focusPropertyListener);
}
@SuppressWarnings("rawtypes")
private void showSelectedThread() {
final Node[] nodes = rootTablePane.getExplorerManager().getSelectedNodes();
if (nodes == null) {
return;
}
if (nodes.length == 0 || nodes.length > 1) {
return;
}
ArrayList<String> threadIDList = new ArrayList<>();
String subject = "";
PropertySet[] propertySets = nodes[0].getPropertySets();
for (PropertySet pset : propertySets) {
Property[] properties = pset.getProperties();
for (Property prop : properties) {
if (prop.getName().equalsIgnoreCase("threadid")) {
try {
String threadID = prop.getValue().toString();
if (!threadIDList.contains(threadID)) {
threadIDList.add(threadID);
}
} catch (IllegalAccessException | InvocationTargetException ex) {
logger.log(Level.WARNING, String.format("Unable to get threadid for node: %s", nodes[0].getDisplayName()), ex);
}
} else if (prop.getName().equalsIgnoreCase("subject")) {
try {
subject = prop.getValue().toString();
} catch (IllegalAccessException | InvocationTargetException ex) {
logger.log(Level.WARNING, String.format("Unable to get subject for node: %s", nodes[0].getDisplayName()), ex);
subject = "<unavailable>";
}
}
}
}
if (!threadIDList.isEmpty()) {
threadMessageNodeFactory.refresh(currentSelectionInfo, threadIDList);
if (!subject.isEmpty()) {
threadNameLabel.setText(subject);
} else {
threadNameLabel.setText(Bundle.MessageViewer_viewMessage_unthreaded());
}
showMessagesPane();
}
}
/**
* Make the threads pane visible.
*/
private void showThreadsPane() {
switchCard("threads");
}
/**
* Make the message pane visible.
*/
private void showMessagesPane() {
switchCard("messages");
}
/**
* Changes the visible panel (card).
*
* @param cardName Name of card to show
*/
private void switchCard(String cardName) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
CardLayout layout = (CardLayout)getLayout();
layout.show(MessageViewer.this, cardName);
}
});
}
/**
* Scales the given image to the given width and height.
*
* @param srcImg Image to scale
* @param w Image width
* @param h Image height
*
* @return Scaled version of srcImg
*/
private Image getScaledImage(Image srcImg, int w, int h){
BufferedImage resizedImg = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = resizedImg.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2.drawImage(srcImg, 0, 0, w, h, null);
g2.dispose();
return resizedImg;
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.'
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
java.awt.GridBagConstraints gridBagConstraints;
rootMessagesPane = new javax.swing.JPanel();
threadsLabel = new javax.swing.JLabel();
showAllButton = new javax.swing.JButton();
rootTablePane = new org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel();
messagePanel = new javax.swing.JPanel();
threadMessagesPanel = new MessagesPanel();
backButton = new javax.swing.JButton();
showingMessagesLabel = new javax.swing.JLabel();
threadNameLabel = new javax.swing.JLabel();
setLayout(new java.awt.CardLayout());
rootMessagesPane.setOpaque(false);
rootMessagesPane.setLayout(new java.awt.GridBagLayout());
org.openide.awt.Mnemonics.setLocalizedText(threadsLabel, org.openide.util.NbBundle.getMessage(MessageViewer.class, "MessageViewer.threadsLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.insets = new java.awt.Insets(15, 15, 9, 0);
rootMessagesPane.add(threadsLabel, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(showAllButton, org.openide.util.NbBundle.getMessage(MessageViewer.class, "MessageViewer.showAllButton.text")); // NOI18N
showAllButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
showAllButtonActionPerformed(evt);
}
});
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 2;
gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
gridBagConstraints.insets = new java.awt.Insets(0, 15, 15, 0);
rootMessagesPane.add(showAllButton, gridBagConstraints);
rootTablePane.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0)));
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 1;
gridBagConstraints.gridwidth = 2;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.weighty = 1.0;
gridBagConstraints.insets = new java.awt.Insets(0, 15, 9, 15);
rootMessagesPane.add(rootTablePane, gridBagConstraints);
add(rootMessagesPane, "threads");
messagePanel.setLayout(new java.awt.GridBagLayout());
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 3;
gridBagConstraints.gridwidth = 3;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.weighty = 1.0;
gridBagConstraints.insets = new java.awt.Insets(0, 15, 0, 15);
messagePanel.add(threadMessagesPanel, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(backButton, org.openide.util.NbBundle.getMessage(MessageViewer.class, "MessageViewer.backButton.text")); // NOI18N
backButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
backButtonActionPerformed(evt);
}
});
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 2;
gridBagConstraints.gridy = 0;
gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.insets = new java.awt.Insets(9, 0, 9, 15);
messagePanel.add(backButton, gridBagConstraints);
backButton.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(MessageViewer.class, "MessageViewer.backButton.AccessibleContext.accessibleDescription")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(showingMessagesLabel, org.openide.util.NbBundle.getMessage(MessageViewer.class, "MessageViewer.showingMessagesLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
gridBagConstraints.insets = new java.awt.Insets(9, 15, 5, 0);
messagePanel.add(showingMessagesLabel, gridBagConstraints);
org.openide.awt.Mnemonics.setLocalizedText(threadNameLabel, org.openide.util.NbBundle.getMessage(MessageViewer.class, "MessageViewer.threadNameLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 0;
gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
gridBagConstraints.insets = new java.awt.Insets(9, 5, 5, 15);
messagePanel.add(threadNameLabel, gridBagConstraints);
add(messagePanel, "messages");
}// </editor-fold>//GEN-END:initComponents
private void backButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_backButtonActionPerformed
try {
rootTablePane.getExplorerManager().setSelectedNodes(new Node[0]);
} catch (PropertyVetoException ex) {
Exceptions.printStackTrace(ex);
}
showThreadsPane();
}//GEN-LAST:event_backButtonActionPerformed
private void showAllButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_showAllButtonActionPerformed
threadMessageNodeFactory.refresh(currentSelectionInfo, null);
threadNameLabel.setText("All Messages");
showMessagesPane();
}//GEN-LAST:event_showAllButtonActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton backButton;
private javax.swing.JPanel messagePanel;
private javax.swing.JPanel rootMessagesPane;
private org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel rootTablePane;
private javax.swing.JButton showAllButton;
private javax.swing.JLabel showingMessagesLabel;
private org.sleuthkit.autopsy.communications.relationships.MessagesPanel threadMessagesPanel;
private javax.swing.JLabel threadNameLabel;
private javax.swing.JLabel threadsLabel;
// End of variables declaration//GEN-END:variables
/**
* The preferred action of the table nodes.
*/
class ShowThreadMessagesAction extends AbstractAction {
@Override
public void actionPerformed(ActionEvent e) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
showSelectedThread();
}
});
}
}
}

View File

@ -27,52 +27,51 @@ import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.CommunicationsManager;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskCoreException;
/**
* ChildFactory that creates createKeys and nodes from a given selectionInfo for
* only emails, call logs and messages.
* A ChildFactory subclass for creating MessageNodes from a set of
* BlackboardArtifact objects.
*
*/
final class MessagesChildNodeFactory extends ChildFactory<BlackboardArtifact> {
public class MessagesChildNodeFactory extends ChildFactory<BlackboardArtifact>{
private static final Logger logger = Logger.getLogger(MessagesChildNodeFactory.class.getName());
private SelectionInfo selectionInfo;
/**
* Construct a new MessageChildNodeFactory from the currently selectionInfo
*
* @param selectionInfo SelectionInfo object for the currently selected
* accounts
*/
MessagesChildNodeFactory(SelectionInfo selectionInfo) {
private List<String> threadIDs;
MessagesChildNodeFactory(SelectionInfo selectionInfo, List<String> threadIDs) {
this.selectionInfo = selectionInfo;
this.threadIDs = threadIDs;
}
MessagesChildNodeFactory() {
this(null, null);
}
/**
* Updates the current instance of selectionInfo and calls the refresh method.
*
* @param selectionInfo New instance of the currently selected accounts
* @param threadIDs A list of threadIDs to filter the keys by, null will
* return all keys for the selected accounts.
*/
public void refresh(SelectionInfo selectionInfo) {
public void refresh(SelectionInfo selectionInfo, List<String> threadIDs) {
this.threadIDs = threadIDs;
this.selectionInfo = selectionInfo;
refresh(true);
}
/**
* Creates a list of Keys (BlackboardArtifact) for only messages for the
* currently selected accounts
*
* @param list List of BlackboardArtifact to populate
*
* @return True on success
*/
@Override
protected boolean createKeys(List<BlackboardArtifact> list) {
CommunicationsManager communicationManager;
try {
communicationManager = Case.getCurrentCaseThrows().getSleuthkitCase().getCommunicationsManager();
} catch (NoCurrentCaseException | TskCoreException ex) {
@ -87,19 +86,37 @@ final class MessagesChildNodeFactory extends ChildFactory<BlackboardArtifact> {
final Set<Content> relationshipSources;
try {
relationshipSources = communicationManager.getRelationshipSources(selectionInfo.getAccountDevicesInstances(), selectionInfo.getCommunicationsFilter());
relationshipSources.stream().filter((content) -> (content instanceof BlackboardArtifact)).forEachOrdered((content) -> {
relationshipSources = communicationManager.getRelationshipSources(selectionInfo.getAccountDevicesInstances(), selectionInfo.getCommunicationsFilter());
for(Content content: relationshipSources) {
if( !(content instanceof BlackboardArtifact)){
continue;
}
BlackboardArtifact bba = (BlackboardArtifact) content;
BlackboardArtifact.ARTIFACT_TYPE fromID = BlackboardArtifact.ARTIFACT_TYPE.fromID(bba.getArtifactTypeID());
if (fromID == BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG
|| fromID == BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG
|| fromID == BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE) {
if (fromID != BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG
&& fromID != BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG
&& fromID != BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE) {
continue;
}
// We want all artifacts that do not have "threadIDs" to appear as one thread in the UI
// To achive this assign any artifact that does not have a threadID
// the "UNTHREADED_ID"
String artifactThreadID = MessageNode.UNTHREADED_ID;
BlackboardAttribute attribute = bba.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_THREAD_ID));
if(attribute != null) {
artifactThreadID = attribute.getValueString();
}
if(threadIDs == null || threadIDs.contains(artifactThreadID)) {
list.add(bba);
}
});
}
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Failed to get relationship sources.", ex); //NON-NLS
@ -110,6 +127,7 @@ final class MessagesChildNodeFactory extends ChildFactory<BlackboardArtifact> {
@Override
protected Node createNodeForKey(BlackboardArtifact key) {
return new MessageNode(key);
return new MessageNode(key, null, null);
}
}

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.4" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
@ -11,33 +11,23 @@
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,3,13,0,0,2,103"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,44,0,0,1,-112"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
<SubComponents>
<Container class="javax.swing.JSplitPane" name="splitPane">
<Properties>
<Property name="orientation" type="int" value="0"/>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="1.0" weightY="1.0"/>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
<BorderConstraints direction="Center"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
<SubComponents>
<Component class="org.sleuthkit.autopsy.contentviewers.MessageContentViewer" name="contentViewer">
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new MessageDataContent()"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
<JSplitPaneConstraints position="bottom"/>
</Constraint>
</Constraints>
</Component>
<Component class="org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel" name="outlineViewPanel">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
@ -45,6 +35,16 @@
</Constraint>
</Constraints>
</Component>
<Component class="org.sleuthkit.autopsy.contentviewers.MessageContentViewer" name="messageContentViewer">
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new MessageDataContent()"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
<JSplitPaneConstraints position="right"/>
</Constraint>
</Constraints>
</Component>
</SubComponents>
</Container>
</SubComponents>

View File

@ -6,7 +6,7 @@
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obt ain a copy of the License at
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
@ -22,57 +22,38 @@ import java.awt.Component;
import java.awt.KeyboardFocusManager;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JPanel;
import static javax.swing.SwingUtilities.isDescendingFrom;
import org.netbeans.swing.outline.DefaultOutlineModel;
import org.netbeans.swing.outline.Outline;
import org.openide.explorer.ExplorerManager;
import static org.openide.explorer.ExplorerUtils.createLookup;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.nodes.NodeAdapter;
import org.openide.nodes.NodeMemberEvent;
import org.openide.util.Lookup;
import org.openide.util.NbBundle.Messages;
import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.communications.ModifiableProxyLookup;
import org.sleuthkit.autopsy.corecomponents.TableFilterNode;
import org.sleuthkit.autopsy.directorytree.DataResultFilterNode;
/**
* Visualation for the messages of the currently selected accounts.
*
* General Purpose class for panels that need OutlineView of message nodes at
* the top with a MessageContentViewer at the bottom.
*/
@ServiceProvider(service = RelationshipsViewer.class)
public final class MessagesViewer extends JPanel implements RelationshipsViewer {
public class MessagesPanel extends javax.swing.JPanel implements Lookup.Provider {
private final Outline outline;
private final ModifiableProxyLookup proxyLookup;
private final PropertyChangeListener focusPropertyListener;
private final MessagesChildNodeFactory nodeFactory;
@Messages({
"MessageViewer_tabTitle=Messages",
"MessageViewer_columnHeader_From=From",
"MessageViewer_columnHeader_To=To",
"MessageViewer_columnHeader_Date=Date",
"MessageViewer_columnHeader_Subject=Subject",
"MessageViewer_columnHeader_Attms=Attachments",
"MessageViewer_no_messages=<No messages found for selected account>"
})
/**
* Visualation for the messages of the currently selected accounts.
* Creates new form MessagesPanel
*/
public MessagesViewer() {
public MessagesPanel() {
initComponents();
splitPane.setResizeWeight(0.5);
outlineViewPanel.hideOutlineView(Bundle.MessageViewer_no_messages());
proxyLookup = new ModifiableProxyLookup(createLookup(outlineViewPanel.getExplorerManager(), getActionMap()));
nodeFactory = new MessagesChildNodeFactory(null);
// See org.sleuthkit.autopsy.timeline.TimeLineTopComponent for a detailed
// explaination of focusPropertyListener
@ -83,10 +64,10 @@ public final class MessagesViewer extends JPanel implements RelationshipsViewer
if (newFocusOwner == null) {
return;
}
if (isDescendingFrom(newFocusOwner, contentViewer)) {
if (isDescendingFrom(newFocusOwner, messageContentViewer)) {
//if the focus owner is within the MessageContentViewer (the attachments table)
proxyLookup.setNewLookups(createLookup(((MessageDataContent) contentViewer).getExplorerManager(), getActionMap()));
} else if (isDescendingFrom(newFocusOwner, MessagesViewer.this)) {
proxyLookup.setNewLookups(createLookup(((MessageDataContent) messageContentViewer).getExplorerManager(), getActionMap()));
} else if (isDescendingFrom(newFocusOwner, MessagesPanel.this)) {
//... or if it is within the Results table.
proxyLookup.setNewLookups(createLookup(outlineViewPanel.getExplorerManager(), getActionMap()));
@ -110,52 +91,22 @@ public final class MessagesViewer extends JPanel implements RelationshipsViewer
final Node[] nodes = outlineViewPanel.getExplorerManager().getSelectedNodes();
if (nodes != null && nodes.length == 1) {
contentViewer.setNode(nodes[0]);
messageContentViewer.setNode(nodes[0]);
}
else {
contentViewer.setNode(null);
messageContentViewer.setNode(null);
}
}
});
outlineViewPanel.getExplorerManager().setRootContext(
new TableFilterNode(
new DataResultFilterNode(
new AbstractNode(
Children.create(nodeFactory, true)),
outlineViewPanel.getExplorerManager()),
true));
// When a new set of nodes are added to the OutlineView the childrenAdded
// seems to be fired before the childrenRemoved.
outlineViewPanel.getExplorerManager().getRootContext().addNodeListener(new NodeAdapter() {
@Override
public void childrenAdded(NodeMemberEvent nme) {
updateOutlineViewPanel();
}
@Override
public void childrenRemoved(NodeMemberEvent nme) {
updateOutlineViewPanel();
}
});
splitPane.setResizeWeight(0.5);
splitPane.setDividerLocation(0.5);
outlineViewPanel.setTableColumnsWidth(5,10,10,15,50,10);
}
@Override
public String getDisplayName() {
return Bundle.MessageViewer_tabTitle();
}
@Override
public JPanel getPanel() {
return this;
}
@Override
public void setSelectionInfo(SelectionInfo info) {
nodeFactory.refresh(info);
public MessagesPanel(ChildFactory<?> nodeFactory) {
this();
setChildFactory(nodeFactory);
}
@Override
@ -178,13 +129,13 @@ public final class MessagesViewer extends JPanel implements RelationshipsViewer
.removePropertyChangeListener("focusOwner", focusPropertyListener);
}
private void updateOutlineViewPanel() {
int nodeCount = outlineViewPanel.getExplorerManager().getRootContext().getChildren().getNodesCount();
if(nodeCount == 0) {
outlineViewPanel.hideOutlineView(Bundle.MessageViewer_no_messages());
} else {
outlineViewPanel.showOutlineView();
}
final void setChildFactory(ChildFactory<?> nodeFactory) {
outlineViewPanel.getExplorerManager().setRootContext(
new TableFilterNode(
new DataResultFilterNode(
new AbstractNode(
Children.create(nodeFactory, true)),
outlineViewPanel.getExplorerManager()),true));
}
/**
@ -195,31 +146,23 @@ public final class MessagesViewer extends JPanel implements RelationshipsViewer
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
java.awt.GridBagConstraints gridBagConstraints;
splitPane = new javax.swing.JSplitPane();
contentViewer = new MessageDataContent();
outlineViewPanel = new org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel();
messageContentViewer = new MessageDataContent();
setLayout(new java.awt.GridBagLayout());
setLayout(new java.awt.BorderLayout());
splitPane.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);
splitPane.setBottomComponent(contentViewer);
splitPane.setLeftComponent(outlineViewPanel);
splitPane.setRightComponent(messageContentViewer);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.weighty = 1.0;
add(splitPane, gridBagConstraints);
add(splitPane, java.awt.BorderLayout.CENTER);
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
private org.sleuthkit.autopsy.contentviewers.MessageContentViewer contentViewer;
private org.sleuthkit.autopsy.contentviewers.MessageContentViewer messageContentViewer;
private org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel outlineViewPanel;
private javax.swing.JSplitPane splitPane;
// End of variables declaration//GEN-END:variables

View File

@ -1,9 +1,6 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<Properties>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
@ -23,12 +20,6 @@
</Layout>
<SubComponents>
<Component class="org.openide.explorer.view.OutlineView" name="outlineView">
<Properties>
<Property name="horizontalScrollBarPolicy" type="int" value="32"/>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[300, 400]"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignCardLayout" value="org.netbeans.modules.form.compat2.layouts.DesignCardLayout$CardConstraintsDescription">
<CardConstraints cardName="outlineCard"/>

View File

@ -101,6 +101,15 @@ public class OutlineViewPanel extends javax.swing.JPanel implements ExplorerMana
outlineView.setEnabled(enabled);
}
/**
* Sets the width of the columns of the OutlineView based on the passed in
* list of percentages. There should be on double value for each column
* in the OutlineView.
*
* @param percentages A series of double percentages values representing
* what percent of the total width of the table each
* column should have.
*/
public void setTableColumnsWidth(double... percentages) {
JTable table = outlineView.getOutline();
double total = 0;
@ -127,11 +136,7 @@ public class OutlineViewPanel extends javax.swing.JPanel implements ExplorerMana
messagePanel = new javax.swing.JPanel();
messageLabel = new javax.swing.JLabel();
setEnabled(false);
setLayout(new java.awt.CardLayout(5, 5));
outlineView.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
outlineView.setPreferredSize(new java.awt.Dimension(300, 400));
add(outlineView, "outlineCard");
messagePanel.setLayout(new java.awt.BorderLayout());

View File

@ -11,7 +11,6 @@
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,44,0,0,1,-112"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
@ -22,7 +21,7 @@
</Events>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="1" insetsLeft="1" insetsBottom="1" insetsRight="1" anchor="18" weightX="1.0" weightY="1.0"/>
<GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="1.0" weightY="1.0"/>
</Constraint>
</Constraints>

View File

@ -31,7 +31,7 @@ public final class RelationshipBrowser extends JPanel implements Lookup.Provider
private SelectionInfo currentSelection;
private final MessagesViewer messagesViewer;
private final MessageViewer messagesViewer;
private final ContactsViewer contactsViewer;
private final SummaryViewer summaryViewer;
private final MediaViewer mediaViewer;
@ -42,15 +42,14 @@ public final class RelationshipBrowser extends JPanel implements Lookup.Provider
* Creates new form RelationshipBrowser
*/
public RelationshipBrowser() {
messagesViewer = new MessagesViewer();
initComponents();
messagesViewer = new MessageViewer();
contactsViewer = new ContactsViewer();
summaryViewer = new SummaryViewer();
mediaViewer = new MediaViewer();
proxyLookup = new ModifiableProxyLookup(messagesViewer.getLookup());
initComponents();
tabPane.add(summaryViewer.getDisplayName(), summaryViewer);
tabPane.add(messagesViewer.getDisplayName(), messagesViewer);
tabPane.add(contactsViewer.getDisplayName(), contactsViewer);
@ -59,7 +58,7 @@ public final class RelationshipBrowser extends JPanel implements Lookup.Provider
/**
* Sets the value of currentSelection and passes the SelectionInfo onto the
* currently selected or visible tab.
* currently selected\visible tab.
*
* @param info Currently selected account nodes
*/
@ -94,7 +93,6 @@ public final class RelationshipBrowser extends JPanel implements Lookup.Provider
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.weighty = 1.0;
gridBagConstraints.insets = new java.awt.Insets(1, 1, 1, 1);
add(tabPane, gridBagConstraints);
}// </editor-fold>//GEN-END:initComponents

View File

@ -0,0 +1,216 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 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.communications.relationships;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import javax.swing.Action;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.nodes.Sheet;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.NodeProperty;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.CommunicationsManager;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskCoreException;
/**
* ChildFactory that creates createKeys and nodes from a given selectionInfo for
* only emails, call logs and messages.
*
*/
final class ThreadChildNodeFactory extends ChildFactory<BlackboardArtifact> {
private static final Logger logger = Logger.getLogger(ThreadChildNodeFactory.class.getName());
private SelectionInfo selectionInfo;
private final Action preferredAction;
/**
* Construct a new ThreadChildNodeFactory from the currently selectionInfo
*
* @param preferredAction SelectionInfo object for the currently selected
* accounts
*/
ThreadChildNodeFactory(Action preferredAction) {
this.preferredAction = preferredAction;
}
/**
* Updates the current instance of selectionInfo and calls the refresh method.
*
* @param selectionInfo New instance of the currently selected accounts
*/
public void refresh(SelectionInfo selectionInfo) {
this.selectionInfo = selectionInfo;
refresh(true);
}
/**
* Creates a list of Keys (BlackboardArtifact) for only messages for the
* currently selected accounts.
*
* @param list List of BlackboardArtifact to populate
*
* @return True on success
*/
@Override
protected boolean createKeys(List<BlackboardArtifact> list) {
CommunicationsManager communicationManager;
try {
communicationManager = Case.getCurrentCaseThrows().getSleuthkitCase().getCommunicationsManager();
} catch (NoCurrentCaseException | TskCoreException ex) {
logger.log(Level.SEVERE, "Failed to get communications manager from case.", ex); //NON-NLS
return false;
}
if(selectionInfo == null) {
return true;
}
final Set<Content> relationshipSources;
try {
relationshipSources = communicationManager.getRelationshipSources(selectionInfo.getAccountDevicesInstances(), selectionInfo.getCommunicationsFilter());
createRootMessageKeys(list, relationshipSources) ;
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Failed to get relationship sources.", ex); //NON-NLS
}
return true;
}
/**
* Adds only BlackboardArtifact objects to the list where are the earliest
* message in a message thread (based on threadID). If there are "unthreaded"
* messages (messages that do not have a threadID) one representitive artifact
* will be added to the list and dealt with a node creation time.
*
* @param list List to populate with BlackboardArtifact keys
* @param relationshipSources Set of Content objects
* @return True on success
* @throws TskCoreException
*/
private boolean createRootMessageKeys(List<BlackboardArtifact> list, Set<Content> relationshipSources) throws TskCoreException{
Map<String, BlackboardArtifact> rootMessageMap = new HashMap<>();
for(Content content: relationshipSources) {
if(!(content instanceof BlackboardArtifact)) {
continue;
}
BlackboardArtifact bba = (BlackboardArtifact) content;
BlackboardArtifact.ARTIFACT_TYPE fromID = BlackboardArtifact.ARTIFACT_TYPE.fromID(bba.getArtifactTypeID());
if (fromID == BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG
|| fromID == BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG
|| fromID == BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE) {
// We want all artifacts that do not have "threadIDs" to appear as one thread in the UI
// To achive this assign any artifact that does not have a threadID
// the "UNTHREADED_ID"
String threadID = MessageNode.UNTHREADED_ID;
BlackboardAttribute attribute = bba.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_THREAD_ID));
if(attribute != null) {
threadID = attribute.getValueString();
}
BlackboardArtifact tableArtifact = rootMessageMap.get(threadID);
if(tableArtifact == null) {
rootMessageMap.put(threadID, bba);
} else {
// Get the date of the message
BlackboardAttribute tableAttribute = tableArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_SENT));
attribute = bba.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_SENT));
// put the earliest message into the table
if(tableAttribute != null
&& attribute != null
&& tableAttribute.getValueLong() > attribute.getValueLong()) {
rootMessageMap.put(threadID, bba);
}
}
}
}
for(BlackboardArtifact bba: rootMessageMap.values()) {
list.add(bba);
}
return true;
}
@Override
protected Node createNodeForKey(BlackboardArtifact bba) {
BlackboardAttribute attribute = null;
try {
attribute = bba.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_THREAD_ID));
} catch (TskCoreException ex) {
logger.log(Level.WARNING, String.format("Unable to get threadID for artifact: %s", bba.getName()), ex);
}
if (attribute != null) {
return new ThreadNode(bba, attribute.getValueString(), preferredAction);
} else {
// Only one of these should occur.
return new UnthreadedNode();
}
}
/**
* An this node represents the "unthreaded" thread.
*/
final class UnthreadedNode extends AbstractNode {
/**
* Construct an instance of UnthreadNode.
*/
UnthreadedNode() {
super(Children.LEAF);
setDisplayName("Unthreaded");
this.setIconBaseWithExtension("org/sleuthkit/autopsy/communications/images/unthreaded.png" );
}
@Override
protected Sheet createSheet() {
Sheet sheet = super.createSheet();
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
if (sheetSet == null) {
sheetSet = Sheet.createPropertiesSet();
sheet.put(sheetSet);
}
// Give this node a threadID of "UNTHEADED_ID"
sheetSet.put(new NodeProperty<>("ThreadID", "ThreadID","",MessageNode.UNTHREADED_ID));
return sheet;
}
}
}

View File

@ -0,0 +1,60 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 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.communications.relationships;
import javax.swing.Action;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.Children;
import org.openide.nodes.Sheet;
import org.sleuthkit.datamodel.BlackboardArtifact;
/**
* An AbstractNode subclass which wraps a MessagNode object. Doing this allows
* for the reuse of the createSheet and other function from MessageNode, but
* also some customizing of how a ThreadNode is shown.
*/
final class ThreadNode extends AbstractNode{
final private MessageNode messageNode;
ThreadNode(BlackboardArtifact artifact, String threadID, Action preferredAction) {
super(Children.LEAF);
messageNode = new MessageNode(artifact, threadID, preferredAction);
this.setIconBaseWithExtension("org/sleuthkit/autopsy/communications/images/threaded.png" );
}
@Override
protected Sheet createSheet() {
return messageNode.createSheet();
}
String getThreadID() {
return messageNode.getThreadID();
}
@Override
public Action getPreferredAction() {
return messageNode.getPreferredAction();
}
@Override
public String getDisplayName() {
return messageNode.getDisplayName();
}
}

View File

@ -302,6 +302,8 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
try {
if (rootNode != null) {
this.rootNode = rootNode;
/**
* Check to see if we have previously created a paging support
* class for this node.
@ -364,8 +366,6 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
* case database round trips.
*/
if (rootNode != null && rootNode.getChildren().getNodesCount() > 0) {
this.rootNode = rootNode;
this.getExplorerManager().setRootContext(this.rootNode);
setupTable();
} else {

View File

@ -490,6 +490,12 @@ public class DataResultFilterNode extends FilterNode {
@Override
public AbstractAction visit(BlackboardArtifactNode ban) {
Action preferredAction = ban.getPreferredAction();
if(preferredAction instanceof AbstractAction) {
return (AbstractAction) preferredAction;
}
BlackboardArtifact artifact = ban.getArtifact();
try {
if ((artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID())

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2014-2018 Basis Technology Corp.
* Copyright 2014-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -286,7 +286,7 @@ public final class DataSourceIngestJob {
this.addIngestModules(fileIngestModuleTemplates, IngestModuleType.FILE_LEVEL, skCase);
this.addIngestModules(secondStageDataSourceModuleTemplates, IngestModuleType.DATA_SOURCE_LEVEL, skCase);
} catch (TskCoreException | NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "Failed to add ingest modules to database.", ex);
logErrorMessage(Level.WARNING, "Failed to add ingest modules listing to case database", ex);
}
}
@ -421,13 +421,13 @@ public final class DataSourceIngestJob {
try {
this.ingestJob = Case.getCurrentCaseThrows().getSleuthkitCase().addIngestJob(dataSource, NetworkUtils.getLocalHostName(), ingestModules, new Date(this.createTime), new Date(0), IngestJobStatusType.STARTED, "");
} catch (TskCoreException | NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "Failed to add ingest job to database.", ex);
logErrorMessage(Level.WARNING, "Failed to add ingest job info to case database", ex); //NON-NLS
}
if (this.hasFirstStageDataSourceIngestPipeline() || this.hasFileIngestPipeline()) {
logger.log(Level.INFO, "Starting first stage analysis for {0} (jobId={1})", new Object[]{dataSource.getName(), this.id}); //NON-NLS
logInfoMessage("Starting first stage analysis"); //NON-NLS
this.startFirstStage();
} else if (this.hasSecondStageDataSourceIngestPipeline()) {
logger.log(Level.INFO, "Starting second stage analysis for {0} (jobId={1}), no first stage configured", new Object[]{dataSource.getName(), this.id}); //NON-NLS
logInfoMessage("Starting second stage analysis"); //NON-NLS
this.startSecondStage();
}
}
@ -521,13 +521,13 @@ public final class DataSourceIngestJob {
* Schedule the first stage tasks.
*/
if (this.hasFirstStageDataSourceIngestPipeline() && this.hasFileIngestPipeline()) {
logger.log(Level.INFO, "Scheduling first stage data source and file level analysis tasks for {0} (jobId={1})", new Object[]{dataSource.getName(), this.id}); //NON-NLS
logInfoMessage("Scheduling first stage data source and file level analysis tasks"); //NON-NLS
DataSourceIngestJob.taskScheduler.scheduleIngestTasks(this);
} else if (this.hasFirstStageDataSourceIngestPipeline()) {
logger.log(Level.INFO, "Scheduling first stage data source level analysis tasks for {0} (jobId={1}), no file level analysis configured", new Object[]{dataSource.getName(), this.id}); //NON-NLS
logInfoMessage("Scheduling first stage data source level analysis tasks"); //NON-NLS
DataSourceIngestJob.taskScheduler.scheduleDataSourceIngestTask(this);
} else {
logger.log(Level.INFO, "Scheduling file level analysis tasks for {0} (jobId={1}), no first stage data source level analysis configured", new Object[]{dataSource.getName(), this.id}); //NON-NLS
logInfoMessage("Scheduling file level analysis tasks, no first stage data source level analysis configured"); //NON-NLS
DataSourceIngestJob.taskScheduler.scheduleFileIngestTasks(this, this.files);
/**
@ -546,7 +546,7 @@ public final class DataSourceIngestJob {
* Starts the second stage of this ingest job.
*/
private void startSecondStage() {
logger.log(Level.INFO, "Starting second stage analysis for {0} (jobId={1})", new Object[]{dataSource.getName(), this.id}); //NON-NLS
logInfoMessage("Starting second stage analysis"); //NON-NLS
this.stage = DataSourceIngestJob.Stages.SECOND;
if (this.doUI) {
this.startDataSourceIngestProgressBar();
@ -554,7 +554,7 @@ public final class DataSourceIngestJob {
synchronized (this.dataSourceIngestPipelineLock) {
this.currentDataSourceIngestPipeline = this.secondStageDataSourceIngestPipeline;
}
logger.log(Level.INFO, "Scheduling second stage data source level analysis tasks for {0} (jobId={1})", new Object[]{dataSource.getName(), this.id}); //NON-NLS
logInfoMessage("Scheduling second stage data source level analysis tasks"); //NON-NLS
DataSourceIngestJob.taskScheduler.scheduleDataSourceIngestTask(this);
}
@ -643,7 +643,7 @@ public final class DataSourceIngestJob {
* job and starts the second stage, if appropriate.
*/
private void finishFirstStage() {
logger.log(Level.INFO, "Finished first stage analysis for {0} (jobId={1})", new Object[]{dataSource.getName(), this.id}); //NON-NLS
logInfoMessage("Finished first stage analysis"); //NON-NLS
// Shut down the file ingest pipelines. Note that no shut down is
// required for the data source ingest pipeline because data source
@ -693,7 +693,7 @@ public final class DataSourceIngestJob {
* Shuts down the ingest pipelines and progress bars for this job.
*/
private void finish() {
logger.log(Level.INFO, "Finished analysis for {0} (jobId={1})", new Object[]{dataSource.getName(), this.id}); //NON-NLS
logInfoMessage("Finished analysis"); //NON-NLS
this.stage = DataSourceIngestJob.Stages.FINALIZATION;
if (this.doUI) {
@ -711,19 +711,19 @@ public final class DataSourceIngestJob {
try {
ingestJob.setIngestJobStatus(IngestJobStatusType.CANCELLED);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Failed to set ingest status for ingest job in database.", ex);
logErrorMessage(Level.WARNING, "Failed to update ingest job status in case database", ex);
}
} else {
try {
ingestJob.setIngestJobStatus(IngestJobStatusType.COMPLETED);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Failed to set ingest status for ingest job in database.", ex);
logErrorMessage(Level.WARNING, "Failed to update ingest job status in case database", ex);
}
}
try {
this.ingestJob.setEndDateTime(new Date());
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Failed to set end date for ingest job in database.", ex);
logErrorMessage(Level.WARNING, "Failed to set job end date in case database", ex);
}
}
this.parentJob.dataSourceJobFinished(this);
@ -842,7 +842,7 @@ public final class DataSourceIngestJob {
if (DataSourceIngestJob.Stages.FIRST == this.stage) {
DataSourceIngestJob.taskScheduler.fastTrackFileIngestTasks(this, files);
} else {
DataSourceIngestJob.logger.log(Level.SEVERE, "Adding files during second stage not supported"); //NON-NLS
logErrorMessage(Level.SEVERE, "Adding files to job during second stage analysis not supported");
}
/**
@ -1066,6 +1066,39 @@ public final class DataSourceIngestJob {
return this.cancellationReason;
}
/**
* Writes an info message to the application log that includes the data
* source name, data source object id, and the job id.
*
* @param message The message.
*/
private void logInfoMessage(String message) {
logger.log(Level.INFO, String.format("%s (data source = %s, objId = %d, jobId = %d)", message, dataSource.getName(), dataSource.getId(), id)); //NON-NLS
}
/**
* Writes an error message to the application log that includes the data
* source name, data source object id, and the job id.
*
* @param level The logging level for the message.
* @param message The message.
* @param throwable The throwable associated with the error.
*/
private void logErrorMessage(Level level, String message, Throwable throwable) {
logger.log(level, String.format("%s (data source = %s, objId = %d, jobId = %d)", message, dataSource.getName(), dataSource.getId(), id), throwable); //NON-NLS
}
/**
* Writes an error message to the application log that includes the data
* source name, data source object id, and the job id.
*
* @param level The logging level for the message.
* @param message The message.
*/
private void logErrorMessage(Level level, String message) {
logger.log(level, String.format("%s (data source = %s, objId = %d, jobId = %d)", message, dataSource.getName(), dataSource.getId(), id)); //NON-NLS
}
/**
* Write ingest module errors to the log.
*
@ -1073,7 +1106,7 @@ public final class DataSourceIngestJob {
*/
private void logIngestModuleErrors(List<IngestModuleError> errors) {
for (IngestModuleError error : errors) {
DataSourceIngestJob.logger.log(Level.SEVERE, String.format("%s experienced an error analyzing %s (jobId=%d)", error.getModuleDisplayName(), dataSource.getName(), this.id), error.getThrowable()); //NON-NLS
logErrorMessage(Level.SEVERE, String.format("%s experienced an error during analysis", error.getModuleDisplayName()), error.getThrowable()); //NON-NLS
}
}

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2012-2018 Basis Technology Corp.
* Copyright 2012-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -403,10 +403,10 @@ public class IngestManager implements IngestProgressSnapshotProvider {
synchronized (ingestJobsById) {
ingestJobsById.put(job.getId(), job);
}
IngestManager.logger.log(Level.INFO, "Starting ingest job {0}", job.getId()); //NON-NLS
errors = job.start();
if (errors.isEmpty()) {
this.fireIngestJobStarted(job.getId());
IngestManager.logger.log(Level.INFO, "Ingest job {0} started", job.getId()); //NON-NLS
} else {
synchronized (ingestJobsById) {
this.ingestJobsById.remove(job.getId());

View File

@ -41,6 +41,9 @@ public final class OpenCvLoader {
* Return whether or not the OpenCV library has been loaded.
*
* @return - true if the opencv library is loaded or false if it is not
* @throws UnsatisfiedLinkError - A COPY of the exception that prevented OpenCV from loading.
* Note that the stack trace in the exception can be confusing because it refers to a
* past invocation.
*/
public static boolean isOpenCvLoaded() throws UnsatisfiedLinkError {
if (!OPEN_CV_LOADED) {

View File

@ -99,13 +99,14 @@ class TextMessageAnalyzer(general.AndroidComponentAnalyzer):
resultSet = None
try:
resultSet = statement.executeQuery(
"SELECT address, date, read, type, subject, body FROM sms;")
"SELECT address, date, read, type, subject, body, thread_id FROM sms;")
while resultSet.next():
address = resultSet.getString("address") # may be phone number, or other addresses
date = Long.valueOf(resultSet.getString("date")) / 1000
read = resultSet.getInt("read") # may be unread = 0, read = 1
subject = resultSet.getString("subject") # message subject
body = resultSet.getString("body") # message body
thread_id = "{0}_{1}".format(abstractFile.getId(), resultSet.getInt("thread_id"))
attributes = ArrayList()
artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE); #create Message artifact and then add attributes from result set.
if resultSet.getString("type") == "1":
@ -119,6 +120,7 @@ class TextMessageAnalyzer(general.AndroidComponentAnalyzer):
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SUBJECT, general.MODULE_NAME, subject))
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT, general.MODULE_NAME, body))
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MESSAGE_TYPE, general.MODULE_NAME, "SMS Message"))
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_THREAD_ID, general.MODULE_NAME, thread_id))
artifact.addAttributes(attributes)

View File

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

View File

@ -38,7 +38,7 @@ PROJECT_NAME = "Autopsy User Documentation"
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = 4.11.0
PROJECT_NUMBER = 4.12.0
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
@ -1025,7 +1025,7 @@ GENERATE_HTML = YES
# The default directory is: html.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_OUTPUT = 4.11.0
HTML_OUTPUT = 4.12.0
# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
# generated HTML page (for example: .htm, .php, .asp).

View File

@ -38,7 +38,7 @@ PROJECT_NAME = "Autopsy"
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = 4.11.0
PROJECT_NUMBER = 4.12.0
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears a the top of each page and should give viewer a
@ -1063,7 +1063,7 @@ GENERATE_HTML = YES
# The default directory is: html.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_OUTPUT = api-docs/4.11.0/
HTML_OUTPUT = api-docs/4.12.0/
# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
# generated HTML page (for example: .htm, .php, .asp).

View File

@ -4,10 +4,10 @@ app.title=Autopsy
### lowercase version of above
app.name=${branding.token}
### if left unset, version will default to today's date
app.version=4.11.0
app.version=4.12.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

@ -5,7 +5,7 @@
# NOTE: update_sleuthkit_version.pl updates this value and relies
# on it keeping the same name and whitespace. Don't change it.
TSK_VERSION=4.6.6
TSK_VERSION=4.6.7
# In the beginning...