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.VolumeIcon.text=Volume
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.
ApplicationContentViewer.title=Application
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.progress.bufferingCancelled=media buffering was canceled
FXVideoPanel.progress.bufferingInterrupted=media buffering was interrupted
@ -164,6 +175,10 @@ MediaPlayerPanel.playButton.text=\u25ba
MediaPlayerPanel.infoLabel.text=No Errors
MediaPlayerPanel.VolumeIcon.text=Volume
MediaPlayerPanel.playBackSpeedLabel.text=Speed:
ContextViewer.jSourceGoToResultButton.text=Go to Result
ContextViewer.jSourceNameLabel.text=jSourceNameLabel
ContextViewer.jSourceTextLabel.text=jLabel2
ContextViewer.jSourceLabel.text=Source
# {0} - tableName
SQLiteViewer.readTable.errorText=Error getting rows for table: {0}
# {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 {
List<BlackboardArtifact> sourceArtifacts = file.getArtifacts(ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE);
if (!sourceArtifacts.isEmpty()) {
BlackboardArtifact artifact = sourceArtifacts.get(0);
BlackboardAttribute urlAttr = artifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_URL));
if (urlAttr != null) {
addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.downloadSource"), urlAttr.getValueString());
List<BlackboardArtifact> associatedObjectArtifacts = file.getArtifacts(ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT);
if (!associatedObjectArtifacts.isEmpty()) {
BlackboardArtifact artifact = associatedObjectArtifacts.get(0);
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);
addDownloadSourceRow(sb, associatedArtifact);
}
}
} catch (TskCoreException ex) {
@ -292,6 +294,26 @@ public class Metadata extends javax.swing.JPanel implements DataContentViewer {
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)
*

View File

@ -52,7 +52,11 @@ final class XRYFileReader implements AutoCloseable {
//Assume 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;
//Assume all headers are 5 lines in length.
@ -91,8 +95,12 @@ final class XRYFileReader implements AutoCloseable {
reader = Files.newBufferedReader(xryFile, CHARSET);
xryFilePath = xryFile;
//Advance the reader to the start of the first XRY entity.
for (int i = 0; i < HEADER_LENGTH_IN_LINES; i++) {
//Advance the reader to the start of the header.
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();
}
@ -298,8 +306,11 @@ final class XRYFileReader implements AutoCloseable {
*/
private static Optional<String> getType(Path file) throws IOException {
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.
for (int i = 0; i < LINE_WITH_REPORT_TYPE - 1; i++) {
for (int i = 1; i < LINE_WITH_REPORT_TYPE - 1; i++) {
reader.readLine();
}
@ -310,4 +321,51 @@ final class XRYFileReader implements AutoCloseable {
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());
setSelected(value.isChecked());
setText(value.getDisplayName());
setEnabled(list.isEnabled());
return this;
}
}

View File

@ -37,10 +37,6 @@
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="uncheckButtonActionPerformed"/>
</Events>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Constraints>
<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"/>
@ -56,10 +52,6 @@
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="checkButtonActionPerformed"/>
</Events>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Constraints>
<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"/>

View File

@ -62,6 +62,14 @@ final class CheckBoxListPanel<T> extends javax.swing.JPanel {
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.
*
@ -126,8 +134,8 @@ final class CheckBoxListPanel<T> extends javax.swing.JPanel {
java.awt.GridBagConstraints gridBagConstraints;
titleLabel = new javax.swing.JLabel();
javax.swing.JButton uncheckButton = new javax.swing.JButton();
javax.swing.JButton checkButton = new javax.swing.JButton();
uncheckButton = new javax.swing.JButton();
checkButton = new javax.swing.JButton();
scrollPane = new javax.swing.JScrollPane();
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
private javax.swing.JButton checkButton;
private javax.swing.JScrollPane scrollPane;
private javax.swing.JLabel titleLabel;
private javax.swing.JButton uncheckButton;
// 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;)"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Constraints>
<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"/>

View File

@ -74,6 +74,20 @@ class GeoFilterPanel extends javax.swing.JPanel {
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() {
try {
initCheckboxList();
@ -155,7 +169,7 @@ class GeoFilterPanel extends javax.swing.JPanel {
mostRecentButton = new javax.swing.JRadioButton();
showWaypointsWOTSCheckBox = new javax.swing.JCheckBox();
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();
applyButton = new javax.swing.JButton();
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
private javax.swing.JRadioButton allButton;
private javax.swing.JButton applyButton;
private javax.swing.JLabel daysLabel;
private javax.swing.JSpinner daysSpinner;
private javax.swing.JRadioButton mostRecentButton;
private javax.swing.JCheckBox showWaypointsWOTSCheckBox;

View File

@ -22,6 +22,7 @@ import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
@ -171,6 +172,7 @@ public final class GeolocationTopComponent extends TopComponent {
logger.log(Level.SEVERE, ex.getMessage(), ex);
return; // Doen't set the waypoints.
}
mapPanel.setWaypoints(new ArrayList<>());
updateWaypoints();
}
@ -214,35 +216,11 @@ public final class GeolocationTopComponent extends TopComponent {
return;
}
SwingUtilities.invokeLater(new Runnable() {
public void run() {
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);
mapPanel.setWaypointLoading(true);
geoFilterPanel.setEnabled(false);
return;
}
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);
}
}
});
Thread thread = new Thread(new WaypointRunner(filters));
thread.start();
}
/**
@ -269,4 +247,76 @@ public final class GeolocationTopComponent extends TopComponent {
private org.sleuthkit.autopsy.geolocation.HidingPane filterPane;
private org.sleuthkit.autopsy.geolocation.MapPanel mapPanel;
// 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>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<SubComponents>
<Component class="javax.swing.JLabel" name="cordLabel">
<Properties>
@ -107,8 +107,19 @@
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
<BorderConstraints direction="East"/>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<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>
</Constraints>
</Component>

View File

@ -179,6 +179,17 @@ final public class MapPanel extends javax.swing.JPanel {
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.
*/
@ -265,6 +276,7 @@ final public class MapPanel extends javax.swing.JPanel {
}
mapViewer.repaint();
setWaypointLoading(false);
}
/**
@ -486,6 +498,7 @@ final public class MapPanel extends javax.swing.JPanel {
zoomSlider = new javax.swing.JSlider();
infoPanel = new javax.swing.JPanel();
cordLabel = new javax.swing.JLabel();
progressBar = new javax.swing.JProgressBar();
setFocusable(false);
setLayout(new java.awt.BorderLayout());
@ -553,10 +566,24 @@ final public class MapPanel extends javax.swing.JPanel {
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
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);
}// </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.JPanel infoPanel;
private org.jxmapviewer.JXMapViewer mapViewer;
private javax.swing.JProgressBar progressBar;
private javax.swing.JPanel zoomPanel;
private javax.swing.JSlider zoomSlider;
// End of variables declaration//GEN-END:variables

View File

@ -103,8 +103,7 @@ public class PlasoIngestModule implements DataSourceIngestModule {
@NbBundle.Messages({
"PlasoIngestModule.executable.not.found=Plaso Executable Not Found.",
"PlasoIngestModule.requires.windows=Plaso module requires windows.",
"PlasoIngestModule.dataSource.not.an.image=Datasource is not an Image."})
"PlasoIngestModule.requires.windows=Plaso module requires windows."})
@Override
public void startUp(IngestJobContext context) throws IngestModuleException {
this.context = context;
@ -121,11 +120,6 @@ public class PlasoIngestModule implements DataSourceIngestModule {
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({
@ -138,80 +132,90 @@ public class PlasoIngestModule implements DataSourceIngestModule {
"PlasoIngestModule.psort.cancelled=psort run was canceled",
"PlasoIngestModule.bad.imageFile=Cannot find image file name and path",
"PlasoIngestModule.completed=Plaso Processing Completed",
"PlasoIngestModule.has.run=Plaso Plugin has been run.",
"PlasoIngestModule.psort.fail=Plaso returned an error when sorting events. Results are not complete."})
"PlasoIngestModule.has.run=Plaso",
"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
public ProcessResult process(Content dataSource, DataSourceIngestModuleProgress statusHelper) {
assert dataSource.equals(image);
statusHelper.switchToDeterminate(100);
currentCase = Case.getCurrentCase();
fileManager = currentCase.getServices().getFileManager();
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;
String currentTime = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss z", Locale.US).format(System.currentTimeMillis());//NON-NLS
Path moduleOutputPath = Paths.get(currentCase.getModuleDirectory(), PLASO, currentTime);
try {
Files.createDirectories(moduleOutputPath);
} catch (IOException ex) {
logger.log(Level.SEVERE, "Error creating Plaso module output directory.", ex); //NON-NLS
return ProcessResult.ERROR;
}
statusHelper.switchToDeterminate(100);
currentCase = Case.getCurrentCase();
fileManager = currentCase.getServices().getFileManager();
// Run log2timeline
logger.log(Level.INFO, "Starting Plaso Run.");//NON-NLS
statusHelper.progress(Bundle.PlasoIngestModule_starting_log2timeline(), 0);
ProcessBuilder log2TimeLineCommand = buildLog2TimeLineCommand(moduleOutputPath, image);
try {
Process log2TimeLineProcess = log2TimeLineCommand.start();
try (BufferedReader log2TimeLineOutpout = new BufferedReader(new InputStreamReader(log2TimeLineProcess.getInputStream()))) {
L2TStatusProcessor statusReader = new L2TStatusProcessor(log2TimeLineOutpout, statusHelper, moduleOutputPath);
new Thread(statusReader, "log2timeline status reader").start(); //NON-NLS
ExecUtil.waitForTermination(LOG2TIMELINE_EXECUTABLE, log2TimeLineProcess, TERMINATION_CHECK_INTERVAL, TERMINATION_CHECK_INTERVAL_UNITS, new DataSourceIngestModuleProcessTerminator(context));
statusReader.cancel();
}
if (context.dataSourceIngestIsCancelled()) {
logger.log(Level.INFO, "Log2timeline run was canceled"); //NON-NLS
return ProcessResult.OK;
}
if (Files.notExists(moduleOutputPath.resolve(PLASO))) {
logger.log(Level.WARNING, "Error running log2timeline: there was no storage file."); //NON-NLS
String currentTime = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss z", Locale.US).format(System.currentTimeMillis());//NON-NLS
Path moduleOutputPath = Paths.get(currentCase.getModuleDirectory(), PLASO, currentTime);
try {
Files.createDirectories(moduleOutputPath);
} catch (IOException ex) {
logger.log(Level.SEVERE, "Error creating Plaso module output directory.", ex); //NON-NLS
return ProcessResult.ERROR;
}
// sort the output
statusHelper.progress(Bundle.PlasoIngestModule_running_psort(), 33);
ProcessBuilder psortCommand = buildPsortCommand(moduleOutputPath);
int result = ExecUtil.execute(psortCommand, new DataSourceIngestModuleProcessTerminator(context));
if (result != 0) {
logger.log(Level.SEVERE, String.format("Error running Psort, error code returned %d", result)); //NON-NLS
MessageNotifyUtil.Notify.error(MODULE_NAME, Bundle.PlasoIngestModule_psort_fail());
return ProcessResult.ERROR;
}
// Run log2timeline
logger.log(Level.INFO, "Starting Plaso Run.");//NON-NLS
statusHelper.progress(Bundle.PlasoIngestModule_starting_log2timeline(), 0);
ProcessBuilder log2TimeLineCommand = buildLog2TimeLineCommand(moduleOutputPath, image);
try {
Process log2TimeLineProcess = log2TimeLineCommand.start();
try (BufferedReader log2TimeLineOutpout = new BufferedReader(new InputStreamReader(log2TimeLineProcess.getInputStream()))) {
L2TStatusProcessor statusReader = new L2TStatusProcessor(log2TimeLineOutpout, statusHelper, moduleOutputPath);
new Thread(statusReader, "log2timeline status reader").start(); //NON-NLS
ExecUtil.waitForTermination(LOG2TIMELINE_EXECUTABLE, log2TimeLineProcess, TERMINATION_CHECK_INTERVAL, TERMINATION_CHECK_INTERVAL_UNITS, new DataSourceIngestModuleProcessTerminator(context));
statusReader.cancel();
}
if (context.dataSourceIngestIsCancelled()) {
logger.log(Level.INFO, "psort run was canceled"); //NON-NLS
return ProcessResult.OK;
}
Path plasoFile = moduleOutputPath.resolve("plasodb.db3"); //NON-NLS
if (Files.notExists(plasoFile)) {
logger.log(Level.SEVERE, "Error running Psort: there was no sqlite db file."); //NON-NLS
if (context.dataSourceIngestIsCancelled()) {
logger.log(Level.INFO, "Log2timeline run was canceled"); //NON-NLS
return ProcessResult.OK;
}
if (Files.notExists(moduleOutputPath.resolve(PLASO))) {
logger.log(Level.WARNING, "Error running log2timeline: there was no storage file."); //NON-NLS
return ProcessResult.ERROR;
}
// sort the output
statusHelper.progress(Bundle.PlasoIngestModule_running_psort(), 33);
ProcessBuilder psortCommand = buildPsortCommand(moduleOutputPath);
int result = ExecUtil.execute(psortCommand, new DataSourceIngestModuleProcessTerminator(context));
if (result != 0) {
logger.log(Level.SEVERE, String.format("Error running Psort, error code returned %d", result)); //NON-NLS
MessageNotifyUtil.Notify.error(MODULE_NAME, Bundle.PlasoIngestModule_psort_fail());
return ProcessResult.ERROR;
}
if (context.dataSourceIngestIsCancelled()) {
logger.log(Level.INFO, "psort run was canceled"); //NON-NLS
return ProcessResult.OK;
}
Path plasoFile = moduleOutputPath.resolve("plasodb.db3"); //NON-NLS
if (Files.notExists(plasoFile)) {
logger.log(Level.SEVERE, "Error running Psort: there was no sqlite db file."); //NON-NLS
return ProcessResult.ERROR;
}
// parse the output and make artifacts
createPlasoArtifacts(plasoFile.toString(), statusHelper);
} catch (IOException ex) {
logger.log(Level.SEVERE, "Error running Plaso.", ex);//NON-NLS
return ProcessResult.ERROR;
}
// parse the output and make artifacts
createPlasoArtifacts(plasoFile.toString(), statusHelper);
} catch (IOException ex) {
logger.log(Level.SEVERE, "Error running Plaso.", ex);//NON-NLS
return ProcessResult.ERROR;
IngestMessage message = IngestMessage.createMessage(IngestMessage.MessageType.DATA,
Bundle.PlasoIngestModule_has_run(),
Bundle.PlasoIngestModule_completed());
IngestServices.getInstance().postMessage(message);
return ProcessResult.OK;
}
IngestMessage message = IngestMessage.createMessage(IngestMessage.MessageType.DATA,
Bundle.PlasoIngestModule_has_run(),
Bundle.PlasoIngestModule_completed());
IngestServices.getInstance().postMessage(message);
return ProcessResult.OK;
}
private ProcessBuilder buildLog2TimeLineCommand(Path moduleOutputPath, Image image) {
@ -240,8 +244,10 @@ public class PlasoIngestModule implements DataSourceIngestModule {
static private ProcessBuilder buildProcessWithRunAsInvoker(String... 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
return processBuilder;
}
@ -277,22 +283,21 @@ public class PlasoIngestModule implements DataSourceIngestModule {
"PlasoIngestModule.create.artifacts.cancelled=Cancelled Plaso Artifact Creation ",
"# {0} - file that events are from",
"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) {
Blackboard blackboard = currentCase.getSleuthkitCase().getBlackboard();
String sqlStatement = "SELECT substr(filename,1) AS filename, "
+ " strftime('%s', datetime) AS epoch_date, "
+ " description, "
+ " source, "
+ " type, "
+ " sourcetype "
+ " FROM log2timeline "
+ " WHERE source NOT IN ('FILE', "
+ " 'WEBHIST') " // bad dates and duplicates with what we have.
+ " AND sourcetype NOT IN ('UNKNOWN', "
+ " 'PE Import Time');"; // lots of bad dates //NON-NLS
+ " strftime('%s', datetime) AS epoch_date, "
+ " description, "
+ " source, "
+ " type, "
+ " sourcetype "
+ " FROM log2timeline "
+ " WHERE source NOT IN ('FILE', "
+ " 'WEBHIST') " // bad dates and duplicates with what we have.
+ " AND sourcetype NOT IN ('UNKNOWN', "
+ " 'PE Import Time');"; // lots of bad dates //NON-NLS
try (SQLiteDBConnect tempdbconnect = new SQLiteDBConnect("org.sqlite.JDBC", "jdbc:sqlite:" + plasoDb); //NON-NLS
ResultSet resultSet = tempdbconnect.executeQry(sqlStatement)) {
@ -320,7 +325,7 @@ public class PlasoIngestModule implements DataSourceIngestModule {
// If the description is empty use the event type display name
// as the description.
if ( description == null || description.isEmpty() ) {
if (description == null || description.isEmpty()) {
if (eventType != TimelineEventType.OTHER) {
description = eventType.getDisplayName();
} else {
@ -343,9 +348,11 @@ public class PlasoIngestModule implements DataSourceIngestModule {
BlackboardArtifact bbart = resolvedFile.newArtifact(TSK_TL_EVENT);
bbart.addAttributes(bbattributes);
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
* this new artifact */
* this new artifact
*/
blackboard.postArtifact(bbart, MODULE_NAME);
} catch (BlackboardException ex) {
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
if( !dbHasData ) {
if (!dbHasData) {
logger.log(Level.INFO, String.format("PlasoDB was empty: %s", plasoDb));
MessageNotifyUtil.Notify.info(MODULE_NAME, Bundle.PlasoIngestModule_info_empty_database());
}
@ -377,8 +384,8 @@ public class PlasoIngestModule implements DataSourceIngestModule {
// check the cached file
//TODO: would we reduce 'cache misses' if we retrieved the events sorted by file? Is that overhead worth it?
if (previousFile != null
&& previousFile.getName().equalsIgnoreCase(fileName)
&& previousFile.getParentPath().equalsIgnoreCase(filePath)) {
&& previousFile.getName().equalsIgnoreCase(fileName)
&& previousFile.getParentPath().equalsIgnoreCase(filePath)) {
return previousFile;
}
@ -416,7 +423,7 @@ public class PlasoIngestModule implements DataSourceIngestModule {
switch (row.getString("source")) {
case "WEBHIST": //These shouldn't actually be present, but keeping the logic just in case...
if (fileName.toLowerCase().contains(COOKIE)
|| row.getString("type").toLowerCase().contains(COOKIE)) {//NON-NLS
|| row.getString("type").toLowerCase().contains(COOKIE)) {//NON-NLS
return TimelineEventType.WEB_COOKIE;
} else {

View File

@ -46,6 +46,8 @@ from org.sleuthkit.datamodel import Account
from org.sleuthkit.datamodel.blackboardutils import CommunicationArtifactsHelper
from org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper import MessageReadStatus
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 TskContactsParser import TskContactsParser
from TskCallLogsParser import TskCallLogsParser
@ -137,7 +139,7 @@ class SkypeAnalyzer(general.AndroidComponentAnalyzer):
)
self.parse_contacts(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:
self._logger.log(Level.WARNING, "No case currently open.", ex)
self._logger.log(Level.WARNING, traceback.format_exc())
@ -209,23 +211,30 @@ class SkypeAnalyzer(general.AndroidComponentAnalyzer):
"Failed to post call log artifact to the blackboard", ex)
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
#each message artifact
try:
messages_parser = SkypeMessagesParser(skype_db)
while messages_parser.next():
helper.addMessage(
messages_parser.get_message_type(),
messages_parser.get_message_direction(),
messages_parser.get_phone_number_from(),
messages_parser.get_phone_number_to(),
messages_parser.get_message_date_time(),
messages_parser.get_message_read_status(),
messages_parser.get_message_subject(),
messages_parser.get_message_text(),
messages_parser.get_thread_id()
)
message_artifact = helper.addMessage(
messages_parser.get_message_type(),
messages_parser.get_message_direction(),
messages_parser.get_phone_number_from(),
messages_parser.get_phone_number_to(),
messages_parser.get_message_date_time(),
messages_parser.get_message_read_status(),
messages_parser.get_message_subject(),
messages_parser.get_message_text(),
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()
except SQLException as ex:
#Error parsing Skype db
@ -425,12 +434,6 @@ class SkypeMessagesParser(TskMessagesParser):
content = self.result_set.getString("content")
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 super(SkypeMessagesParser, self).get_message_text()
@ -440,3 +443,11 @@ class SkypeMessagesParser(TskMessagesParser):
if group_ids is not None:
return self.result_set.getString("conversation_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.CommunicationArtifactsHelper import MessageReadStatus
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 TskContactsParser import TskContactsParser
from TskCallLogsParser import TskCallLogsParser
@ -151,7 +154,7 @@ class WhatsAppAnalyzer(general.AndroidComponentAnalyzer):
current_case.getSleuthkitCase(), self._PARSER_NAME,
calllog_and_message_db.getDBFile(), Account.Type.WHATSAPP)
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:
#If there is no current case, bail out immediately.
@ -227,24 +230,32 @@ class WhatsAppAnalyzer(general.AndroidComponentAnalyzer):
"Error posting calllog artifact to the blackboard.", ex)
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:
messages_db.attachDatabase(dataSource, "wa.db",
messages_db.getDBFile().getParentPath(), "wadb")
messages_parser = WhatsAppMessagesParser(messages_db)
while messages_parser.next():
helper.addMessage(
messages_parser.get_message_type(),
messages_parser.get_message_direction(),
messages_parser.get_phone_number_from(),
messages_parser.get_phone_number_to(),
messages_parser.get_message_date_time(),
messages_parser.get_message_read_status(),
messages_parser.get_message_subject(),
messages_parser.get_message_text(),
messages_parser.get_thread_id()
)
message_artifact = helper.addMessage(
messages_parser.get_message_type(),
messages_parser.get_message_direction(),
messages_parser.get_phone_number_from(),
messages_parser.get_phone_number_to(),
messages_parser.get_message_date_time(),
messages_parser.get_message_read_status(),
messages_parser.get_message_subject(),
messages_parser.get_message_text(),
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()
except SQLException as 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")
if message is None:
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
def get_thread_id(self):
@ -512,3 +520,14 @@ class WhatsAppMessagesParser(TskMessagesParser):
if group is not None:
return self.result_set.getString("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(),
NbBundle.getMessage(this.getClass(), "Chrome.moduleName")));
BlackboardArtifact bbart = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadFile, bbattributes);
if (bbart != null) {
bbartifacts.add(bbart);
}
BlackboardArtifact webDownloadArtifact = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadFile, bbattributes);
if (webDownloadArtifact != null) {
bbartifacts.add(webDownloadArtifact);
// find the downloaded file and create a TSK_DOWNLOAD_SOURCE for it..
try {
for (AbstractFile downloadedFile : fileManager.findFiles(dataSource, FilenameUtils.getName(fullPath), FilenameUtils.getPath(fullPath))) {
BlackboardArtifact downloadSourceArt = downloadedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE);
downloadSourceArt.addAttributes(createDownloadSourceAttributes(result.get("url").toString()));
// find the downloaded file and create a TSK_ASSOCIATED_OBJECT for it, associating it with the TSK_WEB_DOWNLOAD artifact.
try {
for (AbstractFile downloadedFile : fileManager.findFiles(dataSource, FilenameUtils.getName(fullPath), FilenameUtils.getPath(fullPath))) {
BlackboardArtifact associatedObjectArtifact = downloadedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT);
associatedObjectArtifact.addAttribute(
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT,
RecentActivityExtracterModuleFactory.getModuleName(), webDownloadArtifact.getArtifactID()));
bbartifacts.add(downloadSourceArt);
break;
bbartifacts.add(associatedObjectArtifact);
break;
}
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Error creating associated object artifact for file '%s'", fullPath), ex); //NON-NLS
}
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Error creating download source 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
*
* @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
*
* @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<>();
@ -437,10 +437,6 @@ final class ChromeCacheExtractor {
moduleName,
cacheEntry.getHTTPHeaders());
Collection<BlackboardAttribute> sourceArtifactAttributes = new ArrayList<>();
sourceArtifactAttributes.add(urlAttr);
sourceArtifactAttributes.add(createTimeAttr);
Collection<BlackboardAttribute> webCacheAttributes = new ArrayList<>();
webCacheAttributes.add(urlAttr);
webCacheAttributes.add(createTimeAttr);
@ -450,11 +446,6 @@ final class ChromeCacheExtractor {
// add artifacts to the f_XXX file
if (dataSegment.isInExternalFile() ) {
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);
if (webCacheArtifact != null) {
@ -469,6 +460,14 @@ final class ChromeCacheExtractor {
moduleName, cachedFileAbstractFile.get().getId()));
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) {
@ -497,11 +496,6 @@ final class ChromeCacheExtractor {
"",
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);
if (webCacheArtifact != null) {
@ -516,6 +510,14 @@ final class ChromeCacheExtractor {
moduleName, derivedFile.getId()));
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) {

View File

@ -50,6 +50,7 @@ import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
import org.sleuthkit.autopsy.recentactivity.BinaryCookieReader.Cookie;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskCoreException;
import org.xml.sax.SAXException;
@ -637,15 +638,17 @@ final class ExtractSafari extends Extract {
time = date.getDate().getTime();
}
BlackboardArtifact bbart = origFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD);
bbart.addAttributes(this.createDownloadAttributes(path, pathID, url, time, NetworkUtils.extractDomain(url), getName()));
bbartifacts.add(bbart);
BlackboardArtifact webDownloadArtifact = origFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD);
webDownloadArtifact.addAttributes(this.createDownloadAttributes(path, pathID, url, time, NetworkUtils.extractDomain(url), getName()));
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))) {
BlackboardArtifact downloadSourceArt = downloadedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE);
downloadSourceArt.addAttributes(createDownloadSourceAttributes(url));
bbartifacts.add(downloadSourceArt);
BlackboardArtifact associatedObjectArtifact = downloadedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT);
associatedObjectArtifact.addAttribute(
new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT,
RecentActivityExtracterModuleFactory.getModuleName(), webDownloadArtifact.getArtifactID()));
bbartifacts.add(associatedObjectArtifact);
break;
}

View File

@ -28,7 +28,6 @@ import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.logging.Level;
import org.apache.commons.lang3.StringUtils;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.coreutils.Logger;
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.datamodel.AbstractFile;
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 org.sleuthkit.datamodel.BlackboardAttribute;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_LOCATION;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT;
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.ReadContentInputStream;
import org.sleuthkit.datamodel.TskCoreException;
@ -94,7 +91,7 @@ final class ExtractZoneIdentifier extends Extract {
return;
}
Collection<BlackboardArtifact> sourceArtifacts = new ArrayList<>();
Collection<BlackboardArtifact> associatedObjectArtifacts = new ArrayList<>();
Collection<BlackboardArtifact> downloadArtifacts = new ArrayList<>();
for (AbstractFile zoneFile : zoneFiles) {
@ -104,7 +101,7 @@ final class ExtractZoneIdentifier extends Extract {
}
try {
processZoneFile(context, dataSource, zoneFile, sourceArtifacts, downloadArtifacts, knownPathIDs);
processZoneFile(context, dataSource, zoneFile, associatedObjectArtifacts, downloadArtifacts, knownPathIDs);
} catch (TskCoreException ex) {
addErrorMessage(Bundle.ExtractZone_process_errMsg());
String message = String.format("Failed to process zone identifier file %s", zoneFile.getName()); //NON-NLS
@ -112,23 +109,23 @@ final class ExtractZoneIdentifier extends Extract {
}
}
postArtifacts(sourceArtifacts);
postArtifacts(associatedObjectArtifacts);
postArtifacts(downloadArtifacts);
}
/**
* Process a single Zone Identifier file.
*
* @param context IngetJobContext
* @param dataSource Content
* @param zoneFile Zone Indentifier file
* @param sourceArtifacts List for TSK_DOWNLOAD_SOURCE artifacts
* @param downloadArtifacts List for TSK_WEB_DOWNLOAD aritfacts
* @param context IngetJobContext
* @param dataSource Content
* @param zoneFile Zone Indentifier file
* @param associatedObjectArtifacts List for TSK_ASSOCIATED_OBJECT artifacts
* @param downloadArtifacts List for TSK_WEB_DOWNLOAD artifacts
*
* @throws TskCoreException
*/
private void processZoneFile(IngestJobContext context, Content dataSource,
AbstractFile zoneFile, Collection<BlackboardArtifact> sourceArtifacts,
AbstractFile zoneFile, Collection<BlackboardArtifact> associatedObjectArtifacts,
Collection<BlackboardArtifact> downloadArtifacts,
Set<Long> knownPathIDs) throws TskCoreException {
@ -155,16 +152,16 @@ final class ExtractZoneIdentifier extends Extract {
BlackboardArtifact downloadBba = createDownloadArtifact(zoneFile, zoneInfo);
if (downloadBba != null) {
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.
*
* @param downloadFile AbstractFile representing the file downloaded, not
* the zone identifier file.
* @param zoneInfo Zone identifier file wrapper object
* the zone identifier file.
* @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<>();
bbattributes.addAll(Arrays.asList(
new BlackboardAttribute(TSK_URL,
RecentActivityExtracterModuleFactory.getModuleName(),
StringUtils.defaultString(zoneInfo.getURL(), "")),
new BlackboardAttribute(TSK_ASSOCIATED_ARTIFACT,
RecentActivityExtracterModuleFactory.getModuleName(),
downloadBba.getArtifactID())
));
new BlackboardAttribute(TSK_DOMAIN,
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);
return createArtifactWithAttributes(TSK_ASSOCIATED_OBJECT, downloadFile, bbattributes);
}
/**

View File

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