19 package org.sleuthkit.autopsy.contentviewers.artifactviewers;
 
   21 import java.awt.Component;
 
   22 import java.awt.GridBagConstraints;
 
   23 import java.awt.GridBagLayout;
 
   24 import java.awt.Insets;
 
   25 import java.awt.event.ActionListener;
 
   26 import java.awt.image.BufferedImage;
 
   28 import java.io.IOException;
 
   29 import java.util.ArrayList;
 
   30 import java.util.Collection;
 
   31 import java.util.Collections;
 
   32 import java.util.HashMap;
 
   33 import java.util.List;
 
   35 import java.util.Optional;
 
   36 import java.util.concurrent.CancellationException;
 
   37 import java.util.concurrent.ExecutionException;
 
   38 import java.util.logging.Level;
 
   39 import java.util.stream.Collectors;
 
   40 import javax.imageio.ImageIO;
 
   41 import javax.swing.ImageIcon;
 
   42 import javax.swing.JButton;
 
   43 import javax.swing.JLabel;
 
   44 import javax.swing.JScrollPane;
 
   45 import javax.swing.SwingWorker;
 
   46 import javax.swing.border.EmptyBorder;
 
   47 import org.apache.commons.lang.StringUtils;
 
   48 import org.openide.util.NbBundle;
 
   49 import org.openide.util.lookup.ServiceProvider;
 
   76 @ServiceProvider(service = ArtifactContentViewer.class)
 
   80     private static final long serialVersionUID = 1L;
 
   82     private GridBagLayout m_gridBagLayout = 
new GridBagLayout();
 
   83     private GridBagConstraints m_constraints = 
new GridBagConstraints();
 
   92     private List<BlackboardAttribute> phoneNumList = 
new ArrayList<>();
 
   93     private List<BlackboardAttribute> emailList = 
new ArrayList<>();
 
   94     private List<BlackboardAttribute> nameList = 
new ArrayList<>();
 
   95     private List<BlackboardAttribute> otherList = 
new ArrayList<>();
 
   96     private List<BlackboardAttribute> accountAttributesList = 
new ArrayList<>();
 
   98     private final static String DEFAULT_IMAGE_PATH = 
"/org/sleuthkit/autopsy/images/defaultContact.png";
 
  102     private final List<CentralRepoAccount> contactUniqueAccountsList = 
new ArrayList<>();
 
  106     private final Map<Persona, ArrayList<CentralRepoAccount>> contactUniquePersonasMap = 
new HashMap<>();
 
  124     @SuppressWarnings(
"unchecked")
 
  126     private 
void initComponents() {
 
  129         setLayout(
new java.awt.GridBagLayout());
 
  137         if (artifact != null) {
 
  139                 extractArtifactData(artifact);
 
  141                 logger.log(Level.SEVERE, String.format(
"Error getting attributes for artifact (artifact_id=%d, obj_id=%d)", artifact.
getArtifactID(), artifact.
getObjectID()), ex);
 
  146         this.setLayout(this.m_gridBagLayout);
 
  154         return new JScrollPane(
this, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
 
  159         return (artifact != null)
 
  172         this.contactArtifact = artifact;
 
  174         phoneNumList = 
new ArrayList<>();
 
  175         emailList = 
new ArrayList<>();
 
  176         nameList = 
new ArrayList<>();
 
  177         otherList = 
new ArrayList<>();
 
  178         accountAttributesList = 
new ArrayList<>();
 
  182             if (bba.getAttributeType().getTypeName().startsWith(
"TSK_PHONE")) {
 
  183                 phoneNumList.add(bba);
 
  184                 accountAttributesList.add(bba);
 
  185             } 
else if (bba.getAttributeType().getTypeName().startsWith(
"TSK_EMAIL")) {
 
  187                 accountAttributesList.add(bba);
 
  188             } 
else if (bba.getAttributeType().getTypeName().startsWith(
"TSK_NAME")) {
 
  192                 if (bba.getAttributeType().getTypeName().equalsIgnoreCase(
"TSK_ID")) {
 
  193                     accountAttributesList.add(bba);
 
  201                 .map(h -> h.getName())
 
  211         updateContactDetails();
 
  217         initiatePersonasSearch();
 
  225         "ContactArtifactViewer_phones_header=Phone",
 
  226         "ContactArtifactViewer_emails_header=Email",
 
  227         "ContactArtifactViewer_others_header=Other",})
 
  231         updateContactImage(m_gridBagLayout, m_constraints);
 
  232         updateContactName(m_gridBagLayout, m_constraints);
 
  235         updateContactMethodSection(phoneNumList, Bundle.ContactArtifactViewer_phones_header(), m_gridBagLayout, m_constraints);
 
  236         updateContactMethodSection(emailList, Bundle.ContactArtifactViewer_emails_header(), m_gridBagLayout, m_constraints);
 
  237         updateContactMethodSection(otherList, Bundle.ContactArtifactViewer_others_header(), m_gridBagLayout, m_constraints);
 
  248         "ContactArtifactViewer.contactImage.text=",})
 
  249     private void updateContactImage(GridBagLayout contactPanelLayout, GridBagConstraints contactPanelConstraints) {
 
  251         Insets savedInsets = contactPanelConstraints.insets;
 
  252         contactPanelConstraints.gridy = 0;
 
  253         contactPanelConstraints.gridx = 0;
 
  255         int prevGridWidth = contactPanelConstraints.gridwidth;
 
  256         contactPanelConstraints.gridwidth = 3;
 
  257         contactPanelConstraints.anchor = GridBagConstraints.LINE_START;
 
  259         javax.swing.JLabel contactImage = 
new javax.swing.JLabel();
 
  260         contactImage.setIcon(getImageFromArtifact(contactArtifact));
 
  261         contactImage.setText(Bundle.ContactArtifactViewer_contactImage_text());
 
  264         CommunicationArtifactViewerHelper.addComponent(
this, contactPanelLayout, contactPanelConstraints, contactImage);
 
  265         CommunicationArtifactViewerHelper.addLineEndGlue(
this, contactPanelLayout, contactPanelConstraints);
 
  266         contactPanelConstraints.gridy++;
 
  268         contactPanelConstraints.gridwidth = prevGridWidth;
 
  269         contactPanelConstraints.insets = savedInsets;
 
  280         "ContactArtifactViewer_contactname_unknown=Unknown",})
 
  281     private void updateContactName(GridBagLayout contactPanelLayout, GridBagConstraints contactPanelConstraints) {
 
  283         boolean foundName = 
false;
 
  285             if (StringUtils.isEmpty(bba.getValueString()) == 
false) {
 
  286                 contactName = bba.getDisplayString();
 
  288                 CommunicationArtifactViewerHelper.addHeader(
this, contactPanelLayout, contactPanelConstraints, 0, contactName);
 
  293         if (foundName == 
false) {
 
  294             CommunicationArtifactViewerHelper.addHeader(
this, contactPanelLayout, contactPanelConstraints, 
ContentViewerDefaults.
getSectionSpacing(), Bundle.ContactArtifactViewer_contactname_unknown());
 
  308     private void updateContactMethodSection(List<BlackboardAttribute> sectionAttributesList, String sectionHeader, GridBagLayout contactPanelLayout, GridBagConstraints contactPanelConstraints) {
 
  311         if (sectionAttributesList.isEmpty()) {
 
  317             CommunicationArtifactViewerHelper.addKey(
this, contactPanelLayout, contactPanelConstraints, bba.getAttributeType().getDisplayName());
 
  318             CommunicationArtifactViewerHelper.addValue(
this, contactPanelLayout, contactPanelConstraints, bba.getDisplayString());
 
  326         "ContactArtifactViewer_heading_Source=Source",
 
  327         "ContactArtifactViewer_label_datasource=Data Source",
 
  328         "ContactArtifactViewer_label_host=Host",})
 
  331         CommunicationArtifactViewerHelper.addKey(
this, m_gridBagLayout, m_constraints, Bundle.ContactArtifactViewer_label_host());
 
  332         CommunicationArtifactViewerHelper.addValue(
this, m_gridBagLayout, m_constraints, StringUtils.defaultString(hostName));
 
  333         CommunicationArtifactViewerHelper.addKey(
this, m_gridBagLayout, m_constraints, Bundle.ContactArtifactViewer_label_datasource());
 
  334         CommunicationArtifactViewerHelper.addValue(
this, m_gridBagLayout, m_constraints, datasourceName);
 
  343         "ContactArtifactViewer_persona_header=Persona",
 
  344         "ContactArtifactViewer_persona_searching=Searching...",
 
  345         "ContactArtifactViewer_cr_disabled_message=Enable Central Repository to view, create and edit personas.",
 
  346         "ContactArtifactViewer_persona_unknown=Unknown" 
  351         JLabel personaHeader = CommunicationArtifactViewerHelper.addHeader(
this, m_gridBagLayout, m_constraints, 
ContentViewerDefaults.
getSectionSpacing(), Bundle.ContactArtifactViewer_persona_header());
 
  353         m_constraints.gridy++;
 
  357                 ? Bundle.ContactArtifactViewer_persona_searching()
 
  358                 : Bundle.ContactArtifactViewer_persona_unknown();
 
  360         this.personaSearchStatusLabel = 
new javax.swing.JLabel();
 
  361         personaSearchStatusLabel.setText(personaStatusLabelText);
 
  364         m_constraints.gridx = 0;
 
  365         m_constraints.anchor = GridBagConstraints.LINE_START;
 
  367         CommunicationArtifactViewerHelper.addComponent(
this, m_gridBagLayout, m_constraints, personaSearchStatusLabel);
 
  372             personaSearchTask.execute();
 
  374             personaHeader.setEnabled(
false);
 
  375             personaSearchStatusLabel.setEnabled(
false);
 
  378             CommunicationArtifactViewerHelper.addMessageRow(
this, m_gridBagLayout, messageInsets, m_constraints, Bundle.ContactArtifactViewer_cr_disabled_message());
 
  379             m_constraints.gridy++;
 
  381             CommunicationArtifactViewerHelper.addPageEndGlue(
this, m_gridBagLayout, this.m_constraints);
 
  392         this.
remove(personaSearchStatusLabel);
 
  394         m_constraints.gridx = 0;
 
  395         if (contactUniquePersonasMap.isEmpty()) {
 
  397             showPersona(null, 0, Collections.emptyList(), this.m_gridBagLayout, this.m_constraints);
 
  399             int matchCounter = 0;
 
  400             for (Map.Entry<
Persona, ArrayList<CentralRepoAccount>> entry : contactUniquePersonasMap.entrySet()) {
 
  401                 List<CentralRepoAccount> missingAccounts = 
new ArrayList<>();
 
  402                 ArrayList<CentralRepoAccount> personaAccounts = entry.getValue();
 
  407                     if (personaAccounts.contains(account) == 
false) {
 
  408                         missingAccounts.add(account);
 
  412                 showPersona(entry.getKey(), matchCounter, missingAccounts, m_gridBagLayout, m_constraints);
 
  413                 m_constraints.gridy += 2;
 
  418         CommunicationArtifactViewerHelper.addPageEndGlue(
this, m_gridBagLayout, this.m_constraints);
 
  421         this.setLayout(this.m_gridBagLayout);
 
  439         "ContactArtifactViewer_persona_label=Persona ",
 
  440         "ContactArtifactViewer_persona_no_match=No matches found",
 
  441         "ContactArtifactViewer_persona_button_view=View",
 
  442         "ContactArtifactViewer_persona_button_new=Create",
 
  443         "ContactArtifactViewer_persona_match_num=Match ",
 
  444         "ContactArtifactViewer_missing_account_label=Missing contact account",
 
  445         "ContactArtifactViewer_found_all_accounts_label=All accounts found." 
  447     private void showPersona(
Persona persona, 
int matchNumber, List<CentralRepoAccount> missingAccountsList, GridBagLayout gridBagLayout, GridBagConstraints constraints) {
 
  450         Insets savedInsets = constraints.insets;
 
  453         constraints.gridx = 0;
 
  454         javax.swing.JLabel matchNumberLabel = CommunicationArtifactViewerHelper.addKey(
this, gridBagLayout, constraints, String.format(
"%s %d", Bundle.ContactArtifactViewer_persona_match_num(), matchNumber).trim());
 
  456         javax.swing.JLabel personaNameLabel = 
new javax.swing.JLabel();
 
  457         javax.swing.JButton personaButton = 
new javax.swing.JButton();
 
  460         String personaButtonText;
 
  461         ActionListener personaButtonListener;
 
  462         if (persona != null) {
 
  463             personaName = persona.
getName();
 
  464             personaButtonText = Bundle.ContactArtifactViewer_persona_button_view();
 
  467             matchNumberLabel.setVisible(
false);
 
  468             personaName = Bundle.ContactArtifactViewer_persona_no_match();
 
  469             personaButtonText = Bundle.ContactArtifactViewer_persona_button_new();
 
  476         constraints.anchor = GridBagConstraints.LINE_START;
 
  477         personaNameLabel.setText(personaName);
 
  478         gridBagLayout.setConstraints(personaNameLabel, constraints);
 
  479         CommunicationArtifactViewerHelper.addComponent(
this, gridBagLayout, constraints, personaNameLabel);
 
  485         personaButton.setText(personaButtonText);
 
  486         personaButton.addActionListener(personaButtonListener);
 
  489         personaButton.setMargin(
new Insets(0, 5, 0, 5));
 
  491         constraints.anchor = GridBagConstraints.LINE_START;
 
  492         gridBagLayout.setConstraints(personaButton, constraints);
 
  493         CommunicationArtifactViewerHelper.addComponent(
this, gridBagLayout, constraints, personaButton);
 
  494         CommunicationArtifactViewerHelper.addLineEndGlue(
this, gridBagLayout, constraints);
 
  496         constraints.insets = savedInsets;
 
  499         if (persona != null) {
 
  500             if (missingAccountsList.isEmpty()) {
 
  502                 constraints.gridx = 1;
 
  505                 javax.swing.JLabel accountsStatus = 
new javax.swing.JLabel(Bundle.ContactArtifactViewer_found_all_accounts_label());
 
  507                 constraints.anchor = GridBagConstraints.LINE_START;
 
  508                 CommunicationArtifactViewerHelper.addComponent(
this, gridBagLayout, constraints, accountsStatus);
 
  509                 constraints.insets = savedInsets;
 
  511                 CommunicationArtifactViewerHelper.addLineEndGlue(
this, gridBagLayout, constraints);
 
  516                     constraints.gridx = 0;
 
  520                     CommunicationArtifactViewerHelper.addKeyAtCol(
this, gridBagLayout, constraints, Bundle.ContactArtifactViewer_missing_account_label(), 1);
 
  521                     constraints.insets = savedInsets;
 
  523                     CommunicationArtifactViewerHelper.addValueAtCol(
this, gridBagLayout, constraints, missingAccount.getIdentifier(), 2);
 
  529         constraints.insets = savedInsets;
 
  537         contactArtifact = null;
 
  539         datasourceName = null;
 
  541         contactUniqueAccountsList.clear();
 
  542         contactUniquePersonasMap.clear();
 
  544         phoneNumList.clear();
 
  548         accountAttributesList.clear();
 
  550         if (personaSearchTask != null) {
 
  551             personaSearchTask.cancel(Boolean.TRUE);
 
  552             personaSearchTask = null;
 
  557         this.setLayout(null);
 
  559         m_gridBagLayout = 
new GridBagLayout();
 
  560         m_constraints = 
new GridBagConstraints();
 
  562         m_constraints.anchor = GridBagConstraints.LINE_START;
 
  563         m_constraints.gridy = 0;
 
  564         m_constraints.gridx = 0;
 
  565         m_constraints.weighty = 0.0;
 
  566         m_constraints.weightx = 0.0;    
 
  568         m_constraints.fill = GridBagConstraints.NONE;
 
  581         ImageIcon imageIcon = defaultImage;
 
  583         if (artifact == null) {
 
  595                     AbstractFile file = (AbstractFile) content;
 
  599                         imageIcon = 
new ImageIcon(image);
 
  601                     } 
catch (IOException ex) {
 
  609             logger.log(Level.WARNING, String.format(
"Unable to load image for contact: %d", artifact.
getId()), ex);
 
  622         private final List<CentralRepoAccount> uniqueAccountsList = 
new ArrayList<>();
 
  631             this.artifact = artifact;
 
  635         protected Map<Persona, ArrayList<CentralRepoAccount>> 
doInBackground() throws Exception {
 
  637             Map<Persona, ArrayList<CentralRepoAccount>> uniquePersonas = 
new HashMap<>();
 
  641             for (
Account account : contactAccountsList) {
 
  644                         return new HashMap<>();
 
  650                         if (optCrAccountType.isPresent()) {
 
  653                             if (crAccount != null && uniqueAccountsList.contains(crAccount) == 
false) {
 
  654                                 uniqueAccountsList.add(crAccount);
 
  660                     if (personaAccounts != null && !personaAccounts.isEmpty()) {
 
  662                         Collection<Persona> personas
 
  666                                         .collect(Collectors.toList());
 
  669                         for (
Persona persona : personas) {
 
  670                             if (uniquePersonas.containsKey(persona) == 
false) {
 
  674                                         .collect(Collectors.toList());
 
  676                                 ArrayList<CentralRepoAccount> personaAccountsList = 
new ArrayList<>(accounts);
 
  677                                 uniquePersonas.put(persona, personaAccountsList);
 
  687             return uniquePersonas;
 
  693             Map<Persona, ArrayList<CentralRepoAccount>> personasMap;
 
  695                 personasMap = super.get();
 
  697                 if (this.isCancelled()) {
 
  701                 contactUniquePersonasMap.clear();
 
  702                 contactUniquePersonasMap.putAll(personasMap);
 
  703                 contactUniqueAccountsList.clear();
 
  704                 contactUniqueAccountsList.addAll(uniqueAccountsList);
 
  708             } 
catch (CancellationException ex) {
 
  709                 logger.log(Level.INFO, 
"Persona searching was canceled."); 
 
  710             } 
catch (InterruptedException ex) {
 
  711                 logger.log(Level.INFO, 
"Persona searching was interrupted."); 
 
  712             } 
catch (ExecutionException ex) {
 
  713                 logger.log(Level.SEVERE, 
"Fatal error during Persona search.", ex); 
 
  735             this.personaNameLabel = personaNameLabel;
 
  736             this.personaActionButton = personaActionButton;
 
  745             return personaNameLabel;
 
  754             return personaActionButton;
 
  772             this.personaUIComponents = personaUIComponents;
 
  773             this.parentComponent = parentComponent;
 
  777             "ContactArtifactViewer_persona_account_justification=Account found in Contact artifact",
 
  778             "# {0} - accountIdentifer",
 
  779             "ContactArtifactViewer_id_not_found_in_cr=Unable to find account(s) associated with contact {0} in the Central Repository." 
  791             if (contactName != null) {
 
  800             if (contactName != null && contactUniqueAccountsList.isEmpty()) {
 
  823             this.persona = persona;
 
  824             this.parentComponent = parentComponent;
 
  839         private final Component parentComponent;
 
  840         private final PersonaUIComponents personaUIComponents;
 
  847         PersonaCreateCallbackImpl(Component parentComponent, PersonaUIComponents personaUIComponents) {
 
  848             this.parentComponent = parentComponent;
 
  849             this.personaUIComponents = personaUIComponents;
 
  853         public void callback(Persona persona) {
 
  855             if (persona != null) {
 
  858                 personaUIComponents.getPersonaNameLabel().setText(persona.getName());
 
  859                 personaUIComponents.getPersonaActionButton().setText(Bundle.ContactArtifactViewer_persona_button_view());
 
  862                 for (ActionListener act : personaButton.getActionListeners()) {
 
  863                     personaButton.removeActionListener(act);
 
  865                 personaButton.addActionListener(
new ViewPersonaButtonListener(parentComponent, persona));
 
  869             personaButton.getParent().revalidate();
 
  870             personaButton.getParent().repaint();
 
  877     class PersonaViewCallbackImpl 
implements PersonaDetailsDialogCallback {
 
  880         public void callback(Persona persona) {
 
boolean addAccount(CentralRepoAccount account, String justification, Persona.Confidence confidence)
 
static ARTIFACT_TYPE fromID(int id)
 
Host getHostByDataSource(DataSource dataSource)
 
JButton getPersonaActionButton()
 
static Collection< PersonaAccount > getPersonaAccountsForAccount(long accountId)
 
CommunicationsManager getCommunicationsManager()
 
void actionPerformed(java.awt.event.ActionEvent evt)
 
static Integer getLineSpacing()
 
void setStartupPopupMessage(String message)
 
Collection< PersonaAccount > getPersonaAccounts()
 
void showPersona(Persona persona, int matchNumber, List< CentralRepoAccount > missingAccountsList, GridBagLayout gridBagLayout, GridBagConstraints constraints)
 
CentralRepoAccount getAccount(CentralRepoAccount.CentralRepoAccountType crAccountType, String accountUniqueID)
 
final ImageIcon defaultImage
 
final JButton personaActionButton
 
static Insets getPanelInsets()
 
void updateContactName(GridBagLayout contactPanelLayout, GridBagConstraints contactPanelConstraints)
 
void updateContactImage(GridBagLayout contactPanelLayout, GridBagConstraints contactPanelConstraints)
 
PersonaDetailsPanel getDetailsPanel()
 
List< Account > getAccountsRelatedToArtifact(BlackboardArtifact artifact)
 
void initiatePersonasSearch()
 
Map< Persona, ArrayList< CentralRepoAccount > > doInBackground()
 
final PersonaUIComponents personaUIComponents
 
JLabel personaSearchStatusLabel
 
static Integer getSectionIndent()
 
Optional< CentralRepoAccountType > getAccountTypeByName(String accountTypeName)
 
void updateContactDetails()
 
void updateContactMethodSection(List< BlackboardAttribute > sectionAttributesList, String sectionHeader, GridBagLayout contactPanelLayout, GridBagConstraints contactPanelConstraints)
 
void actionPerformed(java.awt.event.ActionEvent evt)
 
final BlackboardArtifact artifact
 
BlackboardArtifact contactArtifact
 
HostManager getHostManager()
 
SleuthkitCase getSleuthkitCase()
 
List< BlackboardAttribute > getAttributes()
 
List< Content > getChildren()
 
static int getColumnSpacing()
 
void setPersonaName(String name)
 
static Integer getSectionSpacing()
 
static Case getCurrentCase()
 
synchronized static Logger getLogger(String name)
 
final JLabel personaNameLabel
 
static Case getCurrentCaseThrows()
 
CentralRepoAccount getAccount()
 
static final Account.Type DEVICE
 
static Font getMessageFont()
 
final Component parentComponent
 
ContactPersonaSearcherTask personaSearchTask
 
boolean isSupported(BlackboardArtifact artifact)
 
static CentralRepository getInstance()
 
final Component parentComponent
 
ImageIcon getImageFromArtifact(BlackboardArtifact artifact)
 
static boolean isEnabled()
 
void setArtifact(BlackboardArtifact artifact)
 
JLabel getPersonaNameLabel()
 
void extractArtifactData(BlackboardArtifact artifact)