diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties index 54ee47c55a..0b7a6b9fe3 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties @@ -962,16 +962,11 @@ ContactArtifactViewer.emailsLabel.text=Email ContactArtifactViewer.othersLabel.text=Other DefaultArtifactContentViewer.copyMenuItem.text=Copy DefaultArtifactContentViewer.selectAllMenuItem.text=Select All -CallLogArtifactViewer.jButton3.text=Persona -CallLogArtifactViewer.jLabel9.text=Number/id n -CallLogArtifactViewer.jButton2.text=Persona -CallLogArtifactViewer.jLabel8.text=Number 2 CallLogArtifactViewer.otherParticipantsLabel.text=Other Participants CallLogArtifactViewer.toOrFromNumberLabel.text=Number/Id CallLogArtifactViewer.durationLabel.text=Duration.... CallLogArtifactViewer.dateTimeLabel.text=Date/Time..... CallLogArtifactViewer.directionLabel.text=Direction -CallLogArtifactViewer.toOrFromNameLabel.text=Name, if available CallLogArtifactViewer.personaButton1.text=Persona CallLogArtifactViewer.dataSourceNameLabel.text=jLabel2 CallLogArtifactViewer.jLabel2.text=Device Id @@ -980,3 +975,4 @@ CallLogArtifactViewer.jLabel3.text=Other Info CallLogArtifactViewer.localAccountIdLabel.text=jLabel1 CallLogArtifactViewer.localAccountLabel.text=Local Account CallLogArtifactViewer.jLabel4.text=Data Source Name +CallLogArtifactViewer.toOrFromNameLabel.text=Name, if available diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/CallLogArtifactViewer.form b/Core/src/org/sleuthkit/autopsy/contentviewers/CallLogArtifactViewer.form index 58656aad61..f02b25a7ae 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/CallLogArtifactViewer.form +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/CallLogArtifactViewer.form @@ -11,7 +11,7 @@ - + @@ -52,7 +52,7 @@ - + @@ -70,7 +70,7 @@ - + @@ -150,102 +150,52 @@ - + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + @@ -271,11 +221,11 @@ - - + + - + @@ -327,7 +277,7 @@ - + @@ -341,7 +291,7 @@ - + diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/CallLogArtifactViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/CallLogArtifactViewer.java index beffa6124d..1c687b8af7 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/CallLogArtifactViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/CallLogArtifactViewer.java @@ -19,9 +19,20 @@ package org.sleuthkit.autopsy.contentviewers; import java.awt.Component; +import java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; import java.util.Locale; +import java.util.Map; +import java.util.Set; import java.util.logging.Level; import javax.swing.JScrollPane; import org.sleuthkit.autopsy.coreutils.Logger; @@ -34,17 +45,26 @@ import org.sleuthkit.datamodel.TskCoreException; /** * This is a viewer for TSK_CALLLOG artifacts. - * - * + * + * */ public class CallLogArtifactViewer extends javax.swing.JPanel implements ArtifactContentViewer { private final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US); - + private final static Logger logger = Logger.getLogger(CallLogArtifactViewer.class.getName()); private static final long serialVersionUID = 1L; - + private static final Set HANDLED_ATTRIBUTE_TYPES = new HashSet(Arrays.asList( + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER.getTypeID(), + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO.getTypeID(), + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM.getTypeID(), + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION.getTypeID(), + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID(), + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START.getTypeID(), + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_END.getTypeID() + )); + /** * Creates new form CalllogArtifactViewer */ @@ -71,10 +91,6 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac durationLabel = new javax.swing.JLabel(); otherParticipantsPanel = new javax.swing.JPanel(); otherParticipantsListPanel = new javax.swing.JPanel(); - jLabel8 = new javax.swing.JLabel(); - jButton2 = new javax.swing.JButton(); - jLabel9 = new javax.swing.JLabel(); - jButton3 = new javax.swing.JButton(); otherParticipantsLabel = new javax.swing.JLabel(); bottomPanel = new javax.swing.JPanel(); otherAttributesPanel = new javax.swing.JPanel(); @@ -132,7 +148,7 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac .addComponent(dateTimeLabel) .addGap(18, 18, 18) .addComponent(durationLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 88, javax.swing.GroupLayout.PREFERRED_SIZE))) - .addContainerGap(199, Short.MAX_VALUE)) + .addContainerGap(211, Short.MAX_VALUE)) ); callDetailsPanelLayout.setVerticalGroup( callDetailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -146,7 +162,7 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac .addGap(28, 28, 28)) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, callDetailsPanelLayout.createSequentialGroup() .addComponent(toOrFromNameLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(18, 18, 18))) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED))) .addGroup(callDetailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(dateTimeLabel) .addComponent(durationLabel)) @@ -157,60 +173,37 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac add(callDetailsPanel, java.awt.BorderLayout.PAGE_START); - otherParticipantsPanel.setLayout(new java.awt.BorderLayout()); - - org.openide.awt.Mnemonics.setLocalizedText(jLabel8, org.openide.util.NbBundle.getMessage(CallLogArtifactViewer.class, "CallLogArtifactViewer.jLabel8.text")); // NOI18N - - org.openide.awt.Mnemonics.setLocalizedText(jButton2, org.openide.util.NbBundle.getMessage(CallLogArtifactViewer.class, "CallLogArtifactViewer.jButton2.text")); // NOI18N - jButton2.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - jButton2ActionPerformed(evt); - } - }); - - org.openide.awt.Mnemonics.setLocalizedText(jLabel9, org.openide.util.NbBundle.getMessage(CallLogArtifactViewer.class, "CallLogArtifactViewer.jLabel9.text")); // NOI18N - - org.openide.awt.Mnemonics.setLocalizedText(jButton3, org.openide.util.NbBundle.getMessage(CallLogArtifactViewer.class, "CallLogArtifactViewer.jButton3.text")); // NOI18N - - org.openide.awt.Mnemonics.setLocalizedText(otherParticipantsLabel, org.openide.util.NbBundle.getMessage(CallLogArtifactViewer.class, "CallLogArtifactViewer.otherParticipantsLabel.text")); // NOI18N - javax.swing.GroupLayout otherParticipantsListPanelLayout = new javax.swing.GroupLayout(otherParticipantsListPanel); otherParticipantsListPanel.setLayout(otherParticipantsListPanelLayout); otherParticipantsListPanelLayout.setHorizontalGroup( otherParticipantsListPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(otherParticipantsListPanelLayout.createSequentialGroup() - .addGroup(otherParticipantsListPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(otherParticipantsListPanelLayout.createSequentialGroup() - .addGap(56, 56, 56) - .addGroup(otherParticipantsListPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jLabel8) - .addComponent(jLabel9)) - .addGap(37, 37, 37) - .addGroup(otherParticipantsListPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jButton3) - .addComponent(jButton2))) - .addGroup(otherParticipantsListPanelLayout.createSequentialGroup() - .addGap(24, 24, 24) - .addComponent(otherParticipantsLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 378, javax.swing.GroupLayout.PREFERRED_SIZE))) - .addContainerGap(23, Short.MAX_VALUE)) + .addGap(0, 406, Short.MAX_VALUE) ); otherParticipantsListPanelLayout.setVerticalGroup( otherParticipantsListPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(otherParticipantsListPanelLayout.createSequentialGroup() - .addGap(12, 12, 12) - .addComponent(otherParticipantsLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(otherParticipantsListPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(jLabel8) - .addComponent(jButton2)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 80, Short.MAX_VALUE) - .addGroup(otherParticipantsListPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(jLabel9) - .addComponent(jButton3)) - .addContainerGap()) + .addGap(0, 168, Short.MAX_VALUE) ); - otherParticipantsPanel.add(otherParticipantsListPanel, java.awt.BorderLayout.CENTER); + org.openide.awt.Mnemonics.setLocalizedText(otherParticipantsLabel, org.openide.util.NbBundle.getMessage(CallLogArtifactViewer.class, "CallLogArtifactViewer.otherParticipantsLabel.text")); // NOI18N + + javax.swing.GroupLayout otherParticipantsPanelLayout = new javax.swing.GroupLayout(otherParticipantsPanel); + otherParticipantsPanel.setLayout(otherParticipantsPanelLayout); + otherParticipantsPanelLayout.setHorizontalGroup( + otherParticipantsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(otherParticipantsPanelLayout.createSequentialGroup() + .addGap(25, 25, 25) + .addGroup(otherParticipantsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(otherParticipantsListPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(otherParticipantsLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 398, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + otherParticipantsPanelLayout.setVerticalGroup( + otherParticipantsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(otherParticipantsPanelLayout.createSequentialGroup() + .addComponent(otherParticipantsLabel) + .addGap(0, 0, 0) + .addComponent(otherParticipantsListPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + ); add(otherParticipantsPanel, java.awt.BorderLayout.CENTER); @@ -237,9 +230,9 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac .addGroup(otherAttributesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(otherAttributesListPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addGroup(otherAttributesPanelLayout.createSequentialGroup() - .addGap(27, 27, 27) - .addComponent(jLabel3, javax.swing.GroupLayout.PREFERRED_SIZE, 367, javax.swing.GroupLayout.PREFERRED_SIZE))) - .addGap(12, 12, 12)) + .addGap(25, 25, 25) + .addComponent(jLabel3, javax.swing.GroupLayout.PREFERRED_SIZE, 372, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addGap(24, 24, 24)) ); otherAttributesPanelLayout.setVerticalGroup( otherAttributesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -270,7 +263,7 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac localAccountInfoPanelLayout.setHorizontalGroup( localAccountInfoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(localAccountInfoPanelLayout.createSequentialGroup() - .addGap(19, 19, 19) + .addGap(25, 25, 25) .addGroup(localAccountInfoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(jLabel4) .addComponent(jLabel2) @@ -281,7 +274,7 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac .addGroup(localAccountInfoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) .addComponent(dataSourceNameLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 130, Short.MAX_VALUE) .addComponent(deviceIdLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))) - .addContainerGap(152, Short.MAX_VALUE)) + .addContainerGap(158, Short.MAX_VALUE)) ); localAccountInfoPanelLayout.setVerticalGroup( localAccountInfoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -306,18 +299,13 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac add(bottomPanel, java.awt.BorderLayout.PAGE_END); }// //GEN-END:initComponents - private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton2ActionPerformed - // TODO add your handling code here: - }//GEN-LAST:event_jButton2ActionPerformed - private void personaButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_personaButton1ActionPerformed // TODO add your handling code here: }//GEN-LAST:event_personaButton1ActionPerformed - private void customizeComponents() { // disable the name label for now. - this.toOrFromNameLabel.setEnabled(false); + this.toOrFromNameLabel.setVisible(false); } // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JPanel bottomPanel; @@ -327,13 +315,9 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac private javax.swing.JLabel deviceIdLabel; private javax.swing.JLabel directionLabel; private javax.swing.JLabel durationLabel; - private javax.swing.JButton jButton2; - private javax.swing.JButton jButton3; private javax.swing.JLabel jLabel2; private javax.swing.JLabel jLabel3; private javax.swing.JLabel jLabel4; - private javax.swing.JLabel jLabel8; - private javax.swing.JLabel jLabel9; private javax.swing.JLabel localAccountIdLabel; private javax.swing.JPanel localAccountInfoPanel; private javax.swing.JLabel localAccountLabel; @@ -347,32 +331,32 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac private javax.swing.JLabel toOrFromNumberLabel; // End of variables declaration//GEN-END:variables + /** + * Encapsulates the information to be displayed about the call log artifact. + */ private class CallLogViewData { + // primary to/from number/adddress/accountId String number; String name = null; String direction; - //String mediaType = null; String dateTimeStr = null; String duration = null; - Collection otherParticipants = null; + Collection otherParticipants = new ArrayList<>(); String dataSourceName = null; String dataSourceDeviceId = null; - String localAccountId = null; - - - - - + String localAccountId = null; // number/accountId of device owner, may not be always known + Map otherAttributes = new HashMap<>(); CallLogViewData(String number) { - this(number, null); + this(number, null); } + CallLogViewData(String number, String direction) { this.number = number; this.direction = direction; } - + String getNumber() { return number; } @@ -430,13 +414,25 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac } Collection getOtherParticipants() { - return otherParticipants; + return Collections.unmodifiableCollection(otherParticipants); } void setOtherParticipants(Collection otherParticipants) { - otherParticipants = otherParticipants; + if (otherParticipants != null) { + this.otherParticipants = new ArrayList<>(otherParticipants); + } } - + + public Map getOtherAttributes() { + return Collections.unmodifiableMap(otherAttributes); + } + + public void setOtherAttributes(Map otherAttributes) { + if (otherParticipants != null) { + this.otherAttributes = new HashMap<>(otherAttributes); + } + } + public String getLocalAccountId() { return localAccountId; } @@ -444,38 +440,37 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac public void setLocalAccountId(String localAccountId) { this.localAccountId = localAccountId; } - + } - + @Override public void setArtifact(BlackboardArtifact artifact) { - + this.removeAll(); this.initComponents(); this.customizeComponents(); - + CallLogViewData callLogViewData = null; - + try { - callLogViewData= getCallLogViewData(artifact); - - } - catch (TskCoreException ex) { + callLogViewData = getCallLogViewData(artifact); + } catch (TskCoreException ex) { logger.log(Level.SEVERE, String.format("Error getting attributes for Calllog artifact (artifact_id=%d, obj_id=%d)", artifact.getArtifactID(), artifact.getObjectID()), ex); } - + if (callLogViewData != null) { - + this.toOrFromNumberLabel.setText(callLogViewData.getNumber()); - - // TBD: wire up the Persona button to search/add this Persona.... - + + // TBD: Vik-6383 find and display the persona for this account, and a button + this.personaButton1.setVisible(false); + if (callLogViewData.getDirection() != null) { - this.directionLabel.setText(callLogViewData.getDirection()); + this.directionLabel.setText(callLogViewData.getDirection()); } else { this.directionLabel.setVisible(false); } - + if (callLogViewData.getDateTimeStr() != null) { this.dateTimeLabel.setText(callLogViewData.getDateTimeStr()); if (callLogViewData.getDuration() != null) { @@ -487,20 +482,16 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac this.dateTimeLabel.setVisible(false); this.durationLabel.setVisible(false); } - - if (callLogViewData.getOtherParticipants() != null) { - // TBD: popluate lower panel - // TBD: updateOtherParticipantsPanel(TBD) } - } else { - this.remove(this.otherParticipantsPanel); - } - - - // TBD: Populate other attributs panel - this.remove(this.otherAttributesPanel); - + + // Populate other participants + updateOtherParticipantsPanel(callLogViewData.getOtherParticipants()); + + // Populate other attributs panel + updateOtherAttributesPanel(callLogViewData.getOtherAttributes()); + // populate local account and data source if (callLogViewData.getLocalAccountId() != null) { + // Vik-6383 find and display the persona for this account, and a button this.localAccountIdLabel.setText(callLogViewData.getLocalAccountId()); } else { this.localAccountLabel.setVisible(false); @@ -512,24 +503,126 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac if (callLogViewData.getDataSourceDeviceId() != null) { this.deviceIdLabel.setText(callLogViewData.getDataSourceDeviceId()); } - } - - + // repaint this.revalidate(); } + private void updateOtherParticipantsPanel(Collection otherParticipants) { + + if (otherParticipants == null || otherParticipants.isEmpty()) { + otherParticipantsPanel.setVisible(false); + return; + } + + // create a gridbag layout to show each participant on one line + GridBagLayout gridBagLayout = new GridBagLayout(); + GridBagConstraints constraints = new GridBagConstraints(); + constraints.anchor = GridBagConstraints.FIRST_LINE_START; + constraints.gridy = 0; + constraints.insets = new java.awt.Insets(4, 12, 0, 0); + for (String participant : otherParticipants) { + constraints.fill = GridBagConstraints.NONE; + constraints.weightx = 0; + constraints.gridx = 0; + + javax.swing.Box.Filler filler1 = new javax.swing.Box.Filler(new Dimension(25, 0), new Dimension(25, 0), new Dimension(25, 0)); + otherParticipantsListPanel.add(filler1, constraints); + + // Add a label partcipant's number/Id + javax.swing.JLabel participantNumberLabel = new javax.swing.JLabel(); + participantNumberLabel.setText(participant); + + gridBagLayout.setConstraints(participantNumberLabel, constraints); + otherParticipantsListPanel.add(participantNumberLabel); + + // TBD Vik-6383 find and display the persona for this account, and a button +// constraints.gridx += 2; +// javax.swing.JButton personaButton = new javax.swing.JButton(); +// personaButton.setText("Persona"); +// gridBagLayout.setConstraints(personaButton, constraints); +// otherParticipantsListPanel.add(personaButton); + // add a filler to take up rest of the space + constraints.gridx++; + constraints.weightx = 1.0; + constraints.fill = GridBagConstraints.HORIZONTAL; + otherParticipantsListPanel.add(new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(32767, 0))); + + constraints.gridy++; + } + otherParticipantsListPanel.setLayout(gridBagLayout); + + otherParticipantsPanel.revalidate(); + + } + + private void updateOtherAttributesPanel(Map otherAttributes) { + if (otherAttributes == null || otherAttributes.isEmpty()) { + this.otherAttributesPanel.setVisible(false); + return; + } + + // create a gridbag layout to show attribute on one line + GridBagLayout gridBagLayout = new GridBagLayout(); + GridBagConstraints constraints = new GridBagConstraints(); + constraints.anchor = GridBagConstraints.FIRST_LINE_START; + constraints.gridy = 0; + constraints.insets = new java.awt.Insets(4, 12, 0, 0); + for (Map.Entry attribute : otherAttributes.entrySet()) { + constraints.fill = GridBagConstraints.NONE; + constraints.weightx = 0; + constraints.gridx = 0; + + // Add a small horizontal filler at the beginning + javax.swing.Box.Filler filler1 = new javax.swing.Box.Filler(new Dimension(25, 0), new Dimension(25, 0), new Dimension(25, 0)); + otherAttributesListPanel.add(filler1, constraints); + + // Add attribute name label + constraints.gridx++; + javax.swing.JLabel attrNameLabel = new javax.swing.JLabel(); + attrNameLabel.setText(attribute.getKey()); + + gridBagLayout.setConstraints(attrNameLabel, constraints); + this.otherAttributesListPanel.add(attrNameLabel); + + // Add value + constraints.gridx += 2; + javax.swing.JLabel attrValueLabel = new javax.swing.JLabel(); + attrValueLabel.setText(attribute.getValue()); + + gridBagLayout.setConstraints(attrValueLabel, constraints); + this.otherAttributesListPanel.add(attrValueLabel); + + // add a filler to take up rest of the horizontal space + constraints.gridx++; + constraints.weightx = 1.0; + constraints.fill = GridBagConstraints.HORIZONTAL; + otherAttributesListPanel.add(new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(32767, 0))); + + constraints.gridy++; + } + otherAttributesListPanel.setLayout(gridBagLayout); + otherAttributesListPanel.revalidate(); + } + @Override public Component getComponent() { - return new JScrollPane(this, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + return new JScrollPane(this, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); } @Override public boolean isSupported(BlackboardArtifact artifact) { return artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG.getTypeID(); } - + + /** + * Extracts data from the call log artifact for display in the view. + * + * @param artifact Artifact to extract data from. + * @return CallLogViewData + * @throws TskCoreException + */ private CallLogViewData getCallLogViewData(BlackboardArtifact artifact) throws TskCoreException { BlackboardAttribute directionAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION)); @@ -538,19 +631,23 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac CallLogViewData callLogViewData = null; String direction = null; - + if (directionAttr != null) { + // if direction is known, depending on the direction, + // the TO or the FROM attribute is the primary number of interest. + // annd the other is is possibly the number/address of device owner. direction = directionAttr.getValueString(); - + if (direction.equalsIgnoreCase("Incoming")) { numberAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM)); localAccountAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO)); } else if (direction.equalsIgnoreCase("Outgoing")) { numberAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO)); localAccountAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM)); - } - + } + } + // if direction isn't known, look for any attribute that may have the number/address if (numberAttr == null) { numberAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM)); } @@ -569,10 +666,20 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac if (numberAttr != null) { - callLogViewData = new CallLogViewData(numberAttr.getValueString(), direction); - - // TBD: check if it's a list of numbers and and if so, return only the first one. and put the others in otherParticicpants.... - + // check if it's a list of numbers and and if so, + // split it, take the first one. and put the others in otherParticicpants.... + String[] numbers = numberAttr.getValueString().split(","); + List otherNumbers = null; + if (numbers.length > 1) { + otherNumbers = new ArrayList<>(); + for (int i = 1; i < numbers.length; i++) { + otherNumbers.add(numbers[i]); + } + + } + callLogViewData = new CallLogViewData(numbers[0], direction); + callLogViewData.setOtherParticipants(otherNumbers); + // get date, duration, BlackboardAttribute startTimeAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START)); if (startTimeAttr == null) { @@ -585,32 +692,54 @@ public class CallLogArtifactViewer extends javax.swing.JPanel implements Artifac dateFormatter.setTimeZone(ContentUtils.getTimeZone(content)); callLogViewData.setDateTimeStr(dateFormatter.format(new java.util.Date(startTime * 1000))); } - - BlackboardAttribute endTimeAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_END)); - if (endTimeAttr != null) { - long endTime = endTimeAttr.getValueLong(); - if (endTime > 0) { - callLogViewData.setDuration(String.format("%d seconds", (endTime-startTime))); - } - } - } - + + BlackboardAttribute endTimeAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_END)); + if (endTimeAttr != null) { + long endTime = endTimeAttr.getValueLong(); + if (endTime > 0) { + callLogViewData.setDuration(String.format("%d seconds", (endTime - startTime))); + } + } + } + Content dataSource = artifact.getDataSource(); - + callLogViewData.setDataSourceName(dataSource.getName()); String deviceId = ((DataSource) dataSource).getDeviceId(); callLogViewData.setDataSourceDeviceId(deviceId); - + if (localAccountAttr != null) { String attrValue = localAccountAttr.getValueString(); - if (attrValue.equalsIgnoreCase(deviceId) == false && attrValue.contains(",") == false ) { + if (attrValue.equalsIgnoreCase(deviceId) == false && attrValue.contains(",") == false) { callLogViewData.setLocalAccountId(attrValue); } } - + + callLogViewData.setOtherAttributes(extractOtherAttributes(artifact)); + } return callLogViewData; } + + /** + * Returns a map of attribute name/value pairs + * + * @param artifact + * @return + * @throws TskCoreException + */ + private Map extractOtherAttributes(BlackboardArtifact artifact) throws TskCoreException { + List attributes = artifact.getAttributes(); + Map otherAttributes = new HashMap<>(); + + for (BlackboardAttribute attr : attributes) { + if (HANDLED_ATTRIBUTE_TYPES.contains(attr.getAttributeType().getTypeID()) == false) { + otherAttributes.put(attr.getAttributeType().getDisplayName(), attr.getDisplayString()); + } + } + + return otherAttributes; + } }