diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/MessagesChildNodeFactory.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/MessagesChildNodeFactory.java index 5039ce225e..2ea076fe0a 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/MessagesChildNodeFactory.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/MessagesChildNodeFactory.java @@ -18,17 +18,15 @@ */ package org.sleuthkit.autopsy.communications.relationships; +import java.util.Comparator; import java.util.List; import java.util.Set; import java.util.logging.Level; import org.openide.nodes.ChildFactory; import org.openide.nodes.Node; -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; -import org.sleuthkit.datamodel.CommunicationsManager; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TskCoreException; @@ -123,6 +121,8 @@ public class MessagesChildNodeFactory extends ChildFactory{ } catch (TskCoreException ex) { logger.log(Level.SEVERE, "Failed to load artifacts for relationship sources.", ex); //NON-NLS } + + list.sort(new DateComparator()); return true; } @@ -132,4 +132,80 @@ public class MessagesChildNodeFactory extends ChildFactory{ return new MessageNode(key, null, null); } + /** + * A comparator class for comparing BlackboardArtifacts of type + * TSK_EMAIL_MSG, TSK_MESSAGE, and TSK_CALLLOG by their respective creation + * date-time. + */ + class DateComparator implements Comparator { + @Override + public int compare(BlackboardArtifact bba1, BlackboardArtifact bba2) { + + BlackboardAttribute attribute1 = null; + BlackboardAttribute attribute2 = null; + // Inializing to Long.MAX_VALUE so that if a BlackboardArtifact of + // any unexpected type is passed in, it will bubble to the top of + // the list. + long dateTime1 = Long.MAX_VALUE; + long dateTime2 = Long.MAX_VALUE; + + if (bba1 != null) { + BlackboardArtifact.ARTIFACT_TYPE fromID = BlackboardArtifact.ARTIFACT_TYPE.fromID(bba1.getArtifactTypeID()); + if (fromID != null) { + try { + switch (fromID) { + case TSK_EMAIL_MSG: + attribute1 = bba1.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_SENT)); + break; + case TSK_MESSAGE: + attribute1 = bba1.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME)); + break; + case TSK_CALLLOG: + attribute1 = bba1.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START)); + break; + default: + attribute1 = null; + break; + } + } catch (TskCoreException ex) { + logger.log(Level.WARNING, String.format("Unable to compare attributes for artifact %d", bba1.getArtifactID()), ex); + } + } + } + + if (bba2 != null) { + BlackboardArtifact.ARTIFACT_TYPE fromID = BlackboardArtifact.ARTIFACT_TYPE.fromID(bba2.getArtifactTypeID()); + if (fromID != null) { + try { + switch (fromID) { + case TSK_EMAIL_MSG: + attribute2 = bba2.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_SENT)); + break; + case TSK_MESSAGE: + attribute2 = bba2.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME)); + break; + case TSK_CALLLOG: + attribute2 = bba2.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START)); + break; + default: + attribute2 = null; + break; + } + } catch (TskCoreException ex) { + logger.log(Level.WARNING, String.format("Unable to compare attributes for artifact %d", bba2.getArtifactID()), ex); + } + } + } + + if (attribute1 != null) { + dateTime1 = attribute1.getValueLong(); + } + + if (attribute2 != null) { + dateTime2 = attribute2.getValueLong(); + } + + return Long.compare(dateTime1, dateTime2); + } + } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/ThreadChildNodeFactory.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/ThreadChildNodeFactory.java index 1cf987d132..9e0a8fcc26 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/ThreadChildNodeFactory.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/ThreadChildNodeFactory.java @@ -18,6 +18,7 @@ */ package org.sleuthkit.autopsy.communications.relationships; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -29,13 +30,10 @@ import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.nodes.Sheet; -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.NodeProperty; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; -import org.sleuthkit.datamodel.CommunicationsManager; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TskCoreException; @@ -144,15 +142,41 @@ final class ThreadChildNodeFactory extends ChildFactory { rootMessageMap.put(threadID, bba); } else { // Get the date of the message - BlackboardAttribute tableAttribute = tableArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_SENT)); - attribute = bba.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_SENT)); - - // put the earliest message into the table - if(tableAttribute != null - && attribute != null - && tableAttribute.getValueLong() > attribute.getValueLong()) { - rootMessageMap.put(threadID, bba); + BlackboardAttribute tableAttribute = null; + switch(fromID) { + case TSK_EMAIL_MSG: + tableAttribute = tableArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_SENT)); + attribute = bba.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_SENT)); + // put the earliest message into the table + if(tableAttribute != null + && attribute != null + && tableAttribute.getValueLong() > attribute.getValueLong()) { + rootMessageMap.put(threadID, bba); + } + break; + case TSK_MESSAGE: + tableAttribute = tableArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME)); + attribute = bba.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME)); + // put the earliest message into the table + if(tableAttribute != null + && attribute != null + && tableAttribute.getValueLong() < attribute.getValueLong()) { + rootMessageMap.put(threadID, bba); + } + break; + case TSK_CALLLOG: + tableAttribute = tableArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START)); + attribute = bba.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START)); + // put the earliest message into the table + if(tableAttribute != null + && attribute != null + && tableAttribute.getValueLong() > attribute.getValueLong()) { + rootMessageMap.put(threadID, bba); + } + break; } + + } } } @@ -160,6 +184,8 @@ final class ThreadChildNodeFactory extends ChildFactory { for(BlackboardArtifact bba: rootMessageMap.values()) { list.add(bba); } + + list.sort(new ThreadDateComparator()); return true; } @@ -242,4 +268,80 @@ final class ThreadChildNodeFactory extends ChildFactory { return sheet; } } + + /** + * A comparator class for comparing BlackboardArtifacts of type + * TSK_EMAIL_MSG, TSK_MESSAGE, and TSK_CALLLOG by their respective creation + * date-time. + * + * Nodes will be sorted newest to oldest. + */ + class ThreadDateComparator implements Comparator { + + @Override + public int compare(BlackboardArtifact bba1, BlackboardArtifact bba2) { + BlackboardAttribute attribute1 = null; + BlackboardAttribute attribute2 = null; + // Inializing to Long.MAX_VALUE so that if a BlackboardArtifact of + // any unexpected type is passed in, it will bubble to the top of + // the list. + long dateTime1 = Long.MAX_VALUE; + long dateTime2 = Long.MAX_VALUE; + + if (bba1 != null) { + BlackboardArtifact.ARTIFACT_TYPE fromID = BlackboardArtifact.ARTIFACT_TYPE.fromID(bba1.getArtifactTypeID()); + if (fromID != null) { + try { + switch (fromID) { + case TSK_EMAIL_MSG: + attribute1 = bba1.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_SENT)); + + break; + case TSK_MESSAGE: + attribute1 = bba1.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME)); + + break; + case TSK_CALLLOG: + attribute1 = bba1.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START)); + + break; + } + } catch (TskCoreException ex) { + logger.log(Level.WARNING, String.format("Unable to compare attributes for artifact %d", bba1.getArtifactID()), ex); + } + } + } + + if (bba1 != null) { + BlackboardArtifact.ARTIFACT_TYPE fromID = BlackboardArtifact.ARTIFACT_TYPE.fromID(bba2.getArtifactTypeID()); + if (fromID != null) { + try { + switch (fromID) { + case TSK_EMAIL_MSG: + attribute2 = bba2.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_SENT)); + break; + case TSK_MESSAGE: + attribute2 = bba2.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME)); + break; + case TSK_CALLLOG: + attribute2 = bba2.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START)); + break; + } + } catch (TskCoreException ex) { + logger.log(Level.WARNING, String.format("Unable to compare attributes for artifact %d", bba2.getArtifactID()), ex); + } + } + } + + if (attribute1 != null) { + dateTime1 = attribute1.getValueLong(); + } + + if (attribute2 != null) { + dateTime2 = attribute2.getValueLong(); + } + + return Long.compare(dateTime1, dateTime2) * -1; + } + } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/ThreadNode.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/ThreadNode.java index 013730a097..43e6e82308 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/ThreadNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/ThreadNode.java @@ -18,11 +18,18 @@ */ package org.sleuthkit.autopsy.communications.relationships; +import java.util.logging.Level; import javax.swing.Action; import org.openide.nodes.AbstractNode; import org.openide.nodes.Children; import org.openide.nodes.Sheet; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.datamodel.NodeProperty; import org.sleuthkit.datamodel.BlackboardArtifact; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE; +import org.sleuthkit.datamodel.BlackboardAttribute; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT; +import org.sleuthkit.datamodel.TskCoreException; /** * An AbstractNode subclass which wraps a MessageNode object. Doing this allows @@ -31,6 +38,10 @@ import org.sleuthkit.datamodel.BlackboardArtifact; */ final class ThreadNode extends AbstractNode{ + private static final Logger logger = Logger.getLogger(ThreadNode.class.getName()); + + private final static int MAX_SUBJECT_LENGTH = 120; + final private MessageNode messageNode; ThreadNode(BlackboardArtifact artifact, String threadID, Action preferredAction) { @@ -41,7 +52,36 @@ final class ThreadNode extends AbstractNode{ @Override protected Sheet createSheet() { - return messageNode.createSheet(); + BlackboardArtifact artifact = messageNode.getArtifact(); + if(artifact == null) { + return messageNode.createSheet() ; + } + + Sheet sheet = messageNode.createSheet(); + BlackboardArtifact.ARTIFACT_TYPE artifactTypeID = BlackboardArtifact.ARTIFACT_TYPE.fromID(artifact.getArtifactTypeID()); + + // If its a text message, replace the subject node which is probably + // an empty string with the firest 120 characters of the text message + if(artifactTypeID != null && artifactTypeID == TSK_MESSAGE) { + try { + BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.fromID(TSK_TEXT.getTypeID()))); + if(attribute != null) { + Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES); + sheetSet.remove("Subject"); + + String msg = attribute.getDisplayString(); + if(msg != null && msg.length() > MAX_SUBJECT_LENGTH) { + msg = msg.substring(0, MAX_SUBJECT_LENGTH) + "..."; + } + + sheetSet.put(new NodeProperty<>("Subject", Bundle.MessageNode_Node_Property_Subject(), "", msg)); //NON-NLS + } + } catch (TskCoreException ex) { + logger.log(Level.WARNING, String.format("Unable to get the text message from message artifact %d", artifact.getId()), ex); + } + } + + return sheet; } String getThreadID() { diff --git a/Core/src/org/sleuthkit/autopsy/logicalimager/configuration/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/logicalimager/configuration/Bundle.properties-MERGED index 7ef61a8452..123dfa3b69 100644 --- a/Core/src/org/sleuthkit/autopsy/logicalimager/configuration/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/logicalimager/configuration/Bundle.properties-MERGED @@ -76,7 +76,7 @@ EditNonFullPathsRulePanel.modifiedDaysNotPositiveException=Modified days must be EditNonFullPathsRulePanel.units.bytes=Bytes EditNonFullPathsRulePanel.units.gigabytes=Gigabytes EditNonFullPathsRulePanel.units.kilobytes=Kilobytes -EditNonFullPathsRulePanel.units.megabytes=MegaBytes +EditNonFullPathsRulePanel.units.megabytes=Megabytes # {0} - fieldName EditRulePanel.blankLineException={0} cannot have a blank line EditRulePanel.emptyRuleName.message=Rule name cannot be empty diff --git a/Core/src/org/sleuthkit/autopsy/logicalimager/configuration/ConfigVisualPanel1.java b/Core/src/org/sleuthkit/autopsy/logicalimager/configuration/ConfigVisualPanel1.java index 1be96b52a2..2765bb1856 100644 --- a/Core/src/org/sleuthkit/autopsy/logicalimager/configuration/ConfigVisualPanel1.java +++ b/Core/src/org/sleuthkit/autopsy/logicalimager/configuration/ConfigVisualPanel1.java @@ -36,9 +36,9 @@ import java.util.logging.Level; import javax.swing.JFileChooser; import javax.swing.JOptionPane; import javax.swing.JPanel; +import javax.swing.SwingUtilities; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; -import javax.swing.SwingUtilities; import javax.swing.filechooser.FileFilter; import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.filechooser.FileSystemView; @@ -251,6 +251,9 @@ final class ConfigVisualPanel1 extends JPanel { int firstRemovableDrive = -1; int i = 0; for (File root : roots) { + if (DriveListUtils.isNetworkDrive(root.toString().replace(":\\", ""))) { + continue; + } String description = FileSystemView.getFileSystemView().getSystemTypeDescription(root); long spaceInBytes = root.getTotalSpace(); String sizeWithUnit = DriveListUtils.humanReadableByteCount(spaceInBytes, false); diff --git a/Core/src/org/sleuthkit/autopsy/logicalimager/configuration/EditNonFullPathsRulePanel.java b/Core/src/org/sleuthkit/autopsy/logicalimager/configuration/EditNonFullPathsRulePanel.java index a48db9b2c4..22dbe55ca3 100644 --- a/Core/src/org/sleuthkit/autopsy/logicalimager/configuration/EditNonFullPathsRulePanel.java +++ b/Core/src/org/sleuthkit/autopsy/logicalimager/configuration/EditNonFullPathsRulePanel.java @@ -66,7 +66,7 @@ final class EditNonFullPathsRulePanel extends javax.swing.JPanel { "EditNonFullPathsRulePanel.example=Example: ", "EditNonFullPathsRulePanel.units.bytes=Bytes", "EditNonFullPathsRulePanel.units.kilobytes=Kilobytes", - "EditNonFullPathsRulePanel.units.megabytes=MegaBytes", + "EditNonFullPathsRulePanel.units.megabytes=Megabytes", "EditNonFullPathsRulePanel.units.gigabytes=Gigabytes" }) EditNonFullPathsRulePanel(JButton okButton, JButton cancelButton, String ruleName, LogicalImagerRule rule, boolean editing) { diff --git a/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/DriveListUtils.java b/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/DriveListUtils.java index 12b10d3000..d033d0fdce 100644 --- a/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/DriveListUtils.java +++ b/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/DriveListUtils.java @@ -18,6 +18,12 @@ */ package org.sleuthkit.autopsy.logicalimager.dsp; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.Arrays; +import java.util.List; + /** * Utility class for displaying a list of drives */ @@ -48,4 +54,33 @@ public final class DriveListUtils { private DriveListUtils() { //empty private constructor for util class } + + /** Use the command net to determine what this drive is. + * net use will return an error for anything which isn't a share. + */ + public static boolean isNetworkDrive(String driveLetter) { + List cmd = Arrays.asList("cmd", "/c", "net", "use", driveLetter + ":"); + + try { + Process p = new ProcessBuilder(cmd) + .redirectErrorStream(true) + .start(); + + p.getOutputStream().close(); + + StringBuilder consoleOutput = new StringBuilder(); + + String line; + try (BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()))) { + while ((line = in.readLine()) != null) { + consoleOutput.append(line).append("\r\n"); + } + } + + int rc = p.waitFor(); + return rc == 0; + } catch(IOException | InterruptedException e) { + return false; // assume not a network drive + } + } } diff --git a/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/LogicalImagerPanel.java b/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/LogicalImagerPanel.java index 880259f95a..d7f4c8b3c1 100644 --- a/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/LogicalImagerPanel.java +++ b/Core/src/org/sleuthkit/autopsy/logicalimager/dsp/LogicalImagerPanel.java @@ -481,6 +481,9 @@ final class LogicalImagerPanel extends JPanel implements DocumentListener { int firstRemovableDrive = -1; int i = 0; for (File root : roots) { + if (DriveListUtils.isNetworkDrive(root.toString().replace(":\\", ""))) { + continue; + } String description = FileSystemView.getFileSystemView().getSystemTypeDescription(root); long spaceInBytes = root.getTotalSpace(); String sizeWithUnit = DriveListUtils.humanReadableByteCount(spaceInBytes, false); diff --git a/nbproject/project.properties b/nbproject/project.properties index 5d59189544..0014be6b88 100644 --- a/nbproject/project.properties +++ b/nbproject/project.properties @@ -6,8 +6,8 @@ app.name=${branding.token} ### if left unset, version will default to today's date app.version=4.12.0 ### build.type must be one of: DEVELOPMENT, RELEASE -#build.type=RELEASE -build.type=DEVELOPMENT +build.type=RELEASE +#build.type=DEVELOPMENT project.org.netbeans.progress=org-netbeans-api-progress project.org.sleuthkit.autopsy.experimental=Experimental