mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-12 16:06:15 +00:00
64565: Reorganize call log artifact viewer
Interim commit.
This commit is contained in:
parent
5550a37293
commit
77a0563ac0
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2020 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 javax.swing.JButton;
|
||||
import javax.swing.JLabel;
|
||||
|
||||
/**
|
||||
* A data bag for the persona searching thread. It wraps the account id to
|
||||
* search for, and the UI label and button to update once the search completes.
|
||||
*/
|
||||
class AccountPersonaSearcherData {
|
||||
|
||||
// Account identifier to search personas for.
|
||||
private final String accountIdentifer;
|
||||
// Persona name label to be updated when the search is complete.
|
||||
private final JLabel personaNameLabel;
|
||||
// Persona action button to be updated when the search is complete
|
||||
private final JButton personaActionButton;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param accountIdentifer Account identifier.
|
||||
* @param personaNameLabel Persona name label.
|
||||
* @param personaActionButton Persona button.
|
||||
*/
|
||||
AccountPersonaSearcherData(String accountIdentifer, JLabel personaNameLabel, JButton personaActionButton) {
|
||||
this.accountIdentifer = accountIdentifer;
|
||||
this.personaNameLabel = personaNameLabel;
|
||||
this.personaActionButton = personaActionButton;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the account identifier.
|
||||
*
|
||||
* @return Account identifier.
|
||||
*/
|
||||
public String getAccountIdentifer() {
|
||||
return accountIdentifer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the persona name label.
|
||||
*
|
||||
* @return Persona name label.
|
||||
*/
|
||||
public JLabel getPersonaNameLabel() {
|
||||
return personaNameLabel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the persona button.
|
||||
*
|
||||
* @return Persona button.
|
||||
*/
|
||||
public JButton getPersonaActionButton() {
|
||||
return personaActionButton;
|
||||
}
|
||||
}
|
@ -68,7 +68,7 @@ import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsMode;
|
||||
*
|
||||
*
|
||||
*/
|
||||
@ServiceProvider(service = ArtifactContentViewer.class)
|
||||
//@ServiceProvider(service = ArtifactContentViewer.class)
|
||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||
public class CallLogArtifactViewer extends javax.swing.JPanel implements ArtifactContentViewer {
|
||||
|
||||
|
@ -0,0 +1,18 @@
|
||||
<?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"/>
|
||||
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,44,0,0,1,-112"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
|
||||
</Form>
|
@ -0,0 +1,435 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2020 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.awt.GridBagConstraints;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.JScrollPane;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.openide.util.lookup.ServiceProvider;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* Artifact viewer for Call log artifacts.
|
||||
*
|
||||
* Displays the To/From and other parties, and metadata for a call.
|
||||
*/
|
||||
@ServiceProvider(service = ArtifactContentViewer.class)
|
||||
public class CallLogArtifactViewerNew extends javax.swing.JPanel implements ArtifactContentViewer {
|
||||
|
||||
private final static Logger logger = Logger.getLogger(CallLogArtifactViewerNew.class.getName());
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final Set<Integer> HANDLED_ATTRIBUTE_TYPES = new HashSet<Integer>(Arrays.asList(
|
||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER.getTypeID(),
|
||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO.getTypeID(),
|
||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM.getTypeID(),
|
||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ID.getTypeID(),
|
||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION.getTypeID(),
|
||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID(),
|
||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START.getTypeID(),
|
||||
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_END.getTypeID()
|
||||
));
|
||||
|
||||
private GridBagLayout m_gridBagLayout = new GridBagLayout();
|
||||
private GridBagConstraints m_constraints = new GridBagConstraints();
|
||||
|
||||
private final List<PersonaSearchAndDisplayTask> personaSearchtasks = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Creates new form CallLogArtifactViewerNew
|
||||
*/
|
||||
public CallLogArtifactViewerNew() {
|
||||
initComponents();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
|
||||
setLayout(new java.awt.GridBagLayout());
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
@Override
|
||||
public void setArtifact(BlackboardArtifact artifact) {
|
||||
resetComponent();
|
||||
|
||||
CallLogViewDataNew callLogViewData = null;
|
||||
try {
|
||||
callLogViewData = getCallLogViewData(artifact);
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Error getting attributes for Calllog artifact (artifact_id=%d, obj_id=%d)", artifact.getArtifactID(), artifact.getObjectID()), ex);
|
||||
}
|
||||
|
||||
// update the view with the call log data
|
||||
if (callLogViewData != null) {
|
||||
updateView(callLogViewData);
|
||||
}
|
||||
// repaint
|
||||
this.revalidate();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts data from the call log artifact for display in the view.
|
||||
*
|
||||
* @param artifact Artifact to extract data from.
|
||||
*
|
||||
* @return CallLogViewData Extracted data to be displayed.
|
||||
*
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
private CallLogViewDataNew getCallLogViewData(BlackboardArtifact artifact) throws TskCoreException {
|
||||
|
||||
if (artifact == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
BlackboardAttribute directionAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION));
|
||||
BlackboardAttribute toAccountAttr = null;
|
||||
BlackboardAttribute fromAccountAttr = null;
|
||||
BlackboardAttribute localAccountAttr = null;
|
||||
|
||||
CallLogViewDataNew callLogViewData = null;
|
||||
|
||||
String direction = null;
|
||||
String fromAccountIdentifier = null;
|
||||
String toAccountIdentifier = null;
|
||||
List<String> otherParties = null;
|
||||
|
||||
Content dataSource = artifact.getDataSource();
|
||||
String deviceId = ((DataSource) dataSource).getDeviceId();
|
||||
|
||||
if (directionAttr != null) {
|
||||
direction = directionAttr.getValueString();
|
||||
if (direction.equalsIgnoreCase("Incoming")) {
|
||||
fromAccountAttr = ObjectUtils.firstNonNull(
|
||||
artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM)),
|
||||
artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER)),
|
||||
artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ID))
|
||||
);
|
||||
|
||||
toAccountAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO));
|
||||
localAccountAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO));
|
||||
} else if (direction.equalsIgnoreCase("Outgoing")) {
|
||||
toAccountAttr = ObjectUtils.firstNonNull(
|
||||
artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO)),
|
||||
artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER)),
|
||||
artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ID))
|
||||
);
|
||||
|
||||
fromAccountAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM));
|
||||
localAccountAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM));
|
||||
}
|
||||
}
|
||||
|
||||
// if direction isn't known, check all the usual attributes that may have the number/address
|
||||
// in the absence of sufficent data, any number availabel will be displayed as a From address.
|
||||
if (fromAccountAttr == null) {
|
||||
fromAccountAttr = ObjectUtils.firstNonNull(
|
||||
artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM)),
|
||||
artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO)),
|
||||
artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER)),
|
||||
artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ID))
|
||||
);
|
||||
}
|
||||
|
||||
if (fromAccountAttr != null) {
|
||||
String fromAccountAttrValue = fromAccountAttr.getValueString();
|
||||
if (fromAccountAttrValue.equalsIgnoreCase(deviceId) == false) {
|
||||
fromAccountIdentifier = fromAccountAttrValue;
|
||||
}
|
||||
}
|
||||
|
||||
if (toAccountAttr != null) {
|
||||
// TO may be a list of comma separated values.
|
||||
String[] numbers = toAccountAttr.getValueString().split(",");
|
||||
String toAccountAttrValue = StringUtils.trim(numbers[0]);
|
||||
if (toAccountAttrValue.equalsIgnoreCase(deviceId) == false) {
|
||||
toAccountIdentifier = toAccountAttrValue;
|
||||
}
|
||||
|
||||
// if more than one To address, then stcik the rest of them in the
|
||||
// "Other parties" list.
|
||||
if (numbers.length > 1) {
|
||||
otherParties = new ArrayList<>();
|
||||
for (int i = 1; i < numbers.length; i++) {
|
||||
otherParties.add(StringUtils.trim(numbers[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we have at least one address attribute
|
||||
if (null != fromAccountAttr || null != toAccountAttr) {
|
||||
callLogViewData = new CallLogViewDataNew(fromAccountIdentifier, toAccountIdentifier);
|
||||
callLogViewData.setDirection(direction);
|
||||
|
||||
callLogViewData.setOtherParties(otherParties);
|
||||
|
||||
extractTimeAndDuration(artifact, callLogViewData);
|
||||
|
||||
callLogViewData.setDataSourceName(dataSource.getName());
|
||||
|
||||
// set local account, if it can be deduced.
|
||||
if (localAccountAttr != null) {
|
||||
String attrValue = localAccountAttr.getValueString();
|
||||
// value must be a singular address and not a deviceId to be the local account id
|
||||
if (attrValue.equalsIgnoreCase(deviceId) == false && attrValue.contains(",") == false) {
|
||||
callLogViewData.setLocalAccountId(attrValue);
|
||||
}
|
||||
}
|
||||
|
||||
callLogViewData.setOtherAttributes(extractOtherAttributes(artifact));
|
||||
}
|
||||
|
||||
return callLogViewData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the call time and duration from the artifact and saves in the
|
||||
* CallLogViewData.
|
||||
*
|
||||
* @param artifact Call log artifact.
|
||||
* @param callLogViewData CallLogViewData object to save the time & duration
|
||||
* in.
|
||||
*
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
private void extractTimeAndDuration(BlackboardArtifact artifact, CallLogViewDataNew callLogViewData) throws TskCoreException {
|
||||
|
||||
BlackboardAttribute startTimeAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START));
|
||||
if (startTimeAttr == null) {
|
||||
startTimeAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME));
|
||||
}
|
||||
if (startTimeAttr != null) {
|
||||
long startTime = startTimeAttr.getValueLong();
|
||||
callLogViewData.setDateTimeStr(startTimeAttr.getDisplayString());
|
||||
|
||||
BlackboardAttribute endTimeAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_END));
|
||||
if (endTimeAttr != null) {
|
||||
long endTime = endTimeAttr.getValueLong();
|
||||
if (endTime > 0 && (endTime - startTime) > 0) {
|
||||
callLogViewData.setDuration(String.format("%d seconds", (endTime - startTime)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the attributes from the given artifact that are not already
|
||||
* displayed by the artifact viewer.
|
||||
*
|
||||
* @param artifact Call log artifact.
|
||||
*
|
||||
* @return Attribute names/values.
|
||||
*
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
private Map<String, String> extractOtherAttributes(BlackboardArtifact artifact) throws TskCoreException {
|
||||
List<BlackboardAttribute> attributes = artifact.getAttributes();
|
||||
Map<String, String> otherAttributes = new HashMap<>();
|
||||
|
||||
for (BlackboardAttribute attr : attributes) {
|
||||
if (HANDLED_ATTRIBUTE_TYPES.contains(attr.getAttributeType().getTypeID()) == false) {
|
||||
otherAttributes.put(attr.getAttributeType().getDisplayName(), attr.getDisplayString());
|
||||
}
|
||||
}
|
||||
|
||||
return otherAttributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the viewer with the call log data.
|
||||
*
|
||||
* @param callLogViewData Call log data to update the view with.
|
||||
*/
|
||||
private void updateView(CallLogViewDataNew callLogViewData) {
|
||||
|
||||
CommunicationArtifactViewerHelper.addHeader(this, m_gridBagLayout, this.m_constraints, "Parties");
|
||||
|
||||
// Display From address
|
||||
CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, "From");
|
||||
|
||||
if (callLogViewData.getFromAccount() != null) {
|
||||
// check if this is local account
|
||||
String accountDisplayString = getAccountDisplayString(callLogViewData.getFromAccount(), callLogViewData);
|
||||
CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, accountDisplayString);
|
||||
|
||||
// show persona
|
||||
Optional<PersonaSearchAndDisplayTask> task = CommunicationArtifactViewerHelper.addPersonaRow(this, m_gridBagLayout, this.m_constraints, callLogViewData.getFromAccount());
|
||||
if (task.isPresent()) {
|
||||
personaSearchtasks.add(task.get());
|
||||
}
|
||||
} else {
|
||||
CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, "Unknown");
|
||||
}
|
||||
|
||||
// Display To:
|
||||
CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, "To");
|
||||
if (callLogViewData.getToAccount() != null) {
|
||||
String accountDisplayString = getAccountDisplayString(callLogViewData.getToAccount(), callLogViewData);
|
||||
CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, accountDisplayString);
|
||||
|
||||
Optional<PersonaSearchAndDisplayTask> task = CommunicationArtifactViewerHelper.addPersonaRow(this, m_gridBagLayout, this.m_constraints, callLogViewData.getToAccount());
|
||||
if (task.isPresent()) {
|
||||
personaSearchtasks.add(task.get());
|
||||
}
|
||||
} else {
|
||||
CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, "Unknown");
|
||||
}
|
||||
|
||||
// Display other parties
|
||||
for (String otherParty : callLogViewData.getOtherParties()) {
|
||||
CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, "To");
|
||||
CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, otherParty);
|
||||
|
||||
Optional<PersonaSearchAndDisplayTask> task = CommunicationArtifactViewerHelper.addPersonaRow(this, m_gridBagLayout, this.m_constraints, otherParty);
|
||||
if (task.isPresent()) {
|
||||
personaSearchtasks.add(task.get());
|
||||
}
|
||||
}
|
||||
|
||||
updateMetadataView(callLogViewData);
|
||||
updateSourceView(callLogViewData);
|
||||
|
||||
CommunicationArtifactViewerHelper.addPageEndGlue(this, m_gridBagLayout, this.m_constraints);
|
||||
|
||||
this.setLayout(m_gridBagLayout);
|
||||
this.revalidate();
|
||||
this.repaint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the call log meta data section.
|
||||
*
|
||||
* @param callLogViewData Call log data.
|
||||
*/
|
||||
private void updateMetadataView(CallLogViewDataNew callLogViewData) {
|
||||
|
||||
CommunicationArtifactViewerHelper.addHeader(this, m_gridBagLayout, this.m_constraints, "Metadata");
|
||||
|
||||
CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, "Direction");
|
||||
if (callLogViewData.getDirection() != null) {
|
||||
CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, callLogViewData.getDirection());
|
||||
} else {
|
||||
CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, "Unknown");
|
||||
}
|
||||
|
||||
if (callLogViewData.getDateTimeStr() != null) {
|
||||
CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, "Date");
|
||||
CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, callLogViewData.getDateTimeStr());
|
||||
}
|
||||
|
||||
if (callLogViewData.getDuration() != null) {
|
||||
CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, "Duration");
|
||||
CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, callLogViewData.getDuration());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the call log source section.
|
||||
*
|
||||
* @param callLogViewData
|
||||
*/
|
||||
private void updateSourceView(CallLogViewDataNew callLogViewData) {
|
||||
CommunicationArtifactViewerHelper.addHeader(this, m_gridBagLayout, this.m_constraints, "Source");
|
||||
CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, "Data Source");
|
||||
CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, callLogViewData.getDataSourceName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns display string for a account.
|
||||
* Checks if the given account is the local account, if it is known.
|
||||
* If it is, it appends a "(Local)" suffix to account display string.
|
||||
*
|
||||
* @param accountIdentifier Account identifier to check.
|
||||
* @param callLogViewDataNew Call log data which may have the lock account.
|
||||
*
|
||||
* @return Account string to display.
|
||||
*/
|
||||
private String getAccountDisplayString(String accountIdentifier, CallLogViewDataNew callLogViewDataNew) {
|
||||
String accountDisplayValue = accountIdentifier;
|
||||
if (callLogViewDataNew.getLocalAccountId() != null && callLogViewDataNew.getLocalAccountId().equalsIgnoreCase(accountIdentifier)) {
|
||||
accountDisplayValue += " (Local)";
|
||||
}
|
||||
return accountDisplayValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getComponent() {
|
||||
return new JScrollPane(this, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSupported(BlackboardArtifact artifact) {
|
||||
return artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG.getTypeID();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets all artifact specific state.
|
||||
*/
|
||||
private void resetComponent() {
|
||||
|
||||
// cancel any outstanding persona searching threads.
|
||||
personaSearchtasks.forEach(task -> task.cancel(Boolean.TRUE));
|
||||
personaSearchtasks.clear();
|
||||
|
||||
// clear the panel
|
||||
this.removeAll();
|
||||
this.setLayout(null);
|
||||
|
||||
m_gridBagLayout = new GridBagLayout();
|
||||
m_constraints = new GridBagConstraints();
|
||||
|
||||
m_constraints.anchor = GridBagConstraints.FIRST_LINE_START;
|
||||
m_constraints.gridy = 0;
|
||||
m_constraints.gridx = 0;
|
||||
m_constraints.weighty = 0.05;
|
||||
m_constraints.weightx = 0.05;
|
||||
m_constraints.insets = new java.awt.Insets(0, 0, 0, 0);
|
||||
m_constraints.fill = GridBagConstraints.NONE;
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
// End of variables declaration//GEN-END:variables
|
||||
}
|
@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2020 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.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Encapsulates the information to be displayed about a call log artifact.
|
||||
*/
|
||||
final class CallLogViewDataNew {
|
||||
|
||||
private String fromAccount = null;
|
||||
private String toAccount = null;
|
||||
|
||||
// account identifier of the device owner, if known.
|
||||
// will be one of the to or from account.
|
||||
private String localAccountId = null;
|
||||
|
||||
private String direction;
|
||||
private String dateTimeStr = null;
|
||||
private String duration = null;
|
||||
|
||||
// Account identifers of other parties in the call.
|
||||
private Collection<String> otherParties = new ArrayList<>();
|
||||
|
||||
private Map<String, String> otherAttributes = new HashMap<>();
|
||||
|
||||
private String dataSourceName = null;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param fromAccount From account identifier, may be null;
|
||||
* @param toAccount To account identifier, may be null;
|
||||
*/
|
||||
CallLogViewDataNew(String fromAccount, String toAccount) {
|
||||
this(fromAccount, toAccount, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param fromAccount From account identifier, may be null;
|
||||
* @param toAccount To account identifier, may be null;
|
||||
* @param direction Direction, may be null.
|
||||
*/
|
||||
CallLogViewDataNew(String fromAccount, String toAccount, String direction) {
|
||||
this.fromAccount = fromAccount;
|
||||
this.toAccount = toAccount;
|
||||
this.direction = direction;
|
||||
}
|
||||
|
||||
String getFromAccount() {
|
||||
return fromAccount;
|
||||
}
|
||||
|
||||
void setFromAccount(String fromAccount) {
|
||||
this.fromAccount = fromAccount;
|
||||
}
|
||||
|
||||
String getToAccount() {
|
||||
return toAccount;
|
||||
}
|
||||
|
||||
void setToAccount(String toAccount) {
|
||||
this.toAccount = toAccount;
|
||||
}
|
||||
|
||||
String getDirection() {
|
||||
return direction;
|
||||
}
|
||||
|
||||
void setDirection(String direction) {
|
||||
this.direction = direction;
|
||||
}
|
||||
|
||||
String getDataSourceName() {
|
||||
return dataSourceName;
|
||||
}
|
||||
|
||||
void setDataSourceName(String dataSourceName) {
|
||||
this.dataSourceName = dataSourceName;
|
||||
}
|
||||
|
||||
String getDateTimeStr() {
|
||||
return dateTimeStr;
|
||||
}
|
||||
|
||||
void setDateTimeStr(String dateTimeStr) {
|
||||
this.dateTimeStr = dateTimeStr;
|
||||
}
|
||||
|
||||
String getDuration() {
|
||||
return duration;
|
||||
}
|
||||
|
||||
void setDuration(String duration) {
|
||||
this.duration = duration;
|
||||
}
|
||||
|
||||
Collection<String> getOtherParties() {
|
||||
return Collections.unmodifiableCollection(otherParties);
|
||||
}
|
||||
|
||||
void setOtherParties(Collection<String> otherParticipants) {
|
||||
if (otherParticipants != null) {
|
||||
this.otherParties = new ArrayList<>(otherParticipants);
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, String> getOtherAttributes() {
|
||||
return Collections.unmodifiableMap(otherAttributes);
|
||||
}
|
||||
|
||||
public void setOtherAttributes(Map<String, String> otherAttributes) {
|
||||
if (otherAttributes != null) {
|
||||
this.otherAttributes = new HashMap<>(otherAttributes);
|
||||
}
|
||||
}
|
||||
|
||||
public String getLocalAccountId() {
|
||||
return localAccountId;
|
||||
}
|
||||
|
||||
public void setLocalAccountId(String localAccountId) {
|
||||
this.localAccountId = localAccountId;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,334 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2020 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.Dimension;
|
||||
import java.awt.Font;
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.awt.Insets;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.datatransfer.StringSelection;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.SwingUtilities;
|
||||
import java.util.Optional;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
|
||||
|
||||
/**
|
||||
*
|
||||
* A class to help display a communication artifact in a panel using a
|
||||
* gridbaglayout.
|
||||
*/
|
||||
public class CommunicationArtifactViewerHelper {
|
||||
|
||||
// Number of columns in the gridbag layout.
|
||||
private final static int MAX_COLS = 4;
|
||||
|
||||
private final static int LEFT_INDENT = 12;
|
||||
/**
|
||||
* Adds a new heading to the panel.
|
||||
*
|
||||
* @param panel Panel to update.
|
||||
* @param gridbagLayout Layout to use.
|
||||
* @param constraints Constrains to use.
|
||||
* @param headerString Heading string to display.
|
||||
*/
|
||||
static void addHeader(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, String headerString) {
|
||||
|
||||
// add a blank line before the start of new section, unless it's
|
||||
// the first section
|
||||
if (constraints.gridy != 0) {
|
||||
addBlankLine(panel, gridbagLayout, constraints);
|
||||
}
|
||||
constraints.gridy++;
|
||||
constraints.gridx = 0;
|
||||
|
||||
// let the header span all of the row
|
||||
constraints.gridwidth = MAX_COLS;
|
||||
|
||||
// create label for heading
|
||||
javax.swing.JLabel headingLabel = new javax.swing.JLabel();
|
||||
headingLabel.setText(headerString);
|
||||
|
||||
// make it large and bold
|
||||
headingLabel.setFont(headingLabel.getFont().deriveFont(Font.BOLD, headingLabel.getFont().getSize() + 2));
|
||||
|
||||
// add to panel
|
||||
gridbagLayout.setConstraints(headingLabel, constraints);
|
||||
panel.add(headingLabel);
|
||||
|
||||
// reset constraints to normal
|
||||
constraints.gridwidth = 1;
|
||||
|
||||
// add line end glue
|
||||
addLineEndGlue(panel, gridbagLayout, constraints);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a filler/glue at the end of the line to keep the other columns
|
||||
* aligned, in case the panel is resized.
|
||||
*
|
||||
* @param panel Panel to update.
|
||||
* @param gridbagLayout Layout to use.
|
||||
* @param constraints Constrains to use.
|
||||
*/
|
||||
private static void addLineEndGlue(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints) {
|
||||
// Place the filler just past the last column.
|
||||
constraints.gridx = MAX_COLS;
|
||||
|
||||
double savedWeightX = constraints.weightx;
|
||||
int savedFill = constraints.fill;
|
||||
|
||||
constraints.weightx = 1.0; // take up all the horizontal space
|
||||
constraints.fill = GridBagConstraints.BOTH;
|
||||
|
||||
javax.swing.Box.Filler horizontalFiller = new javax.swing.Box.Filler(new Dimension(0, 0), new Dimension(0, 0), new Dimension(32767, 0));
|
||||
gridbagLayout.setConstraints(horizontalFiller, constraints);
|
||||
panel.add(horizontalFiller);
|
||||
|
||||
// restore fill & weight
|
||||
constraints.fill = savedFill;
|
||||
constraints.weightx = savedWeightX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a filler/glue at the bottom of the panel to keep the data rows
|
||||
* aligned, in case the panel is resized.
|
||||
*
|
||||
* @param panel Panel to update.
|
||||
* @param gridbagLayout Layout to use.
|
||||
* @param constraints Constrains to use.
|
||||
*/
|
||||
static void addPageEndGlue(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints) {
|
||||
|
||||
constraints.gridx = 0;
|
||||
|
||||
double savedWeighty = constraints.weighty;
|
||||
int savedFill = constraints.fill;
|
||||
|
||||
constraints.weighty = 1.0; // take up all the vertical space
|
||||
constraints.fill = GridBagConstraints.VERTICAL;
|
||||
|
||||
javax.swing.Box.Filler vertFiller = new javax.swing.Box.Filler(new Dimension(0, 0), new Dimension(0, 0), new Dimension(0, 32767));
|
||||
gridbagLayout.setConstraints(vertFiller, constraints);
|
||||
panel.add(vertFiller, constraints);
|
||||
|
||||
//Resore weight & fill
|
||||
constraints.weighty = savedWeighty;
|
||||
constraints.fill = savedFill;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a blank line to the panel.
|
||||
*
|
||||
* @param panel Panel to update.
|
||||
* @param gridbagLayout Layout to use.
|
||||
* @param constraints Constrains to use.
|
||||
*/
|
||||
private static void addBlankLine(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints) {
|
||||
constraints.gridy++;
|
||||
constraints.gridx = 0;
|
||||
|
||||
javax.swing.JLabel filler = new javax.swing.JLabel(" ");
|
||||
gridbagLayout.setConstraints(filler, constraints);
|
||||
panel.add(filler);
|
||||
|
||||
addLineEndGlue(panel, gridbagLayout, constraints);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a label/key to the panel.
|
||||
*
|
||||
* @param panel Panel to update.
|
||||
* @param gridbagLayout Layout to use.
|
||||
* @param constraints Constrains to use.
|
||||
* @param keyString Key name to display.
|
||||
*/
|
||||
static void addKey(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, String keyString) {
|
||||
|
||||
constraints.gridy++;
|
||||
constraints.gridx = 0;
|
||||
|
||||
Insets savedInsets = constraints.insets;
|
||||
|
||||
// Set inset to indent in
|
||||
constraints.insets = new java.awt.Insets(0, LEFT_INDENT, 0, 0);
|
||||
|
||||
// create label,
|
||||
javax.swing.JLabel keyLabel = new javax.swing.JLabel();
|
||||
keyLabel.setText(keyString + ": ");
|
||||
|
||||
// add to panel
|
||||
gridbagLayout.setConstraints(keyLabel, constraints);
|
||||
panel.add(keyLabel);
|
||||
|
||||
// restore inset
|
||||
constraints.insets = savedInsets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a value string to the panel.
|
||||
*
|
||||
* @param panel Panel to update.
|
||||
* @param gridbagLayout Layout to use.
|
||||
* @param constraints Constrains to use.
|
||||
* @param keyString Value string to display.
|
||||
*/
|
||||
static void addValue(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, String valueString) {
|
||||
|
||||
constraints.gridx = 1;
|
||||
|
||||
int savedGridwidth = constraints.gridwidth;
|
||||
|
||||
// let the value span 2 cols
|
||||
constraints.gridwidth = 2;
|
||||
|
||||
// create label,
|
||||
javax.swing.JLabel valueField = new javax.swing.JLabel();
|
||||
valueField.setText(valueString);
|
||||
|
||||
// attach a right click menu with Copy option
|
||||
valueField.addMouseListener(new java.awt.event.MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(java.awt.event.MouseEvent evt) {
|
||||
valueLabelMouseClicked(evt, valueField);
|
||||
}
|
||||
});
|
||||
|
||||
// add label to panel
|
||||
gridbagLayout.setConstraints(valueField, constraints);
|
||||
panel.add(valueField);
|
||||
|
||||
// restore constraints
|
||||
constraints.gridwidth = savedGridwidth;
|
||||
|
||||
// end the line
|
||||
addLineEndGlue(panel, gridbagLayout, constraints);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a Persona row to the panel.
|
||||
*
|
||||
* Adds a persona name label and a button to the panel. Kicks off a
|
||||
* background task to search for persona for the given account. Updates the
|
||||
* persona name and button when the task is done.
|
||||
*
|
||||
* If CentralRepostory is disabled, just displays 'Unknown' persona name.
|
||||
*
|
||||
* @param panel Panel to update.
|
||||
* @param gridbagLayout Layout to use.
|
||||
* @param constraints Constrains to use.
|
||||
* @param accountIdentifier Account identifier to search the persona.
|
||||
*
|
||||
* @return Optional PersonaSearchAndDisplayTask started to search for
|
||||
* persona.
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"CommunicationArtifactViewerHelper_persona_label=Persona: ",
|
||||
"CommunicationArtifactViewerHelper_persona_searching=Searching...",
|
||||
"CommunicationArtifactViewerHelper_persona_unknown=Unknown",
|
||||
"CommunicationArtifactViewerHelper_persona_button_view=View",
|
||||
"CommunicationArtifactViewerHelper_persona_button_create=Create"
|
||||
})
|
||||
static Optional<PersonaSearchAndDisplayTask> addPersonaRow(JPanel panel, GridBagLayout gridbagLayout, GridBagConstraints constraints, String accountIdentifier) {
|
||||
|
||||
PersonaSearchAndDisplayTask personaTask = null;
|
||||
|
||||
constraints.gridy++;
|
||||
constraints.gridx = 1;
|
||||
|
||||
Insets savedInsets = constraints.insets;
|
||||
|
||||
// Indent in
|
||||
constraints.insets = new java.awt.Insets(0, LEFT_INDENT, 0, 0);
|
||||
|
||||
// create label
|
||||
javax.swing.JLabel personaLabel = new javax.swing.JLabel();
|
||||
String personaLabelText = Bundle.CommunicationArtifactViewerHelper_persona_label();
|
||||
personaLabelText = personaLabelText.concat(CentralRepository.isEnabled()
|
||||
? Bundle.CommunicationArtifactViewerHelper_persona_searching()
|
||||
: Bundle.CommunicationArtifactViewerHelper_persona_unknown());
|
||||
|
||||
personaLabel.setText(personaLabelText);
|
||||
|
||||
// add to panel
|
||||
gridbagLayout.setConstraints(personaLabel, constraints);
|
||||
panel.add(personaLabel);
|
||||
|
||||
// restore constraint
|
||||
constraints.insets = savedInsets;
|
||||
|
||||
constraints.gridx++;
|
||||
|
||||
// Place a button as place holder. It will be enabled when persona is available.
|
||||
javax.swing.JButton personaButton = new javax.swing.JButton();
|
||||
personaButton.setText(Bundle.CommunicationArtifactViewerHelper_persona_button_view());
|
||||
personaButton.setEnabled(false);
|
||||
|
||||
|
||||
gridbagLayout.setConstraints(personaButton, constraints);
|
||||
panel.add(personaButton);
|
||||
|
||||
if (CentralRepository.isEnabled()) {
|
||||
// kick off a task to find the persona for this account
|
||||
personaTask = new PersonaSearchAndDisplayTask(panel, new AccountPersonaSearcherData(accountIdentifier, personaLabel, personaButton));
|
||||
personaTask.execute();
|
||||
} else {
|
||||
personaLabel.setEnabled(false);
|
||||
}
|
||||
|
||||
addLineEndGlue(panel, gridbagLayout, constraints);
|
||||
|
||||
return Optional.ofNullable(personaTask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Event handler for mouse click event. Attaches a 'Copy' menu item to right
|
||||
* click.
|
||||
*
|
||||
* @param evt Event to check.
|
||||
* @param valueLabel Label to attach the menu item to.
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"CommunicationArtifactViewerHelper_menuitem_copy=Copy"
|
||||
})
|
||||
private static void valueLabelMouseClicked(java.awt.event.MouseEvent evt, JLabel valueLabel) {
|
||||
if (SwingUtilities.isRightMouseButton(evt)) {
|
||||
JPopupMenu popup = new JPopupMenu();
|
||||
|
||||
JMenuItem copyMenu = new JMenuItem(Bundle.CommunicationArtifactViewerHelper_menuitem_copy()); // NON-NLS
|
||||
copyMenu.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(valueLabel.getText()), null);
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
popup.add(copyMenu);
|
||||
popup.show(valueLabel, evt.getX(), evt.getY());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,220 @@
|
||||
/**
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2020 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.awt.event.ActionListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.SwingWorker;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.Persona;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.PersonaAccount;
|
||||
import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsDialog;
|
||||
import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsDialogCallback;
|
||||
import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsMode;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
|
||||
/**
|
||||
* Background task to search for a persona for a given account.
|
||||
*
|
||||
* When the search is complete, it updates the UI components
|
||||
* for the persona appropriately.
|
||||
*
|
||||
*/
|
||||
|
||||
@NbBundle.Messages({
|
||||
"# {0} - Persona count",
|
||||
"PersonaDisplayTask_persona_count_suffix=(1 of {0})"
|
||||
})
|
||||
class PersonaSearchAndDisplayTask extends SwingWorker<Collection<Persona>, Void> {
|
||||
|
||||
private final static Logger logger = Logger.getLogger(PersonaSearchAndDisplayTask.class.getName());
|
||||
|
||||
private final Component parentComponent;
|
||||
private final AccountPersonaSearcherData personaSearcherData;
|
||||
|
||||
PersonaSearchAndDisplayTask(Component parentComponent, AccountPersonaSearcherData personaSearcherData) {
|
||||
this.parentComponent = parentComponent;
|
||||
this.personaSearcherData = personaSearcherData;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<Persona> doInBackground() throws Exception {
|
||||
|
||||
Collection<Persona> personas = new ArrayList<>();
|
||||
|
||||
if (CentralRepository.isEnabled()) {
|
||||
Collection<CentralRepoAccount> accountCandidates
|
||||
= CentralRepoAccount.getAccountsWithIdentifier(personaSearcherData.getAccountIdentifer());
|
||||
|
||||
if (accountCandidates.isEmpty() == false) {
|
||||
CentralRepoAccount account = accountCandidates.iterator().next();
|
||||
|
||||
// get personas for the account
|
||||
Collection<PersonaAccount> personaAccountsList = PersonaAccount.getPersonaAccountsForAccount(account.getId());
|
||||
personas = personaAccountsList.stream().map(PersonaAccount::getPersona)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
return personas;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done() {
|
||||
Collection<Persona> personas;
|
||||
try {
|
||||
personas = super.get();
|
||||
|
||||
if (this.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Update the Persona label and button based on the search result
|
||||
String personaLabelText = Bundle.CommunicationArtifactViewerHelper_persona_label();
|
||||
String personaButtonText;
|
||||
ActionListener buttonActionListener;
|
||||
|
||||
if (personas.isEmpty()) {
|
||||
// No persona found
|
||||
personaLabelText += Bundle.CommunicationArtifactViewerHelper_persona_unknown();
|
||||
|
||||
// show a 'Create' button
|
||||
personaButtonText = Bundle.CommunicationArtifactViewerHelper_persona_button_create();
|
||||
buttonActionListener = new CreatePersonaButtonListener(parentComponent, personaSearcherData);
|
||||
} else {
|
||||
Persona persona = personas.iterator().next();
|
||||
personaLabelText += persona.getName();
|
||||
if (personas.size() > 1) {
|
||||
personaLabelText += Bundle.PersonaDisplayTask_persona_count_suffix(Integer.toString(personas.size()));
|
||||
}
|
||||
// Show a 'View' button
|
||||
personaButtonText = Bundle.CommunicationArtifactViewerHelper_persona_button_view();
|
||||
buttonActionListener = new ViewPersonaButtonListener(parentComponent, persona);
|
||||
}
|
||||
|
||||
personaSearcherData.getPersonaNameLabel().setText(personaLabelText);
|
||||
personaSearcherData.getPersonaActionButton().setText(personaButtonText);
|
||||
personaSearcherData.getPersonaActionButton().setEnabled(true);
|
||||
|
||||
// set button action
|
||||
personaSearcherData.getPersonaActionButton().addActionListener(buttonActionListener);
|
||||
} catch (CancellationException ex) {
|
||||
logger.log(Level.INFO, "Persona searching was canceled."); //NON-NLS
|
||||
} catch (InterruptedException ex) {
|
||||
logger.log(Level.INFO, "Persona searching was interrupted."); //NON-NLS
|
||||
} catch (ExecutionException ex) {
|
||||
logger.log(Level.SEVERE, "Fatal error during Persona search.", ex); //NON-NLS
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Action listener for Create persona button.
|
||||
*/
|
||||
private class CreatePersonaButtonListener implements ActionListener {
|
||||
|
||||
private final Component parentComponent;
|
||||
private final AccountPersonaSearcherData personaSearcherData;
|
||||
|
||||
CreatePersonaButtonListener(Component parentComponent, AccountPersonaSearcherData personaSearcherData) {
|
||||
this.parentComponent = parentComponent;
|
||||
this.personaSearcherData = personaSearcherData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
// Launch the Persona Create dialog
|
||||
new PersonaDetailsDialog(parentComponent,
|
||||
PersonaDetailsMode.CREATE, null, new PersonaCreateCallbackImpl(parentComponent, personaSearcherData));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Action listener for View persona button.
|
||||
*/
|
||||
private class ViewPersonaButtonListener implements ActionListener {
|
||||
|
||||
private final Component parentComponent;
|
||||
private final Persona persona;
|
||||
|
||||
ViewPersonaButtonListener(Component parentComponent, Persona persona) {
|
||||
this.parentComponent = parentComponent;
|
||||
this.persona = persona;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
new PersonaDetailsDialog(parentComponent,
|
||||
PersonaDetailsMode.VIEW, persona, new PersonaViewCallbackImpl());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback method for the create mode of the PersonaDetailsDialog
|
||||
*/
|
||||
class PersonaCreateCallbackImpl implements PersonaDetailsDialogCallback {
|
||||
|
||||
private final Component parentComponent;
|
||||
private final AccountPersonaSearcherData personaSearcherData;
|
||||
|
||||
PersonaCreateCallbackImpl(Component parentComponent, AccountPersonaSearcherData personaSearcherData) {
|
||||
this.parentComponent = parentComponent;
|
||||
this.personaSearcherData = personaSearcherData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void callback(Persona persona) {
|
||||
JButton personaButton = personaSearcherData.getPersonaActionButton();
|
||||
if (persona != null) {
|
||||
// update the persona name label with newly created persona,
|
||||
// and change the button to a "View" button
|
||||
personaSearcherData.getPersonaNameLabel().setText(Bundle.CommunicationArtifactViewerHelper_persona_label() + persona.getName());
|
||||
personaSearcherData.getPersonaActionButton().setText(Bundle.CallLogArtifactViewer_persona_button_view());
|
||||
|
||||
// replace action listener with a View button listener
|
||||
for (ActionListener act : personaButton.getActionListeners()) {
|
||||
personaButton.removeActionListener(act);
|
||||
}
|
||||
personaButton.addActionListener(new ViewPersonaButtonListener(parentComponent, persona));
|
||||
|
||||
}
|
||||
|
||||
personaButton.getParent().revalidate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback method for the view mode of the PersonaDetailsDialog
|
||||
*/
|
||||
class PersonaViewCallbackImpl implements PersonaDetailsDialogCallback {
|
||||
|
||||
@Override
|
||||
public void callback(Persona persona) {
|
||||
// nothing to do
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user