mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-12 16:06:15 +00:00
Merge branch 'develop' of github.com:sleuthkit/autopsy into 5014_known_slack_filtering
This commit is contained in:
commit
698b464bc4
@ -110,11 +110,11 @@
|
||||
<target name="getTestDataFiles">
|
||||
<mkdir dir="${basedir}/test/qa-functional/data"/>
|
||||
<get src="https://drive.google.com/uc?id=1FkinvA7EFqP4nOSOyTAOli5KefM67ufA" dest="${test-input}/EmbeddedIM_img1_v2.vhd" skipexisting="true"/>
|
||||
<get src="https://drive.google.com/uc?id=1JACMDyH4y54ypGzFWl82ZzMQf3qbrioP" dest="${test-input}/BitlockerDetection_img1_v1.vhd" skipexisting="true"/>
|
||||
<get src="https://drive.google.com/uc?id=17sGybvmBGsWWJYo1IWKmO04oG9hKpPi3" dest="${test-input}/SqlCipherDetection_img1_v1.vhd" skipexisting="true"/>
|
||||
<get src="https://drive.google.com/uc?id=1zPlIn1xmOeC1VYKmV3fJHl5qE6E_M7K-" dest="${test-input}/BitlockerDetection_img1_v1.vhd" skipexisting="true"/>
|
||||
<get src="https://drive.google.com/uc?id=1brQqmX8MJnBPUFEbsdUvZ1X81PmQNVCY" dest="${test-input}/SqlCipherDetection_img1_v1.vhd" skipexisting="true"/>
|
||||
<get src="https://drive.google.com/uc?id=0BxdBkzm5VKGNT0dGY0dqcHVsU3M" dest="${test-input}/IngestFilters_img1_v1.img" skipexisting="true"/>
|
||||
<get src="https://drive.google.com/uc?id=1bghoSm7z7nhmGIxlllyY1MMlbLntxm7n" dest="${test-input}/IngestFilters_local1_v1.zip" skipexisting="true"/>
|
||||
<get src="https://drive.google.com/uc?id=1BrSiUQ1fzxFS9vIaK4mYKX6qIVp9kRWT" dest="${test-input}/PasswordDetection_img1_v1.img" skipexisting="true"/>
|
||||
<get src="https://drive.google.com/uc?id=1Ovyv_xVty8xMYgsc3C45pvphfhbdKg1x" dest="${test-input}/PasswordDetection_img1_v1.img" skipexisting="true"/>
|
||||
<get src="https://drive.google.com/uc?id=1HD8s4rculgHV1qZT5g80Kg7j4m1qccrN" dest="${test-input}/VeracryptDetection_img1_v1.vhd" skipexisting="true"/>
|
||||
<get src="https://drive.google.com/uc?id=1mr9waEDG5H8GBBn_yXwMUQ6senwC1goL" dest="${test-input}/CommonFiles_img1_v1.vhd" skipexisting="true"/>
|
||||
<get src="https://drive.google.com/uc?id=1-vmbmAAb2HBLbf58GpAA97ozGUFiYHbN" dest="${test-input}/CommonFiles_img2_v1.vhd" skipexisting="true"/>
|
||||
|
@ -181,7 +181,7 @@ final class AddImageWizardSelectDspVisual extends JPanel {
|
||||
/**
|
||||
* Create a list of the DataSourceProcessors which should exist as options
|
||||
* on this panel. The default Autopsy DataSourceProcessors will appear at
|
||||
* the beggining of the list in the same order.
|
||||
* the beginning of the list in the same order.
|
||||
*
|
||||
* @return dspList a list of DataSourceProcessors which can be chose in this
|
||||
* panel
|
||||
@ -200,6 +200,7 @@ final class AddImageWizardSelectDspVisual extends JPanel {
|
||||
dspList.add(LocalDiskDSProcessor.getType());
|
||||
dspList.add(LocalFilesDSProcessor.getType());
|
||||
dspList.add(RawDSProcessor.getType());
|
||||
dspList.add(LogicalImagerDSProcessor.getType());
|
||||
// now add any addtional DSPs that haven't already been added
|
||||
for (String dspType : datasourceProcessorsMap.keySet()) {
|
||||
if (!dspList.contains(dspType)) {
|
||||
|
@ -238,6 +238,13 @@ OpenMultiUserCasePanel.cancelButton.text=Cancel
|
||||
OpenMultiUserCasePanel.openSingleUserCaseButton.text=Open Single-User Case...
|
||||
OpenMultiUserCasePanel.openSelectedCaseButton.text=Open Selected Case
|
||||
OpenMultiUserCasePanel.searchLabel.text=Select any case and start typing to search by case name
|
||||
LogicalImagerPanel.jLabel1.text=Insert external drive
|
||||
LogicalImagerPanel.scanButton.text=Scan
|
||||
LogicalImagerPanel.jLabel6.text=Or, pick a Logical Imager folder
|
||||
LogicalImagerPanel.browseButton.text=Browse
|
||||
LogicalImagerPanel.topLabel.text=Import Autopsy Imager Results
|
||||
LogicalImagerPanel.selectDriveLabel.text=Select Drive
|
||||
LogicalImagerPanel.messageLabel.text=Error/Status message
|
||||
UnpackagePortableCaseDialog.desc2Label.text=Portable Case Report Module.
|
||||
UnpackagePortableCaseDialog.desc1Label.text=Unpackage a portable case so it can be opened in Autopsy. Portable cases are created through the
|
||||
UnpackagePortableCaseDialog.exitButton.text=Exit
|
||||
@ -252,4 +259,4 @@ UnpackagePortableCaseProgressDialog.cancelButton.text=Cancel
|
||||
UnpackagePortableCaseProgressDialog.okButton.text=OK
|
||||
UnpackagePortableCaseProgressDialog.resultLabel.text=resultLabel
|
||||
UnpackagePortableCaseDialog.extractLabel.text=Folder to extract to:
|
||||
UnpackagePortableCaseDialog.caseLabel.text=Portable Case:
|
||||
UnpackagePortableCaseDialog.caseLabel.text=Portable Case:
|
@ -168,6 +168,18 @@ LogicalEvidenceFilePanel.pathValidation.getOpenCase.Error=Warning: Exception whi
|
||||
LogicalEvidenceFilePanel.validatePanel.nonL01Error.text=Only files with the .l01 file extension are supported here.
|
||||
LogicalFilesDspPanel.subTypeComboBox.l01FileOption.text=Logical evidence file (L01)
|
||||
LogicalFilesDspPanel.subTypeComboBox.localFilesOption.text=Local files and folders
|
||||
LogicalImagerDSProcessor.dataSourceType=Autopsy Imager
|
||||
LogicalImagerPanel.imageTable.columnModel.title0=Hostname
|
||||
LogicalImagerPanel.imageTable.columnModel.title1=Extracted Date
|
||||
LogicalImagerPanel.messageLabel.clickScanOrBrowse=Click SCAN or BROWSE button to find images
|
||||
LogicalImagerPanel.messageLabel.directoryDoesNotContainSparseImage=Directory {0} does not contain {1}
|
||||
LogicalImagerPanel.messageLabel.directoryFormatInvalid=Directory {0} does not match format Logical_Imager_HOSTNAME_yyyymmdd_HH_MM_SS
|
||||
LogicalImagerPanel.messageLabel.driveHasNoImages=Drive has no images
|
||||
LogicalImagerPanel.messageLabel.noExternalDriveFound=No drive found
|
||||
LogicalImagerPanel.messageLabel.noImageSelected=No image selected
|
||||
LogicalImagerPanel.messageLabel.scanningExternalDrives=Scanning external drives for sparse_image.vhd ...
|
||||
LogicalImagerPanel.messageLabel.selectedImage=Selected folder
|
||||
LogicalImagerPanel.selectAcquisitionFromDriveLabel.text=Select acquisition from Drive
|
||||
Menu/Case/OpenRecentCase=Open Recent Case
|
||||
CTL_CaseDeleteAction=Delete Case
|
||||
OpenIDE-Module-Name=Case
|
||||
@ -440,6 +452,13 @@ OpenMultiUserCasePanel.cancelButton.text=Cancel
|
||||
OpenMultiUserCasePanel.openSingleUserCaseButton.text=Open Single-User Case...
|
||||
OpenMultiUserCasePanel.openSelectedCaseButton.text=Open Selected Case
|
||||
OpenMultiUserCasePanel.searchLabel.text=Select any case and start typing to search by case name
|
||||
LogicalImagerPanel.jLabel1.text=Insert external drive
|
||||
LogicalImagerPanel.scanButton.text=Scan
|
||||
LogicalImagerPanel.jLabel6.text=Or, pick a Logical Imager folder
|
||||
LogicalImagerPanel.browseButton.text=Browse
|
||||
LogicalImagerPanel.topLabel.text=Import Autopsy Imager Results
|
||||
LogicalImagerPanel.selectDriveLabel.text=Select Drive
|
||||
LogicalImagerPanel.messageLabel.text=Error/Status message
|
||||
UnpackagePortableCaseDialog.desc2Label.text=Portable Case Report Module.
|
||||
UnpackagePortableCaseDialog.desc1Label.text=Unpackage a portable case so it can be opened in Autopsy. Portable cases are created through the
|
||||
UnpackagePortableCaseDialog.exitButton.text=Exit
|
||||
|
@ -116,7 +116,7 @@
|
||||
<Component id="hashValuesNoteLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace type="separate" max="-2" attributes="0"/>
|
||||
<Component id="errorLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace pref="51" max="32767" attributes="0"/>
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
|
@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-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.casemodule;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import javax.swing.JPanel;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.openide.util.lookup.ServiceProvider;
|
||||
import org.openide.util.lookup.ServiceProviders;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor;
|
||||
|
||||
/**
|
||||
* A Logical Imager data source processor that implements the DataSourceProcessor service
|
||||
* provider interface to allow integration with the add data source wizard. It
|
||||
* also provides a run method overload to allow it to be used independently of
|
||||
* the wizard.
|
||||
*/
|
||||
@ServiceProviders(value={
|
||||
@ServiceProvider(service=DataSourceProcessor.class)}
|
||||
)
|
||||
public class LogicalImagerDSProcessor implements DataSourceProcessor {
|
||||
|
||||
private final LogicalImagerPanel configPanel;
|
||||
|
||||
/*
|
||||
* Constructs a Logical Imager data source processor that implements the
|
||||
* DataSourceProcessor service provider interface to allow integration with
|
||||
* the add data source wizard. It also provides a run method overload to
|
||||
* allow it to be used independently of the wizard.
|
||||
*/
|
||||
public LogicalImagerDSProcessor() {
|
||||
configPanel = LogicalImagerPanel.createInstance(LogicalImagerDSProcessor.class.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string that describes the type of data sources this processor is
|
||||
* able to add to the case database. The string is suitable for display in a
|
||||
* type selection UI component (e.g., a combo box).
|
||||
*
|
||||
* @return A data source type display string for this data source processor.
|
||||
*/
|
||||
@Messages({"LogicalImagerDSProcessor.dataSourceType=Autopsy Imager"})
|
||||
public static String getType() {
|
||||
return Bundle.LogicalImagerDSProcessor_dataSourceType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string that describes the type of data sources this processor is
|
||||
* able to add to the case database. The string is suitable for display in a
|
||||
* type selection UI component (e.g., a combo box).
|
||||
*
|
||||
* @return A data source type display string for this data source processor.
|
||||
*/
|
||||
@Override
|
||||
public String getDataSourceType() {
|
||||
return Bundle.LogicalImagerDSProcessor_dataSourceType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the panel that allows a user to select a data source and do any
|
||||
* configuration required by the data source. The panel is less than 544
|
||||
* pixels wide and less than 173 pixels high.
|
||||
*
|
||||
* @return A selection and configuration panel for this data source
|
||||
* processor.
|
||||
*/
|
||||
@Override
|
||||
public JPanel getPanel() {
|
||||
configPanel.reset();
|
||||
return configPanel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the settings in the selection and configuration panel
|
||||
* are valid and complete.
|
||||
*
|
||||
* @return True if the settings are valid and complete and the processor is
|
||||
* ready to have its run method called, false otherwise.
|
||||
*/
|
||||
@Override
|
||||
public boolean isPanelValid() {
|
||||
return configPanel.validatePanel();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a data source to the case database using a background task in a
|
||||
* separate thread and the settings provided by the selection and
|
||||
* configuration panel. Returns as soon as the background task is started.
|
||||
* The background task uses a callback object to signal task completion and
|
||||
* return results.
|
||||
*
|
||||
* This method should not be called unless isPanelValid returns true.
|
||||
*
|
||||
* @param progressMonitor Progress monitor that will be used by the
|
||||
* background task to report progress.
|
||||
* @param callback Callback that will be used by the background task
|
||||
* to return results.
|
||||
*/
|
||||
@Override
|
||||
public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||
configPanel.storeSettings();
|
||||
Path dirPath = configPanel.getImageDirPath();
|
||||
System.out.println("Choosen directory " + dirPath.toString());
|
||||
// TODO: process the data source in 5011
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a "Logical Imager" data source to the case database using a background task in
|
||||
* a separate thread and the given settings instead of those provided by the
|
||||
* selection and configuration panel. Returns as soon as the background task
|
||||
* is started and uses the callback object to signal task completion and
|
||||
* return results.
|
||||
*
|
||||
* @param deviceId An ASCII-printable identifier for the device
|
||||
* associated with the data source that is
|
||||
* intended to be unique across multiple cases
|
||||
* (e.g., a UUID).
|
||||
* @param imageFilePath Path to the image file.
|
||||
* @param timeZone The time zone to use when processing dates
|
||||
* and times for the image, obtained from
|
||||
* java.util.TimeZone.getID.
|
||||
* @param chunkSize The maximum size of each chunk of the raw
|
||||
* data source as it is divided up into virtual
|
||||
* unallocated space files.
|
||||
* @param progressMonitor Progress monitor for reporting progress
|
||||
* during processing.
|
||||
* @param callback Callback to call when processing is done.
|
||||
*/
|
||||
private void run(String deviceId, String imagePath, int sectorSize, String timeZone, boolean ignoreFatOrphanFiles, String md5, String sha1, String sha256, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||
AddImageTask addImageTask = new AddImageTask(deviceId, imagePath, sectorSize, timeZone, ignoreFatOrphanFiles, md5, sha1, sha256, null, progressMonitor, callback);
|
||||
new Thread(addImageTask).start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the selection and configuration panel for this data source
|
||||
* processor.
|
||||
*/
|
||||
@Override
|
||||
public void reset() {
|
||||
configPanel.reset();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,233 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.5" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
||||
<Properties>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[0, 65]"/>
|
||||
</Property>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[403, 65]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<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 min="-2" pref="238" max="-2" attributes="0"/>
|
||||
<Component id="topLabel" min="-2" pref="163" max="-2" attributes="0"/>
|
||||
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace min="-2" pref="28" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="messageLabel" alignment="1" max="32767" attributes="0"/>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Group type="103" groupAlignment="1" max="-2" attributes="0">
|
||||
<Component id="jSeparator1" alignment="0" max="32767" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<Component id="selectDriveLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="289" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<Component id="scanButton" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="126" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
<EmptySpace min="-2" pref="36" max="-2" attributes="0"/>
|
||||
<Component id="browseButton" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="driveListScrollPane" min="-2" pref="211" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="28" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="selectAcquisitionFromDriveLabel" min="-2" pref="305" max="-2" attributes="0"/>
|
||||
<Component id="imageScrollPane" min="-2" pref="346" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace min="-2" pref="346" max="-2" attributes="0"/>
|
||||
<Component id="jLabel6" min="-2" pref="154" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace min="-2" pref="144" max="-2" attributes="0"/>
|
||||
<Component id="jLabel1" min="-2" pref="116" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
<EmptySpace pref="48" 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">
|
||||
<Component id="topLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="jLabel1" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="jLabel6" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="scanButton" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="browseButton" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||
<Component id="jSeparator1" min="-2" pref="4" max="-2" attributes="0"/>
|
||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="selectDriveLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="selectAcquisitionFromDriveLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" max="-2" attributes="0">
|
||||
<Component id="imageScrollPane" pref="0" max="32767" attributes="0"/>
|
||||
<Component id="driveListScrollPane" pref="194" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace min="-2" pref="26" max="-2" attributes="0"/>
|
||||
<Component id="messageLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="154" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JLabel" name="topLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="LogicalImagerPanel.topLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="jLabel1">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="LogicalImagerPanel.jLabel1.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="scanButton">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="LogicalImagerPanel.scanButton.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="scanButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="messageLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="LogicalImagerPanel.messageLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="selectDriveLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="LogicalImagerPanel.selectDriveLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Container class="javax.swing.JScrollPane" name="driveListScrollPane">
|
||||
<AuxValues>
|
||||
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JList" name="driveList">
|
||||
<Properties>
|
||||
<Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.editors2.ListModelEditor">
|
||||
<StringArray count="0"/>
|
||||
</Property>
|
||||
<Property name="selectionMode" type="int" value="0"/>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="mouseClicked" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="driveListMouseClicked"/>
|
||||
<EventHandler event="keyReleased" listener="java.awt.event.KeyListener" parameters="java.awt.event.KeyEvent" handler="driveListKeyReleased"/>
|
||||
</Events>
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="<String>"/>
|
||||
</AuxValues>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Component class="javax.swing.JLabel" name="selectAcquisitionFromDriveLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="LogicalImagerPanel.selectAcquisitionFromDriveLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="jLabel6">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="LogicalImagerPanel.jLabel6.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="browseButton">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="LogicalImagerPanel.browseButton.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="browseButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Container class="javax.swing.JScrollPane" name="imageScrollPane">
|
||||
<Properties>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[346, 402]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JTable" name="imageTable">
|
||||
<Properties>
|
||||
<Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.editors2.TableModelEditor">
|
||||
<Table columnCount="0" rowCount="0"/>
|
||||
</Property>
|
||||
<Property name="autoResizeMode" type="int" value="0"/>
|
||||
<Property name="columnModel" type="javax.swing.table.TableColumnModel" editor="org.netbeans.modules.form.editors2.TableColumnModelEditor">
|
||||
<TableColumnModel selectionModel="1"/>
|
||||
</Property>
|
||||
<Property name="showHorizontalLines" type="boolean" value="false"/>
|
||||
<Property name="showVerticalLines" type="boolean" value="false"/>
|
||||
<Property name="tableHeader" type="javax.swing.table.JTableHeader" editor="org.netbeans.modules.form.editors2.JTableHeaderEditor">
|
||||
<TableHeader reorderingAllowed="false" resizingAllowed="true"/>
|
||||
</Property>
|
||||
<Property name="updateSelectionOnSort" type="boolean" value="false"/>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="mouseClicked" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="imageTableMouseClicked"/>
|
||||
<EventHandler event="keyReleased" listener="java.awt.event.KeyListener" parameters="java.awt.event.KeyEvent" handler="imageTableKeyReleased"/>
|
||||
</Events>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Component class="javax.swing.JSeparator" name="jSeparator1">
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
@ -0,0 +1,583 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-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.casemodule;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.FileStore;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.swing.JFileChooser;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import javax.swing.filechooser.FileSystemView;
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
import javax.swing.table.TableModel;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
|
||||
|
||||
/**
|
||||
* Panel for adding an logical image file from drive letters. Allows the user
|
||||
* to select a file.
|
||||
*/
|
||||
@Messages({
|
||||
"LogicalImagerPanel.messageLabel.selectedImage=Selected folder",
|
||||
"LogicalImagerPanel.messageLabel.noImageSelected=No image selected",
|
||||
"LogicalImagerPanel.messageLabel.driveHasNoImages=Drive has no images",
|
||||
"LogicalImagerPanel.selectAcquisitionFromDriveLabel.text=Select acquisition from Drive",
|
||||
})
|
||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||
public class LogicalImagerPanel extends JPanel implements DocumentListener {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final String SPARSE_IMAGE_VHD = "sparse_image.vhd"; //NON-NLS
|
||||
private static final String SELECTED_IMAGE = Bundle.LogicalImagerPanel_messageLabel_selectedImage();
|
||||
private static final String NO_IMAGE_SELECTED = Bundle.LogicalImagerPanel_messageLabel_noImageSelected();
|
||||
private static final String DRIVE_HAS_NO_IMAGES = Bundle.LogicalImagerPanel_messageLabel_driveHasNoImages();
|
||||
private static final String[] EMPTY_LIST_DATA = {};
|
||||
|
||||
private final JFileChooser fileChooser = new JFileChooser();
|
||||
private final Pattern regex = Pattern.compile("Logical_Imager_(.+)_(\\d{4})(\\d{2})(\\d{2})_(\\d{2})_(\\d{2})_(\\d{2})");
|
||||
private final String contextName;
|
||||
private Path choosenImageDirPath;
|
||||
private TableModel imageTableModel;
|
||||
|
||||
/**
|
||||
* Creates new form LogicalImagerPanel
|
||||
*
|
||||
* @param context A string context name used to read/store last
|
||||
* used settings.
|
||||
*/
|
||||
private LogicalImagerPanel(String context) {
|
||||
this.contextName = context;
|
||||
initComponents();
|
||||
clearImageTable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns an instance of a LogicalImagerPanel.
|
||||
*
|
||||
* @param context A string context name used to read/store last
|
||||
* used settings.
|
||||
*
|
||||
* @return instance of the LogicalImagerPanel
|
||||
*/
|
||||
@Messages({
|
||||
"LogicalImagerPanel.messageLabel.clickScanOrBrowse=Click SCAN or BROWSE button to find images"
|
||||
})
|
||||
public static synchronized LogicalImagerPanel createInstance(String context) {
|
||||
LogicalImagerPanel instance = new LogicalImagerPanel(context);
|
||||
// post-constructor initialization of listener support without leaking references of uninitialized objects
|
||||
instance.messageLabel.setText(Bundle.LogicalImagerPanel_messageLabel_clickScanOrBrowse());
|
||||
instance.imageTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
topLabel = new javax.swing.JLabel();
|
||||
jLabel1 = new javax.swing.JLabel();
|
||||
scanButton = new javax.swing.JButton();
|
||||
messageLabel = new javax.swing.JLabel();
|
||||
selectDriveLabel = new javax.swing.JLabel();
|
||||
driveListScrollPane = new javax.swing.JScrollPane();
|
||||
driveList = new javax.swing.JList<>();
|
||||
selectAcquisitionFromDriveLabel = new javax.swing.JLabel();
|
||||
jLabel6 = new javax.swing.JLabel();
|
||||
browseButton = new javax.swing.JButton();
|
||||
imageScrollPane = new javax.swing.JScrollPane();
|
||||
imageTable = new javax.swing.JTable();
|
||||
jSeparator1 = new javax.swing.JSeparator();
|
||||
|
||||
setMinimumSize(new java.awt.Dimension(0, 65));
|
||||
setPreferredSize(new java.awt.Dimension(403, 65));
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(topLabel, org.openide.util.NbBundle.getMessage(LogicalImagerPanel.class, "LogicalImagerPanel.topLabel.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(LogicalImagerPanel.class, "LogicalImagerPanel.jLabel1.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(scanButton, org.openide.util.NbBundle.getMessage(LogicalImagerPanel.class, "LogicalImagerPanel.scanButton.text")); // NOI18N
|
||||
scanButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
scanButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(messageLabel, org.openide.util.NbBundle.getMessage(LogicalImagerPanel.class, "LogicalImagerPanel.messageLabel.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(selectDriveLabel, org.openide.util.NbBundle.getMessage(LogicalImagerPanel.class, "LogicalImagerPanel.selectDriveLabel.text")); // NOI18N
|
||||
|
||||
driveList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
|
||||
driveList.addMouseListener(new java.awt.event.MouseAdapter() {
|
||||
public void mouseClicked(java.awt.event.MouseEvent evt) {
|
||||
driveListMouseClicked(evt);
|
||||
}
|
||||
});
|
||||
driveList.addKeyListener(new java.awt.event.KeyAdapter() {
|
||||
public void keyReleased(java.awt.event.KeyEvent evt) {
|
||||
driveListKeyReleased(evt);
|
||||
}
|
||||
});
|
||||
driveListScrollPane.setViewportView(driveList);
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(selectAcquisitionFromDriveLabel, org.openide.util.NbBundle.getMessage(LogicalImagerPanel.class, "LogicalImagerPanel.selectAcquisitionFromDriveLabel.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(jLabel6, org.openide.util.NbBundle.getMessage(LogicalImagerPanel.class, "LogicalImagerPanel.jLabel6.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(browseButton, org.openide.util.NbBundle.getMessage(LogicalImagerPanel.class, "LogicalImagerPanel.browseButton.text")); // NOI18N
|
||||
browseButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
browseButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
imageScrollPane.setPreferredSize(new java.awt.Dimension(346, 402));
|
||||
|
||||
imageTable.setModel(new javax.swing.table.DefaultTableModel(
|
||||
new Object [][] {
|
||||
|
||||
},
|
||||
new String [] {
|
||||
|
||||
}
|
||||
));
|
||||
imageTable.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_OFF);
|
||||
imageTable.setShowHorizontalLines(false);
|
||||
imageTable.setShowVerticalLines(false);
|
||||
imageTable.getTableHeader().setReorderingAllowed(false);
|
||||
imageTable.setUpdateSelectionOnSort(false);
|
||||
imageTable.addMouseListener(new java.awt.event.MouseAdapter() {
|
||||
public void mouseClicked(java.awt.event.MouseEvent evt) {
|
||||
imageTableMouseClicked(evt);
|
||||
}
|
||||
});
|
||||
imageTable.addKeyListener(new java.awt.event.KeyAdapter() {
|
||||
public void keyReleased(java.awt.event.KeyEvent evt) {
|
||||
imageTableKeyReleased(evt);
|
||||
}
|
||||
});
|
||||
imageScrollPane.setViewportView(imageTable);
|
||||
imageTable.getColumnModel().getSelectionModel().setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGap(238, 238, 238)
|
||||
.addComponent(topLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 163, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(0, 0, Short.MAX_VALUE))
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGap(28, 28, 28)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(messageLabel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
|
||||
.addComponent(jSeparator1, javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(selectDriveLabel)
|
||||
.addGap(289, 289, 289))
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
|
||||
.addComponent(scanButton)
|
||||
.addGap(126, 126, 126)))
|
||||
.addGap(36, 36, 36)
|
||||
.addComponent(browseButton))
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(driveListScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 211, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(28, 28, 28)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(selectAcquisitionFromDriveLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 305, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(imageScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 346, javax.swing.GroupLayout.PREFERRED_SIZE)))
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGap(346, 346, 346)
|
||||
.addComponent(jLabel6, javax.swing.GroupLayout.PREFERRED_SIZE, 154, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGap(144, 144, 144)
|
||||
.addComponent(jLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, 116, javax.swing.GroupLayout.PREFERRED_SIZE))))
|
||||
.addContainerGap(48, Short.MAX_VALUE))))
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(topLabel)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(jLabel1)
|
||||
.addComponent(jLabel6))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(scanButton)
|
||||
.addComponent(browseButton))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, 4, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(selectDriveLabel)
|
||||
.addComponent(selectAcquisitionFromDriveLabel))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
|
||||
.addComponent(imageScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)
|
||||
.addComponent(driveListScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 194, Short.MAX_VALUE))
|
||||
.addGap(26, 26, 26)
|
||||
.addComponent(messageLabel)
|
||||
.addGap(154, 154, 154))
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
public static String humanReadableByteCount(long bytes, boolean si) {
|
||||
int unit = si ? 1000 : 1024;
|
||||
if (bytes < unit) {
|
||||
return bytes + " B"; //NON-NLS
|
||||
}
|
||||
int exp = (int) (Math.log(bytes) / Math.log(unit));
|
||||
String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp-1) + (si ? "" : "i"); //NON-NLS
|
||||
return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre); //NON-NLS
|
||||
}
|
||||
|
||||
@Messages({
|
||||
"LogicalImagerPanel.messageLabel.scanningExternalDrives=Scanning external drives for sparse_image.vhd ...",
|
||||
"LogicalImagerPanel.messageLabel.noExternalDriveFound=No drive found"
|
||||
})
|
||||
private void scanButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_scanButtonActionPerformed
|
||||
// Scan external drives for sparse_image.vhd
|
||||
clearImageTable();
|
||||
setNormalMessage(Bundle.LogicalImagerPanel_messageLabel_scanningExternalDrives());
|
||||
List<String> listData = new ArrayList<>();
|
||||
File[] roots = File.listRoots();
|
||||
int firstRemovableDrive = -1;
|
||||
int i = 0;
|
||||
for (File root : roots) {
|
||||
String description = FileSystemView.getFileSystemView().getSystemTypeDescription(root);
|
||||
long spaceInBytes = root.getTotalSpace();
|
||||
String sizeWithUnit = humanReadableByteCount(spaceInBytes, false);
|
||||
listData.add(root + " (" + description + ") (" + sizeWithUnit + ")");
|
||||
if (firstRemovableDrive == -1) {
|
||||
try {
|
||||
FileStore fileStore = Files.getFileStore(root.toPath());
|
||||
if ((boolean) fileStore.getAttribute("volume:isRemovable")) { //NON-NLS
|
||||
firstRemovableDrive = i;
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
; // skip
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
driveList.setListData(listData.toArray(new String[0]));
|
||||
if (!listData.isEmpty()) {
|
||||
// auto-select the first external drive, if any
|
||||
driveList.setSelectedIndex(firstRemovableDrive == -1 ? 0 : firstRemovableDrive);
|
||||
driveListMouseClicked(null);
|
||||
driveList.requestFocusInWindow();
|
||||
} else {
|
||||
setErrorMessage(Bundle.LogicalImagerPanel_messageLabel_noExternalDriveFound());
|
||||
}
|
||||
firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), true, false);
|
||||
}//GEN-LAST:event_scanButtonActionPerformed
|
||||
|
||||
@Messages({
|
||||
"LogicalImagerPanel.messageLabel.directoryDoesNotContainSparseImage=Directory {0} does not contain {1}",
|
||||
"LogicalImagerPanel.messageLabel.directoryFormatInvalid=Directory {0} does not match format Logical_Imager_HOSTNAME_yyyymmdd_HH_MM_SS"
|
||||
})
|
||||
private void browseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browseButtonActionPerformed
|
||||
imageTable.clearSelection();
|
||||
choosenImageDirPath = null;
|
||||
setErrorMessage(NO_IMAGE_SELECTED);
|
||||
fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
|
||||
int retval = fileChooser.showOpenDialog(this);
|
||||
if (retval == JFileChooser.APPROVE_OPTION) {
|
||||
String path = fileChooser.getSelectedFile().getPath();
|
||||
Matcher m = regex.matcher(path);
|
||||
if (m.find()) {
|
||||
Path vhdPath = Paths.get(path, SPARSE_IMAGE_VHD);
|
||||
if (!vhdPath.toFile().exists()) {
|
||||
setErrorMessage(Bundle.LogicalImagerPanel_messageLabel_directoryDoesNotContainSparseImage(path,SPARSE_IMAGE_VHD));
|
||||
firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), true, false);
|
||||
return;
|
||||
}
|
||||
choosenImageDirPath = Paths.get(path);
|
||||
setNormalMessage(SELECTED_IMAGE + " " + path);
|
||||
firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), false, true);
|
||||
} else {
|
||||
setErrorMessage(Bundle.LogicalImagerPanel_messageLabel_directoryFormatInvalid(path));
|
||||
firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), true, false);
|
||||
}
|
||||
} else {
|
||||
firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), true, false);
|
||||
}
|
||||
}//GEN-LAST:event_browseButtonActionPerformed
|
||||
|
||||
private void imageTableSelect() {
|
||||
int index = imageTable.getSelectedRow();
|
||||
if (index != -1) {
|
||||
choosenImageDirPath = Paths.get((String) imageTableModel.getValueAt(index, 2));
|
||||
setNormalMessage(SELECTED_IMAGE + " " + choosenImageDirPath.toString());
|
||||
firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), false, true);
|
||||
} else {
|
||||
choosenImageDirPath = null;
|
||||
setErrorMessage(NO_IMAGE_SELECTED);
|
||||
firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), true, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void imageTableMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_imageTableMouseClicked
|
||||
imageTableSelect();
|
||||
}//GEN-LAST:event_imageTableMouseClicked
|
||||
|
||||
private void driveListSelect() {
|
||||
String selectedStr = driveList.getSelectedValue();
|
||||
if (selectedStr == null) {
|
||||
return;
|
||||
}
|
||||
String driveLetter = selectedStr.substring(0, 3);
|
||||
File directory = new File(driveLetter);
|
||||
File[] fList = directory.listFiles();
|
||||
|
||||
if (fList != null) {
|
||||
imageTableModel = new ImageTableModel();
|
||||
int row = 0;
|
||||
// Find all directories with name like Logical_Imager_HOSTNAME_yyyymmdd_HH_MM_SS
|
||||
// and has a sparse_image.vhd file in it
|
||||
for (File file : fList) {
|
||||
if (file.isDirectory()
|
||||
&& Paths.get(driveLetter, file.getName(), SPARSE_IMAGE_VHD).toFile().exists()) {
|
||||
String dir = file.getName();
|
||||
Matcher m = regex.matcher(dir);
|
||||
if (m.find()) {
|
||||
String imageDirPath = driveLetter + dir;
|
||||
String hostname = m.group(1);
|
||||
String year = m.group(2);
|
||||
String month = m.group(3);
|
||||
String day = m.group(4);
|
||||
String hour = m.group(5);
|
||||
String minute = m.group(6);
|
||||
String second = m.group(7);
|
||||
String extractDate = year + "/" + month + "/" + day
|
||||
+ " " + hour + ":" + minute + ":" + second;
|
||||
imageTableModel.setValueAt(hostname, row, 0);
|
||||
imageTableModel.setValueAt(extractDate, row, 1);
|
||||
imageTableModel.setValueAt(imageDirPath, row, 2);
|
||||
row++;
|
||||
}
|
||||
}
|
||||
}
|
||||
selectAcquisitionFromDriveLabel.setText(Bundle.LogicalImagerPanel_selectAcquisitionFromDriveLabel_text()
|
||||
+ " " + driveLetter);
|
||||
imageTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
|
||||
imageTable.setModel(imageTableModel);
|
||||
fixImageTableColumnWidth();
|
||||
// If there are any images, select the first one
|
||||
if (imageTable.getRowCount() > 0) {
|
||||
imageTable.setRowSelectionInterval(0, 0);
|
||||
imageTableSelect();
|
||||
} else {
|
||||
choosenImageDirPath = null;
|
||||
setErrorMessage(DRIVE_HAS_NO_IMAGES);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void fixImageTableColumnWidth() {
|
||||
int width = imageScrollPane.getPreferredSize().width - 2;
|
||||
imageTable.getColumnModel().getColumn(0).setPreferredWidth((int) (.60 * width));
|
||||
imageTable.getColumnModel().getColumn(1).setPreferredWidth((int) (.40 * width));
|
||||
}
|
||||
|
||||
private void setErrorMessage(String msg) {
|
||||
messageLabel.setForeground(Color.red);
|
||||
messageLabel.setText(msg);
|
||||
}
|
||||
|
||||
private void setNormalMessage(String msg) {
|
||||
messageLabel.setForeground(Color.black);
|
||||
messageLabel.setText(msg);
|
||||
}
|
||||
|
||||
private void clearImageTable() {
|
||||
imageTableModel = new ImageTableModel();
|
||||
imageTable.setModel(imageTableModel);
|
||||
fixImageTableColumnWidth();
|
||||
}
|
||||
|
||||
private void driveListMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_driveListMouseClicked
|
||||
driveListSelect();
|
||||
firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), true, false);
|
||||
}//GEN-LAST:event_driveListMouseClicked
|
||||
|
||||
private void driveListKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_driveListKeyReleased
|
||||
driveListSelect();
|
||||
firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), true, false);
|
||||
}//GEN-LAST:event_driveListKeyReleased
|
||||
|
||||
private void imageTableKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_imageTableKeyReleased
|
||||
imageTableSelect();
|
||||
firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), true, false);
|
||||
}//GEN-LAST:event_imageTableKeyReleased
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JButton browseButton;
|
||||
private javax.swing.JList<String> driveList;
|
||||
private javax.swing.JScrollPane driveListScrollPane;
|
||||
private javax.swing.JScrollPane imageScrollPane;
|
||||
private javax.swing.JTable imageTable;
|
||||
private javax.swing.JLabel jLabel1;
|
||||
private javax.swing.JLabel jLabel6;
|
||||
private javax.swing.JSeparator jSeparator1;
|
||||
private javax.swing.JLabel messageLabel;
|
||||
private javax.swing.JButton scanButton;
|
||||
private javax.swing.JLabel selectAcquisitionFromDriveLabel;
|
||||
private javax.swing.JLabel selectDriveLabel;
|
||||
private javax.swing.JLabel topLabel;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
public void reset() {
|
||||
//reset the UI elements to default
|
||||
choosenImageDirPath = null;
|
||||
driveList.setListData(EMPTY_LIST_DATA);
|
||||
clearImageTable();
|
||||
setNormalMessage(Bundle.LogicalImagerPanel_messageLabel_clickScanOrBrowse());
|
||||
}
|
||||
|
||||
/**
|
||||
* Should we enable the next button of the wizard?
|
||||
*
|
||||
* @return true if a proper image has been selected, false otherwise
|
||||
*/
|
||||
public boolean validatePanel() {
|
||||
return choosenImageDirPath != null && choosenImageDirPath.toFile().exists();
|
||||
}
|
||||
|
||||
Path getImageDirPath() {
|
||||
return choosenImageDirPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e) {
|
||||
}
|
||||
|
||||
void storeSettings() {
|
||||
}
|
||||
|
||||
private class ImageTableModel extends AbstractTableModel {
|
||||
private final List<String> hostnames = new ArrayList<>();
|
||||
private final List<String> extractDates = new ArrayList<>();
|
||||
private final List<String> imageDirPaths = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
return hostnames.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Messages({
|
||||
"LogicalImagerPanel.imageTable.columnModel.title0=Hostname",
|
||||
"LogicalImagerPanel.imageTable.columnModel.title1=Extracted Date"
|
||||
})
|
||||
@Override
|
||||
public String getColumnName(int column) {
|
||||
String colName = null;
|
||||
switch (column) {
|
||||
case 0:
|
||||
colName = Bundle.LogicalImagerPanel_imageTable_columnModel_title0();
|
||||
break;
|
||||
case 1:
|
||||
colName = Bundle.LogicalImagerPanel_imageTable_columnModel_title1();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return colName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValueAt(int rowIndex, int columnIndex) {
|
||||
Object ret = null;
|
||||
switch (columnIndex) {
|
||||
case 0:
|
||||
ret = hostnames.get(rowIndex);
|
||||
break;
|
||||
case 1:
|
||||
ret = extractDates.get(rowIndex);
|
||||
break;
|
||||
case 2:
|
||||
ret = imageDirPaths.get(rowIndex);
|
||||
break;
|
||||
default:
|
||||
throw new UnsupportedOperationException("Invalid table column index: " + columnIndex); //NON-NLS
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCellEditable(int rowIndex, int columnIndex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
|
||||
switch (columnIndex) {
|
||||
case 0:
|
||||
hostnames.add((String) aValue);
|
||||
break;
|
||||
case 1:
|
||||
extractDates.add((String) aValue);
|
||||
break;
|
||||
case 2:
|
||||
imageDirPaths.add((String) aValue);
|
||||
break;
|
||||
default:
|
||||
throw new UnsupportedOperationException("Invalid table column index: " + columnIndex); //NON-NLS
|
||||
}
|
||||
// Only show the hostname and extractDates column
|
||||
if (columnIndex < 2) {
|
||||
super.setValueAt(aValue, rowIndex, columnIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,88 +0,0 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2017 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.communications;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import org.openide.nodes.AbstractNode;
|
||||
import org.openide.nodes.ChildFactory;
|
||||
import org.openide.nodes.Children;
|
||||
import org.openide.nodes.Node;
|
||||
import org.python.google.common.collect.Iterables;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.datamodel.AccountDeviceInstance;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.CommunicationsFilter;
|
||||
import org.sleuthkit.datamodel.CommunicationsManager;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* 'Root' Node for the Account/Messages area. Has children which are all the
|
||||
* relationships of all the accounts in this node.
|
||||
*
|
||||
*/
|
||||
final class AccountDetailsNode extends AbstractNode {
|
||||
|
||||
private final static Logger logger = Logger.getLogger(AccountDetailsNode.class.getName());
|
||||
|
||||
AccountDetailsNode(Set<AccountDeviceInstance> accountDeviceInstances, CommunicationsFilter filter, CommunicationsManager commsManager) {
|
||||
super(Children.create(new AccountRelationshipChildren(accountDeviceInstances, commsManager, filter), true));
|
||||
String displayName = (accountDeviceInstances.size() == 1)
|
||||
? Iterables.getOnlyElement(accountDeviceInstances).getAccount().getTypeSpecificID()
|
||||
: accountDeviceInstances.size() + " accounts";
|
||||
setDisplayName(displayName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Children object for the relationships that the accounts are part of.
|
||||
*/
|
||||
private static class AccountRelationshipChildren extends ChildFactory<Content> {
|
||||
|
||||
private final Set<AccountDeviceInstance> accountDeviceInstances;
|
||||
private final CommunicationsManager commsManager;
|
||||
private final CommunicationsFilter filter;
|
||||
|
||||
private AccountRelationshipChildren(Set<AccountDeviceInstance> accountDeviceInstances, CommunicationsManager commsManager, CommunicationsFilter filter) {
|
||||
this.accountDeviceInstances = accountDeviceInstances;
|
||||
this.commsManager = commsManager;
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean createKeys(List<Content> list) {
|
||||
try {
|
||||
list.addAll(commsManager.getRelationshipSources(accountDeviceInstances, filter));
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Error getting communications", ex);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node createNodeForKey(Content t) {
|
||||
if (t instanceof BlackboardArtifact) {
|
||||
return new RelationshipNode((BlackboardArtifact) t);
|
||||
} else {
|
||||
throw new UnsupportedOperationException("Cannot create a RelationshipNode for non BlackboardArtifact content.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -20,6 +20,8 @@ package org.sleuthkit.autopsy.communications;
|
||||
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import java.awt.Component;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.ListSelectionModel;
|
||||
@ -31,11 +33,16 @@ import org.openide.explorer.ExplorerManager;
|
||||
import org.openide.explorer.ExplorerUtils;
|
||||
import org.openide.nodes.AbstractNode;
|
||||
import org.openide.nodes.Children;
|
||||
import org.openide.nodes.Node;
|
||||
import org.openide.util.Lookup;
|
||||
import org.openide.util.lookup.ProxyLookup;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.communications.relationships.RelationshipBrowser;
|
||||
import org.sleuthkit.autopsy.communications.relationships.SelectionInfo;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.datamodel.AccountDeviceInstance;
|
||||
import org.sleuthkit.datamodel.CommunicationsFilter;
|
||||
import org.sleuthkit.datamodel.CommunicationsManager;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
@ -56,8 +63,9 @@ public final class AccountsBrowser extends JPanel implements ExplorerManager.Pro
|
||||
|
||||
private final Outline outline;
|
||||
|
||||
private final ExplorerManager messageBrowserEM = new ExplorerManager();
|
||||
private final ExplorerManager accountsTableEM = new ExplorerManager();
|
||||
|
||||
final RelationshipBrowser relationshipBrowser;
|
||||
|
||||
/*
|
||||
* This lookup proxies the selection lookup of both he accounts table and
|
||||
@ -78,21 +86,30 @@ public final class AccountsBrowser extends JPanel implements ExplorerManager.Pro
|
||||
((DefaultOutlineModel) outline.getOutlineModel()).setNodesColumnLabel(Bundle.AccountNode_accountName());
|
||||
outline.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
||||
outline.setColumnSorted(3, false, 1); //it would be nice if the column index wasn't hardcoded
|
||||
|
||||
relationshipBrowser = new RelationshipBrowser();
|
||||
jSplitPane1.setRightComponent(relationshipBrowser);
|
||||
|
||||
accountsTableEM.addPropertyChangeListener(evt -> {
|
||||
if (ExplorerManager.PROP_ROOT_CONTEXT.equals(evt.getPropertyName())) {
|
||||
SwingUtilities.invokeLater(this::setColumnWidths);
|
||||
} else if (ExplorerManager.PROP_EXPLORED_CONTEXT.equals(evt.getPropertyName())) {
|
||||
SwingUtilities.invokeLater(this::setColumnWidths);
|
||||
} else if (evt.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) {
|
||||
final Node[] selectedNodes = accountsTableEM.getSelectedNodes();
|
||||
final Set<AccountDeviceInstance> accountDeviceInstances = new HashSet<>();
|
||||
|
||||
CommunicationsFilter filter = null;
|
||||
for (final Node node : selectedNodes) {
|
||||
accountDeviceInstances.add(((AccountDeviceInstanceNode) node).getAccountDeviceInstance());
|
||||
filter = ((AccountDeviceInstanceNode)node).getFilter();
|
||||
}
|
||||
relationshipBrowser.setSelectionInfo(new SelectionInfo(accountDeviceInstances, filter));
|
||||
}
|
||||
});
|
||||
final MessageBrowser messageBrowser = new MessageBrowser(accountsTableEM, messageBrowserEM);
|
||||
|
||||
jSplitPane1.setRightComponent(messageBrowser);
|
||||
|
||||
proxyLookup = new ProxyLookup(
|
||||
messageBrowser.getLookup(),
|
||||
ExplorerUtils.createLookup(accountsTableEM, getActionMap()));
|
||||
|
||||
proxyLookup = new ProxyLookup(relationshipBrowser.getLookup(),
|
||||
ExplorerUtils.createLookup(accountsTableEM, getActionMap()));
|
||||
}
|
||||
|
||||
private void setColumnWidths() {
|
||||
|
@ -32,21 +32,16 @@ VisualizationPanel.zoomInButton.toolTipText=Zoom in
|
||||
VisualizationPanel.zoomInButton.text=
|
||||
VisualizationPanel.zoomOutButton.toolTipText=Zoom out
|
||||
VisualizationPanel.zoomOutButton.text=
|
||||
<<<<<<< HEAD
|
||||
VisualizationPanel.fastOrganicLayoutButton.text=Redraw
|
||||
VisualizationPanel.clearVizButton.text_1=Clear
|
||||
VisualizationPanel.fastOrganicLayoutButton.text=
|
||||
VisualizationPanel.backButton.text_1=
|
||||
VisualizationPanel.forwardButton.text=
|
||||
=======
|
||||
VisualizationPanel.circleLayoutButton.text=Circle
|
||||
VisualizationPanel.organicLayoutButton.text=Organic
|
||||
VisualizationPanel.fastOrganicLayoutButton.text=
|
||||
VisualizationPanel.hierarchyLayoutButton.text=Hierarchical
|
||||
VisualizationPanel.clearVizButton.text_1=
|
||||
VisualizationPanel.snapshotButton.text_1=Snapshot Report
|
||||
>>>>>>> develop
|
||||
VisualizationPanel.clearVizButton.actionCommand=
|
||||
VisualizationPanel.backButton.toolTipText=Click to go back
|
||||
VisualizationPanel.forwardButton.toolTipText=Click to go forward
|
||||
VisualizationPanel.fastOrganicLayoutButton.toolTipText=Click to redraw the chart
|
||||
VisualizationPanel.clearVizButton.toolTipText=Click to clear the chart
|
||||
VisualizationPanel.forwardButton.text=
|
||||
|
@ -22,7 +22,6 @@ FiltersPanel.refreshButton.text=Refresh
|
||||
FiltersPanel.deviceRequiredLabel.text=Select at least one.
|
||||
FiltersPanel.accountTypeRequiredLabel.text=Select at least one.
|
||||
FiltersPanel.needsRefreshLabel.text=Displayed data is out of date. Press Refresh.
|
||||
MessageBrowser.DataResultViewerTable.title=Messages
|
||||
OpenCVTAction.displayName=Communications
|
||||
PinAccountsAction.pluralText=Add Selected Accounts to Visualization
|
||||
PinAccountsAction.singularText=Add Selected Account to Visualization
|
||||
@ -76,24 +75,19 @@ VisualizationPanel.zoomInButton.toolTipText=Zoom in
|
||||
VisualizationPanel.zoomInButton.text=
|
||||
VisualizationPanel.zoomOutButton.toolTipText=Zoom out
|
||||
VisualizationPanel.zoomOutButton.text=
|
||||
<<<<<<< HEAD
|
||||
VisualizationPanel.fastOrganicLayoutButton.text=Redraw
|
||||
VisualizationPanel.clearVizButton.text_1=Clear
|
||||
VisualizationPanel.fastOrganicLayoutButton.text=
|
||||
VisualizationPanel.backButton.text_1=
|
||||
VisualizationPanel.forwardButton.text=
|
||||
=======
|
||||
VisualizationPanel.circleLayoutButton.text=Circle
|
||||
VisualizationPanel.organicLayoutButton.text=Organic
|
||||
VisualizationPanel.fastOrganicLayoutButton.text=
|
||||
VisualizationPanel.hierarchyLayoutButton.text=Hierarchical
|
||||
VisualizationPanel.clearVizButton.text_1=
|
||||
VisualizationPanel.snapshotButton.text_1=Snapshot Report
|
||||
>>>>>>> develop
|
||||
VisualizationPanel.clearVizButton.actionCommand=
|
||||
VisualizationPanel.backButton.toolTipText=Click to go back
|
||||
VisualizationPanel.forwardButton.toolTipText=Click to go forward
|
||||
VisualizationPanel.fastOrganicLayoutButton.toolTipText=Click to redraw the chart
|
||||
VisualizationPanel.clearVizButton.toolTipText=Click to clear the chart
|
||||
VisualizationPanel.forwardButton.text=
|
||||
VisualizationPanel_action_dialogs_title=Communications
|
||||
VisualizationPanel_action_name_text=Snapshot Report
|
||||
VisualizationPanel_module_name=Communications
|
||||
|
@ -19,6 +19,7 @@
|
||||
package org.sleuthkit.autopsy.communications;
|
||||
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Font;
|
||||
import java.util.List;
|
||||
@ -61,8 +62,11 @@ public final class CVTTopComponent extends TopComponent {
|
||||
associateLookup(proxyLookup);
|
||||
// Make sure the Global Actions Context is proxying the selection of the active tab.
|
||||
browseVisualizeTabPane.addChangeListener(changeEvent -> {
|
||||
Lookup.Provider selectedComponent = (Lookup.Provider) browseVisualizeTabPane.getSelectedComponent();
|
||||
proxyLookup.setNewLookups(selectedComponent.getLookup());
|
||||
Component selectedComponent = browseVisualizeTabPane.getSelectedComponent();
|
||||
if(selectedComponent instanceof Lookup.Provider) {
|
||||
Lookup lookup = ((Lookup.Provider)selectedComponent).getLookup();
|
||||
proxyLookup.setNewLookups(lookup);
|
||||
}
|
||||
filtersPane.setDeviceAccountTypeEnabled(browseVisualizeTabPane.getSelectedIndex() != 0);
|
||||
});
|
||||
|
||||
|
@ -52,6 +52,7 @@ import org.sleuthkit.datamodel.CommunicationsFilter.DateRangeFilter;
|
||||
import org.sleuthkit.datamodel.CommunicationsFilter.DeviceFilter;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
import static org.sleuthkit.datamodel.Relationship.Type.CALL_LOG;
|
||||
import static org.sleuthkit.datamodel.Relationship.Type.CONTACT;
|
||||
import static org.sleuthkit.datamodel.Relationship.Type.MESSAGE;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
@ -598,7 +599,7 @@ final public class FiltersPanel extends JPanel {
|
||||
commsFilter.addAndFilter(getAccountTypeFilter());
|
||||
commsFilter.addAndFilter(getDateRangeFilter());
|
||||
commsFilter.addAndFilter(new CommunicationsFilter.RelationshipTypeFilter(
|
||||
ImmutableSet.of(CALL_LOG, MESSAGE)));
|
||||
ImmutableSet.of(CALL_LOG, MESSAGE, CONTACT)));
|
||||
return commsFilter;
|
||||
}
|
||||
|
||||
|
@ -1,231 +0,0 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2017-2018 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 obt ain 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.communications;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.KeyboardFocusManager;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import javax.swing.JPanel;
|
||||
import static javax.swing.SwingUtilities.isDescendingFrom;
|
||||
import org.openide.explorer.ExplorerManager;
|
||||
import static org.openide.explorer.ExplorerUtils.createLookup;
|
||||
import org.openide.nodes.Node;
|
||||
import org.openide.util.Lookup;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.corecomponents.DataResultPanel;
|
||||
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
|
||||
import org.sleuthkit.autopsy.corecomponents.TableFilterNode;
|
||||
import org.sleuthkit.autopsy.directorytree.DataResultFilterNode;
|
||||
|
||||
/**
|
||||
* The right hand side of the CVT. Has a DataResultPanel to show a listing of
|
||||
* messages and other account details, and a ContentViewer to show individual
|
||||
* messages.
|
||||
*/
|
||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||
public final class MessageBrowser extends JPanel implements ExplorerManager.Provider, Lookup.Provider {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final ExplorerManager tableEM;
|
||||
private final ExplorerManager gacExplorerManager;
|
||||
private final DataResultPanel messagesResultPanel;
|
||||
/* lookup that will be exposed through the (Global Actions Context) */
|
||||
private final ModifiableProxyLookup proxyLookup = new ModifiableProxyLookup();
|
||||
|
||||
private final PropertyChangeListener focusPropertyListener = new PropertyChangeListener() {
|
||||
/**
|
||||
* Listener that keeps the proxyLookup in sync with the focused area of
|
||||
* the UI.
|
||||
*
|
||||
* Since the embedded MessageContentViewer (attachments panel) is not in
|
||||
* its own TopComponenet, its selection does not get proxied into the
|
||||
* Global Actions Context (GAC), and many of the available actions don't
|
||||
* work on it. Further, we can't put the selection from both the
|
||||
* Messages table and the Attachments table in the GAC because they
|
||||
* could both include AbstractFiles, muddling the selection seen by the
|
||||
* actions. Instead, depending on where the focus is in the window, we
|
||||
* want to put different Content in the Global Actions Context to be
|
||||
* picked up by, e.g., the tagging actions. The best way I could figure
|
||||
* to do this was to listen to all focus events and swap out what is in
|
||||
* the lookup appropriately. An alternative to this would be to
|
||||
* investigate using the ContextAwareAction interface.
|
||||
*
|
||||
* @see org.sleuthkit.autopsy.timeline.TimeLineTopComponent for a
|
||||
* similar situation and a similar solution.
|
||||
*
|
||||
* @param focusEvent The focus change event.
|
||||
*/
|
||||
@Override
|
||||
public void propertyChange(final PropertyChangeEvent focusEvent) {
|
||||
if (focusEvent.getPropertyName().equalsIgnoreCase("focusOwner")) {
|
||||
final Component newFocusOwner = (Component) focusEvent.getNewValue();
|
||||
|
||||
if (newFocusOwner == null) {
|
||||
return;
|
||||
}
|
||||
if (isDescendingFrom(newFocusOwner, messageDataContent)) {
|
||||
//if the focus owner is within the MessageContentViewer ( the attachments table)
|
||||
proxyLookup.setNewLookups(createLookup(messageDataContent.getExplorerManager(), getActionMap()));
|
||||
} else if (isDescendingFrom(newFocusOwner, messagesResultPanel)) {
|
||||
//... or if it is within the Messages table.
|
||||
proxyLookup.setNewLookups(createLookup(gacExplorerManager, getActionMap()));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructs the right hand side of the Communications Visualization Tool
|
||||
* (CVT).
|
||||
*
|
||||
* @param tableEM An explorer manager to listen to as the driver
|
||||
* of the Message Table.
|
||||
* @param gacExplorerManager An explorer manager associated with the
|
||||
* GlobalActionsContext (GAC) so that selections
|
||||
* in the messages browser can be exposed to
|
||||
* context-sensitive actions.
|
||||
*/
|
||||
@NbBundle.Messages({"MessageBrowser.DataResultViewerTable.title=Messages"})
|
||||
MessageBrowser(final ExplorerManager tableEM, final ExplorerManager gacExplorerManager) {
|
||||
this.tableEM = tableEM;
|
||||
this.gacExplorerManager = gacExplorerManager;
|
||||
initComponents();
|
||||
//create an uninitialized DataResultPanel so we can control the ResultViewers that get added.
|
||||
messagesResultPanel = DataResultPanel.createInstanceUninitialized("Account", "", Node.EMPTY, 0, messageDataContent);
|
||||
splitPane.setTopComponent(messagesResultPanel);
|
||||
splitPane.setBottomComponent(messageDataContent);
|
||||
messagesResultPanel.addResultViewer(new DataResultViewerTable(gacExplorerManager,
|
||||
Bundle.MessageBrowser_DataResultViewerTable_title()));
|
||||
messagesResultPanel.open();
|
||||
|
||||
this.tableEM.addPropertyChangeListener(new PropertyChangeListener() {
|
||||
/**
|
||||
* Listener that pushes selections in the tableEM (the Accounts
|
||||
* table) into the Messages table.
|
||||
*
|
||||
* @param pce The ExplorerManager event.
|
||||
*/
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent pce) {
|
||||
if (pce.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) {
|
||||
final Node[] selectedNodes = MessageBrowser.this.tableEM.getSelectedNodes();
|
||||
messagesResultPanel.setNumberOfChildNodes(0);
|
||||
messagesResultPanel.setNode(null);
|
||||
messagesResultPanel.setPath("");
|
||||
if (selectedNodes.length > 0) {
|
||||
Node rootNode;
|
||||
final Node selectedNode = selectedNodes[0];
|
||||
|
||||
if (selectedNode instanceof AccountDeviceInstanceNode) {
|
||||
rootNode = makeRootNodeFromAccountDeviceInstanceNodes(selectedNodes);
|
||||
} else {
|
||||
rootNode = selectedNode;
|
||||
}
|
||||
messagesResultPanel.setPath(rootNode.getDisplayName());
|
||||
messagesResultPanel.setNode(new TableFilterNode(new DataResultFilterNode(rootNode, gacExplorerManager), true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Node makeRootNodeFromAccountDeviceInstanceNodes(final Node[] selectedNodes) {
|
||||
//Use lookup here?
|
||||
final AccountDeviceInstanceNode adiNode = (AccountDeviceInstanceNode) selectedNodes[0];
|
||||
|
||||
final Set<AccountDeviceInstanceKey> accountDeviceInstances = new HashSet<>();
|
||||
for (final Node n : selectedNodes) {
|
||||
//Use lookup here?
|
||||
accountDeviceInstances.add(((AccountDeviceInstanceNode) n).getAccountDeviceInstanceKey());
|
||||
}
|
||||
return SelectionNode.createFromAccounts(accountDeviceInstances, adiNode.getFilter(), adiNode.getCommsManager());
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExplorerManager getExplorerManager() {
|
||||
return gacExplorerManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Lookup getLookup() {
|
||||
return proxyLookup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addNotify() {
|
||||
super.addNotify();
|
||||
//add listener that maintains correct selection in the Global Actions Context
|
||||
KeyboardFocusManager.getCurrentKeyboardFocusManager()
|
||||
.addPropertyChangeListener("focusOwner", focusPropertyListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeNotify() {
|
||||
super.removeNotify();
|
||||
KeyboardFocusManager.getCurrentKeyboardFocusManager()
|
||||
.removePropertyChangeListener("focusOwner", focusPropertyListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
|
||||
splitPane = new javax.swing.JSplitPane();
|
||||
messageDataContent = new org.sleuthkit.autopsy.communications.MessageDataContent();
|
||||
|
||||
splitPane.setDividerLocation(400);
|
||||
splitPane.setDividerSize(10);
|
||||
splitPane.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);
|
||||
splitPane.setResizeWeight(0.5);
|
||||
splitPane.setBottomComponent(messageDataContent);
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
|
||||
.addGap(0, 0, 0)
|
||||
.addComponent(splitPane))
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGap(0, 0, 0)
|
||||
.addComponent(splitPane, javax.swing.GroupLayout.DEFAULT_SIZE, 1083, Short.MAX_VALUE)
|
||||
.addGap(0, 0, 0))
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private org.sleuthkit.autopsy.communications.MessageDataContent messageDataContent;
|
||||
private javax.swing.JSplitPane splitPane;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
}
|
@ -26,9 +26,9 @@ import org.openide.util.lookup.ProxyLookup;
|
||||
* delegated to.
|
||||
*
|
||||
*/
|
||||
final class ModifiableProxyLookup extends ProxyLookup {
|
||||
final public class ModifiableProxyLookup extends ProxyLookup {
|
||||
|
||||
ModifiableProxyLookup(final Lookup... lookups) {
|
||||
public ModifiableProxyLookup(final Lookup... lookups) {
|
||||
super(lookups);
|
||||
}
|
||||
|
||||
|
@ -1,35 +0,0 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.communications;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import org.openide.nodes.ChildFactory;
|
||||
import org.openide.nodes.Node;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class RelaionshipSetNodeFactory extends ChildFactory<BlackboardArtifact> {
|
||||
|
||||
private final Collection<BlackboardArtifact> artifacts;
|
||||
|
||||
public RelaionshipSetNodeFactory(Collection<BlackboardArtifact> artifacts) {
|
||||
this.artifacts = artifacts;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean createKeys(List<BlackboardArtifact> list) {
|
||||
list.addAll(artifacts);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node createNodeForKey(BlackboardArtifact key) {
|
||||
return new RelationshipNode(key);
|
||||
}
|
||||
}
|
@ -1,140 +0,0 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2018 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.communications;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Sets;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import org.openide.nodes.AbstractNode;
|
||||
import org.openide.nodes.ChildFactory;
|
||||
import org.openide.nodes.Children;
|
||||
import org.openide.nodes.Node;
|
||||
import org.openide.util.Lookup;
|
||||
import org.openide.util.lookup.Lookups;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.datamodel.AccountDeviceInstance;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.CommunicationsFilter;
|
||||
import org.sleuthkit.datamodel.CommunicationsManager;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* 'Root' Node for the Account/Messages area. Represents all the relationships
|
||||
* that are selected in the AccountsBrowser or the VisualizationPanel. Can be
|
||||
* populated with AccountDeviceInstance and/or directly with relationships
|
||||
* (Content).
|
||||
*/
|
||||
final class SelectionNode extends AbstractNode {
|
||||
|
||||
private SelectionNode(Children children, Lookup lookup) {
|
||||
super(children, lookup);
|
||||
}
|
||||
|
||||
static SelectionNode createFromAccountsAndRelationships(
|
||||
Set<Content> edgeRelationshipArtifacts,
|
||||
Set<AccountDeviceInstanceKey> accountDeviceInstanceKeys,
|
||||
CommunicationsFilter filter,
|
||||
CommunicationsManager commsManager) {
|
||||
|
||||
Set<AccountDeviceInstance> accountDeviceInstances = accountDeviceInstanceKeys.stream()
|
||||
.map(AccountDeviceInstanceKey::getAccountDeviceInstance)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
SelectionNode node = new SelectionNode(Children.create(
|
||||
new RelationshipChildren(
|
||||
edgeRelationshipArtifacts,
|
||||
accountDeviceInstances,
|
||||
commsManager,
|
||||
filter),
|
||||
true), Lookups.fixed(accountDeviceInstanceKeys.toArray()));
|
||||
|
||||
//This is not good for internationalization!!!
|
||||
String name = "";
|
||||
final int accounts = accountDeviceInstances.size();
|
||||
if (accounts > 1) {
|
||||
name = accounts + " accounts";
|
||||
} else if (accounts == 1) {
|
||||
name = Iterables.getOnlyElement(accountDeviceInstances).getAccount().getTypeSpecificID();
|
||||
}
|
||||
|
||||
final int edges = edgeRelationshipArtifacts.size();
|
||||
|
||||
if (edges > 0) {
|
||||
name = name + (name.isEmpty() ? "" : " and ") + edges + " relationship" + (edges > 1 ? "s" : "");
|
||||
}
|
||||
|
||||
node.setDisplayName(name);
|
||||
return node;
|
||||
}
|
||||
|
||||
static SelectionNode createFromAccounts(
|
||||
Set<AccountDeviceInstanceKey> accountDeviceInstances,
|
||||
CommunicationsFilter filter,
|
||||
CommunicationsManager commsManager) {
|
||||
|
||||
return createFromAccountsAndRelationships(Collections.emptySet(), accountDeviceInstances, filter, commsManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Children object for the relationships that the accounts are part of.
|
||||
*/
|
||||
private static class RelationshipChildren extends ChildFactory<Content> {
|
||||
|
||||
static final private Logger logger = Logger.getLogger(RelationshipChildren.class.getName());
|
||||
|
||||
private final Set<Content> edgeRelationshipArtifacts;
|
||||
|
||||
private final Set<AccountDeviceInstance> accountDeviceInstances;
|
||||
|
||||
private final CommunicationsManager commsManager;
|
||||
private final CommunicationsFilter filter;
|
||||
|
||||
private RelationshipChildren(Set<Content> selectedEdgeRelationshipSources, Set<AccountDeviceInstance> selecedAccountDeviceInstances, CommunicationsManager commsManager, CommunicationsFilter filter) {
|
||||
this.edgeRelationshipArtifacts = selectedEdgeRelationshipSources;
|
||||
this.accountDeviceInstances = selecedAccountDeviceInstances;
|
||||
this.commsManager = commsManager;
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean createKeys(List<Content> list) {
|
||||
try {
|
||||
final Set<Content> relationshipSources = commsManager.getRelationshipSources(accountDeviceInstances, filter);
|
||||
list.addAll(Sets.union(relationshipSources, edgeRelationshipArtifacts));
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Error getting communications", ex);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node createNodeForKey(Content content) {
|
||||
if (content instanceof BlackboardArtifact) {
|
||||
return new RelationshipNode((BlackboardArtifact) content);
|
||||
} else {
|
||||
throw new UnsupportedOperationException("Cannot create a RelationshipNode for non BlackboardArtifact content.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -28,12 +28,12 @@ import org.sleuthkit.datamodel.Account;
|
||||
/**
|
||||
* Utility class with helpers for dealing with accounts.
|
||||
*/
|
||||
class Utils {
|
||||
public final class Utils {
|
||||
|
||||
private Utils() {
|
||||
}
|
||||
|
||||
static ZoneId getUserPreferredZoneId() {
|
||||
static public ZoneId getUserPreferredZoneId() {
|
||||
ZoneId zone = UserPreferences.displayTimesInLocalTime() ?
|
||||
ZoneOffset.systemDefault() : TimeZone.getTimeZone(UserPreferences.getTimeZoneForDisplays()).toZoneId();
|
||||
return zone;
|
||||
@ -44,7 +44,7 @@ class Utils {
|
||||
*
|
||||
* @return The path of the icon for the given Account Type.
|
||||
*/
|
||||
static final String getIconFilePath(Account.Type type) {
|
||||
static public final String getIconFilePath(Account.Type type) {
|
||||
return Accounts.getIconFilePath(type);
|
||||
}
|
||||
|
||||
|
@ -54,7 +54,6 @@ import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseWheelEvent;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyVetoException;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
@ -62,11 +61,11 @@ import java.nio.file.Paths;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
@ -98,23 +97,20 @@ import org.apache.commons.lang3.StringUtils;
|
||||
import org.controlsfx.control.Notifications;
|
||||
import org.jdesktop.layout.GroupLayout;
|
||||
import org.jdesktop.layout.LayoutStyle;
|
||||
import org.openide.explorer.ExplorerManager;
|
||||
import org.openide.explorer.ExplorerUtils;
|
||||
import org.openide.nodes.Node;
|
||||
import org.openide.util.Lookup;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.lookup.ProxyLookup;
|
||||
import org.openide.windows.WindowManager;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.communications.relationships.RelationshipBrowser;
|
||||
import org.sleuthkit.autopsy.communications.relationships.SelectionInfo;
|
||||
import org.sleuthkit.autopsy.communications.snapshot.CommSnapShotReportWriter;
|
||||
import org.sleuthkit.autopsy.coreutils.FileUtil;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
||||
import org.sleuthkit.autopsy.progress.ModalDialogProgressIndicator;
|
||||
import org.sleuthkit.datamodel.AccountDeviceInstance;
|
||||
import org.sleuthkit.datamodel.CommunicationsFilter;
|
||||
import org.sleuthkit.datamodel.CommunicationsManager;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
/**
|
||||
* A panel that goes in the Visualize tab of the Communications Visualization
|
||||
@ -127,7 +123,7 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
* actions to work correctly.
|
||||
*/
|
||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||
final public class VisualizationPanel extends JPanel implements Lookup.Provider {
|
||||
final public class VisualizationPanel extends JPanel {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Logger logger = Logger.getLogger(VisualizationPanel.class.getName());
|
||||
@ -140,9 +136,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
||||
@NbBundle.Messages("VisualizationPanel.cancelButton.text=Cancel")
|
||||
private static final String CANCEL = Bundle.VisualizationPanel_cancelButton_text();
|
||||
|
||||
private final ExplorerManager vizEM = new ExplorerManager();
|
||||
private final ExplorerManager gacEM = new ExplorerManager();
|
||||
private final ProxyLookup proxyLookup;
|
||||
private Frame windowAncestor;
|
||||
|
||||
private CommunicationsManager commsManager;
|
||||
@ -161,6 +154,8 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
||||
|
||||
private final Map<NamedGraphLayout, JButton> layoutButtons = new HashMap<>();
|
||||
private NamedGraphLayout currentLayout;
|
||||
|
||||
private final RelationshipBrowser relationshipBrowser;
|
||||
|
||||
private final StateManager stateManager;
|
||||
|
||||
@ -225,13 +220,9 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
||||
final GraphMouseListener graphMouseListener = new GraphMouseListener();
|
||||
graphComponent.getGraphControl().addMouseWheelListener(graphMouseListener);
|
||||
graphComponent.getGraphControl().addMouseListener(graphMouseListener);
|
||||
|
||||
final MessageBrowser messageBrowser = new MessageBrowser(vizEM, gacEM);
|
||||
splitPane.setRightComponent(messageBrowser);
|
||||
proxyLookup = new ProxyLookup(
|
||||
ExplorerUtils.createLookup(vizEM, getActionMap()),
|
||||
messageBrowser.getLookup()
|
||||
);
|
||||
|
||||
relationshipBrowser = new RelationshipBrowser();
|
||||
splitPane.setRightComponent(relationshipBrowser);
|
||||
|
||||
//feed selection to explorermanager
|
||||
graph.getSelectionModel().addListener(mxEvent.CHANGE, new SelectionListener());
|
||||
@ -257,12 +248,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
||||
|
||||
setStateButtonsEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Lookup getLookup() {
|
||||
return proxyLookup;
|
||||
}
|
||||
|
||||
|
||||
@Subscribe
|
||||
void handle(LockedVertexModel.VertexLockEvent event) {
|
||||
final Set<mxCell> vertices = event.getVertices();
|
||||
@ -387,219 +373,223 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
splitPane = new JSplitPane();
|
||||
borderLayoutPanel = new JPanel();
|
||||
placeHolderPanel = new JPanel();
|
||||
jTextArea1 = new JTextArea();
|
||||
toolbar = new JPanel();
|
||||
fastOrganicLayoutButton = new JButton();
|
||||
zoomOutButton = new JButton();
|
||||
zoomInButton = new JButton();
|
||||
zoomActualButton = new JButton();
|
||||
fitZoomButton = new JButton();
|
||||
jLabel2 = new JLabel();
|
||||
zoomLabel = new JLabel();
|
||||
clearVizButton = new JButton();
|
||||
jSeparator2 = new JToolBar.Separator();
|
||||
backButton = new JButton();
|
||||
forwardButton = new JButton();
|
||||
snapshotButton = new JButton();
|
||||
jSeparator3 = new JToolBar.Separator();
|
||||
jSeparator4 = new JToolBar.Separator();
|
||||
notificationsJFXPanel = new JFXPanel();
|
||||
splitPane = new javax.swing.JSplitPane();
|
||||
borderLayoutPanel = new javax.swing.JPanel();
|
||||
placeHolderPanel = new javax.swing.JPanel();
|
||||
jTextArea1 = new javax.swing.JTextArea();
|
||||
toolbar = new javax.swing.JPanel();
|
||||
fastOrganicLayoutButton = new javax.swing.JButton();
|
||||
zoomOutButton = new javax.swing.JButton();
|
||||
zoomInButton = new javax.swing.JButton();
|
||||
zoomActualButton = new javax.swing.JButton();
|
||||
fitZoomButton = new javax.swing.JButton();
|
||||
jLabel2 = new javax.swing.JLabel();
|
||||
zoomLabel = new javax.swing.JLabel();
|
||||
clearVizButton = new javax.swing.JButton();
|
||||
jSeparator2 = new javax.swing.JToolBar.Separator();
|
||||
backButton = new javax.swing.JButton();
|
||||
forwardButton = new javax.swing.JButton();
|
||||
snapshotButton = new javax.swing.JButton();
|
||||
jSeparator3 = new javax.swing.JToolBar.Separator();
|
||||
jSeparator4 = new javax.swing.JToolBar.Separator();
|
||||
notificationsJFXPanel = new javafx.embed.swing.JFXPanel();
|
||||
|
||||
setLayout(new BorderLayout());
|
||||
setLayout(new java.awt.BorderLayout());
|
||||
|
||||
splitPane.setDividerLocation(800);
|
||||
splitPane.setResizeWeight(0.5);
|
||||
|
||||
borderLayoutPanel.setLayout(new BorderLayout());
|
||||
borderLayoutPanel.setLayout(new java.awt.BorderLayout());
|
||||
|
||||
jTextArea1.setBackground(new Color(240, 240, 240));
|
||||
jTextArea1.setBackground(new java.awt.Color(240, 240, 240));
|
||||
jTextArea1.setColumns(20);
|
||||
jTextArea1.setLineWrap(true);
|
||||
jTextArea1.setRows(5);
|
||||
jTextArea1.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jTextArea1.text")); // NOI18N
|
||||
jTextArea1.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jTextArea1.text")); // NOI18N
|
||||
|
||||
GroupLayout placeHolderPanelLayout = new GroupLayout(placeHolderPanel);
|
||||
org.jdesktop.layout.GroupLayout placeHolderPanelLayout = new org.jdesktop.layout.GroupLayout(placeHolderPanel);
|
||||
placeHolderPanel.setLayout(placeHolderPanelLayout);
|
||||
placeHolderPanelLayout.setHorizontalGroup(placeHolderPanelLayout.createParallelGroup(GroupLayout.LEADING)
|
||||
placeHolderPanelLayout.setHorizontalGroup(
|
||||
placeHolderPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
|
||||
.add(placeHolderPanelLayout.createSequentialGroup()
|
||||
.addContainerGap(250, Short.MAX_VALUE)
|
||||
.add(jTextArea1, GroupLayout.PREFERRED_SIZE, 424, GroupLayout.PREFERRED_SIZE)
|
||||
.add(jTextArea1, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 424, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
|
||||
.addContainerGap(423, Short.MAX_VALUE))
|
||||
);
|
||||
placeHolderPanelLayout.setVerticalGroup(placeHolderPanelLayout.createParallelGroup(GroupLayout.LEADING)
|
||||
placeHolderPanelLayout.setVerticalGroup(
|
||||
placeHolderPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
|
||||
.add(placeHolderPanelLayout.createSequentialGroup()
|
||||
.addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.add(jTextArea1, GroupLayout.PREFERRED_SIZE, 47, GroupLayout.PREFERRED_SIZE)
|
||||
.addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
.addContainerGap(org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.add(jTextArea1, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 47, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
|
||||
.addContainerGap(org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
);
|
||||
|
||||
borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER);
|
||||
borderLayoutPanel.add(placeHolderPanel, java.awt.BorderLayout.CENTER);
|
||||
|
||||
fastOrganicLayoutButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/arrow-circle-double-135.png"))); // NOI18N
|
||||
fastOrganicLayoutButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.fastOrganicLayoutButton.text")); // NOI18N
|
||||
fastOrganicLayoutButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.fastOrganicLayoutButton.toolTipText")); // NOI18N
|
||||
fastOrganicLayoutButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/arrow-circle-double-135.png"))); // NOI18N
|
||||
fastOrganicLayoutButton.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.fastOrganicLayoutButton.text")); // NOI18N
|
||||
fastOrganicLayoutButton.setToolTipText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.fastOrganicLayoutButton.toolTipText")); // NOI18N
|
||||
fastOrganicLayoutButton.setFocusable(false);
|
||||
fastOrganicLayoutButton.setVerticalTextPosition(SwingConstants.BOTTOM);
|
||||
fastOrganicLayoutButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
|
||||
|
||||
zoomOutButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-out-red.png"))); // NOI18N
|
||||
zoomOutButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomOutButton.text")); // NOI18N
|
||||
zoomOutButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomOutButton.toolTipText")); // NOI18N
|
||||
zoomOutButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-out-red.png"))); // NOI18N
|
||||
zoomOutButton.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomOutButton.text")); // NOI18N
|
||||
zoomOutButton.setToolTipText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomOutButton.toolTipText")); // NOI18N
|
||||
zoomOutButton.setFocusable(false);
|
||||
zoomOutButton.setHorizontalTextPosition(SwingConstants.CENTER);
|
||||
zoomOutButton.setVerticalTextPosition(SwingConstants.BOTTOM);
|
||||
zoomOutButton.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
zoomOutButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
|
||||
zoomOutButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
|
||||
zoomOutButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
zoomOutButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
zoomInButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-in-green.png"))); // NOI18N
|
||||
zoomInButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomInButton.text")); // NOI18N
|
||||
zoomInButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomInButton.toolTipText")); // NOI18N
|
||||
zoomInButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-in-green.png"))); // NOI18N
|
||||
zoomInButton.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomInButton.text")); // NOI18N
|
||||
zoomInButton.setToolTipText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomInButton.toolTipText")); // NOI18N
|
||||
zoomInButton.setFocusable(false);
|
||||
zoomInButton.setHorizontalTextPosition(SwingConstants.CENTER);
|
||||
zoomInButton.setVerticalTextPosition(SwingConstants.BOTTOM);
|
||||
zoomInButton.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
zoomInButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
|
||||
zoomInButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
|
||||
zoomInButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
zoomInButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
zoomActualButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-actual.png"))); // NOI18N
|
||||
zoomActualButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomActualButton.text")); // NOI18N
|
||||
zoomActualButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomActualButton.toolTipText")); // NOI18N
|
||||
zoomActualButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-actual.png"))); // NOI18N
|
||||
zoomActualButton.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomActualButton.text")); // NOI18N
|
||||
zoomActualButton.setToolTipText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomActualButton.toolTipText")); // NOI18N
|
||||
zoomActualButton.setFocusable(false);
|
||||
zoomActualButton.setHorizontalTextPosition(SwingConstants.CENTER);
|
||||
zoomActualButton.setVerticalTextPosition(SwingConstants.BOTTOM);
|
||||
zoomActualButton.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
zoomActualButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
|
||||
zoomActualButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
|
||||
zoomActualButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
zoomActualButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
fitZoomButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-fit.png"))); // NOI18N
|
||||
fitZoomButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.fitZoomButton.text")); // NOI18N
|
||||
fitZoomButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.fitZoomButton.toolTipText")); // NOI18N
|
||||
fitZoomButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-fit.png"))); // NOI18N
|
||||
fitZoomButton.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.fitZoomButton.text")); // NOI18N
|
||||
fitZoomButton.setToolTipText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.fitZoomButton.toolTipText")); // NOI18N
|
||||
fitZoomButton.setFocusable(false);
|
||||
fitZoomButton.setHorizontalTextPosition(SwingConstants.CENTER);
|
||||
fitZoomButton.setVerticalTextPosition(SwingConstants.BOTTOM);
|
||||
fitZoomButton.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
fitZoomButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
|
||||
fitZoomButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
|
||||
fitZoomButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
fitZoomButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
jLabel2.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jLabel2.text")); // NOI18N
|
||||
jLabel2.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jLabel2.text")); // NOI18N
|
||||
|
||||
zoomLabel.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomLabel.text")); // NOI18N
|
||||
zoomLabel.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomLabel.text")); // NOI18N
|
||||
|
||||
clearVizButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/broom.png"))); // NOI18N
|
||||
clearVizButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.clearVizButton.text_1")); // NOI18N
|
||||
clearVizButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.clearVizButton.toolTipText")); // NOI18N
|
||||
clearVizButton.setActionCommand(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.clearVizButton.actionCommand")); // NOI18N
|
||||
clearVizButton.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
clearVizButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/broom.png"))); // NOI18N
|
||||
clearVizButton.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.clearVizButton.text_1")); // NOI18N
|
||||
clearVizButton.setToolTipText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.clearVizButton.toolTipText")); // NOI18N
|
||||
clearVizButton.setActionCommand(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.clearVizButton.actionCommand")); // NOI18N
|
||||
clearVizButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
clearVizButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
jSeparator2.setOrientation(SwingConstants.VERTICAL);
|
||||
jSeparator2.setOrientation(javax.swing.SwingConstants.VERTICAL);
|
||||
|
||||
backButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/resultset_previous.png"))); // NOI18N
|
||||
backButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.backButton.text_1")); // NOI18N
|
||||
backButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.backButton.toolTipText")); // NOI18N
|
||||
backButton.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
backButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/resultset_previous.png"))); // NOI18N
|
||||
backButton.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.backButton.text_1")); // NOI18N
|
||||
backButton.setToolTipText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.backButton.toolTipText")); // NOI18N
|
||||
backButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
backButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
forwardButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/resultset_next.png"))); // NOI18N
|
||||
forwardButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.forwardButton.text")); // NOI18N
|
||||
forwardButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.forwardButton.toolTipText")); // NOI18N
|
||||
forwardButton.setHorizontalTextPosition(SwingConstants.LEADING);
|
||||
forwardButton.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
forwardButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/resultset_next.png"))); // NOI18N
|
||||
forwardButton.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.forwardButton.text")); // NOI18N
|
||||
forwardButton.setToolTipText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.forwardButton.toolTipText")); // NOI18N
|
||||
forwardButton.setHorizontalTextPosition(javax.swing.SwingConstants.LEADING);
|
||||
forwardButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
forwardButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
snapshotButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/report/images/image.png"))); // NOI18N
|
||||
snapshotButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.snapshotButton.text_1")); // NOI18N
|
||||
snapshotButton.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
snapshotButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/report/images/image.png"))); // NOI18N
|
||||
snapshotButton.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.snapshotButton.text_1")); // NOI18N
|
||||
snapshotButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
snapshotButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
jSeparator3.setOrientation(SwingConstants.VERTICAL);
|
||||
jSeparator3.setOrientation(javax.swing.SwingConstants.VERTICAL);
|
||||
|
||||
jSeparator4.setOrientation(SwingConstants.VERTICAL);
|
||||
jSeparator4.setOrientation(javax.swing.SwingConstants.VERTICAL);
|
||||
|
||||
GroupLayout toolbarLayout = new GroupLayout(toolbar);
|
||||
org.jdesktop.layout.GroupLayout toolbarLayout = new org.jdesktop.layout.GroupLayout(toolbar);
|
||||
toolbar.setLayout(toolbarLayout);
|
||||
toolbarLayout.setHorizontalGroup(toolbarLayout.createParallelGroup(GroupLayout.LEADING)
|
||||
toolbarLayout.setHorizontalGroup(
|
||||
toolbarLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
|
||||
.add(toolbarLayout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.add(backButton)
|
||||
.addPreferredGap(LayoutStyle.RELATED)
|
||||
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
|
||||
.add(forwardButton)
|
||||
.addPreferredGap(LayoutStyle.RELATED)
|
||||
.add(jSeparator4, GroupLayout.PREFERRED_SIZE, 10, GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(LayoutStyle.RELATED)
|
||||
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
|
||||
.add(jSeparator4, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 10, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
|
||||
.add(fastOrganicLayoutButton)
|
||||
.addPreferredGap(LayoutStyle.RELATED)
|
||||
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
|
||||
.add(clearVizButton)
|
||||
.addPreferredGap(LayoutStyle.RELATED)
|
||||
.add(jSeparator2, GroupLayout.PREFERRED_SIZE, 10, GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(LayoutStyle.RELATED)
|
||||
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
|
||||
.add(jSeparator2, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 10, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
|
||||
.add(jLabel2)
|
||||
.addPreferredGap(LayoutStyle.RELATED)
|
||||
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
|
||||
.add(zoomLabel)
|
||||
.addPreferredGap(LayoutStyle.RELATED)
|
||||
.add(zoomOutButton, GroupLayout.PREFERRED_SIZE, 32, GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(LayoutStyle.RELATED)
|
||||
.add(zoomInButton, GroupLayout.PREFERRED_SIZE, 32, GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(LayoutStyle.RELATED)
|
||||
.add(zoomActualButton, GroupLayout.PREFERRED_SIZE, 33, GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(LayoutStyle.RELATED)
|
||||
.add(fitZoomButton, GroupLayout.PREFERRED_SIZE, 32, GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(LayoutStyle.RELATED)
|
||||
.add(jSeparator3, GroupLayout.PREFERRED_SIZE, 10, GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(LayoutStyle.RELATED)
|
||||
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
|
||||
.add(zoomOutButton, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 32, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
|
||||
.add(zoomInButton, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 32, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
|
||||
.add(zoomActualButton, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 33, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
|
||||
.add(fitZoomButton, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 32, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
|
||||
.add(jSeparator3, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 10, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
|
||||
.add(snapshotButton)
|
||||
.addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
.addContainerGap(org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
);
|
||||
toolbarLayout.setVerticalGroup(toolbarLayout.createParallelGroup(GroupLayout.LEADING)
|
||||
toolbarLayout.setVerticalGroup(
|
||||
toolbarLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
|
||||
.add(toolbarLayout.createSequentialGroup()
|
||||
.add(3, 3, 3)
|
||||
.add(toolbarLayout.createParallelGroup(GroupLayout.CENTER)
|
||||
.add(toolbarLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.CENTER)
|
||||
.add(fastOrganicLayoutButton)
|
||||
.add(zoomOutButton)
|
||||
.add(zoomInButton, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.add(zoomActualButton, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.add(fitZoomButton, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.add(zoomInButton, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.add(zoomActualButton, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.add(fitZoomButton, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.add(jLabel2)
|
||||
.add(zoomLabel)
|
||||
.add(clearVizButton)
|
||||
.add(jSeparator2, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.add(jSeparator2, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.add(backButton)
|
||||
.add(forwardButton)
|
||||
.add(snapshotButton)
|
||||
.add(jSeparator3, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.add(jSeparator4, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
.add(jSeparator3, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.add(jSeparator4, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
.add(3, 3, 3))
|
||||
);
|
||||
|
||||
borderLayoutPanel.add(toolbar, BorderLayout.PAGE_START);
|
||||
borderLayoutPanel.add(notificationsJFXPanel, BorderLayout.PAGE_END);
|
||||
borderLayoutPanel.add(toolbar, java.awt.BorderLayout.PAGE_START);
|
||||
borderLayoutPanel.add(notificationsJFXPanel, java.awt.BorderLayout.PAGE_END);
|
||||
|
||||
splitPane.setLeftComponent(borderLayoutPanel);
|
||||
|
||||
add(splitPane, BorderLayout.CENTER);
|
||||
add(splitPane, java.awt.BorderLayout.CENTER);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
private void fitZoomButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_fitZoomButtonActionPerformed
|
||||
@ -890,26 +880,26 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
||||
}
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private JButton backButton;
|
||||
private JPanel borderLayoutPanel;
|
||||
private JButton clearVizButton;
|
||||
private JButton fastOrganicLayoutButton;
|
||||
private JButton fitZoomButton;
|
||||
private JButton forwardButton;
|
||||
private JLabel jLabel2;
|
||||
private JToolBar.Separator jSeparator2;
|
||||
private JToolBar.Separator jSeparator3;
|
||||
private JToolBar.Separator jSeparator4;
|
||||
private JTextArea jTextArea1;
|
||||
private JFXPanel notificationsJFXPanel;
|
||||
private JPanel placeHolderPanel;
|
||||
private JButton snapshotButton;
|
||||
private JSplitPane splitPane;
|
||||
private JPanel toolbar;
|
||||
private JButton zoomActualButton;
|
||||
private JButton zoomInButton;
|
||||
private JLabel zoomLabel;
|
||||
private JButton zoomOutButton;
|
||||
private javax.swing.JButton backButton;
|
||||
private javax.swing.JPanel borderLayoutPanel;
|
||||
private javax.swing.JButton clearVizButton;
|
||||
private javax.swing.JButton fastOrganicLayoutButton;
|
||||
private javax.swing.JButton fitZoomButton;
|
||||
private javax.swing.JButton forwardButton;
|
||||
private javax.swing.JLabel jLabel2;
|
||||
private javax.swing.JToolBar.Separator jSeparator2;
|
||||
private javax.swing.JToolBar.Separator jSeparator3;
|
||||
private javax.swing.JToolBar.Separator jSeparator4;
|
||||
private javax.swing.JTextArea jTextArea1;
|
||||
private javafx.embed.swing.JFXPanel notificationsJFXPanel;
|
||||
private javax.swing.JPanel placeHolderPanel;
|
||||
private javax.swing.JButton snapshotButton;
|
||||
private javax.swing.JSplitPane splitPane;
|
||||
private javax.swing.JPanel toolbar;
|
||||
private javax.swing.JButton zoomActualButton;
|
||||
private javax.swing.JButton zoomInButton;
|
||||
private javax.swing.JLabel zoomLabel;
|
||||
private javax.swing.JButton zoomOutButton;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
/**
|
||||
@ -922,40 +912,25 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider
|
||||
@Override
|
||||
public void invoke(Object sender, mxEventObject evt) {
|
||||
Object[] selectionCells = graph.getSelectionCells();
|
||||
Node rootNode = Node.EMPTY;
|
||||
Node[] selectedNodes = new Node[0];
|
||||
if (selectionCells.length > 0) {
|
||||
mxICell[] selectedCells = Arrays.asList(selectionCells).toArray(new mxCell[selectionCells.length]);
|
||||
HashSet<Content> relationshipSources = new HashSet<>();
|
||||
HashSet<AccountDeviceInstanceKey> adis = new HashSet<>();
|
||||
HashSet<AccountDeviceInstance> deviceInstances = new HashSet<>();
|
||||
for (mxICell cell : selectedCells) {
|
||||
if (cell.isEdge()) {
|
||||
mxICell source = (mxICell) graph.getModel().getTerminal(cell, true);
|
||||
AccountDeviceInstanceKey account1 = (AccountDeviceInstanceKey) source.getValue();
|
||||
mxICell target = (mxICell) graph.getModel().getTerminal(cell, false);
|
||||
AccountDeviceInstanceKey account2 = (AccountDeviceInstanceKey) target.getValue();
|
||||
try {
|
||||
final List<Content> relationshipSources1 = commsManager.getRelationshipSources(
|
||||
account1.getAccountDeviceInstance(),
|
||||
account2.getAccountDeviceInstance(),
|
||||
currentFilter);
|
||||
relationshipSources.addAll(relationshipSources1);
|
||||
} catch (TskCoreException tskCoreException) {
|
||||
logger.log(Level.SEVERE, " Error getting relationsips....", tskCoreException);
|
||||
}
|
||||
|
||||
deviceInstances.add(((AccountDeviceInstanceKey) source.getValue()).getAccountDeviceInstance());
|
||||
deviceInstances.add(((AccountDeviceInstanceKey) target.getValue()).getAccountDeviceInstance());
|
||||
|
||||
} else if (cell.isVertex()) {
|
||||
adis.add((AccountDeviceInstanceKey) cell.getValue());
|
||||
deviceInstances.add(((AccountDeviceInstanceKey) cell.getValue()).getAccountDeviceInstance());
|
||||
}
|
||||
}
|
||||
|
||||
rootNode = SelectionNode.createFromAccountsAndRelationships(relationshipSources, adis, currentFilter, commsManager);
|
||||
selectedNodes = new Node[]{rootNode};
|
||||
}
|
||||
vizEM.setRootContext(rootNode);
|
||||
try {
|
||||
vizEM.setSelectedNodes(selectedNodes);
|
||||
} catch (PropertyVetoException ex) {
|
||||
logger.log(Level.SEVERE, "Selection vetoed.", ex);
|
||||
relationshipBrowser.setSelectionInfo(new SelectionInfo(deviceInstances, currentFilter));
|
||||
} else {
|
||||
relationshipBrowser.setSelectionInfo(new SelectionInfo(Collections.EMPTY_SET, currentFilter));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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.communications.relationships;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import org.openide.nodes.AbstractNode;
|
||||
import org.openide.nodes.ChildFactory;
|
||||
import org.openide.nodes.Children;
|
||||
import org.openide.nodes.Node;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.datamodel.Account;
|
||||
import org.sleuthkit.datamodel.AccountFileInstance;
|
||||
import org.sleuthkit.datamodel.CommunicationsManager;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* ChildFactory that creates ContentNode representing the files that reference
|
||||
* the given list of accounts.
|
||||
*/
|
||||
final class AccountSourceContentChildNodeFactory extends ChildFactory<Content> {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(AccountSourceContentChildNodeFactory.class.getName());
|
||||
|
||||
private final Set<Account> accounts;
|
||||
|
||||
AccountSourceContentChildNodeFactory(Set<Account> accounts) {
|
||||
this.accounts = accounts;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean createKeys(List<Content> list) {
|
||||
if (accounts == null || accounts.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
CommunicationsManager communicationManager;
|
||||
try {
|
||||
communicationManager = Case.getCurrentCaseThrows().getSleuthkitCase().getCommunicationsManager();
|
||||
} catch (NoCurrentCaseException | TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "Failed to get communications manager from case.", ex); //NON-NLS
|
||||
return false;
|
||||
}
|
||||
|
||||
accounts.forEach((account) -> {
|
||||
try {
|
||||
List<AccountFileInstance> accountFileInstanceList = communicationManager.getAccountFileInstances(account);
|
||||
|
||||
for (AccountFileInstance fileInstance : accountFileInstanceList) {
|
||||
list.add(fileInstance.getFile());
|
||||
}
|
||||
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.WARNING, String.format("Failed to getAccountFileInstances for account: %d", account.getAccountID()), ex); //NON-NLS
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node createNodeForKey(Content content) {
|
||||
return new ContentNode(content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple AbstractNode for a Content (file) object.
|
||||
*/
|
||||
final class ContentNode extends AbstractNode {
|
||||
|
||||
private final Content content;
|
||||
|
||||
ContentNode(Content content) {
|
||||
super(Children.LEAF);
|
||||
this.content = content;
|
||||
|
||||
try {
|
||||
setDisplayName(content.getUniquePath());
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.WARNING, String.format("Unable to getUniquePath for Content: %d", content.getId()), ex); //NON-NLS
|
||||
setDisplayName(content.getName());
|
||||
}
|
||||
|
||||
setIconBaseWithExtension("org/sleuthkit/autopsy/images/file-icon.png"); //NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,127 @@
|
||||
/*
|
||||
* 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 obt ain 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.communications.relationships;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.logging.Level;
|
||||
import org.openide.nodes.Children;
|
||||
import org.openide.nodes.Node;
|
||||
import org.openide.nodes.Sheet;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode;
|
||||
import org.sleuthkit.autopsy.datamodel.FileNode;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* Factory for creating thumbnail children nodes.
|
||||
*/
|
||||
final class AttachmentsChildren extends Children.Keys<AbstractFile> {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(AttachmentsChildren.class.getName());
|
||||
|
||||
private final Set<BlackboardArtifact> artifacts;
|
||||
|
||||
/*
|
||||
* Creates the list of thumbnails from the given list of
|
||||
* BlackboardArtifacts.
|
||||
*
|
||||
* The thumbnails will be initialls sorted by size, then name so that they
|
||||
* appear sorted by size by default.
|
||||
*/
|
||||
AttachmentsChildren(Set<BlackboardArtifact> artifacts) {
|
||||
super(false);
|
||||
|
||||
this.artifacts = artifacts;
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node[] createNodes(AbstractFile t) {
|
||||
return new Node[]{new AttachementNode(t)};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addNotify() {
|
||||
super.addNotify();
|
||||
|
||||
Set<AbstractFile> thumbnails = new TreeSet<>((AbstractFile file1, AbstractFile file2) -> {
|
||||
int result = Long.compare(file1.getSize(), file2.getSize());
|
||||
if (result == 0) {
|
||||
result = file1.getName().compareTo(file2.getName());
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
|
||||
artifacts.forEach((bba) -> {
|
||||
try {
|
||||
for (Content childContent : bba.getChildren()) {
|
||||
if (childContent instanceof AbstractFile) {
|
||||
thumbnails.add((AbstractFile) childContent);
|
||||
}
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "Unable to get children from artifact.", ex); //NON-NLS
|
||||
}
|
||||
});
|
||||
|
||||
setKeys(thumbnails);
|
||||
}
|
||||
|
||||
/**
|
||||
* A node for representing a thumbnail.
|
||||
*/
|
||||
static class AttachementNode extends FileNode {
|
||||
|
||||
AttachementNode(AbstractFile file) {
|
||||
super(file, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Sheet createSheet() {
|
||||
Sheet sheet = super.createSheet();
|
||||
Set<String> keepProps = new HashSet<>(Arrays.asList(
|
||||
NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.nameColLbl"),
|
||||
NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.createSheet.score.name"),
|
||||
NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.createSheet.comment.name"),
|
||||
NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.createSheet.count.name"),
|
||||
NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.sizeColLbl"),
|
||||
NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.mimeType"),
|
||||
NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.knownColLbl")));
|
||||
|
||||
//Remove all other props except for the ones above
|
||||
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
|
||||
for (Node.Property<?> p : sheetSet.getProperties()) {
|
||||
if (!keepProps.contains(p.getName())) {
|
||||
sheetSet.remove(p.getName());
|
||||
}
|
||||
}
|
||||
|
||||
return sheet;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
ContactDetailsPane.nameLabel.text=Placeholder
|
||||
SummaryViewer.countsPanel.border.title=Counts
|
||||
SummaryViewer.emailLabel.text=Emails:
|
||||
SummaryViewer.contactsLabel.text=Contacts:
|
||||
SummaryViewer.attachmentsLabel.text=Attachments:
|
||||
SummaryViewer.fileReferencesPanel.border.title=File References in Current Case
|
||||
SummaryViewer.caseReferencesPanel.border.title=Other Occurrences
|
||||
OutlineViewPanel.messageLabel.text=<Control Disabled>
|
||||
SummaryViewer.messagesDataLabel.text=messages
|
||||
SummaryViewer.callLogsDataLabel.text=callLogs
|
||||
SummaryViewer.contactsDataLabel.text=contacts
|
||||
SummaryViewer.emailDataLabel.text=emails
|
||||
SummaryViewer.attachmentsDataLabel.text=attachments
|
||||
SummaryViewer.messagesLabel.text=Messages:
|
||||
SummaryViewer.callLogsLabel.text=Call Logs:
|
@ -0,0 +1,44 @@
|
||||
ContactDetailsPane.nameLabel.text=Placeholder
|
||||
ContactNode_Email=Email Address
|
||||
ContactNode_Home_Number=Home Number
|
||||
ContactNode_Mobile_Number=Mobile Number
|
||||
ContactNode_Name=Name
|
||||
ContactNode_Office_Number=Office Number
|
||||
ContactNode_Phone=Phone Number
|
||||
ContactNode_URL=URL
|
||||
ContactsViewer_columnHeader_Email=Email
|
||||
ContactsViewer_columnHeader_Name=Name
|
||||
ContactsViewer_columnHeader_Phone=Phone
|
||||
ContactsViewer_tabTitle=Contacts
|
||||
MediaViewer_Name=Media
|
||||
MessageNode_Node_Property_Attms=Attachments
|
||||
MessageNode_Node_Property_Date=Date
|
||||
MessageNode_Node_Property_From=From
|
||||
MessageNode_Node_Property_Subject=Subject
|
||||
MessageNode_Node_Property_To=To
|
||||
MessageNode_Node_Property_Type=Type
|
||||
MessageViewer_columnHeader_Attms=Attachments
|
||||
MessageViewer_columnHeader_Date=Date
|
||||
MessageViewer_columnHeader_From=From
|
||||
MessageViewer_columnHeader_Subject=Subject
|
||||
MessageViewer_columnHeader_To=To
|
||||
MessageViewer_tabTitle=Messages
|
||||
SummaryViewer.countsPanel.border.title=Counts
|
||||
SummaryViewer.emailLabel.text=Emails:
|
||||
SummaryViewer.contactsLabel.text=Contacts:
|
||||
SummaryViewer.attachmentsLabel.text=Attachments:
|
||||
SummaryViewer.fileReferencesPanel.border.title=File References in Current Case
|
||||
SummaryViewer.caseReferencesPanel.border.title=Other Occurrences
|
||||
OutlineViewPanel.messageLabel.text=<Control Disabled>
|
||||
SummaryViewer.messagesDataLabel.text=messages
|
||||
SummaryViewer.callLogsDataLabel.text=callLogs
|
||||
SummaryViewer.contactsDataLabel.text=contacts
|
||||
SummaryViewer.emailDataLabel.text=emails
|
||||
SummaryViewer.attachmentsDataLabel.text=attachments
|
||||
SummaryViewer.messagesLabel.text=Messages:
|
||||
SummaryViewer.callLogsLabel.text=Call Logs:
|
||||
SummaryViewer_CaseRefNameColumn_Title=Case Name
|
||||
SummaryViewer_CentralRepository_Message=<Enable Central Resposity to see Case References>
|
||||
SummaryViewer_Creation_Date_Title=Creation Date
|
||||
SummaryViewer_FileRefNameColumn_Title=Path
|
||||
SummaryViewer_TabTitle=Summary
|
@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
||||
<NonVisualComponents>
|
||||
<Component class="org.sleuthkit.autopsy.contentviewers.MessageContentViewer" name="messageContentViewer1">
|
||||
</Component>
|
||||
</NonVisualComponents>
|
||||
<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">
|
||||
<Component id="propertySheet" max="32767" attributes="0"/>
|
||||
<Group type="102" attributes="0">
|
||||
<Component id="nameLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</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="nameLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="propertySheet" pref="283" max="32767" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JLabel" name="nameLabel">
|
||||
<Properties>
|
||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
|
||||
<Font name="Tahoma" size="24" style="0"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="ContactDetailsPane.nameLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="org.openide.explorer.propertysheet.PropertySheet" name="propertySheet">
|
||||
<Properties>
|
||||
<Property name="descriptionAreaVisible" type="boolean" value="false"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* 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.communications.relationships;
|
||||
|
||||
import org.openide.explorer.ExplorerManager;
|
||||
import org.openide.nodes.Node;
|
||||
|
||||
/**
|
||||
* Displays the propertied of a ContactNode in a PropertySheet.
|
||||
*/
|
||||
public final class ContactDetailsPane extends javax.swing.JPanel implements ExplorerManager.Provider {
|
||||
|
||||
final private ExplorerManager explorerManager = new ExplorerManager();
|
||||
|
||||
/**
|
||||
* Displays the propertied of a ContactNode in a PropertySheet.
|
||||
*/
|
||||
public ContactDetailsPane() {
|
||||
initComponents();
|
||||
this.setEnabled(false);
|
||||
|
||||
nameLabel.setText("");
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the list of nodes for the property sheet.
|
||||
*
|
||||
* @param nodes List of nodes to set
|
||||
*/
|
||||
public void setNode(Node[] nodes) {
|
||||
if (nodes != null && nodes.length == 1) {
|
||||
nameLabel.setText(nodes[0].getDisplayName());
|
||||
propertySheet.setNodes(nodes);
|
||||
} else {
|
||||
nameLabel.setText("");
|
||||
propertySheet.setNodes(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExplorerManager getExplorerManager() {
|
||||
return explorerManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
super.setEnabled(enabled);
|
||||
nameLabel.setEnabled(enabled);
|
||||
propertySheet.setEnabled(enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
|
||||
messageContentViewer1 = new org.sleuthkit.autopsy.contentviewers.MessageContentViewer();
|
||||
nameLabel = new javax.swing.JLabel();
|
||||
propertySheet = new org.openide.explorer.propertysheet.PropertySheet();
|
||||
|
||||
nameLabel.setFont(new java.awt.Font("Tahoma", 0, 24)); // NOI18N
|
||||
org.openide.awt.Mnemonics.setLocalizedText(nameLabel, org.openide.util.NbBundle.getMessage(ContactDetailsPane.class, "ContactDetailsPane.nameLabel.text")); // NOI18N
|
||||
|
||||
propertySheet.setDescriptionAreaVisible(false);
|
||||
|
||||
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)
|
||||
.addComponent(propertySheet, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(nameLabel)
|
||||
.addGap(0, 0, Short.MAX_VALUE)))
|
||||
.addContainerGap())
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(nameLabel)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(propertySheet, javax.swing.GroupLayout.DEFAULT_SIZE, 283, Short.MAX_VALUE)
|
||||
.addContainerGap())
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private org.sleuthkit.autopsy.contentviewers.MessageContentViewer messageContentViewer1;
|
||||
private javax.swing.JLabel nameLabel;
|
||||
private org.openide.explorer.propertysheet.PropertySheet propertySheet;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
}
|
161
Core/src/org/sleuthkit/autopsy/communications/relationships/ContactNode.java
Executable file
161
Core/src/org/sleuthkit/autopsy/communications/relationships/ContactNode.java
Executable file
@ -0,0 +1,161 @@
|
||||
/*
|
||||
* 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.communications.relationships;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
import java.util.logging.Level;
|
||||
import org.openide.nodes.Sheet;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode;
|
||||
import org.sleuthkit.autopsy.datamodel.NodeProperty;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME;
|
||||
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME_PERSON;
|
||||
import static org.sleuthkit.datamodel.BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME;
|
||||
import org.sleuthkit.datamodel.TimeUtilities;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.autopsy.communications.Utils;
|
||||
|
||||
/**
|
||||
* Extends BlackboardArtifactNode to override createSheet to create a contact
|
||||
* artifact specific sheet.
|
||||
*/
|
||||
final class ContactNode extends BlackboardArtifactNode {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(ContactNode.class.getName());
|
||||
|
||||
@Messages({
|
||||
"ContactNode_Name=Name",
|
||||
"ContactNode_Phone=Phone Number",
|
||||
"ContactNode_Email=Email Address",
|
||||
"ContactNode_Mobile_Number=Mobile Number",
|
||||
"ContactNode_Office_Number=Office Number",
|
||||
"ContactNode_URL=URL",
|
||||
"ContactNode_Home_Number=Home Number",})
|
||||
|
||||
ContactNode(BlackboardArtifact artifact) {
|
||||
super(artifact);
|
||||
|
||||
String name = getAttributeDisplayString(artifact, TSK_NAME);
|
||||
if (name == null || name.trim().isEmpty()) {
|
||||
// VCards use TSK_NAME_PERSON instead of TSK_NAME
|
||||
name = getAttributeDisplayString(artifact, TSK_NAME_PERSON);
|
||||
}
|
||||
setDisplayName(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Sheet createSheet() {
|
||||
Sheet sheet = super.createSheet();
|
||||
|
||||
final BlackboardArtifact artifact = getArtifact();
|
||||
BlackboardArtifact.ARTIFACT_TYPE fromID = BlackboardArtifact.ARTIFACT_TYPE.fromID(artifact.getArtifactTypeID());
|
||||
if (fromID != TSK_CONTACT) {
|
||||
return sheet;
|
||||
}
|
||||
|
||||
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
|
||||
if (sheetSet == null) {
|
||||
sheetSet = Sheet.createPropertiesSet();
|
||||
sheet.put(sheetSet);
|
||||
}
|
||||
|
||||
// Sorting the attributes by type so that the duplicates can be removed
|
||||
// and they can be grouped by type for display. The attribute prefixes
|
||||
// are used so that all attributed of that type are found, including
|
||||
// ones that are not predefined as part of BlackboardAttributes
|
||||
try {
|
||||
HashMap<String, BlackboardAttribute> phoneNumMap = new HashMap<>();
|
||||
HashMap<String, BlackboardAttribute> emailMap = new HashMap<>();
|
||||
HashMap<String, BlackboardAttribute> nameMap = new HashMap<>();
|
||||
HashMap<String, BlackboardAttribute> otherMap = new HashMap<>();
|
||||
for (BlackboardAttribute bba : artifact.getAttributes()) {
|
||||
if (bba.getAttributeType().getTypeName().startsWith("TSK_PHONE")) {
|
||||
phoneNumMap.put(bba.getDisplayString(), bba);
|
||||
} else if (bba.getAttributeType().getTypeName().startsWith("TSK_EMAIL")) {
|
||||
emailMap.put(bba.getDisplayString(), bba);
|
||||
} else if (bba.getAttributeType().getTypeName().startsWith("TSK_NAME")) {
|
||||
nameMap.put(bba.getDisplayString(), bba);
|
||||
} else {
|
||||
otherMap.put(bba.getDisplayString(), bba);
|
||||
}
|
||||
}
|
||||
|
||||
addPropertiesToSheet(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME.getLabel(),
|
||||
sheetSet, nameMap);
|
||||
addPropertiesToSheet(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER.getLabel(),
|
||||
sheetSet, phoneNumMap);
|
||||
addPropertiesToSheet(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL.getLabel(),
|
||||
sheetSet, emailMap);
|
||||
|
||||
for (BlackboardAttribute bba : otherMap.values()) {
|
||||
sheetSet.put(new NodeProperty<>(bba.getAttributeType().getTypeName(), bba.getAttributeType().getDisplayName(), "", bba.getDisplayString()));
|
||||
}
|
||||
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "Error getting attribute values.", ex); //NON-NLS
|
||||
}
|
||||
|
||||
return sheet;
|
||||
}
|
||||
|
||||
private void addPropertiesToSheet(String propertyID, Sheet.Set sheetSet, Map<String, BlackboardAttribute> attributeMap) {
|
||||
int count = 0;
|
||||
for (BlackboardAttribute bba : attributeMap.values()) {
|
||||
if (count++ > 0) {
|
||||
sheetSet.put(new NodeProperty<>(propertyID + "_" + count, bba.getAttributeType().getDisplayName(), "", bba.getDisplayString()));
|
||||
} else {
|
||||
sheetSet.put(new NodeProperty<>(propertyID, bba.getAttributeType().getDisplayName(), "", bba.getDisplayString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String getAttributeDisplayString(final BlackboardArtifact artifact, final BlackboardAttribute.ATTRIBUTE_TYPE attributeType) {
|
||||
try {
|
||||
BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.fromID(attributeType.getTypeID())));
|
||||
if (attribute == null) {
|
||||
return "";
|
||||
} else if (attributeType.getValueType() == DATETIME) {
|
||||
return TimeUtilities.epochToTime(attribute.getValueLong(),
|
||||
TimeZone.getTimeZone(Utils.getUserPreferredZoneId()));
|
||||
} else {
|
||||
return attribute.getDisplayString();
|
||||
}
|
||||
} catch (TskCoreException tskCoreException) {
|
||||
logger.log(Level.WARNING, "Error getting attribute value.", tskCoreException); //NON-NLS
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Circumvent DataResultFilterNode's slightly odd delegation to
|
||||
* BlackboardArtifactNode.getSourceName().
|
||||
*
|
||||
* @return the displayName of this Node, which is the type.
|
||||
*/
|
||||
@Override
|
||||
public String getSourceName() {
|
||||
return getDisplayName();
|
||||
}
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* 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.communications.relationships;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import org.openide.nodes.ChildFactory;
|
||||
import org.openide.nodes.Node;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT;
|
||||
import org.sleuthkit.datamodel.CommunicationsManager;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* ChildFactory for ContactNodes.
|
||||
*/
|
||||
final class ContactsChildNodeFactory extends ChildFactory<BlackboardArtifact>{
|
||||
private static final Logger logger = Logger.getLogger(MessagesChildNodeFactory.class.getName());
|
||||
|
||||
private SelectionInfo selectionInfo;
|
||||
|
||||
/**
|
||||
* Construct a new ContactsChildNodeFactory from the currently selectionInfo
|
||||
*
|
||||
* @param selectionInfo SelectionInfo object for the currently selected
|
||||
* accounts
|
||||
*/
|
||||
ContactsChildNodeFactory(SelectionInfo selectionInfo) {
|
||||
this.selectionInfo = selectionInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the current instance of selectionInfo and calls the refresh method.
|
||||
*
|
||||
* @param selectionInfo New instance of the currently selected accounts
|
||||
*/
|
||||
public void refresh(SelectionInfo selectionInfo) {
|
||||
this.selectionInfo = selectionInfo;
|
||||
refresh(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a list of Keys (BlackboardArtifact) for only contacts of the
|
||||
* currently selected accounts
|
||||
* @param list List of BlackboardArtifact to populate
|
||||
* @return True on success
|
||||
*/
|
||||
@Override
|
||||
protected boolean createKeys(List<BlackboardArtifact> list) {
|
||||
CommunicationsManager communicationManager;
|
||||
try {
|
||||
communicationManager = Case.getCurrentCaseThrows().getSleuthkitCase().getCommunicationsManager();
|
||||
} catch (NoCurrentCaseException | TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Failed to get communications manager from case.", ex); //NON-NLS
|
||||
return false;
|
||||
}
|
||||
|
||||
if(selectionInfo == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final Set<Content> relationshipSources;
|
||||
|
||||
try {
|
||||
relationshipSources = communicationManager.getRelationshipSources(selectionInfo.getAccountDevicesInstances(), selectionInfo.getCommunicationsFilter());
|
||||
|
||||
relationshipSources.stream().filter((content) -> (content instanceof BlackboardArtifact)).forEachOrdered((content) -> {
|
||||
|
||||
BlackboardArtifact bba = (BlackboardArtifact) content;
|
||||
BlackboardArtifact.ARTIFACT_TYPE fromID = BlackboardArtifact.ARTIFACT_TYPE.fromID(bba.getArtifactTypeID());
|
||||
|
||||
if (fromID == TSK_CONTACT) {
|
||||
list.add(bba);
|
||||
}
|
||||
});
|
||||
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Failed to get relationship sources.", ex); //NON-NLS
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node createNodeForKey(BlackboardArtifact key) {
|
||||
return new ContactNode(key);
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.4" 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">
|
||||
<Component id="outlineView" max="32767" attributes="0"/>
|
||||
<Component id="contactPane" alignment="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="outlineView" pref="350" max="32767" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="1" max="-2" attributes="0"/>
|
||||
<Component id="contactPane" pref="400" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="org.openide.explorer.view.OutlineView" name="outlineView">
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new org.openide.explorer.view.OutlineView()"/>
|
||||
</AuxValues>
|
||||
</Component>
|
||||
<Component class="org.sleuthkit.autopsy.communications.relationships.ContactDetailsPane" name="contactPane">
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
186
Core/src/org/sleuthkit/autopsy/communications/relationships/ContactsViewer.java
Executable file
186
Core/src/org/sleuthkit/autopsy/communications/relationships/ContactsViewer.java
Executable file
@ -0,0 +1,186 @@
|
||||
/*
|
||||
* 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 obt ain 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.communications.relationships;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.KeyboardFocusManager;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import static javax.swing.SwingUtilities.isDescendingFrom;
|
||||
import org.netbeans.swing.outline.DefaultOutlineModel;
|
||||
import org.netbeans.swing.outline.Outline;
|
||||
import org.openide.explorer.ExplorerManager;
|
||||
import static org.openide.explorer.ExplorerUtils.createLookup;
|
||||
import org.openide.nodes.AbstractNode;
|
||||
import org.openide.nodes.Children;
|
||||
import org.openide.nodes.Node;
|
||||
import org.openide.util.Lookup;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.lookup.ServiceProvider;
|
||||
import org.sleuthkit.autopsy.communications.ModifiableProxyLookup;
|
||||
import org.sleuthkit.autopsy.corecomponents.TableFilterNode;
|
||||
import org.sleuthkit.autopsy.directorytree.DataResultFilterNode;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
|
||||
/**
|
||||
* Visualization for contact nodes.
|
||||
*
|
||||
*/
|
||||
@ServiceProvider(service = RelationshipsViewer.class)
|
||||
public final class ContactsViewer extends JPanel implements RelationshipsViewer, ExplorerManager.Provider, Lookup.Provider {
|
||||
|
||||
private final ExplorerManager tableEM;
|
||||
private final Outline outline;
|
||||
private final ModifiableProxyLookup proxyLookup;
|
||||
private final PropertyChangeListener focusPropertyListener;
|
||||
private final ContactsChildNodeFactory nodeFactory;
|
||||
|
||||
@NbBundle.Messages({
|
||||
"ContactsViewer_tabTitle=Contacts",
|
||||
"ContactsViewer_columnHeader_Name=Name",
|
||||
"ContactsViewer_columnHeader_Phone=Phone",
|
||||
"ContactsViewer_columnHeader_Email=Email",})
|
||||
|
||||
/**
|
||||
* Visualization for contact nodes.
|
||||
*/
|
||||
public ContactsViewer() {
|
||||
tableEM = new ExplorerManager();
|
||||
proxyLookup = new ModifiableProxyLookup(createLookup(tableEM, getActionMap()));
|
||||
nodeFactory = new ContactsChildNodeFactory(null);
|
||||
|
||||
// See org.sleuthkit.autopsy.timeline.TimeLineTopComponent for a detailed
|
||||
// explaination of focusPropertyListener
|
||||
focusPropertyListener = (final PropertyChangeEvent focusEvent) -> {
|
||||
if (focusEvent.getPropertyName().equalsIgnoreCase("focusOwner")) {
|
||||
final Component newFocusOwner = (Component) focusEvent.getNewValue();
|
||||
|
||||
if (newFocusOwner == null) {
|
||||
return;
|
||||
}
|
||||
if (isDescendingFrom(newFocusOwner, contactPane)) {
|
||||
//if the focus owner is within the MessageContentViewer (the attachments table)
|
||||
proxyLookup.setNewLookups(createLookup(contactPane.getExplorerManager(), getActionMap()));
|
||||
} else if (isDescendingFrom(newFocusOwner, ContactsViewer.this)) {
|
||||
//... or if it is within the Results table.
|
||||
proxyLookup.setNewLookups(createLookup(tableEM, getActionMap()));
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
initComponents();
|
||||
|
||||
outline = outlineView.getOutline();
|
||||
outlineView.setPropertyColumns(
|
||||
"TSK_EMAIL", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL.getDisplayName(),
|
||||
"TSK_PHONE_NUMBER", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER.getDisplayName()
|
||||
);
|
||||
outline.setRootVisible(false);
|
||||
((DefaultOutlineModel) outline.getOutlineModel()).setNodesColumnLabel(Bundle.ContactsViewer_columnHeader_Name());
|
||||
|
||||
tableEM.addPropertyChangeListener((PropertyChangeEvent evt) -> {
|
||||
if (evt.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) {
|
||||
final Node[] nodes = tableEM.getSelectedNodes();
|
||||
contactPane.setNode(nodes);
|
||||
}
|
||||
});
|
||||
|
||||
tableEM.setRootContext(new TableFilterNode(new DataResultFilterNode(new AbstractNode(Children.create(nodeFactory, true)), getExplorerManager()), true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return Bundle.ContactsViewer_tabTitle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JPanel getPanel() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelectionInfo(SelectionInfo info) {
|
||||
contactPane.setNode(new Node[]{new AbstractNode(Children.LEAF)});
|
||||
contactPane.setEnabled(false);
|
||||
|
||||
nodeFactory.refresh(info);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExplorerManager getExplorerManager() {
|
||||
return tableEM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Lookup getLookup() {
|
||||
return proxyLookup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addNotify() {
|
||||
super.addNotify();
|
||||
//add listener that maintains correct selection in the Global Actions Context
|
||||
KeyboardFocusManager.getCurrentKeyboardFocusManager()
|
||||
.addPropertyChangeListener("focusOwner", focusPropertyListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeNotify() {
|
||||
super.removeNotify();
|
||||
KeyboardFocusManager.getCurrentKeyboardFocusManager()
|
||||
.removePropertyChangeListener("focusOwner", focusPropertyListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
|
||||
outlineView = new org.openide.explorer.view.OutlineView();
|
||||
contactPane = new org.sleuthkit.autopsy.communications.relationships.ContactDetailsPane();
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(contactPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 350, Short.MAX_VALUE)
|
||||
.addGap(1, 1, 1)
|
||||
.addComponent(contactPane, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE))
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private org.sleuthkit.autopsy.communications.relationships.ContactDetailsPane contactPane;
|
||||
private org.openide.explorer.view.OutlineView outlineView;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
}
|
@ -0,0 +1,169 @@
|
||||
/*
|
||||
* 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.communications.relationships;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import org.openide.nodes.AbstractNode;
|
||||
import org.openide.nodes.ChildFactory;
|
||||
import org.openide.nodes.Children;
|
||||
import org.openide.nodes.Node;
|
||||
import org.openide.nodes.Sheet;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNormalizationException;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datamodel.NodeProperty;
|
||||
import org.sleuthkit.datamodel.Account;
|
||||
|
||||
/**
|
||||
* ChildFactory for CorrelationCases. Finds the cases that reference the given
|
||||
* list of accounts.
|
||||
*/
|
||||
final class CorrelationCaseChildNodeFactory extends ChildFactory<CorrelationCase> {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(CorrelationCaseChildNodeFactory.class.getName());
|
||||
|
||||
private Map<Integer, CorrelationAttributeInstance.Type> correlationTypeMap;
|
||||
private final Set<Account> accounts;
|
||||
|
||||
/**
|
||||
* ChildFactory for CorrelationCases.
|
||||
*
|
||||
* @param accounts List of Account objects
|
||||
*/
|
||||
CorrelationCaseChildNodeFactory(Set<Account> accounts) {
|
||||
this.accounts = accounts;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean createKeys(List<CorrelationCase> list) {
|
||||
if (!EamDb.isEnabled()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
EamDb dbInstance;
|
||||
try {
|
||||
dbInstance = EamDb.getInstance();
|
||||
} catch (EamDbException ex) {
|
||||
logger.log(Level.SEVERE, "Unable to connect to the Central Repository database.", ex); //NON-NLS
|
||||
return false;
|
||||
}
|
||||
|
||||
Map<String, CorrelationCase> uniqueCaseMap = new HashMap<>();
|
||||
|
||||
accounts.forEach((account) -> {
|
||||
try {
|
||||
CorrelationAttributeInstance.Type correlationType = getCorrelationType(account.getAccountType());
|
||||
if (correlationType != null) {
|
||||
List<CorrelationAttributeInstance> correlationInstances = dbInstance.getArtifactInstancesByTypeValue(correlationType, account.getTypeSpecificID());
|
||||
correlationInstances.forEach((correlationInstance) -> {
|
||||
CorrelationCase correlationCase = correlationInstance.getCorrelationCase();
|
||||
uniqueCaseMap.put(correlationCase.getCaseUUID(), correlationCase);
|
||||
});
|
||||
}
|
||||
} catch (EamDbException | CorrelationAttributeNormalizationException ex) {
|
||||
logger.log(Level.WARNING, String.format("Unable to getArtifactInstance for accountID: %d", account.getAccountID()), ex); //NON-NLS
|
||||
}
|
||||
});
|
||||
|
||||
list.addAll(uniqueCaseMap.values());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node createNodeForKey(CorrelationCase correlationCase) {
|
||||
return new CaseNode(correlationCase);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the CorrelationAttributeInstance.Type for the given Account.Type.
|
||||
*
|
||||
* @param accountType Account type
|
||||
*
|
||||
* @return CorrelationAttributeInstance.Type for given account or null if
|
||||
* there is no match
|
||||
*
|
||||
* @throws EamDbException
|
||||
*/
|
||||
private CorrelationAttributeInstance.Type getCorrelationType(Account.Type accountType) throws EamDbException {
|
||||
if (correlationTypeMap == null) {
|
||||
correlationTypeMap = new HashMap<>();
|
||||
List<CorrelationAttributeInstance.Type> correcationTypeList = CorrelationAttributeInstance.getDefaultCorrelationTypes();
|
||||
correcationTypeList.forEach((type) -> {
|
||||
correlationTypeMap.put(type.getId(), type);
|
||||
});
|
||||
}
|
||||
|
||||
if (Account.Type.EMAIL.equals(accountType)) {
|
||||
return correlationTypeMap.get(CorrelationAttributeInstance.EMAIL_TYPE_ID);
|
||||
} else if (Account.Type.PHONE.equals(accountType)) {
|
||||
return correlationTypeMap.get(CorrelationAttributeInstance.PHONE_TYPE_ID);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple AbstractNode for a CorrelationCase. The property sheet only
|
||||
* contains the creation date.
|
||||
*/
|
||||
final class CaseNode extends AbstractNode {
|
||||
|
||||
private final CorrelationCase correlationCase;
|
||||
|
||||
/**
|
||||
* Construct the object, set the display name and icon.
|
||||
*
|
||||
* @param correlationCase
|
||||
*/
|
||||
CaseNode(CorrelationCase correlationCase) {
|
||||
super(Children.LEAF);
|
||||
this.correlationCase = correlationCase;
|
||||
|
||||
setDisplayName(correlationCase.getDisplayName());
|
||||
setIconBaseWithExtension("org/sleuthkit/autopsy/images/briefcase.png"); //NON-NLS
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Sheet createSheet() {
|
||||
super.createSheet();
|
||||
Sheet sheet = new Sheet();
|
||||
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
|
||||
if (sheetSet == null) {
|
||||
sheetSet = Sheet.createPropertiesSet();
|
||||
sheet.put(sheetSet);
|
||||
}
|
||||
|
||||
sheetSet.put(new NodeProperty<>("creationDate", //NON-NLS
|
||||
correlationCase.getTitleCreationDate(),
|
||||
correlationCase.getTitleCreationDate(),
|
||||
correlationCase.getCreationDate()));
|
||||
|
||||
return sheet;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
68
Core/src/org/sleuthkit/autopsy/communications/relationships/MediaViewer.form
Executable file
68
Core/src/org/sleuthkit/autopsy/communications/relationships/MediaViewer.form
Executable file
@ -0,0 +1,68 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.4" 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">
|
||||
<Component id="thumbnailViewer" alignment="1" max="32767" attributes="0"/>
|
||||
<Component id="contentViewer" alignment="1" max="32767" attributes="0"/>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="separator" max="32767" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="thumbnailViewer" pref="350" max="32767" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="separator" min="-2" pref="2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="contentViewer" min="-2" pref="450" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="3" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="org.sleuthkit.autopsy.corecomponents.DataResultViewerThumbnail" name="thumbnailViewer">
|
||||
<Properties>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[350, 102]"/>
|
||||
</Property>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[450, 400]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new org.sleuthkit.autopsy.corecomponents.DataResultViewerThumbnail(tableEM)"/>
|
||||
</AuxValues>
|
||||
</Component>
|
||||
<Component class="org.sleuthkit.autopsy.contentviewers.MessageContentViewer" name="contentViewer">
|
||||
<Properties>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[450, 400]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new MessageDataContent()"/>
|
||||
</AuxValues>
|
||||
</Component>
|
||||
<Component class="javax.swing.JSeparator" name="separator">
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
233
Core/src/org/sleuthkit/autopsy/communications/relationships/MediaViewer.java
Executable file
233
Core/src/org/sleuthkit/autopsy/communications/relationships/MediaViewer.java
Executable file
@ -0,0 +1,233 @@
|
||||
/*
|
||||
* 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 obt ain 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.communications.relationships;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.KeyboardFocusManager;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.JPanel;
|
||||
import static javax.swing.SwingUtilities.isDescendingFrom;
|
||||
import org.openide.explorer.ExplorerManager;
|
||||
import static org.openide.explorer.ExplorerUtils.createLookup;
|
||||
import org.openide.nodes.AbstractNode;
|
||||
import org.openide.nodes.Node;
|
||||
import org.openide.util.Lookup;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.communications.ModifiableProxyLookup;
|
||||
import org.sleuthkit.autopsy.corecomponents.TableFilterNode;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode;
|
||||
import org.sleuthkit.autopsy.directorytree.DataResultFilterNode;
|
||||
import org.sleuthkit.datamodel.AbstractContent;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.CommunicationsManager;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* A Panel that shows the media (thumbnails) for the selected account.
|
||||
*/
|
||||
final class MediaViewer extends JPanel implements RelationshipsViewer, ExplorerManager.Provider, Lookup.Provider {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(MediaViewer.class.getName());
|
||||
|
||||
private final ExplorerManager tableEM = new ExplorerManager();
|
||||
private final PropertyChangeListener focusPropertyListener;
|
||||
|
||||
private final ModifiableProxyLookup proxyLookup;
|
||||
|
||||
@Messages({
|
||||
"MediaViewer_Name=Media"
|
||||
})
|
||||
/**
|
||||
* Creates new form ThumbnailViewer
|
||||
*/
|
||||
public MediaViewer() {
|
||||
proxyLookup = new ModifiableProxyLookup(createLookup(tableEM, getActionMap()));
|
||||
|
||||
// See org.sleuthkit.autopsy.timeline.TimeLineTopComponent for a detailed
|
||||
// explaination of focusPropertyListener
|
||||
focusPropertyListener = (final PropertyChangeEvent focusEvent) -> {
|
||||
if (focusEvent.getPropertyName().equalsIgnoreCase("focusOwner")) {
|
||||
final Component newFocusOwner = (Component) focusEvent.getNewValue();
|
||||
|
||||
if (newFocusOwner == null) {
|
||||
return;
|
||||
}
|
||||
if (isDescendingFrom(newFocusOwner, contentViewer)) {
|
||||
//if the focus owner is within the MessageContentViewer (the attachments table)
|
||||
proxyLookup.setNewLookups(createLookup(((MessageDataContent) contentViewer).getExplorerManager(), getActionMap()));
|
||||
} else if (isDescendingFrom(newFocusOwner, MediaViewer.this)) {
|
||||
//... or if it is within the Results table.
|
||||
proxyLookup.setNewLookups(createLookup(tableEM, getActionMap()));
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
initComponents();
|
||||
|
||||
tableEM.addPropertyChangeListener((PropertyChangeEvent evt) -> {
|
||||
if (evt.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) {
|
||||
handleNodeSelectionChange();
|
||||
}
|
||||
});
|
||||
|
||||
thumbnailViewer.resetComponent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return Bundle.MediaViewer_Name();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JPanel getPanel() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelectionInfo(SelectionInfo info) {
|
||||
final Set<Content> relationshipSources;
|
||||
|
||||
CommunicationsManager communicationManager;
|
||||
Set<BlackboardArtifact> artifactList = new HashSet<>();
|
||||
|
||||
try {
|
||||
communicationManager = Case.getCurrentCaseThrows().getSleuthkitCase().getCommunicationsManager();
|
||||
relationshipSources = communicationManager.getRelationshipSources(info.getAccountDevicesInstances(), info.getCommunicationsFilter());
|
||||
|
||||
relationshipSources.stream().filter((content) -> (content instanceof BlackboardArtifact)).forEachOrdered((content) -> {
|
||||
artifactList.add((BlackboardArtifact) content);
|
||||
});
|
||||
|
||||
} catch (TskCoreException | NoCurrentCaseException ex) {
|
||||
logger.log(Level.WARNING, "Unable to update selection." , ex);
|
||||
}
|
||||
|
||||
if(artifactList.size() == 0) {
|
||||
thumbnailViewer.resetComponent();
|
||||
}
|
||||
|
||||
thumbnailViewer.setNode(new TableFilterNode(new DataResultFilterNode(new AbstractNode(new AttachmentsChildren(artifactList)), tableEM), true, this.getClass().getName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExplorerManager getExplorerManager() {
|
||||
return tableEM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Lookup getLookup() {
|
||||
return proxyLookup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addNotify() {
|
||||
super.addNotify();
|
||||
//add listener that maintains correct selection in the Global Actions Context
|
||||
KeyboardFocusManager.getCurrentKeyboardFocusManager()
|
||||
.addPropertyChangeListener("focusOwner", focusPropertyListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeNotify() {
|
||||
super.removeNotify();
|
||||
KeyboardFocusManager.getCurrentKeyboardFocusManager()
|
||||
.removePropertyChangeListener("focusOwner", focusPropertyListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the change in thumbnail node selection.
|
||||
*/
|
||||
private void handleNodeSelectionChange() {
|
||||
final Node[] nodes = tableEM.getSelectedNodes();
|
||||
|
||||
if (nodes != null && nodes.length == 1) {
|
||||
AbstractContent thumbnail = nodes[0].getLookup().lookup(AbstractContent.class);
|
||||
if (thumbnail != null) {
|
||||
try {
|
||||
Content parentContent = thumbnail.getParent();
|
||||
if (parentContent != null && parentContent instanceof BlackboardArtifact) {
|
||||
contentViewer.setNode(new BlackboardArtifactNode((BlackboardArtifact) parentContent));
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "Unable to get parent Content from AbstraceContent instance.", ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
} else {
|
||||
contentViewer.setNode(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
|
||||
thumbnailViewer = new org.sleuthkit.autopsy.corecomponents.DataResultViewerThumbnail(tableEM);
|
||||
contentViewer = new MessageDataContent();
|
||||
separator = new javax.swing.JSeparator();
|
||||
|
||||
thumbnailViewer.setMinimumSize(new java.awt.Dimension(350, 102));
|
||||
thumbnailViewer.setPreferredSize(new java.awt.Dimension(450, 400));
|
||||
|
||||
contentViewer.setPreferredSize(new java.awt.Dimension(450, 400));
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(thumbnailViewer, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(contentViewer, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(separator)
|
||||
.addContainerGap())
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(thumbnailViewer, javax.swing.GroupLayout.DEFAULT_SIZE, 350, Short.MAX_VALUE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(separator, javax.swing.GroupLayout.PREFERRED_SIZE, 2, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(contentViewer, javax.swing.GroupLayout.PREFERRED_SIZE, 450, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(3, 3, 3))
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private org.sleuthkit.autopsy.contentviewers.MessageContentViewer contentViewer;
|
||||
private javax.swing.JSeparator separator;
|
||||
private org.sleuthkit.autopsy.corecomponents.DataResultViewerThumbnail thumbnailViewer;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
}
|
@ -16,7 +16,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.communications;
|
||||
package org.sleuthkit.autopsy.communications.relationships;
|
||||
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import org.openide.explorer.ExplorerManager;
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2017-2018 Basis Technology Corp.
|
||||
* Copyright 2019 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -16,7 +16,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.communications;
|
||||
package org.sleuthkit.autopsy.communications.relationships;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
@ -25,12 +25,12 @@ import org.apache.commons.lang3.StringUtils;
|
||||
import org.openide.nodes.Sheet;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
|
||||
import org.sleuthkit.autopsy.core.UserPreferences;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode;
|
||||
import org.sleuthkit.autopsy.datamodel.NodeProperty;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
|
||||
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME;
|
||||
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_SENT;
|
||||
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START;
|
||||
@ -43,24 +43,35 @@ import static org.sleuthkit.datamodel.BlackboardAttribute.TSK_BLACKBOARD_ATTRIBU
|
||||
import org.sleuthkit.datamodel.Tag;
|
||||
import org.sleuthkit.datamodel.TimeUtilities;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.autopsy.communications.Utils;
|
||||
|
||||
/**
|
||||
* Node for a relationship, as represented by a BlackboardArtifact.
|
||||
* Wraps a BlackboardArtifact as an AbstractNode for use in an OutlookView
|
||||
*/
|
||||
final class RelationshipNode extends BlackboardArtifactNode {
|
||||
final class MessageNode extends BlackboardArtifactNode {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(RelationshipNode.class.getName());
|
||||
private static final Logger logger = Logger.getLogger(MessageNode.class.getName());
|
||||
|
||||
RelationshipNode(BlackboardArtifact artifact) {
|
||||
MessageNode(BlackboardArtifact artifact) {
|
||||
super(artifact);
|
||||
final String stripEnd = StringUtils.stripEnd(artifact.getDisplayName(), "s");
|
||||
String removeEndIgnoreCase = StringUtils.removeEndIgnoreCase(stripEnd, "message");
|
||||
|
||||
final String stripEnd = StringUtils.stripEnd(artifact.getDisplayName(), "s"); // NON-NLS
|
||||
String removeEndIgnoreCase = StringUtils.removeEndIgnoreCase(stripEnd, "message"); // NON-NLS
|
||||
setDisplayName(removeEndIgnoreCase.isEmpty() ? stripEnd : removeEndIgnoreCase);
|
||||
}
|
||||
|
||||
@Messages({
|
||||
"MessageNode_Node_Property_Type=Type",
|
||||
"MessageNode_Node_Property_From=From",
|
||||
"MessageNode_Node_Property_To=To",
|
||||
"MessageNode_Node_Property_Date=Date",
|
||||
"MessageNode_Node_Property_Subject=Subject",
|
||||
"MessageNode_Node_Property_Attms=Attachments"
|
||||
})
|
||||
|
||||
@Override
|
||||
protected Sheet createSheet() {
|
||||
Sheet sheet = new Sheet();
|
||||
Sheet sheet = super.createSheet();
|
||||
List<Tag> tags = getAllTagsFromDatabase();
|
||||
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
|
||||
if (sheetSet == null) {
|
||||
@ -68,62 +79,63 @@ final class RelationshipNode extends BlackboardArtifactNode {
|
||||
sheet.put(sheetSet);
|
||||
}
|
||||
|
||||
sheetSet.put(new NodeProperty<>("Type", "Type", "Type", getDisplayName()));
|
||||
|
||||
sheetSet.put(new NodeProperty<>("Type", Bundle.MessageNode_Node_Property_Type(), "", getDisplayName())); //NON-NLS
|
||||
|
||||
addScoreProperty(sheetSet, tags);
|
||||
|
||||
|
||||
CorrelationAttributeInstance correlationAttribute = null;
|
||||
if (UserPreferences.hideCentralRepoCommentsAndOccurrences()== false) {
|
||||
if (UserPreferences.hideCentralRepoCommentsAndOccurrences() == false) {
|
||||
correlationAttribute = getCorrelationAttributeInstance();
|
||||
}
|
||||
addCommentProperty(sheetSet, tags, correlationAttribute);
|
||||
|
||||
if (UserPreferences.hideCentralRepoCommentsAndOccurrences()== false) {
|
||||
|
||||
if (UserPreferences.hideCentralRepoCommentsAndOccurrences() == false) {
|
||||
addCountProperty(sheetSet, correlationAttribute);
|
||||
}
|
||||
final BlackboardArtifact artifact = getArtifact();
|
||||
BlackboardArtifact.ARTIFACT_TYPE fromID = BlackboardArtifact.ARTIFACT_TYPE.fromID(getArtifact().getArtifactTypeID());
|
||||
|
||||
BlackboardArtifact.ARTIFACT_TYPE fromID = BlackboardArtifact.ARTIFACT_TYPE.fromID(artifact.getArtifactTypeID());
|
||||
if (null != fromID) {
|
||||
//Consider refactoring this to reduce boilerplate
|
||||
switch (fromID) {
|
||||
case TSK_EMAIL_MSG:
|
||||
sheetSet.put(new NodeProperty<>("From", "From", "From",
|
||||
StringUtils.strip(getAttributeDisplayString(artifact, TSK_EMAIL_FROM), " \t\n;")));
|
||||
sheetSet.put(new NodeProperty<>("To", "To", "To",
|
||||
StringUtils.strip(getAttributeDisplayString(artifact, TSK_EMAIL_TO), " \t\n;")));
|
||||
sheetSet.put(new NodeProperty<>("Date", "Date", "Date",
|
||||
getAttributeDisplayString(artifact, TSK_DATETIME_SENT)));
|
||||
sheetSet.put(new NodeProperty<>("Subject", "Subject", "Subject",
|
||||
getAttributeDisplayString(artifact, TSK_SUBJECT)));
|
||||
sheetSet.put(new NodeProperty<>("From", Bundle.MessageNode_Node_Property_From(), "",
|
||||
StringUtils.strip(getAttributeDisplayString(artifact, TSK_EMAIL_FROM), " \t\n;"))); //NON-NLS
|
||||
sheetSet.put(new NodeProperty<>("To", Bundle.MessageNode_Node_Property_To(), "",
|
||||
StringUtils.strip(getAttributeDisplayString(artifact, TSK_EMAIL_TO), " \t\n;"))); //NON-NLS
|
||||
sheetSet.put(new NodeProperty<>("Date", Bundle.MessageNode_Node_Property_Date(), "",
|
||||
getAttributeDisplayString(artifact, TSK_DATETIME_SENT))); //NON-NLS
|
||||
sheetSet.put(new NodeProperty<>("Subject", Bundle.MessageNode_Node_Property_Subject(), "",
|
||||
getAttributeDisplayString(artifact, TSK_SUBJECT))); //NON-NLS
|
||||
try {
|
||||
sheetSet.put(new NodeProperty<>("Attms", "Attms", "Attms", artifact.getChildrenCount()));
|
||||
sheetSet.put(new NodeProperty<>("Attms", Bundle.MessageNode_Node_Property_Attms(), "", artifact.getChildrenCount())); //NON-NLS
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "Error loading attachment count for " + artifact, ex);
|
||||
logger.log(Level.WARNING, "Error loading attachment count for " + artifact, ex); //NON-NLS
|
||||
}
|
||||
|
||||
break;
|
||||
case TSK_MESSAGE:
|
||||
sheetSet.put(new NodeProperty<>("From", "From", "From",
|
||||
getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_FROM)));
|
||||
sheetSet.put(new NodeProperty<>("To", "To", "To",
|
||||
getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_TO)));
|
||||
sheetSet.put(new NodeProperty<>("Date", "Date", "Date",
|
||||
getAttributeDisplayString(artifact, TSK_DATETIME)));
|
||||
sheetSet.put(new NodeProperty<>("Subject", "Subject", "Subject",
|
||||
getAttributeDisplayString(artifact, TSK_SUBJECT)));
|
||||
sheetSet.put(new NodeProperty<>("From", Bundle.MessageNode_Node_Property_From(), "",
|
||||
getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_FROM))); //NON-NLS
|
||||
sheetSet.put(new NodeProperty<>("To", Bundle.MessageNode_Node_Property_To(), "",
|
||||
getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_TO))); //NON-NLS
|
||||
sheetSet.put(new NodeProperty<>("Date", Bundle.MessageNode_Node_Property_Date(), "",
|
||||
getAttributeDisplayString(artifact, TSK_DATETIME))); //NON-NLS
|
||||
sheetSet.put(new NodeProperty<>("Subject", Bundle.MessageNode_Node_Property_Subject(), "",
|
||||
getAttributeDisplayString(artifact, TSK_SUBJECT))); //NON-NLS
|
||||
try {
|
||||
sheetSet.put(new NodeProperty<>("Attms", "Attms", "Attms", artifact.getChildrenCount()));
|
||||
sheetSet.put(new NodeProperty<>("Attms", Bundle.MessageNode_Node_Property_Attms(), "", artifact.getChildrenCount())); //NON-NLS
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "Error loading attachment count for " + artifact, ex);
|
||||
logger.log(Level.WARNING, "Error loading attachment count for " + artifact, ex); //NON-NLS
|
||||
}
|
||||
break;
|
||||
case TSK_CALLLOG:
|
||||
sheetSet.put(new NodeProperty<>("From", "From", "From",
|
||||
getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_FROM)));
|
||||
sheetSet.put(new NodeProperty<>("To", "To", "To",
|
||||
getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_TO)));
|
||||
sheetSet.put(new NodeProperty<>("Date", "Date", "Date",
|
||||
getAttributeDisplayString(artifact, TSK_DATETIME_START)));
|
||||
sheetSet.put(new NodeProperty<>("From", Bundle.MessageNode_Node_Property_From(), "",
|
||||
getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_FROM))); //NON-NLS
|
||||
sheetSet.put(new NodeProperty<>("To", Bundle.MessageNode_Node_Property_To(), "",
|
||||
getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_TO))); //NON-NLS
|
||||
sheetSet.put(new NodeProperty<>("Date", Bundle.MessageNode_Node_Property_Date(), "",
|
||||
getAttributeDisplayString(artifact, TSK_DATETIME_START))); //NON-NLS
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -144,7 +156,7 @@ final class RelationshipNode extends BlackboardArtifactNode {
|
||||
* @return The display string, or an empty string if there is no such
|
||||
* attribute or an an error.
|
||||
*/
|
||||
private static String getAttributeDisplayString(final BlackboardArtifact artifact, final ATTRIBUTE_TYPE attributeType) {
|
||||
private static String getAttributeDisplayString(final BlackboardArtifact artifact, final BlackboardAttribute.ATTRIBUTE_TYPE attributeType) {
|
||||
try {
|
||||
BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.fromID(attributeType.getTypeID())));
|
||||
if (attribute == null) {
|
||||
@ -156,7 +168,7 @@ final class RelationshipNode extends BlackboardArtifactNode {
|
||||
return attribute.getDisplayString();
|
||||
}
|
||||
} catch (TskCoreException tskCoreException) {
|
||||
logger.log(Level.WARNING, "Error getting attribute value.", tskCoreException);
|
||||
logger.log(Level.WARNING, "Error getting attribute value.", tskCoreException); //NON-NLS
|
||||
return "";
|
||||
}
|
||||
}
|
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* 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.communications.relationships;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import org.openide.nodes.ChildFactory;
|
||||
import org.openide.nodes.Node;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.CommunicationsManager;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* ChildFactory that creates createKeys and nodes from a given selectionInfo for
|
||||
* only emails, call logs and messages.
|
||||
*
|
||||
*/
|
||||
final class MessagesChildNodeFactory extends ChildFactory<BlackboardArtifact> {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(MessagesChildNodeFactory.class.getName());
|
||||
|
||||
private SelectionInfo selectionInfo;
|
||||
|
||||
/**
|
||||
* Construct a new MessageChildNodeFactory from the currently selectionInfo
|
||||
*
|
||||
* @param selectionInfo SelectionInfo object for the currently selected
|
||||
* accounts
|
||||
*/
|
||||
MessagesChildNodeFactory(SelectionInfo selectionInfo) {
|
||||
this.selectionInfo = selectionInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the current instance of selectionInfo and calls the refresh method.
|
||||
*
|
||||
* @param selectionInfo New instance of the currently selected accounts
|
||||
*/
|
||||
public void refresh(SelectionInfo selectionInfo) {
|
||||
this.selectionInfo = selectionInfo;
|
||||
refresh(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a list of Keys (BlackboardArtifact) for only messages for the
|
||||
* currently selected accounts
|
||||
*
|
||||
* @param list List of BlackboardArtifact to populate
|
||||
*
|
||||
* @return True on success
|
||||
*/
|
||||
@Override
|
||||
protected boolean createKeys(List<BlackboardArtifact> list) {
|
||||
CommunicationsManager communicationManager;
|
||||
try {
|
||||
communicationManager = Case.getCurrentCaseThrows().getSleuthkitCase().getCommunicationsManager();
|
||||
} catch (NoCurrentCaseException | TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Failed to get communications manager from case.", ex); //NON-NLS
|
||||
return false;
|
||||
}
|
||||
|
||||
if(selectionInfo == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final Set<Content> relationshipSources;
|
||||
|
||||
try {
|
||||
relationshipSources = communicationManager.getRelationshipSources(selectionInfo.getAccountDevicesInstances(), selectionInfo.getCommunicationsFilter());
|
||||
|
||||
relationshipSources.stream().filter((content) -> (content instanceof BlackboardArtifact)).forEachOrdered((content) -> {
|
||||
|
||||
BlackboardArtifact bba = (BlackboardArtifact) content;
|
||||
BlackboardArtifact.ARTIFACT_TYPE fromID = BlackboardArtifact.ARTIFACT_TYPE.fromID(bba.getArtifactTypeID());
|
||||
|
||||
if (fromID == BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG
|
||||
|| fromID == BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG
|
||||
|| fromID == BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE) {
|
||||
list.add(bba);
|
||||
}
|
||||
});
|
||||
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Failed to get relationship sources.", ex); //NON-NLS
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node createNodeForKey(BlackboardArtifact key) {
|
||||
return new MessageNode(key);
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.4" 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">
|
||||
<Component id="outlineView" alignment="0" max="32767" attributes="0"/>
|
||||
<Component id="contentViewer" alignment="1" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="outlineView" pref="300" max="32767" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="contentViewer" pref="500" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="org.openide.explorer.view.OutlineView" name="outlineView">
|
||||
</Component>
|
||||
<Component class="org.sleuthkit.autopsy.contentviewers.MessageContentViewer" name="contentViewer">
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new MessageDataContent()"/>
|
||||
</AuxValues>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
194
Core/src/org/sleuthkit/autopsy/communications/relationships/MessagesViewer.java
Executable file
194
Core/src/org/sleuthkit/autopsy/communications/relationships/MessagesViewer.java
Executable file
@ -0,0 +1,194 @@
|
||||
/*
|
||||
* 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 obt ain 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.communications.relationships;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.KeyboardFocusManager;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import static javax.swing.SwingUtilities.isDescendingFrom;
|
||||
import org.netbeans.swing.outline.DefaultOutlineModel;
|
||||
import org.netbeans.swing.outline.Outline;
|
||||
import org.openide.explorer.ExplorerManager;
|
||||
import static org.openide.explorer.ExplorerUtils.createLookup;
|
||||
import org.openide.nodes.AbstractNode;
|
||||
import org.openide.nodes.Children;
|
||||
import org.openide.nodes.Node;
|
||||
import org.openide.util.Lookup;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.openide.util.lookup.ServiceProvider;
|
||||
import org.sleuthkit.autopsy.communications.ModifiableProxyLookup;
|
||||
import org.sleuthkit.autopsy.corecomponents.TableFilterNode;
|
||||
import org.sleuthkit.autopsy.directorytree.DataResultFilterNode;
|
||||
|
||||
/**
|
||||
* Visualation for the messages of the currently selected accounts.
|
||||
*/
|
||||
@ServiceProvider(service = RelationshipsViewer.class)
|
||||
public final class MessagesViewer extends JPanel implements RelationshipsViewer, ExplorerManager.Provider, Lookup.Provider {
|
||||
|
||||
private final ExplorerManager tableEM;
|
||||
private final Outline outline;
|
||||
private final ModifiableProxyLookup proxyLookup;
|
||||
private final PropertyChangeListener focusPropertyListener;
|
||||
private final MessagesChildNodeFactory nodeFactory;
|
||||
|
||||
@Messages({
|
||||
"MessageViewer_tabTitle=Messages",
|
||||
"MessageViewer_columnHeader_From=From",
|
||||
"MessageViewer_columnHeader_To=To",
|
||||
"MessageViewer_columnHeader_Date=Date",
|
||||
"MessageViewer_columnHeader_Subject=Subject",
|
||||
"MessageViewer_columnHeader_Attms=Attachments"
|
||||
})
|
||||
|
||||
/**
|
||||
* Visualation for the messages of the currently selected accounts.
|
||||
*/
|
||||
public MessagesViewer() {
|
||||
tableEM = new ExplorerManager();
|
||||
proxyLookup = new ModifiableProxyLookup(createLookup(tableEM, getActionMap()));
|
||||
nodeFactory = new MessagesChildNodeFactory(null);
|
||||
|
||||
// See org.sleuthkit.autopsy.timeline.TimeLineTopComponent for a detailed
|
||||
// explaination of focusPropertyListener
|
||||
focusPropertyListener = (final PropertyChangeEvent focusEvent) -> {
|
||||
if (focusEvent.getPropertyName().equalsIgnoreCase("focusOwner")) {
|
||||
final Component newFocusOwner = (Component) focusEvent.getNewValue();
|
||||
|
||||
if (newFocusOwner == null) {
|
||||
return;
|
||||
}
|
||||
if (isDescendingFrom(newFocusOwner, contentViewer)) {
|
||||
//if the focus owner is within the MessageContentViewer (the attachments table)
|
||||
proxyLookup.setNewLookups(createLookup(((MessageDataContent) contentViewer).getExplorerManager(), getActionMap()));
|
||||
} else if (isDescendingFrom(newFocusOwner, MessagesViewer.this)) {
|
||||
//... or if it is within the Results table.
|
||||
proxyLookup.setNewLookups(createLookup(tableEM, getActionMap()));
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
initComponents();
|
||||
|
||||
outline = outlineView.getOutline();
|
||||
outlineView.setPropertyColumns(
|
||||
"From", Bundle.MessageViewer_columnHeader_From(),
|
||||
"To", Bundle.MessageViewer_columnHeader_To(),
|
||||
"Date", Bundle.MessageViewer_columnHeader_Date(),
|
||||
"Subject", Bundle.MessageViewer_columnHeader_Subject(),
|
||||
"Attms", Bundle.MessageViewer_columnHeader_Attms(),
|
||||
"Type", "Type"
|
||||
);
|
||||
outline.setRootVisible(false);
|
||||
((DefaultOutlineModel) outline.getOutlineModel()).setNodesColumnLabel("Type");
|
||||
|
||||
tableEM.addPropertyChangeListener((PropertyChangeEvent evt) -> {
|
||||
if (evt.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) {
|
||||
final Node[] nodes = tableEM.getSelectedNodes();
|
||||
|
||||
if (nodes != null && nodes.length == 1) {
|
||||
contentViewer.setNode(nodes[0]);
|
||||
}
|
||||
else {
|
||||
contentViewer.setNode(null);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
tableEM.setRootContext(new TableFilterNode(new DataResultFilterNode(new AbstractNode(Children.create(nodeFactory, true)), getExplorerManager()), true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return Bundle.MessageViewer_tabTitle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JPanel getPanel() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelectionInfo(SelectionInfo info) {
|
||||
nodeFactory.refresh(info);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExplorerManager getExplorerManager() {
|
||||
return tableEM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Lookup getLookup() {
|
||||
return proxyLookup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addNotify() {
|
||||
super.addNotify();
|
||||
//add listener that maintains correct selection in the Global Actions Context
|
||||
KeyboardFocusManager.getCurrentKeyboardFocusManager()
|
||||
.addPropertyChangeListener("focusOwner", focusPropertyListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeNotify() {
|
||||
super.removeNotify();
|
||||
KeyboardFocusManager.getCurrentKeyboardFocusManager()
|
||||
.removePropertyChangeListener("focusOwner", focusPropertyListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
|
||||
outlineView = new org.openide.explorer.view.OutlineView();
|
||||
contentViewer = new MessageDataContent();
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(contentViewer, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(contentViewer, javax.swing.GroupLayout.DEFAULT_SIZE, 500, Short.MAX_VALUE))
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private org.sleuthkit.autopsy.contentviewers.MessageContentViewer contentViewer;
|
||||
private org.openide.explorer.view.OutlineView outlineView;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
<?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"/>
|
||||
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,-45,0,0,1,-32"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignCardLayout">
|
||||
<Property name="horizontalGap" type="int" value="5"/>
|
||||
<Property name="verticalGap" type="int" value="5"/>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="org.openide.explorer.view.OutlineView" name="outlineView">
|
||||
<Properties>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[300, 400]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignCardLayout" value="org.netbeans.modules.form.compat2.layouts.DesignCardLayout$CardConstraintsDescription">
|
||||
<CardConstraints cardName="outlineCard"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
<Container class="javax.swing.JPanel" name="messagePanel">
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignCardLayout" value="org.netbeans.modules.form.compat2.layouts.DesignCardLayout$CardConstraintsDescription">
|
||||
<CardConstraints cardName="messageCard"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JLabel" name="messageLabel">
|
||||
<Properties>
|
||||
<Property name="horizontalAlignment" type="int" value="0"/>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/relationships/Bundle.properties" key="OutlineViewPanel.messageLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="enabled" type="boolean" value="false"/>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
|
||||
<BorderConstraints direction="Center"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Form>
|
@ -0,0 +1,132 @@
|
||||
/*
|
||||
* 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 obt ain 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.communications.relationships;
|
||||
|
||||
import java.awt.CardLayout;
|
||||
import org.openide.explorer.ExplorerManager;
|
||||
import static org.openide.explorer.ExplorerUtils.createLookup;
|
||||
import org.openide.explorer.view.OutlineView;
|
||||
import org.openide.nodes.Node;;
|
||||
import org.openide.util.Lookup;
|
||||
|
||||
/**
|
||||
* This class is a simple wrapper around a OutlineView with its own ExplorerManager.
|
||||
*
|
||||
* This panel has the added feature of being able to hide the OutlineView and show
|
||||
* a message.
|
||||
*
|
||||
*/
|
||||
public class OutlineViewPanel extends javax.swing.JPanel implements ExplorerManager.Provider, Lookup.Provider{
|
||||
|
||||
private final ExplorerManager tableEm;
|
||||
private final Lookup lookup;
|
||||
/**
|
||||
* Creates new form OutlineViewPanel
|
||||
*/
|
||||
public OutlineViewPanel() {
|
||||
tableEm = new ExplorerManager();
|
||||
lookup = createLookup(tableEm, getActionMap());
|
||||
|
||||
initComponents();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExplorerManager getExplorerManager() {
|
||||
return tableEm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Lookup getLookup() {
|
||||
return lookup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the OutlineView and replace with a panel with the given message.
|
||||
*
|
||||
* @param message String message to show on the panel.
|
||||
*/
|
||||
public void hideOutlineView(String message) {
|
||||
CardLayout layout = (CardLayout)this.getLayout();
|
||||
layout.show(this, "messageCard"); //NON-NLS
|
||||
messageLabel.setText(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides the message panel and shows the OutlineView.
|
||||
*/
|
||||
public void showOutlineView() {
|
||||
CardLayout layout = (CardLayout)this.getLayout();
|
||||
layout.show(this, "outlineCard"); //NON-NLS
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the OutlineView instance for ease of customization.
|
||||
*
|
||||
* @return Returns the OutlineView
|
||||
*/
|
||||
public OutlineView getOutlineView() {
|
||||
return outlineView;
|
||||
}
|
||||
|
||||
public void setNode(Node node) {
|
||||
tableEm.setRootContext(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
super.setEnabled(enabled);
|
||||
outlineView.setEnabled(enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
|
||||
outlineView = new org.openide.explorer.view.OutlineView();
|
||||
messagePanel = new javax.swing.JPanel();
|
||||
messageLabel = new javax.swing.JLabel();
|
||||
|
||||
setLayout(new java.awt.CardLayout(5, 5));
|
||||
|
||||
outlineView.setPreferredSize(new java.awt.Dimension(300, 400));
|
||||
add(outlineView, "outlineCard");
|
||||
|
||||
messagePanel.setLayout(new java.awt.BorderLayout());
|
||||
|
||||
messageLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
|
||||
org.openide.awt.Mnemonics.setLocalizedText(messageLabel, org.openide.util.NbBundle.getMessage(OutlineViewPanel.class, "OutlineViewPanel.messageLabel.text")); // NOI18N
|
||||
messageLabel.setEnabled(false);
|
||||
messagePanel.add(messageLabel, java.awt.BorderLayout.CENTER);
|
||||
|
||||
add(messagePanel, "messageCard");
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JLabel messageLabel;
|
||||
private javax.swing.JPanel messagePanel;
|
||||
private org.openide.explorer.view.OutlineView outlineView;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
}
|
@ -5,7 +5,7 @@
|
||||
<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="false"/>
|
||||
<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"/>
|
||||
@ -16,40 +16,36 @@
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<EmptySpace min="-2" pref="0" max="-2" attributes="0"/>
|
||||
<Component id="splitPane" max="32767" attributes="0"/>
|
||||
<EmptySpace min="0" pref="400" max="32767" attributes="0"/>
|
||||
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
|
||||
<Component id="scrollPane" alignment="1" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace min="-2" pref="0" max="-2" attributes="0"/>
|
||||
<Component id="splitPane" pref="1083" max="32767" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="0" max="-2" attributes="0"/>
|
||||
<EmptySpace min="0" pref="300" max="32767" attributes="0"/>
|
||||
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
|
||||
<Component id="scrollPane" alignment="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JSplitPane" name="splitPane">
|
||||
<Container class="javax.swing.JScrollPane" name="scrollPane">
|
||||
<Properties>
|
||||
<Property name="dividerLocation" type="int" value="400"/>
|
||||
<Property name="dividerSize" type="int" value="10"/>
|
||||
<Property name="orientation" type="int" value="0"/>
|
||||
<Property name="resizeWeight" type="double" value="0.5"/>
|
||||
<Property name="verticalScrollBarPolicy" type="int" value="21"/>
|
||||
</Properties>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="org.sleuthkit.autopsy.communications.MessageDataContent" name="messageDataContent">
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
|
||||
<JSplitPaneConstraints position="bottom"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
</Component>
|
||||
<Container class="javax.swing.JTabbedPane" name="tabPane">
|
||||
<Events>
|
||||
<EventHandler event="stateChanged" listener="javax.swing.event.ChangeListener" parameters="javax.swing.event.ChangeEvent" handler="tabPaneStateChanged"/>
|
||||
</Events>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout"/>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
@ -0,0 +1,132 @@
|
||||
/*
|
||||
* 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 obt ain 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.communications.relationships;
|
||||
|
||||
import java.awt.Component;
|
||||
import javax.swing.JPanel;
|
||||
import org.openide.util.Lookup;
|
||||
import org.sleuthkit.autopsy.communications.ModifiableProxyLookup;
|
||||
|
||||
/**
|
||||
* Displays the Relationship information for the currently selected accounts.
|
||||
*
|
||||
*/
|
||||
public final class RelationshipBrowser extends JPanel implements Lookup.Provider {
|
||||
|
||||
private SelectionInfo currentSelection;
|
||||
|
||||
private final MessagesViewer messagesViewer;
|
||||
private final ContactsViewer contactsViewer;
|
||||
private final SummaryViewer summaryViewer;
|
||||
private final MediaViewer mediaViewer;
|
||||
|
||||
private final ModifiableProxyLookup proxyLookup;
|
||||
|
||||
/**
|
||||
* Creates new form RelationshipBrowser
|
||||
*/
|
||||
public RelationshipBrowser() {
|
||||
messagesViewer = new MessagesViewer();
|
||||
contactsViewer = new ContactsViewer();
|
||||
summaryViewer = new SummaryViewer();
|
||||
mediaViewer = new MediaViewer();
|
||||
|
||||
proxyLookup = new ModifiableProxyLookup(messagesViewer.getLookup());
|
||||
|
||||
initComponents();
|
||||
|
||||
tabPane.add(summaryViewer.getDisplayName(), summaryViewer);
|
||||
tabPane.add(messagesViewer.getDisplayName(), messagesViewer);
|
||||
tabPane.add(contactsViewer.getDisplayName(), contactsViewer);
|
||||
tabPane.add(mediaViewer.getDisplayName(), mediaViewer);
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of currentSelection and passes the SelectionInfo onto the
|
||||
* currently selected\visible tab.
|
||||
*
|
||||
* @param info Currently selected account nodes
|
||||
*/
|
||||
public void setSelectionInfo(SelectionInfo info) {
|
||||
currentSelection = info;
|
||||
((RelationshipsViewer) tabPane.getSelectedComponent()).setSelectionInfo(info);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
|
||||
scrollPane = new javax.swing.JScrollPane();
|
||||
tabPane = new javax.swing.JTabbedPane();
|
||||
|
||||
scrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
|
||||
|
||||
tabPane.addChangeListener(new javax.swing.event.ChangeListener() {
|
||||
public void stateChanged(javax.swing.event.ChangeEvent evt) {
|
||||
tabPaneStateChanged(evt);
|
||||
}
|
||||
});
|
||||
scrollPane.setViewportView(tabPane);
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 400, Short.MAX_VALUE)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(scrollPane, javax.swing.GroupLayout.Alignment.TRAILING))
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 300, Short.MAX_VALUE)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(scrollPane))
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
private void tabPaneStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_tabPaneStateChanged
|
||||
if(currentSelection != null) {
|
||||
((RelationshipsViewer) tabPane.getSelectedComponent()).setSelectionInfo(currentSelection);
|
||||
}
|
||||
|
||||
Component selectedComponent = tabPane.getSelectedComponent();
|
||||
if(selectedComponent instanceof Lookup.Provider) {
|
||||
Lookup lookup = ((Lookup.Provider)selectedComponent).getLookup();
|
||||
proxyLookup.setNewLookups(lookup);
|
||||
}
|
||||
}//GEN-LAST:event_tabPaneStateChanged
|
||||
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JScrollPane scrollPane;
|
||||
private javax.swing.JTabbedPane tabPane;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
@Override
|
||||
public Lookup getLookup() {
|
||||
return proxyLookup;
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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 obt ain 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.communications.relationships;
|
||||
|
||||
import javax.swing.JPanel;
|
||||
import org.openide.util.Lookup;
|
||||
|
||||
/**
|
||||
* Interface for Controls wishing to appear in the RelationshipBrowser tabPane.
|
||||
*/
|
||||
public interface RelationshipsViewer extends Lookup.Provider {
|
||||
|
||||
/**
|
||||
* Returns the value to be displayed on the "tab"
|
||||
*
|
||||
* @return String display name
|
||||
*/
|
||||
public String getDisplayName();
|
||||
|
||||
/**
|
||||
* Returns the JPanel to be displayed in the RelationshipBrowser.
|
||||
*
|
||||
* @return JPanel to be displayed
|
||||
*/
|
||||
public JPanel getPanel();
|
||||
|
||||
/**
|
||||
* Sets current SelectionInfo allowing the panel to update accordingly.
|
||||
*
|
||||
* @param info SelectionInfo instance representing the currently selected
|
||||
* accounts
|
||||
*/
|
||||
public void setSelectionInfo(SelectionInfo info);
|
||||
}
|
185
Core/src/org/sleuthkit/autopsy/communications/relationships/SelectionInfo.java
Executable file
185
Core/src/org/sleuthkit/autopsy/communications/relationships/SelectionInfo.java
Executable file
@ -0,0 +1,185 @@
|
||||
/*
|
||||
* 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 obt ain 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.communications.relationships;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.datamodel.Account;
|
||||
import org.sleuthkit.datamodel.AccountDeviceInstance;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.CommunicationsFilter;
|
||||
import org.sleuthkit.datamodel.CommunicationsManager;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* Class to wrap the details of the current selection from the AccountBrowser or
|
||||
* VisualizationPane
|
||||
*/
|
||||
public final class SelectionInfo {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(SelectionInfo.class.getName());
|
||||
|
||||
private final Set<AccountDeviceInstance> accountDeviceInstances;
|
||||
private final CommunicationsFilter communicationFilter;
|
||||
private final Set<Account> accounts;
|
||||
|
||||
private Set<BlackboardArtifact> accountArtifacts = null;
|
||||
private SelectionSummary summary = null;
|
||||
|
||||
/**
|
||||
* Wraps the details of the currently selected accounts.
|
||||
*
|
||||
* @param accountDeviceInstances Selected accountDecivedInstances
|
||||
* @param communicationFilter Currently selected communications filters
|
||||
*/
|
||||
public SelectionInfo(Set<AccountDeviceInstance> accountDeviceInstances, CommunicationsFilter communicationFilter) {
|
||||
this.accountDeviceInstances = accountDeviceInstances;
|
||||
this.communicationFilter = communicationFilter;
|
||||
|
||||
accounts = new HashSet<>();
|
||||
accountDeviceInstances.forEach((instance) -> {
|
||||
accounts.add(instance.getAccount());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currently selected accountDeviceInstances
|
||||
*
|
||||
* @return Set of AccountDeviceInstance
|
||||
*/
|
||||
public Set<AccountDeviceInstance> getAccountDevicesInstances() {
|
||||
return accountDeviceInstances;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currently selected communications filters.
|
||||
*
|
||||
* @return Instance of CommunicationsFilter
|
||||
*/
|
||||
public CommunicationsFilter getCommunicationsFilter() {
|
||||
return communicationFilter;
|
||||
}
|
||||
|
||||
public Set<Account> getAccounts() {
|
||||
return accounts;
|
||||
}
|
||||
|
||||
public Set<BlackboardArtifact> getArtifacts() {
|
||||
if(accountArtifacts == null) {
|
||||
accountArtifacts = new HashSet<>();
|
||||
CommunicationsManager communicationManager;
|
||||
try {
|
||||
communicationManager = Case.getCurrentCaseThrows().getSleuthkitCase().getCommunicationsManager();
|
||||
} catch (NoCurrentCaseException | TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Failed to get communications manager from case.", ex); //NON-NLS
|
||||
return null;
|
||||
}
|
||||
|
||||
final Set<Content> relationshipSources;
|
||||
|
||||
try {
|
||||
relationshipSources = communicationManager.getRelationshipSources(getAccountDevicesInstances(), getCommunicationsFilter());
|
||||
|
||||
relationshipSources.stream().filter((content) -> (content instanceof BlackboardArtifact)).forEachOrdered((content) -> {
|
||||
accountArtifacts.add((BlackboardArtifact) content);
|
||||
});
|
||||
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Failed to get relationship sources.", ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
return accountArtifacts;
|
||||
}
|
||||
|
||||
public SelectionSummary getSummary() {
|
||||
if(summary == null) {
|
||||
summary = new SelectionSummary();
|
||||
}
|
||||
|
||||
return summary;
|
||||
}
|
||||
|
||||
final class SelectionSummary{
|
||||
int attachmentCnt;
|
||||
int messagesCnt;
|
||||
int emailCnt;
|
||||
int callLogCnt;
|
||||
int contactsCnt;
|
||||
|
||||
SelectionSummary() {
|
||||
getCounts();
|
||||
}
|
||||
|
||||
private void getCounts(){
|
||||
for(BlackboardArtifact artifact: getArtifacts()) {
|
||||
BlackboardArtifact.ARTIFACT_TYPE fromID = BlackboardArtifact.ARTIFACT_TYPE.fromID(artifact.getArtifactTypeID());
|
||||
if(null != fromID) switch (fromID) {
|
||||
case TSK_EMAIL_MSG:
|
||||
emailCnt++;
|
||||
break;
|
||||
case TSK_CALLLOG:
|
||||
callLogCnt++;
|
||||
break;
|
||||
case TSK_MESSAGE:
|
||||
messagesCnt++;
|
||||
break;
|
||||
case TSK_CONTACT:
|
||||
contactsCnt++;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
try{
|
||||
attachmentCnt+= artifact.getChildrenCount();
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.WARNING, String.format("Exception thrown "
|
||||
+ "from getChildrenCount artifactID: %d",
|
||||
artifact.getArtifactID()), ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getAttachmentCnt() {
|
||||
return attachmentCnt;
|
||||
}
|
||||
|
||||
public int getMessagesCnt() {
|
||||
return messagesCnt;
|
||||
}
|
||||
|
||||
public int getEmailCnt() {
|
||||
return emailCnt;
|
||||
}
|
||||
|
||||
public int getCallLogCnt() {
|
||||
return callLogCnt;
|
||||
}
|
||||
|
||||
public int getContactsCnt() {
|
||||
return contactsCnt;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
215
Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryViewer.form
Executable file
215
Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryViewer.form
Executable file
@ -0,0 +1,215 @@
|
||||
<?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">
|
||||
<Component id="countsPanel" max="32767" attributes="0"/>
|
||||
<Component id="fileReferencesPanel" alignment="0" pref="485" max="32767" attributes="0"/>
|
||||
<Component id="caseReferencesPanel" alignment="1" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</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="countsPanel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="fileReferencesPanel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="caseReferencesPanel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JPanel" name="countsPanel">
|
||||
<Properties>
|
||||
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
|
||||
<Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo">
|
||||
<TitledBorder title="Counts">
|
||||
<ResourceString PropertyName="titleX" bundle="org/sleuthkit/autopsy/communications/relationships/Bundle.properties" key="SummaryViewer.countsPanel.border.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</TitledBorder>
|
||||
</Border>
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
<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">
|
||||
<Component id="attachmentsLabel" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="messagesLabel" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="callLogsLabel" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="contactsLabel" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="emailLabel" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace type="separate" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="emailDataLabel" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="contactsDataLabel" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="callLogsDataLabel" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="messagesDataLabel" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="attachmentsDataLabel" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace min="-2" pref="7" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="attachmentsLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="attachmentsDataLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="messagesLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="messagesDataLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="callLogsLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="callLogsDataLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="contactsLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="contactsDataLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="emailLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="emailDataLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JLabel" name="emailLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/relationships/Bundle.properties" key="SummaryViewer.emailLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="contactsLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="SummaryViewer.contactsLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="messagesLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/relationships/Bundle.properties" key="SummaryViewer.messagesLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="callLogsLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/relationships/Bundle.properties" key="SummaryViewer.callLogsLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="attachmentsLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/relationships/Bundle.properties" key="SummaryViewer.attachmentsLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="attachmentsDataLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/relationships/Bundle.properties" key="SummaryViewer.attachmentsDataLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="messagesDataLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/relationships/Bundle.properties" key="SummaryViewer.messagesDataLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="callLogsDataLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/relationships/Bundle.properties" key="SummaryViewer.callLogsDataLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="contactsDataLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/relationships/Bundle.properties" key="SummaryViewer.contactsDataLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="emailDataLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/communications/relationships/Bundle.properties" key="SummaryViewer.emailDataLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Component class="org.sleuthkit.autopsy.communications.OutlineViewPanel" name="fileReferencesPanel">
|
||||
<Properties>
|
||||
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
|
||||
<Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo">
|
||||
<TitledBorder title="File References in Current Case">
|
||||
<ResourceString PropertyName="titleX" bundle="org/sleuthkit/autopsy/communications/relationships/Bundle.properties" key="SummaryViewer.fileReferencesPanel.border.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</TitledBorder>
|
||||
</Border>
|
||||
</Property>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[472, 300]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="org.sleuthkit.autopsy.communications.OutlineViewPanel" name="caseReferencesPanel">
|
||||
<Properties>
|
||||
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
|
||||
<Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo">
|
||||
<TitledBorder title="Other Occurrences">
|
||||
<ResourceString PropertyName="titleX" bundle="org/sleuthkit/autopsy/communications/relationships/Bundle.properties" key="SummaryViewer.caseReferencesPanel.border.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</TitledBorder>
|
||||
</Border>
|
||||
</Property>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[472, 300]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
313
Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryViewer.java
Executable file
313
Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryViewer.java
Executable file
@ -0,0 +1,313 @@
|
||||
/*
|
||||
* 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 obt ain 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.communications.relationships;
|
||||
|
||||
import java.util.Set;
|
||||
import javax.swing.JPanel;
|
||||
import org.netbeans.swing.outline.DefaultOutlineModel;
|
||||
import org.netbeans.swing.outline.Outline;
|
||||
import org.openide.explorer.view.OutlineView;
|
||||
import org.openide.nodes.AbstractNode;
|
||||
import org.openide.nodes.Children;
|
||||
import org.openide.util.Lookup;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
|
||||
import org.sleuthkit.autopsy.communications.relationships.SelectionInfo.SelectionSummary;
|
||||
import org.sleuthkit.datamodel.Account;
|
||||
|
||||
/**
|
||||
* Account Summary View Panel. This panel shows a list of various counts related
|
||||
* to the currently selected account. As well has a panel showing a list of
|
||||
* cases and files that reference the account.
|
||||
*
|
||||
*/
|
||||
public class SummaryViewer extends javax.swing.JPanel implements RelationshipsViewer {
|
||||
|
||||
private final Lookup lookup;
|
||||
|
||||
@Messages({
|
||||
"SummaryViewer_TabTitle=Summary",
|
||||
"SummaryViewer_FileRefNameColumn_Title=Path",
|
||||
"SummaryViewer_CaseRefNameColumn_Title=Case Name",
|
||||
"SummaryViewer_CentralRepository_Message=<Enable Central Resposity to see Case References>",
|
||||
"SummaryViewer_Creation_Date_Title=Creation Date"
|
||||
})
|
||||
|
||||
/**
|
||||
* Creates new form SummaryViewer
|
||||
*/
|
||||
public SummaryViewer() {
|
||||
lookup = Lookup.getDefault();
|
||||
initComponents();
|
||||
|
||||
OutlineView outlineView = fileReferencesPanel.getOutlineView();
|
||||
Outline outline = outlineView.getOutline();
|
||||
|
||||
outline.setRootVisible(false);
|
||||
((DefaultOutlineModel) outline.getOutlineModel()).setNodesColumnLabel(Bundle.SummaryViewer_FileRefNameColumn_Title());
|
||||
|
||||
outlineView = caseReferencesPanel.getOutlineView();
|
||||
outline = outlineView.getOutline();
|
||||
outlineView.setPropertyColumns("creationDate", Bundle.SummaryViewer_Creation_Date_Title()); //NON-NLS
|
||||
|
||||
outline.setRootVisible(false);
|
||||
((DefaultOutlineModel) outline.getOutlineModel()).setNodesColumnLabel(Bundle.SummaryViewer_CaseRefNameColumn_Title());
|
||||
|
||||
clearControls();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return Bundle.SummaryViewer_TabTitle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JPanel getPanel() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelectionInfo(SelectionInfo info) {
|
||||
|
||||
if (!EamDb.isEnabled()) {
|
||||
caseReferencesPanel.hideOutlineView(Bundle.SummaryViewer_CentralRepository_Message());
|
||||
} else {
|
||||
caseReferencesPanel.showOutlineView();
|
||||
}
|
||||
|
||||
// Request is that the SummaryViewer only show information if one
|
||||
// account is selected
|
||||
if (info.getAccounts().size() != 1) {
|
||||
setEnabled(false);
|
||||
clearControls();
|
||||
|
||||
} else {
|
||||
SelectionSummary summaryDetails = info.getSummary();
|
||||
|
||||
attachmentsDataLabel.setText(Integer.toString(summaryDetails.getAttachmentCnt()));
|
||||
callLogsDataLabel.setText(Integer.toString(summaryDetails.getCallLogCnt()));
|
||||
contactsDataLabel.setText(Integer.toString(summaryDetails.getContactsCnt()));
|
||||
emailDataLabel.setText(Integer.toString(summaryDetails.getEmailCnt()));
|
||||
messagesDataLabel.setText(Integer.toString(summaryDetails.getMessagesCnt()));
|
||||
|
||||
fileReferencesPanel.setNode(new AbstractNode(Children.create(new AccountSourceContentChildNodeFactory(info.getAccounts()), true)));
|
||||
caseReferencesPanel.setNode(new AbstractNode(Children.create(new CorrelationCaseChildNodeFactory(info.getAccounts()), true)));
|
||||
|
||||
setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Lookup getLookup() {
|
||||
return lookup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether or not the text fields are enabled.
|
||||
*
|
||||
* @param enabled true if this component should be enabled, false otherwise
|
||||
*/
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
super.setEnabled(enabled);
|
||||
attachmentsLabel.setEnabled(enabled);
|
||||
callLogsLabel.setEnabled(enabled);
|
||||
contactsLabel.setEnabled(enabled);
|
||||
emailLabel.setEnabled(enabled);
|
||||
messagesLabel.setEnabled(enabled);
|
||||
caseReferencesPanel.setEnabled(enabled);
|
||||
fileReferencesPanel.setEnabled(enabled);
|
||||
countsPanel.setEnabled(enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the text fields and OutlookViews.
|
||||
*/
|
||||
private void clearControls() {
|
||||
attachmentsDataLabel.setText("");
|
||||
callLogsDataLabel.setText("");
|
||||
contactsDataLabel.setText("");
|
||||
emailDataLabel.setText("");
|
||||
messagesDataLabel.setText("");
|
||||
|
||||
fileReferencesPanel.setNode(new AbstractNode(Children.LEAF));
|
||||
caseReferencesPanel.setNode(new AbstractNode(Children.LEAF));
|
||||
}
|
||||
|
||||
/**
|
||||
* For the given accounts create a comma separated string of all of the
|
||||
* names (TypeSpecificID).
|
||||
*
|
||||
* @param accounts Set of selected accounts
|
||||
*
|
||||
* @return String listing the account names
|
||||
*/
|
||||
private String createAccountLabel(Set<Account> accounts) {
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
accounts.stream().map((account) -> {
|
||||
buffer.append(account.getTypeSpecificID());
|
||||
return account;
|
||||
}).forEachOrdered((_item) -> {
|
||||
buffer.append(", ");
|
||||
});
|
||||
|
||||
return buffer.toString().substring(0, buffer.length() - 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
|
||||
countsPanel = new javax.swing.JPanel();
|
||||
emailLabel = new javax.swing.JLabel();
|
||||
contactsLabel = new javax.swing.JLabel();
|
||||
messagesLabel = new javax.swing.JLabel();
|
||||
callLogsLabel = new javax.swing.JLabel();
|
||||
attachmentsLabel = new javax.swing.JLabel();
|
||||
attachmentsDataLabel = new javax.swing.JLabel();
|
||||
messagesDataLabel = new javax.swing.JLabel();
|
||||
callLogsDataLabel = new javax.swing.JLabel();
|
||||
contactsDataLabel = new javax.swing.JLabel();
|
||||
emailDataLabel = new javax.swing.JLabel();
|
||||
fileReferencesPanel = new org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel();
|
||||
caseReferencesPanel = new org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel();
|
||||
|
||||
countsPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.countsPanel.border.title"))); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(emailLabel, org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.emailLabel.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(contactsLabel, org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.contactsLabel.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(messagesLabel, org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.messagesLabel.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(callLogsLabel, org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.callLogsLabel.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(attachmentsLabel, org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.attachmentsLabel.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(attachmentsDataLabel, org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.attachmentsDataLabel.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(messagesDataLabel, org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.messagesDataLabel.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(callLogsDataLabel, org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.callLogsDataLabel.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(contactsDataLabel, org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.contactsDataLabel.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(emailDataLabel, org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.emailDataLabel.text")); // NOI18N
|
||||
|
||||
javax.swing.GroupLayout countsPanelLayout = new javax.swing.GroupLayout(countsPanel);
|
||||
countsPanel.setLayout(countsPanelLayout);
|
||||
countsPanelLayout.setHorizontalGroup(
|
||||
countsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(countsPanelLayout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(countsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(attachmentsLabel)
|
||||
.addComponent(messagesLabel)
|
||||
.addComponent(callLogsLabel)
|
||||
.addComponent(contactsLabel)
|
||||
.addComponent(emailLabel))
|
||||
.addGap(18, 18, 18)
|
||||
.addGroup(countsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(emailDataLabel)
|
||||
.addComponent(contactsDataLabel)
|
||||
.addComponent(callLogsDataLabel)
|
||||
.addComponent(messagesDataLabel)
|
||||
.addComponent(attachmentsDataLabel))
|
||||
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
);
|
||||
countsPanelLayout.setVerticalGroup(
|
||||
countsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(countsPanelLayout.createSequentialGroup()
|
||||
.addGap(7, 7, 7)
|
||||
.addGroup(countsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(attachmentsLabel)
|
||||
.addComponent(attachmentsDataLabel))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(countsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(messagesLabel)
|
||||
.addComponent(messagesDataLabel))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(countsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(callLogsLabel)
|
||||
.addComponent(callLogsDataLabel))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(countsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(contactsLabel)
|
||||
.addComponent(contactsDataLabel))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(countsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(emailLabel)
|
||||
.addComponent(emailDataLabel))
|
||||
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
);
|
||||
|
||||
fileReferencesPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.fileReferencesPanel.border.title"))); // NOI18N
|
||||
fileReferencesPanel.setPreferredSize(new java.awt.Dimension(472, 300));
|
||||
|
||||
caseReferencesPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.caseReferencesPanel.border.title"))); // NOI18N
|
||||
caseReferencesPanel.setPreferredSize(new java.awt.Dimension(472, 300));
|
||||
|
||||
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)
|
||||
.addComponent(countsPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(fileReferencesPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 485, Short.MAX_VALUE)
|
||||
.addComponent(caseReferencesPanel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
.addContainerGap())
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(countsPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(fileReferencesPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(caseReferencesPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JLabel attachmentsDataLabel;
|
||||
private javax.swing.JLabel attachmentsLabel;
|
||||
private javax.swing.JLabel callLogsDataLabel;
|
||||
private javax.swing.JLabel callLogsLabel;
|
||||
private org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel caseReferencesPanel;
|
||||
private javax.swing.JLabel contactsDataLabel;
|
||||
private javax.swing.JLabel contactsLabel;
|
||||
private javax.swing.JPanel countsPanel;
|
||||
private javax.swing.JLabel emailDataLabel;
|
||||
private javax.swing.JLabel emailLabel;
|
||||
private org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel fileReferencesPanel;
|
||||
private javax.swing.JLabel messagesDataLabel;
|
||||
private javax.swing.JLabel messagesLabel;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-2018 Basis Technology Corp.
|
||||
* Copyright 2011-2019 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*s
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -36,6 +36,7 @@ import org.sleuthkit.datamodel.AbstractFile;
|
||||
class MediaFileViewer extends javax.swing.JPanel implements FileTypeViewer {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(MediaFileViewer.class.getName());
|
||||
private static final long serialVersionUID = 1L;
|
||||
private AbstractFile lastFile;
|
||||
//UI
|
||||
private MediaPlayerPanel mediaPlayerPanel;
|
||||
@ -48,7 +49,7 @@ class MediaFileViewer extends javax.swing.JPanel implements FileTypeViewer {
|
||||
/**
|
||||
* Creates a new MediaFileViewer.
|
||||
*/
|
||||
public MediaFileViewer() {
|
||||
MediaFileViewer() {
|
||||
|
||||
initComponents();
|
||||
|
||||
@ -69,8 +70,8 @@ class MediaFileViewer extends javax.swing.JPanel implements FileTypeViewer {
|
||||
|
||||
private void customizeComponents() {
|
||||
add(imagePanel, IMAGE_VIEWER_LAYER);
|
||||
|
||||
if(mediaPlayerPanel != null) {
|
||||
|
||||
if (mediaPlayerPanel != null) {
|
||||
add(mediaPlayerPanel, MEDIA_PLAYER_LAYER);
|
||||
}
|
||||
|
||||
@ -103,10 +104,10 @@ class MediaFileViewer extends javax.swing.JPanel implements FileTypeViewer {
|
||||
List<String> mimeTypes = new ArrayList<>();
|
||||
|
||||
mimeTypes.addAll(this.imagePanel.getSupportedMimeTypes());
|
||||
if(mediaPlayerPanel != null) {
|
||||
if (mediaPlayerPanel != null) {
|
||||
mimeTypes.addAll(this.mediaPlayerPanel.getSupportedMimeTypes());
|
||||
}
|
||||
|
||||
|
||||
return mimeTypes;
|
||||
}
|
||||
|
||||
|
@ -49,6 +49,10 @@ import javax.swing.JPanel;
|
||||
import org.controlsfx.control.MaskerPane;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.python.google.common.collect.Lists;
|
||||
import javafx.scene.Group;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.shape.Rectangle;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.coreutils.ImageUtils;
|
||||
import org.sleuthkit.autopsy.datamodel.FileNode;
|
||||
@ -70,18 +74,20 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
|
||||
private final boolean fxInited;
|
||||
|
||||
private JFXPanel fxPanel;
|
||||
private Group imageGroup;
|
||||
private ImageTaggingTool tagger;
|
||||
private ImageView fxImageView;
|
||||
private ScrollPane scrollPane;
|
||||
private final ProgressBar progressBar = new ProgressBar();
|
||||
private final MaskerPane maskerPane = new MaskerPane();
|
||||
|
||||
|
||||
private double zoomRatio;
|
||||
private double rotation; // Can be 0, 90, 180, and 270.
|
||||
|
||||
|
||||
private static final double[] ZOOM_STEPS = {
|
||||
0.0625, 0.125, 0.25, 0.375, 0.5, 0.75,
|
||||
1, 1.5, 2, 2.5, 3, 4, 5, 6, 8, 10};
|
||||
|
||||
|
||||
private static final double MIN_ZOOM_RATIO = 0.0625; // 6.25%
|
||||
private static final double MAX_ZOOM_RATIO = 10.0; // 1000%
|
||||
|
||||
@ -115,11 +121,13 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
|
||||
|
||||
// build jfx ui (we could do this in FXML?)
|
||||
fxImageView = new ImageView(); // will hold image
|
||||
scrollPane = new ScrollPane(fxImageView); // scrolls and sizes imageview
|
||||
imageGroup = new Group();
|
||||
imageGroup.getChildren().add(fxImageView);
|
||||
scrollPane = new ScrollPane(imageGroup); // scrolls and sizes imageview
|
||||
scrollPane.getStyleClass().add("bg"); //NOI18N
|
||||
scrollPane.setVbarPolicy(ScrollBarPolicy.AS_NEEDED);
|
||||
scrollPane.setHbarPolicy(ScrollBarPolicy.AS_NEEDED);
|
||||
|
||||
|
||||
fxPanel = new JFXPanel(); // bridge jfx-swing
|
||||
Scene scene = new Scene(scrollPane); //root of jfx tree
|
||||
scene.getStylesheets().add(MediaViewImagePanel.class.getResource("MediaViewImagePanel.css").toExternalForm()); //NOI18N
|
||||
@ -146,9 +154,10 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
|
||||
Platform.runLater(() -> {
|
||||
fxImageView.setViewport(new Rectangle2D(0, 0, 0, 0));
|
||||
fxImageView.setImage(null);
|
||||
|
||||
tagger.defaultSettings();
|
||||
|
||||
scrollPane.setContent(null);
|
||||
scrollPane.setContent(fxImageView);
|
||||
scrollPane.setContent(imageGroup);
|
||||
});
|
||||
}
|
||||
|
||||
@ -160,7 +169,7 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
|
||||
* TODO: why is the name passed into the action constructor? it
|
||||
* means we duplicate this string all over the place -jm
|
||||
*/ new ExternalViewerAction(Bundle.MediaViewImagePanel_externalViewerButton_text(), new FileNode(file))
|
||||
.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "")) //Swing ActionEvent
|
||||
.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "")) //Swing ActionEvent
|
||||
);
|
||||
|
||||
final VBox errorNode = new VBox(10, new Label(errorMessage), externalViewerButton);
|
||||
@ -199,8 +208,11 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
|
||||
if (nonNull(fxImage)) {
|
||||
// We have a non-null image, so let's show it.
|
||||
fxImageView.setImage(fxImage);
|
||||
imageGroup.getChildren().remove(tagger);
|
||||
tagger = new ImageTaggingTool(fxImageView, Color.RED);
|
||||
imageGroup.getChildren().add(tagger);
|
||||
resetView();
|
||||
scrollPane.setContent(fxImageView);
|
||||
scrollPane.setContent(imageGroup);
|
||||
} else {
|
||||
showErrorNode(Bundle.MediaViewImagePanel_errorLabel_text(), file);
|
||||
}
|
||||
@ -410,7 +422,7 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
|
||||
|
||||
private void zoomInButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_zoomInButtonActionPerformed
|
||||
// Find the next zoom step.
|
||||
for (int i=0; i < ZOOM_STEPS.length; i++) {
|
||||
for (int i = 0; i < ZOOM_STEPS.length; i++) {
|
||||
if (zoomRatio < ZOOM_STEPS[i]) {
|
||||
zoomRatio = ZOOM_STEPS[i];
|
||||
break;
|
||||
@ -421,7 +433,7 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
|
||||
|
||||
private void zoomOutButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_zoomOutButtonActionPerformed
|
||||
// Find the next zoom step.
|
||||
for (int i=ZOOM_STEPS.length-1; i >= 0; i--) {
|
||||
for (int i = ZOOM_STEPS.length - 1; i >= 0; i--) {
|
||||
if (zoomRatio > ZOOM_STEPS[i]) {
|
||||
zoomRatio = ZOOM_STEPS[i];
|
||||
break;
|
||||
@ -450,12 +462,12 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
|
||||
private javax.swing.JButton zoomResetButton;
|
||||
private javax.swing.JTextField zoomTextField;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
|
||||
/**
|
||||
* Reset the zoom and rotation values to their defaults. The zoom level gets
|
||||
* defaulted to the current size of the panel. The rotation will be set to
|
||||
* zero.
|
||||
*
|
||||
*
|
||||
* Note: This method will make a call to 'updateView()' after the values
|
||||
* have been reset.
|
||||
*/
|
||||
@ -464,28 +476,28 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
|
||||
if (image == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
double imageWidth = image.getWidth();
|
||||
double imageHeight = image.getHeight();
|
||||
double scrollPaneWidth = fxPanel.getWidth();
|
||||
double scrollPaneHeight = fxPanel.getHeight();
|
||||
double zoomRatioWidth = scrollPaneWidth / imageWidth;
|
||||
double zoomRatioHeight = scrollPaneHeight / imageHeight;
|
||||
|
||||
|
||||
// Use the smallest ratio size to fit the entire image in the view area.
|
||||
zoomRatio = zoomRatioWidth < zoomRatioHeight ? zoomRatioWidth : zoomRatioHeight;
|
||||
|
||||
|
||||
rotation = 0;
|
||||
|
||||
|
||||
scrollPane.setHvalue(0);
|
||||
scrollPane.setVvalue(0);
|
||||
|
||||
|
||||
updateView();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update the image to use the current zoom and rotation values.
|
||||
*
|
||||
*
|
||||
* Note: For zoom levels less than 100%, special accomodations are made in
|
||||
* order to keep the image fully visible. This is because the viewport size
|
||||
* change occurs before any transforms execute, thus chopping off part of
|
||||
@ -498,39 +510,39 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
|
||||
if (image == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Image dimensions
|
||||
double imageWidth = image.getWidth();
|
||||
double imageHeight = image.getHeight();
|
||||
|
||||
|
||||
// Image dimensions with zooming applied
|
||||
double adjustedImageWidth = imageWidth * zoomRatio;
|
||||
double adjustedImageHeight = imageHeight * zoomRatio;
|
||||
|
||||
|
||||
// ImageView viewport dimensions
|
||||
double viewportWidth;
|
||||
double viewportHeight;
|
||||
|
||||
|
||||
// Panel dimensions
|
||||
double panelWidth = fxPanel.getWidth();
|
||||
double panelHeight = fxPanel.getHeight();
|
||||
|
||||
|
||||
// Coordinates to center the image on the panel
|
||||
double centerOffsetX = (panelWidth / 2) - (imageWidth / 2);
|
||||
double centerOffsetY = (panelHeight / 2) - (imageHeight / 2);
|
||||
|
||||
|
||||
// Coordinates to keep the image inside the left/top boundaries
|
||||
double leftOffsetX;
|
||||
double topOffsetY;
|
||||
|
||||
|
||||
// Scroll bar positions
|
||||
double scrollX = scrollPane.getHvalue();
|
||||
double scrollY = scrollPane.getVvalue();
|
||||
|
||||
|
||||
// Scroll bar position boundaries (work-around for viewport size bug)
|
||||
double maxScrollX;
|
||||
double maxScrollY;
|
||||
|
||||
|
||||
// Set viewport size and translation offsets.
|
||||
if ((rotation % 180) == 0) {
|
||||
// Rotation is 0 or 180.
|
||||
@ -549,7 +561,7 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
|
||||
maxScrollX = (adjustedImageHeight - panelWidth) / (imageWidth - panelWidth);
|
||||
maxScrollY = (adjustedImageWidth - panelHeight) / (imageHeight - panelHeight);
|
||||
}
|
||||
|
||||
|
||||
// Work around bug that truncates image if dimensions are too small.
|
||||
if (viewportWidth < imageWidth) {
|
||||
viewportWidth = imageWidth;
|
||||
@ -563,7 +575,7 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
|
||||
scrollY = maxScrollY;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Update the viewport size.
|
||||
fxImageView.setViewport(new Rectangle2D(
|
||||
0, 0, viewportWidth, viewportHeight));
|
||||
@ -589,9 +601,9 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
|
||||
// Add the transforms in reverse order of intended execution.
|
||||
// Note: They MUST be added in this order to ensure translate is
|
||||
// executed last.
|
||||
fxImageView.getTransforms().clear();
|
||||
fxImageView.getTransforms().addAll(translate, rotate, scale);
|
||||
|
||||
imageGroup.getTransforms().clear();
|
||||
imageGroup.getTransforms().addAll(translate, rotate, scale);
|
||||
|
||||
// Adjust scroll bar positions for view changes.
|
||||
if (viewportWidth > fxPanel.getWidth()) {
|
||||
scrollPane.setHvalue(scrollX);
|
||||
@ -599,11 +611,114 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
|
||||
if (viewportHeight > fxPanel.getHeight()) {
|
||||
scrollPane.setVvalue(scrollY);
|
||||
}
|
||||
|
||||
|
||||
// Update all image controls to reflect the current values.
|
||||
zoomOutButton.setEnabled(zoomRatio > MIN_ZOOM_RATIO);
|
||||
zoomInButton.setEnabled(zoomRatio < MAX_ZOOM_RATIO);
|
||||
rotationTextField.setText((int) rotation + "°");
|
||||
zoomTextField.setText((Math.round(zoomRatio * 100.0)) + "%");
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables users to 'tag' a region of an image by clicking and dragging a
|
||||
* rectangle overtop.
|
||||
*/
|
||||
class ImageTaggingTool extends Rectangle {
|
||||
|
||||
private final double imageWidth;
|
||||
private final double imageHeight;
|
||||
private final double imageOriginX;
|
||||
private final double imageOriginY;
|
||||
|
||||
//Origin of the drag event.
|
||||
private double rectangleOriginX;
|
||||
private double rectangleOriginY;
|
||||
|
||||
//Rectangle lines should be 1.5% of the image. This level of thickness has
|
||||
//a good balance between visual acuity and loss of selection at the borders
|
||||
//of the image.
|
||||
private double lineThicknessAsPercent = 1.5;
|
||||
|
||||
/**
|
||||
* Adds tagging support to an image, where the 'tag' rectangle will be
|
||||
* the specified color.
|
||||
*
|
||||
* @param image Image to tag
|
||||
* @param color Color of the 'tag' rectangle
|
||||
*/
|
||||
private ImageTaggingTool(ImageView image, Color color) {
|
||||
defaultSettings();
|
||||
|
||||
imageWidth = image.getImage().getWidth();
|
||||
imageHeight = image.getImage().getHeight();
|
||||
imageOriginX = image.getX();
|
||||
imageOriginY = image.getY();
|
||||
|
||||
setStroke(color);
|
||||
setFill(color.deriveColor(0, 0, 0, 0));
|
||||
|
||||
//Calculate how many pixels the stroke width should be to guarentee
|
||||
//a consistent % of image consumed by the rectangle border.
|
||||
double min = Math.min(imageWidth, imageHeight);
|
||||
double lineThicknessPixels = min * lineThicknessAsPercent / 100.0;
|
||||
setStrokeWidth(lineThicknessPixels);
|
||||
setVisible(false);
|
||||
|
||||
//Create a rectangle by left clicking on the image
|
||||
image.setOnMousePressed((MouseEvent event) -> {
|
||||
if (event.isSecondaryButtonDown()) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Reset box on new click.
|
||||
defaultSettings();
|
||||
|
||||
rectangleOriginX = event.getX();
|
||||
rectangleOriginY = event.getY();
|
||||
|
||||
setX(rectangleOriginX);
|
||||
setY(rectangleOriginY);
|
||||
});
|
||||
|
||||
//Adjust the rectangle by dragging the left mouse button
|
||||
image.setOnMouseDragged((MouseEvent event) -> {
|
||||
if (event.isSecondaryButtonDown()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the rectangle is contained within image boundaries and
|
||||
* that the line thickness is kept within bounds.
|
||||
*/
|
||||
double newX = Math.min(Math.max(event.getX(), imageOriginX)
|
||||
+ lineThicknessPixels / 2, imageWidth - lineThicknessPixels / 2);
|
||||
double newY = Math.min(Math.max(event.getY(), imageOriginY)
|
||||
+ lineThicknessPixels / 2, imageHeight - lineThicknessPixels / 2);
|
||||
|
||||
setVisible(true);
|
||||
double offsetX = newX - rectangleOriginX;
|
||||
if (offsetX < 0) {
|
||||
setX(newX);
|
||||
}
|
||||
setWidth(Math.abs(offsetX));
|
||||
|
||||
double offsetY = newY - rectangleOriginY;
|
||||
if (offsetY < 0) {
|
||||
setY(newY);
|
||||
}
|
||||
setHeight(Math.abs(offsetY));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the rectangle to default dimensions.
|
||||
*/
|
||||
public final void defaultSettings() {
|
||||
setX(0);
|
||||
setY(0);
|
||||
setWidth(0);
|
||||
setHeight(0);
|
||||
setVisible(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -467,6 +467,7 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont
|
||||
htmlPanel.reset();
|
||||
textbodyTextArea.setText("");
|
||||
msgbodyTabbedPane.setEnabled(false);
|
||||
drp.setNode(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -561,7 +562,7 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont
|
||||
msgbodyTabbedPane.setEnabledAt(ATTM_TAB_INDEX, numberOfAttachments > 0);
|
||||
msgbodyTabbedPane.setTitleAt(ATTM_TAB_INDEX, "Attachments (" + numberOfAttachments + ")");
|
||||
drp.setNode(new TableFilterNode(new DataResultFilterNode(new AbstractNode(
|
||||
new AttachmentsChildren(attachments)), null), true));
|
||||
new AttachmentsChildren(attachments))), true));
|
||||
}
|
||||
|
||||
private static String wrapInHtmlBody(String htmlText) {
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2018 Basis Technology Corp.
|
||||
* Copyright 2018-2019 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -79,7 +79,7 @@ class PListViewer extends javax.swing.JPanel implements FileTypeViewer, Explorer
|
||||
/**
|
||||
* Creates new form PListViewer
|
||||
*/
|
||||
public PListViewer() {
|
||||
PListViewer() {
|
||||
|
||||
// Create an Outlineview and add to the panel
|
||||
outlineView = new org.openide.explorer.view.OutlineView();
|
||||
@ -193,16 +193,16 @@ class PListViewer extends javax.swing.JPanel implements FileTypeViewer, Explorer
|
||||
Case openCase;
|
||||
try {
|
||||
openCase = Case.getCurrentCaseThrows();
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
JOptionPane.showMessageDialog(this,
|
||||
"Failed to export plist file.",
|
||||
Bundle.PListViewer_ExportFailed_message(),
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
JOptionPane.showMessageDialog(this,
|
||||
"Failed to export plist file.",
|
||||
Bundle.PListViewer_ExportFailed_message(),
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
|
||||
logger.log(Level.SEVERE, "Exception while getting open case.", ex);
|
||||
return;
|
||||
logger.log(Level.SEVERE, "Exception while getting open case.", ex);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
final JFileChooser fileChooser = new JFileChooser();
|
||||
fileChooser.setCurrentDirectory(new File(openCase.getExportDirectory()));
|
||||
fileChooser.setFileFilter(new FileNameExtensionFilter("XML file", "xml"));
|
||||
@ -289,11 +289,11 @@ class PListViewer extends javax.swing.JPanel implements FileTypeViewer, Explorer
|
||||
// Read in and parse the file
|
||||
final byte[] plistFileBuf = new byte[(int) plistFile.getSize()];
|
||||
plistFile.read(plistFileBuf, 0, plistFile.getSize());
|
||||
final List<PropKeyValue> plist = parsePList(plistFileBuf);
|
||||
|
||||
final List<PropKeyValue> plist = parsePList(plistFileBuf);
|
||||
|
||||
return plist;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void done() {
|
||||
super.done();
|
||||
@ -301,28 +301,28 @@ class PListViewer extends javax.swing.JPanel implements FileTypeViewer, Explorer
|
||||
try {
|
||||
plist = get();
|
||||
setupTable(plist);
|
||||
|
||||
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
setColumnWidths();
|
||||
setColumnWidths();
|
||||
});
|
||||
} catch (InterruptedException ex) {
|
||||
logger.log(Level.SEVERE, "Interruption while parsing/dislaying plist file " + plistFile.getName(), ex);
|
||||
|
||||
JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
|
||||
ex.getMessage(),
|
||||
Bundle.PListViewer_processPlist_interruptedMessage(),
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
|
||||
|
||||
JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
|
||||
ex.getMessage(),
|
||||
Bundle.PListViewer_processPlist_interruptedMessage(),
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
|
||||
} catch (ExecutionException ex) {
|
||||
logger.log(Level.SEVERE, "Exception while parsing/dislaying plist file " + plistFile.getName(), ex);
|
||||
JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
|
||||
ex.getCause().getMessage(),
|
||||
Bundle.PListViewer_processPlist_errorMessage(),
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
|
||||
ex.getCause().getMessage(),
|
||||
Bundle.PListViewer_processPlist_errorMessage(),
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}.execute();
|
||||
}.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -431,16 +431,16 @@ class PListViewer extends javax.swing.JPanel implements FileTypeViewer, Explorer
|
||||
* else is unexpected and will be ignored.
|
||||
*/
|
||||
if (rootDict instanceof NSArray) {
|
||||
for (int i=0; i < ((NSArray)rootDict).count(); i++) {
|
||||
final PropKeyValue pkv = parseProperty("", ((NSArray)rootDict).objectAtIndex(i));
|
||||
for (int i = 0; i < ((NSArray) rootDict).count(); i++) {
|
||||
final PropKeyValue pkv = parseProperty("", ((NSArray) rootDict).objectAtIndex(i));
|
||||
if (null != pkv) {
|
||||
plist.add(pkv);
|
||||
}
|
||||
}
|
||||
} else if (rootDict instanceof NSDictionary) {
|
||||
final String[] keys = ((NSDictionary)rootDict).allKeys();
|
||||
final String[] keys = ((NSDictionary) rootDict).allKeys();
|
||||
for (final String key : keys) {
|
||||
final PropKeyValue pkv = parseProperty(key, ((NSDictionary)rootDict).objectForKey(key));
|
||||
final PropKeyValue pkv = parseProperty(key, ((NSDictionary) rootDict).objectForKey(key));
|
||||
if (null != pkv) {
|
||||
plist.add(pkv);
|
||||
}
|
||||
@ -533,7 +533,7 @@ class PListViewer extends javax.swing.JPanel implements FileTypeViewer, Explorer
|
||||
.map(child -> new PropKeyValue(child))
|
||||
.toArray(PropKeyValue[]::new);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2018 Basis Technology Corp.
|
||||
* Copyright 2018-2019 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -74,7 +74,7 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
|
||||
/**
|
||||
* Constructs a file content viewer for SQLite database files.
|
||||
*/
|
||||
public SQLiteViewer() {
|
||||
SQLiteViewer() {
|
||||
initComponents();
|
||||
jTableDataPanel.add(selectedTableView, BorderLayout.CENTER);
|
||||
}
|
||||
@ -544,19 +544,19 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
|
||||
@Override
|
||||
public void accept(String columnName) {
|
||||
columnIndex++;
|
||||
|
||||
String csvString = columnName;
|
||||
//Format the value to adhere to the format of a CSV file
|
||||
if (columnIndex == 1) {
|
||||
columnName = "\"" + columnName + "\"";
|
||||
csvString = "\"" + csvString + "\"";
|
||||
} else {
|
||||
columnName = ",\"" + columnName + "\"";
|
||||
csvString = ",\"" + csvString + "\"";
|
||||
}
|
||||
if (columnIndex == totalColumnCount) {
|
||||
columnName += "\n";
|
||||
csvString += "\n";
|
||||
}
|
||||
|
||||
try {
|
||||
out.write(columnName.getBytes());
|
||||
out.write(csvString.getBytes());
|
||||
} catch (IOException ex) {
|
||||
/*
|
||||
* If we can no longer write to the output stream, toss a
|
||||
@ -613,7 +613,7 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
|
||||
*/
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
rowIndex = rowIndex % totalColumnCount;
|
||||
rowIndex %= totalColumnCount;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -60,6 +60,7 @@ public final class UserPreferences {
|
||||
private static final String MESSAGE_SERVICE_USER = "MessageServiceUser"; //NON-NLS
|
||||
private static final String MESSAGE_SERVICE_HOST = "MessageServiceHost"; //NON-NLS
|
||||
private static final String MESSAGE_SERVICE_PORT = "MessageServicePort"; //NON-NLS
|
||||
private static final String TEXT_TRANSLATOR_NAME = "TextTranslatorName";
|
||||
public static final String PROCESS_TIME_OUT_ENABLED = "ProcessTimeOutEnabled"; //NON-NLS
|
||||
public static final String PROCESS_TIME_OUT_HOURS = "ProcessTimeOutHours"; //NON-NLS
|
||||
private static final int DEFAULT_PROCESS_TIMEOUT_HR = 60;
|
||||
@ -335,6 +336,14 @@ public final class UserPreferences {
|
||||
public static void setIndexingServerPort(int port) {
|
||||
preferences.putInt(INDEXING_SERVER_PORT, port);
|
||||
}
|
||||
|
||||
public static void setTextTranslatorName(String textTranslatorName){
|
||||
preferences.put(TEXT_TRANSLATOR_NAME, textTranslatorName);
|
||||
}
|
||||
|
||||
public static String getTextTranslatorName(){
|
||||
return preferences.get(TEXT_TRANSLATOR_NAME, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Persists message service connection info.
|
||||
|
@ -386,7 +386,13 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer {
|
||||
rootNodeChildren.cancelLoadingThumbnails();
|
||||
}
|
||||
try {
|
||||
if (givenNode != null) {
|
||||
// There is an issue with ThumbnailViewChildren
|
||||
// addNotify, that it's call to getChildren.getNodes() does not cause the
|
||||
// children nodes to be created. Adding a call to getChildren.getNodesCount()
|
||||
// here will assure that the children nodes are created particularly in the
|
||||
// case where the DataResultViewerThumbnail stands along from the
|
||||
// DataResultViewer. See DataResultViewer setNode for more information.
|
||||
if (givenNode != null && givenNode.getChildren().getNodesCount() > 0) {
|
||||
rootNode = (TableFilterNode) givenNode;
|
||||
/*
|
||||
* Wrap the given node in a ThumbnailViewChildren that will
|
||||
|
@ -29,6 +29,7 @@ import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
@ -798,7 +799,12 @@ public class ImageUtils {
|
||||
imageSaver.execute(() -> {
|
||||
try {
|
||||
synchronized (cacheFile) {
|
||||
Files.createParentDirs(cacheFile);
|
||||
Path path = Paths.get(cacheFile.getParent());
|
||||
File thumbsDir = Paths.get(cacheFile.getParent()).toFile();
|
||||
if (!thumbsDir.exists()) {
|
||||
thumbsDir.mkdirs();
|
||||
}
|
||||
|
||||
if (cacheFile.exists()) {
|
||||
cacheFile.delete();
|
||||
}
|
||||
|
@ -95,7 +95,20 @@ public class DataResultFilterNode extends FilterNode {
|
||||
static private final DisplayableItemNodeVisitor<List<Action>> getActionsDIV = new GetPopupActionsDisplayableItemNodeVisitor();
|
||||
private final DisplayableItemNodeVisitor<AbstractAction> getPreferredActionsDIV = new GetPreferredActionsDisplayableItemNodeVisitor();
|
||||
|
||||
// Assumptions are made in GetPreferredActionsDisplayableItemNodeVisitor that
|
||||
// sourceEm is the directory tree explorer manager.
|
||||
private final ExplorerManager sourceEm;
|
||||
|
||||
/**
|
||||
* Constructs a node used to wrap another node before passing it to the
|
||||
* result viewers. The wrapper node defines the actions associated with the
|
||||
* wrapped node and may filter out some of its children.
|
||||
*
|
||||
* @param node The node to wrap.
|
||||
*/
|
||||
public DataResultFilterNode(Node node) {
|
||||
this(node, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a node used to wrap another node before passing it to the
|
||||
@ -549,6 +562,9 @@ public class DataResultFilterNode extends FilterNode {
|
||||
// is a DirectoryTreeFilterNode that wraps the dataModelNode. We need
|
||||
// to set that wrapped node as the selection and root context of the
|
||||
// directory tree explorer manager (sourceEm)
|
||||
if(sourceEm == null) {
|
||||
return null;
|
||||
}
|
||||
final Node currentSelectionInDirectoryTree = sourceEm.getSelectedNodes()[0];
|
||||
|
||||
return new AbstractAction() {
|
||||
@ -589,6 +605,9 @@ public class DataResultFilterNode extends FilterNode {
|
||||
* @return
|
||||
*/
|
||||
private AbstractAction openParent(AbstractNode node) {
|
||||
if(sourceEm == null) {
|
||||
return null;
|
||||
}
|
||||
// @@@ Why do we ignore node?
|
||||
Node[] selectedFilterNodes = sourceEm.getSelectedNodes();
|
||||
Node selectedFilterNode = selectedFilterNodes[0];
|
||||
|
BIN
Core/src/org/sleuthkit/autopsy/images/translate32.png
Normal file
BIN
Core/src/org/sleuthkit/autopsy/images/translate32.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.2 KiB |
@ -163,6 +163,8 @@ final class ContactAnalyzer {
|
||||
data1 = resultSet.getString("data1"); //NON-NLS
|
||||
mimetype = resultSet.getString("mimetype"); //NON-NLS
|
||||
if (name.equals(oldName) == false) {
|
||||
bba = file.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT);
|
||||
attributes = new ArrayList<>();
|
||||
attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME, moduleName, name));
|
||||
}
|
||||
if (mimetype.equals("vnd.android.cursor.item/phone_v2")) { //NON-NLS
|
||||
@ -170,6 +172,12 @@ final class ContactAnalyzer {
|
||||
} else {
|
||||
attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL, moduleName, data1));
|
||||
}
|
||||
|
||||
// TODO: If this code comes back to life, add code to create the account
|
||||
// and relationship between the phone numbers & emails. Also
|
||||
// investigate if the mimetype "vnd.android.cursor.item/phone_v2"
|
||||
// makes sense in an ios word
|
||||
|
||||
oldName = name;
|
||||
|
||||
bba.addAttributes(attributes);
|
||||
|
@ -1,15 +1,15 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2013-2018 Basis Technology Corp.
|
||||
*
|
||||
* Copyright 2013-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.
|
||||
@ -18,12 +18,15 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.modules.stix;
|
||||
|
||||
import com.williballenthin.rejistry.RegistryHiveFile;
|
||||
import com.williballenthin.rejistry.RegistryKey;
|
||||
import com.williballenthin.rejistry.RegistryParseException;
|
||||
import com.williballenthin.rejistry.RegistryValue;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.io.IOException;
|
||||
@ -31,10 +34,8 @@ import java.io.UnsupportedEncodingException;
|
||||
import java.io.File;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.Matcher;
|
||||
|
||||
import org.mitre.cybox.objects.WindowsRegistryKey;
|
||||
import org.mitre.cybox.common_2.ConditionTypeEnum;
|
||||
import com.williballenthin.rejistry.*;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
|
||||
/**
|
||||
@ -43,9 +44,9 @@ import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
class EvalRegistryObj extends EvaluatableObject {
|
||||
|
||||
private final WindowsRegistryKey obj;
|
||||
private final List<RegistryFileInfo> regFiles = new ArrayList<RegistryFileInfo>();
|
||||
private final List<RegistryFileInfo> regFiles = new ArrayList<>();
|
||||
|
||||
public EvalRegistryObj(WindowsRegistryKey a_obj, String a_id, String a_spacing, List<RegistryFileInfo> a_regFiles) {
|
||||
EvalRegistryObj(WindowsRegistryKey a_obj, String a_id, String a_spacing, List<RegistryFileInfo> a_regFiles) {
|
||||
obj = a_obj;
|
||||
id = a_id;
|
||||
spacing = a_spacing;
|
||||
@ -80,7 +81,7 @@ class EvalRegistryObj extends EvaluatableObject {
|
||||
setUnsupportedFieldWarnings();
|
||||
|
||||
// Make a list of hives to test
|
||||
List<RegistryFileInfo> hiveList = new ArrayList<RegistryFileInfo>();
|
||||
List<RegistryFileInfo> hiveList = new ArrayList<>();
|
||||
if (obj.getHive() == null) {
|
||||
// If the hive field is missing, add everything
|
||||
hiveList.addAll(regFiles);
|
||||
@ -88,9 +89,9 @@ class EvalRegistryObj extends EvaluatableObject {
|
||||
// If the hive name is HKEY_LOCAL_MACHINE, add the ones from the config directory.
|
||||
// Otherwise, add the others
|
||||
for (RegistryFileInfo regFile : regFiles) {
|
||||
if (regFile.abstractFile.getParentPath() != null) {
|
||||
if (regFile.getAbstractFile().getParentPath() != null) {
|
||||
Pattern pattern = Pattern.compile("system32", Pattern.CASE_INSENSITIVE);
|
||||
Matcher matcher = pattern.matcher(regFile.abstractFile.getParentPath());
|
||||
Matcher matcher = pattern.matcher(regFile.getAbstractFile().getParentPath());
|
||||
if (matcher.find()) {
|
||||
// Looking for system files and found one, so add it to the list
|
||||
if (obj.getHive().getValue().toString().equalsIgnoreCase("HKEY_LOCAL_MACHINE")) { //NON-NLS
|
||||
@ -112,7 +113,7 @@ class EvalRegistryObj extends EvaluatableObject {
|
||||
Pattern pattern = Pattern.compile("Temp.STIX." + stixHiveName, Pattern.CASE_INSENSITIVE);
|
||||
|
||||
for (RegistryFileInfo hive : regFiles) {
|
||||
Matcher matcher = pattern.matcher(hive.tempFileName);
|
||||
Matcher matcher = pattern.matcher(hive.getTempFileName());
|
||||
if (matcher.find()) {
|
||||
hiveList.add(hive);
|
||||
}
|
||||
@ -163,7 +164,7 @@ class EvalRegistryObj extends EvaluatableObject {
|
||||
*/
|
||||
private ObservableResult testRegistryFile(RegistryFileInfo a_regInfo) {
|
||||
try {
|
||||
RegistryKey root = openRegistry(a_regInfo.tempFileName);
|
||||
RegistryKey root = openRegistry(a_regInfo.getTempFileName());
|
||||
RegistryKey result = findKey(root, obj.getKey().getValue().toString());
|
||||
|
||||
if (result == null) {
|
||||
@ -192,8 +193,8 @@ class EvalRegistryObj extends EvaluatableObject {
|
||||
|
||||
if ((obj.getValues() == null) || (obj.getValues().getValues().isEmpty())) {
|
||||
// No values to test
|
||||
List<StixArtifactData> artData = new ArrayList<StixArtifactData>();
|
||||
artData.add(new StixArtifactData(a_regInfo.abstractFile.getId(), id, "Registry")); //NON-NLS
|
||||
List<StixArtifactData> artData = new ArrayList<>();
|
||||
artData.add(new StixArtifactData(a_regInfo.getAbstractFile().getId(), id, "Registry")); //NON-NLS
|
||||
return new ObservableResult(id, "RegistryObject: Found key " + obj.getKey().getValue(), //NON-NLS
|
||||
spacing, ObservableResult.ObservableState.TRUE, artData);
|
||||
}
|
||||
@ -262,8 +263,8 @@ class EvalRegistryObj extends EvaluatableObject {
|
||||
|
||||
if (nameSuccess && valueSuccess) {
|
||||
// Found a match for all values
|
||||
List<StixArtifactData> artData = new ArrayList<StixArtifactData>();
|
||||
artData.add(new StixArtifactData(a_regInfo.abstractFile.getId(), id, "Registry")); //NON-NLS
|
||||
List<StixArtifactData> artData = new ArrayList<>();
|
||||
artData.add(new StixArtifactData(a_regInfo.getAbstractFile().getId(), id, "Registry")); //NON-NLS
|
||||
return new ObservableResult(id, "RegistryObject: Found key " + obj.getKey().getValue() //NON-NLS
|
||||
+ " and value " + stixRegValue.getName().getValue().toString() //NON-NLS
|
||||
+ " = " + stixRegValue.getData().getValue().toString(),
|
||||
@ -343,13 +344,13 @@ class EvalRegistryObj extends EvaluatableObject {
|
||||
List<AbstractFile> regFilesAbstract = findRegistryFiles();
|
||||
|
||||
// List to hold all the extracted file names plus their abstract file
|
||||
List<RegistryFileInfo> regFilesLocal = new ArrayList<RegistryFileInfo>();
|
||||
List<RegistryFileInfo> regFilesLocal = new ArrayList<>();
|
||||
|
||||
// Make the temp directory
|
||||
String tmpDir;
|
||||
try {
|
||||
tmpDir = Case.getCurrentCaseThrows().getTempDirectory() + File.separator + "STIX"; //NON-NLS
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
tmpDir = Case.getCurrentCaseThrows().getTempDirectory() + File.separator + "STIX"; //NON-NLS
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
throw new TskCoreException(ex.getLocalizedMessage());
|
||||
}
|
||||
File dir = new File(tmpDir);
|
||||
@ -382,11 +383,11 @@ class EvalRegistryObj extends EvaluatableObject {
|
||||
* RecentActivity
|
||||
*/
|
||||
private static List<AbstractFile> findRegistryFiles() throws TskCoreException {
|
||||
List<AbstractFile> registryFiles = new ArrayList<AbstractFile>();
|
||||
List<AbstractFile> registryFiles = new ArrayList<>();
|
||||
Case openCase;
|
||||
try {
|
||||
openCase = Case.getCurrentCaseThrows();
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
throw new TskCoreException(ex.getLocalizedMessage());
|
||||
}
|
||||
org.sleuthkit.autopsy.casemodule.services.FileManager fileManager = openCase.getServices().getFileManager();
|
||||
@ -413,7 +414,7 @@ class EvalRegistryObj extends EvaluatableObject {
|
||||
}
|
||||
|
||||
private void setUnsupportedFieldWarnings() {
|
||||
List<String> fieldNames = new ArrayList<String>();
|
||||
List<String> fieldNames = new ArrayList<>();
|
||||
|
||||
if (obj.getNumberValues() != null) {
|
||||
fieldNames.add("Number_Values"); //NON-NLS
|
||||
@ -462,5 +463,23 @@ class EvalRegistryObj extends EvaluatableObject {
|
||||
tempFileName = a_tempFileName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the AbstractFile for this RegistryFileInfo
|
||||
*
|
||||
* @return the abstractFile
|
||||
*/
|
||||
AbstractFile getAbstractFile() {
|
||||
return abstractFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Temporary file name for this RegistryFileInfo
|
||||
*
|
||||
* @return the tempFileName
|
||||
*/
|
||||
String getTempFileName() {
|
||||
return tempFileName;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-2018 Basis Technology Corp.
|
||||
* Copyright 2011-2019 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -22,7 +22,9 @@ import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import net.htmlparser.jericho.Attributes;
|
||||
import net.htmlparser.jericho.Config;
|
||||
@ -71,7 +73,7 @@ final class HtmlTextExtractor implements TextExtractor {
|
||||
/**
|
||||
* Determines if this content type is supported by this extractor.
|
||||
*
|
||||
* @param content Content instance to be analyzed
|
||||
* @param content Content instance to be analyzed
|
||||
* @param detectedFormat Mimetype of content instance
|
||||
*
|
||||
* @return flag indicating support
|
||||
@ -84,27 +86,21 @@ final class HtmlTextExtractor implements TextExtractor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reader that will iterate over the text of an HTML document.
|
||||
*
|
||||
* @param content Html document source
|
||||
*
|
||||
* @return A reader instance containing the document source text
|
||||
*
|
||||
* @throws TextExtractorException
|
||||
* Get the metadata as a key -> value map. HTML metadata will include
|
||||
* scripts, links, images, comments, and misc attributes.
|
||||
*
|
||||
* @return Map containing metadata key -> value pairs.
|
||||
*/
|
||||
@Override
|
||||
public Reader getReader() throws InitReaderException {
|
||||
//TODO JIRA-4467, there is only harm in excluding HTML documents greater
|
||||
//than 50MB due to our troubled approach of extraction.
|
||||
ReadContentInputStream stream = new ReadContentInputStream(file);
|
||||
|
||||
//Parse the stream with Jericho and put the results in a Reader
|
||||
public Map<String, String> getMetadata() {
|
||||
Map<String, String> metadataMap = new HashMap<>();
|
||||
try {
|
||||
StringBuilder scripts = new StringBuilder();
|
||||
StringBuilder links = new StringBuilder();
|
||||
StringBuilder images = new StringBuilder();
|
||||
StringBuilder comments = new StringBuilder();
|
||||
StringBuilder others = new StringBuilder();
|
||||
ReadContentInputStream stream = new ReadContentInputStream(file);
|
||||
StringBuilder scripts = new StringBuilder("\n");
|
||||
StringBuilder links = new StringBuilder("\n");
|
||||
StringBuilder images = new StringBuilder("\n");
|
||||
StringBuilder comments = new StringBuilder("\n");
|
||||
StringBuilder others = new StringBuilder("\n");
|
||||
int numScripts = 0;
|
||||
int numLinks = 0;
|
||||
int numImages = 0;
|
||||
@ -113,17 +109,8 @@ final class HtmlTextExtractor implements TextExtractor {
|
||||
|
||||
Source source = new Source(stream);
|
||||
source.fullSequentialParse();
|
||||
Renderer renderer = source.getRenderer();
|
||||
renderer.setNewLine("\n");
|
||||
renderer.setIncludeHyperlinkURLs(false);
|
||||
renderer.setDecorateFontStyles(false);
|
||||
renderer.setIncludeAlternateText(false);
|
||||
|
||||
String text = renderer.toString();
|
||||
// Get all the tags in the source
|
||||
List<StartTag> tags = source.getAllStartTags();
|
||||
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
for (StartTag tag : tags) {
|
||||
if (tag.getName().equals("script")) { //NON-NLS
|
||||
// If the <script> tag has attributes
|
||||
@ -164,30 +151,54 @@ final class HtmlTextExtractor implements TextExtractor {
|
||||
}
|
||||
}
|
||||
}
|
||||
stringBuilder.append(text).append("\n\n");
|
||||
stringBuilder.append("----------NONVISIBLE TEXT----------\n\n"); //NON-NLS
|
||||
|
||||
if (numScripts > 0) {
|
||||
stringBuilder.append("---Scripts---\n"); //NON-NLS
|
||||
stringBuilder.append(scripts).append("\n");
|
||||
metadataMap.put("Scripts", scripts.toString());
|
||||
}
|
||||
if (numLinks > 0) {
|
||||
stringBuilder.append("---Links---\n"); //NON-NLS
|
||||
stringBuilder.append(links).append("\n");
|
||||
metadataMap.put("Links", links.toString());
|
||||
}
|
||||
if (numImages > 0) {
|
||||
stringBuilder.append("---Images---\n"); //NON-NLS
|
||||
stringBuilder.append(images).append("\n");
|
||||
metadataMap.put("Images", images.toString());
|
||||
}
|
||||
if (numComments > 0) {
|
||||
stringBuilder.append("---Comments---\n"); //NON-NLS
|
||||
stringBuilder.append(comments).append("\n");
|
||||
metadataMap.put("Comments", comments.toString());
|
||||
}
|
||||
if (numOthers > 0) {
|
||||
stringBuilder.append("---Others---\n"); //NON-NLS
|
||||
stringBuilder.append(others).append("\n");
|
||||
metadataMap.put("Others", others.toString());
|
||||
}
|
||||
// All done, now make it a reader
|
||||
return new StringReader(stringBuilder.toString());
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.WARNING, "Error extracting HTML metadata from content.", ex);
|
||||
}
|
||||
|
||||
return metadataMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reader that will iterate over the text of an HTML document.
|
||||
*
|
||||
* @param content Html document source
|
||||
*
|
||||
* @return A reader instance containing the document source text
|
||||
*
|
||||
* @throws TextExtractorException
|
||||
*/
|
||||
@Override
|
||||
public Reader getReader() throws InitReaderException {
|
||||
//TODO JIRA-4467, there is only harm in excluding HTML documents greater
|
||||
//than 50MB due to our troubled approach of extraction.
|
||||
ReadContentInputStream stream = new ReadContentInputStream(file);
|
||||
|
||||
//Parse the stream with Jericho and put the results in a Reader
|
||||
try {
|
||||
Source source = new Source(stream);
|
||||
source.fullSequentialParse();
|
||||
Renderer renderer = source.getRenderer();
|
||||
renderer.setNewLine("\n");
|
||||
renderer.setIncludeHyperlinkURLs(false);
|
||||
renderer.setDecorateFontStyles(false);
|
||||
renderer.setIncludeAlternateText(false);
|
||||
return new StringReader(renderer.toString());
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.WARNING, "Error extracting HTML from content.", ex);
|
||||
throw new InitReaderException("Error extracting HTML from content.", ex);
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-18 Basis Technology Corp.
|
||||
* Copyright 2011-19 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -19,6 +19,8 @@
|
||||
package org.sleuthkit.autopsy.textextractors;
|
||||
|
||||
import java.io.Reader;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import org.openide.util.Lookup;
|
||||
|
||||
/**
|
||||
@ -61,6 +63,15 @@ public interface TextExtractor {
|
||||
default void setExtractionSettings(Lookup context) {
|
||||
//no-op by default
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves content metadata, if any.
|
||||
*
|
||||
* @return Metadata as key -> value map
|
||||
*/
|
||||
default Map<String, String> getMetadata() {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* System level exception for reader initialization.
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-2018 Basis Technology Corp.
|
||||
* Copyright 2011-2019 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -29,8 +29,10 @@ import java.io.InputStream;
|
||||
import java.io.PushbackReader;
|
||||
import java.io.Reader;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
@ -40,8 +42,8 @@ import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import org.apache.tika.Tika;
|
||||
import org.apache.tika.exception.TikaException;
|
||||
import org.apache.tika.metadata.Metadata;
|
||||
import org.apache.tika.parser.AutoDetectParser;
|
||||
import org.apache.tika.parser.ParseContext;
|
||||
@ -65,6 +67,10 @@ import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.ReadContentInputStream;
|
||||
import org.xml.sax.ContentHandler;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.helpers.DefaultHandler;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
/**
|
||||
* Extracts text from Tika supported content. Protects against Tika parser hangs
|
||||
@ -136,7 +142,8 @@ final class TikaTextExtractor implements TextExtractor {
|
||||
private static final File TESSERACT_PATH = locateTesseractExecutable();
|
||||
private String languagePacks = formatLanguagePacks(PlatformUtil.getOcrLanguagePacks());
|
||||
private static final String TESSERACT_OUTPUT_FILE_NAME = "tess_output"; //NON-NLS
|
||||
|
||||
private Map<String, String> metadataMap;
|
||||
|
||||
private ProcessTerminator processTerminator;
|
||||
|
||||
private static final List<String> TIKA_SUPPORTED_TYPES
|
||||
@ -151,8 +158,8 @@ final class TikaTextExtractor implements TextExtractor {
|
||||
|
||||
/**
|
||||
* If Tesseract has been installed and is set to be used through
|
||||
* configuration, then ocr is enabled. OCR can only currently be run on
|
||||
* 64 bit Windows OS.
|
||||
* configuration, then ocr is enabled. OCR can only currently be run on 64
|
||||
* bit Windows OS.
|
||||
*
|
||||
* @return Flag indicating if OCR is set to be used.
|
||||
*/
|
||||
@ -201,7 +208,7 @@ final class TikaTextExtractor implements TextExtractor {
|
||||
TesseractOCRConfig ocrConfig = new TesseractOCRConfig();
|
||||
String tesseractFolder = TESSERACT_PATH.getParent();
|
||||
ocrConfig.setTesseractPath(tesseractFolder);
|
||||
|
||||
|
||||
ocrConfig.setLanguage(languagePacks);
|
||||
ocrConfig.setTessdataPath(PlatformUtil.getOcrLanguagePacksPath());
|
||||
parseContext.set(TesseractOCRConfig.class, ocrConfig);
|
||||
@ -233,9 +240,16 @@ final class TikaTextExtractor implements TextExtractor {
|
||||
+ "Tika returned empty reader for " + content);
|
||||
}
|
||||
pushbackReader.unread(read);
|
||||
//concatenate parsed content and meta data into a single reader.
|
||||
CharSource metaDataCharSource = getMetaDataCharSource(metadata);
|
||||
return CharSource.concat(new ReaderCharSource(pushbackReader), metaDataCharSource).openStream();
|
||||
|
||||
//Save the metadata if it has not been fetched already.
|
||||
if (metadataMap == null) {
|
||||
metadataMap = new HashMap<>();
|
||||
for (String mtdtKey : metadata.names()) {
|
||||
metadataMap.put(mtdtKey, metadata.get(mtdtKey));
|
||||
}
|
||||
}
|
||||
|
||||
return new ReaderCharSource(pushbackReader).openStream();
|
||||
} catch (TimeoutException te) {
|
||||
final String msg = NbBundle.getMessage(this.getClass(),
|
||||
"AbstractFileTikaTextExtract.index.tikaParseTimeout.text",
|
||||
@ -273,7 +287,7 @@ final class TikaTextExtractor implements TextExtractor {
|
||||
File outputFile = null;
|
||||
try {
|
||||
String tempDirectory = Case.getCurrentCaseThrows().getTempDirectory();
|
||||
|
||||
|
||||
//Appending file id makes the name unique
|
||||
String tempFileName = FileUtil.escapeFileName(file.getId() + file.getName());
|
||||
inputFile = Paths.get(tempDirectory, tempFileName).toFile();
|
||||
@ -314,7 +328,7 @@ final class TikaTextExtractor implements TextExtractor {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Wraps the creation of a TikaReader into a Future so that it can be
|
||||
* cancelled.
|
||||
@ -403,20 +417,33 @@ final class TikaTextExtractor implements TextExtractor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a CharSource that wraps a formated representation of the given
|
||||
* Metadata.
|
||||
* Get the content metadata, if any.
|
||||
*
|
||||
* @param metadata The Metadata to wrap as a CharSource
|
||||
*
|
||||
* @return A CharSource for the given MetaData
|
||||
* @return Metadata as a name -> value map
|
||||
*/
|
||||
static private CharSource getMetaDataCharSource(Metadata metadata) {
|
||||
return CharSource.wrap(
|
||||
new StringBuilder("\n\n------------------------------METADATA------------------------------\n\n")
|
||||
.append(Stream.of(metadata.names()).sorted()
|
||||
.map(key -> key + ": " + metadata.get(key))
|
||||
.collect(Collectors.joining("\n"))
|
||||
));
|
||||
@Override
|
||||
public Map<String, String> getMetadata() {
|
||||
if (metadataMap != null) {
|
||||
return ImmutableMap.copyOf(metadataMap);
|
||||
}
|
||||
|
||||
try {
|
||||
metadataMap = new HashMap<>();
|
||||
InputStream stream = new ReadContentInputStream(content);
|
||||
ContentHandler doNothingContentHandler = new DefaultHandler();
|
||||
Metadata mtdt = new Metadata();
|
||||
parser.parse(stream, doNothingContentHandler, mtdt);
|
||||
for (String mtdtKey : mtdt.names()) {
|
||||
metadataMap.put(mtdtKey, mtdt.get(mtdtKey));
|
||||
}
|
||||
} catch (IOException | SAXException | TikaException ex) {
|
||||
AUTOPSY_LOGGER.log(Level.WARNING, String.format("Error getting metadata for file [id=%d] %s, see Tika log for details...", //NON-NLS
|
||||
content.getId(), content.getName()));
|
||||
TIKA_LOGGER.log(Level.WARNING, "Exception: Unable to get metadata for " //NON-NLS
|
||||
+ "content" + content.getId() + ": " + content.getName(), ex); //NON-NLS
|
||||
}
|
||||
|
||||
return metadataMap;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -426,11 +453,11 @@ final class TikaTextExtractor implements TextExtractor {
|
||||
*/
|
||||
@Override
|
||||
public boolean isSupported() {
|
||||
if(!(content instanceof AbstractFile)) {
|
||||
if (!(content instanceof AbstractFile)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String detectedType = ((AbstractFile)content).getMIMEType();
|
||||
|
||||
String detectedType = ((AbstractFile) content).getMIMEType();
|
||||
if (detectedType == null
|
||||
|| BINARY_MIME_TYPES.contains(detectedType) //any binary unstructured blobs (string extraction will be used)
|
||||
|| ARCHIVE_MIME_TYPES.contains(detectedType)
|
||||
@ -439,7 +466,7 @@ final class TikaTextExtractor implements TextExtractor {
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return TIKA_SUPPORTED_TYPES.contains(detectedType);
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2018-2018 Basis Technology Corp.
|
||||
* Copyright 2018-2019 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -18,34 +18,52 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.texttranslation;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.openide.util.Lookup;
|
||||
import org.sleuthkit.autopsy.core.UserPreferences;
|
||||
|
||||
/**
|
||||
* Performs a lookup for a TextTranslator service provider and if present,
|
||||
* will use this provider to run translation on the input.
|
||||
* Performs a lookup for a TextTranslator service provider and if present, will
|
||||
* use this provider to run translation on the input.
|
||||
*/
|
||||
public final class TextTranslationService {
|
||||
|
||||
|
||||
private final static TextTranslationService tts = new TextTranslationService();
|
||||
|
||||
private final Optional<TextTranslator> translator;
|
||||
|
||||
private TextTranslationService(){
|
||||
private final Collection<? extends TextTranslator> translators;
|
||||
private Optional<TextTranslator> selectedTranslator;
|
||||
|
||||
private TextTranslationService() {
|
||||
//Perform look up for Text Translation implementations ONLY ONCE during
|
||||
//class loading.
|
||||
translator = Optional.ofNullable(Lookup.getDefault()
|
||||
.lookup(TextTranslator.class));
|
||||
translators = Lookup.getDefault().lookupAll(TextTranslator.class);
|
||||
updateSelectedTranslator();
|
||||
}
|
||||
|
||||
|
||||
public static TextTranslationService getInstance() {
|
||||
return tts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates the input string using whichever TextTranslator Service Provider
|
||||
* was found during lookup.
|
||||
* Update the translator currently in use to match the one saved to the user
|
||||
* preferences
|
||||
*/
|
||||
public void updateSelectedTranslator() {
|
||||
String translatorName = UserPreferences.getTextTranslatorName();
|
||||
for (TextTranslator translator : translators) {
|
||||
if (translator.getName().equals(translatorName)) {
|
||||
selectedTranslator = Optional.ofNullable(translator);
|
||||
return;
|
||||
}
|
||||
}
|
||||
selectedTranslator = Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates the input string using whichever TextTranslator Service
|
||||
* Provider was found during lookup.
|
||||
*
|
||||
* @param input Input string to be translated
|
||||
*
|
||||
@ -58,20 +76,48 @@ public final class TextTranslationService {
|
||||
* implementations fail
|
||||
*/
|
||||
public String translate(String input) throws NoServiceProviderException, TranslationException {
|
||||
if (translator.isPresent()) {
|
||||
return translator.get().translate(input);
|
||||
if (hasProvider()) {
|
||||
return selectedTranslator.get().translate(input);
|
||||
}
|
||||
throw new NoServiceProviderException(
|
||||
"Could not find a TextTranslator service provider");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific translator by name
|
||||
*
|
||||
* @param translatorName the name of the translator to get
|
||||
*
|
||||
* @return the translator which matches the name specified
|
||||
*
|
||||
* @throws NoServiceProviderException
|
||||
*/
|
||||
public TextTranslator getTranslatorByName(String translatorName) throws NoServiceProviderException {
|
||||
for (TextTranslator translator : translators) {
|
||||
if (translator.getName().equals(translatorName)) {
|
||||
return translator;
|
||||
}
|
||||
}
|
||||
throw new NoServiceProviderException(
|
||||
"Could not find the specified TextTranslator service provider: " + translatorName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the TextTranslator implementations which were found to exist
|
||||
*
|
||||
* @return an unmodifiable collection of TextTranslators
|
||||
*/
|
||||
public Collection<? extends TextTranslator> getTranslators() {
|
||||
return Collections.unmodifiableCollection(translators);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if a TextTranslator lookup successfully found an implementing
|
||||
* class.
|
||||
*
|
||||
* @return
|
||||
* @return
|
||||
*/
|
||||
public boolean hasProvider() {
|
||||
return translator.isPresent();
|
||||
return selectedTranslator.isPresent();
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2018-2018 Basis Technology Corp.
|
||||
* Copyright 2018-2019 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -18,12 +18,42 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.texttranslation;
|
||||
|
||||
import java.awt.Component;
|
||||
|
||||
/**
|
||||
* Interface for creating text translators. Implementing classes will be picked
|
||||
* up and run by the Text Translation Service.
|
||||
*/
|
||||
public interface TextTranslator {
|
||||
|
||||
/**
|
||||
* Translates a provided string
|
||||
*
|
||||
* @param input the String to translate
|
||||
*
|
||||
* @return the translated String
|
||||
*
|
||||
* @throws TranslationException
|
||||
*/
|
||||
String translate(String input) throws TranslationException;
|
||||
|
||||
|
||||
/**
|
||||
* Get the name of the TextTranslator implementation
|
||||
*
|
||||
* @return the name of the TextTranslator
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Get the component to display on the settings options panel when this
|
||||
* TextTranslator is selected
|
||||
*
|
||||
* @return the component which displays the settings options
|
||||
*/
|
||||
Component getComponent();
|
||||
|
||||
/**
|
||||
* Save the settings as they have been modified in the component.
|
||||
*/
|
||||
void saveSettings();
|
||||
}
|
||||
|
@ -0,0 +1,8 @@
|
||||
OptionsCategory_Name_Machine_Translation=Machine Translation
|
||||
OptionsCategory_Keywords_Machine_Translation_Settings=Machine Translation Settings
|
||||
TranslationContentPanel.ShowLabel.text=Show:
|
||||
TranslationContentPanel.warningLabel2MB.text=Only the first 1MB of text will be displayed
|
||||
TranslationContentPanel.ocrLabel.text=OCR:
|
||||
TranslationOptionsPanel.translationServiceLabel.text=Text translator:
|
||||
TranslationOptionsPanelController.moduleErr=Module Error
|
||||
TranslationOptionsPanelController.moduleErr.msg=A module caused an error listening to TranslationSettingsPanelController updates. See log to determine which module. Some data could be incomplete.
|
@ -1,3 +1,5 @@
|
||||
OptionsCategory_Name_Machine_Translation=Machine Translation
|
||||
OptionsCategory_Keywords_Machine_Translation_Settings=Machine Translation Settings
|
||||
TranslatedContentPanel.comboBoxOption.originalText=Original Text
|
||||
TranslatedContentPanel.comboBoxOption.translatedText=Translated Text
|
||||
TranslatedContentViewer.emptyTranslation=The resulting translation was empty.
|
||||
@ -13,6 +15,13 @@ TranslatedContentViewer.translationException=Error encountered while attempting
|
||||
TranslatedTextViewer.title=Translation
|
||||
TranslatedTextViewer.toolTip=Displays translated file text.
|
||||
TranslationContentPanel.autoDetectOCR=Autodetect language
|
||||
TranslationContentPanel.warningLabel2MB.text=Only the first 1MB of text will be displayed
|
||||
TranslationContentPanel.ShowLabel.text=Show:
|
||||
TranslationContentPanel.warningLabel2MB.text=Only the first 1MB of text will be displayed
|
||||
TranslationContentPanel.ocrLabel.text=OCR:
|
||||
TranslationOptionsPanel.noTextTranslators.text=No text translators exist, translation is disabled.
|
||||
TranslationOptionsPanel.noTextTranslatorSelected.text=No text translator selected, translation is disabled.
|
||||
TranslationOptionsPanel.textTranslatorsUnavailable.text=Unable to get selected text translator, translation is disabled.
|
||||
TranslationOptionsPanel.translationDisabled.text=Translation disabled
|
||||
TranslationOptionsPanel.translationServiceLabel.text=Text translator:
|
||||
TranslationOptionsPanelController.moduleErr=Module Error
|
||||
TranslationOptionsPanelController.moduleErr.msg=A module caused an error listening to TranslationSettingsPanelController updates. See log to determine which module. Some data could be incomplete.
|
@ -16,7 +16,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.translation;
|
||||
package org.sleuthkit.autopsy.texttranslation.ui;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
@ -53,7 +53,7 @@ import org.sleuthkit.autopsy.texttranslation.TranslationException;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import java.util.List;
|
||||
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
|
||||
import org.sleuthkit.autopsy.translation.TranslationContentPanel.DisplayDropdownOptions;
|
||||
import org.sleuthkit.autopsy.texttranslation.ui.TranslationContentPanel.DisplayDropdownOptions;
|
||||
|
||||
/**
|
||||
* A TextViewer that displays machine translation of text.
|
||||
@ -204,7 +204,7 @@ public final class TranslatedTextViewer implements TextViewer {
|
||||
@Override
|
||||
public void done() {
|
||||
try {
|
||||
String result = get();
|
||||
String result = get();
|
||||
if (this.isCancelled()) {
|
||||
throw new InterruptedException();
|
||||
}
|
||||
@ -294,12 +294,15 @@ public final class TranslatedTextViewer implements TextViewer {
|
||||
/**
|
||||
* Fetches text from a file.
|
||||
*
|
||||
* @param fileReader Reader instance containing file text
|
||||
* @param source the AbstractFile source to get a Reader for
|
||||
* @param ocrEnabled true if OCR is enabled false otherwise
|
||||
*
|
||||
* @return Extracted Text
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws InterruptedException
|
||||
* @throws
|
||||
* org.sleuthkit.autopsy.textextractors.TextExtractor.InitReaderException
|
||||
*/
|
||||
private String extractText(AbstractFile source, boolean ocrEnabled) throws IOException, InterruptedException, TextExtractor.InitReaderException {
|
||||
Reader textExtractor = getTextExtractor(source, ocrEnabled);
|
@ -102,7 +102,7 @@
|
||||
<Component class="javax.swing.JLabel" name="ShowLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/translation/Bundle.properties" key="TranslationContentPanel.ShowLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/texttranslation/ui/Bundle.properties" key="TranslationContentPanel.ShowLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
@ -148,7 +148,7 @@
|
||||
<Component class="javax.swing.JLabel" name="ocrLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/translation/Bundle.properties" key="TranslationContentPanel.ocrLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/texttranslation/ui/Bundle.properties" key="TranslationContentPanel.ocrLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="enabled" type="boolean" value="false"/>
|
||||
</Properties>
|
||||
@ -159,7 +159,7 @@
|
||||
<Image iconType="3" name="/org/sleuthkit/autopsy/images/warning16.png"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/translation/Bundle.properties" key="TranslationContentPanel.warningLabel2MB.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/texttranslation/ui/Bundle.properties" key="TranslationContentPanel.warningLabel2MB.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
@ -16,7 +16,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.translation;
|
||||
package org.sleuthkit.autopsy.texttranslation.ui;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableBiMap;
|
||||
@ -362,7 +362,7 @@ public class TranslationContentPanel extends javax.swing.JPanel {
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(jPanel1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 628, Short.MAX_VALUE)
|
||||
.addComponent(jPanel1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 628, Short.MAX_VALUE)
|
||||
.addComponent(jScrollPane1)
|
||||
);
|
||||
layout.setVerticalGroup(
|
@ -0,0 +1,70 @@
|
||||
<?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">
|
||||
<Component id="translationServiceLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||
<Component id="translatorComboBox" min="-2" pref="214" max="-2" attributes="0"/>
|
||||
<EmptySpace min="0" pref="162" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<Component id="translationServicePanel" alignment="1" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</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"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="translatorComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="translationServiceLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="translationServicePanel" max="32767" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JComboBox" name="translatorComboBox">
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="translatorComboBoxActionPerformed"/>
|
||||
</Events>
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="<String>"/>
|
||||
</AuxValues>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="translationServiceLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/texttranslation/ui/Bundle.properties" key="TranslationOptionsPanel.translationServiceLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Container class="javax.swing.JPanel" name="translationServicePanel">
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Form>
|
@ -0,0 +1,198 @@
|
||||
/*
|
||||
* 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.texttranslation.ui;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.JLabel;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.core.UserPreferences;
|
||||
import org.sleuthkit.autopsy.texttranslation.NoServiceProviderException;
|
||||
import org.sleuthkit.autopsy.texttranslation.TextTranslationService;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
|
||||
/**
|
||||
* Options panel to display translation options
|
||||
*/
|
||||
public class TranslationOptionsPanel extends javax.swing.JPanel {
|
||||
|
||||
private final static Logger logger = Logger.getLogger(TranslationOptionsPanel.class.getName());
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final TranslationOptionsPanelController controller;
|
||||
private String currentSelection = "";
|
||||
|
||||
/**
|
||||
* Creates new form TranslationOptionsPanel
|
||||
*/
|
||||
@Messages({"TranslationOptionsPanel.translationDisabled.text=Translation disabled"})
|
||||
public TranslationOptionsPanel(TranslationOptionsPanelController theController) {
|
||||
initComponents();
|
||||
controller = theController;
|
||||
translatorComboBox.addItem(Bundle.TranslationOptionsPanel_translationDisabled_text());
|
||||
TextTranslationService.getInstance().getTranslators().forEach((translator) -> {
|
||||
translatorComboBox.addItem(translator.getName());
|
||||
});
|
||||
translatorComboBox.setEnabled(translatorComboBox.getItemCount() > 1);
|
||||
load();
|
||||
}
|
||||
|
||||
/**
|
||||
* Private helper method to update the panel if the selected TextTranslator
|
||||
* has changed
|
||||
*/
|
||||
private void updatePanel() {
|
||||
if (!currentSelection.equals(translatorComboBox.getSelectedItem().toString())) {
|
||||
currentSelection = translatorComboBox.getSelectedItem().toString();
|
||||
loadSelectedPanelSettings();
|
||||
controller.changed();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the settings for the selected TextTranslator's panel
|
||||
*/
|
||||
@Messages({"TranslationOptionsPanel.textTranslatorsUnavailable.text=Unable to get selected text translator, translation is disabled.",
|
||||
"TranslationOptionsPanel.noTextTranslatorSelected.text=No text translator selected, translation is disabled.",
|
||||
"TranslationOptionsPanel.noTextTranslators.text=No text translators exist, translation is disabled."})
|
||||
private void loadSelectedPanelSettings() {
|
||||
translationServicePanel.removeAll();
|
||||
if (translatorComboBox.getSelectedItem() != null && !translatorComboBox.getSelectedItem().toString().equals(Bundle.TranslationOptionsPanel_translationDisabled_text())) {
|
||||
try {
|
||||
Component panel = TextTranslationService.getInstance().getTranslatorByName(translatorComboBox.getSelectedItem().toString()).getComponent();
|
||||
panel.addPropertyChangeListener(new PropertyChangeListener() {
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
controller.changed();
|
||||
}
|
||||
});
|
||||
translationServicePanel.add(panel, BorderLayout.PAGE_START);
|
||||
} catch (NoServiceProviderException ex) {
|
||||
logger.log(Level.WARNING, "Unable to get TextExtractor named: " + translatorComboBox.getSelectedItem().toString(), ex);
|
||||
JLabel label = new JLabel(Bundle.TranslationOptionsPanel_textTranslatorsUnavailable_text());
|
||||
label.setForeground(Color.RED);
|
||||
translationServicePanel.add(label, BorderLayout.PAGE_START);
|
||||
}
|
||||
} else {
|
||||
if (translatorComboBox.getItemCount() < 2) {
|
||||
translationServicePanel.add(new JLabel(Bundle.TranslationOptionsPanel_noTextTranslators_text()), BorderLayout.PAGE_START);
|
||||
} else {
|
||||
translationServicePanel.add(new JLabel(Bundle.TranslationOptionsPanel_noTextTranslatorSelected_text()), BorderLayout.PAGE_START);
|
||||
}
|
||||
}
|
||||
revalidate();
|
||||
repaint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load settings from user preferences and update the UI
|
||||
*/
|
||||
final void load() {
|
||||
currentSelection = UserPreferences.getTextTranslatorName();
|
||||
if (currentSelection == null) {
|
||||
currentSelection = Bundle.TranslationOptionsPanel_translationDisabled_text();
|
||||
}
|
||||
translatorComboBox.setSelectedItem(currentSelection);
|
||||
loadSelectedPanelSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the current settings
|
||||
*/
|
||||
void store() {
|
||||
//The current text translator name is saved to user preferences
|
||||
UserPreferences.setTextTranslatorName(currentSelection);
|
||||
//The TextTranslationService updates the TextTranslator in use from user preferences
|
||||
TextTranslationService.getInstance().updateSelectedTranslator();
|
||||
if (currentSelection != null && !currentSelection.equals(Bundle.TranslationOptionsPanel_translationDisabled_text())) {
|
||||
try {
|
||||
TextTranslationService.getInstance().getTranslatorByName(currentSelection).saveSettings();
|
||||
} catch (NoServiceProviderException ex) {
|
||||
logger.log(Level.WARNING, "Unable to save settings for TextTranslator named: " + currentSelection, ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
|
||||
translatorComboBox = new javax.swing.JComboBox<>();
|
||||
translationServiceLabel = new javax.swing.JLabel();
|
||||
translationServicePanel = new javax.swing.JPanel();
|
||||
|
||||
translatorComboBox.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
translatorComboBoxActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(translationServiceLabel, org.openide.util.NbBundle.getMessage(TranslationOptionsPanel.class, "TranslationOptionsPanel.translationServiceLabel.text")); // NOI18N
|
||||
|
||||
translationServicePanel.setLayout(new java.awt.BorderLayout());
|
||||
|
||||
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()
|
||||
.addComponent(translationServiceLabel)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addComponent(translatorComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 214, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(0, 162, Short.MAX_VALUE))
|
||||
.addComponent(translationServicePanel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
.addContainerGap())
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(translatorComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(translationServiceLabel))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(translationServicePanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addContainerGap())
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
private void translatorComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_translatorComboBoxActionPerformed
|
||||
updatePanel();
|
||||
}//GEN-LAST:event_translatorComboBoxActionPerformed
|
||||
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JLabel translationServiceLabel;
|
||||
private javax.swing.JPanel translationServicePanel;
|
||||
private javax.swing.JComboBox<String> translatorComboBox;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
}
|
@ -0,0 +1,141 @@
|
||||
/*
|
||||
* 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.texttranslation.ui;
|
||||
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.beans.PropertyChangeSupport;
|
||||
import javax.swing.JComponent;
|
||||
import org.netbeans.spi.options.OptionsPanelController;
|
||||
import org.openide.util.HelpCtx;
|
||||
import org.openide.util.Lookup;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
||||
import java.util.logging.Level;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
|
||||
/**
|
||||
* Controller for the TranslationOptionsPanel
|
||||
*/
|
||||
@OptionsPanelController.TopLevelRegistration(categoryName = "#OptionsCategory_Name_Machine_Translation",
|
||||
iconBase = "org/sleuthkit/autopsy/images/translate32.png",
|
||||
position = 7,
|
||||
keywords = "#OptionsCategory_Keywords_Machine_Translation_Settings",
|
||||
keywordsCategory = "Machine Translation")
|
||||
public class TranslationOptionsPanelController extends OptionsPanelController {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(TranslationOptionsPanelController.class.getName());
|
||||
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
|
||||
private boolean changed;
|
||||
private TranslationOptionsPanel panel;
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
getPanel().load();
|
||||
changed = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyChanges() {
|
||||
getPanel().store();
|
||||
changed = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChanged() {
|
||||
return changed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HelpCtx getHelpCtx() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent getComponent(Lookup masterLookup) {
|
||||
return getPanel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPropertyChangeListener(PropertyChangeListener l) {
|
||||
if (pcs.getPropertyChangeListeners().length == 0) {
|
||||
pcs.addPropertyChangeListener(l);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePropertyChangeListener(PropertyChangeListener l) {
|
||||
/**
|
||||
* Note the NetBeans Framework does not appear to call this at all. We
|
||||
* are using NetBeans 7.3.1 Build 201306052037. Perhaps in a future
|
||||
* version of the Framework this will be resolved, but for now, simply
|
||||
* don't unregister anything and add one time only in the
|
||||
* addPropertyChangeListener() method above.
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the translation options panel
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private TranslationOptionsPanel getPanel() {
|
||||
if (panel == null) {
|
||||
panel = new TranslationOptionsPanel(this);
|
||||
}
|
||||
return panel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that a setting has been changed in the options panel
|
||||
*/
|
||||
void changed() {
|
||||
if (!changed) {
|
||||
changed = true;
|
||||
|
||||
try {
|
||||
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, false, true);
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "TranslationOptionsPanelController listener threw exception", e); //NON-NLS
|
||||
MessageNotifyUtil.Notify.show(
|
||||
NbBundle.getMessage(this.getClass(), "TranslationOptionsPanelController.moduleErr"),
|
||||
NbBundle.getMessage(this.getClass(), "TranslationOptionsPanelController.moduleErr.msg"),
|
||||
MessageNotifyUtil.MessageType.ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
pcs.firePropertyChange(OptionsPanelController.PROP_VALID, null, null);
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "TranslationOptionsPanelController listener threw exception", e); //NON-NLS
|
||||
MessageNotifyUtil.Notify.show(
|
||||
NbBundle.getMessage(this.getClass(), "TranslationOptionsPanelController.moduleErr"),
|
||||
NbBundle.getMessage(this.getClass(), "TranslationOptionsPanelController.moduleErr.msg"),
|
||||
MessageNotifyUtil.MessageType.ERROR);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*=
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2013-15 Basis Technology Corp.
|
||||
@ -14,8 +14,8 @@
|
||||
* 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.
|
||||
*/
|
||||
*=limitations under the License.
|
||||
*/=
|
||||
|
||||
AbstractTimelineChart.defaultTooltip.text=Drag the mouse to select a time interval to zoom into.\nRight-click for more actions.
|
||||
HistoryToolBar.historyLabel.text=History
|
||||
|
@ -1,3 +0,0 @@
|
||||
TranslationContentPanel.warningLabel2MB.text=Only the first 1MB of text will be displayed
|
||||
TranslationContentPanel.ShowLabel.text=Show:
|
||||
TranslationContentPanel.ocrLabel.text=OCR:
|
@ -141,6 +141,7 @@ class ContactAnalyzer(general.AndroidComponentAnalyzer):
|
||||
name = resultSet.getString("display_name")
|
||||
data1 = resultSet.getString("data1") # the phone number or email
|
||||
mimetype = resultSet.getString("mimetype") # either phone or email
|
||||
attributes = ArrayList()
|
||||
if name != oldName:
|
||||
artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT)
|
||||
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME, general.MODULE_NAME, name))
|
||||
|
@ -24,6 +24,7 @@ KeywordSearchIngestModule.init.badInitMsg=Keyword search server was not properly
|
||||
KeywordSearchIngestModule.init.exception.errConnToSolr.msg=Error connecting to SOLR server: {0}.
|
||||
# {0} - Reason for not starting Solr
|
||||
KeywordSearchIngestModule.init.tryStopSolrMsg={0}<br />Please try stopping Java Solr processes if any exist and restart the application.
|
||||
KeywordSearchIngestModule.metadataTitle=METADATA
|
||||
KeywordSearchIngestModule.noOpenCase.errMsg=No open case available.
|
||||
KeywordSearchIngestModule.startUp.noOpenCore.msg=The index could not be opened or does not exist.
|
||||
# {0} - schema version number
|
||||
@ -35,7 +36,7 @@ KeywordSearchResultFactory.createNodeForKey.noResultsFound.text=No results found
|
||||
KeywordSearchResultFactory.query.exception.msg=Could not perform the query
|
||||
OpenIDE-Module-Display-Category=Ingest Module
|
||||
|
||||
OpenIDE-Module-Long-Description=Keyword Search ingest module.\n\nThe module indexes files found in the disk image at ingest time.\nIt then periodically runs the search on the indexed files using one or more keyword lists (containing pure words and/or regular expressions) and posts results.\n\n\The module also contains additional tools integrated in the main GUI, such as keyword list configuration, keyword search bar in the top-right corner, extracted text viewer and search results viewer showing highlighted keywords found.
|
||||
OpenIDE-Module-Long-Description=Keyword Search ingest module.\n\nThe module indexes files found in the disk image at ingest time.\nIt then periodically runs the search on the indexed files using one or more keyword lists (containing pure words and/or regular expressions) and posts results.\n\nThe module also contains additional tools integrated in the main GUI, such as keyword list configuration, keyword search bar in the top-right corner, extracted text viewer and search results viewer showing highlighted keywords found.
|
||||
OpenIDE-Module-Name=KeywordSearch
|
||||
OptionsCategory_Name_KeywordSearchOptions=Keyword Search
|
||||
OptionsCategory_Keywords_KeywordSearchOptions=Keyword Search
|
||||
|
@ -123,15 +123,13 @@ class Ingester {
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the given TextExtractor to extract text from the given source. The
|
||||
* text will be chunked and each chunk passed to Solr to add to the index.
|
||||
* Read and chunk the source text for indexing in Solr.
|
||||
*
|
||||
*
|
||||
* @param <A> The type of the Appendix provider that provides
|
||||
* additional text to append to the final chunk.
|
||||
* @param <T> A subclass of SleuthkitVisibleItem.
|
||||
* @param extractor The TextExtractor that will be used to extract text from
|
||||
* the given source.
|
||||
* @param Reader The reader containing extracted text.
|
||||
* @param source The source from which text will be extracted, chunked,
|
||||
* and indexed.
|
||||
* @param context The ingest job context that can be used to cancel this
|
||||
|
@ -19,12 +19,15 @@
|
||||
package org.sleuthkit.autopsy.keywordsearch;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.io.CharSource;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import org.openide.util.Lookup;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
@ -170,8 +173,8 @@ public final class KeywordSearchIngestModule implements FileIngestModule {
|
||||
* for final statistics at the end of the job.
|
||||
*
|
||||
* @param ingestJobId id of ingest job
|
||||
* @param fileId id of file
|
||||
* @param status ingest status of the file
|
||||
* @param fileId id of file
|
||||
* @param status ingest status of the file
|
||||
*/
|
||||
private static void putIngestStatus(long ingestJobId, long fileId, IngestStatus status) {
|
||||
synchronized (ingestStatus) {
|
||||
@ -469,15 +472,14 @@ public final class KeywordSearchIngestModule implements FileIngestModule {
|
||||
* streaming) from the file Divide the file into chunks and index the
|
||||
* chunks
|
||||
*
|
||||
* @param aFile file to extract strings from, divide into
|
||||
* chunks and index
|
||||
* @param detectedFormat mime-type detected, or null if none detected
|
||||
* @param aFile file to extract strings from, divide into chunks and
|
||||
* index
|
||||
*
|
||||
* @return true if the file was text_ingested, false otherwise
|
||||
*
|
||||
* @throws IngesterException exception thrown if indexing failed
|
||||
*/
|
||||
private boolean extractTextAndIndex(AbstractFile aFile, String detectedFormat) throws IngesterException {
|
||||
private boolean extractTextAndIndex(AbstractFile aFile) throws IngesterException {
|
||||
ImageConfig imageConfig = new ImageConfig();
|
||||
imageConfig.setOCREnabled(KeywordSearchSettings.getOcrOption());
|
||||
ProcessTerminator terminator = () -> context.fileIngestIsCancelled();
|
||||
@ -485,20 +487,59 @@ public final class KeywordSearchIngestModule implements FileIngestModule {
|
||||
|
||||
try {
|
||||
TextExtractor extractor = TextExtractorFactory.getExtractor(aFile, extractionContext);
|
||||
Reader extractedTextReader = extractor.getReader();
|
||||
Reader fileText = extractor.getReader();
|
||||
|
||||
Reader finalReader;
|
||||
try {
|
||||
Map<String, String> metadata = extractor.getMetadata();
|
||||
CharSource formattedMetadata = getMetaDataCharSource(metadata);
|
||||
//Append the metadata to end of the file text
|
||||
finalReader = CharSource.concat(new CharSource() {
|
||||
//Wrap fileText reader for concatenation
|
||||
@Override
|
||||
public Reader openStream() throws IOException {
|
||||
return fileText;
|
||||
}
|
||||
}, formattedMetadata).openStream();
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.WARNING, String.format("Could not format extracted metadata for file %s [id=%d]",
|
||||
aFile.getName(), aFile.getId()), ex);
|
||||
//Just send file text.
|
||||
finalReader = fileText;
|
||||
}
|
||||
//divide into chunks and index
|
||||
return Ingester.getDefault().indexText(extractedTextReader, aFile.getId(), aFile.getName(), aFile, context);
|
||||
return Ingester.getDefault().indexText(finalReader, aFile.getId(), aFile.getName(), aFile, context);
|
||||
} catch (TextExtractorFactory.NoTextExtractorFound | TextExtractor.InitReaderException ex) {
|
||||
//No text extractor found... run the default instead
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pretty print the text extractor metadata.
|
||||
*
|
||||
* @param metadata The Metadata map to wrap as a CharSource
|
||||
*
|
||||
* @return A CharSource for the given Metadata
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"KeywordSearchIngestModule.metadataTitle=METADATA"
|
||||
})
|
||||
private CharSource getMetaDataCharSource(Map<String, String> metadata) {
|
||||
return CharSource.wrap(new StringBuilder(
|
||||
String.format("\n\n------------------------------%s------------------------------\n\n",
|
||||
Bundle.KeywordSearchIngestModule_metadataTitle()))
|
||||
.append(metadata.entrySet().stream().sorted(Map.Entry.comparingByKey())
|
||||
.map(entry -> entry.getKey() + ": " + entry.getValue())
|
||||
.collect(Collectors.joining("\n"))
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract strings using heuristics from the file and add to index.
|
||||
*
|
||||
* @param aFile file to extract strings from, divide into chunks and
|
||||
* index
|
||||
* index
|
||||
*
|
||||
* @return true if the file was text_ingested, false otherwise
|
||||
*/
|
||||
@ -527,9 +568,9 @@ public final class KeywordSearchIngestModule implements FileIngestModule {
|
||||
/**
|
||||
* Adds the file to the index. Detects file type, calls extractors, etc.
|
||||
*
|
||||
* @param aFile File to analyze
|
||||
* @param aFile File to analyze
|
||||
* @param indexContent False if only metadata should be text_ingested.
|
||||
* True if content and metadata should be index.
|
||||
* True if content and metadata should be index.
|
||||
*/
|
||||
private void indexFile(AbstractFile aFile, boolean indexContent) {
|
||||
//logger.log(Level.INFO, "Processing AbstractFile: " + abstractFile.getName());
|
||||
@ -595,7 +636,7 @@ public final class KeywordSearchIngestModule implements FileIngestModule {
|
||||
extractStringsAndIndex(aFile);
|
||||
return;
|
||||
}
|
||||
if (!extractTextAndIndex(aFile, fileType)) {
|
||||
if (!extractTextAndIndex(aFile)) {
|
||||
// Text extractor not found for file. Extract string only.
|
||||
putIngestStatus(jobId, aFile.getId(), IngestStatus.SKIPPED_ERROR_TEXTEXTRACT);
|
||||
} else {
|
||||
|
@ -64,7 +64,7 @@ ExtractZone_progress_Msg=Extracting :Zone.Identifer files
|
||||
ExtractZone_Restricted=Restricted Sites Zone
|
||||
ExtractZone_Trusted=Trusted Sites Zone
|
||||
OpenIDE-Module-Display-Category=Ingest Module
|
||||
OpenIDE-Module-Long-Description=Recent Activity ingest module.\n\n\The module extracts useful information about the recent user activity on the disk image being ingested, such as:\n\n- Recently open documents,\n- Web activity (sites visited, stored cookies, book marked sites, search engine queries, file downloads),\n- Recently attached devices,\n- Installed programs.\n\nThe module currently supports Windows only disk images.\nThe plugin is also fully functional when deployed on Windows version of Autopsy.
|
||||
OpenIDE-Module-Long-Description=Recent Activity ingest module.\n\nThe module extracts useful information about the recent user activity on the disk image being ingested, such as:\n\n- Recently open documents,\n- Web activity (sites visited, stored cookies, book marked sites, search engine queries, file downloads),\n- Recently attached devices,\n- Installed programs.\n\nThe module currently supports Windows only disk images.\nThe plugin is also fully functional when deployed on Windows version of Autopsy.
|
||||
OpenIDE-Module-Name=RecentActivity
|
||||
OpenIDE-Module-Short-Description=Recent Activity finder ingest module
|
||||
Chrome.moduleName=Chrome
|
||||
|
@ -22,11 +22,16 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.recentactivity;
|
||||
|
||||
import java.io.*;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
import java.util.logging.Level;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
@ -40,7 +45,6 @@ import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
||||
import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProcessTerminator;
|
||||
import org.sleuthkit.autopsy.ingest.IngestJobContext;
|
||||
import org.sleuthkit.autopsy.recentactivity.UsbDeviceIdMapper.USBInfo;
|
||||
import org.sleuthkit.datamodel.*;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
|
||||
import org.w3c.dom.Document;
|
||||
@ -50,6 +54,14 @@ import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Scanner;
|
||||
import java.util.Set;
|
||||
import java.util.HashSet;
|
||||
import static java.util.TimeZone.getTimeZone;
|
||||
import org.openide.util.Lookup;
|
||||
import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress;
|
||||
@ -57,7 +69,13 @@ import org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException;
|
||||
import org.sleuthkit.autopsy.ingest.IngestServices;
|
||||
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
|
||||
import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchService;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException;
|
||||
import org.sleuthkit.datamodel.Report;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* Extract windows registry data using regripper. Runs two versions of
|
||||
@ -72,21 +90,19 @@ import org.sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamExce
|
||||
})
|
||||
class ExtractRegistry extends Extract {
|
||||
|
||||
private final Logger logger = Logger.getLogger(this.getClass().getName());
|
||||
private String RR_PATH;
|
||||
private String RR_FULL_PATH;
|
||||
private Path rrHome; // Path to the Autopsy version of RegRipper
|
||||
private Path rrFullHome; // Path to the full version of RegRipper
|
||||
private Content dataSource;
|
||||
private IngestJobContext context;
|
||||
final private static UsbDeviceIdMapper USB_MAPPER = new UsbDeviceIdMapper();
|
||||
final private static String RIP_EXE = "rip.exe";
|
||||
final private static String RIP_PL = "rip.pl";
|
||||
final private static int MS_IN_SEC = 1000;
|
||||
final private static String NEVER_DATE = "Never";
|
||||
final private static String SECTION_DIVIDER = "-------------------------";
|
||||
final private static Logger logger = Logger.getLogger(ExtractRegistry.class.getName());
|
||||
private final List<String> rrCmd = new ArrayList<>();
|
||||
private final List<String> rrFullCmd = new ArrayList<>();
|
||||
private final Path rrHome; // Path to the Autopsy version of RegRipper
|
||||
private final Path rrFullHome; // Path to the full version of RegRipper
|
||||
private Content dataSource;
|
||||
private IngestJobContext context;
|
||||
|
||||
ExtractRegistry() throws IngestModuleException {
|
||||
moduleName = NbBundle.getMessage(ExtractIE.class, "ExtractRegistry.moduleName.text");
|
||||
@ -106,19 +122,19 @@ class ExtractRegistry extends Extract {
|
||||
executableToRun = RIP_PL;
|
||||
}
|
||||
rrHome = rrRoot.toPath();
|
||||
RR_PATH = rrHome.resolve(executableToRun).toString();
|
||||
String rrPath = rrHome.resolve(executableToRun).toString();
|
||||
rrFullHome = rrFullRoot.toPath();
|
||||
RR_FULL_PATH = rrFullHome.resolve(executableToRun).toString();
|
||||
|
||||
if (!(new File(RR_PATH).exists())) {
|
||||
if (!(new File(rrPath).exists())) {
|
||||
throw new IngestModuleException(Bundle.RegRipperNotFound());
|
||||
}
|
||||
if (!(new File(RR_FULL_PATH).exists())) {
|
||||
String rrFullPath = rrFullHome.resolve(executableToRun).toString();
|
||||
if (!(new File(rrFullPath).exists())) {
|
||||
throw new IngestModuleException(Bundle.RegRipperFullNotFound());
|
||||
}
|
||||
if (PlatformUtil.isWindowsOS()) {
|
||||
rrCmd.add(RR_PATH);
|
||||
rrFullCmd.add(RR_FULL_PATH);
|
||||
rrCmd.add(rrPath);
|
||||
rrFullCmd.add(rrFullPath);
|
||||
} else {
|
||||
String perl;
|
||||
File usrBin = new File("/usr/bin/perl");
|
||||
@ -131,9 +147,9 @@ class ExtractRegistry extends Extract {
|
||||
throw new IngestModuleException("perl not found in your system");
|
||||
}
|
||||
rrCmd.add(perl);
|
||||
rrCmd.add(RR_PATH);
|
||||
rrCmd.add(rrPath);
|
||||
rrFullCmd.add(perl);
|
||||
rrFullCmd.add(RR_FULL_PATH);
|
||||
rrFullCmd.add(rrFullPath);
|
||||
}
|
||||
}
|
||||
|
||||
@ -224,23 +240,19 @@ class ExtractRegistry extends Extract {
|
||||
}
|
||||
|
||||
// parse the autopsy-specific output
|
||||
if (regOutputFiles.autopsyPlugins.isEmpty() == false) {
|
||||
if (parseAutopsyPluginOutput(regOutputFiles.autopsyPlugins, regFile) == false) {
|
||||
this.addErrorMessage(
|
||||
NbBundle.getMessage(this.getClass(), "ExtractRegistry.analyzeRegFiles.failedParsingResults",
|
||||
this.getName(), regFileName));
|
||||
}
|
||||
if (regOutputFiles.autopsyPlugins.isEmpty() == false && parseAutopsyPluginOutput(regOutputFiles.autopsyPlugins, regFile) == false) {
|
||||
this.addErrorMessage(
|
||||
NbBundle.getMessage(this.getClass(), "ExtractRegistry.analyzeRegFiles.failedParsingResults",
|
||||
this.getName(), regFileName));
|
||||
}
|
||||
|
||||
// create a report for the full output
|
||||
if (!regOutputFiles.fullPlugins.isEmpty()) {
|
||||
//parse the full regripper output from SAM hive files
|
||||
if (regFileNameLocal.toLowerCase().contains("sam")) {
|
||||
if (parseSamPluginOutput(regOutputFiles.fullPlugins, regFile) == false) {
|
||||
this.addErrorMessage(
|
||||
NbBundle.getMessage(this.getClass(), "ExtractRegistry.analyzeRegFiles.failedParsingResults",
|
||||
this.getName(), regFileName));
|
||||
}
|
||||
if (regFileNameLocal.toLowerCase().contains("sam") && parseSamPluginOutput(regOutputFiles.fullPlugins, regFile) == false) {
|
||||
this.addErrorMessage(
|
||||
NbBundle.getMessage(this.getClass(), "ExtractRegistry.analyzeRegFiles.failedParsingResults",
|
||||
this.getName(), regFileName));
|
||||
}
|
||||
try {
|
||||
Report report = currentCase.addReport(regOutputFiles.fullPlugins,
|
||||
@ -259,7 +271,6 @@ class ExtractRegistry extends Extract {
|
||||
this.addErrorMessage("Error adding regripper output as Autopsy report: " + e.getLocalizedMessage()); //NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
// delete the hive
|
||||
regFileNameLocalFile.delete();
|
||||
}
|
||||
@ -273,12 +284,6 @@ class ExtractRegistry extends Extract {
|
||||
}
|
||||
}
|
||||
|
||||
private class RegOutputFiles {
|
||||
|
||||
public String autopsyPlugins = "";
|
||||
public String fullPlugins = "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute regripper on the given registry.
|
||||
*
|
||||
@ -392,11 +397,11 @@ class ExtractRegistry extends Extract {
|
||||
// that we will submit in a ModuleDataEvent for additional processing.
|
||||
Collection<BlackboardArtifact> wifiBBartifacts = new ArrayList<>();
|
||||
for (int i = 0; i < len; i++) {
|
||||
|
||||
|
||||
if (context.dataSourceIngestIsCancelled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
Element tempnode = (Element) children.item(i);
|
||||
|
||||
String dataType = tempnode.getNodeName();
|
||||
@ -406,12 +411,11 @@ class ExtractRegistry extends Extract {
|
||||
Element timenode = (Element) timenodes.item(0);
|
||||
String etime = timenode.getTextContent();
|
||||
try {
|
||||
Long epochtime = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy").parse(etime).getTime();
|
||||
mtime = epochtime;
|
||||
mtime = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy").parse(etime).getTime();
|
||||
String Tempdate = mtime.toString();
|
||||
mtime = Long.valueOf(Tempdate) / MS_IN_SEC;
|
||||
} catch (ParseException ex) {
|
||||
logger.log(Level.WARNING, "Failed to parse epoch time when parsing the registry."); //NON-NLS
|
||||
logger.log(Level.WARNING, "Failed to parse epoch time when parsing the registry.", ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
@ -424,7 +428,6 @@ class ExtractRegistry extends Extract {
|
||||
Element artroot = (Element) artroots.item(0);
|
||||
NodeList myartlist = artroot.getChildNodes();
|
||||
String parentModuleName = RecentActivityExtracterModuleFactory.getModuleName();
|
||||
String winver = "";
|
||||
|
||||
// If all artifact nodes should really go under one Blackboard artifact, need to process it differently
|
||||
switch (dataType) {
|
||||
@ -508,7 +511,6 @@ class ExtractRegistry extends Extract {
|
||||
case "Profiler": // NON-NLS
|
||||
String os = "";
|
||||
String procArch = "";
|
||||
String procId = "";
|
||||
String tempDir = "";
|
||||
for (int j = 0; j < myartlist.getLength(); j++) {
|
||||
Node artchild = myartlist.item(j);
|
||||
@ -526,7 +528,6 @@ class ExtractRegistry extends Extract {
|
||||
procArch = value;
|
||||
break;
|
||||
case "PROCESSOR_IDENTIFIER": //NON-NLS
|
||||
procId = value;
|
||||
break;
|
||||
case "TEMP": //NON-NLS
|
||||
tempDir = value;
|
||||
@ -652,7 +653,7 @@ class ExtractRegistry extends Extract {
|
||||
try {
|
||||
Long epochtime = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy").parse(artnode.getAttribute("mtime")).getTime(); //NON-NLS
|
||||
itemMtime = epochtime;
|
||||
itemMtime = itemMtime / MS_IN_SEC;
|
||||
itemMtime /= MS_IN_SEC;
|
||||
} catch (ParseException e) {
|
||||
logger.log(Level.WARNING, "Failed to parse epoch time for installed program artifact."); //NON-NLS
|
||||
}
|
||||
@ -738,7 +739,7 @@ class ExtractRegistry extends Extract {
|
||||
} else {
|
||||
//add attributes to existing artifact
|
||||
BlackboardAttribute bbattr = bbart.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_USER_NAME));
|
||||
|
||||
|
||||
if (bbattr == null) {
|
||||
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME,
|
||||
parentModuleName, username));
|
||||
@ -852,10 +853,9 @@ class ExtractRegistry extends Extract {
|
||||
String line = bufferedReader.readLine();
|
||||
Set<UserInfo> userSet = new HashSet<>();
|
||||
while (line != null) {
|
||||
if (line.contains(SECTION_DIVIDER) && previousLine != null) {
|
||||
if (previousLine.contains(userInfoSection)) {
|
||||
readUsers(bufferedReader, userSet);
|
||||
}
|
||||
if (line.contains(SECTION_DIVIDER) && previousLine != null && previousLine.contains(userInfoSection)) {
|
||||
readUsers(bufferedReader, userSet);
|
||||
|
||||
}
|
||||
previousLine = line;
|
||||
line = bufferedReader.readLine();
|
||||
@ -923,7 +923,7 @@ class ExtractRegistry extends Extract {
|
||||
} catch (ParseException ex) {
|
||||
logger.log(Level.SEVERE, "Error parsing the the date from the registry file", ex); //NON-NLS
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Error updating TSK_OS_ACCOUNT artifacts to include newly parsed data.", ex); //NON-NLS
|
||||
logger.log(Level.SEVERE, "Error updating TSK_OS_ACCOUNT artifacts to include newly parsed data.", ex); //NON-NLS
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -953,8 +953,7 @@ class ExtractRegistry extends Extract {
|
||||
if (line.contains(userNameLabel)) {
|
||||
String userNameAndIdString = line.replace(userNameLabel, "");
|
||||
userName = userNameAndIdString.substring(0, userNameAndIdString.lastIndexOf('[')).trim();
|
||||
}
|
||||
else if (line.contains(sidLabel) && !userName.isEmpty()){
|
||||
} else if (line.contains(sidLabel) && !userName.isEmpty()) {
|
||||
String sid = line.replace(sidLabel, "").trim();
|
||||
UserInfo userInfo = new UserInfo(userName, sid);
|
||||
//continue reading this users information until end of file or a blank line between users
|
||||
@ -980,12 +979,21 @@ class ExtractRegistry extends Extract {
|
||||
public void process(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar) {
|
||||
this.dataSource = dataSource;
|
||||
this.context = context;
|
||||
|
||||
|
||||
progressBar.progress(Bundle.Progress_Message_Analyze_Registry());
|
||||
analyzeRegistryFiles();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Private wrapper class for Registry output files
|
||||
*/
|
||||
private class RegOutputFiles {
|
||||
|
||||
public String autopsyPlugins = "";
|
||||
public String fullPlugins = "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Class for organizing information associated with a TSK_OS_ACCOUNT before
|
||||
* the artifact is created.
|
||||
@ -1001,7 +1009,7 @@ class ExtractRegistry extends Extract {
|
||||
/**
|
||||
* Create a UserInfo object
|
||||
*
|
||||
* @param name - the os user account name
|
||||
* @param name - the os user account name
|
||||
* @param userSidString - the SID for the user account
|
||||
*/
|
||||
private UserInfo(String name, String userSidString) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
#Updated by build script
|
||||
#Tue, 26 Feb 2019 14:37:44 -0500
|
||||
#Wed, 08 May 2019 21:37:02 -0400
|
||||
LBL_splash_window_title=Starting Autopsy
|
||||
SPLASH_HEIGHT=314
|
||||
SPLASH_WIDTH=538
|
||||
@ -8,4 +8,4 @@ SplashRunningTextBounds=0,289,538,18
|
||||
SplashRunningTextColor=0x0
|
||||
SplashRunningTextFontSize=19
|
||||
|
||||
currentVersion=Autopsy 4.10.0
|
||||
currentVersion=Autopsy 4.11.0
|
||||
|
@ -1,4 +1,4 @@
|
||||
#Updated by build script
|
||||
#Tue, 26 Feb 2019 14:37:44 -0500
|
||||
CTL_MainWindow_Title=Autopsy 4.10.0
|
||||
CTL_MainWindow_Title_No_Project=Autopsy 4.10.0
|
||||
#Wed, 08 May 2019 21:37:02 -0400
|
||||
CTL_MainWindow_Title=Autopsy 4.11.0
|
||||
CTL_MainWindow_Title_No_Project=Autopsy 4.11.0
|
||||
|
@ -193,7 +193,7 @@ final class VcardParser {
|
||||
}
|
||||
}
|
||||
}
|
||||
ThunderbirdMboxFileIngestModule.addArtifactAttribute(name, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME_PERSON, attributes);
|
||||
ThunderbirdMboxFileIngestModule.addArtifactAttribute(name, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME, attributes);
|
||||
|
||||
for (Telephone telephone : vcard.getTelephoneNumbers()) {
|
||||
addPhoneAttributes(telephone, abstractFile, attributes);
|
||||
@ -412,7 +412,7 @@ final class VcardParser {
|
||||
type.getValue().toUpperCase().replaceAll("\\s+","").split(","));
|
||||
|
||||
for (String splitType : splitTelephoneTypes) {
|
||||
String attributeTypeName = "TSK_PHONE_" + splitType;
|
||||
String attributeTypeName = "TSK_PHONE_NUMBER_" + splitType;
|
||||
try {
|
||||
BlackboardAttribute.Type attributeType = tskCase.getAttributeType(attributeTypeName);
|
||||
if (attributeType == null) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user