Autopsy  4.19.1
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  setLayout(new java.awt.CardLayout());
339 
340  rootMessagesPane.setOpaque(false);
341  rootMessagesPane.setLayout(new java.awt.GridBagLayout());
342 
343  org.openide.awt.Mnemonics.setLocalizedText(threadsLabel, org.openide.util.NbBundle.getMessage(MessageViewer.class, "MessageViewer.threadsLabel.text")); // NOI18N
344  gridBagConstraints = new java.awt.GridBagConstraints();
345  gridBagConstraints.gridx = 0;
346  gridBagConstraints.gridy = 0;
347  gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
348  gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
349  gridBagConstraints.weightx = 1.0;
350  gridBagConstraints.insets = new java.awt.Insets(15, 15, 9, 0);
351  rootMessagesPane.add(threadsLabel, gridBagConstraints);
352 
353  org.openide.awt.Mnemonics.setLocalizedText(showAllButton, org.openide.util.NbBundle.getMessage(MessageViewer.class, "MessageViewer.showAllButton.text")); // NOI18N
354  showAllButton.addActionListener(new java.awt.event.ActionListener() {
355  public void actionPerformed(java.awt.event.ActionEvent evt) {
356  showAllButtonActionPerformed(evt);
357  }
358  });
359  gridBagConstraints = new java.awt.GridBagConstraints();
360  gridBagConstraints.gridx = 0;
361  gridBagConstraints.gridy = 2;
362  gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
363  gridBagConstraints.insets = new java.awt.Insets(0, 15, 15, 0);
364  rootMessagesPane.add(showAllButton, gridBagConstraints);
365 
366  rootTablePane.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0)));
367  gridBagConstraints = new java.awt.GridBagConstraints();
368  gridBagConstraints.gridx = 0;
369  gridBagConstraints.gridy = 1;
370  gridBagConstraints.gridwidth = 2;
371  gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
372  gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
373  gridBagConstraints.weightx = 1.0;
374  gridBagConstraints.weighty = 1.0;
375  gridBagConstraints.insets = new java.awt.Insets(0, 15, 9, 15);
376  rootMessagesPane.add(rootTablePane, gridBagConstraints);
377 
378  add(rootMessagesPane, "threads");
379 
380  messagePanel.setLayout(new java.awt.GridBagLayout());
381  gridBagConstraints = new java.awt.GridBagConstraints();
382  gridBagConstraints.gridx = 0;
383  gridBagConstraints.gridy = 3;
384  gridBagConstraints.gridwidth = 3;
385  gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
386  gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
387  gridBagConstraints.weightx = 1.0;
388  gridBagConstraints.weighty = 1.0;
389  gridBagConstraints.insets = new java.awt.Insets(0, 15, 0, 15);
390  messagePanel.add(threadMessagesPanel, gridBagConstraints);
391 
392  org.openide.awt.Mnemonics.setLocalizedText(backButton, org.openide.util.NbBundle.getMessage(MessageViewer.class, "MessageViewer.backButton.text")); // NOI18N
393  backButton.addActionListener(new java.awt.event.ActionListener() {
394  public void actionPerformed(java.awt.event.ActionEvent evt) {
395  backButtonActionPerformed(evt);
396  }
397  });
398  gridBagConstraints = new java.awt.GridBagConstraints();
399  gridBagConstraints.gridx = 2;
400  gridBagConstraints.gridy = 0;
401  gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
402  gridBagConstraints.weightx = 1.0;
403  gridBagConstraints.insets = new java.awt.Insets(9, 0, 9, 15);
404  messagePanel.add(backButton, gridBagConstraints);
405  backButton.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(MessageViewer.class, "MessageViewer.backButton.AccessibleContext.accessibleDescription")); // NOI18N
406 
407  org.openide.awt.Mnemonics.setLocalizedText(showingMessagesLabel, org.openide.util.NbBundle.getMessage(MessageViewer.class, "MessageViewer.showingMessagesLabel.text")); // NOI18N
408  gridBagConstraints = new java.awt.GridBagConstraints();
409  gridBagConstraints.gridx = 0;
410  gridBagConstraints.gridy = 0;
411  gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
412  gridBagConstraints.insets = new java.awt.Insets(9, 15, 5, 0);
413  messagePanel.add(showingMessagesLabel, gridBagConstraints);
414 
415  org.openide.awt.Mnemonics.setLocalizedText(threadNameLabel, org.openide.util.NbBundle.getMessage(MessageViewer.class, "MessageViewer.threadNameLabel.text")); // NOI18N
416  gridBagConstraints = new java.awt.GridBagConstraints();
417  gridBagConstraints.gridx = 1;
418  gridBagConstraints.gridy = 0;
419  gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
420  gridBagConstraints.insets = new java.awt.Insets(9, 5, 5, 15);
421  messagePanel.add(threadNameLabel, gridBagConstraints);
422 
423  add(messagePanel, "messages");
424  }// </editor-fold>//GEN-END:initComponents
425 
426  private void backButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_backButtonActionPerformed
427  try {
428  rootTablePane.getExplorerManager().setSelectedNodes(new Node[0]);
429  } catch (PropertyVetoException ex) {
430  logger.log(Level.WARNING, "Error setting selected nodes", ex);
431  }
432  showThreadsPane();
433  }//GEN-LAST:event_backButtonActionPerformed
434 
435  private void showAllButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_showAllButtonActionPerformed
436  threadMessageNodeFactory.refresh(currentSelectionInfo, null);
437  threadNameLabel.setText("All Messages");
438 
439  showMessagesPane();
440  }//GEN-LAST:event_showAllButtonActionPerformed
441 
442  // Variables declaration - do not modify//GEN-BEGIN:variables
443  private javax.swing.JButton backButton;
444  private javax.swing.JPanel messagePanel;
445  private javax.swing.JPanel rootMessagesPane;
447  private javax.swing.JButton showAllButton;
448  private javax.swing.JLabel showingMessagesLabel;
449  private org.sleuthkit.autopsy.communications.relationships.MessagesPanel threadMessagesPanel;
450  private javax.swing.JLabel threadNameLabel;
451  private javax.swing.JLabel threadsLabel;
452  // End of variables declaration//GEN-END:variables
453 
457  class ShowThreadMessagesAction extends AbstractAction {
458 
459  @Override
460  public void actionPerformed(ActionEvent e) {
461 
462  SwingUtilities.invokeLater(new Runnable() {
463  @Override
464  public void run() {
465  showSelectedThread();
466  }
467  });
468  }
469  }
470 }
synchronized static Logger getLogger(String name)
Definition: Logger.java:124

Copyright © 2012-2021 Basis Technology. Generated on: Thu Sep 30 2021
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.