Autopsy  4.20.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
MessageViewer.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2019 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.communications.relationships;
20 
21 import java.awt.CardLayout;
22 import java.awt.Component;
23 import java.awt.Graphics2D;
24 import java.awt.Image;
25 import java.awt.KeyboardFocusManager;
26 import java.awt.RenderingHints;
27 import java.awt.event.ActionEvent;
28 import java.awt.image.BufferedImage;
29 import java.beans.PropertyChangeEvent;
30 import java.beans.PropertyChangeListener;
31 import java.beans.PropertyVetoException;
32 import java.lang.reflect.InvocationTargetException;
33 import java.util.ArrayList;
34 import java.util.logging.Level;
35 import javax.swing.AbstractAction;
36 import javax.swing.ImageIcon;
37 import javax.swing.JPanel;
38 import javax.swing.ListSelectionModel;
39 import javax.swing.SwingUtilities;
40 import static javax.swing.SwingUtilities.isDescendingFrom;
41 import javax.swing.event.TableModelEvent;
42 import javax.swing.event.TableModelListener;
43 import org.netbeans.swing.outline.DefaultOutlineModel;
44 import org.netbeans.swing.outline.Outline;
45 import org.openide.explorer.ExplorerManager;
46 import static org.openide.explorer.ExplorerUtils.createLookup;
47 import org.openide.nodes.AbstractNode;
48 import org.openide.nodes.Children;
49 import org.openide.nodes.Node;
50 import org.openide.nodes.Node.Property;
51 import org.openide.nodes.Node.PropertySet;
52 import org.openide.util.Exceptions;
53 import org.openide.util.Lookup;
54 import org.openide.util.NbBundle.Messages;
58 
63 @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
64 final class MessageViewer extends JPanel implements RelationshipsViewer {
65 
66  private static final Logger logger = Logger.getLogger(MessageViewer.class.getName());
67  private static final long serialVersionUID = 1L;
68 
69  private final ModifiableProxyLookup proxyLookup;
70  private PropertyChangeListener focusPropertyListener;
71  private final ThreadChildNodeFactory rootMessageFactory;
72  private final MessagesChildNodeFactory threadMessageNodeFactory;
73 
74  private SelectionInfo currentSelectionInfo = null;
75 
76  private OutlineViewPanel currentPanel;
77 
78  @Messages({
79  "MessageViewer_tabTitle=Messages",
80  "MessageViewer_columnHeader_From=From",
81  "MessageViewer_columnHeader_Date=Date",
82  "MessageViewer_columnHeader_To=To",
83  "MessageViewer_columnHeader_EarlyDate=Earliest Message",
84  "MessageViewer_columnHeader_Subject=Subject",
85  "MessageViewer_columnHeader_Attms=Attachments",
86  "MessageViewer_no_messages=<No messages found for selected account>",
87  "MessageViewer_viewMessage_all=All",
88  "MessageViewer_viewMessage_selected=Selected",
89  "MessageViewer_viewMessage_unthreaded=Unthreaded",
90  "MessageViewer_viewMessage_calllogs=Call Logs"})
91 
95  MessageViewer() {
96 
97  initComponents();
98  currentPanel = rootTablePane;
99  proxyLookup = new ModifiableProxyLookup(createLookup(rootTablePane.getExplorerManager(), getActionMap()));
100  rootMessageFactory = new ThreadChildNodeFactory(new ShowThreadMessagesAction());
101  threadMessageNodeFactory = new MessagesChildNodeFactory();
102 
103  rootTablePane.getExplorerManager().setRootContext(
104  new AbstractNode(Children.create(rootMessageFactory, true)));
105 
106  rootTablePane.getOutlineView().setPopupAllowed(false);
107 
108  Outline outline = rootTablePane.getOutlineView().getOutline();
109  rootTablePane.getOutlineView().setPropertyColumns(
110  "Date", Bundle.MessageViewer_columnHeader_EarlyDate(),
111  "Subject", Bundle.MessageViewer_columnHeader_Subject()
112  );
113  outline.setRootVisible(false);
114  ((DefaultOutlineModel) outline.getOutlineModel()).setNodesColumnLabel("Type");
115  outline.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
116 
117  rootTablePane.getExplorerManager().addPropertyChangeListener((PropertyChangeEvent evt) -> {
118  if (evt.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) {
119  showSelectedThread();
120  }
121  });
122 
123  rootTablePane.getOutlineView().getOutline().getModel().addTableModelListener(new TableModelListener() {
124  @Override
125  public void tableChanged(TableModelEvent e) {
126  Utils.setColumnWidths(rootTablePane.getOutlineView().getOutline());
127  }
128  });
129 
130  threadMessagesPanel.setChildFactory(threadMessageNodeFactory);
131 
132  rootTablePane.setTableColumnsWidth(10, 20, 70);
133 
134  Image image = getScaledImage((new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/timeline/images/arrow-180.png"))).getImage(), 16, 16);
135  backButton.setIcon(new ImageIcon(image));
136  }
137 
138  @Override
139  public String getDisplayName() {
140  return Bundle.MessageViewer_tabTitle();
141  }
142 
143  @Override
144  public JPanel getPanel() {
145  return this;
146  }
147 
148  @Override
149  public void setSelectionInfo(SelectionInfo info) {
150  if(currentSelectionInfo != null && currentSelectionInfo.equals(info)) {
151  try {
152  // Clear the currently selected thread so that clicks can
153  // be registered.
154  rootTablePane.getExplorerManager().setSelectedNodes(new Node[0]);
155  } catch (PropertyVetoException ex) {
156  logger.log(Level.WARNING, "Error clearing the selected node", ex);
157  }
158  } else {
159  currentSelectionInfo = info;
160  rootMessageFactory.refresh(info);
161  }
162 
163  currentPanel = rootTablePane;
164 
165  CardLayout layout = (CardLayout) this.getLayout();
166  layout.show(this, "threads");
167  }
168 
169  @Override
170  public Lookup getLookup() {
171  return proxyLookup;
172  }
173 
174  @Override
175  public void addNotify() {
176  super.addNotify();
177 
178  if (focusPropertyListener == null) {
179  // See org.sleuthkit.autopsy.timeline.TimeLineTopComponent for a detailed
180  // explaination of focusPropertyListener
181  focusPropertyListener = (final PropertyChangeEvent focusEvent) -> {
182  if (focusEvent.getPropertyName().equalsIgnoreCase("focusOwner")) {
183  handleFocusChange((Component) focusEvent.getNewValue());
184  }
185  };
186 
187  }
188  //add listener that maintains correct selection in the Global Actions Context
189  KeyboardFocusManager.getCurrentKeyboardFocusManager()
190  .addPropertyChangeListener("focusOwner", focusPropertyListener);
191  }
192 
198  private void handleFocusChange(Component newFocusOwner) {
199  if (newFocusOwner == null) {
200  return;
201  }
202  if (isDescendingFrom(newFocusOwner, rootTablePane)) {
203  proxyLookup.setNewLookups(createLookup(rootTablePane.getExplorerManager(), getActionMap()));
204  } else if (isDescendingFrom(newFocusOwner, this)) {
205  proxyLookup.setNewLookups(createLookup(threadMessagesPanel.getExplorerManager(), getActionMap()));
206  }
207  }
208 
209  @Override
210  public void removeNotify() {
211  super.removeNotify();
212  KeyboardFocusManager.getCurrentKeyboardFocusManager()
213  .removePropertyChangeListener("focusOwner", focusPropertyListener);
214  }
215 
216  @SuppressWarnings("rawtypes")
217  private void showSelectedThread() {
218  final Node[] nodes = rootTablePane.getExplorerManager().getSelectedNodes();
219 
220  if (nodes == null) {
221  return;
222  }
223 
224  if (nodes.length == 0 || nodes.length > 1) {
225  return;
226  }
227 
228  ArrayList<String> threadIDList = new ArrayList<>();
229  String subject = "";
230 
231  PropertySet[] propertySets = nodes[0].getPropertySets();
232  for (PropertySet pset : propertySets) {
233  Property[] properties = pset.getProperties();
234  for (Property prop : properties) {
235  if (prop.getName().equalsIgnoreCase("threadid")) {
236  try {
237  String threadID = prop.getValue().toString();
238  if (!threadIDList.contains(threadID)) {
239  threadIDList.add(threadID);
240  }
241  } catch (IllegalAccessException | InvocationTargetException ex) {
242  logger.log(Level.WARNING, String.format("Unable to get threadid for node: %s", nodes[0].getDisplayName()), ex);
243  }
244  } else if (prop.getName().equalsIgnoreCase("subject")) {
245  try {
246  subject = prop.getValue().toString();
247  } catch (IllegalAccessException | InvocationTargetException ex) {
248  logger.log(Level.WARNING, String.format("Unable to get subject for node: %s", nodes[0].getDisplayName()), ex);
249  subject = "<unavailable>";
250  }
251  }
252  }
253 
254  }
255 
256  if (!threadIDList.isEmpty()) {
257  threadMessageNodeFactory.refresh(currentSelectionInfo, threadIDList);
258 
259  if (!subject.isEmpty()) {
260  threadNameLabel.setText(subject);
261  } else {
262  threadNameLabel.setText(Bundle.MessageViewer_viewMessage_unthreaded());
263  }
264 
265  showMessagesPane();
266  }
267  }
268 
272  private void showThreadsPane() {
273  switchCard("threads");
274  }
275 
279  private void showMessagesPane() {
280  switchCard("messages");
281  }
282 
288  private void switchCard(String cardName) {
289  SwingUtilities.invokeLater(new Runnable() {
290  @Override
291  public void run() {
292  CardLayout layout = (CardLayout) getLayout();
293  layout.show(MessageViewer.this, cardName);
294  }
295  });
296  }
297 
307  private Image getScaledImage(Image srcImg, int w, int h) {
308  BufferedImage resizedImg = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
309  Graphics2D g2 = resizedImg.createGraphics();
310 
311  g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
312  g2.drawImage(srcImg, 0, 0, w, h, null);
313  g2.dispose();
314 
315  return resizedImg;
316  }
317 
323  @SuppressWarnings("unchecked")
324  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
325  private void initComponents() {
326  java.awt.GridBagConstraints gridBagConstraints;
327 
328  rootMessagesPane = new javax.swing.JPanel();
329  threadsLabel = new javax.swing.JLabel();
330  showAllButton = new javax.swing.JButton();
332  messagePanel = new javax.swing.JPanel();
333  threadMessagesPanel = new MessagesPanel();
334  backButton = new javax.swing.JButton();
335  showingMessagesLabel = new javax.swing.JLabel();
336  threadNameLabel = new javax.swing.JLabel();
337 
338  setMinimumSize(new java.awt.Dimension(450, 292));
339  setName(""); // NOI18N
340  setLayout(new java.awt.CardLayout());
341 
342  rootMessagesPane.setOpaque(false);
343  rootMessagesPane.setLayout(new java.awt.GridBagLayout());
344 
345  org.openide.awt.Mnemonics.setLocalizedText(threadsLabel, org.openide.util.NbBundle.getMessage(MessageViewer.class, "MessageViewer.threadsLabel.text")); // NOI18N
346  gridBagConstraints = new java.awt.GridBagConstraints();
347  gridBagConstraints.gridx = 0;
348  gridBagConstraints.gridy = 0;
349  gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
350  gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
351  gridBagConstraints.weightx = 1.0;
352  gridBagConstraints.insets = new java.awt.Insets(15, 15, 9, 0);
353  rootMessagesPane.add(threadsLabel, gridBagConstraints);
354 
355  org.openide.awt.Mnemonics.setLocalizedText(showAllButton, org.openide.util.NbBundle.getMessage(MessageViewer.class, "MessageViewer.showAllButton.text")); // NOI18N
356  showAllButton.addActionListener(new java.awt.event.ActionListener() {
357  public void actionPerformed(java.awt.event.ActionEvent evt) {
358  showAllButtonActionPerformed(evt);
359  }
360  });
361  gridBagConstraints = new java.awt.GridBagConstraints();
362  gridBagConstraints.gridx = 0;
363  gridBagConstraints.gridy = 2;
364  gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
365  gridBagConstraints.insets = new java.awt.Insets(0, 15, 15, 0);
366  rootMessagesPane.add(showAllButton, gridBagConstraints);
367 
368  rootTablePane.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0)));
369  gridBagConstraints = new java.awt.GridBagConstraints();
370  gridBagConstraints.gridx = 0;
371  gridBagConstraints.gridy = 1;
372  gridBagConstraints.gridwidth = 2;
373  gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
374  gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
375  gridBagConstraints.weightx = 1.0;
376  gridBagConstraints.weighty = 1.0;
377  gridBagConstraints.insets = new java.awt.Insets(0, 15, 9, 15);
378  rootMessagesPane.add(rootTablePane, gridBagConstraints);
379 
380  add(rootMessagesPane, "threads");
381 
382  messagePanel.setMinimumSize(new java.awt.Dimension(450, 292));
383  messagePanel.setLayout(new java.awt.GridBagLayout());
384  gridBagConstraints = new java.awt.GridBagConstraints();
385  gridBagConstraints.gridx = 0;
386  gridBagConstraints.gridy = 3;
387  gridBagConstraints.gridwidth = 3;
388  gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
389  gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
390  gridBagConstraints.weightx = 1.0;
391  gridBagConstraints.weighty = 1.0;
392  gridBagConstraints.insets = new java.awt.Insets(0, 15, 0, 15);
393  messagePanel.add(threadMessagesPanel, gridBagConstraints);
394 
395  org.openide.awt.Mnemonics.setLocalizedText(backButton, org.openide.util.NbBundle.getMessage(MessageViewer.class, "MessageViewer.backButton.text")); // NOI18N
396  backButton.addActionListener(new java.awt.event.ActionListener() {
397  public void actionPerformed(java.awt.event.ActionEvent evt) {
398  backButtonActionPerformed(evt);
399  }
400  });
401  gridBagConstraints = new java.awt.GridBagConstraints();
402  gridBagConstraints.gridx = 2;
403  gridBagConstraints.gridy = 0;
404  gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
405  gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
406  gridBagConstraints.insets = new java.awt.Insets(9, 0, 9, 15);
407  messagePanel.add(backButton, gridBagConstraints);
408  backButton.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(MessageViewer.class, "MessageViewer.backButton.AccessibleContext.accessibleDescription")); // NOI18N
409 
410  org.openide.awt.Mnemonics.setLocalizedText(showingMessagesLabel, org.openide.util.NbBundle.getMessage(MessageViewer.class, "MessageViewer.showingMessagesLabel.text")); // NOI18N
411  gridBagConstraints = new java.awt.GridBagConstraints();
412  gridBagConstraints.gridx = 0;
413  gridBagConstraints.gridy = 0;
414  gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
415  gridBagConstraints.insets = new java.awt.Insets(9, 15, 5, 0);
416  messagePanel.add(showingMessagesLabel, gridBagConstraints);
417 
418  org.openide.awt.Mnemonics.setLocalizedText(threadNameLabel, org.openide.util.NbBundle.getMessage(MessageViewer.class, "MessageViewer.threadNameLabel.text")); // NOI18N
419  gridBagConstraints = new java.awt.GridBagConstraints();
420  gridBagConstraints.gridx = 1;
421  gridBagConstraints.gridy = 0;
422  gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
423  gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
424  gridBagConstraints.weightx = 1.0;
425  gridBagConstraints.insets = new java.awt.Insets(9, 5, 5, 15);
426  messagePanel.add(threadNameLabel, gridBagConstraints);
427 
428  add(messagePanel, "messages");
429  }// </editor-fold>//GEN-END:initComponents
430 
431  private void backButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_backButtonActionPerformed
432  try {
433  rootTablePane.getExplorerManager().setSelectedNodes(new Node[0]);
434  } catch (PropertyVetoException ex) {
435  logger.log(Level.WARNING, "Error setting selected nodes", ex);
436  }
437  showThreadsPane();
438  }//GEN-LAST:event_backButtonActionPerformed
439 
440  private void showAllButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_showAllButtonActionPerformed
441  threadMessageNodeFactory.refresh(currentSelectionInfo, null);
442  threadNameLabel.setText("All Messages");
443 
444  showMessagesPane();
445  }//GEN-LAST:event_showAllButtonActionPerformed
446 
447  // Variables declaration - do not modify//GEN-BEGIN:variables
448  private javax.swing.JButton backButton;
449  private javax.swing.JPanel messagePanel;
450  private javax.swing.JPanel rootMessagesPane;
452  private javax.swing.JButton showAllButton;
453  private javax.swing.JLabel showingMessagesLabel;
454  private org.sleuthkit.autopsy.communications.relationships.MessagesPanel threadMessagesPanel;
455  private javax.swing.JLabel threadNameLabel;
456  private javax.swing.JLabel threadsLabel;
457  // End of variables declaration//GEN-END:variables
458 
462  class ShowThreadMessagesAction extends AbstractAction {
463 
464  @Override
465  public void actionPerformed(ActionEvent e) {
466 
467  SwingUtilities.invokeLater(new Runnable() {
468  @Override
469  public void run() {
470  showSelectedThread();
471  }
472  });
473  }
474  }
475 }
synchronized static Logger getLogger(String name)
Definition: Logger.java:124

Copyright © 2012-2022 Basis Technology. Generated on: Tue Aug 1 2023
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.