Merge remote-tracking branch 'upstream/develop' into remove-tsk-jfx-dependency

This commit is contained in:
Richard Cordovano 2019-12-04 19:37:06 -05:00
commit a63a0a26dd
22 changed files with 1087 additions and 303 deletions

View File

@ -93,3 +93,7 @@ MediaPlayerPanel.playButton.text=\u25ba
MediaPlayerPanel.infoLabel.text=No Errors MediaPlayerPanel.infoLabel.text=No Errors
MediaPlayerPanel.VolumeIcon.text=Volume MediaPlayerPanel.VolumeIcon.text=Volume
MediaPlayerPanel.playBackSpeedLabel.text=Speed: MediaPlayerPanel.playBackSpeedLabel.text=Speed:
ContextViewer.jSourceGoToResultButton.text=Go to Result
ContextViewer.jSourceNameLabel.text=jSourceNameLabel
ContextViewer.jSourceTextLabel.text=jLabel2
ContextViewer.jSourceLabel.text=Source

View File

@ -10,6 +10,17 @@ AnnotationsContentViewer.title=Annotations
AnnotationsContentViewer.toolTip=Displays tags and comments associated with the selected content. AnnotationsContentViewer.toolTip=Displays tags and comments associated with the selected content.
ApplicationContentViewer.title=Application ApplicationContentViewer.title=Application
ApplicationContentViewer.toolTip=Displays file contents. ApplicationContentViewer.toolTip=Displays file contents.
ContextViewer.attachmentSource=Attached to:
ContextViewer.downloadedOn=On
ContextViewer.downloadSource=Downloaded from:
ContextViewer.downloadURL=URL
ContextViewer.email=Email
ContextViewer.message=Message
ContextViewer.messageFrom=From
ContextViewer.messageOn=On
ContextViewer.messageTo=From
ContextViewer.title=Context Viewer
ContextViewer.toolTip=Displays context for selected file.
FXVideoPanel.pauseButton.infoLabel.playbackErr=Unable to play video. FXVideoPanel.pauseButton.infoLabel.playbackErr=Unable to play video.
FXVideoPanel.progress.bufferingCancelled=media buffering was canceled FXVideoPanel.progress.bufferingCancelled=media buffering was canceled
FXVideoPanel.progress.bufferingInterrupted=media buffering was interrupted FXVideoPanel.progress.bufferingInterrupted=media buffering was interrupted
@ -164,6 +175,10 @@ MediaPlayerPanel.playButton.text=\u25ba
MediaPlayerPanel.infoLabel.text=No Errors MediaPlayerPanel.infoLabel.text=No Errors
MediaPlayerPanel.VolumeIcon.text=Volume MediaPlayerPanel.VolumeIcon.text=Volume
MediaPlayerPanel.playBackSpeedLabel.text=Speed: MediaPlayerPanel.playBackSpeedLabel.text=Speed:
ContextViewer.jSourceGoToResultButton.text=Go to Result
ContextViewer.jSourceNameLabel.text=jSourceNameLabel
ContextViewer.jSourceTextLabel.text=jLabel2
ContextViewer.jSourceLabel.text=Source
# {0} - tableName # {0} - tableName
SQLiteViewer.readTable.errorText=Error getting rows for table: {0} SQLiteViewer.readTable.errorText=Error getting rows for table: {0}
# {0} - tableName # {0} - tableName

View File

@ -0,0 +1,103 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="jSourceLabel" min="-2" max="-2" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="6" max="-2" attributes="0"/>
<Component id="jSourceNameLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="jSourceTextLabel" pref="192" max="32767" attributes="0"/>
</Group>
</Group>
<EmptySpace min="-2" pref="36" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<Component id="jSourceGoToResultButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="jSourceLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="jSourceNameLabel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="jSourceTextLabel" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="jSourceGoToResultButton" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="203" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JButton" name="jSourceGoToResultButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="ContextViewer.jSourceGoToResultButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jSourceGoToResultButtonActionPerformed"/>
</Events>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
</Component>
<Component class="javax.swing.JLabel" name="jSourceLabel">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Dialog" size="14" style="1"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="ContextViewer.jSourceLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
</Component>
<Component class="javax.swing.JLabel" name="jSourceNameLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="ContextViewer.jSourceNameLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="jSourceTextLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="ContextViewer.jSourceTextLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Form>

View File

@ -0,0 +1,440 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.contentviewers;
import java.awt.Component;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import org.apache.commons.lang.StringUtils;
import org.openide.nodes.Node;
import org.openide.util.NbBundle;
import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.directorytree.DirectoryTreeTopComponent;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Displays additional context for the selected file, such as its source, and
* usage, if known.
*
*/
@ServiceProvider(service = DataContentViewer.class, position = 7)
public final class ContextViewer extends javax.swing.JPanel implements DataContentViewer {
private static final long serialVersionUID = 1L;
private static final Logger logger = Logger.getLogger(ContextViewer.class.getName());
private static final int ARTIFACT_STR_MAX_LEN = 1024;
private static final int ATTRIBUTE_STR_MAX_LEN = 200;
// defines a list of artifacts that provide context for a file
private static final List<BlackboardArtifact.ARTIFACT_TYPE> SOURCE_CONTEXT_ARTIFACTS = new ArrayList<>();
static {
SOURCE_CONTEXT_ARTIFACTS.add(TSK_ASSOCIATED_OBJECT);
}
private BlackboardArtifact sourceContextArtifact;
/**
* Creates new form ContextViewer
*/
public ContextViewer() {
initComponents();
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
javax.swing.JButton jSourceGoToResultButton = new javax.swing.JButton();
javax.swing.JLabel jSourceLabel = new javax.swing.JLabel();
jSourceNameLabel = new javax.swing.JLabel();
jSourceTextLabel = new javax.swing.JLabel();
org.openide.awt.Mnemonics.setLocalizedText(jSourceGoToResultButton, org.openide.util.NbBundle.getMessage(ContextViewer.class, "ContextViewer.jSourceGoToResultButton.text")); // NOI18N
jSourceGoToResultButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jSourceGoToResultButtonActionPerformed(evt);
}
});
jSourceLabel.setFont(new java.awt.Font("Dialog", 1, 14)); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(jSourceLabel, org.openide.util.NbBundle.getMessage(ContextViewer.class, "ContextViewer.jSourceLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(jSourceNameLabel, org.openide.util.NbBundle.getMessage(ContextViewer.class, "ContextViewer.jSourceNameLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(jSourceTextLabel, org.openide.util.NbBundle.getMessage(ContextViewer.class, "ContextViewer.jSourceTextLabel.text")); // NOI18N
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jSourceLabel)
.addGroup(layout.createSequentialGroup()
.addGap(6, 6, 6)
.addComponent(jSourceNameLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jSourceTextLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 192, Short.MAX_VALUE)))
.addGap(36, 36, 36))
.addGroup(layout.createSequentialGroup()
.addComponent(jSourceGoToResultButton)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(jSourceLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jSourceNameLabel)
.addComponent(jSourceTextLabel))
.addGap(18, 18, 18)
.addComponent(jSourceGoToResultButton)
.addGap(0, 203, Short.MAX_VALUE))
);
}// </editor-fold>//GEN-END:initComponents
private void jSourceGoToResultButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jSourceGoToResultButtonActionPerformed
final DirectoryTreeTopComponent dtc = DirectoryTreeTopComponent.findInstance();
// Navigate to the source context artifact.
if (sourceContextArtifact != null) {
dtc.viewArtifact(sourceContextArtifact);
}
}//GEN-LAST:event_jSourceGoToResultButtonActionPerformed
@Override
public void setNode(Node selectedNode) {
if ((selectedNode == null) || (!isSupported(selectedNode))) {
resetComponent();
return;
}
AbstractFile file = selectedNode.getLookup().lookup(AbstractFile.class);
try {
populateSourceContextData(file);
} catch (NoCurrentCaseException | TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Exception displaying context for file %s", file.getName()), ex); //NON-NLS
}
}
@NbBundle.Messages({
"ContextViewer.title=Context Viewer",
"ContextViewer.toolTip=Displays context for selected file."
})
@Override
public String getTitle() {
return Bundle.ContextViewer_title();
}
@Override
public String getToolTip() {
return Bundle.ContextViewer_toolTip();
}
@Override
public DataContentViewer createInstance() {
return new ContextViewer();
}
@Override
public Component getComponent() {
return this;
}
@Override
public void resetComponent() {
setSourceName("");
setSourceText("");
}
@Override
public boolean isSupported(Node node) {
// check if the node has an abstract file and the file has any context defining artifacts.
if (node.getLookup().lookup(AbstractFile.class) != null) {
AbstractFile abstractFile = node.getLookup().lookup(AbstractFile.class);
for (BlackboardArtifact.ARTIFACT_TYPE artifactType : SOURCE_CONTEXT_ARTIFACTS) {
List<BlackboardArtifact> artifactsList;
try {
artifactsList = abstractFile.getArtifacts(artifactType);
if (!artifactsList.isEmpty()) {
return true;
}
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Exception while looking up context artifacts for file %s", abstractFile), ex); //NON-NLS
}
}
}
return false;
}
@Override
public int isPreferred(Node node) {
// this is a low preference viewer.
return 1;
}
/**
* Looks for context providing artifacts for the given file and populates
* the source context.
*
* @param sourceFile File for which to show the context.
*
* @throws NoCurrentCaseException
* @throws TskCoreException
*/
private void populateSourceContextData(AbstractFile sourceFile) throws NoCurrentCaseException, TskCoreException {
SleuthkitCase tskCase = Case.getCurrentCaseThrows().getSleuthkitCase();
// Check for all context artifacts
boolean foundASource = false;
for (BlackboardArtifact.ARTIFACT_TYPE artifactType : SOURCE_CONTEXT_ARTIFACTS) {
List<BlackboardArtifact> artifactsList = tskCase.getBlackboardArtifacts(artifactType, sourceFile.getId());
foundASource = !artifactsList.isEmpty();
for (BlackboardArtifact contextArtifact : artifactsList) {
addSourceEntry(contextArtifact);
}
}
if (foundASource == false) {
setSourceName("Unknown");
showSourceText(false);
}
}
/**
* Adds a source context entry for the selected file based on the given context
* providing artifact.
*
* @param artifact Artifact that may provide context.
*
* @throws NoCurrentCaseException
* @throws TskCoreException
*/
private void addSourceEntry(BlackboardArtifact artifact) throws TskCoreException {
if (BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT.getTypeID() == artifact.getArtifactTypeID()) {
BlackboardAttribute associatedArtifactAttribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT));
if (associatedArtifactAttribute != null) {
long artifactId = associatedArtifactAttribute.getValueLong();
BlackboardArtifact associatedArtifact = artifact.getSleuthkitCase().getBlackboardArtifact(artifactId);
//save the artifact for "Go to Result" button
sourceContextArtifact = associatedArtifact;
setSourceFields(associatedArtifact);
}
}
}
/**
* Sets the source label and text fields based on the given associated
* artifact.
*
* @param associatedArtifact - associated artifact
*
* @throws TskCoreException
*/
@NbBundle.Messages({
"ContextViewer.attachmentSource=Attached to: ",
"ContextViewer.downloadSource=Downloaded from: "
})
private void setSourceFields(BlackboardArtifact associatedArtifact) throws TskCoreException {
if (BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE.getTypeID() == associatedArtifact.getArtifactTypeID()
|| BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID() == associatedArtifact.getArtifactTypeID()) {
setSourceName(Bundle.ContextViewer_attachmentSource());
setSourceText(msgArtifactToAbbreviatedString(associatedArtifact));
} else if (BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID() == associatedArtifact.getArtifactTypeID()
|| BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE.getTypeID() == associatedArtifact.getArtifactTypeID()) {
setSourceName(Bundle.ContextViewer_downloadSource());
setSourceText(webDownloadArtifactToString(associatedArtifact));
}
}
/**
* Sets the source label string.
*
* @param nameLabel String value for source label.
*/
private void setSourceName(String nameLabel) {
jSourceNameLabel.setText(nameLabel);
}
/**
* Sets the source text string.
*
* @param nameLabel String value for source text.
*/
private void setSourceText(String text) {
jSourceTextLabel.setText(text);
showSourceText(true);
}
private void showSourceText(boolean isVisible) {
jSourceTextLabel.setVisible(isVisible);
}
/**
* Returns a display string with download source URL from the given
* artifact.
*
* @param artifact artifact to get download source URL from.
*
* @return Display string with download URL and date/time.
*
* @throws TskCoreException
*/
@NbBundle.Messages({
"ContextViewer.downloadURL=URL",
"ContextViewer.downloadedOn=On"
})
private String webDownloadArtifactToString(BlackboardArtifact artifact) throws TskCoreException {
StringBuilder sb = new StringBuilder(ARTIFACT_STR_MAX_LEN);
Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> attributesMap = getAttributesMap(artifact);
if (BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID() == artifact.getArtifactTypeID()
|| BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE.getTypeID() == artifact.getArtifactTypeID()) {
appendAttributeString(sb, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_URL, attributesMap, Bundle.ContextViewer_downloadURL());
appendAttributeString(sb, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED, attributesMap, Bundle.ContextViewer_downloadedOn());
}
return sb.toString();
}
/**
* Returns a abbreviated display string for a message artifact.
*
* @param artifact artifact to get download source URL from.
*
* @return Display string for message artifact.
*
* @throws TskCoreException
*/
@NbBundle.Messages({
"ContextViewer.message=Message",
"ContextViewer.email=Email",
"ContextViewer.messageFrom=From",
"ContextViewer.messageTo=From",
"ContextViewer.messageOn=On",
})
private String msgArtifactToAbbreviatedString(BlackboardArtifact artifact) throws TskCoreException {
StringBuilder sb = new StringBuilder(ARTIFACT_STR_MAX_LEN);
Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> attributesMap = getAttributesMap(artifact);
if (BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE.getTypeID() == artifact.getArtifactTypeID()) {
sb.append(Bundle.ContextViewer_message()).append(' ');
appendAttributeString(sb, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM, attributesMap, Bundle.ContextViewer_messageFrom());
appendAttributeString(sb, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO, attributesMap, Bundle.ContextViewer_messageTo());
appendAttributeString(sb, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, attributesMap, Bundle.ContextViewer_messageOn());
} else if (BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID() == artifact.getArtifactTypeID()) {
sb.append(Bundle.ContextViewer_email()).append(' ');
appendAttributeString(sb, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL_FROM, attributesMap, Bundle.ContextViewer_messageFrom());
appendAttributeString(sb, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL_TO, attributesMap, Bundle.ContextViewer_messageTo());
appendAttributeString(sb, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_SENT, attributesMap, Bundle.ContextViewer_messageOn());
}
return sb.toString();
}
/**
* Looks up specified attribute in the given map and, if found, appends its
* value to the given string builder.
*
* @param sb String builder to append to.
* @param attribType Attribute type to look for.
* @param attributesMap Attributes map.
* @param prependStr Optional string that is prepended before the attribute
* value.
*/
private void appendAttributeString(StringBuilder sb, BlackboardAttribute.ATTRIBUTE_TYPE attribType,
Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> attributesMap, String prependStr) {
BlackboardAttribute attribute = attributesMap.get(attribType);
if (attribute != null) {
String attrVal = attribute.getDisplayString();
if (!StringUtils.isEmpty(attrVal)) {
if (!StringUtils.isEmpty(prependStr)) {
sb.append(prependStr).append(' ');
}
sb.append(StringUtils.abbreviate(attrVal, ATTRIBUTE_STR_MAX_LEN)).append(' ');
}
}
}
/**
* Gets all attributes for the given artifact, and returns a map of
* attributes keyed by attribute type.
*
* @param artifact Artifact for which to get the attributes.
*
* @return Map of attribute type and value.
*
* @throws TskCoreException
*/
private Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> getAttributesMap(BlackboardArtifact artifact) throws TskCoreException {
Map<BlackboardAttribute.ATTRIBUTE_TYPE, BlackboardAttribute> attributeMap = new HashMap<>();
List<BlackboardAttribute> attributeList = artifact.getAttributes();
for (BlackboardAttribute attribute : attributeList) {
BlackboardAttribute.ATTRIBUTE_TYPE type = BlackboardAttribute.ATTRIBUTE_TYPE.fromID(attribute.getAttributeType().getTypeID());
attributeMap.put(type, attribute);
}
return attributeMap;
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel jSourceNameLabel;
private javax.swing.JLabel jSourceTextLabel;
// End of variables declaration//GEN-END:variables
}

View File

@ -191,12 +191,14 @@ public class Metadata extends javax.swing.JPanel implements DataContentViewer {
} }
try { try {
List<BlackboardArtifact> sourceArtifacts = file.getArtifacts(ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE); List<BlackboardArtifact> associatedObjectArtifacts = file.getArtifacts(ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT);
if (!sourceArtifacts.isEmpty()) { if (!associatedObjectArtifacts.isEmpty()) {
BlackboardArtifact artifact = sourceArtifacts.get(0); BlackboardArtifact artifact = associatedObjectArtifacts.get(0);
BlackboardAttribute urlAttr = artifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_URL)); BlackboardAttribute associatedArtifactAttribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT));
if (urlAttr != null) { if (associatedArtifactAttribute != null) {
addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.downloadSource"), urlAttr.getValueString()); long artifactId = associatedArtifactAttribute.getValueLong();
BlackboardArtifact associatedArtifact = artifact.getSleuthkitCase().getBlackboardArtifact(artifactId);
addDownloadSourceRow(sb, associatedArtifact);
} }
} }
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
@ -292,6 +294,26 @@ public class Metadata extends javax.swing.JPanel implements DataContentViewer {
this.setCursor(null); this.setCursor(null);
} }
/**
* Adds a row for download source from the given associated artifact,
* if the associated artifacts specifies a source.
*
* @param sb string builder.
* @param associatedArtifact
*
* @throws TskCoreException if there is an error
*/
private void addDownloadSourceRow(StringBuilder sb, BlackboardArtifact associatedArtifact ) throws TskCoreException {
if (associatedArtifact != null &&
((associatedArtifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID()) ||
(associatedArtifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_WEB_CACHE.getTypeID())) ) {
BlackboardAttribute urlAttr = associatedArtifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_URL));
if (urlAttr != null) {
addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.downloadSource"), urlAttr.getValueString());
}
}
}
/** /**
* Add the acquisition details to the results (if applicable) * Add the acquisition details to the results (if applicable)
* *

View File

@ -52,7 +52,11 @@ final class XRYFileReader implements AutoCloseable {
//Assume UTF_16LE //Assume UTF_16LE
private static final Charset CHARSET = StandardCharsets.UTF_16LE; private static final Charset CHARSET = StandardCharsets.UTF_16LE;
//Assume all XRY reports have the type on the 3rd line. //Assume the header begins with 'xry export'.
private static final String START_OF_HEADER = "xry export";
//Assume all XRY reports have the type on the 3rd line
//relative to the start of the header.
private static final int LINE_WITH_REPORT_TYPE = 3; private static final int LINE_WITH_REPORT_TYPE = 3;
//Assume all headers are 5 lines in length. //Assume all headers are 5 lines in length.
@ -91,8 +95,12 @@ final class XRYFileReader implements AutoCloseable {
reader = Files.newBufferedReader(xryFile, CHARSET); reader = Files.newBufferedReader(xryFile, CHARSET);
xryFilePath = xryFile; xryFilePath = xryFile;
//Advance the reader to the start of the first XRY entity. //Advance the reader to the start of the header.
for (int i = 0; i < HEADER_LENGTH_IN_LINES; i++) { advanceToHeader(reader);
//Advance the reader past the header to the start
//of the first XRY entity.
for (int i = 1; i < HEADER_LENGTH_IN_LINES; i++) {
reader.readLine(); reader.readLine();
} }
@ -298,8 +306,11 @@ final class XRYFileReader implements AutoCloseable {
*/ */
private static Optional<String> getType(Path file) throws IOException { private static Optional<String> getType(Path file) throws IOException {
try (BufferedReader reader = Files.newBufferedReader(file, CHARSET)) { try (BufferedReader reader = Files.newBufferedReader(file, CHARSET)) {
//Header may not start at the beginning of the file.
advanceToHeader(reader);
//Advance the reader to the line before the report type. //Advance the reader to the line before the report type.
for (int i = 0; i < LINE_WITH_REPORT_TYPE - 1; i++) { for (int i = 1; i < LINE_WITH_REPORT_TYPE - 1; i++) {
reader.readLine(); reader.readLine();
} }
@ -310,4 +321,51 @@ final class XRYFileReader implements AutoCloseable {
return Optional.empty(); return Optional.empty();
} }
} }
/**
* Advances the reader to the start of the header. The XRY Export header may
* not be the first n lines of the file. It may be preceded by new lines or
* white space.
*
* This function will consume the first line of the header, which will be
* 'XRY Export'.
*
* @param reader BufferedReader pointing to the xry file
* @throws IOException if an I/O error occurs
*/
private static void advanceToHeader(BufferedReader reader) throws IOException {
String line;
if((line = reader.readLine()) == null) {
return;
}
String normalizedLine = line.trim().toLowerCase();
if (normalizedLine.equals(START_OF_HEADER)) {
return;
}
/**
* The first line may have 0xFFFE BOM prepended to it, which will cause
* the equality check to fail. This bit a logic will try to remove those
* bytes and attempt another check.
*/
byte[] normalizedBytes = normalizedLine.getBytes(CHARSET);
if (normalizedBytes.length > 2) {
normalizedLine = new String(normalizedBytes, 2,
normalizedBytes.length - 2, CHARSET);
if (normalizedLine.equals(START_OF_HEADER)) {
return;
}
}
/**
* All other lines will need to match completely.
*/
while ((line = reader.readLine()) != null) {
normalizedLine = line.trim().toLowerCase();
if (normalizedLine.equals(START_OF_HEADER)) {
return;
}
}
}
} }

View File

@ -103,6 +103,7 @@ final class CheckBoxJList<T extends CheckBoxJList.CheckboxListItem> extends JLis
setBackground(list.getBackground()); setBackground(list.getBackground());
setSelected(value.isChecked()); setSelected(value.isChecked());
setText(value.getDisplayName()); setText(value.getDisplayName());
setEnabled(list.isEnabled());
return this; return this;
} }
} }

View File

@ -37,10 +37,6 @@
<Events> <Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="uncheckButtonActionPerformed"/> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="uncheckButtonActionPerformed"/>
</Events> </Events>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Constraints> <Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription"> <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="1" gridY="2" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="9" anchor="12" weightX="1.0" weightY="0.0"/> <GridBagConstraints gridX="1" gridY="2" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="9" anchor="12" weightX="1.0" weightY="0.0"/>
@ -56,10 +52,6 @@
<Events> <Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="checkButtonActionPerformed"/> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="checkButtonActionPerformed"/>
</Events> </Events>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Constraints> <Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription"> <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="2" gridY="2" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="12" weightX="0.0" weightY="0.0"/> <GridBagConstraints gridX="2" gridY="2" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="12" weightX="0.0" weightY="0.0"/>

View File

@ -62,6 +62,14 @@ final class CheckBoxListPanel<T> extends javax.swing.JPanel {
model.removeAllElements(); model.removeAllElements();
} }
@Override
public void setEnabled(boolean enabled) {
checkboxList.setEnabled(enabled);
checkButton.setEnabled(enabled);
uncheckButton.setEnabled(enabled);
checkboxList.setEnabled(enabled);
}
/** /**
* Returns a list of all of the selected elements. * Returns a list of all of the selected elements.
* *
@ -126,8 +134,8 @@ final class CheckBoxListPanel<T> extends javax.swing.JPanel {
java.awt.GridBagConstraints gridBagConstraints; java.awt.GridBagConstraints gridBagConstraints;
titleLabel = new javax.swing.JLabel(); titleLabel = new javax.swing.JLabel();
javax.swing.JButton uncheckButton = new javax.swing.JButton(); uncheckButton = new javax.swing.JButton();
javax.swing.JButton checkButton = new javax.swing.JButton(); checkButton = new javax.swing.JButton();
scrollPane = new javax.swing.JScrollPane(); scrollPane = new javax.swing.JScrollPane();
setLayout(new java.awt.GridBagLayout()); setLayout(new java.awt.GridBagLayout());
@ -186,8 +194,10 @@ final class CheckBoxListPanel<T> extends javax.swing.JPanel {
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton checkButton;
private javax.swing.JScrollPane scrollPane; private javax.swing.JScrollPane scrollPane;
private javax.swing.JLabel titleLabel; private javax.swing.JLabel titleLabel;
private javax.swing.JButton uncheckButton;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
/** /**

View File

@ -115,10 +115,6 @@
<ResourceString bundle="org/sleuthkit/autopsy/geolocation/Bundle.properties" key="GeoFilterPanel.daysLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/geolocation/Bundle.properties" key="GeoFilterPanel.daysLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Constraints> <Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription"> <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="3" gridY="1" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="9" insetsLeft="5" insetsBottom="0" insetsRight="0" anchor="17" weightX="1.0" weightY="0.0"/> <GridBagConstraints gridX="3" gridY="1" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="9" insetsLeft="5" insetsBottom="0" insetsRight="0" anchor="17" weightX="1.0" weightY="0.0"/>

View File

@ -74,6 +74,20 @@ class GeoFilterPanel extends javax.swing.JPanel {
add(checkboxPanel, gridBagConstraints); add(checkboxPanel, gridBagConstraints);
} }
@Override
public void setEnabled(boolean enabled) {
applyButton.setEnabled(enabled);
mostRecentButton.setEnabled(enabled);
allButton.setEnabled(enabled);
showWaypointsWOTSCheckBox.setEnabled(enabled && mostRecentButton.isSelected());
checkboxPanel.setEnabled(enabled);
daysLabel.setEnabled(enabled);
daysSpinner.setEnabled(enabled);
}
/**
* Update the data source list with the current data sources
*/
void updateDataSourceList() { void updateDataSourceList() {
try { try {
initCheckboxList(); initCheckboxList();
@ -155,7 +169,7 @@ class GeoFilterPanel extends javax.swing.JPanel {
mostRecentButton = new javax.swing.JRadioButton(); mostRecentButton = new javax.swing.JRadioButton();
showWaypointsWOTSCheckBox = new javax.swing.JCheckBox(); showWaypointsWOTSCheckBox = new javax.swing.JCheckBox();
daysSpinner = new javax.swing.JSpinner(numberModel); daysSpinner = new javax.swing.JSpinner(numberModel);
javax.swing.JLabel daysLabel = new javax.swing.JLabel(); daysLabel = new javax.swing.JLabel();
javax.swing.JPanel buttonPanel = new javax.swing.JPanel(); javax.swing.JPanel buttonPanel = new javax.swing.JPanel();
applyButton = new javax.swing.JButton(); applyButton = new javax.swing.JButton();
javax.swing.JLabel optionsLabel = new javax.swing.JLabel(); javax.swing.JLabel optionsLabel = new javax.swing.JLabel();
@ -272,6 +286,7 @@ class GeoFilterPanel extends javax.swing.JPanel {
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JRadioButton allButton; private javax.swing.JRadioButton allButton;
private javax.swing.JButton applyButton; private javax.swing.JButton applyButton;
private javax.swing.JLabel daysLabel;
private javax.swing.JSpinner daysSpinner; private javax.swing.JSpinner daysSpinner;
private javax.swing.JRadioButton mostRecentButton; private javax.swing.JRadioButton mostRecentButton;
private javax.swing.JCheckBox showWaypointsWOTSCheckBox; private javax.swing.JCheckBox showWaypointsWOTSCheckBox;

View File

@ -22,6 +22,7 @@ import java.awt.BorderLayout;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@ -171,6 +172,7 @@ public final class GeolocationTopComponent extends TopComponent {
logger.log(Level.SEVERE, ex.getMessage(), ex); logger.log(Level.SEVERE, ex.getMessage(), ex);
return; // Doen't set the waypoints. return; // Doen't set the waypoints.
} }
mapPanel.setWaypoints(new ArrayList<>());
updateWaypoints(); updateWaypoints();
} }
@ -214,35 +216,11 @@ public final class GeolocationTopComponent extends TopComponent {
return; return;
} }
SwingUtilities.invokeLater(new Runnable() { mapPanel.setWaypointLoading(true);
public void run() { geoFilterPanel.setEnabled(false);
Case currentCase = Case.getCurrentCase();
try {
WaypointBuilder.getAllWaypoints(currentCase.getSleuthkitCase(), filters.getDataSources(), filters.showAllWaypoints(), filters.getMostRecentNumDays(), filters.showWaypointsWithoutTimeStamp(), new WaypointFilterQueryCallBack() {
@Override
public void process(List<Waypoint> waypoints) {
// If the list is empty, tell the user and do not change
// the visible waypoints.
if (waypoints == null || waypoints.isEmpty()) {
JOptionPane.showMessageDialog(GeolocationTopComponent.this,
Bundle.GeoTopComponent_no_waypoints_returned_Title(),
Bundle.GeoTopComponent_no_waypoints_returned_mgs(),
JOptionPane.INFORMATION_MESSAGE);
return; Thread thread = new Thread(new WaypointRunner(filters));
} thread.start();
mapPanel.setWaypoints(MapWaypoint.getWaypoints(waypoints));
}
});
} catch (GeoLocationDataException ex) {
logger.log(Level.SEVERE, "Failed to filter waypoints.", ex);
JOptionPane.showMessageDialog(GeolocationTopComponent.this,
Bundle.GeoTopComponent_filter_exception_Title(),
Bundle.GeoTopComponent_filter_exception_msg(),
JOptionPane.ERROR_MESSAGE);
}
}
});
} }
/** /**
@ -269,4 +247,76 @@ public final class GeolocationTopComponent extends TopComponent {
private org.sleuthkit.autopsy.geolocation.HidingPane filterPane; private org.sleuthkit.autopsy.geolocation.HidingPane filterPane;
private org.sleuthkit.autopsy.geolocation.MapPanel mapPanel; private org.sleuthkit.autopsy.geolocation.MapPanel mapPanel;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
/**
* A runnable class for getting waypoints based on the current filters.
*/
private class WaypointRunner implements Runnable {
private final GeoFilter filters;
/**
* Constructs the Waypoint Runner
*
* @param filters
*/
WaypointRunner(GeoFilter filters) {
this.filters = filters;
}
@Override
public void run() {
Case currentCase = Case.getCurrentCase();
try {
WaypointBuilder.getAllWaypoints(currentCase.getSleuthkitCase(),
filters.getDataSources(),
filters.showAllWaypoints(),
filters.getMostRecentNumDays(),
filters.showWaypointsWithoutTimeStamp(),
new WaypointCallBack());
} catch (GeoLocationDataException ex) {
logger.log(Level.SEVERE, "Failed to filter waypoints.", ex);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
JOptionPane.showMessageDialog(GeolocationTopComponent.this,
Bundle.GeoTopComponent_filter_exception_Title(),
Bundle.GeoTopComponent_filter_exception_msg(),
JOptionPane.ERROR_MESSAGE);
}
});
}
}
}
/**
* Callback for getting waypoints.
*/
private class WaypointCallBack implements WaypointFilterQueryCallBack {
@Override
public void process(List<Waypoint> waypoints) {
// Make sure that the waypoints are added to the map panel in
// the correct thread.
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
// If the list is empty, tell the user and do not change
// the visible waypoints.
if (waypoints == null || waypoints.isEmpty()) {
JOptionPane.showMessageDialog(GeolocationTopComponent.this,
Bundle.GeoTopComponent_no_waypoints_returned_Title(),
Bundle.GeoTopComponent_no_waypoints_returned_mgs(),
JOptionPane.INFORMATION_MESSAGE);
return;
}
mapPanel.setWaypoints(MapWaypoint.getWaypoints(waypoints));
geoFilterPanel.setEnabled(true);
}
});
}
}
} }

View File

@ -98,7 +98,7 @@
</Constraint> </Constraint>
</Constraints> </Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/> <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<SubComponents> <SubComponents>
<Component class="javax.swing.JLabel" name="cordLabel"> <Component class="javax.swing.JLabel" name="cordLabel">
<Properties> <Properties>
@ -107,8 +107,19 @@
</Property> </Property>
</Properties> </Properties>
<Constraints> <Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription"> <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<BorderConstraints direction="East"/> <GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="5" insetsBottom="0" insetsRight="5" anchor="18" weightX="1.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JProgressBar" name="progressBar">
<Properties>
<Property name="indeterminate" type="boolean" value="true"/>
<Property name="stringPainted" type="boolean" value="true"/>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="1" gridY="0" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="12" weightX="0.0" weightY="0.0"/>
</Constraint> </Constraint>
</Constraints> </Constraints>
</Component> </Component>

View File

@ -179,6 +179,17 @@ final public class MapPanel extends javax.swing.JPanel {
mapViewer.setOverlayPainter(waypointPainter); mapViewer.setOverlayPainter(waypointPainter);
} }
/**
* Show or hide the waypoint loading progress bar.
*
* @param loading
*/
void setWaypointLoading(boolean loading) {
progressBar.setEnabled(true);
progressBar.setVisible(loading);
progressBar.setString("Loading Waypoints");
}
/** /**
* Setup the zoom slider based on the current tileFactory. * Setup the zoom slider based on the current tileFactory.
*/ */
@ -265,6 +276,7 @@ final public class MapPanel extends javax.swing.JPanel {
} }
mapViewer.repaint(); mapViewer.repaint();
setWaypointLoading(false);
} }
/** /**
@ -486,6 +498,7 @@ final public class MapPanel extends javax.swing.JPanel {
zoomSlider = new javax.swing.JSlider(); zoomSlider = new javax.swing.JSlider();
infoPanel = new javax.swing.JPanel(); infoPanel = new javax.swing.JPanel();
cordLabel = new javax.swing.JLabel(); cordLabel = new javax.swing.JLabel();
progressBar = new javax.swing.JProgressBar();
setFocusable(false); setFocusable(false);
setLayout(new java.awt.BorderLayout()); setLayout(new java.awt.BorderLayout());
@ -553,10 +566,24 @@ final public class MapPanel extends javax.swing.JPanel {
add(mapViewer, java.awt.BorderLayout.CENTER); add(mapViewer, java.awt.BorderLayout.CENTER);
infoPanel.setLayout(new java.awt.BorderLayout()); infoPanel.setLayout(new java.awt.GridBagLayout());
org.openide.awt.Mnemonics.setLocalizedText(cordLabel, org.openide.util.NbBundle.getMessage(MapPanel.class, "MapPanel.cordLabel.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(cordLabel, org.openide.util.NbBundle.getMessage(MapPanel.class, "MapPanel.cordLabel.text")); // NOI18N
infoPanel.add(cordLabel, java.awt.BorderLayout.EAST); gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.insets = new java.awt.Insets(0, 5, 0, 5);
infoPanel.add(cordLabel, gridBagConstraints);
progressBar.setIndeterminate(true);
progressBar.setStringPainted(true);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 0;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST;
infoPanel.add(progressBar, gridBagConstraints);
add(infoPanel, java.awt.BorderLayout.SOUTH); add(infoPanel, java.awt.BorderLayout.SOUTH);
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
@ -594,6 +621,7 @@ final public class MapPanel extends javax.swing.JPanel {
private javax.swing.JLabel cordLabel; private javax.swing.JLabel cordLabel;
private javax.swing.JPanel infoPanel; private javax.swing.JPanel infoPanel;
private org.jxmapviewer.JXMapViewer mapViewer; private org.jxmapviewer.JXMapViewer mapViewer;
private javax.swing.JProgressBar progressBar;
private javax.swing.JPanel zoomPanel; private javax.swing.JPanel zoomPanel;
private javax.swing.JSlider zoomSlider; private javax.swing.JSlider zoomSlider;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables

View File

@ -103,8 +103,7 @@ public class PlasoIngestModule implements DataSourceIngestModule {
@NbBundle.Messages({ @NbBundle.Messages({
"PlasoIngestModule.executable.not.found=Plaso Executable Not Found.", "PlasoIngestModule.executable.not.found=Plaso Executable Not Found.",
"PlasoIngestModule.requires.windows=Plaso module requires windows.", "PlasoIngestModule.requires.windows=Plaso module requires windows."})
"PlasoIngestModule.dataSource.not.an.image=Datasource is not an Image."})
@Override @Override
public void startUp(IngestJobContext context) throws IngestModuleException { public void startUp(IngestJobContext context) throws IngestModuleException {
this.context = context; this.context = context;
@ -121,11 +120,6 @@ public class PlasoIngestModule implements DataSourceIngestModule {
throw new IngestModuleException(Bundle.PlasoIngestModule_executable_not_found(), exception); throw new IngestModuleException(Bundle.PlasoIngestModule_executable_not_found(), exception);
} }
Content dataSource = context.getDataSource();
if (!(dataSource instanceof Image)) {
throw new IngestModuleException(Bundle.PlasoIngestModule_dataSource_not_an_image());
}
image = (Image) dataSource;
} }
@NbBundle.Messages({ @NbBundle.Messages({
@ -138,11 +132,20 @@ public class PlasoIngestModule implements DataSourceIngestModule {
"PlasoIngestModule.psort.cancelled=psort run was canceled", "PlasoIngestModule.psort.cancelled=psort run was canceled",
"PlasoIngestModule.bad.imageFile=Cannot find image file name and path", "PlasoIngestModule.bad.imageFile=Cannot find image file name and path",
"PlasoIngestModule.completed=Plaso Processing Completed", "PlasoIngestModule.completed=Plaso Processing Completed",
"PlasoIngestModule.has.run=Plaso Plugin has been run.", "PlasoIngestModule.has.run=Plaso",
"PlasoIngestModule.psort.fail=Plaso returned an error when sorting events. Results are not complete."}) "PlasoIngestModule.psort.fail=Plaso returned an error when sorting events. Results are not complete.",
"PlasoIngestModule.dataSource.not.an.image=Skipping non-disk image datasource"})
@Override @Override
public ProcessResult process(Content dataSource, DataSourceIngestModuleProgress statusHelper) { public ProcessResult process(Content dataSource, DataSourceIngestModuleProgress statusHelper) {
assert dataSource.equals(image);
if (!(dataSource instanceof Image)) {
IngestMessage message = IngestMessage.createMessage(IngestMessage.MessageType.DATA,
Bundle.PlasoIngestModule_has_run(),
Bundle.PlasoIngestModule_dataSource_not_an_image());
IngestServices.getInstance().postMessage(message);
return ProcessResult.OK;
} else {
image = (Image) dataSource;
statusHelper.switchToDeterminate(100); statusHelper.switchToDeterminate(100);
currentCase = Case.getCurrentCase(); currentCase = Case.getCurrentCase();
@ -213,6 +216,7 @@ public class PlasoIngestModule implements DataSourceIngestModule {
IngestServices.getInstance().postMessage(message); IngestServices.getInstance().postMessage(message);
return ProcessResult.OK; return ProcessResult.OK;
} }
}
private ProcessBuilder buildLog2TimeLineCommand(Path moduleOutputPath, Image image) { private ProcessBuilder buildLog2TimeLineCommand(Path moduleOutputPath, Image image) {
//make a csv list of disabled parsers. //make a csv list of disabled parsers.
@ -240,8 +244,10 @@ public class PlasoIngestModule implements DataSourceIngestModule {
static private ProcessBuilder buildProcessWithRunAsInvoker(String... commandLine) { static private ProcessBuilder buildProcessWithRunAsInvoker(String... commandLine) {
ProcessBuilder processBuilder = new ProcessBuilder(commandLine); ProcessBuilder processBuilder = new ProcessBuilder(commandLine);
/* Add an environment variable to force log2timeline/psort to run with /*
* the same permissions Autopsy uses. */ * Add an environment variable to force log2timeline/psort to run with
* the same permissions Autopsy uses.
*/
processBuilder.environment().put("__COMPAT_LAYER", "RunAsInvoker"); //NON-NLS processBuilder.environment().put("__COMPAT_LAYER", "RunAsInvoker"); //NON-NLS
return processBuilder; return processBuilder;
} }
@ -277,8 +283,7 @@ public class PlasoIngestModule implements DataSourceIngestModule {
"PlasoIngestModule.create.artifacts.cancelled=Cancelled Plaso Artifact Creation ", "PlasoIngestModule.create.artifacts.cancelled=Cancelled Plaso Artifact Creation ",
"# {0} - file that events are from", "# {0} - file that events are from",
"PlasoIngestModule.artifact.progress=Adding events to case: {0}", "PlasoIngestModule.artifact.progress=Adding events to case: {0}",
"PlasoIngestModule.info.empty.database=Plaso database was empty.", "PlasoIngestModule.info.empty.database=Plaso database was empty.",})
})
private void createPlasoArtifacts(String plasoDb, DataSourceIngestModuleProgress statusHelper) { private void createPlasoArtifacts(String plasoDb, DataSourceIngestModuleProgress statusHelper) {
Blackboard blackboard = currentCase.getSleuthkitCase().getBlackboard(); Blackboard blackboard = currentCase.getSleuthkitCase().getBlackboard();
@ -320,7 +325,7 @@ public class PlasoIngestModule implements DataSourceIngestModule {
// If the description is empty use the event type display name // If the description is empty use the event type display name
// as the description. // as the description.
if ( description == null || description.isEmpty() ) { if (description == null || description.isEmpty()) {
if (eventType != TimelineEventType.OTHER) { if (eventType != TimelineEventType.OTHER) {
description = eventType.getDisplayName(); description = eventType.getDisplayName();
} else { } else {
@ -343,9 +348,11 @@ public class PlasoIngestModule implements DataSourceIngestModule {
BlackboardArtifact bbart = resolvedFile.newArtifact(TSK_TL_EVENT); BlackboardArtifact bbart = resolvedFile.newArtifact(TSK_TL_EVENT);
bbart.addAttributes(bbattributes); bbart.addAttributes(bbattributes);
try { try {
/* Post the artifact which will index the artifact for /*
* Post the artifact which will index the artifact for
* keyword search, and fire an event to notify UI of * keyword search, and fire an event to notify UI of
* this new artifact */ * this new artifact
*/
blackboard.postArtifact(bbart, MODULE_NAME); blackboard.postArtifact(bbart, MODULE_NAME);
} catch (BlackboardException ex) { } catch (BlackboardException ex) {
logger.log(Level.SEVERE, "Error Posting Artifact.", ex);//NON-NLS logger.log(Level.SEVERE, "Error Posting Artifact.", ex);//NON-NLS
@ -356,7 +363,7 @@ public class PlasoIngestModule implements DataSourceIngestModule {
} }
// Check if there is data the db // Check if there is data the db
if( !dbHasData ) { if (!dbHasData) {
logger.log(Level.INFO, String.format("PlasoDB was empty: %s", plasoDb)); logger.log(Level.INFO, String.format("PlasoDB was empty: %s", plasoDb));
MessageNotifyUtil.Notify.info(MODULE_NAME, Bundle.PlasoIngestModule_info_empty_database()); MessageNotifyUtil.Notify.info(MODULE_NAME, Bundle.PlasoIngestModule_info_empty_database());
} }

View File

@ -46,6 +46,8 @@ from org.sleuthkit.datamodel import Account
from org.sleuthkit.datamodel.blackboardutils import CommunicationArtifactsHelper from org.sleuthkit.datamodel.blackboardutils import CommunicationArtifactsHelper
from org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper import MessageReadStatus from org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper import MessageReadStatus
from org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper import CommunicationDirection from org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper import CommunicationDirection
from org.sleuthkit.datamodel.blackboardutils import FileAttachment
from org.sleuthkit.datamodel.blackboardutils import MessageAttachments
from TskMessagesParser import TskMessagesParser from TskMessagesParser import TskMessagesParser
from TskContactsParser import TskContactsParser from TskContactsParser import TskContactsParser
from TskCallLogsParser import TskCallLogsParser from TskCallLogsParser import TskCallLogsParser
@ -137,7 +139,7 @@ class SkypeAnalyzer(general.AndroidComponentAnalyzer):
) )
self.parse_contacts(skype_db, helper) self.parse_contacts(skype_db, helper)
self.parse_calllogs(skype_db, helper) self.parse_calllogs(skype_db, helper)
self.parse_messages(skype_db, helper) self.parse_messages(skype_db, helper, current_case)
except NoCurrentCaseException as ex: except NoCurrentCaseException as ex:
self._logger.log(Level.WARNING, "No case currently open.", ex) self._logger.log(Level.WARNING, "No case currently open.", ex)
self._logger.log(Level.WARNING, traceback.format_exc()) self._logger.log(Level.WARNING, traceback.format_exc())
@ -209,13 +211,13 @@ class SkypeAnalyzer(general.AndroidComponentAnalyzer):
"Failed to post call log artifact to the blackboard", ex) "Failed to post call log artifact to the blackboard", ex)
self._logger.log(Level.WARNING, traceback.format_exc()) self._logger.log(Level.WARNING, traceback.format_exc())
def parse_messages(self, skype_db, helper): def parse_messages(self, skype_db, helper, current_case):
#Query for messages and iterate row by row adding #Query for messages and iterate row by row adding
#each message artifact #each message artifact
try: try:
messages_parser = SkypeMessagesParser(skype_db) messages_parser = SkypeMessagesParser(skype_db)
while messages_parser.next(): while messages_parser.next():
helper.addMessage( message_artifact = helper.addMessage(
messages_parser.get_message_type(), messages_parser.get_message_type(),
messages_parser.get_message_direction(), messages_parser.get_message_direction(),
messages_parser.get_phone_number_from(), messages_parser.get_phone_number_from(),
@ -226,6 +228,13 @@ class SkypeAnalyzer(general.AndroidComponentAnalyzer):
messages_parser.get_message_text(), messages_parser.get_message_text(),
messages_parser.get_thread_id() messages_parser.get_thread_id()
) )
if (messages_parser.get_file_attachment() is not None):
file_attachments = ArrayList()
file_attachments.add(FileAttachment(current_case.getSleuthkitCase(), skype_db.getDBFile().getDataSource(), messages_parser.get_file_attachment()))
message_attachments = MessageAttachments(file_attachments, [])
helper.addAttachments(message_artifact, message_attachments)
messages_parser.close() messages_parser.close()
except SQLException as ex: except SQLException as ex:
#Error parsing Skype db #Error parsing Skype db
@ -425,12 +434,6 @@ class SkypeMessagesParser(TskMessagesParser):
content = self.result_set.getString("content") content = self.result_set.getString("content")
if content is not None: if content is not None:
file_path = self.result_set.getString("device_gallery_path")
#if a file name and file path are associated with a message, append it
if file_path is not None:
return general.appendAttachmentList(content, [file_path])
return content return content
return super(SkypeMessagesParser, self).get_message_text() return super(SkypeMessagesParser, self).get_message_text()
@ -440,3 +443,11 @@ class SkypeMessagesParser(TskMessagesParser):
if group_ids is not None: if group_ids is not None:
return self.result_set.getString("conversation_id") return self.result_set.getString("conversation_id")
return super(SkypeMessagesParser, self).get_thread_id() return super(SkypeMessagesParser, self).get_thread_id()
def get_file_attachment(self):
if (self.result_set.getString("device_gallery_path") is None):
return None
else:
return self.result_set.getString("device_gallery_path")

View File

@ -45,6 +45,9 @@ from org.sleuthkit.datamodel import Account
from org.sleuthkit.datamodel.blackboardutils import CommunicationArtifactsHelper from org.sleuthkit.datamodel.blackboardutils import CommunicationArtifactsHelper
from org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper import MessageReadStatus from org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper import MessageReadStatus
from org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper import CommunicationDirection from org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper import CommunicationDirection
from org.sleuthkit.datamodel.blackboardutils import FileAttachment
from org.sleuthkit.datamodel.blackboardutils import URLAttachment
from org.sleuthkit.datamodel.blackboardutils import MessageAttachments
from TskMessagesParser import TskMessagesParser from TskMessagesParser import TskMessagesParser
from TskContactsParser import TskContactsParser from TskContactsParser import TskContactsParser
from TskCallLogsParser import TskCallLogsParser from TskCallLogsParser import TskCallLogsParser
@ -151,7 +154,7 @@ class WhatsAppAnalyzer(general.AndroidComponentAnalyzer):
current_case.getSleuthkitCase(), self._PARSER_NAME, current_case.getSleuthkitCase(), self._PARSER_NAME,
calllog_and_message_db.getDBFile(), Account.Type.WHATSAPP) calllog_and_message_db.getDBFile(), Account.Type.WHATSAPP)
self.parse_calllogs(calllog_and_message_db, helper) self.parse_calllogs(calllog_and_message_db, helper)
self.parse_messages(dataSource, calllog_and_message_db, helper) self.parse_messages(dataSource, calllog_and_message_db, helper, current_case)
except NoCurrentCaseException as ex: except NoCurrentCaseException as ex:
#If there is no current case, bail out immediately. #If there is no current case, bail out immediately.
@ -227,14 +230,14 @@ class WhatsAppAnalyzer(general.AndroidComponentAnalyzer):
"Error posting calllog artifact to the blackboard.", ex) "Error posting calllog artifact to the blackboard.", ex)
self._logger.log(Level.WARNING, traceback.format_exc()) self._logger.log(Level.WARNING, traceback.format_exc())
def parse_messages(self, dataSource, messages_db, helper): def parse_messages(self, dataSource, messages_db, helper, current_case):
try: try:
messages_db.attachDatabase(dataSource, "wa.db", messages_db.attachDatabase(dataSource, "wa.db",
messages_db.getDBFile().getParentPath(), "wadb") messages_db.getDBFile().getParentPath(), "wadb")
messages_parser = WhatsAppMessagesParser(messages_db) messages_parser = WhatsAppMessagesParser(messages_db)
while messages_parser.next(): while messages_parser.next():
helper.addMessage( message_artifact = helper.addMessage(
messages_parser.get_message_type(), messages_parser.get_message_type(),
messages_parser.get_message_direction(), messages_parser.get_message_direction(),
messages_parser.get_phone_number_from(), messages_parser.get_phone_number_from(),
@ -245,6 +248,14 @@ class WhatsAppAnalyzer(general.AndroidComponentAnalyzer):
messages_parser.get_message_text(), messages_parser.get_message_text(),
messages_parser.get_thread_id() messages_parser.get_thread_id()
) )
# add attachments, if any
if (messages_parser.get_url_attachment() is not None):
url_attachments = ArrayList()
url_attachments.add(URLAttachment(messages_parser.get_url_attachment()))
message_attachments = MessageAttachments([], url_attachments)
helper.addAttachments(message_artifact, message_attachments)
messages_parser.close() messages_parser.close()
except SQLException as ex: except SQLException as ex:
self._logger.log(Level.WARNING, "Error querying the whatsapp database for contacts.", ex) self._logger.log(Level.WARNING, "Error querying the whatsapp database for contacts.", ex)
@ -502,9 +513,6 @@ class WhatsAppMessagesParser(TskMessagesParser):
message = self.result_set.getString("content") message = self.result_set.getString("content")
if message is None: if message is None:
message = super(WhatsAppMessagesParser, self).get_message_text() message = super(WhatsAppMessagesParser, self).get_message_text()
attachment = self.result_set.getString("attachment")
if attachment is not None:
return general.appendAttachmentList(message, [attachment])
return message return message
def get_thread_id(self): def get_thread_id(self):
@ -512,3 +520,14 @@ class WhatsAppMessagesParser(TskMessagesParser):
if group is not None: if group is not None:
return self.result_set.getString("id") return self.result_set.getString("id")
return super(WhatsAppMessagesParser, self).get_thread_id() return super(WhatsAppMessagesParser, self).get_thread_id()
def get_url_attachment(self):
attachment = self.result_set.getString("attachment")
if (attachment is None):
return None
elif (str(attachment).startswith("http:") or str(attachment).startswith("https:") ):
return attachment
else:
return None

View File

@ -554,22 +554,24 @@ class Chrome extends Extract {
RecentActivityExtracterModuleFactory.getModuleName(), RecentActivityExtracterModuleFactory.getModuleName(),
NbBundle.getMessage(this.getClass(), "Chrome.moduleName"))); NbBundle.getMessage(this.getClass(), "Chrome.moduleName")));
BlackboardArtifact bbart = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadFile, bbattributes); BlackboardArtifact webDownloadArtifact = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadFile, bbattributes);
if (bbart != null) { if (webDownloadArtifact != null) {
bbartifacts.add(bbart); bbartifacts.add(webDownloadArtifact);
}
// find the downloaded file and create a TSK_DOWNLOAD_SOURCE for it.. // find the downloaded file and create a TSK_ASSOCIATED_OBJECT for it, associating it with the TSK_WEB_DOWNLOAD artifact.
try { try {
for (AbstractFile downloadedFile : fileManager.findFiles(dataSource, FilenameUtils.getName(fullPath), FilenameUtils.getPath(fullPath))) { for (AbstractFile downloadedFile : fileManager.findFiles(dataSource, FilenameUtils.getName(fullPath), FilenameUtils.getPath(fullPath))) {
BlackboardArtifact downloadSourceArt = downloadedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE); BlackboardArtifact associatedObjectArtifact = downloadedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT);
downloadSourceArt.addAttributes(createDownloadSourceAttributes(result.get("url").toString())); associatedObjectArtifact.addAttribute(
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT,
RecentActivityExtracterModuleFactory.getModuleName(), webDownloadArtifact.getArtifactID()));
bbartifacts.add(downloadSourceArt); bbartifacts.add(associatedObjectArtifact);
break; break;
} }
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Error creating download source artifact for file '%s'", fullPath), ex); //NON-NLS logger.log(Level.SEVERE, String.format("Error creating associated object artifact for file '%s'", fullPath), ex); //NON-NLS
}
} }
} }

View File

@ -380,12 +380,12 @@ final class ChromeCacheExtractor {
* Extracts the files if needed and adds as derived files, creates artifacts * Extracts the files if needed and adds as derived files, creates artifacts
* *
* @param cacheEntryAddress cache entry address * @param cacheEntryAddress cache entry address
* @param sourceArtifacts any source artifacts created are added to this collection * @param associatedObjectArtifacts any associated object artifacts created are added to this collection
* @param webCacheArtifacts any web cache artifacts created are added to this collection * @param webCacheArtifacts any web cache artifacts created are added to this collection
* *
* @return Optional derived file, is a derived file is added for the given entry * @return Optional derived file, is a derived file is added for the given entry
*/ */
private List<DerivedFile> processCacheEntry(CacheAddress cacheEntryAddress, Collection<BlackboardArtifact> sourceArtifacts, Collection<BlackboardArtifact> webCacheArtifacts ) throws TskCoreException, IngestModuleException { private List<DerivedFile> processCacheEntry(CacheAddress cacheEntryAddress, Collection<BlackboardArtifact> associatedObjectArtifacts, Collection<BlackboardArtifact> webCacheArtifacts ) throws TskCoreException, IngestModuleException {
List<DerivedFile> derivedFiles = new ArrayList<>(); List<DerivedFile> derivedFiles = new ArrayList<>();
@ -437,10 +437,6 @@ final class ChromeCacheExtractor {
moduleName, moduleName,
cacheEntry.getHTTPHeaders()); cacheEntry.getHTTPHeaders());
Collection<BlackboardAttribute> sourceArtifactAttributes = new ArrayList<>();
sourceArtifactAttributes.add(urlAttr);
sourceArtifactAttributes.add(createTimeAttr);
Collection<BlackboardAttribute> webCacheAttributes = new ArrayList<>(); Collection<BlackboardAttribute> webCacheAttributes = new ArrayList<>();
webCacheAttributes.add(urlAttr); webCacheAttributes.add(urlAttr);
webCacheAttributes.add(createTimeAttr); webCacheAttributes.add(createTimeAttr);
@ -450,11 +446,6 @@ final class ChromeCacheExtractor {
// add artifacts to the f_XXX file // add artifacts to the f_XXX file
if (dataSegment.isInExternalFile() ) { if (dataSegment.isInExternalFile() ) {
try { try {
BlackboardArtifact sourceArtifact = cachedFileAbstractFile.get().newArtifact(ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE);
if (sourceArtifact != null) {
sourceArtifact.addAttributes(sourceArtifactAttributes);
sourceArtifacts.add(sourceArtifact);
}
BlackboardArtifact webCacheArtifact = cacheEntryFile.get().getAbstractFile().newArtifact(ARTIFACT_TYPE.TSK_WEB_CACHE); BlackboardArtifact webCacheArtifact = cacheEntryFile.get().getAbstractFile().newArtifact(ARTIFACT_TYPE.TSK_WEB_CACHE);
if (webCacheArtifact != null) { if (webCacheArtifact != null) {
@ -469,6 +460,14 @@ final class ChromeCacheExtractor {
moduleName, cachedFileAbstractFile.get().getId())); moduleName, cachedFileAbstractFile.get().getId()));
webCacheArtifacts.add(webCacheArtifact); webCacheArtifacts.add(webCacheArtifact);
BlackboardArtifact associatedObjectArtifact = cachedFileAbstractFile.get().newArtifact(ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT);
if (associatedObjectArtifact != null) {
associatedObjectArtifact.addAttribute(
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT,
moduleName, webCacheArtifact.getArtifactID()));
associatedObjectArtifacts.add(associatedObjectArtifact);
}
} }
if (isBrotliCompressed) { if (isBrotliCompressed) {
@ -497,11 +496,6 @@ final class ChromeCacheExtractor {
"", "",
TskData.EncodingType.NONE); TskData.EncodingType.NONE);
BlackboardArtifact sourceArtifact = derivedFile.newArtifact(ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE);
if (sourceArtifact != null) {
sourceArtifact.addAttributes(sourceArtifactAttributes);
sourceArtifacts.add(sourceArtifact);
}
BlackboardArtifact webCacheArtifact = cacheEntryFile.get().getAbstractFile().newArtifact(ARTIFACT_TYPE.TSK_WEB_CACHE); BlackboardArtifact webCacheArtifact = cacheEntryFile.get().getAbstractFile().newArtifact(ARTIFACT_TYPE.TSK_WEB_CACHE);
if (webCacheArtifact != null) { if (webCacheArtifact != null) {
@ -516,6 +510,14 @@ final class ChromeCacheExtractor {
moduleName, derivedFile.getId())); moduleName, derivedFile.getId()));
webCacheArtifacts.add(webCacheArtifact); webCacheArtifacts.add(webCacheArtifact);
BlackboardArtifact associatedObjectArtifact = derivedFile.newArtifact(ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT);
if (associatedObjectArtifact != null) {
associatedObjectArtifact.addAttribute(
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT,
moduleName, webCacheArtifact.getArtifactID()));
associatedObjectArtifacts.add(associatedObjectArtifact);
}
} }
if (isBrotliCompressed) { if (isBrotliCompressed) {

View File

@ -50,6 +50,7 @@ import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
import org.sleuthkit.autopsy.recentactivity.BinaryCookieReader.Cookie; import org.sleuthkit.autopsy.recentactivity.BinaryCookieReader.Cookie;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
@ -637,15 +638,17 @@ final class ExtractSafari extends Extract {
time = date.getDate().getTime(); time = date.getDate().getTime();
} }
BlackboardArtifact bbart = origFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD); BlackboardArtifact webDownloadArtifact = origFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD);
bbart.addAttributes(this.createDownloadAttributes(path, pathID, url, time, NetworkUtils.extractDomain(url), getName())); webDownloadArtifact.addAttributes(this.createDownloadAttributes(path, pathID, url, time, NetworkUtils.extractDomain(url), getName()));
bbartifacts.add(bbart); bbartifacts.add(webDownloadArtifact);
// find the downloaded file and create a TSK_DOWNLOAD_SOURCE for it. // find the downloaded file and create a TSK_ASSOCIATED_OBJECT for it, associating it with the TSK_WEB_DOWNLOAD artifact.
for (AbstractFile downloadedFile : fileManager.findFiles(dataSource, FilenameUtils.getName(path), FilenameUtils.getPath(path))) { for (AbstractFile downloadedFile : fileManager.findFiles(dataSource, FilenameUtils.getName(path), FilenameUtils.getPath(path))) {
BlackboardArtifact downloadSourceArt = downloadedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE); BlackboardArtifact associatedObjectArtifact = downloadedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT);
downloadSourceArt.addAttributes(createDownloadSourceAttributes(url)); associatedObjectArtifact.addAttribute(
bbartifacts.add(downloadSourceArt); new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT,
RecentActivityExtracterModuleFactory.getModuleName(), webDownloadArtifact.getArtifactID()));
bbartifacts.add(associatedObjectArtifact);
break; break;
} }

View File

@ -28,7 +28,6 @@ import java.util.List;
import java.util.Properties; import java.util.Properties;
import java.util.Set; import java.util.Set;
import java.util.logging.Level; import java.util.logging.Level;
import org.apache.commons.lang3.StringUtils;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.NetworkUtils; import org.sleuthkit.autopsy.coreutils.NetworkUtils;
@ -36,13 +35,11 @@ import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress;
import org.sleuthkit.autopsy.ingest.IngestJobContext; import org.sleuthkit.autopsy.ingest.IngestJobContext;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE; import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD; import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_LOCATION;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_URL;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.ReadContentInputStream; import org.sleuthkit.datamodel.ReadContentInputStream;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
@ -94,7 +91,7 @@ final class ExtractZoneIdentifier extends Extract {
return; return;
} }
Collection<BlackboardArtifact> sourceArtifacts = new ArrayList<>(); Collection<BlackboardArtifact> associatedObjectArtifacts = new ArrayList<>();
Collection<BlackboardArtifact> downloadArtifacts = new ArrayList<>(); Collection<BlackboardArtifact> downloadArtifacts = new ArrayList<>();
for (AbstractFile zoneFile : zoneFiles) { for (AbstractFile zoneFile : zoneFiles) {
@ -104,7 +101,7 @@ final class ExtractZoneIdentifier extends Extract {
} }
try { try {
processZoneFile(context, dataSource, zoneFile, sourceArtifacts, downloadArtifacts, knownPathIDs); processZoneFile(context, dataSource, zoneFile, associatedObjectArtifacts, downloadArtifacts, knownPathIDs);
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
addErrorMessage(Bundle.ExtractZone_process_errMsg()); addErrorMessage(Bundle.ExtractZone_process_errMsg());
String message = String.format("Failed to process zone identifier file %s", zoneFile.getName()); //NON-NLS String message = String.format("Failed to process zone identifier file %s", zoneFile.getName()); //NON-NLS
@ -112,7 +109,7 @@ final class ExtractZoneIdentifier extends Extract {
} }
} }
postArtifacts(sourceArtifacts); postArtifacts(associatedObjectArtifacts);
postArtifacts(downloadArtifacts); postArtifacts(downloadArtifacts);
} }
@ -122,13 +119,13 @@ final class ExtractZoneIdentifier extends Extract {
* @param context IngetJobContext * @param context IngetJobContext
* @param dataSource Content * @param dataSource Content
* @param zoneFile Zone Indentifier file * @param zoneFile Zone Indentifier file
* @param sourceArtifacts List for TSK_DOWNLOAD_SOURCE artifacts * @param associatedObjectArtifacts List for TSK_ASSOCIATED_OBJECT artifacts
* @param downloadArtifacts List for TSK_WEB_DOWNLOAD aritfacts * @param downloadArtifacts List for TSK_WEB_DOWNLOAD artifacts
* *
* @throws TskCoreException * @throws TskCoreException
*/ */
private void processZoneFile(IngestJobContext context, Content dataSource, private void processZoneFile(IngestJobContext context, Content dataSource,
AbstractFile zoneFile, Collection<BlackboardArtifact> sourceArtifacts, AbstractFile zoneFile, Collection<BlackboardArtifact> associatedObjectArtifacts,
Collection<BlackboardArtifact> downloadArtifacts, Collection<BlackboardArtifact> downloadArtifacts,
Set<Long> knownPathIDs) throws TskCoreException { Set<Long> knownPathIDs) throws TskCoreException {
@ -155,16 +152,16 @@ final class ExtractZoneIdentifier extends Extract {
BlackboardArtifact downloadBba = createDownloadArtifact(zoneFile, zoneInfo); BlackboardArtifact downloadBba = createDownloadArtifact(zoneFile, zoneInfo);
if (downloadBba != null) { if (downloadBba != null) {
downloadArtifacts.add(downloadBba); downloadArtifacts.add(downloadBba);
// create a TSK_ASSOCIATED_OBJECT for the downloaded file, associating it with the TSK_WEB_DOWNLOAD artifact.
if (downloadFile.getArtifactsCount(TSK_ASSOCIATED_OBJECT) == 0) {
BlackboardArtifact associatedObjectBba = createAssociatedObjectArtifact(downloadFile, downloadBba);
if (associatedObjectBba != null) {
associatedObjectArtifacts.add(associatedObjectBba);
}
}
} }
} }
// check if download has a child TSK_DOWNLOAD_SOURCE artifact, if not create one
if (downloadFile.getArtifactsCount(TSK_DOWNLOAD_SOURCE) == 0) {
BlackboardArtifact sourceBba = createDownloadSourceArtifact(downloadFile, zoneInfo);
if (sourceBba != null) {
sourceArtifacts.add(sourceBba);
}
}
} }
} }
@ -203,33 +200,26 @@ final class ExtractZoneIdentifier extends Extract {
} }
/** /**
* Create a Download Source Artifact for the given ZoneIdentifierInfo * Create a Associated Object Artifact for the given ZoneIdentifierInfo
* object. * object.
* *
* @param downloadFile AbstractFile representing the file downloaded, not * @param downloadFile AbstractFile representing the file downloaded, not
* the zone identifier file. * the zone identifier file.
* @param zoneInfo Zone identifier file wrapper object * @param downloadBba TSK_WEB_DOWNLOAD artifact to associate with.
* *
* @return TSK_DOWNLOAD_SOURCE object for given parameters * @return TSK_ASSOCIATED_OBJECT artifact.
*/ */
private BlackboardArtifact createDownloadSourceArtifact(AbstractFile downloadFile, ZoneIdentifierInfo zoneInfo) { private BlackboardArtifact createAssociatedObjectArtifact(AbstractFile downloadFile, BlackboardArtifact downloadBba) {
Collection<BlackboardAttribute> bbattributes = new ArrayList<>(); Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
bbattributes.addAll(Arrays.asList( bbattributes.addAll(Arrays.asList(
new BlackboardAttribute(TSK_URL, new BlackboardAttribute(TSK_ASSOCIATED_ARTIFACT,
RecentActivityExtracterModuleFactory.getModuleName(), RecentActivityExtracterModuleFactory.getModuleName(),
StringUtils.defaultString(zoneInfo.getURL(), "")), downloadBba.getArtifactID())
));
new BlackboardAttribute(TSK_DOMAIN, return createArtifactWithAttributes(TSK_ASSOCIATED_OBJECT, downloadFile, bbattributes);
RecentActivityExtracterModuleFactory.getModuleName(),
(zoneInfo.getURL() != null) ? NetworkUtils.extractDomain(zoneInfo.getURL()) : ""),
new BlackboardAttribute(TSK_LOCATION,
RecentActivityExtracterModuleFactory.getModuleName(),
StringUtils.defaultString(zoneInfo.getZoneIdAsString(), "")))); //NON-NLS
return createArtifactWithAttributes(TSK_DOWNLOAD_SOURCE, downloadFile, bbattributes);
} }
/** /**

View File

@ -544,24 +544,27 @@ class Firefox extends Extract {
domain)); //NON-NLS domain)); //NON-NLS
} }
BlackboardArtifact bbart = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadsFile, bbattributes); BlackboardArtifact webDownloadArtifact = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadsFile, bbattributes);
if (bbart != null) { if (webDownloadArtifact != null) {
bbartifacts.add(bbart); bbartifacts.add(webDownloadArtifact);
}
// find the downloaded file and create a TSK_DOWNLOAD_SOURCE for it. // find the downloaded file and create a TSK_ASSOCIATED_OBJECT for it, associating it with the TSK_WEB_DOWNLOAD artifact.
try { try {
for (AbstractFile downloadedFile : fileManager.findFiles(dataSource, FilenameUtils.getName(downloadedFilePath), FilenameUtils.getPath(downloadedFilePath))) { for (AbstractFile downloadedFile : fileManager.findFiles(dataSource, FilenameUtils.getName(downloadedFilePath), FilenameUtils.getPath(downloadedFilePath))) {
BlackboardArtifact downloadSourceArt = downloadedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE); BlackboardArtifact associatedObjectArtifact = downloadedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT);
downloadSourceArt.addAttributes(createDownloadSourceAttributes(source)); associatedObjectArtifact.addAttribute(
bbartifacts.add(downloadSourceArt); new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT,
RecentActivityExtracterModuleFactory.getModuleName(), webDownloadArtifact.getArtifactID()));
bbartifacts.add(associatedObjectArtifact);
break; break;
} }
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Error creating download source artifact for file '%s'", logger.log(Level.SEVERE, String.format("Error creating associated object artifact for file '%s'",
downloadedFilePath), ex); //NON-NLS downloadedFilePath), ex); //NON-NLS
} }
} }
}
if (errors > 0) { if (errors > 0) {
this.addErrorMessage( this.addErrorMessage(
NbBundle.getMessage(this.getClass(), "Firefox.getDlPre24.errMsg.errParsingArtifacts", NbBundle.getMessage(this.getClass(), "Firefox.getDlPre24.errMsg.errParsingArtifacts",
@ -681,24 +684,26 @@ class Firefox extends Extract {
RecentActivityExtracterModuleFactory.getModuleName(), domain)); //NON-NLS RecentActivityExtracterModuleFactory.getModuleName(), domain)); //NON-NLS
} }
BlackboardArtifact bbart = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadsFile, bbattributes); BlackboardArtifact webDownloadArtifact = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadsFile, bbattributes);
if (bbart != null) { if (webDownloadArtifact != null) {
bbartifacts.add(bbart); bbartifacts.add(webDownloadArtifact);
}
// find the downloaded file and create a TSK_DOWNLOAD_SOURCE for it. // find the downloaded file and create a TSK_ASSOCIATED_OBJECT for it, associating it with the TSK_WEB_DOWNLOAD artifact.
try { try {
for (AbstractFile downloadedFile : fileManager.findFiles(dataSource, FilenameUtils.getName(downloadedFilePath), FilenameUtils.getPath(downloadedFilePath))) { for (AbstractFile downloadedFile : fileManager.findFiles(dataSource, FilenameUtils.getName(downloadedFilePath), FilenameUtils.getPath(downloadedFilePath))) {
BlackboardArtifact downloadSourceArt = downloadedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE); BlackboardArtifact associatedObjectArtifact = downloadedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT);
downloadSourceArt.addAttributes(createDownloadSourceAttributes(url)); associatedObjectArtifact.addAttribute(
bbartifacts.add(downloadSourceArt); new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT,
RecentActivityExtracterModuleFactory.getModuleName(), webDownloadArtifact.getArtifactID()));
bbartifacts.add(associatedObjectArtifact);
break; break;
} }
} catch (TskCoreException ex) { } catch (TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Error creating download source artifact for file '%s'", logger.log(Level.SEVERE, String.format("Error creating associated object artifact for file '%s'",
downloadedFilePath), ex); //NON-NLS downloadedFilePath), ex); //NON-NLS
} }
} }
}
if (errors > 0) { if (errors > 0) {
this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Firefox.getDlV24.errMsg.errParsingArtifacts", this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Firefox.getDlV24.errMsg.errParsingArtifacts",
this.getName(), errors)); this.getName(), errors));