Merge pull request #3274 from sleuthkit/accounts_relationships
907: merge account_relationships to develop
@ -22,5 +22,6 @@
|
||||
|
||||
<dependency conf="core->default" org="org.apache.commons" name="commons-dbcp2" rev="2.1.1"/>
|
||||
<dependency conf="core->default" org="org.apache.commons" name="commons-pool2" rev="2.4.2"/>
|
||||
<dependency conf="core->default" org="org.jsoup" name="jsoup" rev="1.10.3"/>
|
||||
</dependencies>
|
||||
</ivy-module>
|
||||
|
@ -5,6 +5,7 @@ file.reference.commons-dbcp2-2.1.1.jar=release\\modules\\ext\\commons-dbcp2-2.1.
|
||||
file.reference.commons-pool2-2.4.2.jar=release\\modules\\ext\\commons-pool2-2.4.2.jar
|
||||
file.reference.jdom-2.0.5-contrib.jar=release/modules/ext/jdom-2.0.5-contrib.jar
|
||||
file.reference.jdom-2.0.5.jar=release/modules/ext/jdom-2.0.5.jar
|
||||
file.reference.jsoup-1.10.3.jar=release/modules/ext/jsoup-1.10.3.jar
|
||||
file.reference.jython-standalone-2.7.0.jar=release/modules/ext/jython-standalone-2.7.0.jar
|
||||
file.reference.mchange-commons-java-0.2.9.jar=release/modules/ext/mchange-commons-java-0.2.9.jar
|
||||
file.reference.metadata-extractor-2.9.1.jar=release/modules/ext/metadata-extractor-2.9.1.jar
|
||||
|
@ -320,6 +320,10 @@
|
||||
<package>org.sleuthkit.autopsy.report</package>
|
||||
<package>org.sleuthkit.datamodel</package>
|
||||
</public-packages>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/xmpcore-5.1.2.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/xmpcore-5.1.2.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/zookeeper-3.4.6.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/zookeeper-3.4.6.jar</binary-origin>
|
||||
@ -336,6 +340,10 @@
|
||||
<runtime-relative-path>ext/Tsk_DataModel_PostgreSQL.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/Tsk_DataModel_PostgreSQL.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/StixLib.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/StixLib.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/opencv-248.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/opencv-248.jar</binary-origin>
|
||||
@ -344,6 +352,10 @@
|
||||
<runtime-relative-path>ext/curator-framework-2.8.0.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/curator-framework-2.8.0.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/curator-client-2.8.0.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/curator-client-2.8.0.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/commons-dbcp2-2.1.1.jar</runtime-relative-path>
|
||||
<binary-origin>release\modules\ext\commons-dbcp2-2.1.1.jar</binary-origin>
|
||||
@ -356,6 +368,14 @@
|
||||
<runtime-relative-path>ext/jython-standalone-2.7.0.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/jython-standalone-2.7.0.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/activemq-all-5.11.1.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/activemq-all-5.11.1.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/opencv-248.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/opencv-248.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/sevenzipjbinding.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/sevenzipjbinding.jar</binary-origin>
|
||||
@ -377,12 +397,12 @@
|
||||
<binary-origin>release/modules/ext/xmpcore-5.1.2.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/StixLib.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/StixLib.jar</binary-origin>
|
||||
<runtime-relative-path>ext/sevenzipjbinding-AllPlatforms.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/sevenzipjbinding-AllPlatforms.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/curator-client-2.8.0.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/curator-client-2.8.0.jar</binary-origin>
|
||||
<runtime-relative-path>ext/commons-pool2-2.4.2.jar</runtime-relative-path>
|
||||
<binary-origin>release\modules\ext\commons-pool2-2.4.2.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/sqlite-jdbc-3.8.11.jar</runtime-relative-path>
|
||||
@ -392,18 +412,14 @@
|
||||
<runtime-relative-path>ext/activemq-all-5.11.1.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/activemq-all-5.11.1.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/jsoup-1.10.3.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/jsoup-1.10.3.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/Rejistry-1.0-SNAPSHOT.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/Rejistry-1.0-SNAPSHOT.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/sevenzipjbinding-AllPlatforms.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/sevenzipjbinding-AllPlatforms.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/commons-pool2-2.4.2.jar</runtime-relative-path>
|
||||
<binary-origin>release\modules\ext\commons-pool2-2.4.2.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/metadata-extractor-2.9.1.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/metadata-extractor-2.9.1.jar</binary-origin>
|
||||
@ -420,6 +436,10 @@
|
||||
<runtime-relative-path>ext/c3p0-0.9.5.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/c3p0-0.9.5.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/c3p0-0.9.5.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/c3p0-0.9.5.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
</data>
|
||||
</configuration>
|
||||
</project>
|
||||
|
@ -76,6 +76,7 @@ import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.events.DataSourceAddedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.events.ReportAddedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.services.Services;
|
||||
import org.sleuthkit.autopsy.communications.OpenCommVisualizationToolAction;
|
||||
import org.sleuthkit.autopsy.coordinationservice.CoordinationService;
|
||||
import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CategoryNode;
|
||||
import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CoordinationServiceException;
|
||||
@ -1050,6 +1051,7 @@ public class Case {
|
||||
CallableSystemAction.get(CasePropertiesAction.class).setEnabled(true);
|
||||
CallableSystemAction.get(CaseDeleteAction.class).setEnabled(true);
|
||||
CallableSystemAction.get(OpenTimelineAction.class).setEnabled(true);
|
||||
CallableSystemAction.get(OpenCommVisualizationToolAction.class).setEnabled(true);
|
||||
CallableSystemAction.get(OpenOutputFolderAction.class).setEnabled(false);
|
||||
|
||||
/*
|
||||
@ -1092,24 +1094,13 @@ public class Case {
|
||||
/*
|
||||
* Disable the case-specific menu items.
|
||||
*/
|
||||
CallableSystemAction
|
||||
.get(AddImageAction.class
|
||||
).setEnabled(false);
|
||||
CallableSystemAction
|
||||
.get(CaseCloseAction.class
|
||||
).setEnabled(false);
|
||||
CallableSystemAction
|
||||
.get(CasePropertiesAction.class
|
||||
).setEnabled(false);
|
||||
CallableSystemAction
|
||||
.get(CaseDeleteAction.class
|
||||
).setEnabled(false);
|
||||
CallableSystemAction
|
||||
.get(OpenTimelineAction.class
|
||||
).setEnabled(false);
|
||||
CallableSystemAction
|
||||
.get(OpenOutputFolderAction.class
|
||||
).setEnabled(false);
|
||||
CallableSystemAction.get(AddImageAction.class).setEnabled(false);
|
||||
CallableSystemAction.get(CaseCloseAction.class).setEnabled(false);
|
||||
CallableSystemAction.get(CasePropertiesAction.class).setEnabled(false);
|
||||
CallableSystemAction.get(CaseDeleteAction.class).setEnabled(false);
|
||||
CallableSystemAction.get(OpenTimelineAction.class).setEnabled(false);
|
||||
CallableSystemAction.get(OpenCommVisualizationToolAction.class).setEnabled(false);
|
||||
CallableSystemAction.get(OpenOutputFolderAction.class).setEnabled(false);
|
||||
|
||||
/*
|
||||
* Clear the notifications in the notfier component in the lower
|
||||
|
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2017 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;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import org.openide.nodes.AbstractNode;
|
||||
import org.openide.nodes.ChildFactory;
|
||||
import org.openide.nodes.Children;
|
||||
import org.openide.nodes.Node;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.datamodel.AccountDeviceInstance;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.CommunicationsFilter;
|
||||
import org.sleuthkit.datamodel.CommunicationsManager;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* 'Root' Node for the Account/Messages area. Has children which are all the
|
||||
* relationships of all the accounts in this node.
|
||||
*
|
||||
*/
|
||||
class AccountDetailsNode extends AbstractNode {
|
||||
|
||||
private final static Logger logger = Logger.getLogger(AccountDetailsNode.class.getName());
|
||||
|
||||
AccountDetailsNode(Set<AccountDeviceInstance> accountDeviceInstances, CommunicationsFilter filter, CommunicationsManager commsManager) {
|
||||
super(Children.create(new AccountRelationshipChildren(accountDeviceInstances, commsManager, filter), true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Children object for the relationships that the accounts are part of.
|
||||
*/
|
||||
private static class AccountRelationshipChildren extends ChildFactory<BlackboardArtifact> {
|
||||
|
||||
private final Set<AccountDeviceInstance> accountDeviceInstances;
|
||||
private final CommunicationsManager commsManager;
|
||||
private final CommunicationsFilter filter;
|
||||
|
||||
private AccountRelationshipChildren(Set<AccountDeviceInstance> accountDeviceInstances, CommunicationsManager commsManager, CommunicationsFilter filter) {
|
||||
this.accountDeviceInstances = accountDeviceInstances;
|
||||
this.commsManager = commsManager;
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean createKeys(List<BlackboardArtifact> list) {
|
||||
try {
|
||||
list.addAll(commsManager.getRelationshipSources(accountDeviceInstances, filter));
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Error getting communications", ex);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node createNodeForKey(BlackboardArtifact t) {
|
||||
return new RelationShipNode(t);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2017 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;
|
||||
|
||||
import org.sleuthkit.datamodel.AccountDeviceInstance;
|
||||
import org.sleuthkit.datamodel.CommunicationsFilter;
|
||||
|
||||
/**
|
||||
* Key for AccountDeviceInstance node.
|
||||
*
|
||||
* Encapsulates a AccountDeviceInstance, and CommunicationsFilter.
|
||||
*/
|
||||
class AccountDeviceInstanceKey {
|
||||
|
||||
private final AccountDeviceInstance accountDeviceInstance;
|
||||
private final CommunicationsFilter filter;
|
||||
private final long messageCount;
|
||||
|
||||
|
||||
|
||||
AccountDeviceInstanceKey(AccountDeviceInstance accountDeviceInstance, CommunicationsFilter filter, long msgCount) {
|
||||
this.accountDeviceInstance = accountDeviceInstance;
|
||||
this.filter = filter;
|
||||
this.messageCount = msgCount;
|
||||
}
|
||||
|
||||
AccountDeviceInstance getAccountDeviceInstance() {
|
||||
return accountDeviceInstance;
|
||||
}
|
||||
|
||||
CommunicationsFilter getCommunicationsFilter() {
|
||||
return filter;
|
||||
}
|
||||
|
||||
long getMessageCount() {
|
||||
return messageCount;
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.4" 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="false"/>
|
||||
<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"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
|
||||
<Component id="outlineView" pref="400" max="32767" attributes="0"/>
|
||||
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
|
||||
<Component id="outlineView" pref="300" max="32767" attributes="0"/>
|
||||
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Container class="org.openide.explorer.view.OutlineView" name="outlineView">
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Form>
|
@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2017 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;
|
||||
|
||||
import java.awt.Component;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.table.TableCellRenderer;
|
||||
import org.netbeans.swing.outline.DefaultOutlineModel;
|
||||
import org.netbeans.swing.outline.Outline;
|
||||
import org.openide.explorer.ExplorerManager;
|
||||
|
||||
/**
|
||||
* A panel that goes in the Browse tab of the Communications Visualization Tool.
|
||||
* Hosts an OutlineView that shows information about Accounts.
|
||||
*/
|
||||
public class AccountsBrowser extends JPanel {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final Outline outline;
|
||||
private ExplorerManager em;
|
||||
|
||||
/**
|
||||
* Creates new form AccountsBrowser
|
||||
*/
|
||||
public AccountsBrowser() {
|
||||
initComponents();
|
||||
outline = outlineView.getOutline();
|
||||
outlineView.setPropertyColumns(
|
||||
"device", Bundle.AccountNode_device(),
|
||||
"type", Bundle.AccountNode_accountType(),
|
||||
"count", Bundle.AccountNode_messageCount()
|
||||
);
|
||||
|
||||
outline.setRootVisible(false);
|
||||
((DefaultOutlineModel) outline.getOutlineModel()).setNodesColumnLabel(Bundle.AccountNode_accountName());
|
||||
outline.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
||||
outline.setColumnSorted(3, false, 1); //it would be nice if the column index wasn't hardcoded
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addNotify() {
|
||||
super.addNotify();
|
||||
em = ExplorerManager.find(this);
|
||||
em.addPropertyChangeListener(evt -> {
|
||||
if (ExplorerManager.PROP_ROOT_CONTEXT.equals(evt.getPropertyName())) {
|
||||
SwingUtilities.invokeLater(this::setColumnWidths);
|
||||
} else if (ExplorerManager.PROP_EXPLORED_CONTEXT.equals(evt.getPropertyName())) {
|
||||
SwingUtilities.invokeLater(this::setColumnWidths);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setColumnWidths() {
|
||||
int margin = 4;
|
||||
int padding = 8;
|
||||
|
||||
final int rows = Math.min(100, outline.getRowCount());
|
||||
|
||||
for (int column = 0; column < outline.getModel().getColumnCount(); column++) {
|
||||
int columnWidthLimit = 500;
|
||||
int columnWidth = 0;
|
||||
|
||||
// find the maximum width needed to fit the values for the first 100 rows, at most
|
||||
for (int row = 0; row < rows; row++) {
|
||||
TableCellRenderer renderer = outline.getCellRenderer(row, column);
|
||||
Component comp = outline.prepareRenderer(renderer, row, column);
|
||||
columnWidth = Math.max(comp.getPreferredSize().width, columnWidth);
|
||||
}
|
||||
|
||||
columnWidth += 2 * margin + padding; // add margin and regular padding
|
||||
columnWidth = Math.min(columnWidth, columnWidthLimit);
|
||||
|
||||
outline.getColumnModel().getColumn(column).setPreferredWidth(columnWidth);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
|
||||
outlineView = new org.openide.explorer.view.OutlineView();
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
|
||||
.addGap(0, 0, 0)
|
||||
.addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE)
|
||||
.addGap(0, 0, 0))
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
|
||||
.addGap(0, 0, 0)
|
||||
.addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE)
|
||||
.addGap(0, 0, 0))
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private org.openide.explorer.view.OutlineView outlineView;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
}
|
@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2017 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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.openide.nodes.AbstractNode;
|
||||
import org.openide.nodes.ChildFactory;
|
||||
import org.openide.nodes.Children;
|
||||
import org.openide.nodes.Node;
|
||||
import org.openide.nodes.Sheet;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.lookup.Lookups;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.datamodel.NodeProperty;
|
||||
import org.sleuthkit.datamodel.Account;
|
||||
import org.sleuthkit.datamodel.AccountDeviceInstance;
|
||||
import org.sleuthkit.datamodel.CommunicationsFilter;
|
||||
import org.sleuthkit.datamodel.CommunicationsManager;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
class AccountsRootChildren extends ChildFactory<AccountDeviceInstanceKey> {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(AccountsRootChildren.class.getName());
|
||||
|
||||
private final CommunicationsManager commsManager;
|
||||
private final CommunicationsFilter commsFilter;
|
||||
|
||||
AccountsRootChildren(CommunicationsManager commsManager, CommunicationsFilter commsFilter) {
|
||||
super();
|
||||
this.commsManager = commsManager;
|
||||
this.commsFilter = commsFilter;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean createKeys(List<AccountDeviceInstanceKey> list) {
|
||||
List<AccountDeviceInstanceKey> accountDeviceInstanceKeys = new ArrayList<>();
|
||||
try {
|
||||
for (AccountDeviceInstance adi : commsManager.getAccountDeviceInstancesWithRelationships(commsFilter)) {
|
||||
long communicationsCount = commsManager.getRelationshipSourcesCount(adi, commsFilter);
|
||||
accountDeviceInstanceKeys.add(new AccountDeviceInstanceKey(adi, commsFilter, communicationsCount));
|
||||
};
|
||||
} catch (TskCoreException tskCoreException) {
|
||||
logger.log(Level.SEVERE, "Error getting filtered account device instances", tskCoreException);
|
||||
}
|
||||
list.addAll(accountDeviceInstanceKeys);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node createNodeForKey(AccountDeviceInstanceKey key) {
|
||||
return new AccountDeviceInstanceNode(key, commsManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Node to represent an Account in the AccountsBrowser
|
||||
*/
|
||||
static class AccountDeviceInstanceNode extends AbstractNode {
|
||||
|
||||
private final AccountDeviceInstanceKey accountDeviceInstanceKey;
|
||||
private final CommunicationsManager commsManager;
|
||||
private final Account account;
|
||||
|
||||
private AccountDeviceInstanceNode(AccountDeviceInstanceKey accountDeviceInstanceKey, CommunicationsManager commsManager) {
|
||||
super(Children.LEAF, Lookups.fixed(accountDeviceInstanceKey, commsManager));
|
||||
this.accountDeviceInstanceKey = accountDeviceInstanceKey;
|
||||
this.commsManager = commsManager;
|
||||
this.account = accountDeviceInstanceKey.getAccountDeviceInstance().getAccount();
|
||||
setName(account.getTypeSpecificID());
|
||||
setIconBaseWithExtension("org/sleuthkit/autopsy/communications/images/" + Utils.getIconFileName(account.getAccountType()));
|
||||
}
|
||||
|
||||
public AccountDeviceInstance getAccountDeviceInstance() {
|
||||
return accountDeviceInstanceKey.getAccountDeviceInstance();
|
||||
}
|
||||
|
||||
public CommunicationsManager getCommsManager() {
|
||||
return commsManager;
|
||||
}
|
||||
|
||||
public CommunicationsFilter getFilter() {
|
||||
return accountDeviceInstanceKey.getCommunicationsFilter();
|
||||
}
|
||||
|
||||
@Override
|
||||
@NbBundle.Messages(value = {"AccountNode.device=Device",
|
||||
"AccountNode.accountName=Account",
|
||||
"AccountNode.accountType=Type",
|
||||
"AccountNode.messageCount=Msgs"})
|
||||
protected Sheet createSheet() {
|
||||
Sheet s = super.createSheet();
|
||||
Sheet.Set properties = s.get(Sheet.PROPERTIES);
|
||||
if (properties == null) {
|
||||
properties = Sheet.createPropertiesSet();
|
||||
s.put(properties);
|
||||
}
|
||||
|
||||
properties.put(new NodeProperty<>("type", Bundle.AccountNode_accountType(), "type",
|
||||
account.getAccountType().getDisplayName())); // NON-NLS
|
||||
properties.put(new NodeProperty<>("count", Bundle.AccountNode_messageCount(), "count",
|
||||
accountDeviceInstanceKey.getMessageCount())); // NON-NLS
|
||||
properties.put(new NodeProperty<>("device", Bundle.AccountNode_device(), "device",
|
||||
getDataSourceName())); // NON-NLS
|
||||
return s;
|
||||
}
|
||||
|
||||
private String getDataSourceName() {
|
||||
try {
|
||||
final SleuthkitCase sleuthkitCase = Case.getCurrentCase().getSleuthkitCase();
|
||||
for (DataSource dataSource : sleuthkitCase.getDataSources()) {
|
||||
if (dataSource.getDeviceId().equals(getAccountDeviceInstance().getDeviceId())) {
|
||||
return sleuthkitCase.getContentById(dataSource.getId()).getName();
|
||||
}
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Error getting datasource name, falling back on device ID.", ex);
|
||||
}
|
||||
return getAccountDeviceInstance().getDeviceId();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
CVTTopComponent.TabConstraints.tabTitle=Visualize
|
||||
CVTTopComponent.accountsBrowser.TabConstraints.tabTitle=Browse
|
||||
FiltersPanel.applyFiltersButton.text=Apply
|
||||
FiltersPanel.devicesLabel.text=Devices:
|
||||
FiltersPanel.accountTypesLabel.text=Account Types:
|
||||
FiltersPanel.filtersTitleLabel.text=Filters
|
||||
FiltersPanel.unCheckAllAccountTypesButton.text=Uncheck All
|
||||
FiltersPanel.checkAllAccountTypesButton.text=Check All
|
||||
FiltersPanel.unCheckAllDevicesButton.text=Uncheck All
|
||||
FiltersPanel.checkAllDevicesButton.text=Check All
|
||||
FiltersPanel.dateRangeLabel.text=Date Range:
|
||||
FiltersPanel.startCheckBox.text=Start:
|
||||
FiltersPanel.endCheckBox.text=End:
|
||||
FiltersPanel.refreshButton.text=Refresh
|
@ -0,0 +1,120 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.4" 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"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace min="-2" pref="6" max="-2" attributes="0"/>
|
||||
<Component id="filtersPane" min="-2" pref="250" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="splitPane" pref="1339" max="32767" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="0" 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 min="-2" pref="6" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="filtersPane" max="32767" attributes="0"/>
|
||||
<Component id="splitPane" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace min="-2" pref="5" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JSplitPane" name="splitPane">
|
||||
<Properties>
|
||||
<Property name="dividerLocation" type="int" value="400"/>
|
||||
<Property name="resizeWeight" type="double" value="0.7"/>
|
||||
</Properties>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JTabbedPane" name="browseVisualizeTabPane">
|
||||
<Properties>
|
||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
|
||||
<Font name="Tahoma" size="18" style="0"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
|
||||
<JSplitPaneConstraints position="left"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="org.sleuthkit.autopsy.communications.AccountsBrowser" name="accountsBrowser">
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
|
||||
<JTabbedPaneConstraints tabName="Browse">
|
||||
<Property name="tabTitle" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="CVTTopComponent.accountsBrowser.TabConstraints.tabTitle" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="tabIcon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||
<Image iconType="3" name="/org/sleuthkit/autopsy/communications/images/table.png"/>
|
||||
</Property>
|
||||
</JTabbedPaneConstraints>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
<Container class="javax.swing.JPanel" name="jPanel1">
|
||||
<Properties>
|
||||
<Property name="name" type="java.lang.String" value="" noResource="true"/>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
|
||||
<JTabbedPaneConstraints tabName="Visualize">
|
||||
<Property name="tabTitle" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="CVTTopComponent.TabConstraints.tabTitle" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="tabIcon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||
<Image iconType="3" name="/org/sleuthkit/autopsy/communications/images/emblem-web.png"/>
|
||||
</Property>
|
||||
</JTabbedPaneConstraints>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<EmptySpace min="0" pref="397" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<EmptySpace min="0" pref="725" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Component class="org.sleuthkit.autopsy.communications.FiltersPanel" name="filtersPane">
|
||||
<Properties>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[256, 495]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2017 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;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import org.openide.explorer.ExplorerManager;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.windows.Mode;
|
||||
import org.openide.windows.RetainLocation;
|
||||
import org.openide.windows.TopComponent;
|
||||
import org.openide.windows.WindowManager;
|
||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
||||
|
||||
/**
|
||||
* Top component which displays the Communications Visualization Tool.
|
||||
*/
|
||||
@TopComponent.Description(
|
||||
preferredID = "CVTTopComponent",
|
||||
//iconBase="SET/PATH/TO/ICON/HERE", //use this to put icon in window title area,
|
||||
persistenceType = TopComponent.PERSISTENCE_NEVER)
|
||||
@TopComponent.Registration(mode = "cvt", openAtStartup = false)
|
||||
@RetainLocation("cvt")
|
||||
@NbBundle.Messages("CVTTopComponent.name= Communications Visualization")
|
||||
public final class CVTTopComponent extends TopComponent implements ExplorerManager.Provider {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
private final ExplorerManager em = new ExplorerManager();
|
||||
|
||||
public CVTTopComponent() {
|
||||
initComponents();
|
||||
browseVisualizeTabPane.setEnabledAt(1, false);
|
||||
setName(Bundle.CVTTopComponent_name());
|
||||
splitPane.setRightComponent(new MessageBrowser());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
splitPane = new javax.swing.JSplitPane();
|
||||
browseVisualizeTabPane = new javax.swing.JTabbedPane();
|
||||
accountsBrowser = new org.sleuthkit.autopsy.communications.AccountsBrowser();
|
||||
jPanel1 = new javax.swing.JPanel();
|
||||
filtersPane = new org.sleuthkit.autopsy.communications.FiltersPanel();
|
||||
|
||||
splitPane.setDividerLocation(400);
|
||||
splitPane.setResizeWeight(0.7);
|
||||
|
||||
browseVisualizeTabPane.setFont(new java.awt.Font("Tahoma", 0, 18)); // NOI18N
|
||||
browseVisualizeTabPane.addTab(org.openide.util.NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.accountsBrowser.TabConstraints.tabTitle"), new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/table.png")), accountsBrowser); // NOI18N
|
||||
|
||||
jPanel1.setName(""); // NOI18N
|
||||
|
||||
javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
|
||||
jPanel1.setLayout(jPanel1Layout);
|
||||
jPanel1Layout.setHorizontalGroup(
|
||||
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 397, Short.MAX_VALUE)
|
||||
);
|
||||
jPanel1Layout.setVerticalGroup(
|
||||
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 725, Short.MAX_VALUE)
|
||||
);
|
||||
|
||||
browseVisualizeTabPane.addTab(org.openide.util.NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.TabConstraints.tabTitle"), new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/emblem-web.png")), jPanel1); // NOI18N
|
||||
|
||||
splitPane.setLeftComponent(browseVisualizeTabPane);
|
||||
|
||||
filtersPane.setMinimumSize(new java.awt.Dimension(256, 495));
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGap(6, 6, 6)
|
||||
.addComponent(filtersPane, javax.swing.GroupLayout.PREFERRED_SIZE, 250, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(splitPane, javax.swing.GroupLayout.DEFAULT_SIZE, 1339, Short.MAX_VALUE)
|
||||
.addGap(0, 0, 0))
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGap(6, 6, 6)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(filtersPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(splitPane))
|
||||
.addGap(5, 5, 5))
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private org.sleuthkit.autopsy.communications.AccountsBrowser accountsBrowser;
|
||||
private javax.swing.JTabbedPane browseVisualizeTabPane;
|
||||
private org.sleuthkit.autopsy.communications.FiltersPanel filtersPane;
|
||||
private javax.swing.JPanel jPanel1;
|
||||
private javax.swing.JSplitPane splitPane;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
@Override
|
||||
public void componentOpened() {
|
||||
super.componentOpened();
|
||||
WindowManager.getDefault().setTopComponentFloating(this, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExplorerManager getExplorerManager() {
|
||||
return em;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void open() {
|
||||
super.open();
|
||||
/*
|
||||
* when the window is (re)opened make sure the filters and accounts are
|
||||
* in an up to date and consistent state.
|
||||
*
|
||||
* Re-applying the filters means we will lose the selection...
|
||||
*/
|
||||
filtersPane.updateAndApplyFilters();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Mode> availableModes(List<Mode> modes) {
|
||||
/*
|
||||
* This looks like the right thing to do, but online discussions seems
|
||||
* to indicate this method is effectively deprecated. A break point
|
||||
* placed here was never hit.
|
||||
*/
|
||||
return modes.stream().filter(mode -> mode.getName().equals("cvt"))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
364
Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.form
Normal file
@ -0,0 +1,364 @@
|
||||
<?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="false"/>
|
||||
<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="18"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="jPanel3" max="32767" attributes="0"/>
|
||||
<Component id="jPanel2" alignment="1" max="32767" attributes="0"/>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="filtersTitleLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
<Component id="applyFiltersButton" min="-2" pref="83" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="refreshButton" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Component id="jPanel4" alignment="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="filtersTitleLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="applyFiltersButton" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="refreshButton" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace type="separate" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="jPanel3" max="32767" attributes="0"/>
|
||||
<EmptySpace type="separate" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="jPanel2" max="32767" attributes="0"/>
|
||||
<EmptySpace type="separate" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="jPanel4" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace pref="21" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JButton" name="applyFiltersButton">
|
||||
<Properties>
|
||||
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||
<Image iconType="3" name="/org/sleuthkit/autopsy/communications/images/tick.png"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="FiltersPanel.applyFiltersButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="null"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="filtersTitleLabel">
|
||||
<Properties>
|
||||
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||
<Image iconType="3" name="/org/sleuthkit/autopsy/communications/images/funnel.png"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="FiltersPanel.filtersTitleLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
|
||||
<Font name="Tahoma" size="16" style="0"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Container class="javax.swing.JPanel" name="jPanel2">
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<Group type="103" groupAlignment="1" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="accountTypesLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="102" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="1" attributes="0">
|
||||
<Component id="jScrollPane3" max="32767" attributes="0"/>
|
||||
<Group type="102" attributes="0">
|
||||
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
|
||||
<Component id="unCheckAllAccountTypesButton" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="checkAllAccountTypesButton" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="accountTypesLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
<Component id="jScrollPane3" pref="245" max="32767" attributes="0"/>
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="checkAllAccountTypesButton" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="unCheckAllAccountTypesButton" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JButton" name="unCheckAllAccountTypesButton">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="FiltersPanel.unCheckAllAccountTypesButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="unCheckAllAccountTypesButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="accountTypesLabel">
|
||||
<Properties>
|
||||
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||
<Image iconType="3" name="/org/sleuthkit/autopsy/images/accounts.png"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="FiltersPanel.accountTypesLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="checkAllAccountTypesButton">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="FiltersPanel.checkAllAccountTypesButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="checkAllAccountTypesButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Container class="javax.swing.JScrollPane" name="jScrollPane3">
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JPanel" name="accountTypePane">
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBoxLayout">
|
||||
<Property name="axis" type="int" value="1"/>
|
||||
</Layout>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Container class="javax.swing.JPanel" name="jPanel3">
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<Component id="devicesLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="102" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
|
||||
<Component id="unCheckAllDevicesButton" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="checkAllDevicesButton" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Component id="jScrollPane2" alignment="1" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<Component id="devicesLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="jScrollPane2" pref="96" max="32767" attributes="0"/>
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="checkAllDevicesButton" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="unCheckAllDevicesButton" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace min="5" pref="5" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JButton" name="unCheckAllDevicesButton">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="FiltersPanel.unCheckAllDevicesButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="unCheckAllDevicesButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="devicesLabel">
|
||||
<Properties>
|
||||
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||
<Image iconType="3" name="/org/sleuthkit/autopsy/communications/images/image.png"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="FiltersPanel.devicesLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="checkAllDevicesButton">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="FiltersPanel.checkAllDevicesButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="checkAllDevicesButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Container class="javax.swing.JScrollPane" name="jScrollPane2">
|
||||
<Properties>
|
||||
<Property name="horizontalScrollBarPolicy" type="int" value="31"/>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[27, 75]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JPanel" name="devicesPane">
|
||||
<Properties>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[4, 100]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBoxLayout">
|
||||
<Property name="axis" type="int" value="1"/>
|
||||
</Layout>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Container class="javax.swing.JPanel" name="jPanel4">
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<Component id="dateRangeLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="102" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<Component id="endCheckBox" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
<Component id="endDatePicker" min="-2" pref="163" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="startCheckBox" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
<Component id="startDatePicker" min="-2" pref="162" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="dateRangeLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="6" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="startDatePicker" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="startCheckBox" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="endDatePicker" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="endCheckBox" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="com.github.lgooddatepicker.components.DatePicker" name="startDatePicker">
|
||||
<Properties>
|
||||
<Property name="enabled" type="boolean" value="false"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="dateRangeLabel">
|
||||
<Properties>
|
||||
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||
<Image iconType="3" name="/org/sleuthkit/autopsy/images/calendar.png"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="FiltersPanel.dateRangeLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JCheckBox" name="startCheckBox">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="FiltersPanel.startCheckBox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="stateChanged" listener="javax.swing.event.ChangeListener" parameters="javax.swing.event.ChangeEvent" handler="startCheckBoxStateChanged"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JCheckBox" name="endCheckBox">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="FiltersPanel.endCheckBox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="stateChanged" listener="javax.swing.event.ChangeListener" parameters="javax.swing.event.ChangeEvent" handler="endCheckBoxStateChanged"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="com.github.lgooddatepicker.components.DatePicker" name="endDatePicker">
|
||||
<Properties>
|
||||
<Property name="enabled" type="boolean" value="false"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Component class="javax.swing.JButton" name="refreshButton">
|
||||
<Properties>
|
||||
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||
<Image iconType="3" name="/org/sleuthkit/autopsy/communications/images/arrow-circle-double-135.png"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="FiltersPanel.refreshButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
556
Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java
Normal file
@ -0,0 +1,556 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2017 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;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.swing.JCheckBox;
|
||||
import org.openide.explorer.ExplorerManager;
|
||||
import org.openide.nodes.AbstractNode;
|
||||
import org.openide.nodes.Children;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import static org.sleuthkit.autopsy.casemodule.Case.Events.CURRENT_CASE;
|
||||
import org.sleuthkit.autopsy.core.UserPreferences;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
||||
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||
import static org.sleuthkit.autopsy.ingest.IngestManager.IngestModuleEvent.DATA_ADDED;
|
||||
import org.sleuthkit.datamodel.Account;
|
||||
import org.sleuthkit.datamodel.CommunicationsFilter;
|
||||
import org.sleuthkit.datamodel.CommunicationsFilter.AccountTypeFilter;
|
||||
import org.sleuthkit.datamodel.CommunicationsFilter.DateRangeFilter;
|
||||
import org.sleuthkit.datamodel.CommunicationsFilter.DeviceFilter;
|
||||
import org.sleuthkit.datamodel.CommunicationsManager;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
import static org.sleuthkit.datamodel.Relationship.Type.CALL_LOG;
|
||||
import static org.sleuthkit.datamodel.Relationship.Type.MESSAGE;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* Panel that holds the Filter control widgets and translates user filtering
|
||||
* changes into queries against the CommunicationsManager.
|
||||
*/
|
||||
final public class FiltersPanel extends javax.swing.JPanel {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(FiltersPanel.class.getName());
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private ExplorerManager em;
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
private final Map<Account.Type, JCheckBox> accountTypeMap = new HashMap<>();
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
private final Map<String, JCheckBox> devicesMap = new HashMap<>();
|
||||
|
||||
private final PropertyChangeListener ingestListener;
|
||||
|
||||
@NbBundle.Messages({"refreshText=Refresh Results",
|
||||
"applyText=Apply"})
|
||||
public FiltersPanel() {
|
||||
initComponents();
|
||||
startDatePicker.setDate(LocalDate.now().minusWeeks(3));
|
||||
endDatePicker.setDateToToday();
|
||||
startDatePicker.getSettings().setVetoPolicy(
|
||||
//no end date, or start is before end
|
||||
startDate -> endCheckBox.isSelected() == false
|
||||
|| startDate.compareTo(endDatePicker.getDate()) <= 0
|
||||
);
|
||||
endDatePicker.getSettings().setVetoPolicy(
|
||||
//no start date, or end is after start
|
||||
endDate -> startCheckBox.isSelected() == false
|
||||
|| endDate.compareTo(startDatePicker.getDate()) >= 0
|
||||
);
|
||||
|
||||
updateTimeZone();
|
||||
updateFilters();
|
||||
UserPreferences.addChangeListener(preferenceChangeEvent -> {
|
||||
if (preferenceChangeEvent.getKey().equals(UserPreferences.DISPLAY_TIMES_IN_LOCAL_TIME)) {
|
||||
updateTimeZone();
|
||||
}
|
||||
});
|
||||
|
||||
this.ingestListener = pce -> {
|
||||
String eventType = pce.getPropertyName();
|
||||
if (eventType.equals(DATA_ADDED.toString())) {
|
||||
updateFilters();
|
||||
refreshButton.setEnabled(true);
|
||||
}
|
||||
};
|
||||
|
||||
applyFiltersButton.addActionListener(e -> applyFilters());
|
||||
refreshButton.addActionListener(e -> applyFilters());
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the filter widgets, and apply them.
|
||||
*/
|
||||
void updateAndApplyFilters() {
|
||||
updateFilters();
|
||||
if (em != null) {
|
||||
applyFilters();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateTimeZone() {
|
||||
dateRangeLabel.setText("Date Range ( " + Utils.getUserPreferredZoneId().toString() + "):");
|
||||
}
|
||||
|
||||
private void updateFilters() {
|
||||
updateAccountTypeFilter();
|
||||
updateDeviceFilter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addNotify() {
|
||||
super.addNotify();
|
||||
/*
|
||||
* Since we get the exploreremanager from the parent JComponenet, wait
|
||||
* till this FiltersPanel is actaully added to a parent.
|
||||
*/
|
||||
em = ExplorerManager.find(this);
|
||||
IngestManager.getInstance().addIngestModuleEventListener(ingestListener);
|
||||
Case.addEventTypeSubscriber(EnumSet.of(CURRENT_CASE), evt -> {
|
||||
devicesMap.clear();
|
||||
devicesPane.removeAll();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeNotify() {
|
||||
super.removeNotify();
|
||||
IngestManager.getInstance().removeIngestModuleEventListener(ingestListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the Account Types filter widgets
|
||||
*/
|
||||
private void updateAccountTypeFilter() {
|
||||
|
||||
//TODO: something like this commented code could be used to show only
|
||||
//the account types that are found:
|
||||
//final CommunicationsManager communicationsManager = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager();
|
||||
//List<Account.Type> accountTypesInUse = communicationsManager.getAccountTypesInUse();
|
||||
//accountTypesInUSe.forEach(...)
|
||||
Account.Type.PREDEFINED_ACCOUNT_TYPES.forEach(type -> {
|
||||
if (type.equals(Account.Type.CREDIT_CARD)) {
|
||||
//don't show a check box for credit cards
|
||||
} else if (type.equals(Account.Type.DEVICE)) {
|
||||
//don't show a check box fro device
|
||||
} else {
|
||||
accountTypeMap.computeIfAbsent(type, t -> {
|
||||
final JCheckBox jCheckBox = new JCheckBox(
|
||||
"<html><table cellpadding=0><tr><td><img src=\""
|
||||
+ FiltersPanel.class.getResource("/org/sleuthkit/autopsy/communications/images/"
|
||||
+ Utils.getIconFileName(type))
|
||||
+ "\"/></td><td width=" + 3 + "><td>" + type.getDisplayName() + "</td></tr></table></html>",
|
||||
true
|
||||
);
|
||||
accountTypePane.add(jCheckBox);
|
||||
return jCheckBox;
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the devices filter widgets
|
||||
*/
|
||||
private void updateDeviceFilter() {
|
||||
try {
|
||||
final SleuthkitCase sleuthkitCase = Case.getCurrentCase().getSleuthkitCase();
|
||||
|
||||
for (DataSource dataSource : sleuthkitCase.getDataSources()) {
|
||||
String dsName = sleuthkitCase.getContentById(dataSource.getId()).getName();
|
||||
//store the device id in the map, but display a datasource name in the UI.
|
||||
devicesMap.computeIfAbsent(dataSource.getDeviceId(), ds -> {
|
||||
final JCheckBox jCheckBox = new JCheckBox(dsName, false);
|
||||
devicesPane.add(jCheckBox);
|
||||
return jCheckBox;
|
||||
});
|
||||
};
|
||||
|
||||
} catch (IllegalStateException ex) {
|
||||
logger.log(Level.WARNING, "Communications Visualization Tool opened with no open case.", ex);
|
||||
} catch (TskCoreException tskCoreException) {
|
||||
logger.log(Level.SEVERE, "There was a error loading the datasources for the case.", tskCoreException);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
|
||||
applyFiltersButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/tick.png"))); // NOI18N
|
||||
applyFiltersButton.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.applyFiltersButton.text")); // NOI18N
|
||||
applyFiltersButton.setPreferredSize(null);
|
||||
|
||||
filtersTitleLabel.setFont(new java.awt.Font("Tahoma", 0, 16)); // NOI18N
|
||||
filtersTitleLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/funnel.png"))); // NOI18N
|
||||
filtersTitleLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.filtersTitleLabel.text")); // NOI18N
|
||||
|
||||
unCheckAllAccountTypesButton.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.unCheckAllAccountTypesButton.text")); // NOI18N
|
||||
unCheckAllAccountTypesButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
unCheckAllAccountTypesButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
accountTypesLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/accounts.png"))); // NOI18N
|
||||
accountTypesLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.accountTypesLabel.text")); // NOI18N
|
||||
|
||||
checkAllAccountTypesButton.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.checkAllAccountTypesButton.text")); // NOI18N
|
||||
checkAllAccountTypesButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
checkAllAccountTypesButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
accountTypePane.setLayout(new javax.swing.BoxLayout(accountTypePane, javax.swing.BoxLayout.Y_AXIS));
|
||||
jScrollPane3.setViewportView(accountTypePane);
|
||||
|
||||
javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2);
|
||||
jPanel2.setLayout(jPanel2Layout);
|
||||
jPanel2Layout.setHorizontalGroup(
|
||||
jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel2Layout.createSequentialGroup()
|
||||
.addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.LEADING, jPanel2Layout.createSequentialGroup()
|
||||
.addComponent(accountTypesLabel)
|
||||
.addGap(0, 0, Short.MAX_VALUE))
|
||||
.addGroup(jPanel2Layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
|
||||
.addComponent(jScrollPane3)
|
||||
.addGroup(jPanel2Layout.createSequentialGroup()
|
||||
.addGap(0, 0, Short.MAX_VALUE)
|
||||
.addComponent(unCheckAllAccountTypesButton)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(checkAllAccountTypesButton)))))
|
||||
.addGap(0, 0, 0))
|
||||
);
|
||||
jPanel2Layout.setVerticalGroup(
|
||||
jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(jPanel2Layout.createSequentialGroup()
|
||||
.addComponent(accountTypesLabel)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(jScrollPane3, javax.swing.GroupLayout.DEFAULT_SIZE, 245, Short.MAX_VALUE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(checkAllAccountTypesButton)
|
||||
.addComponent(unCheckAllAccountTypesButton)))
|
||||
);
|
||||
|
||||
unCheckAllDevicesButton.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.unCheckAllDevicesButton.text")); // NOI18N
|
||||
unCheckAllDevicesButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
unCheckAllDevicesButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
devicesLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/image.png"))); // NOI18N
|
||||
devicesLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.devicesLabel.text")); // NOI18N
|
||||
|
||||
checkAllDevicesButton.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.checkAllDevicesButton.text")); // NOI18N
|
||||
checkAllDevicesButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
checkAllDevicesButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
jScrollPane2.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
|
||||
jScrollPane2.setMinimumSize(new java.awt.Dimension(27, 75));
|
||||
|
||||
devicesPane.setMinimumSize(new java.awt.Dimension(4, 100));
|
||||
devicesPane.setLayout(new javax.swing.BoxLayout(devicesPane, javax.swing.BoxLayout.Y_AXIS));
|
||||
jScrollPane2.setViewportView(devicesPane);
|
||||
|
||||
javax.swing.GroupLayout jPanel3Layout = new javax.swing.GroupLayout(jPanel3);
|
||||
jPanel3.setLayout(jPanel3Layout);
|
||||
jPanel3Layout.setHorizontalGroup(
|
||||
jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(jPanel3Layout.createSequentialGroup()
|
||||
.addComponent(devicesLabel)
|
||||
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
.addGroup(jPanel3Layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(jPanel3Layout.createSequentialGroup()
|
||||
.addGap(0, 0, Short.MAX_VALUE)
|
||||
.addComponent(unCheckAllDevicesButton)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(checkAllDevicesButton))
|
||||
.addComponent(jScrollPane2, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
|
||||
);
|
||||
jPanel3Layout.setVerticalGroup(
|
||||
jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(jPanel3Layout.createSequentialGroup()
|
||||
.addComponent(devicesLabel)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 96, Short.MAX_VALUE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(checkAllDevicesButton)
|
||||
.addComponent(unCheckAllDevicesButton))
|
||||
.addGap(5, 5, 5))
|
||||
);
|
||||
|
||||
startDatePicker.setEnabled(false);
|
||||
|
||||
dateRangeLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/calendar.png"))); // NOI18N
|
||||
dateRangeLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.dateRangeLabel.text")); // NOI18N
|
||||
|
||||
startCheckBox.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.startCheckBox.text")); // NOI18N
|
||||
startCheckBox.addChangeListener(new javax.swing.event.ChangeListener() {
|
||||
public void stateChanged(javax.swing.event.ChangeEvent evt) {
|
||||
startCheckBoxStateChanged(evt);
|
||||
}
|
||||
});
|
||||
|
||||
endCheckBox.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.endCheckBox.text")); // NOI18N
|
||||
endCheckBox.addChangeListener(new javax.swing.event.ChangeListener() {
|
||||
public void stateChanged(javax.swing.event.ChangeEvent evt) {
|
||||
endCheckBoxStateChanged(evt);
|
||||
}
|
||||
});
|
||||
|
||||
endDatePicker.setEnabled(false);
|
||||
|
||||
javax.swing.GroupLayout jPanel4Layout = new javax.swing.GroupLayout(jPanel4);
|
||||
jPanel4.setLayout(jPanel4Layout);
|
||||
jPanel4Layout.setHorizontalGroup(
|
||||
jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(jPanel4Layout.createSequentialGroup()
|
||||
.addComponent(dateRangeLabel)
|
||||
.addGap(0, 0, Short.MAX_VALUE))
|
||||
.addGroup(jPanel4Layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel4Layout.createSequentialGroup()
|
||||
.addComponent(endCheckBox)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(endDatePicker, javax.swing.GroupLayout.PREFERRED_SIZE, 163, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addGroup(jPanel4Layout.createSequentialGroup()
|
||||
.addComponent(startCheckBox)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(startDatePicker, javax.swing.GroupLayout.PREFERRED_SIZE, 162, javax.swing.GroupLayout.PREFERRED_SIZE))))
|
||||
);
|
||||
jPanel4Layout.setVerticalGroup(
|
||||
jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(jPanel4Layout.createSequentialGroup()
|
||||
.addComponent(dateRangeLabel)
|
||||
.addGap(6, 6, 6)
|
||||
.addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(startDatePicker, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(startCheckBox))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(endDatePicker, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(endCheckBox)))
|
||||
);
|
||||
|
||||
refreshButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/arrow-circle-double-135.png"))); // NOI18N
|
||||
refreshButton.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.refreshButton.text")); // NOI18N
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGap(0, 0, 0)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(jPanel3, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(jPanel2, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(filtersTitleLabel)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(applyFiltersButton, javax.swing.GroupLayout.PREFERRED_SIZE, 83, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(refreshButton))
|
||||
.addComponent(jPanel4, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGap(0, 0, 0)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(filtersTitleLabel)
|
||||
.addComponent(applyFiltersButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(refreshButton))
|
||||
.addGap(18, 18, 18)
|
||||
.addComponent(jPanel3, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addGap(18, 18, 18)
|
||||
.addComponent(jPanel2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addGap(18, 18, 18)
|
||||
.addComponent(jPanel4, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addContainerGap(21, Short.MAX_VALUE))
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
/**
|
||||
* Query for accounts using the selected filters, and send the results to
|
||||
* the AccountsBrowser via the ExplorerManager.
|
||||
*/
|
||||
private void applyFilters() {
|
||||
CommunicationsFilter commsFilter = new CommunicationsFilter();
|
||||
commsFilter.addAndFilter(getDeviceFilter());
|
||||
commsFilter.addAndFilter(getAccountTypeFilter());
|
||||
commsFilter.addAndFilter(getDateRangeFilter());
|
||||
commsFilter.addAndFilter(new CommunicationsFilter.RelationshipTypeFilter(
|
||||
ImmutableSet.of(CALL_LOG, MESSAGE)));
|
||||
|
||||
try {
|
||||
final CommunicationsManager commsManager = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager();
|
||||
em.setRootContext(new AbstractNode(Children.create(new AccountsRootChildren(commsManager, commsFilter), true)));
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "There was an error getting the CommunicationsManager for the current case.", ex);
|
||||
}
|
||||
|
||||
refreshButton.setEnabled(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a DeviceFilter that matches the state of the UI widgets.
|
||||
*
|
||||
* @return a DeviceFilter
|
||||
*/
|
||||
private DeviceFilter getDeviceFilter() {
|
||||
DeviceFilter deviceFilter = new DeviceFilter(
|
||||
devicesMap.entrySet().stream()
|
||||
.filter(entry -> entry.getValue().isSelected())
|
||||
.map(Entry::getKey)
|
||||
.collect(Collectors.toSet()));
|
||||
return deviceFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an AccountTypeFilter that matches the state of the UI widgets
|
||||
*
|
||||
* @return an AccountTypeFilter
|
||||
*/
|
||||
private AccountTypeFilter getAccountTypeFilter() {
|
||||
AccountTypeFilter accountTypeFilter = new AccountTypeFilter(accountTypeMap.entrySet().stream()
|
||||
.filter(entry -> entry.getValue().isSelected())
|
||||
.map(entry -> entry.getKey()).collect(Collectors.toSet()));
|
||||
return accountTypeFilter;
|
||||
}
|
||||
|
||||
private DateRangeFilter getDateRangeFilter() {
|
||||
ZoneId zone = Utils.getUserPreferredZoneId();
|
||||
long start = startDatePicker.isEnabled() ? startDatePicker.getDate().atStartOfDay(zone).toEpochSecond() : 0;
|
||||
long end = endDatePicker.isEnabled() ? endDatePicker.getDate().atStartOfDay(zone).toEpochSecond() : 0;
|
||||
return new DateRangeFilter(start, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the selection state of all the account type check boxes
|
||||
*
|
||||
* @param selected The selection state to set the check boxes to.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
private void setAllAccountTypesSelected(boolean selected) {
|
||||
setAllSelected(accountTypeMap, selected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the selection state of all the device check boxes
|
||||
*
|
||||
* @param selected The selection state to set the check boxes to.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
private void setAllDevicesSelected(boolean selected) {
|
||||
setAllSelected(devicesMap, selected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method that sets all the checkboxes in the given map to the given
|
||||
* selection state.
|
||||
*
|
||||
* @param map A map from anything to JCheckBoxes.
|
||||
* @param selected The selection state to set all the checkboxes to.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
|
||||
private void setAllSelected(Map<?, JCheckBox> map, boolean selected) {
|
||||
map.values().forEach(box -> box.setSelected(selected));
|
||||
}
|
||||
private void unCheckAllAccountTypesButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_unCheckAllAccountTypesButtonActionPerformed
|
||||
setAllAccountTypesSelected(false);
|
||||
}//GEN-LAST:event_unCheckAllAccountTypesButtonActionPerformed
|
||||
|
||||
private void checkAllAccountTypesButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_checkAllAccountTypesButtonActionPerformed
|
||||
setAllAccountTypesSelected(true);
|
||||
}//GEN-LAST:event_checkAllAccountTypesButtonActionPerformed
|
||||
|
||||
private void unCheckAllDevicesButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_unCheckAllDevicesButtonActionPerformed
|
||||
setAllDevicesSelected(false);
|
||||
}//GEN-LAST:event_unCheckAllDevicesButtonActionPerformed
|
||||
|
||||
private void checkAllDevicesButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_checkAllDevicesButtonActionPerformed
|
||||
setAllDevicesSelected(true);
|
||||
}//GEN-LAST:event_checkAllDevicesButtonActionPerformed
|
||||
|
||||
private void startCheckBoxStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_startCheckBoxStateChanged
|
||||
startDatePicker.setEnabled(startCheckBox.isSelected());
|
||||
}//GEN-LAST:event_startCheckBoxStateChanged
|
||||
|
||||
private void endCheckBoxStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_endCheckBoxStateChanged
|
||||
endDatePicker.setEnabled(endCheckBox.isSelected());
|
||||
}//GEN-LAST:event_endCheckBoxStateChanged
|
||||
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private final javax.swing.JPanel accountTypePane = new javax.swing.JPanel();
|
||||
private final javax.swing.JLabel accountTypesLabel = new javax.swing.JLabel();
|
||||
private final javax.swing.JButton applyFiltersButton = new javax.swing.JButton();
|
||||
private final javax.swing.JButton checkAllAccountTypesButton = new javax.swing.JButton();
|
||||
private final javax.swing.JButton checkAllDevicesButton = new javax.swing.JButton();
|
||||
private final javax.swing.JLabel dateRangeLabel = new javax.swing.JLabel();
|
||||
private final javax.swing.JLabel devicesLabel = new javax.swing.JLabel();
|
||||
private final javax.swing.JPanel devicesPane = new javax.swing.JPanel();
|
||||
private final javax.swing.JCheckBox endCheckBox = new javax.swing.JCheckBox();
|
||||
private final com.github.lgooddatepicker.components.DatePicker endDatePicker = new com.github.lgooddatepicker.components.DatePicker();
|
||||
private final javax.swing.JLabel filtersTitleLabel = new javax.swing.JLabel();
|
||||
private final javax.swing.JPanel jPanel2 = new javax.swing.JPanel();
|
||||
private final javax.swing.JPanel jPanel3 = new javax.swing.JPanel();
|
||||
private final javax.swing.JPanel jPanel4 = new javax.swing.JPanel();
|
||||
private final javax.swing.JScrollPane jScrollPane2 = new javax.swing.JScrollPane();
|
||||
private final javax.swing.JScrollPane jScrollPane3 = new javax.swing.JScrollPane();
|
||||
private final javax.swing.JButton refreshButton = new javax.swing.JButton();
|
||||
private final javax.swing.JCheckBox startCheckBox = new javax.swing.JCheckBox();
|
||||
private final com.github.lgooddatepicker.components.DatePicker startDatePicker = new com.github.lgooddatepicker.components.DatePicker();
|
||||
private final javax.swing.JButton unCheckAllAccountTypesButton = new javax.swing.JButton();
|
||||
private final javax.swing.JButton unCheckAllDevicesButton = new javax.swing.JButton();
|
||||
// End of variables declaration//GEN-END:variables
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.4" 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="false"/>
|
||||
<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"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<EmptySpace min="-2" pref="0" max="-2" attributes="0"/>
|
||||
<Component id="splitPane" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace min="-2" pref="0" max="-2" attributes="0"/>
|
||||
<Component id="splitPane" pref="1083" max="32767" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="0" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JSplitPane" name="splitPane">
|
||||
<Properties>
|
||||
<Property name="dividerLocation" type="int" value="400"/>
|
||||
<Property name="dividerSize" type="int" value="10"/>
|
||||
<Property name="orientation" type="int" value="0"/>
|
||||
<Property name="resizeWeight" type="double" value="0.5"/>
|
||||
</Properties>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="org.sleuthkit.autopsy.communications.MessageDataContent" name="messageDataContent">
|
||||
<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>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Form>
|
@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2017 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 obt ain 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;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import org.openide.explorer.ExplorerManager;
|
||||
import org.openide.nodes.Node;
|
||||
import org.sleuthkit.autopsy.communications.AccountsRootChildren.AccountDeviceInstanceNode;
|
||||
import org.sleuthkit.autopsy.corecomponents.DataResultPanel;
|
||||
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
|
||||
import org.sleuthkit.autopsy.corecomponents.TableFilterNode;
|
||||
import org.sleuthkit.autopsy.directorytree.DataResultFilterNode;
|
||||
import org.sleuthkit.datamodel.AccountDeviceInstance;
|
||||
import org.sleuthkit.datamodel.CommunicationsFilter;
|
||||
import org.sleuthkit.datamodel.CommunicationsManager;
|
||||
|
||||
/**
|
||||
* The right hand side of the CVT. Has a DataResultPanel to show messages and
|
||||
* other account details, and a ContentViewer to show individual
|
||||
*/
|
||||
final class MessageBrowser extends javax.swing.JPanel implements ExplorerManager.Provider {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
|
||||
private final DataResultPanel messagesResultPanel;
|
||||
private final ExplorerManager explorerManager = new ExplorerManager();
|
||||
private final DataResultViewerTable dataResultViewerTable = new DataResultViewerTable(explorerManager, "Messages");
|
||||
|
||||
;
|
||||
|
||||
MessageBrowser() {
|
||||
initComponents();
|
||||
//create an uninitialized DataResultPanel so we can control the ResultViewers that get added.
|
||||
messagesResultPanel = DataResultPanel.createInstanceUninitialized("Account", "", Node.EMPTY, 0, messageDataContent);
|
||||
|
||||
splitPane.setTopComponent(messagesResultPanel);
|
||||
splitPane.setBottomComponent(messageDataContent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addNotify() {
|
||||
super.addNotify();
|
||||
ExplorerManager parentExplorereManager = ExplorerManager.find(this);
|
||||
parentExplorereManager.addPropertyChangeListener(pce -> {
|
||||
if (pce.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) {
|
||||
final Node[] selectedNodes = parentExplorereManager.getSelectedNodes();
|
||||
|
||||
messagesResultPanel.setNumMatches(0);
|
||||
messagesResultPanel.setNode(null);
|
||||
|
||||
if (selectedNodes.length == 0) {
|
||||
//reset panel when there is no selection
|
||||
messagesResultPanel.setPath("");
|
||||
} else {
|
||||
AccountDeviceInstanceNode adiNode = (AccountDeviceInstanceNode) selectedNodes[0];
|
||||
CommunicationsFilter filter = adiNode.getFilter();
|
||||
CommunicationsManager commsManager = adiNode.getCommsManager();
|
||||
final Set<AccountDeviceInstance> accountDeviceInstances;
|
||||
|
||||
if (selectedNodes.length == 1) {
|
||||
final AccountDeviceInstance accountDeviceInstance = adiNode.getAccountDeviceInstance();
|
||||
accountDeviceInstances = Collections.singleton(accountDeviceInstance);
|
||||
messagesResultPanel.setPath(accountDeviceInstance.getAccount().getTypeSpecificID());
|
||||
} else {
|
||||
accountDeviceInstances = Stream.of(selectedNodes)
|
||||
.map(node -> (AccountDeviceInstanceNode) node)
|
||||
.map(AccountDeviceInstanceNode::getAccountDeviceInstance)
|
||||
.collect(Collectors.toSet());
|
||||
messagesResultPanel.setPath(selectedNodes.length + " accounts");
|
||||
}
|
||||
AccountDetailsNode accountDetailsNode =
|
||||
new AccountDetailsNode(accountDeviceInstances, filter, commsManager);
|
||||
TableFilterNode wrappedNode =
|
||||
new TableFilterNode(new DataResultFilterNode(accountDetailsNode, parentExplorereManager), true);
|
||||
messagesResultPanel.setNode(wrappedNode);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//add the required result viewers and THEN open the panel
|
||||
if (messagesResultPanel.getViewers().contains(dataResultViewerTable) == false) {
|
||||
messagesResultPanel.addResultViewer(dataResultViewerTable);
|
||||
}
|
||||
messagesResultPanel.open();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public ExplorerManager getExplorerManager() {
|
||||
return explorerManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
|
||||
splitPane = new javax.swing.JSplitPane();
|
||||
messageDataContent = new org.sleuthkit.autopsy.communications.MessageDataContent();
|
||||
|
||||
splitPane.setDividerLocation(400);
|
||||
splitPane.setDividerSize(10);
|
||||
splitPane.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);
|
||||
splitPane.setResizeWeight(0.5);
|
||||
splitPane.setBottomComponent(messageDataContent);
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
|
||||
.addGap(0, 0, 0)
|
||||
.addComponent(splitPane))
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGap(0, 0, 0)
|
||||
.addComponent(splitPane, javax.swing.GroupLayout.DEFAULT_SIZE, 1083, Short.MAX_VALUE)
|
||||
.addGap(0, 0, 0))
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private org.sleuthkit.autopsy.communications.MessageDataContent messageDataContent;
|
||||
private javax.swing.JSplitPane splitPane;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2017 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;
|
||||
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import org.sleuthkit.autopsy.contentviewers.MessageContentViewer;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContent;
|
||||
|
||||
/**
|
||||
* Extends MessageContentViewer so that it implements DataContent and can be set
|
||||
* as the only ContentViewer for a DataResultPanel
|
||||
*/
|
||||
public class MessageDataContent extends MessageContentViewer implements DataContent {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
}
|
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2017 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;
|
||||
|
||||
import java.awt.Component;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JButton;
|
||||
import org.openide.awt.ActionID;
|
||||
import org.openide.awt.ActionReference;
|
||||
import org.openide.awt.ActionReferences;
|
||||
import org.openide.awt.ActionRegistration;
|
||||
import org.openide.util.HelpCtx;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.openide.util.actions.CallableSystemAction;
|
||||
import org.openide.util.actions.Presenter;
|
||||
import org.openide.windows.TopComponent;
|
||||
import org.openide.windows.WindowManager;
|
||||
|
||||
/**
|
||||
* Action that opens the CVT. Available through the Tools menu and the main
|
||||
* toolbar.
|
||||
*/
|
||||
@ActionID(category = "Tools",
|
||||
id = "org.sleuthkit.autopsy.communicationsVisualization.OpenCVTAction")
|
||||
@ActionRegistration(displayName = "#CTL_OpenCVTAction", lazy = false)
|
||||
@ActionReferences(value = {
|
||||
@ActionReference(path = "Menu/Tools", position = 102)
|
||||
, @ActionReference(path = "Toolbars/Case", position = 102)})
|
||||
@Messages("CTL_OpenCVTAction=Visualize Communications")
|
||||
public final class OpenCommVisualizationToolAction extends CallableSystemAction implements Presenter.Toolbar {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final JButton toolbarButton = new JButton(getName(),
|
||||
new ImageIcon(getClass().getResource("images/emblem-web24.png"))); //NON-NLS
|
||||
|
||||
public OpenCommVisualizationToolAction() {
|
||||
toolbarButton.addActionListener(actionEvent -> performAction());
|
||||
setEnabled(false); //disabled by default. Will be enabled in Case.java when a case is opened.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performAction() {
|
||||
final TopComponent tc = WindowManager.getDefault().findTopComponent("CVTTopComponent");
|
||||
if (tc != null) {
|
||||
if (tc.isOpened() == false) {
|
||||
tc.open();
|
||||
}
|
||||
tc.toFront();
|
||||
tc.requestActive();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this action to be enabled/disabled
|
||||
*
|
||||
* @param value whether to enable this action or not
|
||||
*/
|
||||
@Override
|
||||
public void setEnabled(boolean value) {
|
||||
super.setEnabled(value);
|
||||
toolbarButton.setEnabled(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NbBundle.Messages("OpenCVTAction.displayName=Communications Visualizaton")
|
||||
public String getName() {
|
||||
return Bundle.OpenCVTAction_displayName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the toolbar component of this action
|
||||
*
|
||||
* @return component the toolbar button
|
||||
*/
|
||||
@Override
|
||||
public Component getToolbarPresenter() {
|
||||
return toolbarButton;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HelpCtx getHelpCtx() {
|
||||
return HelpCtx.DEFAULT_HELP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean asynchronous() {
|
||||
return false; // run on edt
|
||||
}
|
||||
}
|
@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2017 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;
|
||||
|
||||
import java.util.TimeZone;
|
||||
import java.util.logging.Level;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.openide.nodes.Sheet;
|
||||
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;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
|
||||
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME;
|
||||
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_SENT;
|
||||
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START;
|
||||
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL_FROM;
|
||||
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL_TO;
|
||||
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM;
|
||||
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO;
|
||||
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SUBJECT;
|
||||
import static org.sleuthkit.datamodel.BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME;
|
||||
import org.sleuthkit.datamodel.TimeUtilities;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* Node for a relationship, as represented by a BlackboardArtifact.
|
||||
*/
|
||||
public class RelationShipNode extends BlackboardArtifactNode {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(RelationShipNode.class.getName());
|
||||
|
||||
public RelationShipNode(BlackboardArtifact artifact) {
|
||||
super(artifact);
|
||||
final String stripEnd = StringUtils.stripEnd(artifact.getDisplayName(), "s");
|
||||
String removeEndIgnoreCase = StringUtils.removeEndIgnoreCase(stripEnd, "message");
|
||||
setDisplayName(removeEndIgnoreCase.isEmpty() ? stripEnd : removeEndIgnoreCase);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Sheet createSheet() {
|
||||
Sheet s = new Sheet();
|
||||
Sheet.Set ss = s.get(Sheet.PROPERTIES);
|
||||
if (ss == null) {
|
||||
ss = Sheet.createPropertiesSet();
|
||||
s.put(ss);
|
||||
}
|
||||
|
||||
ss.put(new NodeProperty<>("Type", "Type", "Type", getDisplayName()));
|
||||
|
||||
final BlackboardArtifact artifact = getArtifact();
|
||||
BlackboardArtifact.ARTIFACT_TYPE fromID = BlackboardArtifact.ARTIFACT_TYPE.fromID(getArtifact().getArtifactTypeID());
|
||||
if (null != fromID) {
|
||||
//Consider refactoring this to reduce boilerplate
|
||||
switch (fromID) {
|
||||
case TSK_EMAIL_MSG:
|
||||
ss.put(new NodeProperty<>("From", "From", "From",
|
||||
StringUtils.strip(getAttributeDisplayString(artifact, TSK_EMAIL_FROM), " \t\n;")));
|
||||
ss.put(new NodeProperty<>("To", "To", "To",
|
||||
StringUtils.strip(getAttributeDisplayString(artifact, TSK_EMAIL_TO), " \t\n;")));
|
||||
ss.put(new NodeProperty<>("Date", "Date", "Date",
|
||||
getAttributeDisplayString(artifact, TSK_DATETIME_SENT)));
|
||||
ss.put(new NodeProperty<>("Subject", "Subject", "Subject",
|
||||
getAttributeDisplayString(artifact, TSK_SUBJECT)));
|
||||
try {
|
||||
ss.put(new NodeProperty<>("Attms", "Attms", "Attms", artifact.getChildrenCount()));
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "Error loading attachment count for " + artifact, ex);
|
||||
}
|
||||
|
||||
break;
|
||||
case TSK_MESSAGE:
|
||||
ss.put(new NodeProperty<>("From", "From", "From",
|
||||
getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_FROM)));
|
||||
ss.put(new NodeProperty<>("To", "To", "To",
|
||||
getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_TO)));
|
||||
ss.put(new NodeProperty<>("Date", "Date", "Date",
|
||||
getAttributeDisplayString(artifact, TSK_DATETIME)));
|
||||
ss.put(new NodeProperty<>("Subject", "Subject", "Subject",
|
||||
getAttributeDisplayString(artifact, TSK_SUBJECT)));
|
||||
try {
|
||||
ss.put(new NodeProperty<>("Attms", "Attms", "Attms", artifact.getChildrenCount()));
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "Error loading attachment count for " + artifact, ex);
|
||||
}
|
||||
break;
|
||||
case TSK_CALLLOG:
|
||||
ss.put(new NodeProperty<>("From", "From", "From",
|
||||
getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_FROM)));
|
||||
ss.put(new NodeProperty<>("To", "To", "To",
|
||||
getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_TO)));
|
||||
ss.put(new NodeProperty<>("Date", "Date", "Date",
|
||||
getAttributeDisplayString(artifact, TSK_DATETIME_START)));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Get the display string for the attribute of the given type from the given
|
||||
* artifact.
|
||||
*
|
||||
* @param artifact the value of artifact
|
||||
* @param attributeType the value of TSK_SUBJECT1
|
||||
*
|
||||
* @return The display string, or an empty string if there is no such
|
||||
* attribute or an an error.
|
||||
*/
|
||||
private static String getAttributeDisplayString(final BlackboardArtifact artifact, final ATTRIBUTE_TYPE attributeType) {
|
||||
try {
|
||||
BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.fromID(attributeType.getTypeID())));
|
||||
if (attribute == null) {
|
||||
return "";
|
||||
} else if (attributeType.getValueType() == DATETIME) {
|
||||
return TimeUtilities.epochToTime(attribute.getValueLong(),
|
||||
TimeZone.getTimeZone(Utils.getUserPreferredZoneId()));
|
||||
} else {
|
||||
return attribute.getDisplayString();
|
||||
}
|
||||
} catch (TskCoreException tskCoreException) {
|
||||
logger.log(Level.WARNING, "Error getting attribute value.", tskCoreException);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
72
Core/src/org/sleuthkit/autopsy/communications/Utils.java
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2017 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;
|
||||
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZoneOffset;
|
||||
import org.sleuthkit.autopsy.core.UserPreferences;
|
||||
import org.sleuthkit.datamodel.Account;
|
||||
|
||||
/**
|
||||
* Utility class with helpers for dealing with accounts.
|
||||
*/
|
||||
class Utils {
|
||||
|
||||
private Utils() {
|
||||
}
|
||||
|
||||
static ZoneId getUserPreferredZoneId() {
|
||||
ZoneId zone = UserPreferences.displayTimesInLocalTime() ? ZoneOffset.systemDefault() : ZoneOffset.UTC;
|
||||
return zone;
|
||||
}
|
||||
|
||||
/**
|
||||
* The file name of the icon for the given Account Type. Will not include
|
||||
* the path but will include the extension.
|
||||
*
|
||||
* @return The file name of the icon for the given Account Type.
|
||||
*/
|
||||
static final String getIconFileName(Account.Type type) {
|
||||
if (type.equals(Account.Type.CREDIT_CARD)) {
|
||||
return "credit-card.png";
|
||||
} else if (type.equals(Account.Type.DEVICE)) {
|
||||
return "image.png";
|
||||
} else if (type.equals(Account.Type.EMAIL)) {
|
||||
return "email.png";
|
||||
} else if (type.equals(Account.Type.FACEBOOK)) {
|
||||
return "facebook.png";
|
||||
} else if (type.equals(Account.Type.INSTAGRAM)) {
|
||||
return "instagram.png";
|
||||
} else if (type.equals(Account.Type.MESSAGING_APP)) {
|
||||
return "messaging.png";
|
||||
} else if (type.equals(Account.Type.PHONE)) {
|
||||
return "phone.png";
|
||||
} else if (type.equals(Account.Type.TWITTER)) {
|
||||
return "twitter.png";
|
||||
} else if (type.equals(Account.Type.WEBSITE)) {
|
||||
return "web-file.png";
|
||||
} else if (type.equals(Account.Type.WHATSAPP)) {
|
||||
return "WhatsApp.png";
|
||||
} else {
|
||||
//there could be a default icon instead...
|
||||
throw new IllegalArgumentException("Unknown Account.Type: " + type.getTypeName());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
After Width: | Height: | Size: 1.7 KiB |
BIN
Core/src/org/sleuthkit/autopsy/communications/images/arrow-circle-double-135.png
Executable file
After Width: | Height: | Size: 864 B |
BIN
Core/src/org/sleuthkit/autopsy/communications/images/calllog.png
Executable file
After Width: | Height: | Size: 691 B |
After Width: | Height: | Size: 499 B |
BIN
Core/src/org/sleuthkit/autopsy/communications/images/credit-card.png
Executable file
After Width: | Height: | Size: 536 B |
BIN
Core/src/org/sleuthkit/autopsy/communications/images/email.png
Executable file
After Width: | Height: | Size: 710 B |
After Width: | Height: | Size: 821 B |
After Width: | Height: | Size: 887 B |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 541 B |
BIN
Core/src/org/sleuthkit/autopsy/communications/images/folder-icon-16.png
Executable file
After Width: | Height: | Size: 483 B |
BIN
Core/src/org/sleuthkit/autopsy/communications/images/funnel.png
Executable file
After Width: | Height: | Size: 591 B |
BIN
Core/src/org/sleuthkit/autopsy/communications/images/image.png
Executable file
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 804 B |
BIN
Core/src/org/sleuthkit/autopsy/communications/images/messaging.png
Executable file
After Width: | Height: | Size: 829 B |
BIN
Core/src/org/sleuthkit/autopsy/communications/images/phone.png
Normal file
After Width: | Height: | Size: 488 B |
BIN
Core/src/org/sleuthkit/autopsy/communications/images/table.png
Normal file
After Width: | Height: | Size: 566 B |
BIN
Core/src/org/sleuthkit/autopsy/communications/images/tick.png
Executable file
After Width: | Height: | Size: 582 B |
BIN
Core/src/org/sleuthkit/autopsy/communications/images/twitter.png
Normal file
After Width: | Height: | Size: 656 B |
BIN
Core/src/org/sleuthkit/autopsy/communications/images/web-file.png
Executable file
After Width: | Height: | Size: 928 B |
@ -17,3 +17,19 @@ Metadata.toolTip=Displays metadata about the file.
|
||||
Metadata.nodeText.nonFilePassedIn=Non-file passed in
|
||||
Metadata.nodeText.text=From The Sleuth Kit istat Tool\:
|
||||
Metadata.nodeText.exceptionNotice.text=Error getting file metadata\:
|
||||
MessageContentViewer.rtfbodyScrollPane.TabConstraints.tabTitle=RTF
|
||||
MessageContentViewer.headersScrollPane.TabConstraints.tabTitle=Headers
|
||||
MessageContentViewer.textbodyScrollPane.TabConstraints.tabTitle=Text
|
||||
MessageContentViewer.htmlPane.TabConstraints.tabTitle=HTML
|
||||
MessageContentViewer.fromText.text=from address goes here
|
||||
MessageContentViewer.fromLabel.text=From:
|
||||
MessageContentViewer.datetimeText.text=date goes here
|
||||
MessageContentViewer.toText.text=to list goes here
|
||||
MessageContentViewer.toLabel.text=To:
|
||||
MessageContentViewer.ccText.text=cc list goes here
|
||||
MessageContentViewer.subjectLabel.text=Subject:
|
||||
MessageContentViewer.subjectText.text=subject goes here
|
||||
MessageContentViewer.directionText.text=direction
|
||||
MessageContentViewer.ccLabel.text=CC:
|
||||
MessageContentViewer.attachmentsPanel.TabConstraints.tabTitle=Attachments
|
||||
MessageContentViewer.viewInNewWindowButton.text=View in New Window
|
||||
|
@ -0,0 +1,395 @@
|
||||
<?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"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace min="-2" pref="5" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="msgbodyTabbedPane" max="32767" attributes="0"/>
|
||||
<Component id="envelopePanel" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace min="-2" pref="5" 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 min="-2" pref="5" max="-2" attributes="0"/>
|
||||
<Component id="envelopePanel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
<Component id="msgbodyTabbedPane" max="32767" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="5" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JPanel" name="envelopePanel">
|
||||
<Properties>
|
||||
<Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
|
||||
<Color blue="cc" green="cc" red="cc" type="rgb"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<EmptySpace min="-2" pref="5" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="fromLabel" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="toLabel" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace type="unrelated" min="-2" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<Component id="toText" max="32767" attributes="0"/>
|
||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||
<Component id="directionText" min="-2" pref="66" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="102" attributes="0">
|
||||
<Component id="fromText" max="32767" attributes="0"/>
|
||||
<EmptySpace type="unrelated" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="datetimeText" min="-2" pref="140" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
<Group type="102" attributes="0">
|
||||
<Component id="ccLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="26" max="-2" attributes="0"/>
|
||||
<Component id="ccText" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="subjectLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
<Component id="subjectText" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
<EmptySpace min="-2" pref="5" 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 min="-2" pref="5" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="fromLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="datetimeText" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="fromText" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="toLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="toText" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="directionText" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="ccLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="ccText" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="subjectLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="subjectText" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace min="-2" pref="5" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JLabel" name="fromLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MessageContentViewer.fromLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="datetimeText">
|
||||
<Properties>
|
||||
<Property name="horizontalAlignment" type="int" value="4"/>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MessageContentViewer.datetimeText.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="fromText">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MessageContentViewer.fromText.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="toLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MessageContentViewer.toLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="toText">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MessageContentViewer.toText.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="ccLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MessageContentViewer.ccLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="ccText">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MessageContentViewer.ccText.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="subjectLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MessageContentViewer.subjectLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="subjectText">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MessageContentViewer.subjectText.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="directionText">
|
||||
<Properties>
|
||||
<Property name="horizontalAlignment" type="int" value="4"/>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MessageContentViewer.directionText.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Container class="javax.swing.JTabbedPane" name="msgbodyTabbedPane">
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JScrollPane" name="headersScrollPane">
|
||||
<Properties>
|
||||
<Property name="horizontalScrollBarPolicy" type="int" value="31"/>
|
||||
<Property name="verticalScrollBarPolicy" type="int" value="22"/>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
|
||||
<JTabbedPaneConstraints tabName="Headers">
|
||||
<Property name="tabTitle" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MessageContentViewer.headersScrollPane.TabConstraints.tabTitle" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</JTabbedPaneConstraints>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JTextArea" name="headersTextArea">
|
||||
<Properties>
|
||||
<Property name="editable" type="boolean" value="false"/>
|
||||
<Property name="columns" type="int" value="20"/>
|
||||
<Property name="lineWrap" type="boolean" value="true"/>
|
||||
<Property name="rows" type="int" value="5"/>
|
||||
<Property name="wrapStyleWord" type="boolean" value="true"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Container class="javax.swing.JScrollPane" name="textbodyScrollPane">
|
||||
<Properties>
|
||||
<Property name="horizontalScrollBarPolicy" type="int" value="31"/>
|
||||
<Property name="verticalScrollBarPolicy" type="int" value="22"/>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
|
||||
<JTabbedPaneConstraints tabName="Text">
|
||||
<Property name="tabTitle" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MessageContentViewer.textbodyScrollPane.TabConstraints.tabTitle" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</JTabbedPaneConstraints>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JTextArea" name="textbodyTextArea">
|
||||
<Properties>
|
||||
<Property name="editable" type="boolean" value="false"/>
|
||||
<Property name="lineWrap" type="boolean" value="true"/>
|
||||
<Property name="rows" type="int" value="5"/>
|
||||
<Property name="wrapStyleWord" type="boolean" value="true"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Container class="javax.swing.JPanel" name="htmlPane">
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
|
||||
<JTabbedPaneConstraints tabName="HTML">
|
||||
<Property name="tabTitle" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MessageContentViewer.htmlPane.TabConstraints.tabTitle" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</JTabbedPaneConstraints>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="htmlScrollPane" alignment="0" max="32767" attributes="0"/>
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<EmptySpace pref="533" max="32767" attributes="0"/>
|
||||
<Component id="showImagesToggleButton" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="3" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="showImagesToggleButton" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
<Component id="htmlScrollPane" pref="333" max="32767" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="0" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JScrollPane" name="htmlScrollPane">
|
||||
<Properties>
|
||||
<Property name="verticalScrollBarPolicy" type="int" value="22"/>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JTextPane" name="htmlbodyTextPane">
|
||||
<Properties>
|
||||
<Property name="editable" type="boolean" value="false"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Component class="javax.swing.JToggleButton" name="showImagesToggleButton">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" value="Show Images"/>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="showImagesToggleButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Container class="javax.swing.JScrollPane" name="rtfbodyScrollPane">
|
||||
<Properties>
|
||||
<Property name="verticalScrollBarPolicy" type="int" value="22"/>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
|
||||
<JTabbedPaneConstraints tabName="RTF">
|
||||
<Property name="tabTitle" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MessageContentViewer.rtfbodyScrollPane.TabConstraints.tabTitle" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</JTabbedPaneConstraints>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JTextPane" name="rtfbodyTextPane">
|
||||
<Properties>
|
||||
<Property name="editable" type="boolean" value="false"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Container class="javax.swing.JPanel" name="attachmentsPanel">
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
|
||||
<JTabbedPaneConstraints tabName="Attachments">
|
||||
<Property name="tabTitle" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MessageContentViewer.attachmentsPanel.TabConstraints.tabTitle" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</JTabbedPaneConstraints>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<Component id="viewInNewWindowButton" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="3" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Component id="attachmentsScrollPane" alignment="1" pref="647" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace min="-2" pref="0" max="-2" attributes="0"/>
|
||||
<Component id="viewInNewWindowButton" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="attachmentsScrollPane" pref="333" max="32767" attributes="0"/>
|
||||
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JButton" name="viewInNewWindowButton">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MessageContentViewer.viewInNewWindowButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="viewInNewWindowButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Container class="javax.swing.JScrollPane" name="attachmentsScrollPane">
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Form>
|
@ -0,0 +1,658 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-2017 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.contentviewers;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.swing.text.JTextComponent;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.openide.explorer.ExplorerManager;
|
||||
import org.openide.nodes.AbstractNode;
|
||||
import org.openide.nodes.Children;
|
||||
import org.openide.nodes.Node;
|
||||
import org.openide.nodes.Sheet;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.lookup.ServiceProvider;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
|
||||
import org.sleuthkit.autopsy.corecomponents.DataResultPanel;
|
||||
import org.sleuthkit.autopsy.corecomponents.TableFilterNode;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datamodel.FileNode;
|
||||
import org.sleuthkit.autopsy.datamodel.NodeProperty;
|
||||
import org.sleuthkit.autopsy.directorytree.DataResultFilterNode;
|
||||
import org.sleuthkit.autopsy.directorytree.NewWindowViewAction;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG;
|
||||
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME;
|
||||
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_RCVD;
|
||||
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION;
|
||||
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL_CC;
|
||||
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_HTML;
|
||||
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_PLAIN;
|
||||
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_RTF;
|
||||
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL_FROM;
|
||||
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL_TO;
|
||||
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_HEADERS;
|
||||
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM;
|
||||
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO;
|
||||
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SUBJECT;
|
||||
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* Shows SMS/MMS/EMail messages
|
||||
*/
|
||||
@ServiceProvider(service = DataContentViewer.class, position = 4)
|
||||
public class MessageContentViewer extends javax.swing.JPanel implements DataContentViewer {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Logger LOGGER = Logger.getLogger(MessageContentViewer.class.getName());
|
||||
|
||||
private static final int HDR_TAB_INDEX = 0;
|
||||
private static final int TEXT_TAB_INDEX = 1;
|
||||
private static final int HTML_TAB_INDEX = 2;
|
||||
private static final int RTF_TAB_INDEX = 3;
|
||||
private static final int ATTM_TAB_INDEX = 4;
|
||||
|
||||
private final List<JTextComponent> textAreas;
|
||||
|
||||
/**
|
||||
* Artifact currently being displayed
|
||||
*/
|
||||
private BlackboardArtifact artifact;
|
||||
private final DataResultPanel drp;
|
||||
private final ExplorerManager drpExplorerManager;
|
||||
|
||||
/**
|
||||
* Creates new MessageContentViewer
|
||||
*/
|
||||
public MessageContentViewer() {
|
||||
initComponents();
|
||||
drp = DataResultPanel.createInstanceUninitialized("Attachments", "", Node.EMPTY, 0, null);
|
||||
attachmentsScrollPane.setViewportView(drp);
|
||||
msgbodyTabbedPane.setEnabledAt(ATTM_TAB_INDEX, true);
|
||||
|
||||
textAreas = Arrays.asList(headersTextArea, textbodyTextArea, htmlbodyTextPane, rtfbodyTextPane);
|
||||
|
||||
Utilities.configureTextPaneAsHtml(htmlbodyTextPane);
|
||||
Utilities.configureTextPaneAsRtf(rtfbodyTextPane);
|
||||
resetComponent();
|
||||
|
||||
drp.open();
|
||||
drpExplorerManager = drp.getExplorerManager();
|
||||
drpExplorerManager.addPropertyChangeListener(evt
|
||||
-> viewInNewWindowButton.setEnabled(drpExplorerManager.getSelectedNodes().length == 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
|
||||
envelopePanel = new javax.swing.JPanel();
|
||||
fromLabel = new javax.swing.JLabel();
|
||||
datetimeText = new javax.swing.JLabel();
|
||||
fromText = new javax.swing.JLabel();
|
||||
toLabel = new javax.swing.JLabel();
|
||||
toText = new javax.swing.JLabel();
|
||||
ccLabel = new javax.swing.JLabel();
|
||||
ccText = new javax.swing.JLabel();
|
||||
subjectLabel = new javax.swing.JLabel();
|
||||
subjectText = new javax.swing.JLabel();
|
||||
directionText = new javax.swing.JLabel();
|
||||
msgbodyTabbedPane = new javax.swing.JTabbedPane();
|
||||
headersScrollPane = new javax.swing.JScrollPane();
|
||||
headersTextArea = new javax.swing.JTextArea();
|
||||
textbodyScrollPane = new javax.swing.JScrollPane();
|
||||
textbodyTextArea = new javax.swing.JTextArea();
|
||||
htmlPane = new javax.swing.JPanel();
|
||||
htmlScrollPane = new javax.swing.JScrollPane();
|
||||
htmlbodyTextPane = new javax.swing.JTextPane();
|
||||
showImagesToggleButton = new javax.swing.JToggleButton();
|
||||
rtfbodyScrollPane = new javax.swing.JScrollPane();
|
||||
rtfbodyTextPane = new javax.swing.JTextPane();
|
||||
attachmentsPanel = new javax.swing.JPanel();
|
||||
viewInNewWindowButton = new javax.swing.JButton();
|
||||
attachmentsScrollPane = new javax.swing.JScrollPane();
|
||||
|
||||
envelopePanel.setBackground(new java.awt.Color(204, 204, 204));
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(fromLabel, org.openide.util.NbBundle.getMessage(MessageContentViewer.class, "MessageContentViewer.fromLabel.text")); // NOI18N
|
||||
|
||||
datetimeText.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
|
||||
org.openide.awt.Mnemonics.setLocalizedText(datetimeText, org.openide.util.NbBundle.getMessage(MessageContentViewer.class, "MessageContentViewer.datetimeText.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(fromText, org.openide.util.NbBundle.getMessage(MessageContentViewer.class, "MessageContentViewer.fromText.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(toLabel, org.openide.util.NbBundle.getMessage(MessageContentViewer.class, "MessageContentViewer.toLabel.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(toText, org.openide.util.NbBundle.getMessage(MessageContentViewer.class, "MessageContentViewer.toText.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(ccLabel, org.openide.util.NbBundle.getMessage(MessageContentViewer.class, "MessageContentViewer.ccLabel.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(ccText, org.openide.util.NbBundle.getMessage(MessageContentViewer.class, "MessageContentViewer.ccText.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(subjectLabel, org.openide.util.NbBundle.getMessage(MessageContentViewer.class, "MessageContentViewer.subjectLabel.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(subjectText, org.openide.util.NbBundle.getMessage(MessageContentViewer.class, "MessageContentViewer.subjectText.text")); // NOI18N
|
||||
|
||||
directionText.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
|
||||
org.openide.awt.Mnemonics.setLocalizedText(directionText, org.openide.util.NbBundle.getMessage(MessageContentViewer.class, "MessageContentViewer.directionText.text")); // NOI18N
|
||||
|
||||
javax.swing.GroupLayout envelopePanelLayout = new javax.swing.GroupLayout(envelopePanel);
|
||||
envelopePanel.setLayout(envelopePanelLayout);
|
||||
envelopePanelLayout.setHorizontalGroup(
|
||||
envelopePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(envelopePanelLayout.createSequentialGroup()
|
||||
.addGap(5, 5, 5)
|
||||
.addGroup(envelopePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(envelopePanelLayout.createSequentialGroup()
|
||||
.addGroup(envelopePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(fromLabel)
|
||||
.addComponent(toLabel))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addGroup(envelopePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(envelopePanelLayout.createSequentialGroup()
|
||||
.addComponent(toText, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addComponent(directionText, javax.swing.GroupLayout.PREFERRED_SIZE, 66, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addGroup(envelopePanelLayout.createSequentialGroup()
|
||||
.addComponent(fromText, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addComponent(datetimeText, javax.swing.GroupLayout.PREFERRED_SIZE, 140, javax.swing.GroupLayout.PREFERRED_SIZE))))
|
||||
.addGroup(envelopePanelLayout.createSequentialGroup()
|
||||
.addComponent(ccLabel)
|
||||
.addGap(26, 26, 26)
|
||||
.addComponent(ccText, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
.addGroup(envelopePanelLayout.createSequentialGroup()
|
||||
.addComponent(subjectLabel)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(subjectText, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
|
||||
.addGap(5, 5, 5))
|
||||
);
|
||||
envelopePanelLayout.setVerticalGroup(
|
||||
envelopePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(envelopePanelLayout.createSequentialGroup()
|
||||
.addGap(5, 5, 5)
|
||||
.addGroup(envelopePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(fromLabel)
|
||||
.addComponent(datetimeText)
|
||||
.addComponent(fromText))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(envelopePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(toLabel)
|
||||
.addComponent(toText)
|
||||
.addComponent(directionText))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(envelopePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(ccLabel)
|
||||
.addComponent(ccText))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(envelopePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(subjectLabel)
|
||||
.addComponent(subjectText))
|
||||
.addGap(5, 5, 5))
|
||||
);
|
||||
|
||||
headersScrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
|
||||
headersScrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
|
||||
|
||||
headersTextArea.setEditable(false);
|
||||
headersTextArea.setColumns(20);
|
||||
headersTextArea.setLineWrap(true);
|
||||
headersTextArea.setRows(5);
|
||||
headersTextArea.setWrapStyleWord(true);
|
||||
headersScrollPane.setViewportView(headersTextArea);
|
||||
|
||||
msgbodyTabbedPane.addTab(org.openide.util.NbBundle.getMessage(MessageContentViewer.class, "MessageContentViewer.headersScrollPane.TabConstraints.tabTitle"), headersScrollPane); // NOI18N
|
||||
|
||||
textbodyScrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
|
||||
textbodyScrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
|
||||
|
||||
textbodyTextArea.setEditable(false);
|
||||
textbodyTextArea.setLineWrap(true);
|
||||
textbodyTextArea.setRows(5);
|
||||
textbodyTextArea.setWrapStyleWord(true);
|
||||
textbodyScrollPane.setViewportView(textbodyTextArea);
|
||||
|
||||
msgbodyTabbedPane.addTab(org.openide.util.NbBundle.getMessage(MessageContentViewer.class, "MessageContentViewer.textbodyScrollPane.TabConstraints.tabTitle"), textbodyScrollPane); // NOI18N
|
||||
|
||||
htmlScrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
|
||||
|
||||
htmlbodyTextPane.setEditable(false);
|
||||
htmlScrollPane.setViewportView(htmlbodyTextPane);
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(showImagesToggleButton, "Show Images");
|
||||
showImagesToggleButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
showImagesToggleButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
javax.swing.GroupLayout htmlPaneLayout = new javax.swing.GroupLayout(htmlPane);
|
||||
htmlPane.setLayout(htmlPaneLayout);
|
||||
htmlPaneLayout.setHorizontalGroup(
|
||||
htmlPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(htmlScrollPane)
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, htmlPaneLayout.createSequentialGroup()
|
||||
.addContainerGap(533, Short.MAX_VALUE)
|
||||
.addComponent(showImagesToggleButton)
|
||||
.addGap(3, 3, 3))
|
||||
);
|
||||
htmlPaneLayout.setVerticalGroup(
|
||||
htmlPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(htmlPaneLayout.createSequentialGroup()
|
||||
.addComponent(showImagesToggleButton)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(htmlScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 333, Short.MAX_VALUE)
|
||||
.addGap(0, 0, 0))
|
||||
);
|
||||
|
||||
msgbodyTabbedPane.addTab(org.openide.util.NbBundle.getMessage(MessageContentViewer.class, "MessageContentViewer.htmlPane.TabConstraints.tabTitle"), htmlPane); // NOI18N
|
||||
|
||||
rtfbodyScrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
|
||||
|
||||
rtfbodyTextPane.setEditable(false);
|
||||
rtfbodyScrollPane.setViewportView(rtfbodyTextPane);
|
||||
|
||||
msgbodyTabbedPane.addTab(org.openide.util.NbBundle.getMessage(MessageContentViewer.class, "MessageContentViewer.rtfbodyScrollPane.TabConstraints.tabTitle"), rtfbodyScrollPane); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(viewInNewWindowButton, org.openide.util.NbBundle.getMessage(MessageContentViewer.class, "MessageContentViewer.viewInNewWindowButton.text")); // NOI18N
|
||||
viewInNewWindowButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
viewInNewWindowButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
javax.swing.GroupLayout attachmentsPanelLayout = new javax.swing.GroupLayout(attachmentsPanel);
|
||||
attachmentsPanel.setLayout(attachmentsPanelLayout);
|
||||
attachmentsPanelLayout.setHorizontalGroup(
|
||||
attachmentsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(attachmentsPanelLayout.createSequentialGroup()
|
||||
.addGap(0, 0, 0)
|
||||
.addGroup(attachmentsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, attachmentsPanelLayout.createSequentialGroup()
|
||||
.addComponent(viewInNewWindowButton)
|
||||
.addGap(3, 3, 3))
|
||||
.addComponent(attachmentsScrollPane, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 647, Short.MAX_VALUE)))
|
||||
);
|
||||
attachmentsPanelLayout.setVerticalGroup(
|
||||
attachmentsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(attachmentsPanelLayout.createSequentialGroup()
|
||||
.addGap(0, 0, 0)
|
||||
.addComponent(viewInNewWindowButton)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(attachmentsScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 333, Short.MAX_VALUE)
|
||||
.addGap(0, 0, 0))
|
||||
);
|
||||
|
||||
msgbodyTabbedPane.addTab(org.openide.util.NbBundle.getMessage(MessageContentViewer.class, "MessageContentViewer.attachmentsPanel.TabConstraints.tabTitle"), attachmentsPanel); // NOI18N
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGap(5, 5, 5)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(msgbodyTabbedPane)
|
||||
.addComponent(envelopePanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
.addGap(5, 5, 5))
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGap(5, 5, 5)
|
||||
.addComponent(envelopePanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(msgbodyTabbedPane)
|
||||
.addGap(5, 5, 5))
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
@NbBundle.Messages({
|
||||
"MessageContentViewer.showImagesToggleButton.hide.text=Hide Images",
|
||||
"MessageContentViewer.showImagesToggleButton.text=Show Images"})
|
||||
private void showImagesToggleButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_showImagesToggleButtonActionPerformed
|
||||
try {
|
||||
String htmlText = getAttributeValueSafe(artifact, TSK_EMAIL_CONTENT_HTML);
|
||||
if (false == htmlText.isEmpty()) {
|
||||
if (showImagesToggleButton.isSelected()) {
|
||||
showImagesToggleButton.setText(Bundle.MessageContentViewer_showImagesToggleButton_hide_text());
|
||||
this.htmlbodyTextPane.setText(wrapInHtmlBody(htmlText));
|
||||
} else {
|
||||
showImagesToggleButton.setText(Bundle.MessageContentViewer_showImagesToggleButton_text());
|
||||
this.htmlbodyTextPane.setText(wrapInHtmlBody(cleanseHTML(htmlText)));
|
||||
}
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
LOGGER.log(Level.WARNING, "Failed to get attributes for email message.", ex); //NON-NLS
|
||||
}
|
||||
}//GEN-LAST:event_showImagesToggleButtonActionPerformed
|
||||
|
||||
private void viewInNewWindowButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_viewInNewWindowButtonActionPerformed
|
||||
new NewWindowViewAction("View in new window", drpExplorerManager.getSelectedNodes()[0]).actionPerformed(evt);
|
||||
}//GEN-LAST:event_viewInNewWindowButtonActionPerformed
|
||||
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JPanel attachmentsPanel;
|
||||
private javax.swing.JScrollPane attachmentsScrollPane;
|
||||
private javax.swing.JLabel ccLabel;
|
||||
private javax.swing.JLabel ccText;
|
||||
private javax.swing.JLabel datetimeText;
|
||||
private javax.swing.JLabel directionText;
|
||||
private javax.swing.JPanel envelopePanel;
|
||||
private javax.swing.JLabel fromLabel;
|
||||
private javax.swing.JLabel fromText;
|
||||
private javax.swing.JScrollPane headersScrollPane;
|
||||
private javax.swing.JTextArea headersTextArea;
|
||||
private javax.swing.JPanel htmlPane;
|
||||
private javax.swing.JScrollPane htmlScrollPane;
|
||||
private javax.swing.JTextPane htmlbodyTextPane;
|
||||
private javax.swing.JTabbedPane msgbodyTabbedPane;
|
||||
private javax.swing.JScrollPane rtfbodyScrollPane;
|
||||
private javax.swing.JTextPane rtfbodyTextPane;
|
||||
private javax.swing.JToggleButton showImagesToggleButton;
|
||||
private javax.swing.JLabel subjectLabel;
|
||||
private javax.swing.JLabel subjectText;
|
||||
private javax.swing.JScrollPane textbodyScrollPane;
|
||||
private javax.swing.JTextArea textbodyTextArea;
|
||||
private javax.swing.JLabel toLabel;
|
||||
private javax.swing.JLabel toText;
|
||||
private javax.swing.JButton viewInNewWindowButton;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
@Override
|
||||
public void setNode(Node node) {
|
||||
if (node == null) {
|
||||
resetComponent();
|
||||
return;
|
||||
}
|
||||
|
||||
artifact = node.getLookup().lookup(BlackboardArtifact.class);
|
||||
if (artifact == null) {
|
||||
resetComponent();
|
||||
return;
|
||||
}
|
||||
|
||||
if (artifact.getArtifactTypeID() == TSK_MESSAGE.getTypeID()) {
|
||||
displayMsg();
|
||||
} else if (artifact.getArtifactTypeID() == TSK_EMAIL_MSG.getTypeID()) {
|
||||
displayEmailMsg();
|
||||
} else {
|
||||
resetComponent();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@NbBundle.Messages("MessageContentViewer.title=Message")
|
||||
public String getTitle() {
|
||||
return Bundle.MessageContentViewer_title();
|
||||
}
|
||||
|
||||
@Override
|
||||
@NbBundle.Messages("MessageContentViewer.toolTip=Displays messages.")
|
||||
public String getToolTip() {
|
||||
return Bundle.MessageContentViewer_toolTip();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataContentViewer createInstance() {
|
||||
return new MessageContentViewer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getComponent() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
final public void resetComponent() {
|
||||
// reset all fields
|
||||
fromText.setText("");
|
||||
fromLabel.setEnabled(false);
|
||||
toText.setText("");
|
||||
toLabel.setEnabled(false);
|
||||
ccText.setText("");
|
||||
ccLabel.setEnabled(false);
|
||||
subjectText.setText("");
|
||||
subjectLabel.setEnabled(false);
|
||||
datetimeText.setText("");
|
||||
datetimeText.setEnabled(false);
|
||||
directionText.setText("");
|
||||
directionText.setEnabled(false);
|
||||
|
||||
headersTextArea.setText("");
|
||||
rtfbodyTextPane.setText("");
|
||||
htmlbodyTextPane.setText("");
|
||||
textbodyTextArea.setText("");
|
||||
drp.setNode(null);
|
||||
msgbodyTabbedPane.setEnabled(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSupported(Node node) {
|
||||
BlackboardArtifact artifact = node.getLookup().lookup(BlackboardArtifact.class);
|
||||
return ((artifact != null)
|
||||
&& ((artifact.getArtifactTypeID() == TSK_EMAIL_MSG.getTypeID())
|
||||
|| (artifact.getArtifactTypeID() == TSK_MESSAGE.getTypeID())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int isPreferred(Node node) {
|
||||
if (isSupported(node)) {
|
||||
return 6;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the text area at the given index to show the content of the
|
||||
* given type.
|
||||
*
|
||||
* @param type The ATTRIBUT_TYPE to show in the indexed tab.
|
||||
* @param index The index of the text area to configure.
|
||||
*
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
private void configureTextArea(BlackboardAttribute.ATTRIBUTE_TYPE type, int index) throws TskCoreException {
|
||||
String attributeText = getAttributeValueSafe(artifact, type);
|
||||
|
||||
if (index == HTML_TAB_INDEX && StringUtils.isNotBlank(attributeText)) {
|
||||
//special case for HTML, we need to 'cleanse' it
|
||||
attributeText = wrapInHtmlBody(cleanseHTML(attributeText));
|
||||
}
|
||||
JTextComponent textComponent = textAreas.get(index);
|
||||
textComponent.setText(attributeText);
|
||||
textComponent.setCaretPosition(0); //make sure we start at the top
|
||||
final boolean hasText = attributeText.length() > 0;
|
||||
|
||||
msgbodyTabbedPane.setEnabledAt(index, hasText);
|
||||
if (hasText) {
|
||||
msgbodyTabbedPane.setSelectedIndex(index);
|
||||
}
|
||||
}
|
||||
|
||||
private void enableCommonFields() {
|
||||
msgbodyTabbedPane.setEnabled(true);
|
||||
fromLabel.setEnabled(true);
|
||||
toLabel.setEnabled(true);
|
||||
subjectLabel.setEnabled(true);
|
||||
datetimeText.setEnabled(true);
|
||||
}
|
||||
|
||||
private void configureAttachments() throws TskCoreException {
|
||||
//TODO: Replace this with code to get the actual attachements!
|
||||
final Set<AbstractFile> attachments = artifact.getChildren().stream()
|
||||
.filter(AbstractFile.class::isInstance)
|
||||
.map(AbstractFile.class::cast)
|
||||
.collect(Collectors.toSet());
|
||||
final int numberOfAttachments = attachments.size();
|
||||
|
||||
msgbodyTabbedPane.setEnabledAt(ATTM_TAB_INDEX, numberOfAttachments > 0);
|
||||
msgbodyTabbedPane.setTitleAt(ATTM_TAB_INDEX, "Attachments (" + numberOfAttachments + ")");
|
||||
drp.setNode(new TableFilterNode(new DataResultFilterNode(new AbstractNode(
|
||||
new AttachmentsChildren(attachments)), null), true));
|
||||
}
|
||||
|
||||
private static String wrapInHtmlBody(String htmlText) {
|
||||
return "<html><body>" + htmlText + "</body></html>";
|
||||
}
|
||||
|
||||
private void displayEmailMsg() {
|
||||
enableCommonFields();
|
||||
|
||||
directionText.setEnabled(false);
|
||||
ccLabel.setEnabled(true);
|
||||
|
||||
showImagesToggleButton.setText("Show Images");
|
||||
showImagesToggleButton.setSelected(false);
|
||||
|
||||
try {
|
||||
this.fromText.setText(getAttributeValueSafe(artifact, TSK_EMAIL_FROM));
|
||||
this.toText.setText(getAttributeValueSafe(artifact, TSK_EMAIL_TO));
|
||||
this.directionText.setText("");
|
||||
this.ccText.setText(getAttributeValueSafe(artifact, TSK_EMAIL_CC));
|
||||
this.subjectText.setText(getAttributeValueSafe(artifact, TSK_SUBJECT));
|
||||
this.datetimeText.setText(getAttributeValueSafe(artifact, TSK_DATETIME_RCVD));
|
||||
|
||||
configureTextArea(TSK_HEADERS, HDR_TAB_INDEX);
|
||||
configureTextArea(TSK_EMAIL_CONTENT_PLAIN, TEXT_TAB_INDEX);
|
||||
configureTextArea(TSK_EMAIL_CONTENT_HTML, HTML_TAB_INDEX);
|
||||
configureTextArea(TSK_EMAIL_CONTENT_RTF, RTF_TAB_INDEX);
|
||||
configureAttachments();
|
||||
} catch (TskCoreException ex) {
|
||||
LOGGER.log(Level.WARNING, "Failed to get attributes for email message.", ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
private void displayMsg() {
|
||||
enableCommonFields();
|
||||
|
||||
directionText.setEnabled(true);
|
||||
ccLabel.setEnabled(false);
|
||||
|
||||
try {
|
||||
this.fromText.setText(getAttributeValueSafe(artifact, TSK_PHONE_NUMBER_FROM));
|
||||
this.toText.setText(getAttributeValueSafe(artifact, TSK_PHONE_NUMBER_TO));
|
||||
this.directionText.setText(getAttributeValueSafe(artifact, TSK_DIRECTION));
|
||||
this.ccText.setText("");
|
||||
this.subjectText.setText(getAttributeValueSafe(artifact, TSK_SUBJECT));
|
||||
this.datetimeText.setText(getAttributeValueSafe(artifact, TSK_DATETIME));
|
||||
|
||||
msgbodyTabbedPane.setEnabledAt(HTML_TAB_INDEX, false);
|
||||
msgbodyTabbedPane.setEnabledAt(RTF_TAB_INDEX, false);
|
||||
msgbodyTabbedPane.setEnabledAt(HDR_TAB_INDEX, false);
|
||||
msgbodyTabbedPane.setEnabledAt(HDR_TAB_INDEX, false);
|
||||
configureTextArea(TSK_TEXT, TEXT_TAB_INDEX);
|
||||
configureAttachments();
|
||||
} catch (TskCoreException ex) {
|
||||
LOGGER.log(Level.WARNING, "Failed to get attributes for message.", ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
private static String getAttributeValueSafe(BlackboardArtifact artifact, BlackboardAttribute.ATTRIBUTE_TYPE type) throws TskCoreException {
|
||||
return Optional.ofNullable(artifact.getAttribute(new BlackboardAttribute.Type(type)))
|
||||
.map(BlackboardAttribute::getDisplayString)
|
||||
.orElse("");
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans out input HTML string
|
||||
*
|
||||
* @param htmlInString The HTML string to cleanse
|
||||
*
|
||||
* @return The cleansed HTML String
|
||||
*/
|
||||
static private String cleanseHTML(String htmlInString) {
|
||||
|
||||
Document doc = Jsoup.parse(htmlInString);
|
||||
|
||||
//fix all img tags
|
||||
doc.select("img[src]").forEach(img -> img.attr("src", ""));
|
||||
|
||||
return doc.html();
|
||||
}
|
||||
|
||||
private static class AttachmentsChildren extends Children.Keys<AbstractFile> {
|
||||
|
||||
private final Set<AbstractFile> attachments;
|
||||
|
||||
AttachmentsChildren(Set<AbstractFile> attachments) {
|
||||
this.attachments = attachments;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node[] createNodes(AbstractFile t) {
|
||||
return new Node[]{new AttachmentNode(t)};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addNotify() {
|
||||
super.addNotify();
|
||||
setKeys(attachments);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension of FileNode customized for viewing attachments in the
|
||||
* MessageContentViewer. It overrides createSheet() to customize what
|
||||
* properties are shown in the table, and could also override getActions(),
|
||||
* getPreferedAction(), etc.
|
||||
*/
|
||||
private static class AttachmentNode extends FileNode {
|
||||
|
||||
AttachmentNode(AbstractFile file) {
|
||||
super(file, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Sheet createSheet() {
|
||||
Sheet s = new Sheet();
|
||||
Sheet.Set ss = s.get(Sheet.PROPERTIES);
|
||||
if (ss == null) {
|
||||
ss = Sheet.createPropertiesSet();
|
||||
s.put(ss);
|
||||
}
|
||||
AbstractFile file = getContent();
|
||||
ss.put(new NodeProperty<>("Name", "Name", "Name", file.getName()));
|
||||
ss.put(new NodeProperty<>("Size", "Size", "Size", file.getSize()));
|
||||
ss.put(new NodeProperty<>("Mime Type", "Mime Type", "Mime Type", StringUtils.defaultString(file.getMIMEType())));
|
||||
ss.put(new NodeProperty<>("Known", "Known", "Known", file.getKnown().getName()));
|
||||
|
||||
addTagProperty(ss);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.contentviewers;
|
||||
import javax.swing.JTextPane;
|
||||
import javax.swing.text.html.HTMLEditorKit;
|
||||
import javax.swing.text.html.StyleSheet;
|
||||
import javax.swing.text.rtf.RTFEditorKit;
|
||||
|
||||
/**
|
||||
*
|
||||
@ -47,4 +48,11 @@ public class Utilities {
|
||||
styleSheet.addRule("th {font-family:Arial, 'ヒラギノ角ゴ Pro W3','Hiragino Kaku Gothic Pro','メイリオ',Meiryo,'MS Pゴシック','MS PGothic',sans-serif;font-size:14pt;overflow:hidden;padding-right:5px;padding-left:5px;font-weight:bold;}"); //NON-NLS
|
||||
styleSheet.addRule("p {font-family:Arial, 'ヒラギノ角ゴ Pro W3','Hiragino Kaku Gothic Pro','メイリオ',Meiryo,'MS Pゴシック','MS PGothic',sans-serif;font-size:14pt;}"); //NON-NLS
|
||||
}
|
||||
|
||||
public static void configureTextPaneAsRtf(JTextPane pane) {
|
||||
|
||||
pane.setContentType("text/html;charset=UTF-8"); //NON-NLS
|
||||
RTFEditorKit rtfkit = new RTFEditorKit();
|
||||
pane.setEditorKit(rtfkit);
|
||||
}
|
||||
}
|
||||
|
11
Core/src/org/sleuthkit/autopsy/core/cvtWsmode.xml
Normal file
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<mode version="2.4">
|
||||
<name unique="cvt"/>
|
||||
<kind type="editor"/>
|
||||
<state type="separated"/>
|
||||
<bounds x="76" y="68" width="996" height="672"/>
|
||||
<frame state="0"/>
|
||||
|
||||
<empty-behavior permanent="false"/>
|
||||
</mode>
|
||||
|
@ -443,6 +443,7 @@
|
||||
<folder name="floatingLeftBottom"/>
|
||||
<file name="floatingLeftBottom.wsmode" url="floatingLeftBottomWsmode.xml"/>
|
||||
<file name="timeline.wsmode" url="timelineWsmode.xml"/>
|
||||
<file name="cvt.wsmode" url="cvtWsmode.xml"/>
|
||||
</folder>
|
||||
</folder>
|
||||
|
||||
|
@ -20,12 +20,10 @@ package org.sleuthkit.autopsy.corecomponentinterfaces;
|
||||
|
||||
import java.beans.PropertyChangeListener;
|
||||
import org.openide.nodes.Node;
|
||||
import org.openide.windows.TopComponent;
|
||||
|
||||
/**
|
||||
* The interface for the "bottom right component" window.
|
||||
*
|
||||
* @author jantonius
|
||||
*/
|
||||
public interface DataContent extends PropertyChangeListener {
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2013 Basis Technology Corp.
|
||||
* Copyright 2011-17 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -85,10 +85,13 @@ public interface DataResultViewer {
|
||||
|
||||
/**
|
||||
* Set a custom content viewer to respond to selection events from this
|
||||
* result viewer. If not set, the default content viewer is user
|
||||
* result viewer. If not set, the default content viewer is used
|
||||
*
|
||||
* @param contentViewer content viewer to respond to selection events from
|
||||
* this viewer
|
||||
*
|
||||
* @deprecated All implementations of this in the standard DataResultViewers are now no-ops.
|
||||
*/
|
||||
@Deprecated
|
||||
public void setContentViewer(DataContent contentViewer);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2013 Basis Technology Corp.
|
||||
* Copyright 2011-17 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -25,7 +25,6 @@ import javax.swing.JPanel;
|
||||
import org.openide.explorer.ExplorerManager;
|
||||
import org.openide.explorer.ExplorerManager.Provider;
|
||||
import org.openide.nodes.Node;
|
||||
import org.openide.util.Lookup;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContent;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
@ -40,11 +39,6 @@ abstract class AbstractDataResultViewer extends JPanel implements DataResultView
|
||||
|
||||
private static final Logger logger = Logger.getLogger(AbstractDataResultViewer.class.getName());
|
||||
protected transient ExplorerManager em;
|
||||
/**
|
||||
* Content viewer to respond to selection events Either the main one, or
|
||||
* custom one if set
|
||||
*/
|
||||
protected DataContent contentViewer;
|
||||
|
||||
/**
|
||||
* This constructor is intended to allow an AbstractDataResultViewer to use
|
||||
@ -53,10 +47,11 @@ abstract class AbstractDataResultViewer extends JPanel implements DataResultView
|
||||
* TopComponent has focus. The ExplorerManager must be present when the
|
||||
* object is constructed so that its child components can discover it using
|
||||
* the ExplorerManager.find() method.
|
||||
*
|
||||
* @param explorerManager
|
||||
*/
|
||||
public AbstractDataResultViewer(ExplorerManager explorerManager) {
|
||||
AbstractDataResultViewer(ExplorerManager explorerManager) {
|
||||
this.em = explorerManager;
|
||||
initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -65,14 +60,7 @@ abstract class AbstractDataResultViewer extends JPanel implements DataResultView
|
||||
* context lookup.
|
||||
*/
|
||||
public AbstractDataResultViewer() {
|
||||
em = new ExplorerManager();
|
||||
initialize();
|
||||
}
|
||||
|
||||
private void initialize() {
|
||||
//DataContent is designed to return only the default viewer from lookup
|
||||
//use the default one unless set otherwise
|
||||
contentViewer = Lookup.getDefault().lookup(DataContent.class);
|
||||
this(new ExplorerManager());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -115,8 +103,8 @@ abstract class AbstractDataResultViewer extends JPanel implements DataResultView
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public void setContentViewer(DataContent contentViewer) {
|
||||
this.contentViewer = contentViewer;
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ import java.awt.Cursor;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import javax.swing.JTabbedPane;
|
||||
import javax.swing.SwingUtilities;
|
||||
@ -64,7 +65,7 @@ import org.sleuthkit.autopsy.datamodel.NodeSelectionInfo;
|
||||
* normally docked into the lower right hand side of the main window, underneath
|
||||
* the results view. A custom content view may be specified instead.
|
||||
*/
|
||||
public class DataResultPanel extends javax.swing.JPanel implements DataResult, ChangeListener {
|
||||
public class DataResultPanel extends javax.swing.JPanel implements DataResult, ChangeListener, ExplorerManager.Provider {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final int NO_TAB_SELECTED = -1;
|
||||
@ -79,8 +80,8 @@ public class DataResultPanel extends javax.swing.JPanel implements DataResult, C
|
||||
private DataContent contentView;
|
||||
|
||||
/**
|
||||
* Constructs and opens a Swing JPanel with a JTabbedPane child component.
|
||||
* The tabbed pane contains result viewers.
|
||||
* Constructs and opens a DataResultPanel with the given initial data, and
|
||||
* the default DataContent.
|
||||
*
|
||||
* @param title The title for the panel.
|
||||
* @param pathText Descriptive text about the source of the nodes
|
||||
@ -98,8 +99,8 @@ public class DataResultPanel extends javax.swing.JPanel implements DataResult, C
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs and opens a Swing JPanel with a JTabbedPane child component.
|
||||
* The tabbed pane contains result viewers.
|
||||
* Constructs and opens a DataResultPanel with the given initial data, and a
|
||||
* custom DataContent.
|
||||
*
|
||||
* @param title The title for the panel.
|
||||
* @param pathText Descriptive text about the source of the nodes
|
||||
@ -119,9 +120,9 @@ public class DataResultPanel extends javax.swing.JPanel implements DataResult, C
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a Swing JPanel with a JTabbedPane child component. The tabbed
|
||||
* pane contains result viewers. The panel is NOT opened; the client of this
|
||||
* method must call open on the panel that is returned.
|
||||
* Constructs a DataResultPanel with the given initial data, and a custom
|
||||
* DataContent. The panel is NOT opened; the client of this method must call
|
||||
* open on the panel that is returned.
|
||||
*
|
||||
* @param title The title for the panel.
|
||||
* @param pathText Descriptive text about the source of the nodes
|
||||
@ -159,42 +160,32 @@ public class DataResultPanel extends javax.swing.JPanel implements DataResult, C
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a Swing JPanel with a JTabbedPane child component that
|
||||
* contains result viewers (implementations of the DataResultViewer
|
||||
* interface).
|
||||
*/
|
||||
private DataResultPanel() {
|
||||
this.isMain = true;
|
||||
initComponents();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a Swing JPanel with a JTabbedPane child component that
|
||||
* contains result viewers (implementations of the DataResultViewer
|
||||
* interface).
|
||||
* Constructs a DataResultPanel with the default DataContent
|
||||
*
|
||||
* @param title The title for the panel.
|
||||
* @param isMain True if the DataResultPanel being constructed is the "main"
|
||||
* DataResultPanel.
|
||||
*/
|
||||
DataResultPanel(String title, boolean isMain) {
|
||||
this();
|
||||
this(isMain, Lookup.getDefault().lookup(DataContent.class));
|
||||
setTitle(title);
|
||||
}
|
||||
|
||||
private DataResultPanel(boolean isMain, DataContent contentView) {
|
||||
this.isMain = isMain;
|
||||
this.contentView = Lookup.getDefault().lookup(DataContent.class);
|
||||
this.contentView = contentView;
|
||||
initComponents();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a Swing JPanel with a JTabbedPane child component that
|
||||
* contains result viewers (implementations of the DataResultViewer
|
||||
* interface).
|
||||
* Constructs a DataResultPanel with the a custom DataContent.
|
||||
*
|
||||
* @param title The title for the panel.
|
||||
* @param customContentView A content view to use in place of the default
|
||||
* content view.
|
||||
*/
|
||||
DataResultPanel(String title, DataContent customContentView) {
|
||||
this(title, false);
|
||||
this.contentView = customContentView;
|
||||
this(false, customContentView);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -245,10 +236,7 @@ public class DataResultPanel extends javax.swing.JPanel implements DataResult, C
|
||||
*
|
||||
* @param resultViewer The result viewer.
|
||||
*/
|
||||
private void addResultViewer(DataResultViewer resultViewer) {
|
||||
if (null != contentView) {
|
||||
resultViewer.setContentViewer(contentView);
|
||||
}
|
||||
public void addResultViewer(DataResultViewer resultViewer) {
|
||||
resultViewers.add(resultViewer);
|
||||
dataResultTabbedPanel.addTab(resultViewer.getTitle(), resultViewer.getComponent());
|
||||
}
|
||||
@ -260,11 +248,7 @@ public class DataResultPanel extends javax.swing.JPanel implements DataResult, C
|
||||
*/
|
||||
@Override
|
||||
public List<DataResultViewer> getViewers() {
|
||||
List<DataResultViewer> viewers = new ArrayList<>();
|
||||
resultViewers.forEach((viewer) -> {
|
||||
viewers.add(viewer);
|
||||
});
|
||||
return viewers;
|
||||
return Collections.unmodifiableList(resultViewers);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -397,9 +381,7 @@ public class DataResultPanel extends javax.swing.JPanel implements DataResult, C
|
||||
* @param selectedNodes The nodes to be selected.
|
||||
*/
|
||||
public void setSelectedNodes(Node[] selectedNodes) {
|
||||
this.resultViewers.forEach((viewer) -> {
|
||||
viewer.setSelectedNodes(selectedNodes);
|
||||
});
|
||||
this.resultViewers.forEach((viewer) -> viewer.setSelectedNodes(selectedNodes));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -517,14 +499,10 @@ public class DataResultPanel extends javax.swing.JPanel implements DataResult, C
|
||||
explorerManager = null;
|
||||
}
|
||||
|
||||
this.resultViewers.forEach((viewer) -> {
|
||||
viewer.setNode(null);
|
||||
});
|
||||
this.resultViewers.forEach((viewer) -> viewer.setNode(null));
|
||||
|
||||
if (!this.isMain) {
|
||||
this.resultViewers.forEach((viewer) -> {
|
||||
viewer.clearComponent();
|
||||
});
|
||||
this.resultViewers.forEach(DataResultViewer::clearComponent);
|
||||
this.directoryTablePath.removeAll();
|
||||
this.directoryTablePath = null;
|
||||
this.numberMatchLabel.removeAll();
|
||||
@ -537,6 +515,11 @@ public class DataResultPanel extends javax.swing.JPanel implements DataResult, C
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExplorerManager getExplorerManager() {
|
||||
return explorerManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Responds to node selection change events from the explorer manager.
|
||||
*/
|
||||
@ -563,9 +546,7 @@ public class DataResultPanel extends javax.swing.JPanel implements DataResult, C
|
||||
* Pass the selected nodes to all of the result viewers
|
||||
* sharing this explorer manager.
|
||||
*/
|
||||
resultViewers.forEach((viewer) -> {
|
||||
viewer.setSelectedNodes(selectedNodes);
|
||||
});
|
||||
resultViewers.forEach((viewer) -> viewer.setSelectedNodes(selectedNodes));
|
||||
|
||||
/*
|
||||
* Passing null signals that either multiple nodes are
|
||||
|
@ -80,6 +80,9 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
||||
@NbBundle.Messages("DataResultViewerTable.firstColLbl=Name")
|
||||
static private final String FIRST_COLUMN_LABEL = Bundle.DataResultViewerTable_firstColLbl();
|
||||
private static final Color TAGGED_COLOR = new Color(255, 255, 195);
|
||||
|
||||
private final String title;
|
||||
|
||||
/**
|
||||
* The properties map:
|
||||
*
|
||||
@ -108,17 +111,42 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
||||
/**
|
||||
* Listener for table model event and mouse clicks.
|
||||
*/
|
||||
private TableListener tableListener;
|
||||
private final TableListener tableListener;
|
||||
|
||||
/**
|
||||
* Creates a DataResultViewerTable object that is compatible with node
|
||||
* multiple selection actions.
|
||||
* multiple selection actions, and the default title.
|
||||
*
|
||||
* @param explorerManager allow for explorer manager sharing
|
||||
*/
|
||||
public DataResultViewerTable(ExplorerManager explorerManager) {
|
||||
this(explorerManager, Bundle.DataResultViewerTable_title());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a DataResultViewerTable object that is compatible with node
|
||||
* multiple selection actions, and a custom title.
|
||||
*
|
||||
* @param explorerManager allow for explorer manager sharing
|
||||
* @param title The custom title.
|
||||
*/
|
||||
public DataResultViewerTable(ExplorerManager explorerManager, String title) {
|
||||
super(explorerManager);
|
||||
initialize();
|
||||
this.title = title;
|
||||
|
||||
initComponents();
|
||||
|
||||
outlineView.setAllowedDragActions(DnDConstants.ACTION_NONE);
|
||||
outline = outlineView.getOutline();
|
||||
outline.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
||||
outline.setRootVisible(false); // don't show the root node
|
||||
outline.setDragEnabled(false);
|
||||
outline.setDefaultRenderer(Object.class, new ColorTagCustomRenderer());
|
||||
// add a listener so that when columns are moved, the new order is stored
|
||||
tableListener = new TableListener();
|
||||
outline.getColumnModel().addColumnModelListener(tableListener);
|
||||
// the listener also moves columns back if user tries to move the first column out of place
|
||||
outline.getTableHeader().addMouseListener(tableListener);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -126,26 +154,9 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
||||
* multiple selection actions.
|
||||
*/
|
||||
public DataResultViewerTable() {
|
||||
initialize();
|
||||
this(new ExplorerManager(),Bundle.DataResultViewerTable_title());
|
||||
}
|
||||
|
||||
private void initialize() {
|
||||
initComponents();
|
||||
|
||||
outlineView.setAllowedDragActions(DnDConstants.ACTION_NONE);
|
||||
|
||||
outline = outlineView.getOutline();
|
||||
outline.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
||||
outline.setRootVisible(false); // don't show the root node
|
||||
outline.setDragEnabled(false);
|
||||
outline.setDefaultRenderer(Object.class, new ColorTagCustomRenderer());
|
||||
|
||||
// add a listener so that when columns are moved, the new order is stored
|
||||
tableListener = new TableListener();
|
||||
outline.getColumnModel().addColumnModelListener(tableListener);
|
||||
// the listener also moves columns back if user tries to move the first column out of place
|
||||
outline.getTableHeader().addMouseListener(tableListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand node
|
||||
@ -579,7 +590,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
||||
@Override
|
||||
@NbBundle.Messages("DataResultViewerTable.title=Table")
|
||||
public String getTitle() {
|
||||
return Bundle.DataResultViewerTable_title();
|
||||
return title;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -764,7 +775,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
||||
|
||||
Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, col);
|
||||
// only override the color if a node is not selected
|
||||
if (!isSelected) {
|
||||
if (currentRoot != null && !isSelected) {
|
||||
Node node = currentRoot.getChildren().getNodeAt(table.convertRowIndexToModel(row));
|
||||
boolean tagFound = false;
|
||||
if (node != null) {
|
||||
|
@ -61,6 +61,7 @@ public abstract class AbstractFsContentNode<T extends AbstractFile> extends Abst
|
||||
}
|
||||
|
||||
@Override
|
||||
@NbBundle.Messages("AbstractFsContentNode.noDesc.text=no description")
|
||||
protected Sheet createSheet() {
|
||||
Sheet s = super.createSheet();
|
||||
Sheet.Set ss = s.get(Sheet.PROPERTIES);
|
||||
@ -72,7 +73,7 @@ public abstract class AbstractFsContentNode<T extends AbstractFile> extends Abst
|
||||
Map<String, Object> map = new LinkedHashMap<>();
|
||||
fillPropertyMap(map, getContent());
|
||||
|
||||
final String NO_DESCR = NbBundle.getMessage(this.getClass(), "AbstractFsContentNode.noDesc.text");
|
||||
final String NO_DESCR = Bundle.AbstractFsContentNode_noDesc_text();
|
||||
for (AbstractFilePropertyType propType : AbstractFilePropertyType.values()) {
|
||||
final String propString = propType.toString();
|
||||
ss.put(new NodeProperty<>(propString, propString, NO_DESCR, map.get(propString)));
|
||||
|
@ -2,7 +2,6 @@ OpenIDE-Module-Name=DataModel
|
||||
AbstractContentChildren.CreateTSKNodeVisitor.exception.noNodeMsg=No Node defined for the given SleuthkitItem
|
||||
AbstractContentChildren.createAutopsyNodeVisitor.exception.noNodeMsg=No Node defined for the given DisplayableItem
|
||||
AbstractContentNode.exception.cannotChangeSysName.msg=Cannot change the system name.
|
||||
AbstractFsContentNode.noDesc.text=no description
|
||||
ArtifactStringContent.getStr.srcFilePath.text=Source File Path
|
||||
ArtifactStringContent.getStr.err=Error getting content
|
||||
ArtifactTypeNode.createSheet.artType.name=Artifact Type
|
||||
|
@ -71,6 +71,7 @@ import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.Account;
|
||||
import org.sleuthkit.datamodel.AccountFileInstance;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
@ -294,13 +295,13 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
@Override
|
||||
protected Node[] createNodes(String key) {
|
||||
try {
|
||||
Account.Type accountType = Account.Type.valueOf(key);
|
||||
switch (accountType) {
|
||||
case CREDIT_CARD:
|
||||
String accountType = key;
|
||||
if (accountType.equals(Account.Type.CREDIT_CARD.getTypeName())) {
|
||||
return new Node[]{new CreditCardNumberAccountTypeNode()};
|
||||
default:
|
||||
} else {
|
||||
return new Node[]{new DefaultAccountTypeNode(key)};
|
||||
}
|
||||
|
||||
} catch (IllegalArgumentException ex) {
|
||||
LOGGER.log(Level.WARNING, "Unknown account type: {0}", key);
|
||||
//Flesh out what happens with other account types here.
|
||||
@ -547,7 +548,7 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
+ " AND solr_attribute.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_DOCUMENT_ID.getTypeID() //NON-NLS
|
||||
+ " LEFT JOIN blackboard_attributes as account_type ON blackboard_artifacts.artifact_id = account_type.artifact_id " //NON-NLS
|
||||
+ " AND account_type.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS
|
||||
+ " AND account_type.value_text = '" + Account.Type.CREDIT_CARD.name() + "'" //NON-NLS
|
||||
+ " AND account_type.value_text = '" + Account.Type.CREDIT_CARD.getTypeName() + "'" //NON-NLS
|
||||
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
|
||||
+ getRejectedArtifactFilterClause()
|
||||
+ " GROUP BY blackboard_artifacts.obj_id, solr_document_id " //NON-NLS
|
||||
@ -607,7 +608,7 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
+ " AND solr_attribute.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_DOCUMENT_ID.getTypeID() //NON-NLS
|
||||
+ " LEFT JOIN blackboard_attributes as account_type ON blackboard_artifacts.artifact_id = account_type.artifact_id " //NON-NLS
|
||||
+ " AND account_type.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS
|
||||
+ " AND account_type.value_text = '" + Account.Type.CREDIT_CARD.name() + "'" //NON-NLS
|
||||
+ " AND account_type.value_text = '" + Account.Type.CREDIT_CARD.getTypeName() + "'" //NON-NLS
|
||||
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
|
||||
+ getRejectedArtifactFilterClause()
|
||||
+ " GROUP BY blackboard_artifacts.obj_id, solr_attribute.value_text ) AS foo";
|
||||
|
@ -990,7 +990,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
return;
|
||||
}
|
||||
|
||||
if (accountType.equals(Account.Type.CREDIT_CARD.name())) {
|
||||
if (accountType.equals(Account.Type.CREDIT_CARD.getTypeName())) {
|
||||
Node accountNode = accountRootChilds.findChild(Account.Type.CREDIT_CARD.getDisplayName());
|
||||
if (accountNode == null) {
|
||||
return;
|
||||
|
@ -170,21 +170,27 @@ class TableReportGenerator {
|
||||
return "unknown";
|
||||
}
|
||||
});
|
||||
for (String accountType : groupedArtifacts.keySet()) {
|
||||
for (String accountTypeStr : groupedArtifacts.keySet()) {
|
||||
/* If the report is a ReportHTML, the data type name
|
||||
* eventualy makes it to useDataTypeIcon which expects but
|
||||
* does not require a artifact name, so we make a synthetic
|
||||
* compund name by appending a ":" and the account type.
|
||||
*/
|
||||
String accountDisplayname = accountType;
|
||||
for (Account.Type acct : Account.Type.values()) {
|
||||
if (acct.equals(Account.Type.valueOf(accountType))) {
|
||||
accountDisplayname = acct.getDisplayName();
|
||||
break;
|
||||
String accountDisplayname = accountTypeStr;
|
||||
if (accountTypeStr != null) {
|
||||
try {
|
||||
Account.Type acctType = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().getAccountType(accountTypeStr);
|
||||
if (acctType != null) {
|
||||
accountDisplayname = acctType.getDisplayName();
|
||||
}
|
||||
}
|
||||
catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Unable to get display name for account type " + accountTypeStr, ex);
|
||||
}
|
||||
}
|
||||
|
||||
final String compundDataTypeName = BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getDisplayName() + ": " + accountDisplayname;
|
||||
writeTableForDataType(new ArrayList<>(groupedArtifacts.get(accountType)), type, compundDataTypeName, comment);
|
||||
writeTableForDataType(new ArrayList<>(groupedArtifacts.get(accountTypeStr)), type, compundDataTypeName, comment);
|
||||
}
|
||||
} else {
|
||||
//all other artifact types are sent to writeTableForDataType directly
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
<!-- for file search -->
|
||||
<dependency conf="autopsy_core->*" org="org.jbundle.thin.base.screen" name="jcalendarbutton" rev="1.4.6"/>
|
||||
<dependency conf="autopsy_core->*" org="com.github.lgooddatepicker" name="LGoodDatePicker" rev="10.3.1"/>
|
||||
|
||||
<!-- commmon -->
|
||||
<dependency org="com.google.guava" name="guava" rev="19.0"/>
|
||||
|
@ -53,6 +53,7 @@ file.reference.joda-time-2.4-javadoc.jar=release/modules/ext/joda-time-2.4-javad
|
||||
file.reference.joda-time-2.4-sources.jar=release/modules/ext/joda-time-2.4-sources.jar
|
||||
file.reference.joda-time-2.4.jar=release/modules/ext/joda-time-2.4.jar
|
||||
file.reference.jsr305-1.3.9.jar=release/modules/ext/jsr305-1.3.9.jar
|
||||
file.reference.LGoodDatePicker-10.3.1.jar=release/modules/ext/LGoodDatePicker-10.3.1.jar
|
||||
file.reference.log4j-1.2.17.jar=release/modules/ext/log4j-1.2.17.jar
|
||||
file.reference.logkit-1.0.1.jar=release/modules/ext/logkit-1.0.1.jar
|
||||
file.reference.mail-1.4.3.jar=release/modules/ext/mail-1.4.3.jar
|
||||
@ -82,6 +83,7 @@ javadoc.reference.guava-19.0.jar=release/modules/ext/guava-19.0-javadoc.jar
|
||||
javadoc.reference.jfxtras-common-8.0-r4.jar=release/modules/ext/jfxtras-common-8.0-r4-javadoc.jar
|
||||
javadoc.reference.jfxtras-controls-8.0-r4.jar=release/modules/ext/jfxtras-controls-8.0-r4-javadoc.jar
|
||||
javadoc.reference.jfxtras-fxml-8.0-r4.jar=release/modules/ext/jfxtras-fxml-8.0-r4-javadoc.jar
|
||||
javadoc.reference.LGoodDatePicker-10.3.1.jar=release/modules/ext/LGoodDatePicker-10.3.1-javadoc.jar
|
||||
nbm.needs.restart=true
|
||||
source.reference.commons-csv-1.4.jar=release/modules/ext/commons-csv-1.4-sources.jar
|
||||
source.reference.commons-io-2.5.jar=release/modules/ext/commons-io-2.5-sources.jar
|
||||
@ -91,3 +93,4 @@ source.reference.guava-19.0.jar=release/modules/ext/guava-19.0-sources.jar
|
||||
source.reference.jfxtras-common-8.0-r4.jar=release/modules/ext/jfxtras-common-8.0-r4-sources.jar
|
||||
source.reference.jfxtras-controls-8.0-r4.jar=release/modules/ext/jfxtras-controls-8.0-r4-sources.jar
|
||||
source.reference.jfxtras-fxml-8.0-r4.jar=release/modules/ext/jfxtras-fxml-8.0-r4-sources.jar
|
||||
source.reference.LGoodDatePicker-10.3.1.jar=release/modules/ext/LGoodDatePicker-10.3.1-sources.jar
|
||||
|
@ -37,6 +37,9 @@
|
||||
<package>com.apple.eawt</package>
|
||||
<package>com.apple.eawt.event</package>
|
||||
<package>com.apple.eio</package>
|
||||
<package>com.github.lgooddatepicker.components</package>
|
||||
<package>com.github.lgooddatepicker.optionalusertools</package>
|
||||
<package>com.github.lgooddatepicker.zinternaltools</package>
|
||||
<package>com.github.mustachejava</package>
|
||||
<package>com.github.mustachejava.codes</package>
|
||||
<package>com.github.mustachejava.functions</package>
|
||||
@ -679,6 +682,10 @@
|
||||
<runtime-relative-path>ext/compiler-0.9.1.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/compiler-0.9.1.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/LGoodDatePicker-10.3.1.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/LGoodDatePicker-10.3.1.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/imageio-iff-3.2.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/imageio-iff-3.2.jar</binary-origin>
|
||||
|
@ -1,7 +1,7 @@
|
||||
"""
|
||||
Autopsy Forensic Browser
|
||||
|
||||
Copyright 2016 Basis Technology Corp.
|
||||
Copyright 2016-17 Basis Technology Corp.
|
||||
Contact: carrier <at> sleuthkit <dot> org
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -44,10 +44,14 @@ from org.sleuthkit.datamodel import BlackboardAttribute
|
||||
from org.sleuthkit.datamodel.BlackboardAttribute import ATTRIBUTE_TYPE
|
||||
from org.sleuthkit.datamodel import Content
|
||||
from org.sleuthkit.datamodel import TskCoreException
|
||||
from org.sleuthkit.datamodel import Account
|
||||
from org.sleuthkit.datamodel import Relationship
|
||||
|
||||
import traceback
|
||||
import general
|
||||
|
||||
deviceAccountInstance = None
|
||||
|
||||
"""
|
||||
Locates a variety of different call log databases, parses them, and populates the blackboard.
|
||||
"""
|
||||
@ -82,6 +86,15 @@ class CallLogAnalyzer(general.AndroidComponentAnalyzer):
|
||||
|
||||
def analyze(self, dataSource, fileManager, context):
|
||||
try:
|
||||
|
||||
# Create a 'Device' account using the data source device id
|
||||
datasourceObjId = dataSource.getDataSource().getId()
|
||||
ds = Case.getCurrentCase().getSleuthkitCase().getDataSource(datasourceObjId)
|
||||
deviceID = ds.getDeviceId()
|
||||
|
||||
global deviceAccountInstance
|
||||
deviceAccountInstance = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.DEVICE, deviceID, general.MODULE_NAME, dataSource)
|
||||
|
||||
absFiles = fileManager.findFiles(dataSource, "logs.db")
|
||||
absFiles.addAll(fileManager.findFiles(dataSource, "contacts.db"))
|
||||
absFiles.addAll(fileManager.findFiles(dataSource, "contacts2.db"))
|
||||
@ -133,6 +146,13 @@ class CallLogAnalyzer(general.AndroidComponentAnalyzer):
|
||||
attributes.add(BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME, general.MODULE_NAME, name))
|
||||
|
||||
artifact.addAttributes(attributes)
|
||||
|
||||
# Create an account
|
||||
calllogAccountInstance = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.PHONE, number, general.MODULE_NAME, abstractFile);
|
||||
|
||||
# create relationship between accounts
|
||||
Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().addRelationships(deviceAccountInstance, [calllogAccountInstance], artifact, Relationship.Type.CALL_LOG, date);
|
||||
|
||||
bbartifacts.append(artifact)
|
||||
|
||||
try:
|
||||
|
@ -1,7 +1,7 @@
|
||||
"""
|
||||
Autopsy Forensic Browser
|
||||
|
||||
Copyright 2016 Basis Technology Corp.
|
||||
Copyright 2016-17 Basis Technology Corp.
|
||||
Contact: carrier <at> sleuthkit <dot> org
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -42,10 +42,14 @@ from org.sleuthkit.datamodel import BlackboardArtifact
|
||||
from org.sleuthkit.datamodel import BlackboardAttribute
|
||||
from org.sleuthkit.datamodel import Content
|
||||
from org.sleuthkit.datamodel import TskCoreException
|
||||
from org.sleuthkit.datamodel import Account
|
||||
from org.sleuthkit.datamodel import Relationship
|
||||
|
||||
import traceback
|
||||
import general
|
||||
|
||||
deviceAccountInstance = None
|
||||
|
||||
"""
|
||||
Locates a variety of different contacts databases, parses them, and populates the blackboard.
|
||||
"""
|
||||
@ -56,6 +60,15 @@ class ContactAnalyzer(general.AndroidComponentAnalyzer):
|
||||
|
||||
def analyze(self, dataSource, fileManager, context):
|
||||
try:
|
||||
|
||||
# Create a 'Device' account using the data source device id
|
||||
datasourceObjId = dataSource.getDataSource().getId()
|
||||
ds = Case.getCurrentCase().getSleuthkitCase().getDataSource(datasourceObjId)
|
||||
deviceID = ds.getDeviceId()
|
||||
|
||||
global deviceAccountInstance
|
||||
deviceAccountInstance = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().createAccountFileInstance (Account.Type.DEVICE, deviceID, general.MODULE_NAME, dataSource)
|
||||
|
||||
absFiles = fileManager.findFiles(dataSource, "contacts.db")
|
||||
absFiles.addAll(fileManager.findFiles(dataSource, "contacts2.db"))
|
||||
if absFiles.isEmpty():
|
||||
@ -131,12 +144,21 @@ class ContactAnalyzer(general.AndroidComponentAnalyzer):
|
||||
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME, general.MODULE_NAME, name))
|
||||
if mimetype == "vnd.android.cursor.item/phone_v2":
|
||||
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER, general.MODULE_NAME, data1))
|
||||
acctType = Account.Type.PHONE
|
||||
else:
|
||||
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL, general.MODULE_NAME, data1))
|
||||
acctType = Account.Type.EMAIL
|
||||
|
||||
artifact.addAttributes(attributes)
|
||||
|
||||
# Create an account instance
|
||||
contactAccountInstance = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().createAccountFileInstance (acctType, data1, general.MODULE_NAME, abstractFile);
|
||||
|
||||
# create relationship between accounts
|
||||
Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().addRelationships(deviceAccountInstance, [contactAccountInstance], artifact,Relationship.Type.CONTACT, 0);
|
||||
|
||||
oldName = name
|
||||
|
||||
artifact.addAttributes(attributes)
|
||||
bbartifacts.append(artifact)
|
||||
|
||||
try:
|
||||
|
@ -42,10 +42,13 @@ from org.sleuthkit.datamodel import BlackboardArtifact
|
||||
from org.sleuthkit.datamodel import BlackboardAttribute
|
||||
from org.sleuthkit.datamodel import Content
|
||||
from org.sleuthkit.datamodel import TskCoreException
|
||||
from org.sleuthkit.datamodel import Account
|
||||
|
||||
import traceback
|
||||
import general
|
||||
|
||||
deviceAccountInstance = None
|
||||
|
||||
"""
|
||||
Locates database for the Tango app and adds info to blackboard.
|
||||
"""
|
||||
@ -56,6 +59,14 @@ class TangoMessageAnalyzer(general.AndroidComponentAnalyzer):
|
||||
|
||||
def analyze(self, dataSource, fileManager, context):
|
||||
try:
|
||||
# Create a 'Device' account using the data source device id
|
||||
datasourceObjId = dataSource.getDataSource().getId()
|
||||
ds = Case.getCurrentCase().getSleuthkitCase().getDataSource(datasourceObjId)
|
||||
deviceID = ds.getDeviceId()
|
||||
|
||||
global deviceAccountInstance
|
||||
deviceAccountInstance = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.DEVICE, deviceID, general.MODULE_NAME, dataSource)
|
||||
|
||||
absFiles = fileManager.findFiles(dataSource, "tc.db")
|
||||
for abstractFile in absFiles:
|
||||
try:
|
||||
|
@ -1,7 +1,7 @@
|
||||
"""
|
||||
Autopsy Forensic Browser
|
||||
|
||||
Copyright 2016 Basis Technology Corp.
|
||||
Copyright 2016-17 Basis Technology Corp.
|
||||
Contact: carrier <at> sleuthkit <dot> org
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -43,10 +43,14 @@ from org.sleuthkit.datamodel import BlackboardArtifact
|
||||
from org.sleuthkit.datamodel import BlackboardAttribute
|
||||
from org.sleuthkit.datamodel import Content
|
||||
from org.sleuthkit.datamodel import TskCoreException
|
||||
from org.sleuthkit.datamodel import Account
|
||||
from org.sleuthkit.datamodel import Relationship
|
||||
|
||||
import traceback
|
||||
import general
|
||||
|
||||
deviceAccountInstance = None
|
||||
|
||||
"""
|
||||
Finds database with SMS/MMS messages and adds them to blackboard.
|
||||
"""
|
||||
@ -57,6 +61,15 @@ class TextMessageAnalyzer(general.AndroidComponentAnalyzer):
|
||||
|
||||
def analyze(self, dataSource, fileManager, context):
|
||||
try:
|
||||
|
||||
# Create a 'Device' account using the data source device id
|
||||
datasourceObjId = dataSource.getDataSource().getId()
|
||||
ds = Case.getCurrentCase().getSleuthkitCase().getDataSource(datasourceObjId)
|
||||
deviceID = ds.getDeviceId()
|
||||
|
||||
global deviceAccountInstance
|
||||
deviceAccountInstance = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.DEVICE, deviceID, general.MODULE_NAME, dataSource)
|
||||
|
||||
absFiles = fileManager.findFiles(dataSource, "mmssms.db")
|
||||
for abstractFile in absFiles:
|
||||
try:
|
||||
@ -108,6 +121,13 @@ class TextMessageAnalyzer(general.AndroidComponentAnalyzer):
|
||||
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MESSAGE_TYPE, general.MODULE_NAME, "SMS Message"))
|
||||
|
||||
artifact.addAttributes(attributes)
|
||||
|
||||
# Create an account
|
||||
msgAccountInstance = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.PHONE, address, general.MODULE_NAME, abstractFile);
|
||||
|
||||
# create relationship between accounts
|
||||
Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().addRelationships(deviceAccountInstance, [msgAccountInstance], artifact,Relationship.Type.MESSAGE, date);
|
||||
|
||||
bbartifacts.append(artifact)
|
||||
try:
|
||||
# index the artifact for keyword search
|
||||
|
@ -1,7 +1,7 @@
|
||||
"""
|
||||
Autopsy Forensic Browser
|
||||
|
||||
Copyright 2016 Basis Technology Corp.
|
||||
Copyright 2016-17 Basis Technology Corp.
|
||||
Contact: carrier <at> sleuthkit <dot> org
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -39,10 +39,15 @@ from org.sleuthkit.datamodel import BlackboardArtifact
|
||||
from org.sleuthkit.datamodel import BlackboardAttribute
|
||||
from org.sleuthkit.datamodel import Content
|
||||
from org.sleuthkit.datamodel import TskCoreException
|
||||
from org.sleuthkit.datamodel import Account
|
||||
from org.sleuthkit.datamodel import Relationship
|
||||
|
||||
import traceback
|
||||
import general
|
||||
|
||||
wwfAccountType = None
|
||||
deviceAccountInstance = None
|
||||
|
||||
"""
|
||||
Analyzes messages from Words With Friends
|
||||
"""
|
||||
@ -53,6 +58,18 @@ class WWFMessageAnalyzer(general.AndroidComponentAnalyzer):
|
||||
|
||||
def analyze(self, dataSource, fileManager, context):
|
||||
try:
|
||||
|
||||
global wwfAccountType
|
||||
wwfAccountType = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().addAccountType("WWF", "Words with Friends")
|
||||
|
||||
# Create a 'Device' account using the data source device id
|
||||
datasourceObjId = dataSource.getDataSource().getId()
|
||||
ds = Case.getCurrentCase().getSleuthkitCase().getDataSource(datasourceObjId)
|
||||
deviceID = ds.getDeviceId()
|
||||
|
||||
global deviceAccountInstance
|
||||
deviceAccountInstance = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.DEVICE, deviceID, general.MODULE_NAME, dataSource)
|
||||
|
||||
absFiles = fileManager.findFiles(dataSource, "WordsFramework")
|
||||
for abstractFile in absFiles:
|
||||
try:
|
||||
@ -98,6 +115,13 @@ class WWFMessageAnalyzer(general.AndroidComponentAnalyzer):
|
||||
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MESSAGE_TYPE, general.MODULE_NAME, "Words With Friends Message"))
|
||||
|
||||
artifact.addAttributes(attributes)
|
||||
|
||||
# Create an account
|
||||
wwfAccountInstance = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(wwfAccountType, user_id, general.MODULE_NAME, abstractFile);
|
||||
|
||||
# create relationship between accounts
|
||||
Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().addRelationships(deviceAccountInstance, [wwfAccountInstance], artifact,Relationship.Type.MESSAGE, created_at);
|
||||
|
||||
try:
|
||||
# index the artifact for keyword search
|
||||
blackboard = Case.getCurrentCase().getServices().getBlackboard()
|
||||
|
@ -173,7 +173,14 @@ public class ExtractedContentViewer implements DataContentViewer {
|
||||
}
|
||||
}
|
||||
panel.updateControls(currentSource);
|
||||
setPanel(content.getName(), sources);
|
||||
|
||||
String contentName = "";
|
||||
if (content != null) {
|
||||
contentName = content.getName();
|
||||
}
|
||||
setPanel(contentName, sources);
|
||||
|
||||
|
||||
}
|
||||
|
||||
static private IndexedText getRawArtifactText(Lookup nodeLookup) throws TskCoreException {
|
||||
|
@ -222,9 +222,6 @@ class QueryResults {
|
||||
* Post an artifact for the hit to the blackboard.
|
||||
*/
|
||||
BlackboardArtifact artifact = query.postKeywordHitToBlackboard(content, keyword, hit, snippet, query.getKeywordList().getName());
|
||||
if (null == artifact) {
|
||||
logger.log(Level.SEVERE, "Error posting keyword hit artifact for keyword {0} in {1} to the blackboard", new Object[]{keyword.toString(), content}); //NON-NLS
|
||||
}
|
||||
|
||||
/*
|
||||
* Send an ingest inbox message for the hit.
|
||||
|
@ -36,7 +36,9 @@ import org.apache.solr.client.solrj.response.QueryResponse;
|
||||
import org.apache.solr.common.SolrDocument;
|
||||
import org.apache.solr.common.SolrDocumentList;
|
||||
import org.apache.solr.common.params.CursorMarkParams;
|
||||
import org.openide.util.Exceptions;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
||||
import org.sleuthkit.autopsy.datamodel.CreditCards;
|
||||
@ -46,6 +48,7 @@ import static org.sleuthkit.autopsy.keywordsearch.TermsComponentQuery.CREDIT_CAR
|
||||
import static org.sleuthkit.autopsy.keywordsearch.TermsComponentQuery.KEYWORD_SEARCH_DOCUMENT_ID;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.Account;
|
||||
import org.sleuthkit.datamodel.AccountFileInstance;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
@ -444,28 +447,67 @@ final class RegexQuery implements KeywordSearchQuery {
|
||||
}
|
||||
|
||||
/*
|
||||
* Create either a "plain vanilla" keyword hit artifact with keyword and
|
||||
* regex attributes, or a credit card account artifact with attributes
|
||||
* parsed from from the snippet for the hit and looked up based on the
|
||||
* parsed bank identifcation number.
|
||||
* Credit Card number hits are handled differently
|
||||
*/
|
||||
if (originalKeyword.getArtifactAttributeType() == ATTRIBUTE_TYPE.TSK_CARD_NUMBER) {
|
||||
createCCNAccount(content, foundKeyword, hit, snippet, listName);
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a "plain vanilla" keyword hit artifact with keyword and
|
||||
* regex attributes
|
||||
*/
|
||||
BlackboardArtifact newArtifact;
|
||||
Collection<BlackboardAttribute> attributes = new ArrayList<>();
|
||||
if (originalKeyword.getArtifactAttributeType() != ATTRIBUTE_TYPE.TSK_CARD_NUMBER) {
|
||||
|
||||
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD, MODULE_NAME, foundKeyword.getSearchTerm()));
|
||||
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP, MODULE_NAME, getQueryString()));
|
||||
|
||||
try {
|
||||
newArtifact = content.newArtifact(ARTIFACT_TYPE.TSK_KEYWORD_HIT);
|
||||
} catch (TskCoreException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Error adding artifact for keyword hit to blackboard", ex); //NON-NLS
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
|
||||
if (StringUtils.isNotBlank(listName)) {
|
||||
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, listName));
|
||||
}
|
||||
if (snippet != null) {
|
||||
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW, MODULE_NAME, snippet));
|
||||
}
|
||||
|
||||
hit.getArtifactID().ifPresent(artifactID
|
||||
-> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, artifactID))
|
||||
);
|
||||
|
||||
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE, MODULE_NAME, KeywordSearch.QueryType.REGEX.ordinal()));
|
||||
|
||||
try {
|
||||
newArtifact.addAttributes(attributes);
|
||||
return newArtifact;
|
||||
} catch (TskCoreException e) {
|
||||
LOGGER.log(Level.SEVERE, "Error adding bb attributes for terms search artifact", e); //NON-NLS
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void createCCNAccount(Content content, Keyword foundKeyword, KeywordHit hit, String snippet, String listName) {
|
||||
|
||||
final String MODULE_NAME = KeywordSearchModuleFactory.getModuleName();
|
||||
|
||||
if (originalKeyword.getArtifactAttributeType() != ATTRIBUTE_TYPE.TSK_CARD_NUMBER) {
|
||||
LOGGER.log(Level.SEVERE, "Keyword hit is not a credit card number"); //NON-NLS
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* Parse the credit card account attributes from the snippet for the
|
||||
* hit.
|
||||
* Create a credit card account with attributes
|
||||
* parsed from the snippet for the hit and looked up based on the
|
||||
* parsed bank identifcation number.
|
||||
*/
|
||||
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE, MODULE_NAME, Account.Type.CREDIT_CARD.name()));
|
||||
Collection<BlackboardAttribute> attributes = new ArrayList<>();
|
||||
|
||||
Map<BlackboardAttribute.Type, BlackboardAttribute> parsedTrackAttributeMap = new HashMap<>();
|
||||
Matcher matcher = TermsComponentQuery.CREDIT_CARD_TRACK1_PATTERN.matcher(hit.getSnippet());
|
||||
if (matcher.find()) {
|
||||
@ -477,12 +519,18 @@ final class RegexQuery implements KeywordSearchQuery {
|
||||
}
|
||||
final BlackboardAttribute ccnAttribute = parsedTrackAttributeMap.get(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_CARD_NUMBER));
|
||||
if (ccnAttribute == null || StringUtils.isBlank(ccnAttribute.getValueString())) {
|
||||
|
||||
if (hit.isArtifactHit()) {
|
||||
LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for artifact keyword hit: term = %s, snippet = '%s', artifact id = %d", foundKeyword.getSearchTerm(), hit.getSnippet(), hit.getArtifactID().get())); //NON-NLS
|
||||
} else {
|
||||
LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for content keyword hit: term = %s, snippet = '%s', object id = %d", foundKeyword.getSearchTerm(), hit.getSnippet(), hit.getSolrObjectId())); //NON-NLS
|
||||
try {
|
||||
LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for content keyword hit: term = %s, snippet = '%s', object id = %d", foundKeyword.getSearchTerm(), hit.getSnippet(), hit.getContentID())); //NON-NLS
|
||||
} catch (TskCoreException ex) {
|
||||
LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for content keyword hit: term = %s, snippet = '%s' ", foundKeyword.getSearchTerm(), hit.getSnippet())); //NON-NLS
|
||||
LOGGER.log(Level.SEVERE, "There was a error getting contentID for keyword hit.", ex); //NON-NLS
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return;
|
||||
}
|
||||
attributes.addAll(parsedTrackAttributeMap.values());
|
||||
|
||||
@ -524,17 +572,6 @@ final class RegexQuery implements KeywordSearchQuery {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Create an account artifact.
|
||||
*/
|
||||
try {
|
||||
newArtifact = content.newArtifact(ARTIFACT_TYPE.TSK_ACCOUNT);
|
||||
} catch (TskCoreException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Error adding artifact for account to blackboard", ex); //NON-NLS
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(listName)) {
|
||||
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, listName));
|
||||
}
|
||||
@ -548,15 +585,21 @@ final class RegexQuery implements KeywordSearchQuery {
|
||||
|
||||
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE, MODULE_NAME, KeywordSearch.QueryType.REGEX.ordinal()));
|
||||
|
||||
|
||||
/*
|
||||
* Create an account instance.
|
||||
*/
|
||||
try {
|
||||
newArtifact.addAttributes(attributes);
|
||||
return newArtifact;
|
||||
} catch (TskCoreException e) {
|
||||
LOGGER.log(Level.SEVERE, "Error adding bb attributes for terms search artifact", e); //NON-NLS
|
||||
return null;
|
||||
}
|
||||
AccountFileInstance ccAccountInstance = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.CREDIT_CARD, ccnAttribute.getValueString() , MODULE_NAME, content);
|
||||
|
||||
ccAccountInstance.addAttributes(attributes);
|
||||
|
||||
} catch (TskCoreException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Error creating CCN account instance", ex); //NON-NLS
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* Parses the track 2 data from the snippet for a credit card account number
|
||||
* hit and turns them into artifact attributes.
|
||||
|
@ -32,11 +32,14 @@ import java.util.regex.Pattern;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.solr.client.solrj.SolrQuery;
|
||||
import org.apache.solr.client.solrj.response.TermsResponse.Term;
|
||||
import org.openide.util.Exceptions;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.Version;
|
||||
import org.sleuthkit.autopsy.datamodel.CreditCards;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.Account;
|
||||
import org.sleuthkit.datamodel.AccountFileInstance;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
@ -342,15 +345,22 @@ final class TermsComponentQuery implements KeywordSearchQuery {
|
||||
*/
|
||||
@Override
|
||||
public BlackboardArtifact postKeywordHitToBlackboard(Content content, Keyword foundKeyword, KeywordHit hit, String snippet, String listName) {
|
||||
|
||||
/*
|
||||
* Create either a "plain vanilla" keyword hit artifact with keyword and
|
||||
* regex attributes, or a credit card account artifact with attributes
|
||||
* parsed from from the snippet for the hit and looked up based on the
|
||||
* parsed bank identifcation number.
|
||||
* CCN hits are handled specially
|
||||
*/
|
||||
if (originalKeyword.getArtifactAttributeType() == ATTRIBUTE_TYPE.TSK_CARD_NUMBER) {
|
||||
createCCNAccount(content, hit, snippet, listName);
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a "plain vanilla" keyword hit artifact with keyword and regex
|
||||
* attributes,
|
||||
*/
|
||||
BlackboardArtifact newArtifact;
|
||||
Collection<BlackboardAttribute> attributes = new ArrayList<>();
|
||||
if (originalKeyword.getArtifactAttributeType() != ATTRIBUTE_TYPE.TSK_CARD_NUMBER) {
|
||||
|
||||
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD, MODULE_NAME, foundKeyword.getSearchTerm()));
|
||||
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_REGEXP, MODULE_NAME, originalKeyword.getSearchTerm()));
|
||||
|
||||
@ -361,12 +371,44 @@ final class TermsComponentQuery implements KeywordSearchQuery {
|
||||
LOGGER.log(Level.SEVERE, "Error adding artifact for keyword hit to blackboard", ex); //NON-NLS
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
|
||||
if (StringUtils.isNotBlank(listName)) {
|
||||
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, listName));
|
||||
}
|
||||
if (snippet != null) {
|
||||
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW, MODULE_NAME, snippet));
|
||||
}
|
||||
|
||||
hit.getArtifactID().ifPresent(
|
||||
artifactID -> attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, artifactID))
|
||||
);
|
||||
|
||||
// TermsComponentQuery is now being used exclusively for substring searches.
|
||||
attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE, MODULE_NAME, KeywordSearch.QueryType.SUBSTRING.ordinal()));
|
||||
|
||||
try {
|
||||
newArtifact.addAttributes(attributes);
|
||||
return newArtifact;
|
||||
} catch (TskCoreException e) {
|
||||
LOGGER.log(Level.SEVERE, "Error adding bb attributes for terms search artifact", e); //NON-NLS
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void createCCNAccount(Content content, KeywordHit hit, String snippet, String listName) {
|
||||
|
||||
if (originalKeyword.getArtifactAttributeType() != ATTRIBUTE_TYPE.TSK_CARD_NUMBER) {
|
||||
LOGGER.log(Level.SEVERE, "Keyword hit is not a credit card number"); //NON-NLS
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the credit card account attributes from the snippet for the
|
||||
* hit.
|
||||
* Create a credit card account with attributes parsed from from the
|
||||
* snippet for the hit and looked up based on the parsed bank
|
||||
* identifcation number.
|
||||
*/
|
||||
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE, MODULE_NAME, Account.Type.CREDIT_CARD.name()));
|
||||
Collection<BlackboardAttribute> attributes = new ArrayList<>();
|
||||
|
||||
Map<BlackboardAttribute.Type, BlackboardAttribute> parsedTrackAttributeMap = new HashMap<>();
|
||||
Matcher matcher = CREDIT_CARD_TRACK1_PATTERN.matcher(hit.getSnippet());
|
||||
if (matcher.find()) {
|
||||
@ -381,9 +423,19 @@ final class TermsComponentQuery implements KeywordSearchQuery {
|
||||
if (hit.isArtifactHit()) {
|
||||
LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for artifact keyword hit: term = %s, snippet = '%s', artifact id = %d", searchTerm, hit.getSnippet(), hit.getArtifactID().get())); //NON-NLS
|
||||
} else {
|
||||
LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for content keyword hit: term = %s, snippet = '%s', object id = %d", searchTerm, hit.getSnippet(), hit.getSolrObjectId())); //NON-NLS
|
||||
long contentId = 0;
|
||||
try {
|
||||
contentId = hit.getContentID();
|
||||
} catch (TskCoreException ex) {
|
||||
LOGGER.log(Level.SEVERE, String.format("Failed to content id from keyword hit: term = %s, snippet = '%s'", searchTerm, hit.getSnippet()), ex); //NON-NLS
|
||||
}
|
||||
return null;
|
||||
if (contentId > 0) {
|
||||
LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for content keyword hit: term = %s, snippet = '%s', object id = %d", searchTerm, hit.getSnippet(), contentId)); //NON-NLS
|
||||
} else {
|
||||
LOGGER.log(Level.SEVERE, String.format("Failed to parse credit card account number for content keyword hit: term = %s, snippet = '%s'", searchTerm, hit.getSnippet())); //NON-NLS
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
attributes.addAll(parsedTrackAttributeMap.values());
|
||||
|
||||
@ -414,8 +466,7 @@ final class TermsComponentQuery implements KeywordSearchQuery {
|
||||
|
||||
/*
|
||||
* If the hit is from unused or unallocated space, record the Solr
|
||||
* document id to support showing just the chunk that contained the
|
||||
* hit.
|
||||
* document id to support showing just the chunk that contained the hit.
|
||||
*/
|
||||
if (content instanceof AbstractFile) {
|
||||
AbstractFile file = (AbstractFile) content;
|
||||
@ -425,17 +476,6 @@ final class TermsComponentQuery implements KeywordSearchQuery {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Create an account artifact.
|
||||
*/
|
||||
try {
|
||||
newArtifact = content.newArtifact(ARTIFACT_TYPE.TSK_ACCOUNT);
|
||||
} catch (TskCoreException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Error adding artifact for account to blackboard", ex); //NON-NLS
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(listName)) {
|
||||
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, listName));
|
||||
}
|
||||
@ -450,13 +490,17 @@ final class TermsComponentQuery implements KeywordSearchQuery {
|
||||
// TermsComponentQuery is now being used exclusively for substring searches.
|
||||
attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE, MODULE_NAME, KeywordSearch.QueryType.SUBSTRING.ordinal()));
|
||||
|
||||
/*
|
||||
* Create an account.
|
||||
*/
|
||||
try {
|
||||
newArtifact.addAttributes(attributes);
|
||||
return newArtifact;
|
||||
} catch (TskCoreException e) {
|
||||
LOGGER.log(Level.SEVERE, "Error adding bb attributes for terms search artifact", e); //NON-NLS
|
||||
return null;
|
||||
AccountFileInstance ccAccountInstance = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.CREDIT_CARD, ccnAttribute.getValueString(), MODULE_NAME, content);
|
||||
ccAccountInstance.addAttributes(attributes);
|
||||
//newArtifact = Case.getCurrentCase().getSleuthkitCase().getBlackboardArtifact(ccAccountInstance.getArtifactId());
|
||||
} catch (TskCoreException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Error creating CCN account instance", ex); //NON-NLS
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,5 +1,5 @@
|
||||
#Updated by build script
|
||||
#Wed, 08 Nov 2017 17:45:11 -0500
|
||||
#Wed, 06 Dec 2017 09:53:33 -0500
|
||||
LBL_splash_window_title=Starting Autopsy
|
||||
SPLASH_HEIGHT=314
|
||||
SPLASH_WIDTH=538
|
||||
|
@ -1,4 +1,4 @@
|
||||
#Updated by build script
|
||||
#Wed, 08 Nov 2017 17:45:11 -0500
|
||||
#Wed, 06 Dec 2017 09:53:33 -0500
|
||||
CTL_MainWindow_Title=Autopsy 4.5.0
|
||||
CTL_MainWindow_Title_No_Project=Autopsy 4.5.0
|
||||
|
@ -21,8 +21,13 @@ package org.sleuthkit.autopsy.thunderbirdparser;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
@ -40,12 +45,16 @@ import org.sleuthkit.autopsy.ingest.IngestServices;
|
||||
import org.sleuthkit.autopsy.ingest.ModuleContentEvent;
|
||||
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.Account;
|
||||
import org.sleuthkit.datamodel.AccountFileInstance;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
|
||||
import org.sleuthkit.datamodel.DerivedFile;
|
||||
import org.sleuthkit.datamodel.Relationship;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
import org.sleuthkit.datamodel.TskDataException;
|
||||
import org.sleuthkit.datamodel.TskException;
|
||||
|
||||
/**
|
||||
@ -354,6 +363,24 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
return files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds and returns a set of unique email addresses found in the input string
|
||||
*
|
||||
* @param input - input string, like the To/CC line from an email header
|
||||
*
|
||||
* @param Set<String>: set of email addresses found in the input string
|
||||
*/
|
||||
private Set<String> findEmailAddresess(String input) {
|
||||
Pattern p = Pattern.compile("\\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}\\b",
|
||||
Pattern.CASE_INSENSITIVE);
|
||||
Matcher m = p.matcher(input);
|
||||
Set<String> emailAddresses = new HashSet<String>();
|
||||
while (m.find()) {
|
||||
emailAddresses.add( m.group());
|
||||
}
|
||||
return emailAddresses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a blackboard artifact for the given email message.
|
||||
*
|
||||
@ -377,55 +404,71 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
long id = email.getId();
|
||||
String localPath = email.getLocalPath();
|
||||
|
||||
if (headers.isEmpty() == false) {
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_HEADERS, EmailParserModuleFactory.getModuleName(), headers));
|
||||
List<String> senderAddressList = new ArrayList<>();
|
||||
String senderAddress;
|
||||
senderAddressList.addAll(findEmailAddresess(from));
|
||||
|
||||
AccountFileInstance senderAccountInstance = null;
|
||||
if (senderAddressList.size() == 1) {
|
||||
senderAddress = senderAddressList.get(0);
|
||||
try {
|
||||
senderAccountInstance = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.EMAIL, senderAddress, EmailParserModuleFactory.getModuleName(), abstractFile);
|
||||
}
|
||||
if (from.isEmpty() == false) {
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_EMAIL_FROM, EmailParserModuleFactory.getModuleName(), from));
|
||||
catch(TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "Failed to create account for email address " + senderAddress, ex); //NON-NLS
|
||||
}
|
||||
if (to.isEmpty() == false) {
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_EMAIL_TO, EmailParserModuleFactory.getModuleName(), to));
|
||||
}
|
||||
if (subject.isEmpty() == false) {
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SUBJECT, EmailParserModuleFactory.getModuleName(), subject));
|
||||
else {
|
||||
logger.log(Level.WARNING, "Failed to find sender address, from = "+ from); //NON-NLS
|
||||
}
|
||||
|
||||
if (dateL > 0) {
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_RCVD, EmailParserModuleFactory.getModuleName(), dateL));
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_SENT, EmailParserModuleFactory.getModuleName(), dateL));
|
||||
}
|
||||
if (body.isEmpty() == false) {
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_PLAIN, EmailParserModuleFactory.getModuleName(), body));
|
||||
}
|
||||
List<String> recipientAddresses = new ArrayList<>();
|
||||
recipientAddresses.addAll(findEmailAddresess(to));
|
||||
recipientAddresses.addAll(findEmailAddresess(cc));
|
||||
recipientAddresses.addAll(findEmailAddresess(bcc));
|
||||
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_MSG_ID, EmailParserModuleFactory.getModuleName(), ((id < 0L) ? NbBundle
|
||||
.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.notAvail") : String.valueOf(id))));
|
||||
List<AccountFileInstance> recipientAccountInstances = new ArrayList<>();
|
||||
recipientAddresses.forEach((addr) -> {
|
||||
try {
|
||||
AccountFileInstance recipientAccountInstance =
|
||||
Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.EMAIL, addr,
|
||||
EmailParserModuleFactory.getModuleName(), abstractFile);
|
||||
recipientAccountInstances.add(recipientAccountInstance);
|
||||
}
|
||||
catch(TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "Failed to create account for email address " + addr, ex); //NON-NLS
|
||||
}
|
||||
});
|
||||
|
||||
if (localPath.isEmpty() == false) {
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH, EmailParserModuleFactory.getModuleName(), localPath));
|
||||
} else {
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH, EmailParserModuleFactory.getModuleName(), "/foo/bar")); //NON-NLS
|
||||
}
|
||||
addEmailAttribute(headers, ATTRIBUTE_TYPE.TSK_HEADERS, bbattributes);
|
||||
addEmailAttribute(from, ATTRIBUTE_TYPE.TSK_EMAIL_FROM, bbattributes);
|
||||
addEmailAttribute(to, ATTRIBUTE_TYPE.TSK_EMAIL_TO, bbattributes);
|
||||
addEmailAttribute(subject, ATTRIBUTE_TYPE.TSK_SUBJECT, bbattributes);
|
||||
|
||||
if (cc.isEmpty() == false) {
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_EMAIL_CC, EmailParserModuleFactory.getModuleName(), cc));
|
||||
}
|
||||
if (bcc.isEmpty() == false) {
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_EMAIL_BCC, EmailParserModuleFactory.getModuleName(), bcc));
|
||||
}
|
||||
addEmailAttribute(dateL, ATTRIBUTE_TYPE.TSK_DATETIME_RCVD, bbattributes);
|
||||
addEmailAttribute(dateL, ATTRIBUTE_TYPE.TSK_DATETIME_SENT, bbattributes);
|
||||
|
||||
addEmailAttribute(body, ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_PLAIN, bbattributes);
|
||||
|
||||
addEmailAttribute(((id < 0L) ? NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.notAvail") : String.valueOf(id)),
|
||||
ATTRIBUTE_TYPE.TSK_MSG_ID, bbattributes);
|
||||
|
||||
addEmailAttribute(((localPath.isEmpty() == false) ? localPath : "/foo/bar"),
|
||||
ATTRIBUTE_TYPE.TSK_PATH, bbattributes);
|
||||
|
||||
addEmailAttribute(cc, ATTRIBUTE_TYPE.TSK_EMAIL_CC, bbattributes);
|
||||
addEmailAttribute(bodyHTML, ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_HTML, bbattributes);
|
||||
addEmailAttribute(rtf, ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_RTF, bbattributes);
|
||||
|
||||
if (bodyHTML.isEmpty() == false) {
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_HTML, EmailParserModuleFactory.getModuleName(), bodyHTML));
|
||||
}
|
||||
if (rtf.isEmpty() == false) {
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_RTF, EmailParserModuleFactory.getModuleName(), rtf));
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
bbart = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG);
|
||||
bbart.addAttributes(bbattributes);
|
||||
|
||||
// Add account relationships
|
||||
Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().addRelationships(senderAccountInstance, recipientAccountInstances, bbart,Relationship.Type.MESSAGE, dateL);
|
||||
|
||||
try {
|
||||
// index the artifact for keyword search
|
||||
blackboard.indexArtifact(bbart);
|
||||
@ -433,13 +476,24 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bbart.getArtifactID(), ex); //NON-NLS
|
||||
MessageNotifyUtil.Notify.error(Bundle.ThunderbirdMboxFileIngestModule_addArtifact_indexError_message(), bbart.getDisplayName());
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
} catch (TskCoreException | TskDataException ex) {
|
||||
logger.log(Level.WARNING, null, ex);
|
||||
}
|
||||
|
||||
return bbart;
|
||||
}
|
||||
|
||||
private void addEmailAttribute(String stringVal, ATTRIBUTE_TYPE attrType, Collection<BlackboardAttribute> bbattributes) {
|
||||
if (stringVal.isEmpty() == false) {
|
||||
bbattributes.add(new BlackboardAttribute(attrType, EmailParserModuleFactory.getModuleName(), stringVal));
|
||||
}
|
||||
}
|
||||
private void addEmailAttribute(long longVal, ATTRIBUTE_TYPE attrType, Collection<BlackboardAttribute> bbattributes) {
|
||||
if (longVal > 0) {
|
||||
bbattributes.add(new BlackboardAttribute(attrType, EmailParserModuleFactory.getModuleName(), longVal));
|
||||
}
|
||||
}
|
||||
|
||||
void postErrorMessage(String subj, String details) {
|
||||
IngestMessage ingestMessage = IngestMessage.createErrorMessage(EmailParserModuleFactory.getModuleVersion(), subj, details);
|
||||
services.postMessage(ingestMessage);
|
||||
@ -451,5 +505,6 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
|
||||
|
||||
@Override
|
||||
public void shutDown() {
|
||||
// nothing to shut down
|
||||
}
|
||||
}
|
||||
|