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;
68 import org.
sleuthkit.datamodel.InvalidAccountIDException;
74 @ServiceProvider(service = ArtifactContentViewer.class)
78 private static final long serialVersionUID = 1L;
80 private GridBagLayout m_gridBagLayout =
new GridBagLayout();
81 private GridBagConstraints m_constraints =
new GridBagConstraints();
89 private List<BlackboardAttribute> phoneNumList =
new ArrayList<>();
90 private List<BlackboardAttribute> emailList =
new ArrayList<>();
91 private List<BlackboardAttribute> nameList =
new ArrayList<>();
92 private List<BlackboardAttribute> otherList =
new ArrayList<>();
93 private List<BlackboardAttribute> accountAttributesList =
new ArrayList<>();
95 private final static String DEFAULT_IMAGE_PATH =
"/org/sleuthkit/autopsy/images/defaultContact.png";
99 private final List<CentralRepoAccount> contactUniqueAccountsList =
new ArrayList<>();
103 private final Map<Persona, ArrayList<CentralRepoAccount>> contactUniquePersonasMap =
new HashMap<>();
121 @SuppressWarnings(
"unchecked")
123 private
void initComponents() {
126 setLayout(
new java.awt.GridBagLayout());
134 if (artifact != null) {
136 extractArtifactData(artifact);
137 }
catch (TskCoreException ex) {
138 logger.log(Level.SEVERE, String.format(
"Error getting attributes for artifact (artifact_id=%d, obj_id=%d)", artifact.getArtifactID(), artifact.getObjectID()), ex);
143 this.setLayout(this.m_gridBagLayout);
151 return new JScrollPane(
this, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
156 return (artifact != null)
157 && (artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT.getTypeID());
169 this.contactArtifact = artifact;
171 phoneNumList =
new ArrayList<>();
172 emailList =
new ArrayList<>();
173 nameList =
new ArrayList<>();
174 otherList =
new ArrayList<>();
175 accountAttributesList =
new ArrayList<>();
178 for (BlackboardAttribute bba : contactArtifact.getAttributes()) {
179 if (bba.getAttributeType().getTypeName().startsWith(
"TSK_PHONE")) {
180 phoneNumList.add(bba);
181 accountAttributesList.add(bba);
182 }
else if (bba.getAttributeType().getTypeName().startsWith(
"TSK_EMAIL")) {
184 accountAttributesList.add(bba);
185 }
else if (bba.getAttributeType().getTypeName().startsWith(
"TSK_NAME")) {
189 if (bba.getAttributeType().getTypeName().equalsIgnoreCase(
"TSK_ID")) {
190 accountAttributesList.add(bba);
195 datasourceName = contactArtifact.getDataSource().getName();
204 updateContactDetails();
210 initiatePersonasSearch();
218 "ContactArtifactViewer_phones_header=Phone",
219 "ContactArtifactViewer_emails_header=Email",
220 "ContactArtifactViewer_others_header=Other",})
224 updateContactImage(m_gridBagLayout, m_constraints);
225 updateContactName(m_gridBagLayout, m_constraints);
228 updateContactMethodSection(phoneNumList, Bundle.ContactArtifactViewer_phones_header(), m_gridBagLayout, m_constraints);
229 updateContactMethodSection(emailList, Bundle.ContactArtifactViewer_emails_header(), m_gridBagLayout, m_constraints);
230 updateContactMethodSection(otherList, Bundle.ContactArtifactViewer_others_header(), m_gridBagLayout, m_constraints);
241 "ContactArtifactViewer.contactImage.text=",})
242 private void updateContactImage(GridBagLayout contactPanelLayout, GridBagConstraints contactPanelConstraints) {
244 Insets savedInsets = contactPanelConstraints.insets;
245 contactPanelConstraints.gridy = 0;
246 contactPanelConstraints.gridx = 0;
248 int prevGridWidth = contactPanelConstraints.gridwidth;
249 contactPanelConstraints.gridwidth = 3;
250 contactPanelConstraints.anchor = GridBagConstraints.LINE_START;
252 javax.swing.JLabel contactImage =
new javax.swing.JLabel();
253 contactImage.setIcon(getImageFromArtifact(contactArtifact));
254 contactImage.setText(Bundle.ContactArtifactViewer_contactImage_text());
257 CommunicationArtifactViewerHelper.addComponent(
this, contactPanelLayout, contactPanelConstraints, contactImage);
258 CommunicationArtifactViewerHelper.addLineEndGlue(
this, contactPanelLayout, contactPanelConstraints);
259 contactPanelConstraints.gridy++;
261 contactPanelConstraints.gridwidth = prevGridWidth;
262 contactPanelConstraints.insets = savedInsets;
273 "ContactArtifactViewer_contactname_unknown=Unknown",})
274 private void updateContactName(GridBagLayout contactPanelLayout, GridBagConstraints contactPanelConstraints) {
276 boolean foundName =
false;
277 for (BlackboardAttribute bba : this.nameList) {
278 if (StringUtils.isEmpty(bba.getValueString()) ==
false) {
279 contactName = bba.getDisplayString();
281 CommunicationArtifactViewerHelper.addHeader(
this, contactPanelLayout, contactPanelConstraints, 0, contactName);
286 if (foundName ==
false) {
287 CommunicationArtifactViewerHelper.addHeader(
this, contactPanelLayout, contactPanelConstraints,
ContentViewerDefaults.
getSectionSpacing(), Bundle.ContactArtifactViewer_contactname_unknown());
301 private void updateContactMethodSection(List<BlackboardAttribute> sectionAttributesList, String sectionHeader, GridBagLayout contactPanelLayout, GridBagConstraints contactPanelConstraints) {
304 if (sectionAttributesList.isEmpty()) {
309 for (BlackboardAttribute bba : sectionAttributesList) {
310 CommunicationArtifactViewerHelper.addKey(
this, contactPanelLayout, contactPanelConstraints, bba.getAttributeType().getDisplayName());
311 CommunicationArtifactViewerHelper.addValue(
this, contactPanelLayout, contactPanelConstraints, bba.getDisplayString());
319 "ContactArtifactViewer_heading_Source=Source",
320 "ContactArtifactViewer_label_datasource=Data Source",})
323 CommunicationArtifactViewerHelper.addKey(
this, m_gridBagLayout, m_constraints, Bundle.ContactArtifactViewer_label_datasource());
324 CommunicationArtifactViewerHelper.addValue(
this, m_gridBagLayout, m_constraints, datasourceName);
333 "ContactArtifactViewer_persona_header=Persona",
334 "ContactArtifactViewer_persona_searching=Searching...",
335 "ContactArtifactViewer_cr_disabled_message=Enable Central Repository to view, create and edit personas.",
336 "ContactArtifactViewer_persona_unknown=Unknown"
341 JLabel personaHeader = CommunicationArtifactViewerHelper.addHeader(
this, m_gridBagLayout, m_constraints,
ContentViewerDefaults.
getSectionSpacing(), Bundle.ContactArtifactViewer_persona_header());
343 m_constraints.gridy++;
347 ? Bundle.ContactArtifactViewer_persona_searching()
348 : Bundle.ContactArtifactViewer_persona_unknown();
350 this.personaSearchStatusLabel =
new javax.swing.JLabel();
351 personaSearchStatusLabel.setText(personaStatusLabelText);
354 m_constraints.gridx = 0;
355 m_constraints.anchor = GridBagConstraints.LINE_START;
357 CommunicationArtifactViewerHelper.addComponent(
this, m_gridBagLayout, m_constraints, personaSearchStatusLabel);
362 personaSearchTask.execute();
364 personaHeader.setEnabled(
false);
365 personaSearchStatusLabel.setEnabled(
false);
368 CommunicationArtifactViewerHelper.addMessageRow(
this, m_gridBagLayout, messageInsets, m_constraints, Bundle.ContactArtifactViewer_cr_disabled_message());
369 m_constraints.gridy++;
371 CommunicationArtifactViewerHelper.addPageEndGlue(
this, m_gridBagLayout, this.m_constraints);
382 this.
remove(personaSearchStatusLabel);
384 m_constraints.gridx = 0;
385 if (contactUniquePersonasMap.isEmpty()) {
387 showPersona(null, 0, Collections.emptyList(), this.m_gridBagLayout, this.m_constraints);
389 int matchCounter = 0;
390 for (Map.Entry<
Persona, ArrayList<CentralRepoAccount>> entry : contactUniquePersonasMap.entrySet()) {
391 List<CentralRepoAccount> missingAccounts =
new ArrayList<>();
392 ArrayList<CentralRepoAccount> personaAccounts = entry.getValue();
397 if (personaAccounts.contains(account) ==
false) {
398 missingAccounts.add(account);
402 showPersona(entry.getKey(), matchCounter, missingAccounts, m_gridBagLayout, m_constraints);
403 m_constraints.gridy += 2;
408 CommunicationArtifactViewerHelper.addPageEndGlue(
this, m_gridBagLayout, this.m_constraints);
411 this.setLayout(this.m_gridBagLayout);
429 "ContactArtifactViewer_persona_label=Persona ",
430 "ContactArtifactViewer_persona_no_match=No matches found",
431 "ContactArtifactViewer_persona_button_view=View",
432 "ContactArtifactViewer_persona_button_new=Create",
433 "ContactArtifactViewer_persona_match_num=Match ",
434 "ContactArtifactViewer_missing_account_label=Missing contact account",
435 "ContactArtifactViewer_found_all_accounts_label=All accounts found."
437 private void showPersona(
Persona persona,
int matchNumber, List<CentralRepoAccount> missingAccountsList, GridBagLayout gridBagLayout, GridBagConstraints constraints) {
440 Insets savedInsets = constraints.insets;
443 constraints.gridx = 0;
444 javax.swing.JLabel matchNumberLabel = CommunicationArtifactViewerHelper.addKey(
this, gridBagLayout, constraints, String.format(
"%s %d", Bundle.ContactArtifactViewer_persona_match_num(), matchNumber).trim());
446 javax.swing.JLabel personaNameLabel =
new javax.swing.JLabel();
447 javax.swing.JButton personaButton =
new javax.swing.JButton();
450 String personaButtonText;
451 ActionListener personaButtonListener;
452 if (persona != null) {
453 personaName = persona.
getName();
454 personaButtonText = Bundle.ContactArtifactViewer_persona_button_view();
457 matchNumberLabel.setVisible(
false);
458 personaName = Bundle.ContactArtifactViewer_persona_no_match();
459 personaButtonText = Bundle.ContactArtifactViewer_persona_button_new();
466 constraints.anchor = GridBagConstraints.LINE_START;
467 personaNameLabel.setText(personaName);
468 gridBagLayout.setConstraints(personaNameLabel, constraints);
469 CommunicationArtifactViewerHelper.addComponent(
this, gridBagLayout, constraints, personaNameLabel);
475 personaButton.setText(personaButtonText);
476 personaButton.addActionListener(personaButtonListener);
479 personaButton.setMargin(
new Insets(0, 5, 0, 5));
481 constraints.anchor = GridBagConstraints.LINE_START;
482 gridBagLayout.setConstraints(personaButton, constraints);
483 CommunicationArtifactViewerHelper.addComponent(
this, gridBagLayout, constraints, personaButton);
484 CommunicationArtifactViewerHelper.addLineEndGlue(
this, gridBagLayout, constraints);
486 constraints.insets = savedInsets;
489 if (persona != null) {
490 if (missingAccountsList.isEmpty()) {
492 constraints.gridx = 1;
495 javax.swing.JLabel accountsStatus =
new javax.swing.JLabel(Bundle.ContactArtifactViewer_found_all_accounts_label());
497 constraints.anchor = GridBagConstraints.LINE_START;
498 CommunicationArtifactViewerHelper.addComponent(
this, gridBagLayout, constraints, accountsStatus);
499 constraints.insets = savedInsets;
501 CommunicationArtifactViewerHelper.addLineEndGlue(
this, gridBagLayout, constraints);
506 constraints.gridx = 0;
510 CommunicationArtifactViewerHelper.addKeyAtCol(
this, gridBagLayout, constraints, Bundle.ContactArtifactViewer_missing_account_label(), 1);
511 constraints.insets = savedInsets;
513 CommunicationArtifactViewerHelper.addValueAtCol(
this, gridBagLayout, constraints, missingAccount.getIdentifier(), 2);
519 constraints.insets = savedInsets;
527 contactArtifact = null;
529 datasourceName = null;
531 contactUniqueAccountsList.clear();
532 contactUniquePersonasMap.clear();
534 phoneNumList.clear();
538 accountAttributesList.clear();
540 if (personaSearchTask != null) {
541 personaSearchTask.cancel(Boolean.TRUE);
542 personaSearchTask = null;
547 this.setLayout(null);
549 m_gridBagLayout =
new GridBagLayout();
550 m_constraints =
new GridBagConstraints();
552 m_constraints.anchor = GridBagConstraints.LINE_START;
553 m_constraints.gridy = 0;
554 m_constraints.gridx = 0;
555 m_constraints.weighty = 0.0;
556 m_constraints.weightx = 0.0;
558 m_constraints.fill = GridBagConstraints.NONE;
571 ImageIcon imageIcon = defaultImage;
573 if (artifact == null) {
577 BlackboardArtifact.ARTIFACT_TYPE artifactType = BlackboardArtifact.ARTIFACT_TYPE.fromID(artifact.getArtifactTypeID());
578 if (artifactType != BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT) {
583 for (Content content : artifact.getChildren()) {
584 if (content instanceof AbstractFile) {
585 AbstractFile file = (AbstractFile) content;
588 BufferedImage image = ImageIO.read(
new File(file.getLocalAbsPath()));
589 imageIcon =
new ImageIcon(image);
591 }
catch (IOException ex) {
598 }
catch (TskCoreException ex) {
599 logger.log(Level.WARNING, String.format(
"Unable to load image for contact: %d", artifact.getId()), ex);
612 private final List<CentralRepoAccount> uniqueAccountsList =
new ArrayList<>();
621 this.artifact = artifact;
625 protected Map<Persona, ArrayList<CentralRepoAccount>>
doInBackground() throws Exception {
627 Map<Persona, ArrayList<CentralRepoAccount>> uniquePersonas =
new HashMap<>();
629 List<Account> contactAccountsList = commManager.getAccountsRelatedToArtifact(artifact);
631 for (Account account : contactAccountsList) {
634 return new HashMap<>();
638 if (!account.getAccountType().equals(Account.Type.DEVICE)) {
640 if (optCrAccountType.isPresent()) {
643 if (crAccount != null && uniqueAccountsList.contains(crAccount) ==
false) {
644 uniqueAccountsList.add(crAccount);
650 if (personaAccounts != null && !personaAccounts.isEmpty()) {
652 Collection<Persona> personas
656 .collect(Collectors.toList());
659 for (
Persona persona : personas) {
660 if (uniquePersonas.containsKey(persona) ==
false) {
664 .collect(Collectors.toList());
666 ArrayList<CentralRepoAccount> personaAccountsList =
new ArrayList<>(accounts);
667 uniquePersonas.put(persona, personaAccountsList);
671 }
catch (InvalidAccountIDException ex) {
677 return uniquePersonas;
683 Map<Persona, ArrayList<CentralRepoAccount>> personasMap;
685 personasMap = super.get();
687 if (this.isCancelled()) {
691 contactUniquePersonasMap.clear();
692 contactUniquePersonasMap.putAll(personasMap);
693 contactUniqueAccountsList.clear();
694 contactUniqueAccountsList.addAll(uniqueAccountsList);
698 }
catch (CancellationException ex) {
699 logger.log(Level.INFO,
"Persona searching was canceled.");
700 }
catch (InterruptedException ex) {
701 logger.log(Level.INFO,
"Persona searching was interrupted.");
702 }
catch (ExecutionException ex) {
703 logger.log(Level.SEVERE,
"Fatal error during Persona search.", ex);
725 this.personaNameLabel = personaNameLabel;
726 this.personaActionButton = personaActionButton;
735 return personaNameLabel;
744 return personaActionButton;
762 this.personaUIComponents = personaUIComponents;
763 this.parentComponent = parentComponent;
767 "ContactArtifactViewer_persona_account_justification=Account found in Contact artifact",
768 "# {0} - accountIdentifer",
769 "ContactArtifactViewer_id_not_found_in_cr=Unable to find account(s) associated with contact {0} in the Central Repository."
781 if (contactName != null) {
790 if (contactName != null && contactUniqueAccountsList.isEmpty()) {
813 this.persona = persona;
814 this.parentComponent = parentComponent;
829 private final Component parentComponent;
830 private final PersonaUIComponents personaUIComponents;
837 PersonaCreateCallbackImpl(Component parentComponent, PersonaUIComponents personaUIComponents) {
838 this.parentComponent = parentComponent;
839 this.personaUIComponents = personaUIComponents;
843 public void callback(Persona persona) {
845 if (persona != null) {
848 personaUIComponents.getPersonaNameLabel().setText(persona.getName());
849 personaUIComponents.getPersonaActionButton().setText(Bundle.ContactArtifactViewer_persona_button_view());
852 for (ActionListener act : personaButton.getActionListeners()) {
853 personaButton.removeActionListener(act);
855 personaButton.addActionListener(
new ViewPersonaButtonListener(parentComponent, persona));
859 personaButton.getParent().revalidate();
860 personaButton.getParent().repaint();
867 class PersonaViewCallbackImpl
implements PersonaDetailsDialogCallback {
870 public void callback(Persona persona) {
boolean addAccount(CentralRepoAccount account, String justification, Persona.Confidence confidence)
JButton getPersonaActionButton()
static Collection< PersonaAccount > getPersonaAccountsForAccount(long accountId)
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()
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
SleuthkitCase getSleuthkitCase()
static int getColumnSpacing()
void setPersonaName(String name)
static Integer getSectionSpacing()
static Case getCurrentCase()
synchronized static Logger getLogger(String name)
final JLabel personaNameLabel
CentralRepoAccount getAccount()
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)