mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-12 16:06:15 +00:00
Merge remote-tracking branch 'upstream/develop' into remove-tsk-jfx-dependency
This commit is contained in:
commit
a63a0a26dd
@ -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
|
||||
|
@ -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
|
||||
|
103
Core/src/org/sleuthkit/autopsy/contentviewers/ContextViewer.form
Normal file
103
Core/src/org/sleuthkit/autopsy/contentviewers/ContextViewer.form
Normal 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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</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, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
440
Core/src/org/sleuthkit/autopsy/contentviewers/ContextViewer.java
Normal file
440
Core/src/org/sleuthkit/autopsy/contentviewers/ContextViewer.java
Normal 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
|
||||
}
|
@ -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)
|
||||
*
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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"/>
|
||||
|
@ -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
|
||||
|
||||
/**
|
||||
|
@ -115,10 +115,6 @@
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/geolocation/Bundle.properties" key="GeoFilterPanel.daysLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</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"/>
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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")
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user