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;
70 import org.
sleuthkit.datamodel.InvalidAccountIDException;
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)
160 && (artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT.getTypeID());
172 this.contactArtifact = artifact;
174 phoneNumList =
new ArrayList<>();
175 emailList =
new ArrayList<>();
176 nameList =
new ArrayList<>();
177 otherList =
new ArrayList<>();
178 accountAttributesList =
new ArrayList<>();
181 for (BlackboardAttribute bba : contactArtifact.getAttributes()) {
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);
198 datasourceName = contactArtifact.getDataSource().getName();
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;
284 for (BlackboardAttribute bba : this.nameList) {
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()) {
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",})
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) {
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);
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<>();
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()) {
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);
681 }
catch (InvalidAccountIDException ex) {
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)
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
static Case getCurrentCaseThrows()
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)