19package org.sleuthkit.autopsy.contentviewers.artifactviewers;
21import java.awt.Component;
22import java.awt.GridBagConstraints;
23import java.awt.GridBagLayout;
24import java.awt.Insets;
25import java.awt.event.ActionListener;
26import java.awt.image.BufferedImage;
28import java.io.IOException;
29import java.util.ArrayList;
30import java.util.Collection;
31import java.util.Collections;
32import java.util.HashMap;
35import java.util.Optional;
36import java.util.concurrent.CancellationException;
37import java.util.concurrent.ExecutionException;
38import java.util.logging.Level;
39import java.util.stream.Collectors;
40import javax.imageio.ImageIO;
41import javax.swing.ImageIcon;
42import javax.swing.JButton;
43import javax.swing.JLabel;
44import javax.swing.JScrollPane;
45import javax.swing.SwingWorker;
46import javax.swing.border.EmptyBorder;
47import org.apache.commons.lang.StringUtils;
48import org.openide.util.NbBundle;
49import org.openide.util.lookup.ServiceProvider;
50import org.sleuthkit.autopsy.casemodule.Case;
51import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
52import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount;
53import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
54import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
55import org.sleuthkit.autopsy.centralrepository.datamodel.Persona;
56import org.sleuthkit.autopsy.centralrepository.datamodel.PersonaAccount;
57import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsDialog;
58import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsDialogCallback;
59import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsMode;
60import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsPanel;
61import org.sleuthkit.autopsy.contentviewers.layout.ContentViewerDefaults;
62import org.sleuthkit.autopsy.coreutils.Logger;
63import org.sleuthkit.datamodel.AbstractFile;
64import org.sleuthkit.datamodel.Account;
65import org.sleuthkit.datamodel.BlackboardArtifact;
66import org.sleuthkit.datamodel.BlackboardAttribute;
67import org.sleuthkit.datamodel.CommunicationsManager;
68import org.sleuthkit.datamodel.Content;
69import org.sleuthkit.datamodel.DataSource;
70import org.sleuthkit.datamodel.InvalidAccountIDException;
71import org.sleuthkit.datamodel.TskCoreException;
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<>();
98 private final static String
DEFAULT_IMAGE_PATH =
"/org/sleuthkit/autopsy/images/defaultContact.png";
124 @SuppressWarnings(
"unchecked")
129 setLayout(
new java.awt.GridBagLayout());
137 if (artifact !=
null) {
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)
160 && (artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT.getTypeID());
172 this.contactArtifact = artifact;
182 if (bba.getAttributeType().getTypeName().startsWith(
"TSK_PHONE")) {
185 }
else if (bba.getAttributeType().getTypeName().startsWith(
"TSK_EMAIL")) {
188 }
else if (bba.getAttributeType().getTypeName().startsWith(
"TSK_NAME")) {
192 if (bba.getAttributeType().getTypeName().equalsIgnoreCase(
"TSK_ID")) {
201 .map(h -> h.getName())
225 "ContactArtifactViewer_phones_header=Phone",
226 "ContactArtifactViewer_emails_header=Email",
227 "ContactArtifactViewer_others_header=Other",})
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();
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;
284 for (BlackboardAttribute bba : this.nameList) {
285 if (StringUtils.isEmpty(bba.getValueString()) ==
false) {
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()) {
316 for (BlackboardAttribute bba : sectionAttributesList) {
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",})
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"
357 ? Bundle.ContactArtifactViewer_persona_searching()
358 : Bundle.ContactArtifactViewer_persona_unknown();
360 this.personaSearchStatusLabel =
new javax.swing.JLabel();
364 m_constraints.gridx = 0;
365 m_constraints.anchor = GridBagConstraints.LINE_START;
374 personaHeader.setEnabled(
false);
378 CommunicationArtifactViewerHelper.addMessageRow(
this,
m_gridBagLayout, messageInsets,
m_constraints, Bundle.ContactArtifactViewer_cr_disabled_message());
381 CommunicationArtifactViewerHelper.addPageEndGlue(
this,
m_gridBagLayout, this.m_constraints);
392 this.
remove(personaSearchStatusLabel);
394 m_constraints.gridx = 0;
397 showPersona(
null, 0, Collections.emptyList(),
this.m_gridBagLayout,
this.m_constraints);
399 int matchCounter = 0;
401 List<CentralRepoAccount> missingAccounts =
new ArrayList<>();
402 ArrayList<CentralRepoAccount> personaAccounts = entry.getValue();
407 if (personaAccounts.contains(account) ==
false) {
408 missingAccounts.add(account);
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;
557 this.setLayout(
null);
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;
583 if (artifact ==
null) {
587 BlackboardArtifact.ARTIFACT_TYPE artifactType = BlackboardArtifact.ARTIFACT_TYPE.fromID(artifact.getArtifactTypeID());
588 if (artifactType != BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT) {
593 for (Content content : artifact.getChildren()) {
594 if (content instanceof AbstractFile) {
595 AbstractFile file = (AbstractFile) content;
598 BufferedImage image = ImageIO.read(
new File(file.getLocalAbsPath()));
599 imageIcon =
new ImageIcon(image);
601 }
catch (IOException ex) {
608 }
catch (TskCoreException ex) {
609 logger.log(Level.WARNING, String.format(
"Unable to load image for contact: %d", artifact.getId()), ex);
619 private class ContactPersonaSearcherTask
extends SwingWorker<Map<Persona, ArrayList<CentralRepoAccount>>, Void> {
630 ContactPersonaSearcherTask(BlackboardArtifact
artifact) {
635 protected Map<Persona, ArrayList<CentralRepoAccount>>
doInBackground() throws Exception {
637 Map<Persona, ArrayList<CentralRepoAccount>> uniquePersonas =
new HashMap<>();
639 List<Account> contactAccountsList = commManager.getAccountsRelatedToArtifact(
artifact);
641 for (Account account : contactAccountsList) {
644 return new HashMap<>();
648 if (!account.getAccountType().equals(Account.Type.DEVICE)) {
650 if (optCrAccountType.isPresent()) {
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);
681 }
catch (InvalidAccountIDException ex) {
687 return uniquePersonas;
693 Map<Persona, ArrayList<CentralRepoAccount>> personasMap;
695 personasMap = super.get();
697 if (this.isCancelled()) {
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);
723 private class PersonaUIComponents {
761 private class CreatePersonaButtonListener
implements ActionListener {
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."
812 private class ViewPersonaButtonListener
implements ActionListener {
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) {
SleuthkitCase getSleuthkitCase()
static Case getCurrentCaseThrows()
static Case getCurrentCase()
static Collection< PersonaAccount > getPersonaAccountsForAccount(long accountId)
CentralRepoAccount getAccount()
Collection< PersonaAccount > getPersonaAccounts()
PersonaDetailsPanel getDetailsPanel()
void setStartupPopupMessage(String message)
void setPersonaName(String name)
boolean addAccount(CentralRepoAccount account, String justification, Persona.Confidence confidence)
final Component parentComponent
void actionPerformed(java.awt.event.ActionEvent evt)
final BlackboardArtifact artifact
final List< CentralRepoAccount > uniqueAccountsList
Map< Persona, ArrayList< CentralRepoAccount > > doInBackground()
final PersonaUIComponents personaUIComponents
final Component parentComponent
void actionPerformed(java.awt.event.ActionEvent evt)
JLabel getPersonaNameLabel()
JButton getPersonaActionButton()
final JLabel personaNameLabel
final JButton personaActionButton
void setArtifact(BlackboardArtifact artifact)
void updateContactName(GridBagLayout contactPanelLayout, GridBagConstraints contactPanelConstraints)
final Map< Persona, ArrayList< CentralRepoAccount > > contactUniquePersonasMap
List< BlackboardAttribute > phoneNumList
void extractArtifactData(BlackboardArtifact artifact)
static final String DEFAULT_IMAGE_PATH
void showPersona(Persona persona, int matchNumber, List< CentralRepoAccount > missingAccountsList, GridBagLayout gridBagLayout, GridBagConstraints constraints)
GridBagConstraints m_constraints
final List< CentralRepoAccount > contactUniqueAccountsList
JLabel personaSearchStatusLabel
void updateContactMethodSection(List< BlackboardAttribute > sectionAttributesList, String sectionHeader, GridBagLayout contactPanelLayout, GridBagConstraints contactPanelConstraints)
List< BlackboardAttribute > emailList
static final Logger logger
boolean isSupported(BlackboardArtifact artifact)
void updateContactDetails()
List< BlackboardAttribute > otherList
GridBagLayout m_gridBagLayout
List< BlackboardAttribute > nameList
final ImageIcon defaultImage
ContactPersonaSearcherTask personaSearchTask
void initiatePersonasSearch()
ImageIcon getImageFromArtifact(BlackboardArtifact artifact)
List< BlackboardAttribute > accountAttributesList
BlackboardArtifact contactArtifact
static final long serialVersionUID
void updateContactImage(GridBagLayout contactPanelLayout, GridBagConstraints contactPanelConstraints)
static Insets getPanelInsets()
static int getColumnSpacing()
static Integer getSectionIndent()
static Integer getSectionSpacing()
static Font getMessageFont()
static Integer getLineSpacing()
synchronized static Logger getLogger(String name)
static CentralRepository getInstance()
Optional< CentralRepoAccountType > getAccountTypeByName(String accountTypeName)
CentralRepoAccount getAccount(CentralRepoAccount.CentralRepoAccountType crAccountType, String accountUniqueID)
static boolean isEnabled()