diff --git a/Ingest/src/org/sleuthkit/autopsy/ingest/IngestMessage.java b/Ingest/src/org/sleuthkit/autopsy/ingest/IngestMessage.java index 67ed5274b6..7b8d4c73a4 100644 --- a/Ingest/src/org/sleuthkit/autopsy/ingest/IngestMessage.java +++ b/Ingest/src/org/sleuthkit/autopsy/ingest/IngestMessage.java @@ -166,12 +166,6 @@ public class IngestMessage { public static IngestMessage createMessage(long ID, MessageType messageType, IngestServiceAbstract source, String subject, String detailsHtml) { if (messageType == null || source == null || subject == null) { throw new IllegalArgumentException("message type, source and subject cannot be null"); - } - //add html tags - if (detailsHtml != null) { - StringBuilder htmlB = new StringBuilder(); - htmlB.append("").append(detailsHtml).append(""); - detailsHtml = htmlB.toString(); } return new IngestMessage(ID, messageType, source, subject, detailsHtml); } @@ -230,10 +224,6 @@ public class IngestMessage { if (source == null || subject == null || detailsHtml == null || data == null) { throw new IllegalArgumentException("source, subject, details and data cannot be null"); } - //add html tags - StringBuilder htmlB = new StringBuilder(); - htmlB.append("").append(detailsHtml).append(""); - detailsHtml = htmlB.toString(); IngestMessage im = new IngestMessage(ID, MessageType.DATA, source, subject, detailsHtml); im.uniqueKey = uniqueKey; diff --git a/Ingest/src/org/sleuthkit/autopsy/ingest/IngestMessageDetailsPanel.java b/Ingest/src/org/sleuthkit/autopsy/ingest/IngestMessageDetailsPanel.java index 84147b28ce..470500ebd3 100644 --- a/Ingest/src/org/sleuthkit/autopsy/ingest/IngestMessageDetailsPanel.java +++ b/Ingest/src/org/sleuthkit/autopsy/ingest/IngestMessageDetailsPanel.java @@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.ingest; import org.openide.util.Lookup; import org.sleuthkit.autopsy.corecomponentinterfaces.BlackboardResultViewer; +import org.sleuthkit.autopsy.ingest.IngestMessagePanel.IngestMessageGroup; import org.sleuthkit.datamodel.BlackboardArtifact; /** @@ -143,9 +144,9 @@ class IngestMessageDetailsPanel extends javax.swing.JPanel { private void viewArtifact(java.awt.event.ActionEvent evt) { - final IngestMessage message = mainPanel.getMessagePanel().getSelectedMessage(); - if (message != null) { - BlackboardArtifact art = message.getData(); + final IngestMessageGroup messageGroup = mainPanel.getMessagePanel().getSelectedMessage(); + if (messageGroup != null) { + BlackboardArtifact art = messageGroup.getData(); if (art != null) { BlackboardResultViewer v = Lookup.getDefault().lookup(BlackboardResultViewer.class); v.viewArtifact(art); @@ -156,9 +157,9 @@ class IngestMessageDetailsPanel extends javax.swing.JPanel { private void viewContent(java.awt.event.ActionEvent evt) { - final IngestMessage message = mainPanel.getMessagePanel().getSelectedMessage(); - if (message != null) { - BlackboardArtifact art = message.getData(); + final IngestMessageGroup messageGroup = mainPanel.getMessagePanel().getSelectedMessage(); + if (messageGroup != null) { + BlackboardArtifact art = messageGroup.getData(); if (art != null) { BlackboardResultViewer v = Lookup.getDefault().lookup(BlackboardResultViewer.class); v.viewArtifactContent(art); @@ -168,15 +169,18 @@ class IngestMessageDetailsPanel extends javax.swing.JPanel { void showDetails(int rowNumber) { - final IngestMessage message = mainPanel.getMessagePanel().getMessage(rowNumber); - if (message != null) { - String details = message.getDetails(); + final IngestMessageGroup messageGroup = mainPanel.getMessagePanel().getMessageGroup(rowNumber); + if (messageGroup != null) { + String details = messageGroup.getDetails(); if (details != null) { - this.messageDetailsPane.setText(details); + StringBuilder b = new StringBuilder(); + b.append("").append(details).append(""); + this.messageDetailsPane.setText(b.toString()); } else { this.messageDetailsPane.setText(""); } - if (message.getData() != null) { + //show artifact/content only for a message group with a single message + if (messageGroup.getData() != null && messageGroup.getCount() == 1) { viewArtifactButton.setEnabled(true); viewContentButton.setEnabled(true); } else { diff --git a/Ingest/src/org/sleuthkit/autopsy/ingest/IngestMessagePanel.java b/Ingest/src/org/sleuthkit/autopsy/ingest/IngestMessagePanel.java index b043b4d810..b98d1e83de 100644 --- a/Ingest/src/org/sleuthkit/autopsy/ingest/IngestMessagePanel.java +++ b/Ingest/src/org/sleuthkit/autopsy/ingest/IngestMessagePanel.java @@ -22,7 +22,10 @@ import java.awt.Color; import java.awt.Component; import java.awt.Font; import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.DefaultListSelectionModel; @@ -33,18 +36,19 @@ import javax.swing.table.AbstractTableModel; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.TableColumn; import org.sleuthkit.autopsy.ingest.IngestMessage.*; +import org.sleuthkit.datamodel.BlackboardArtifact; /** * Notification window showing messages from services to user * */ class IngestMessagePanel extends javax.swing.JPanel { - + private MessageTableModel tableModel; private IngestMessageMainPanel mainPanel; private static Font visitedFont = new Font("Arial", Font.PLAIN, 11); private static Font notVisitedFont = new Font("Arial", Font.BOLD, 11); - private static Color ERROR_COLOR = new Color (255, 90, 90); + private static Color ERROR_COLOR = new Color(255, 90, 90); private int lastRowSelected = -1; /** Creates new form IngestMessagePanel */ @@ -54,21 +58,21 @@ class IngestMessagePanel extends javax.swing.JPanel { initComponents(); customizeComponents(); } - + int getLastRowSelected() { return this.lastRowSelected; } - - IngestMessage getSelectedMessage() { + + IngestMessageGroup getSelectedMessage() { if (lastRowSelected < 0) { return null; } - - return tableModel.getMessage(lastRowSelected); + + return tableModel.getMessageGroup(lastRowSelected); } - - IngestMessage getMessage(int rowNumber) { - return tableModel.getMessage(rowNumber); + + IngestMessageGroup getMessageGroup(int rowNumber) { + return tableModel.getMessageGroup(rowNumber); } /** This method is called from within the constructor to @@ -122,14 +126,14 @@ class IngestMessagePanel extends javax.swing.JPanel { mainPanel.setOpaque(true); jScrollPane1.setOpaque(true); messageTable.setOpaque(false); - + jScrollPane1.setWheelScrollingEnabled(true); - + messageTable.setAutoscrolls(true); //messageTable.setTableHeader(null); messageTable.setShowHorizontalLines(false); messageTable.setShowVerticalLines(false); - + messageTable.getParent().setBackground(messageTable.getBackground()); //customize column witdhs @@ -141,10 +145,14 @@ class IngestMessagePanel extends javax.swing.JPanel { column = messageTable.getColumnModel().getColumn(i); if (i == 0) { column.setCellRenderer(new MessageTableRenderer()); - column.setPreferredWidth(((int) (width * 0.68))); - } else { - column.setPreferredWidth(((int) (width * 0.30))); + column.setPreferredWidth(((int) (width * 0.61))); + } else if (i == 1) { + column.setPreferredWidth(((int) (width * 0.15))); column.setCellRenderer(new MessageTableRenderer()); + } else { + column.setPreferredWidth(((int) (width * 0.23))); + column.setCellRenderer(new MessageTableRenderer()); + } } messageTable.setCellSelectionEnabled(false); @@ -152,72 +160,90 @@ class IngestMessagePanel extends javax.swing.JPanel { messageTable.setRowSelectionAllowed(true); messageTable.getSelectionModel().addListSelectionListener(new MessageVisitedSelection()); } - - + public void addMessage(IngestMessage m) { tableModel.addMessage(m); //autoscroll - messageTable.scrollRectToVisible(messageTable.getCellRect(messageTable.getRowCount() - 1, messageTable.getColumnCount(), true)); + //messageTable.scrollRectToVisible(messageTable.getCellRect(messageTable.getRowCount() - 1, messageTable.getColumnCount(), true)); } - + public void clearMessages() { tableModel.clearMessages(); } - + private void setVisited(int rowNumber) { tableModel.setVisited(rowNumber); lastRowSelected = rowNumber; } - + private class MessageTableModel extends AbstractTableModel { //data private Logger logger = Logger.getLogger(MessageTableModel.class.getName()); private List messageData = new ArrayList(); - + //for keeping track of messages to group, per service, by uniqness + private Map>> groupings = new HashMap>>(); + private static final int MESSAGE_GROUP_THRESH = 3; //group messages after 3 messages per service with same uniqness + + MessageTableModel() { + //initialize groupings map with services + for (IngestServiceAbstract service : IngestManager.enumerateFsContentServices()) { + groupings.put(service, new HashMap>()); + } + for (IngestServiceAbstract service : IngestManager.enumerateImageServices()) { + groupings.put(service, new HashMap>()); + } + } + @Override public int getColumnCount() { - return 2; + return 3; } - + @Override public int getRowCount() { return messageData.size(); } - + @Override public String getColumnName(int column) { String colName = null; - + switch (column) { case 0: colName = "Subject"; break; case 1: + colName = "Pri"; + break; + case 2: colName = "Module"; break; default: ; - + } return colName; } - + @Override public Object getValueAt(int rowIndex, int columnIndex) { Object ret = null; TableEntry entry = messageData.get(rowIndex); - + switch (columnIndex) { case 0: - ret = (Object) entry.message.getSubject(); + ret = (Object) entry.messageGroup.getSubject(); break; case 1: - Object service = entry.message.getSource(); + ret = (Object) entry.messageGroup.getCount(); + break; + case 2: + Object service = entry.messageGroup.getSource(); if (service == null) { ret = ""; } else { - ret = (Object) entry.message.getSource().getName(); + ret = (Object) entry.messageGroup.getSource().getName(); } break; default: @@ -226,34 +252,113 @@ class IngestMessagePanel extends javax.swing.JPanel { } return ret; } - + @Override public boolean isCellEditable(int rowIndex, int columnIndex) { return false; } - + @Override public Class getColumnClass(int c) { return getValueAt(0, c).getClass(); } + private int getTableEntryIndex(IngestMessageGroup group) { + int ret = -1; + int i = 0; + for (TableEntry e : messageData) { + if (e.messageGroup.equals(group)) { + ret = i; + break; + } + ++i; + } + return ret; + } + public void addMessage(IngestMessage m) { - messageData.add(new TableEntry(m)); + //check how many messages per service with the same uniqness + //and add to existing group or create a new group + IngestServiceAbstract service = m.getSource(); + IngestMessageGroup messageGroup = null; + if (service != null) { + //not a manager message, then group + final Map> groups = groupings.get(service); + //groups for this uniqueness + final String uniqueness = m.getUniqueKey(); + List uniqGroups = groups.get(uniqueness); + if (uniqGroups == null) { + //first one with this uniqueness + uniqGroups = new ArrayList(); + messageGroup = new IngestMessageGroup(m); + uniqGroups.add(messageGroup); + groups.put(uniqueness, uniqGroups); + } else { + int uniqueGroupsCount = uniqGroups.size(); + if (uniqueGroupsCount > MESSAGE_GROUP_THRESH) { + //merge them + messageGroup = uniqGroups.get(0); + for (int i = 1; i 1) { + first.add(m); + messageGroup = first; + //TODO move to bottom of table + //remove from existing position + int toRemove = getTableEntryIndex(messageGroup); + messageData.remove(toRemove); + //remove the row, will be added to the bottom + this.fireTableRowsDeleted(toRemove, toRemove); //TODO check + } else { + //one group with one message + messageGroup = new IngestMessageGroup(m); + uniqGroups.add(messageGroup); + + } + } else { + //multiple groups with 1 msg each + messageGroup = new IngestMessageGroup(m); + uniqGroups.add(messageGroup); + //add to bottom + } + } + + } else { + //manager message + messageGroup = new IngestMessageGroup(m); + } + + //add new or updated row to the bottom + messageData.add(new TableEntry(messageGroup)); int size = messageData.size(); this.fireTableRowsInserted(size - 1, size); } - + public void clearMessages() { messageData.clear(); fireTableDataChanged(); } - + public void setVisited(int rowNumber) { messageData.get(rowNumber).visited = true; //repaint the cell fireTableCellUpdated(rowNumber, 0); } - + public void setVisitedAll() { int row = 0; for (TableEntry e : messageData) { @@ -264,57 +369,188 @@ class IngestMessagePanel extends javax.swing.JPanel { ++row; } } - + public boolean isVisited(int rowNumber) { return messageData.get(rowNumber).visited; } - + public MessageType getMessageType(int rowNumber) { - return messageData.get(rowNumber).message.getMessageType(); + return messageData.get(rowNumber).messageGroup.getMessageType(); } - - public IngestMessage getMessage(int rowNumber) { - return messageData.get(rowNumber).message; + + public IngestMessageGroup getMessageGroup(int rowNumber) { + return messageData.get(rowNumber).messageGroup; } - + class TableEntry implements Comparable { - - IngestMessage message; + + IngestMessageGroup messageGroup; boolean visited; - - TableEntry(IngestMessage message) { - this.message = message; + + TableEntry(IngestMessageGroup messageGroup) { + this.messageGroup = messageGroup; visited = false; } - + @Override public int compareTo(Object o) { - return this.message.getDatePosted().compareTo(((TableEntry) o).message.getDatePosted()); + return this.messageGroup.getDatePosted().compareTo(((TableEntry) o).messageGroup.getDatePosted()); } } } + //represents grouping of similar messages + //with the same uniqness + static class IngestMessageGroup { + + static Color VERY_HIGH_PRI_COLOR = new Color(5, 131, 46); //for a single message in a group + static Color HIGH_PRI_COLOR = new Color(36, 166, 65); + static Color MED_PRI_COLOR = new Color(55, 213, 90); + static Color LOW_PRI_COLOR = new Color(160, 235, 177); + List messages; + int count; + + IngestMessageGroup(IngestMessage message) { + messages = new ArrayList(); + messages.add(message); + count = 1; + } + + List getMessages() { + return messages; + } + + void add(IngestMessage message) { + + IngestMessage first = messages.get(0); + //make sure uniqness agrees + if (!message.getSource().equals(first.getSource()) + || !message.getUniqueKey().equals(first.getUniqueKey())) { + throw new IllegalArgumentException("Tried to add a message to a wrong message group."); + } + + messages.add(message); + ++count; + } + + //add all messages from another group + void add(IngestMessageGroup group) { + + IngestMessage first = messages.get(0); + IngestMessage firstG = group.messages.get(0); + //make sure uniqness agrees + if (!firstG.getSource().equals(first.getSource()) + || !firstG.getUniqueKey().equals(first.getUniqueKey())) { + throw new IllegalArgumentException("Tried to add a message to a wrong message group."); + } + + for (IngestMessage m : group.getMessages()) { + messages.add(m); + ++count; + } + } + + int getCount() { + return count; + } + + String getDetails() { + StringBuilder b = new StringBuilder(); + for (IngestMessage m : messages) { + b.append(m.getDetails()); + b.append("
"); + } + + return b.toString(); + } + + /** + * return color corresp to priority + * @return + */ + Color getColor() { + if (count == 1) { + return VERY_HIGH_PRI_COLOR; + } else if (count < 5) { + return HIGH_PRI_COLOR; + } else if (count < 15) { + return MED_PRI_COLOR; + } else { + return LOW_PRI_COLOR; + } + + } + + /** + * return date of the last message of the group + * used for chrono sort + * @return + */ + Date getDatePosted() { + return messages.get(count - 1).getDatePosted(); + } + + /** + * get subject of the first message + * @return + */ + String getSubject() { + return messages.get(0).getSubject(); + } + + /* + * return unique key, should be the same for all msgs + */ + String getUniqueKey() { + return messages.get(0).getUniqueKey(); + } + + /* + * return source service, should be the same for all msgs + */ + IngestServiceAbstract getSource() { + return messages.get(0).getSource(); + } + + /* + * return data of the first message + */ + BlackboardArtifact getData() { + return messages.get(0).getData(); + } + + /* + * return message type, should be the same for all msgs + */ + IngestMessage.MessageType getMessageType() { + return messages.get(0).getMessageType(); + } + } + /** * bold font if not visited, colors for errors * tooltips that show entire query string, disable selection borders */ private class MessageTableRenderer extends DefaultTableCellRenderer { - + @Override public Component getTableCellRendererComponent( JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { - + final Component cell = super.getTableCellRendererComponent( table, value, false, false, row, column); - - if (column < 2) { + + if (column == 0 || column == 2) { String val = (String) table.getModel().getValueAt(row, column); setToolTipText(val); - setText(val); + //setText(val); + } else if (column == 1) { + //Integer val = (Integer) table.getModel().getValueAt(row, column); + //setToolTipText(Integer.toString(val)); } - + if (column == 0) { if (tableModel.isVisited(row)) { cell.setFont(visitedFont); @@ -322,20 +558,22 @@ class IngestMessagePanel extends javax.swing.JPanel { cell.setFont(notVisitedFont); } if (!isSelected) { - MessageType mt = tableModel.getMessageType(row); + final IngestMessageGroup messageGroup = tableModel.getMessageGroup(row); + MessageType mt = messageGroup.getMessageType(); if (mt == MessageType.ERROR) { cell.setBackground(ERROR_COLOR); } else if (mt == MessageType.WARNING) { cell.setBackground(Color.orange); } else { - cell.setBackground(table.getBackground()); + //cell.setBackground(table.getBackground()); + cell.setBackground(messageGroup.getColor()); } } else { super.setForeground(table.getSelectionForeground()); super.setBackground(table.getSelectionBackground()); } } - + if (column == 1) { if (isSelected) { super.setForeground(table.getSelectionForeground()); @@ -344,10 +582,10 @@ class IngestMessagePanel extends javax.swing.JPanel { cell.setBackground(table.getBackground()); } } - + return this; } - + @Override protected void setValue(Object value) { super.setValue(value); @@ -358,9 +596,9 @@ class IngestMessagePanel extends javax.swing.JPanel { * handle table selections / cell visitations */ private class MessageVisitedSelection implements ListSelectionListener { - + private Logger logger = Logger.getLogger(MessageVisitedSelection.class.getName()); - + @Override public void valueChanged(ListSelectionEvent e) { DefaultListSelectionModel selModel = (DefaultListSelectionModel) e.getSource(); @@ -377,14 +615,14 @@ class IngestMessagePanel extends javax.swing.JPanel { if (selected != -1) { setVisited(selected); //check if has details - IngestMessage m = getMessage(selected); + IngestMessageGroup m = getMessageGroup(selected); String details = m.getDetails(); if (details != null && !details.equals("")) { mainPanel.showDetails(selected); } } - - + + } } }