Autopsy 4.22.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 */
19package org.sleuthkit.autopsy.communications.relationships;
20
21import java.awt.CardLayout;
22import java.awt.Component;
23import java.awt.Graphics2D;
24import java.awt.Image;
25import java.awt.KeyboardFocusManager;
26import java.awt.RenderingHints;
27import java.awt.event.ActionEvent;
28import java.awt.image.BufferedImage;
29import java.beans.PropertyChangeEvent;
30import java.beans.PropertyChangeListener;
31import java.beans.PropertyVetoException;
32import java.lang.reflect.InvocationTargetException;
33import java.util.ArrayList;
34import java.util.logging.Level;
35import javax.swing.AbstractAction;
36import javax.swing.ImageIcon;
37import javax.swing.JPanel;
38import javax.swing.ListSelectionModel;
39import javax.swing.SwingUtilities;
40import static javax.swing.SwingUtilities.isDescendingFrom;
41import javax.swing.event.TableModelEvent;
42import javax.swing.event.TableModelListener;
43import org.netbeans.swing.outline.DefaultOutlineModel;
44import org.netbeans.swing.outline.Outline;
45import org.openide.explorer.ExplorerManager;
46import static org.openide.explorer.ExplorerUtils.createLookup;
47import org.openide.nodes.AbstractNode;
48import org.openide.nodes.Children;
49import org.openide.nodes.Node;
50import org.openide.nodes.Node.Property;
51import org.openide.nodes.Node.PropertySet;
52import org.openide.util.Exceptions;
53import org.openide.util.Lookup;
54import org.openide.util.NbBundle.Messages;
55import org.sleuthkit.autopsy.communications.ModifiableProxyLookup;
56import org.sleuthkit.autopsy.communications.Utils;
57import org.sleuthkit.autopsy.coreutils.Logger;
58
63@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
64final 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();
331 rootTablePane = new org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel();
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;
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}
static void setColumnWidths(Outline outline)
Definition Utils.java:54
synchronized static Logger getLogger(String name)
Definition Logger.java:124

Copyright © 2012-2024 Sleuth Kit Labs. Generated on:
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.