mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-15 09:17:42 +00:00
merge in develop and fixed conflicts
This commit is contained in:
commit
886cc3a696
@ -0,0 +1,145 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.sleuthkit.autopsy.casemodule;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import org.apache.commons.io.FileUtils;
|
||||||
|
import org.openide.util.NbBundle.Messages;
|
||||||
|
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback;
|
||||||
|
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
|
import org.sleuthkit.autopsy.imagewriter.ImageWriterSettings;
|
||||||
|
import org.sleuthkit.datamodel.Content;
|
||||||
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A runnable that
|
||||||
|
* - copy the logical image folder to a destination folder
|
||||||
|
* - add alert.txt and users.txt files to report
|
||||||
|
* - add an image data source to the case database.
|
||||||
|
*/
|
||||||
|
public class AddLogicalImageTask extends AddImageTask {
|
||||||
|
|
||||||
|
private final static Logger logger = Logger.getLogger(AddLogicalImageTask.class.getName());
|
||||||
|
private final static String ALERT_TXT = "alert.txt"; //NON-NLS
|
||||||
|
private final static String USERS_TXT = "users.txt"; //NON-NLS
|
||||||
|
private final File src;
|
||||||
|
private final File dest;
|
||||||
|
private final DataSourceProcessorCallback callback;
|
||||||
|
private final DataSourceProcessorProgressMonitor progressMonitor;
|
||||||
|
|
||||||
|
public AddLogicalImageTask(String deviceId, String imagePath, int sectorSize,
|
||||||
|
String timeZone, boolean ignoreFatOrphanFiles,
|
||||||
|
String md5, String sha1, String sha256,
|
||||||
|
ImageWriterSettings imageWriterSettings,
|
||||||
|
File src, File dest,
|
||||||
|
DataSourceProcessorProgressMonitor progressMonitor,
|
||||||
|
DataSourceProcessorCallback callback
|
||||||
|
) {
|
||||||
|
super(deviceId, imagePath, sectorSize, timeZone, ignoreFatOrphanFiles,
|
||||||
|
md5, sha1, sha256, imageWriterSettings, progressMonitor, callback);
|
||||||
|
this.src = src;
|
||||||
|
this.dest = dest;
|
||||||
|
this.progressMonitor = progressMonitor;
|
||||||
|
this.callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy the src directory to dest.
|
||||||
|
* Add alert.txt and users.txt to the case report
|
||||||
|
* Adds the image to the case database.
|
||||||
|
*/
|
||||||
|
@Messages({
|
||||||
|
"# {0} - src", "# {1} - dest", "AddLogicalImageTask.copyingImageFromTo=Copying image from {0} to {1}",
|
||||||
|
"AddLogicalImageTask.doneCopying=Done copying",
|
||||||
|
"# {0} - src", "# {1} - dest", "AddLogicalImageTask.failedToCopyDirectory=Failed to copy directory {0} to {1}",
|
||||||
|
"# {0} - file", "AddLogicalImageTask.addingToReport=Adding {0} to report",
|
||||||
|
"# {0} - file", "AddLogicalImageTask.doneAddingToReport=Done adding {0} to report"
|
||||||
|
})
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
List<String> errorList = new ArrayList<>();
|
||||||
|
List<Content> emptyDataSources = new ArrayList<>();
|
||||||
|
|
||||||
|
try {
|
||||||
|
progressMonitor.setProgressText(Bundle.AddLogicalImageTask_copyingImageFromTo(src.toString(), dest.toString()));
|
||||||
|
FileUtils.copyDirectory(src, dest);
|
||||||
|
progressMonitor.setProgressText(Bundle.AddLogicalImageTask_doneCopying());
|
||||||
|
} catch (IOException ex) {
|
||||||
|
// Copy directory failed
|
||||||
|
String msg = Bundle.AddLogicalImageTask_failedToCopyDirectory(src.toString(), dest.toString());
|
||||||
|
errorList.add(msg);
|
||||||
|
logger.log(Level.SEVERE, String.format("Failed to copy directory {0} to {1}", src.toString(), dest.toString()));
|
||||||
|
callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the alert.txt and users.txt to the case report
|
||||||
|
progressMonitor.setProgressText(Bundle.AddLogicalImageTask_addingToReport(ALERT_TXT));
|
||||||
|
String status = addReport(Paths.get(dest.toString(), ALERT_TXT), ALERT_TXT + " " + src.getName());
|
||||||
|
if (status != null) {
|
||||||
|
errorList.add(status);
|
||||||
|
callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
progressMonitor.setProgressText(Bundle.AddLogicalImageTask_doneAddingToReport(ALERT_TXT));
|
||||||
|
|
||||||
|
progressMonitor.setProgressText(Bundle.AddLogicalImageTask_addingToReport(USERS_TXT));
|
||||||
|
status = addReport(Paths.get(dest.toString(), USERS_TXT), USERS_TXT + " " + src.getName());
|
||||||
|
if (status != null) {
|
||||||
|
errorList.add(status);
|
||||||
|
callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
progressMonitor.setProgressText(Bundle.AddLogicalImageTask_doneAddingToReport(USERS_TXT));
|
||||||
|
|
||||||
|
super.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a file specified by the reportPath to the case report.
|
||||||
|
*
|
||||||
|
* @param reportPath Path to the report to be added
|
||||||
|
* @param reportName Name associated the report
|
||||||
|
* @returns null if success, or exception message if failure
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Messages({
|
||||||
|
"# {0} - file", "# {1} - exception message", "AddLogicalImageTask.failedToAddReport=Failed to add report {0}. Reason= {1}"
|
||||||
|
})
|
||||||
|
private String addReport(Path reportPath, String reportName) {
|
||||||
|
if (!reportPath.toFile().exists()) {
|
||||||
|
return null; // if the reportPath doesn't exist, just ignore it.
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Case.getCurrentCase().addReport(reportPath.toString(), "LogicalImager", reportName); //NON-NLS
|
||||||
|
return null;
|
||||||
|
} catch (TskCoreException ex) {
|
||||||
|
String msg = Bundle.AddLogicalImageTask_failedToAddReport(reportPath.toString(), ex.getMessage());
|
||||||
|
logger.log(Level.SEVERE, String.format("Failed to add report {0}. Reason= {1}", reportPath.toString(), ex.getMessage()));
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,19 @@
|
|||||||
AddImageWizardIngestConfigPanel.name.text=Configure Ingest Modules
|
AddImageWizardIngestConfigPanel.name.text=Configure Ingest Modules
|
||||||
AddImageWizardSelectDspVisual.multiUserWarning.text=This type of Data Source Processor is not available in multi-user mode
|
AddImageWizardSelectDspVisual.multiUserWarning.text=This type of Data Source Processor is not available in multi-user mode
|
||||||
|
# {0} - file
|
||||||
|
AddLogicalImageTask.addingToReport=Adding {0} to report
|
||||||
|
# {0} - src
|
||||||
|
# {1} - dest
|
||||||
|
AddLogicalImageTask.copyingImageFromTo=Copying image from {0} to {1}
|
||||||
|
# {0} - file
|
||||||
|
AddLogicalImageTask.doneAddingToReport=Done adding {0} to report
|
||||||
|
AddLogicalImageTask.doneCopying=Done copying
|
||||||
|
# {0} - file
|
||||||
|
# {1} - exception message
|
||||||
|
AddLogicalImageTask.failedToAddReport=Failed to add report {0}. Reason= {1}
|
||||||
|
# {0} - src
|
||||||
|
# {1} - dest
|
||||||
|
AddLogicalImageTask.failedToCopyDirectory=Failed to copy directory {0} to {1}
|
||||||
# {0} - exception message
|
# {0} - exception message
|
||||||
Case.closeException.couldNotCloseCase=Error closing case: {0}
|
Case.closeException.couldNotCloseCase=Error closing case: {0}
|
||||||
Case.creationException.couldNotAcquireDirLock=Failed to get lock on case directory
|
Case.creationException.couldNotAcquireDirLock=Failed to get lock on case directory
|
||||||
@ -169,10 +183,19 @@ LogicalEvidenceFilePanel.validatePanel.nonL01Error.text=Only files with the .l01
|
|||||||
LogicalFilesDspPanel.subTypeComboBox.l01FileOption.text=Logical evidence file (L01)
|
LogicalFilesDspPanel.subTypeComboBox.l01FileOption.text=Logical evidence file (L01)
|
||||||
LogicalFilesDspPanel.subTypeComboBox.localFilesOption.text=Local files and folders
|
LogicalFilesDspPanel.subTypeComboBox.localFilesOption.text=Local files and folders
|
||||||
LogicalImagerDSProcessor.dataSourceType=Autopsy Imager
|
LogicalImagerDSProcessor.dataSourceType=Autopsy Imager
|
||||||
|
# {0} - directory
|
||||||
|
LogicalImagerDSProcessor.directoryAlreadyExists=Directory {0} already exists
|
||||||
|
# {0} - directory
|
||||||
|
LogicalImagerDSProcessor.failToCreateDirectory=Failed to create directory {0}
|
||||||
|
# {0} - imageDirPath
|
||||||
|
LogicalImagerDSProcessor.imageDirPathNotFound={0} not found.\nUSB drive has been ejected.
|
||||||
LogicalImagerPanel.imageTable.columnModel.title0=Hostname
|
LogicalImagerPanel.imageTable.columnModel.title0=Hostname
|
||||||
LogicalImagerPanel.imageTable.columnModel.title1=Extracted Date
|
LogicalImagerPanel.imageTable.columnModel.title1=Extracted Date
|
||||||
LogicalImagerPanel.messageLabel.clickScanOrBrowse=Click SCAN or BROWSE button to find images
|
LogicalImagerPanel.messageLabel.clickScanOrBrowse=Click SCAN or BROWSE button to find images
|
||||||
|
# {0} - sparseImageDirectory
|
||||||
|
# {1} - image
|
||||||
LogicalImagerPanel.messageLabel.directoryDoesNotContainSparseImage=Directory {0} does not contain {1}
|
LogicalImagerPanel.messageLabel.directoryDoesNotContainSparseImage=Directory {0} does not contain {1}
|
||||||
|
# {0} - invalidFormatDirectory
|
||||||
LogicalImagerPanel.messageLabel.directoryFormatInvalid=Directory {0} does not match format Logical_Imager_HOSTNAME_yyyymmdd_HH_MM_SS
|
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.driveHasNoImages=Drive has no images
|
||||||
LogicalImagerPanel.messageLabel.noExternalDriveFound=No drive found
|
LogicalImagerPanel.messageLabel.noExternalDriveFound=No drive found
|
||||||
|
@ -18,7 +18,13 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.casemodule;
|
package org.sleuthkit.autopsy.casemodule;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
import org.openide.util.NbBundle.Messages;
|
import org.openide.util.NbBundle.Messages;
|
||||||
import org.openide.util.lookup.ServiceProvider;
|
import org.openide.util.lookup.ServiceProvider;
|
||||||
@ -26,6 +32,7 @@ import org.openide.util.lookup.ServiceProviders;
|
|||||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
|
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
|
||||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback;
|
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback;
|
||||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor;
|
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor;
|
||||||
|
import org.sleuthkit.datamodel.Content;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Logical Imager data source processor that implements the DataSourceProcessor service
|
* A Logical Imager data source processor that implements the DataSourceProcessor service
|
||||||
@ -38,7 +45,10 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgress
|
|||||||
)
|
)
|
||||||
public class LogicalImagerDSProcessor implements DataSourceProcessor {
|
public class LogicalImagerDSProcessor implements DataSourceProcessor {
|
||||||
|
|
||||||
|
private static final String LOGICAL_IMAGER_DIR = "LogicalImager"; //NON-NLS
|
||||||
|
private static final String SPARSE_IMAGE_VHD = "sparse_image.vhd"; //NON-NLS
|
||||||
private final LogicalImagerPanel configPanel;
|
private final LogicalImagerPanel configPanel;
|
||||||
|
private AddLogicalImageTask addLogicalImageTask;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Constructs a Logical Imager data source processor that implements the
|
* Constructs a Logical Imager data source processor that implements the
|
||||||
@ -114,12 +124,55 @@ public class LogicalImagerDSProcessor implements DataSourceProcessor {
|
|||||||
* @param callback Callback that will be used by the background task
|
* @param callback Callback that will be used by the background task
|
||||||
* to return results.
|
* to return results.
|
||||||
*/
|
*/
|
||||||
|
@Messages({
|
||||||
|
"# {0} - imageDirPath", "LogicalImagerDSProcessor.imageDirPathNotFound={0} not found.\nUSB drive has been ejected.",
|
||||||
|
"# {0} - directory", "LogicalImagerDSProcessor.failToCreateDirectory=Failed to create directory {0}",
|
||||||
|
"# {0} - directory", "LogicalImagerDSProcessor.directoryAlreadyExists=Directory {0} already exists",
|
||||||
|
})
|
||||||
@Override
|
@Override
|
||||||
public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
|
||||||
configPanel.storeSettings();
|
configPanel.storeSettings();
|
||||||
Path dirPath = configPanel.getImageDirPath();
|
|
||||||
System.out.println("Choosen directory " + dirPath.toString());
|
Path imageDirPath = configPanel.getImageDirPath();
|
||||||
// TODO: process the data source in 5011
|
List<String> errorList = new ArrayList<>();
|
||||||
|
List<Content> emptyDataSources = new ArrayList<>();
|
||||||
|
|
||||||
|
if (!imageDirPath.toFile().exists()) {
|
||||||
|
// This can happen if the USB drive was selected in the panel, but
|
||||||
|
// was ejected before pressing the NEXT button
|
||||||
|
// TODO: Better ways to detect ejected USB drive?
|
||||||
|
String msg = Bundle.LogicalImagerDSProcessor_imageDirPathNotFound(imageDirPath.toString());
|
||||||
|
errorList.add(msg);
|
||||||
|
callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the LogicalImager directory under ModuleDirectory
|
||||||
|
String moduleDirectory = Case.getCurrentCase().getModuleDirectory();
|
||||||
|
File logicalImagerDir = Paths.get(moduleDirectory, LOGICAL_IMAGER_DIR).toFile();
|
||||||
|
if (!logicalImagerDir.exists() && !logicalImagerDir.mkdir()) {
|
||||||
|
// create failed
|
||||||
|
String msg = Bundle.LogicalImagerDSProcessor_failToCreateDirectory(logicalImagerDir);
|
||||||
|
errorList.add(msg);
|
||||||
|
callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
File dest = Paths.get(logicalImagerDir.toString(), imageDirPath.getFileName().toString()).toFile();
|
||||||
|
if (dest.exists()) {
|
||||||
|
// Destination directory already exists
|
||||||
|
String msg = Bundle.LogicalImagerDSProcessor_directoryAlreadyExists(dest.toString());
|
||||||
|
errorList.add(msg);
|
||||||
|
callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
File src = imageDirPath.toFile();
|
||||||
|
|
||||||
|
String deviceId = UUID.randomUUID().toString();
|
||||||
|
String timeZone = Calendar.getInstance().getTimeZone().getID();
|
||||||
|
boolean ignoreFatOrphanFiles = false;
|
||||||
|
run(deviceId, Paths.get(src.toString(), SPARSE_IMAGE_VHD).toString(), 0,
|
||||||
|
timeZone, ignoreFatOrphanFiles, null, null, null, src, dest,
|
||||||
|
progressMonitor, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -140,17 +193,28 @@ public class LogicalImagerDSProcessor implements DataSourceProcessor {
|
|||||||
* @param chunkSize The maximum size of each chunk of the raw
|
* @param chunkSize The maximum size of each chunk of the raw
|
||||||
* data source as it is divided up into virtual
|
* data source as it is divided up into virtual
|
||||||
* unallocated space files.
|
* unallocated space files.
|
||||||
|
* @param src The source directory of image.
|
||||||
|
* @param dest The destination directory to copy the source.
|
||||||
* @param progressMonitor Progress monitor for reporting progress
|
* @param progressMonitor Progress monitor for reporting progress
|
||||||
* during processing.
|
* during processing.
|
||||||
* @param callback Callback to call when processing is done.
|
* @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) {
|
private void run(String deviceId, String imagePath, int sectorSize, String timeZone,
|
||||||
AddImageTask addImageTask = new AddImageTask(deviceId, imagePath, sectorSize, timeZone, ignoreFatOrphanFiles, md5, sha1, sha256, null, progressMonitor, callback);
|
boolean ignoreFatOrphanFiles, String md5, String sha1, String sha256,
|
||||||
new Thread(addImageTask).start();
|
File src, File dest,
|
||||||
|
DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback
|
||||||
|
) {
|
||||||
|
addLogicalImageTask = new AddLogicalImageTask(deviceId, imagePath, sectorSize,
|
||||||
|
timeZone, ignoreFatOrphanFiles, md5, sha1, sha256, null, src, dest,
|
||||||
|
progressMonitor, callback);
|
||||||
|
new Thread(addLogicalImageTask).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void cancel() {
|
public void cancel() {
|
||||||
|
if (addLogicalImageTask != null) {
|
||||||
|
addLogicalImageTask.cancelTask();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
|
||||||
<Form version="1.5" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
<Form version="1.6" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
||||||
<Properties>
|
<Properties>
|
||||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
<Dimension value="[0, 65]"/>
|
<Dimension value="[0, 65]"/>
|
||||||
@ -213,6 +213,9 @@
|
|||||||
<Property name="columnModel" type="javax.swing.table.TableColumnModel" editor="org.netbeans.modules.form.editors2.TableColumnModelEditor">
|
<Property name="columnModel" type="javax.swing.table.TableColumnModel" editor="org.netbeans.modules.form.editors2.TableColumnModelEditor">
|
||||||
<TableColumnModel selectionModel="1"/>
|
<TableColumnModel selectionModel="1"/>
|
||||||
</Property>
|
</Property>
|
||||||
|
<Property name="selectionModel" type="javax.swing.ListSelectionModel" editor="org.netbeans.modules.form.editors2.JTableSelectionModelEditor">
|
||||||
|
<JTableSelectionModel selectionMode="0"/>
|
||||||
|
</Property>
|
||||||
<Property name="showHorizontalLines" type="boolean" value="false"/>
|
<Property name="showHorizontalLines" type="boolean" value="false"/>
|
||||||
<Property name="showVerticalLines" 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">
|
<Property name="tableHeader" type="javax.swing.table.JTableHeader" editor="org.netbeans.modules.form.editors2.JTableHeaderEditor">
|
||||||
|
@ -42,15 +42,14 @@ import org.openide.util.NbBundle.Messages;
|
|||||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
|
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Panel for adding an logical image file from drive letters. Allows the user
|
* Panel for adding an logical image file from drive letters. Allows the user to
|
||||||
* to select a file.
|
* select a file.
|
||||||
*/
|
*/
|
||||||
@Messages({
|
@Messages({
|
||||||
"LogicalImagerPanel.messageLabel.selectedImage=Selected folder",
|
"LogicalImagerPanel.messageLabel.selectedImage=Selected folder",
|
||||||
"LogicalImagerPanel.messageLabel.noImageSelected=No image selected",
|
"LogicalImagerPanel.messageLabel.noImageSelected=No image selected",
|
||||||
"LogicalImagerPanel.messageLabel.driveHasNoImages=Drive has no images",
|
"LogicalImagerPanel.messageLabel.driveHasNoImages=Drive has no images",
|
||||||
"LogicalImagerPanel.selectAcquisitionFromDriveLabel.text=Select acquisition from Drive",
|
"LogicalImagerPanel.selectAcquisitionFromDriveLabel.text=Select acquisition from Drive",})
|
||||||
})
|
|
||||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||||
public class LogicalImagerPanel extends JPanel implements DocumentListener {
|
public class LogicalImagerPanel extends JPanel implements DocumentListener {
|
||||||
|
|
||||||
@ -70,8 +69,8 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener {
|
|||||||
/**
|
/**
|
||||||
* Creates new form LogicalImagerPanel
|
* Creates new form LogicalImagerPanel
|
||||||
*
|
*
|
||||||
* @param context A string context name used to read/store last
|
* @param context A string context name used to read/store last used
|
||||||
* used settings.
|
* settings.
|
||||||
*/
|
*/
|
||||||
private LogicalImagerPanel(String context) {
|
private LogicalImagerPanel(String context) {
|
||||||
this.contextName = context;
|
this.contextName = context;
|
||||||
@ -82,8 +81,8 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener {
|
|||||||
/**
|
/**
|
||||||
* Creates and returns an instance of a LogicalImagerPanel.
|
* Creates and returns an instance of a LogicalImagerPanel.
|
||||||
*
|
*
|
||||||
* @param context A string context name used to read/store last
|
* @param context A string context name used to read/store last used
|
||||||
* used settings.
|
* settings.
|
||||||
*
|
*
|
||||||
* @return instance of the LogicalImagerPanel
|
* @return instance of the LogicalImagerPanel
|
||||||
*/
|
*/
|
||||||
@ -173,6 +172,7 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener {
|
|||||||
}
|
}
|
||||||
));
|
));
|
||||||
imageTable.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_OFF);
|
imageTable.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_OFF);
|
||||||
|
imageTable.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
|
||||||
imageTable.setShowHorizontalLines(false);
|
imageTable.setShowHorizontalLines(false);
|
||||||
imageTable.setShowVerticalLines(false);
|
imageTable.setShowVerticalLines(false);
|
||||||
imageTable.getTableHeader().setReorderingAllowed(false);
|
imageTable.getTableHeader().setReorderingAllowed(false);
|
||||||
@ -310,7 +310,10 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener {
|
|||||||
}//GEN-LAST:event_scanButtonActionPerformed
|
}//GEN-LAST:event_scanButtonActionPerformed
|
||||||
|
|
||||||
@Messages({
|
@Messages({
|
||||||
|
"# {0} - sparseImageDirectory",
|
||||||
|
"# {1} - image",
|
||||||
"LogicalImagerPanel.messageLabel.directoryDoesNotContainSparseImage=Directory {0} does not contain {1}",
|
"LogicalImagerPanel.messageLabel.directoryDoesNotContainSparseImage=Directory {0} does not contain {1}",
|
||||||
|
"# {0} - invalidFormatDirectory",
|
||||||
"LogicalImagerPanel.messageLabel.directoryFormatInvalid=Directory {0} does not match format Logical_Imager_HOSTNAME_yyyymmdd_HH_MM_SS"
|
"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
|
private void browseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browseButtonActionPerformed
|
||||||
@ -485,6 +488,10 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener {
|
|||||||
return choosenImageDirPath;
|
return choosenImageDirPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setMessageLabel(String message) {
|
||||||
|
messageLabel.setText(message);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void insertUpdate(DocumentEvent e) {
|
public void insertUpdate(DocumentEvent e) {
|
||||||
}
|
}
|
||||||
@ -501,6 +508,7 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private class ImageTableModel extends AbstractTableModel {
|
private class ImageTableModel extends AbstractTableModel {
|
||||||
|
|
||||||
private final List<String> hostnames = new ArrayList<>();
|
private final List<String> hostnames = new ArrayList<>();
|
||||||
private final List<String> extractDates = new ArrayList<>();
|
private final List<String> extractDates = new ArrayList<>();
|
||||||
private final List<String> imageDirPaths = new ArrayList<>();
|
private final List<String> imageDirPaths = new ArrayList<>();
|
||||||
|
@ -22,7 +22,11 @@ import com.google.common.collect.ImmutableSet;
|
|||||||
import com.google.common.eventbus.Subscribe;
|
import com.google.common.eventbus.Subscribe;
|
||||||
import java.awt.event.ItemListener;
|
import java.awt.event.ItemListener;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.time.Instant;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
@ -51,6 +55,7 @@ import static org.sleuthkit.autopsy.ingest.IngestManager.IngestModuleEvent.DATA_
|
|||||||
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
|
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
|
||||||
import org.sleuthkit.datamodel.Account;
|
import org.sleuthkit.datamodel.Account;
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||||
|
import org.sleuthkit.datamodel.CaseDbAccessManager.CaseDbAccessQueryCallback;
|
||||||
import org.sleuthkit.datamodel.CommunicationsFilter;
|
import org.sleuthkit.datamodel.CommunicationsFilter;
|
||||||
import org.sleuthkit.datamodel.CommunicationsFilter.AccountTypeFilter;
|
import org.sleuthkit.datamodel.CommunicationsFilter.AccountTypeFilter;
|
||||||
import org.sleuthkit.datamodel.CommunicationsFilter.DateRangeFilter;
|
import org.sleuthkit.datamodel.CommunicationsFilter.DateRangeFilter;
|
||||||
@ -157,6 +162,29 @@ final public class FiltersPanel extends JPanel {
|
|||||||
|
|
||||||
applyFiltersButton.addActionListener(e -> applyFilters());
|
applyFiltersButton.addActionListener(e -> applyFilters());
|
||||||
refreshButton.addActionListener(e -> applyFilters());
|
refreshButton.addActionListener(e -> applyFilters());
|
||||||
|
|
||||||
|
try {
|
||||||
|
String queryString = "max(date_time) as max, min(date_time) as min from account_relationships"; // NON-NLS
|
||||||
|
Case.getCurrentCaseThrows().getSleuthkitCase().getCaseDbAccessManager().select(queryString, new FilterPanelQueryCallback() {
|
||||||
|
@Override
|
||||||
|
public void process(ResultSet rs) {
|
||||||
|
try {
|
||||||
|
if (rs.next()) {
|
||||||
|
int startDate = rs.getInt("min"); // NON-NLS
|
||||||
|
int endData = rs.getInt("max"); // NON-NLS
|
||||||
|
|
||||||
|
startDatePicker.setDate(LocalDateTime.ofInstant(Instant.ofEpochSecond(startDate), Utils.getUserPreferredZoneId()).toLocalDate());
|
||||||
|
endDatePicker.setDate(LocalDateTime.ofInstant(Instant.ofEpochSecond(endData), Utils.getUserPreferredZoneId()).toLocalDate());
|
||||||
|
}
|
||||||
|
} catch (SQLException ex) {
|
||||||
|
logger.log(Level.WARNING, "Unable to set filter date pickers due to SQL exception", ex); //NON-NLS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
} catch (NoCurrentCaseException | TskCoreException ex) {
|
||||||
|
logger.log(Level.SEVERE, "Unable to set filter date pickers due to exception", ex); //NON-NLS
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -958,4 +986,17 @@ final public class FiltersPanel extends JPanel {
|
|||||||
checkbox.addItemListener(l);
|
checkbox.addItemListener(l);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple class that implements CaseDbAccessQueryCallback. Can be used
|
||||||
|
* as an anonymous innerclass with the CaseDbAccessManager select function.
|
||||||
|
*/
|
||||||
|
class FilterPanelQueryCallback implements CaseDbAccessQueryCallback {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void process(ResultSet rs) {
|
||||||
|
// Subclasses can implement their own process function.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -16,11 +16,11 @@
|
|||||||
<Layout>
|
<Layout>
|
||||||
<DimensionLayout dim="0">
|
<DimensionLayout dim="0">
|
||||||
<Group type="103" groupAlignment="0" attributes="0">
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
<Component id="htmlScrollPane" pref="300" max="32767" attributes="0"/>
|
|
||||||
<Group type="102" attributes="0">
|
<Group type="102" attributes="0">
|
||||||
<Component id="showImagesToggleButton" min="-2" max="-2" attributes="0"/>
|
<Component id="showImagesToggleButton" min="-2" max="-2" attributes="0"/>
|
||||||
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
|
<EmptySpace min="0" pref="95" max="32767" attributes="0"/>
|
||||||
</Group>
|
</Group>
|
||||||
|
<Component id="htmlJPanel" max="32767" attributes="0"/>
|
||||||
</Group>
|
</Group>
|
||||||
</DimensionLayout>
|
</DimensionLayout>
|
||||||
<DimensionLayout dim="1">
|
<DimensionLayout dim="1">
|
||||||
@ -28,29 +28,12 @@
|
|||||||
<Group type="102" alignment="0" attributes="0">
|
<Group type="102" alignment="0" attributes="0">
|
||||||
<Component id="showImagesToggleButton" min="-2" max="-2" attributes="0"/>
|
<Component id="showImagesToggleButton" min="-2" max="-2" attributes="0"/>
|
||||||
<EmptySpace max="-2" attributes="0"/>
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
<Component id="htmlScrollPane" pref="71" max="32767" attributes="0"/>
|
<Component id="htmlJPanel" pref="33" max="32767" attributes="0"/>
|
||||||
</Group>
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
</DimensionLayout>
|
</DimensionLayout>
|
||||||
</Layout>
|
</Layout>
|
||||||
<SubComponents>
|
<SubComponents>
|
||||||
<Container class="javax.swing.JScrollPane" name="htmlScrollPane">
|
|
||||||
<Properties>
|
|
||||||
<Property name="verticalScrollBarPolicy" type="int" value="22"/>
|
|
||||||
</Properties>
|
|
||||||
<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.JTextPane" name="htmlbodyTextPane">
|
|
||||||
<Properties>
|
|
||||||
<Property name="editable" type="boolean" value="false"/>
|
|
||||||
</Properties>
|
|
||||||
</Component>
|
|
||||||
</SubComponents>
|
|
||||||
</Container>
|
|
||||||
<Component class="javax.swing.JToggleButton" name="showImagesToggleButton">
|
<Component class="javax.swing.JToggleButton" name="showImagesToggleButton">
|
||||||
<Properties>
|
<Properties>
|
||||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
@ -61,5 +44,9 @@
|
|||||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="showImagesToggleButtonActionPerformed"/>
|
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="showImagesToggleButtonActionPerformed"/>
|
||||||
</Events>
|
</Events>
|
||||||
</Component>
|
</Component>
|
||||||
|
<Container class="javax.swing.JPanel" name="htmlJPanel">
|
||||||
|
|
||||||
|
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
|
||||||
|
</Container>
|
||||||
</SubComponents>
|
</SubComponents>
|
||||||
</Form>
|
</Form>
|
||||||
|
@ -18,9 +18,19 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.contentviewers;
|
package org.sleuthkit.autopsy.contentviewers;
|
||||||
|
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.beans.value.ChangeListener;
|
||||||
|
import javafx.beans.value.ObservableValue;
|
||||||
|
import javafx.concurrent.Worker;
|
||||||
|
import javafx.scene.web.WebView;
|
||||||
|
import javafx.embed.swing.JFXPanel;
|
||||||
|
import javafx.scene.Scene;
|
||||||
import org.jsoup.Jsoup;
|
import org.jsoup.Jsoup;
|
||||||
import org.jsoup.nodes.Document;
|
import org.jsoup.nodes.Node;
|
||||||
import org.openide.util.NbBundle.Messages;
|
import org.openide.util.NbBundle.Messages;
|
||||||
|
import org.w3c.dom.Document;
|
||||||
|
import org.w3c.dom.NodeList;
|
||||||
|
import org.w3c.dom.events.EventTarget;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A file content viewer for HTML files.
|
* A file content viewer for HTML files.
|
||||||
@ -29,7 +39,9 @@ import org.openide.util.NbBundle.Messages;
|
|||||||
final class HtmlPanel extends javax.swing.JPanel {
|
final class HtmlPanel extends javax.swing.JPanel {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
private static final String TEXT_TYPE = "text/plain";
|
||||||
|
private final JFXPanel jfxPanel = new JFXPanel();
|
||||||
|
private WebView webView;
|
||||||
private String htmlText;
|
private String htmlText;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -37,8 +49,26 @@ final class HtmlPanel extends javax.swing.JPanel {
|
|||||||
*/
|
*/
|
||||||
HtmlPanel() {
|
HtmlPanel() {
|
||||||
initComponents();
|
initComponents();
|
||||||
|
Platform.runLater(() -> {
|
||||||
Utilities.configureTextPaneAsHtml(htmlbodyTextPane);
|
webView = new WebView();
|
||||||
|
//disable the context menu so they can't open linked pages by right clicking
|
||||||
|
webView.setContextMenuEnabled(false);
|
||||||
|
//disable java script
|
||||||
|
webView.getEngine().setJavaScriptEnabled(false);
|
||||||
|
//disable clicking on links
|
||||||
|
webView.getEngine().getLoadWorker().stateProperty().addListener(new ChangeListener<Worker.State>() {
|
||||||
|
@Override
|
||||||
|
public void changed(ObservableValue<? extends Worker.State> observable, Worker.State oldValue, Worker.State newValue) {
|
||||||
|
if (newValue == Worker.State.SUCCEEDED) {
|
||||||
|
disableHyperLinks();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Scene scene = new Scene(webView);
|
||||||
|
jfxPanel.setScene(scene);
|
||||||
|
jfxPanel.setPreferredSize(htmlJPanel.getPreferredSize());
|
||||||
|
htmlJPanel.add(jfxPanel);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -55,21 +85,12 @@ final class HtmlPanel extends javax.swing.JPanel {
|
|||||||
* Clear the HTML in the text pane and disable the show/hide button.
|
* Clear the HTML in the text pane and disable the show/hide button.
|
||||||
*/
|
*/
|
||||||
void reset() {
|
void reset() {
|
||||||
htmlbodyTextPane.setText("");
|
Platform.runLater(() -> {
|
||||||
|
webView.getEngine().loadContent("", TEXT_TYPE);
|
||||||
|
});
|
||||||
showImagesToggleButton.setEnabled(false);
|
showImagesToggleButton.setEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Guarantee the HTML text has 'html' and 'body' tags.
|
|
||||||
*
|
|
||||||
* @param htmlText The HTML text
|
|
||||||
*
|
|
||||||
* @return The HTML text with the 'html' and 'body' tags applied.
|
|
||||||
*/
|
|
||||||
private String wrapInHtmlBody(String htmlText) {
|
|
||||||
return "<html><body>" + htmlText + "</body></html>";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cleans out input HTML string
|
* Cleans out input HTML string
|
||||||
*
|
*
|
||||||
@ -78,12 +99,11 @@ final class HtmlPanel extends javax.swing.JPanel {
|
|||||||
* @return The cleansed HTML String
|
* @return The cleansed HTML String
|
||||||
*/
|
*/
|
||||||
private String cleanseHTML(String htmlInString) {
|
private String cleanseHTML(String htmlInString) {
|
||||||
|
org.jsoup.nodes.Document doc = Jsoup.parse(htmlInString);
|
||||||
Document doc = Jsoup.parse(htmlInString);
|
// remove all 'img' tags.
|
||||||
|
doc.select("img").stream().forEach(Node::remove);
|
||||||
// Update all 'img' tags.
|
// remove all 'span' tags, these are often images which are ads
|
||||||
doc.select("img[src]").forEach(img -> img.attr("src", ""));
|
doc.select("span").stream().forEach(Node::remove);
|
||||||
|
|
||||||
return doc.html();
|
return doc.html();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,22 +113,26 @@ final class HtmlPanel extends javax.swing.JPanel {
|
|||||||
@Messages({
|
@Messages({
|
||||||
"HtmlPanel_showImagesToggleButton_show=Show Images",
|
"HtmlPanel_showImagesToggleButton_show=Show Images",
|
||||||
"HtmlPanel_showImagesToggleButton_hide=Hide Images",
|
"HtmlPanel_showImagesToggleButton_hide=Hide Images",
|
||||||
"Html_text_display_error=The HTML text cannot be displayed, it may not be correctly formed HTML.",
|
"Html_text_display_error=The HTML text cannot be displayed, it may not be correctly formed HTML.",})
|
||||||
})
|
|
||||||
private void refresh() {
|
private void refresh() {
|
||||||
if (false == htmlText.isEmpty()) {
|
if (false == htmlText.isEmpty()) {
|
||||||
try {
|
try {
|
||||||
if (showImagesToggleButton.isSelected()) {
|
if (showImagesToggleButton.isSelected()) {
|
||||||
showImagesToggleButton.setText(Bundle.HtmlPanel_showImagesToggleButton_hide());
|
showImagesToggleButton.setText(Bundle.HtmlPanel_showImagesToggleButton_hide());
|
||||||
this.htmlbodyTextPane.setText(wrapInHtmlBody(htmlText));
|
Platform.runLater(() -> {
|
||||||
|
webView.getEngine().loadContent(htmlText);
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
showImagesToggleButton.setText(Bundle.HtmlPanel_showImagesToggleButton_show());
|
showImagesToggleButton.setText(Bundle.HtmlPanel_showImagesToggleButton_show());
|
||||||
this.htmlbodyTextPane.setText(wrapInHtmlBody(cleanseHTML(htmlText)));
|
Platform.runLater(() -> {
|
||||||
|
webView.getEngine().loadContent(cleanseHTML(htmlText));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
showImagesToggleButton.setEnabled(true);
|
showImagesToggleButton.setEnabled(true);
|
||||||
htmlbodyTextPane.setCaretPosition(0);
|
} catch (Exception ignored) {
|
||||||
} catch(Exception ex) {
|
Platform.runLater(() -> {
|
||||||
this.htmlbodyTextPane.setText(wrapInHtmlBody(Bundle.Html_text_display_error()));
|
webView.getEngine().loadContent(Bundle.Html_text_display_error(), TEXT_TYPE);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -122,14 +146,8 @@ final class HtmlPanel extends javax.swing.JPanel {
|
|||||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||||
private void initComponents() {
|
private void initComponents() {
|
||||||
|
|
||||||
htmlScrollPane = new javax.swing.JScrollPane();
|
|
||||||
htmlbodyTextPane = new javax.swing.JTextPane();
|
|
||||||
showImagesToggleButton = new javax.swing.JToggleButton();
|
showImagesToggleButton = new javax.swing.JToggleButton();
|
||||||
|
htmlJPanel = new javax.swing.JPanel();
|
||||||
htmlScrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
|
|
||||||
|
|
||||||
htmlbodyTextPane.setEditable(false);
|
|
||||||
htmlScrollPane.setViewportView(htmlbodyTextPane);
|
|
||||||
|
|
||||||
org.openide.awt.Mnemonics.setLocalizedText(showImagesToggleButton, org.openide.util.NbBundle.getMessage(HtmlPanel.class, "HtmlPanel.showImagesToggleButton.text")); // NOI18N
|
org.openide.awt.Mnemonics.setLocalizedText(showImagesToggleButton, org.openide.util.NbBundle.getMessage(HtmlPanel.class, "HtmlPanel.showImagesToggleButton.text")); // NOI18N
|
||||||
showImagesToggleButton.addActionListener(new java.awt.event.ActionListener() {
|
showImagesToggleButton.addActionListener(new java.awt.event.ActionListener() {
|
||||||
@ -138,21 +156,23 @@ final class HtmlPanel extends javax.swing.JPanel {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
htmlJPanel.setLayout(new java.awt.BorderLayout());
|
||||||
|
|
||||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||||
this.setLayout(layout);
|
this.setLayout(layout);
|
||||||
layout.setHorizontalGroup(
|
layout.setHorizontalGroup(
|
||||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
.addComponent(htmlScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE)
|
|
||||||
.addGroup(layout.createSequentialGroup()
|
.addGroup(layout.createSequentialGroup()
|
||||||
.addComponent(showImagesToggleButton)
|
.addComponent(showImagesToggleButton)
|
||||||
.addGap(0, 0, Short.MAX_VALUE))
|
.addGap(0, 95, Short.MAX_VALUE))
|
||||||
|
.addComponent(htmlJPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||||
);
|
);
|
||||||
layout.setVerticalGroup(
|
layout.setVerticalGroup(
|
||||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
.addGroup(layout.createSequentialGroup()
|
.addGroup(layout.createSequentialGroup()
|
||||||
.addComponent(showImagesToggleButton)
|
.addComponent(showImagesToggleButton)
|
||||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
.addComponent(htmlScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 71, Short.MAX_VALUE))
|
.addComponent(htmlJPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 33, Short.MAX_VALUE))
|
||||||
);
|
);
|
||||||
}// </editor-fold>//GEN-END:initComponents
|
}// </editor-fold>//GEN-END:initComponents
|
||||||
|
|
||||||
@ -160,10 +180,27 @@ final class HtmlPanel extends javax.swing.JPanel {
|
|||||||
refresh();
|
refresh();
|
||||||
}//GEN-LAST:event_showImagesToggleButtonActionPerformed
|
}//GEN-LAST:event_showImagesToggleButtonActionPerformed
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable the click events on hyper links so that new pages can not be
|
||||||
|
* opened.
|
||||||
|
*/
|
||||||
|
private void disableHyperLinks() {
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
Document document = webView.getEngine().getDocument();
|
||||||
|
if (document != null) {
|
||||||
|
NodeList nodeList = document.getElementsByTagName("a");
|
||||||
|
for (int i = 0; i < nodeList.getLength(); i++) {
|
||||||
|
((EventTarget) nodeList.item(i)).addEventListener("click", (evt) -> {
|
||||||
|
evt.preventDefault();
|
||||||
|
evt.stopPropagation();
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||||
private javax.swing.JScrollPane htmlScrollPane;
|
private javax.swing.JPanel htmlJPanel;
|
||||||
private javax.swing.JTextPane htmlbodyTextPane;
|
|
||||||
private javax.swing.JToggleButton showImagesToggleButton;
|
private javax.swing.JToggleButton showImagesToggleButton;
|
||||||
// End of variables declaration//GEN-END:variables
|
// End of variables declaration//GEN-END:variables
|
||||||
}
|
}
|
||||||
|
@ -77,6 +77,7 @@ public final class UserPreferences {
|
|||||||
public static final String DISPLAY_TRANSLATED_NAMES = "DisplayTranslatedNames";
|
public static final String DISPLAY_TRANSLATED_NAMES = "DisplayTranslatedNames";
|
||||||
public static final String EXTERNAL_HEX_EDITOR_PATH = "ExternalHexEditorPath";
|
public static final String EXTERNAL_HEX_EDITOR_PATH = "ExternalHexEditorPath";
|
||||||
public static final String SOLR_MAX_JVM_SIZE = "SolrMaxJVMSize";
|
public static final String SOLR_MAX_JVM_SIZE = "SolrMaxJVMSize";
|
||||||
|
public static final String RESULTS_TABLE_PAGE_SIZE = "ResultsTablePageSize";
|
||||||
|
|
||||||
// Prevent instantiation.
|
// Prevent instantiation.
|
||||||
private UserPreferences() {
|
private UserPreferences() {
|
||||||
@ -500,6 +501,24 @@ public final class UserPreferences {
|
|||||||
preferences.putInt(SOLR_MAX_JVM_SIZE, maxSize);
|
preferences.putInt(SOLR_MAX_JVM_SIZE, maxSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the maximum number of results to display in a result table.
|
||||||
|
*
|
||||||
|
* @return Saved value or default (0) which indicates no max.
|
||||||
|
*/
|
||||||
|
public static int getResultsTablePageSize() {
|
||||||
|
return preferences.getInt(RESULTS_TABLE_PAGE_SIZE, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the maximum number of results to display in a result table.
|
||||||
|
*
|
||||||
|
* @param pageSize
|
||||||
|
*/
|
||||||
|
public static void setResultsTablePageSize(int pageSize) {
|
||||||
|
preferences.putInt(RESULTS_TABLE_PAGE_SIZE, pageSize);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the HdX path.
|
* Set the HdX path.
|
||||||
*
|
*
|
||||||
|
@ -207,3 +207,13 @@ ExternalViewerGlobalSettingsPanel.jTable1.columnModel.title0_1=Mime type/Extensi
|
|||||||
AutopsyOptionsPanel.maxSolrMemoryLabel.text=Maximum Solr JVM Memory:
|
AutopsyOptionsPanel.maxSolrMemoryLabel.text=Maximum Solr JVM Memory:
|
||||||
AutopsyOptionsPanel.maxMemoryUnitsLabel2.text=MB
|
AutopsyOptionsPanel.maxMemoryUnitsLabel2.text=MB
|
||||||
AutopsyOptionsPanel.solrJVMHeapWarning.text=NOTE: Setting this too large may impact overall performance.
|
AutopsyOptionsPanel.solrJVMHeapWarning.text=NOTE: Setting this too large may impact overall performance.
|
||||||
|
DataResultViewerTable.gotoPageTextField.text=
|
||||||
|
DataResultViewerTable.gotoPageLabel.AccessibleContext.accessibleName=
|
||||||
|
DataResultViewerTable.gotoPageLabel.text=Go to Page:
|
||||||
|
DataResultViewerTable.pageNextButton.text=
|
||||||
|
DataResultViewerTable.pagePrevButton.text=
|
||||||
|
DataResultViewerTable.pagesLabel.text=Pages:
|
||||||
|
DataResultViewerTable.pageNumLabel.text=
|
||||||
|
DataResultViewerTable.pageLabel.text=Page:
|
||||||
|
ViewPreferencesPanel.maxResultsLabel.text=Maximum number of Results to show in table:
|
||||||
|
ViewPreferencesPanel.maxResultsLabel.toolTipText=<html>\nAll results are shown in the results table if this value is 0 (default).\n<br>You may want to consider setting this value if you have large numbers of results that are taking a long time to display.\n</html>
|
||||||
|
@ -33,6 +33,12 @@ DataResultViewerTable.commentRenderer.tagComment.toolTip=Comment exists on assoc
|
|||||||
DataResultViewerTable.countRender.name=O
|
DataResultViewerTable.countRender.name=O
|
||||||
DataResultViewerTable.countRender.toolTip=O(ccurrences) indicates the number of data sources containing the item in the Central Repository
|
DataResultViewerTable.countRender.toolTip=O(ccurrences) indicates the number of data sources containing the item in the Central Repository
|
||||||
DataResultViewerTable.firstColLbl=Name
|
DataResultViewerTable.firstColLbl=Name
|
||||||
|
DataResultViewerTable.goToPageTextField.err=Invalid page number
|
||||||
|
# {0} - totalPages
|
||||||
|
DataResultViewerTable.goToPageTextField.msgDlg=Please enter a valid page number between 1 and {0}
|
||||||
|
# {0} - currentPage
|
||||||
|
# {1} - totalPages
|
||||||
|
DataResultViewerTable.pageNumbers.curOfTotal={0} of {1}
|
||||||
DataResultViewerTable.scoreRender.name=S
|
DataResultViewerTable.scoreRender.name=S
|
||||||
DataResultViewerTable.scoreRender.toolTip=S(core) indicates whether the item is interesting or notable
|
DataResultViewerTable.scoreRender.toolTip=S(core) indicates whether the item is interesting or notable
|
||||||
DataResultViewerTable.title=Table
|
DataResultViewerTable.title=Table
|
||||||
@ -254,3 +260,13 @@ ExternalViewerGlobalSettingsPanel.jTable1.columnModel.title0_1=Mime type/Extensi
|
|||||||
AutopsyOptionsPanel.maxSolrMemoryLabel.text=Maximum Solr JVM Memory:
|
AutopsyOptionsPanel.maxSolrMemoryLabel.text=Maximum Solr JVM Memory:
|
||||||
AutopsyOptionsPanel.maxMemoryUnitsLabel2.text=MB
|
AutopsyOptionsPanel.maxMemoryUnitsLabel2.text=MB
|
||||||
AutopsyOptionsPanel.solrJVMHeapWarning.text=NOTE: Setting this too large may impact overall performance.
|
AutopsyOptionsPanel.solrJVMHeapWarning.text=NOTE: Setting this too large may impact overall performance.
|
||||||
|
DataResultViewerTable.gotoPageTextField.text=
|
||||||
|
DataResultViewerTable.gotoPageLabel.AccessibleContext.accessibleName=
|
||||||
|
DataResultViewerTable.gotoPageLabel.text=Go to Page:
|
||||||
|
DataResultViewerTable.pageNextButton.text=
|
||||||
|
DataResultViewerTable.pagePrevButton.text=
|
||||||
|
DataResultViewerTable.pagesLabel.text=Pages:
|
||||||
|
DataResultViewerTable.pageNumLabel.text=
|
||||||
|
DataResultViewerTable.pageLabel.text=Page:
|
||||||
|
ViewPreferencesPanel.maxResultsLabel.text=Maximum number of Results to show in table:
|
||||||
|
ViewPreferencesPanel.maxResultsLabel.toolTipText=<html>\nAll results are shown in the results table if this value is 0 (default).\n<br>You may want to consider setting this value if you have large numbers of results that are taking a long time to display.\n</html>
|
||||||
|
@ -16,16 +16,127 @@
|
|||||||
<Layout>
|
<Layout>
|
||||||
<DimensionLayout dim="0">
|
<DimensionLayout dim="0">
|
||||||
<Group type="103" groupAlignment="0" attributes="0">
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
<Component id="outlineView" alignment="0" pref="691" max="32767" attributes="0"/>
|
<Component id="outlineView" alignment="0" max="32767" attributes="0"/>
|
||||||
|
<Group type="102" alignment="1" attributes="0">
|
||||||
|
<EmptySpace pref="608" max="32767" attributes="0"/>
|
||||||
|
<Component id="pageLabel" min="-2" max="-2" attributes="0"/>
|
||||||
|
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||||
|
<Component id="pageNumLabel" min="-2" pref="53" max="-2" attributes="0"/>
|
||||||
|
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||||
|
<Component id="pagesLabel" min="-2" max="-2" attributes="0"/>
|
||||||
|
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||||
|
<Component id="pagePrevButton" linkSize="1" min="-2" pref="16" max="-2" attributes="0"/>
|
||||||
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
|
<Component id="pageNextButton" linkSize="1" min="-2" pref="16" max="-2" attributes="0"/>
|
||||||
|
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||||
|
<Component id="gotoPageLabel" min="-2" max="-2" attributes="0"/>
|
||||||
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
|
<Component id="gotoPageTextField" min="-2" pref="33" max="-2" attributes="0"/>
|
||||||
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
</DimensionLayout>
|
</DimensionLayout>
|
||||||
<DimensionLayout dim="1">
|
<DimensionLayout dim="1">
|
||||||
<Group type="103" groupAlignment="0" attributes="0">
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
<Component id="outlineView" alignment="0" pref="366" max="32767" attributes="0"/>
|
<Group type="102" alignment="1" attributes="0">
|
||||||
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
|
<Group type="103" groupAlignment="2" attributes="0">
|
||||||
|
<Component id="pageLabel" alignment="2" min="-2" max="-2" attributes="0"/>
|
||||||
|
<Component id="pageNumLabel" alignment="2" min="-2" max="-2" attributes="0"/>
|
||||||
|
<Component id="pagesLabel" alignment="2" min="-2" max="-2" attributes="0"/>
|
||||||
|
<Component id="pagePrevButton" linkSize="2" alignment="2" min="-2" pref="14" max="-2" attributes="0"/>
|
||||||
|
<Component id="pageNextButton" linkSize="2" alignment="2" min="-2" pref="15" max="-2" attributes="0"/>
|
||||||
|
<Component id="gotoPageLabel" alignment="2" min="-2" max="-2" attributes="0"/>
|
||||||
|
<Component id="gotoPageTextField" alignment="2" min="-2" max="-2" attributes="0"/>
|
||||||
|
</Group>
|
||||||
|
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||||
|
<Component id="outlineView" pref="324" max="32767" attributes="0"/>
|
||||||
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
</DimensionLayout>
|
</DimensionLayout>
|
||||||
</Layout>
|
</Layout>
|
||||||
<SubComponents>
|
<SubComponents>
|
||||||
|
<Component class="javax.swing.JLabel" name="pageLabel">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataResultViewerTable.pageLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
</Component>
|
||||||
|
<Component class="javax.swing.JLabel" name="pageNumLabel">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataResultViewerTable.pageNumLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
</Component>
|
||||||
|
<Component class="javax.swing.JLabel" name="pagesLabel">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataResultViewerTable.pagesLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
</Component>
|
||||||
|
<Component class="javax.swing.JButton" name="pagePrevButton">
|
||||||
|
<Properties>
|
||||||
|
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||||
|
<Image iconType="3" name="/org/sleuthkit/autopsy/corecomponents/btn_step_back.png"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataResultViewerTable.pagePrevButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="disabledIcon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||||
|
<Image iconType="3" name="/org/sleuthkit/autopsy/corecomponents/btn_step_back_disabled.png"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="focusable" type="boolean" value="false"/>
|
||||||
|
<Property name="horizontalTextPosition" type="int" value="0"/>
|
||||||
|
<Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
|
||||||
|
<Insets value="[2, 0, 2, 0]"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[55, 23]"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="rolloverIcon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||||
|
<Image iconType="3" name="/org/sleuthkit/autopsy/corecomponents/btn_step_back_hover.png"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="verticalTextPosition" type="int" value="3"/>
|
||||||
|
</Properties>
|
||||||
|
<Events>
|
||||||
|
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="pagePrevButtonActionPerformed"/>
|
||||||
|
</Events>
|
||||||
|
</Component>
|
||||||
|
<Component class="javax.swing.JButton" name="pageNextButton">
|
||||||
|
<Properties>
|
||||||
|
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||||
|
<Image iconType="3" name="/org/sleuthkit/autopsy/corecomponents/btn_step_forward.png"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataResultViewerTable.pageNextButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="disabledIcon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||||
|
<Image iconType="3" name="/org/sleuthkit/autopsy/corecomponents/btn_step_forward_disabled.png"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="focusable" type="boolean" value="false"/>
|
||||||
|
<Property name="horizontalTextPosition" type="int" value="0"/>
|
||||||
|
<Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
|
||||||
|
<Insets value="[2, 0, 2, 0]"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[27, 23]"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||||
|
<Dimension value="[27, 23]"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="rolloverIcon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||||
|
<Image iconType="3" name="/org/sleuthkit/autopsy/corecomponents/btn_step_forward_hover.png"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="verticalTextPosition" type="int" value="3"/>
|
||||||
|
</Properties>
|
||||||
|
<Events>
|
||||||
|
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="pageNextButtonActionPerformed"/>
|
||||||
|
</Events>
|
||||||
|
</Component>
|
||||||
<Container class="org.openide.explorer.view.OutlineView" name="outlineView">
|
<Container class="org.openide.explorer.view.OutlineView" name="outlineView">
|
||||||
<AuxValues>
|
<AuxValues>
|
||||||
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new OutlineView(DataResultViewerTable.FIRST_COLUMN_LABEL);"/>
|
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new OutlineView(DataResultViewerTable.FIRST_COLUMN_LABEL);"/>
|
||||||
@ -33,5 +144,25 @@
|
|||||||
|
|
||||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||||
</Container>
|
</Container>
|
||||||
|
<Component class="javax.swing.JLabel" name="gotoPageLabel">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataResultViewerTable.gotoPageLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
<AccessibilityProperties>
|
||||||
|
<Property name="AccessibleContext.accessibleName" type="java.lang.String" value=""/>
|
||||||
|
</AccessibilityProperties>
|
||||||
|
</Component>
|
||||||
|
<Component class="javax.swing.JTextField" name="gotoPageTextField">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataResultViewerTable.gotoPageTextField.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="gotoPageTextFieldActionPerformed"/>
|
||||||
|
</Events>
|
||||||
|
</Component>
|
||||||
</SubComponents>
|
</SubComponents>
|
||||||
</Form>
|
</Form>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2012-2018 Basis Technology Corp.
|
* Copyright 2012-2019 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.corecomponents;
|
package org.sleuthkit.autopsy.corecomponents;
|
||||||
|
|
||||||
|
import com.google.common.eventbus.Subscribe;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Cursor;
|
import java.awt.Cursor;
|
||||||
import java.awt.FontMetrics;
|
import java.awt.FontMetrics;
|
||||||
@ -26,6 +27,7 @@ import java.awt.dnd.DnDConstants;
|
|||||||
import java.awt.event.MouseAdapter;
|
import java.awt.event.MouseAdapter;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
import java.beans.FeatureDescriptor;
|
import java.beans.FeatureDescriptor;
|
||||||
|
import java.beans.PropertyChangeEvent;
|
||||||
import java.beans.PropertyVetoException;
|
import java.beans.PropertyVetoException;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -35,9 +37,12 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
import java.util.prefs.PreferenceChangeEvent;
|
||||||
import java.util.prefs.Preferences;
|
import java.util.prefs.Preferences;
|
||||||
import javax.swing.ImageIcon;
|
import javax.swing.ImageIcon;
|
||||||
|
import javax.swing.JOptionPane;
|
||||||
import javax.swing.JTable;
|
import javax.swing.JTable;
|
||||||
import javax.swing.ListSelectionModel;
|
import javax.swing.ListSelectionModel;
|
||||||
import static javax.swing.SwingConstants.CENTER;
|
import static javax.swing.SwingConstants.CENTER;
|
||||||
@ -61,15 +66,24 @@ import org.openide.nodes.AbstractNode;
|
|||||||
import org.openide.nodes.Children;
|
import org.openide.nodes.Children;
|
||||||
import org.openide.nodes.Node;
|
import org.openide.nodes.Node;
|
||||||
import org.openide.nodes.Node.Property;
|
import org.openide.nodes.Node.Property;
|
||||||
|
import org.openide.nodes.NodeEvent;
|
||||||
|
import org.openide.nodes.NodeListener;
|
||||||
|
import org.openide.nodes.NodeMemberEvent;
|
||||||
|
import org.openide.nodes.NodeReorderEvent;
|
||||||
import org.openide.util.ImageUtilities;
|
import org.openide.util.ImageUtilities;
|
||||||
import org.openide.util.NbBundle;
|
import org.openide.util.NbBundle;
|
||||||
import org.openide.util.NbPreferences;
|
import org.openide.util.NbPreferences;
|
||||||
import org.openide.util.lookup.ServiceProvider;
|
import org.openide.util.lookup.ServiceProvider;
|
||||||
|
import org.sleuthkit.autopsy.core.UserPreferences;
|
||||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer;
|
import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
||||||
import org.sleuthkit.autopsy.datamodel.NodeProperty;
|
import org.sleuthkit.autopsy.datamodel.NodeProperty;
|
||||||
import org.sleuthkit.autopsy.datamodel.NodeSelectionInfo;
|
import org.sleuthkit.autopsy.datamodel.NodeSelectionInfo;
|
||||||
|
import org.sleuthkit.autopsy.datamodel.BaseChildFactory;
|
||||||
|
import org.sleuthkit.autopsy.datamodel.BaseChildFactory.PageChangeEvent;
|
||||||
|
import org.sleuthkit.autopsy.datamodel.BaseChildFactory.PageCountChangeEvent;
|
||||||
|
import org.sleuthkit.autopsy.datamodel.BaseChildFactory.PageSizeChangeEvent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A tabular result viewer that displays the children of the given root node
|
* A tabular result viewer that displays the children of the given root node
|
||||||
@ -104,6 +118,18 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
|||||||
private final IconRendererTableListener iconRendererListener;
|
private final IconRendererTableListener iconRendererListener;
|
||||||
private Node rootNode;
|
private Node rootNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Multiple nodes may have been visited in the context of this
|
||||||
|
* DataResultViewerTable. We keep track of the page state for these nodes in
|
||||||
|
* the following map.
|
||||||
|
*/
|
||||||
|
private final Map<String, PagingSupport> nodeNameToPagingSupportMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The paging support instance for the current node.
|
||||||
|
*/
|
||||||
|
private PagingSupport pagingSupport = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a tabular result viewer that displays the children of the
|
* Constructs a tabular result viewer that displays the children of the
|
||||||
* given root node using an OutlineView. The viewer should have an ancestor
|
* given root node using an OutlineView. The viewer should have an ancestor
|
||||||
@ -149,6 +175,8 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
|||||||
*/
|
*/
|
||||||
initComponents();
|
initComponents();
|
||||||
|
|
||||||
|
initializePagingSupport();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Configure the child OutlineView (explorer view) component.
|
* Configure the child OutlineView (explorer view) component.
|
||||||
*/
|
*/
|
||||||
@ -177,6 +205,32 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
|||||||
outline.getTableHeader().addMouseListener(outlineViewListener);
|
outline.getTableHeader().addMouseListener(outlineViewListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void initializePagingSupport() {
|
||||||
|
if (pagingSupport == null) {
|
||||||
|
pagingSupport = new PagingSupport("");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start out with paging controls invisible
|
||||||
|
pagingSupport.togglePageControls(false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set up a change listener so we know when the user changes the page
|
||||||
|
* size
|
||||||
|
*/
|
||||||
|
UserPreferences.addChangeListener((PreferenceChangeEvent evt) -> {
|
||||||
|
if (evt.getKey().equals(UserPreferences.RESULTS_TABLE_PAGE_SIZE)) {
|
||||||
|
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
||||||
|
/**
|
||||||
|
* If multiple nodes have been viewed we have to notify all of
|
||||||
|
* them about the change in page size.
|
||||||
|
*/
|
||||||
|
nodeNameToPagingSupportMap.values().forEach((ps) -> {
|
||||||
|
ps.postPageSizeChangeEvent();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance of a tabular result viewer that displays the
|
* Creates a new instance of a tabular result viewer that displays the
|
||||||
* children of a given root node using an OutlineView. This method exists to
|
* children of a given root node using an OutlineView. This method exists to
|
||||||
@ -252,6 +306,56 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
|||||||
*/
|
*/
|
||||||
if (rootNode != null && rootNode.getChildren().getNodesCount() > 0) {
|
if (rootNode != null && rootNode.getChildren().getNodesCount() > 0) {
|
||||||
this.rootNode = rootNode;
|
this.rootNode = rootNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check to see if we have previously created a paging support
|
||||||
|
* class for this node.
|
||||||
|
*/
|
||||||
|
String nodeName = rootNode.getName();
|
||||||
|
pagingSupport = nodeNameToPagingSupportMap.get(nodeName);
|
||||||
|
if (pagingSupport == null) {
|
||||||
|
pagingSupport = new PagingSupport(nodeName);
|
||||||
|
nodeNameToPagingSupportMap.put(nodeName, pagingSupport);
|
||||||
|
}
|
||||||
|
pagingSupport.updateControls();
|
||||||
|
|
||||||
|
rootNode.addNodeListener(new NodeListener() {
|
||||||
|
@Override
|
||||||
|
public void childrenAdded(NodeMemberEvent nme) {
|
||||||
|
/**
|
||||||
|
* This is the only somewhat reliable way I could find
|
||||||
|
* to reset the cursor after a page change. When you
|
||||||
|
* change page the old children nodes will be removed
|
||||||
|
* and new ones added.
|
||||||
|
*/
|
||||||
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
setCursor(null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void childrenRemoved(NodeMemberEvent nme) {
|
||||||
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
setCursor(null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void childrenReordered(NodeReorderEvent nre) {
|
||||||
|
// No-op
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void nodeDestroyed(NodeEvent ne) {
|
||||||
|
// No-op
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void propertyChange(PropertyChangeEvent evt) {
|
||||||
|
// No-op
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this.getExplorerManager().setRootContext(this.rootNode);
|
this.getExplorerManager().setRootContext(this.rootNode);
|
||||||
setupTable();
|
setupTable();
|
||||||
} else {
|
} else {
|
||||||
@ -665,6 +769,152 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maintains the current page state for a node and provides support for
|
||||||
|
* paging through results. Uses an EventBus to communicate with child
|
||||||
|
* factory implementations.
|
||||||
|
*/
|
||||||
|
private class PagingSupport {
|
||||||
|
|
||||||
|
private int currentPage;
|
||||||
|
private int totalPages;
|
||||||
|
private final String nodeName;
|
||||||
|
|
||||||
|
PagingSupport(String nodeName) {
|
||||||
|
currentPage = 1;
|
||||||
|
totalPages = 0;
|
||||||
|
this.nodeName = nodeName;
|
||||||
|
initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initialize() {
|
||||||
|
if (!nodeName.isEmpty()) {
|
||||||
|
BaseChildFactory.register(nodeName, this);
|
||||||
|
}
|
||||||
|
updateControls();
|
||||||
|
}
|
||||||
|
|
||||||
|
void nextPage() {
|
||||||
|
currentPage++;
|
||||||
|
postPageChangeEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
void previousPage() {
|
||||||
|
currentPage--;
|
||||||
|
postPageChangeEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NbBundle.Messages({"# {0} - totalPages",
|
||||||
|
"DataResultViewerTable.goToPageTextField.msgDlg=Please enter a valid page number between 1 and {0}",
|
||||||
|
"DataResultViewerTable.goToPageTextField.err=Invalid page number"})
|
||||||
|
void gotoPage() {
|
||||||
|
try {
|
||||||
|
currentPage = Integer.decode(gotoPageTextField.getText());
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
//ignore input
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentPage > totalPages || currentPage < 1) {
|
||||||
|
currentPage = 1;
|
||||||
|
JOptionPane.showMessageDialog(DataResultViewerTable.this,
|
||||||
|
Bundle.DataResultViewerTable_goToPageTextField_msgDlg(totalPages),
|
||||||
|
Bundle.DataResultViewerTable_goToPageTextField_err(),
|
||||||
|
JOptionPane.WARNING_MESSAGE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
postPageChangeEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify subscribers (i.e. child factories) that a page change has
|
||||||
|
* occurred.
|
||||||
|
*/
|
||||||
|
void postPageChangeEvent() {
|
||||||
|
try {
|
||||||
|
BaseChildFactory.post(nodeName, new PageChangeEvent(currentPage));
|
||||||
|
} catch (BaseChildFactory.NoSuchEventBusException ex) {
|
||||||
|
LOGGER.log(Level.WARNING, "Failed to post page change event.", ex); //NON-NLS
|
||||||
|
}
|
||||||
|
DataResultViewerTable.this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
||||||
|
updateControls();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify subscribers (i.e. child factories) that a page size change has
|
||||||
|
* occurred.
|
||||||
|
*/
|
||||||
|
void postPageSizeChangeEvent() {
|
||||||
|
// Reset page variables when page size changes
|
||||||
|
currentPage = 1;
|
||||||
|
totalPages = 0;
|
||||||
|
|
||||||
|
if (this == pagingSupport) {
|
||||||
|
updateControls();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
BaseChildFactory.post(nodeName, new PageSizeChangeEvent(UserPreferences.getResultsTablePageSize()));
|
||||||
|
} catch (BaseChildFactory.NoSuchEventBusException ex) {
|
||||||
|
LOGGER.log(Level.WARNING, "Failed to post page size change event.", ex); //NON-NLS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subscribe to notification that the number of pages has changed.
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
*/
|
||||||
|
@Subscribe
|
||||||
|
public void subscribeToPageCountChange(PageCountChangeEvent event) {
|
||||||
|
if (event != null) {
|
||||||
|
totalPages = event.getPageCount();
|
||||||
|
if (totalPages > 1) {
|
||||||
|
// Make paging controls visible if there is more than one page.
|
||||||
|
togglePageControls(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateControls();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make paging controls visible or invisible based on flag.
|
||||||
|
*
|
||||||
|
* @param onOff
|
||||||
|
*/
|
||||||
|
private void togglePageControls(boolean onOff) {
|
||||||
|
pageLabel.setVisible(onOff);
|
||||||
|
pagesLabel.setVisible(onOff);
|
||||||
|
pagePrevButton.setVisible(onOff);
|
||||||
|
pageNextButton.setVisible(onOff);
|
||||||
|
pageNumLabel.setVisible(onOff);
|
||||||
|
gotoPageLabel.setVisible(onOff);
|
||||||
|
gotoPageTextField.setVisible(onOff);
|
||||||
|
gotoPageTextField.setVisible(onOff);
|
||||||
|
validate();
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NbBundle.Messages({"# {0} - currentPage", "# {1} - totalPages",
|
||||||
|
"DataResultViewerTable.pageNumbers.curOfTotal={0} of {1}"})
|
||||||
|
private void updateControls() {
|
||||||
|
if (totalPages == 0) {
|
||||||
|
pagePrevButton.setEnabled(false);
|
||||||
|
pageNextButton.setEnabled(false);
|
||||||
|
pageNumLabel.setText("");
|
||||||
|
gotoPageTextField.setText("");
|
||||||
|
gotoPageTextField.setEnabled(false);
|
||||||
|
} else {
|
||||||
|
pageNumLabel.setText(Bundle.DataResultViewerTable_pageNumbers_curOfTotal(Integer.toString(currentPage), Integer.toString(totalPages)));
|
||||||
|
|
||||||
|
pageNextButton.setEnabled(currentPage != totalPages);
|
||||||
|
pagePrevButton.setEnabled(currentPage != 1);
|
||||||
|
gotoPageTextField.setEnabled(totalPages > 1);
|
||||||
|
gotoPageTextField.setText("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listener which sets the custom icon renderer on columns which contain
|
* Listener which sets the custom icon renderer on columns which contain
|
||||||
* icons instead of text when a column is added.
|
* icons instead of text when a column is added.
|
||||||
@ -1033,21 +1283,129 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
|
|||||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||||
private void initComponents() {
|
private void initComponents() {
|
||||||
|
|
||||||
|
pageLabel = new javax.swing.JLabel();
|
||||||
|
pageNumLabel = new javax.swing.JLabel();
|
||||||
|
pagesLabel = new javax.swing.JLabel();
|
||||||
|
pagePrevButton = new javax.swing.JButton();
|
||||||
|
pageNextButton = new javax.swing.JButton();
|
||||||
outlineView = new OutlineView(DataResultViewerTable.FIRST_COLUMN_LABEL);
|
outlineView = new OutlineView(DataResultViewerTable.FIRST_COLUMN_LABEL);
|
||||||
|
gotoPageLabel = new javax.swing.JLabel();
|
||||||
|
gotoPageTextField = new javax.swing.JTextField();
|
||||||
|
|
||||||
|
pageLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.pageLabel.text")); // NOI18N
|
||||||
|
|
||||||
|
pageNumLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.pageNumLabel.text")); // NOI18N
|
||||||
|
|
||||||
|
pagesLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.pagesLabel.text")); // NOI18N
|
||||||
|
|
||||||
|
pagePrevButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back.png"))); // NOI18N
|
||||||
|
pagePrevButton.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.pagePrevButton.text")); // NOI18N
|
||||||
|
pagePrevButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back_disabled.png"))); // NOI18N
|
||||||
|
pagePrevButton.setFocusable(false);
|
||||||
|
pagePrevButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
|
||||||
|
pagePrevButton.setMargin(new java.awt.Insets(2, 0, 2, 0));
|
||||||
|
pagePrevButton.setPreferredSize(new java.awt.Dimension(55, 23));
|
||||||
|
pagePrevButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back_hover.png"))); // NOI18N
|
||||||
|
pagePrevButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
|
||||||
|
pagePrevButton.addActionListener(new java.awt.event.ActionListener() {
|
||||||
|
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||||
|
pagePrevButtonActionPerformed(evt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
pageNextButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward.png"))); // NOI18N
|
||||||
|
pageNextButton.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.pageNextButton.text")); // NOI18N
|
||||||
|
pageNextButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward_disabled.png"))); // NOI18N
|
||||||
|
pageNextButton.setFocusable(false);
|
||||||
|
pageNextButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
|
||||||
|
pageNextButton.setMargin(new java.awt.Insets(2, 0, 2, 0));
|
||||||
|
pageNextButton.setMaximumSize(new java.awt.Dimension(27, 23));
|
||||||
|
pageNextButton.setMinimumSize(new java.awt.Dimension(27, 23));
|
||||||
|
pageNextButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward_hover.png"))); // NOI18N
|
||||||
|
pageNextButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
|
||||||
|
pageNextButton.addActionListener(new java.awt.event.ActionListener() {
|
||||||
|
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||||
|
pageNextButtonActionPerformed(evt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
gotoPageLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.gotoPageLabel.text")); // NOI18N
|
||||||
|
|
||||||
|
gotoPageTextField.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.gotoPageTextField.text")); // NOI18N
|
||||||
|
gotoPageTextField.addActionListener(new java.awt.event.ActionListener() {
|
||||||
|
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||||
|
gotoPageTextFieldActionPerformed(evt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||||
this.setLayout(layout);
|
this.setLayout(layout);
|
||||||
layout.setHorizontalGroup(
|
layout.setHorizontalGroup(
|
||||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
.addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 691, Short.MAX_VALUE)
|
.addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||||
|
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
|
||||||
|
.addContainerGap(608, Short.MAX_VALUE)
|
||||||
|
.addComponent(pageLabel)
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||||
|
.addComponent(pageNumLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 53, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||||
|
.addComponent(pagesLabel)
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||||
|
.addComponent(pagePrevButton, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
|
.addComponent(pageNextButton, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||||
|
.addComponent(gotoPageLabel)
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
|
.addComponent(gotoPageTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 33, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
|
.addContainerGap())
|
||||||
);
|
);
|
||||||
|
|
||||||
|
layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {pageNextButton, pagePrevButton});
|
||||||
|
|
||||||
layout.setVerticalGroup(
|
layout.setVerticalGroup(
|
||||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
.addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 366, Short.MAX_VALUE)
|
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
|
||||||
|
.addContainerGap()
|
||||||
|
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
|
||||||
|
.addComponent(pageLabel)
|
||||||
|
.addComponent(pageNumLabel)
|
||||||
|
.addComponent(pagesLabel)
|
||||||
|
.addComponent(pagePrevButton, javax.swing.GroupLayout.PREFERRED_SIZE, 14, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
|
.addComponent(pageNextButton, javax.swing.GroupLayout.PREFERRED_SIZE, 15, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
|
.addComponent(gotoPageLabel)
|
||||||
|
.addComponent(gotoPageTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
|
.addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 324, Short.MAX_VALUE)
|
||||||
|
.addContainerGap())
|
||||||
);
|
);
|
||||||
|
|
||||||
|
layout.linkSize(javax.swing.SwingConstants.VERTICAL, new java.awt.Component[] {pageNextButton, pagePrevButton});
|
||||||
|
|
||||||
|
gotoPageLabel.getAccessibleContext().setAccessibleName("");
|
||||||
}// </editor-fold>//GEN-END:initComponents
|
}// </editor-fold>//GEN-END:initComponents
|
||||||
|
|
||||||
|
private void pagePrevButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pagePrevButtonActionPerformed
|
||||||
|
pagingSupport.previousPage();
|
||||||
|
}//GEN-LAST:event_pagePrevButtonActionPerformed
|
||||||
|
|
||||||
|
private void pageNextButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pageNextButtonActionPerformed
|
||||||
|
pagingSupport.nextPage();
|
||||||
|
}//GEN-LAST:event_pageNextButtonActionPerformed
|
||||||
|
|
||||||
|
private void gotoPageTextFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_gotoPageTextFieldActionPerformed
|
||||||
|
pagingSupport.gotoPage();
|
||||||
|
}//GEN-LAST:event_gotoPageTextFieldActionPerformed
|
||||||
|
|
||||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||||
|
private javax.swing.JLabel gotoPageLabel;
|
||||||
|
private javax.swing.JTextField gotoPageTextField;
|
||||||
private org.openide.explorer.view.OutlineView outlineView;
|
private org.openide.explorer.view.OutlineView outlineView;
|
||||||
|
private javax.swing.JLabel pageLabel;
|
||||||
|
private javax.swing.JButton pageNextButton;
|
||||||
|
private javax.swing.JLabel pageNumLabel;
|
||||||
|
private javax.swing.JButton pagePrevButton;
|
||||||
|
private javax.swing.JLabel pagesLabel;
|
||||||
// End of variables declaration//GEN-END:variables
|
// End of variables declaration//GEN-END:variables
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,7 @@
|
|||||||
<Group type="103" groupAlignment="0" attributes="0">
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
<Group type="102" alignment="0" attributes="0">
|
<Group type="102" alignment="0" attributes="0">
|
||||||
<Component id="globalSettingsPanel" min="-2" max="-2" attributes="0"/>
|
<Component id="globalSettingsPanel" min="-2" max="-2" attributes="0"/>
|
||||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
<EmptySpace type="unrelated" min="-2" max="-2" attributes="0"/>
|
||||||
<Component id="currentCaseSettingsPanel" min="-2" max="-2" attributes="0"/>
|
<Component id="currentCaseSettingsPanel" min="-2" max="-2" attributes="0"/>
|
||||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||||
<Component id="currentSessionSettingsPanel" min="-2" max="-2" attributes="0"/>
|
<Component id="currentSessionSettingsPanel" min="-2" max="-2" attributes="0"/>
|
||||||
@ -92,7 +92,7 @@
|
|||||||
<Layout>
|
<Layout>
|
||||||
<DimensionLayout dim="0">
|
<DimensionLayout dim="0">
|
||||||
<Group type="103" groupAlignment="0" attributes="0">
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
<Group type="102" alignment="0" attributes="0">
|
<Group type="102" attributes="0">
|
||||||
<EmptySpace max="-2" attributes="0"/>
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
<Group type="103" groupAlignment="0" attributes="0">
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
<Group type="102" attributes="0">
|
<Group type="102" attributes="0">
|
||||||
@ -153,6 +153,11 @@
|
|||||||
</Group>
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
|
<Group type="102" alignment="0" attributes="0">
|
||||||
|
<Component id="maxResultsLabel" min="-2" max="-2" attributes="0"/>
|
||||||
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
|
<Component id="maxResultsSpinner" min="-2" pref="74" max="-2" attributes="0"/>
|
||||||
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
<EmptySpace max="32767" attributes="0"/>
|
<EmptySpace max="32767" attributes="0"/>
|
||||||
</Group>
|
</Group>
|
||||||
@ -206,6 +211,11 @@
|
|||||||
<Component id="fileNameTranslationColumnCheckbox" min="-2" max="-2" attributes="0"/>
|
<Component id="fileNameTranslationColumnCheckbox" min="-2" max="-2" attributes="0"/>
|
||||||
</Group>
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
|
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||||
|
<Group type="103" groupAlignment="3" attributes="0">
|
||||||
|
<Component id="maxResultsLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||||
|
<Component id="maxResultsSpinner" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||||
|
</Group>
|
||||||
<EmptySpace max="32767" attributes="0"/>
|
<EmptySpace max="32767" attributes="0"/>
|
||||||
</Group>
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
@ -407,6 +417,26 @@
|
|||||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="fileNameTranslationColumnCheckboxActionPerformed"/>
|
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="fileNameTranslationColumnCheckboxActionPerformed"/>
|
||||||
</Events>
|
</Events>
|
||||||
</Component>
|
</Component>
|
||||||
|
<Component class="javax.swing.JLabel" name="maxResultsLabel">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="ViewPreferencesPanel.maxResultsLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="ViewPreferencesPanel.maxResultsLabel.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
</Component>
|
||||||
|
<Component class="javax.swing.JSpinner" name="maxResultsSpinner">
|
||||||
|
<Properties>
|
||||||
|
<Property name="model" type="javax.swing.SpinnerModel" editor="org.netbeans.modules.form.editors2.SpinnerModelEditor">
|
||||||
|
<SpinnerModel initial="0" maximum="50000" minimum="0" numberType="java.lang.Integer" stepSize="10000" type="number"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
<Events>
|
||||||
|
<EventHandler event="stateChanged" listener="javax.swing.event.ChangeListener" parameters="javax.swing.event.ChangeEvent" handler="maxResultsSpinnerStateChanged"/>
|
||||||
|
</Events>
|
||||||
|
</Component>
|
||||||
</SubComponents>
|
</SubComponents>
|
||||||
</Container>
|
</Container>
|
||||||
<Container class="javax.swing.JPanel" name="currentCaseSettingsPanel">
|
<Container class="javax.swing.JPanel" name="currentCaseSettingsPanel">
|
||||||
|
@ -35,6 +35,7 @@ import org.sleuthkit.autopsy.texttranslation.TextTranslationService;
|
|||||||
/**
|
/**
|
||||||
* Panel for configuring view preferences.
|
* Panel for configuring view preferences.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||||
public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
|
public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
|
||||||
|
|
||||||
private final boolean immediateUpdates;
|
private final boolean immediateUpdates;
|
||||||
@ -85,6 +86,8 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
|
|||||||
TextTranslationService tts = TextTranslationService.getInstance();
|
TextTranslationService tts = TextTranslationService.getInstance();
|
||||||
fileNameTranslationColumnCheckbox.setEnabled(tts.hasProvider());
|
fileNameTranslationColumnCheckbox.setEnabled(tts.hasProvider());
|
||||||
|
|
||||||
|
maxResultsSpinner.setValue(UserPreferences.getResultsTablePageSize());
|
||||||
|
|
||||||
// Current Case Settings
|
// Current Case Settings
|
||||||
boolean caseIsOpen = Case.isCaseOpen();
|
boolean caseIsOpen = Case.isCaseOpen();
|
||||||
currentCaseSettingsPanel.setEnabled(caseIsOpen);
|
currentCaseSettingsPanel.setEnabled(caseIsOpen);
|
||||||
@ -114,6 +117,7 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
|
|||||||
UserPreferences.setShowOnlyCurrentUserTags(hideOtherUsersTagsCheckbox.isSelected());
|
UserPreferences.setShowOnlyCurrentUserTags(hideOtherUsersTagsCheckbox.isSelected());
|
||||||
UserPreferences.setHideCentralRepoCommentsAndOccurrences(commentsOccurencesColumnsCheckbox.isSelected());
|
UserPreferences.setHideCentralRepoCommentsAndOccurrences(commentsOccurencesColumnsCheckbox.isSelected());
|
||||||
UserPreferences.setDisplayTranslatedFileNames(fileNameTranslationColumnCheckbox.isSelected());
|
UserPreferences.setDisplayTranslatedFileNames(fileNameTranslationColumnCheckbox.isSelected());
|
||||||
|
UserPreferences.setResultsTablePageSize((int)maxResultsSpinner.getValue());
|
||||||
|
|
||||||
storeGroupItemsInTreeByDataSource();
|
storeGroupItemsInTreeByDataSource();
|
||||||
|
|
||||||
@ -167,6 +171,8 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
|
|||||||
translateTextLabel = new javax.swing.JLabel();
|
translateTextLabel = new javax.swing.JLabel();
|
||||||
commentsOccurencesColumnWrapAroundText = new javax.swing.JLabel();
|
commentsOccurencesColumnWrapAroundText = new javax.swing.JLabel();
|
||||||
fileNameTranslationColumnCheckbox = new javax.swing.JCheckBox();
|
fileNameTranslationColumnCheckbox = new javax.swing.JCheckBox();
|
||||||
|
maxResultsLabel = new javax.swing.JLabel();
|
||||||
|
maxResultsSpinner = new javax.swing.JSpinner();
|
||||||
currentCaseSettingsPanel = new javax.swing.JPanel();
|
currentCaseSettingsPanel = new javax.swing.JPanel();
|
||||||
groupByDataSourceCheckbox = new javax.swing.JCheckBox();
|
groupByDataSourceCheckbox = new javax.swing.JCheckBox();
|
||||||
currentSessionSettingsPanel = new javax.swing.JPanel();
|
currentSessionSettingsPanel = new javax.swing.JPanel();
|
||||||
@ -284,6 +290,16 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
org.openide.awt.Mnemonics.setLocalizedText(maxResultsLabel, org.openide.util.NbBundle.getMessage(ViewPreferencesPanel.class, "ViewPreferencesPanel.maxResultsLabel.text")); // NOI18N
|
||||||
|
maxResultsLabel.setToolTipText(org.openide.util.NbBundle.getMessage(ViewPreferencesPanel.class, "ViewPreferencesPanel.maxResultsLabel.toolTipText")); // NOI18N
|
||||||
|
|
||||||
|
maxResultsSpinner.setModel(new javax.swing.SpinnerNumberModel(0, 0, 50000, 10000));
|
||||||
|
maxResultsSpinner.addChangeListener(new javax.swing.event.ChangeListener() {
|
||||||
|
public void stateChanged(javax.swing.event.ChangeEvent evt) {
|
||||||
|
maxResultsSpinnerStateChanged(evt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
javax.swing.GroupLayout globalSettingsPanelLayout = new javax.swing.GroupLayout(globalSettingsPanel);
|
javax.swing.GroupLayout globalSettingsPanelLayout = new javax.swing.GroupLayout(globalSettingsPanel);
|
||||||
globalSettingsPanel.setLayout(globalSettingsPanelLayout);
|
globalSettingsPanel.setLayout(globalSettingsPanelLayout);
|
||||||
globalSettingsPanelLayout.setHorizontalGroup(
|
globalSettingsPanelLayout.setHorizontalGroup(
|
||||||
@ -333,7 +349,11 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
|
|||||||
.addComponent(keepCurrentViewerRadioButton)
|
.addComponent(keepCurrentViewerRadioButton)
|
||||||
.addComponent(useBestViewerRadioButton)
|
.addComponent(useBestViewerRadioButton)
|
||||||
.addComponent(useLocalTimeRadioButton)
|
.addComponent(useLocalTimeRadioButton)
|
||||||
.addComponent(useAnotherTimeRadioButton))))))
|
.addComponent(useAnotherTimeRadioButton)))))
|
||||||
|
.addGroup(globalSettingsPanelLayout.createSequentialGroup()
|
||||||
|
.addComponent(maxResultsLabel)
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
|
.addComponent(maxResultsSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, 74, javax.swing.GroupLayout.PREFERRED_SIZE)))
|
||||||
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||||
);
|
);
|
||||||
globalSettingsPanelLayout.setVerticalGroup(
|
globalSettingsPanelLayout.setVerticalGroup(
|
||||||
@ -381,6 +401,10 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
|
|||||||
.addComponent(translateTextLabel)
|
.addComponent(translateTextLabel)
|
||||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
.addComponent(fileNameTranslationColumnCheckbox)))
|
.addComponent(fileNameTranslationColumnCheckbox)))
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||||
|
.addGroup(globalSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||||
|
.addComponent(maxResultsLabel)
|
||||||
|
.addComponent(maxResultsSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||||
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -593,6 +617,14 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
|
|||||||
}
|
}
|
||||||
}//GEN-LAST:event_fileNameTranslationColumnCheckboxActionPerformed
|
}//GEN-LAST:event_fileNameTranslationColumnCheckboxActionPerformed
|
||||||
|
|
||||||
|
private void maxResultsSpinnerStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_maxResultsSpinnerStateChanged
|
||||||
|
if (immediateUpdates) {
|
||||||
|
UserPreferences.setResultsTablePageSize((int)maxResultsSpinner.getValue());
|
||||||
|
} else {
|
||||||
|
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
|
||||||
|
}
|
||||||
|
}//GEN-LAST:event_maxResultsSpinnerStateChanged
|
||||||
|
|
||||||
|
|
||||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||||
private javax.swing.JLabel centralRepoLabel;
|
private javax.swing.JLabel centralRepoLabel;
|
||||||
@ -613,6 +645,8 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
|
|||||||
private javax.swing.JLabel hideSlackFilesLabel;
|
private javax.swing.JLabel hideSlackFilesLabel;
|
||||||
private javax.swing.JScrollPane jScrollPane1;
|
private javax.swing.JScrollPane jScrollPane1;
|
||||||
private javax.swing.JRadioButton keepCurrentViewerRadioButton;
|
private javax.swing.JRadioButton keepCurrentViewerRadioButton;
|
||||||
|
private javax.swing.JLabel maxResultsLabel;
|
||||||
|
private javax.swing.JSpinner maxResultsSpinner;
|
||||||
private javax.swing.JLabel selectFileLabel;
|
private javax.swing.JLabel selectFileLabel;
|
||||||
private javax.swing.JList<String> timeZoneList;
|
private javax.swing.JList<String> timeZoneList;
|
||||||
private javax.swing.JLabel translateTextLabel;
|
private javax.swing.JLabel translateTextLabel;
|
||||||
|
@ -34,7 +34,6 @@ import java.util.stream.Collectors;
|
|||||||
import org.apache.commons.io.FilenameUtils;
|
import org.apache.commons.io.FilenameUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
import org.openide.nodes.Children;
|
|
||||||
import org.openide.nodes.Sheet;
|
import org.openide.nodes.Sheet;
|
||||||
import org.openide.util.NbBundle;
|
import org.openide.util.NbBundle;
|
||||||
import org.openide.util.WeakListeners;
|
import org.openide.util.WeakListeners;
|
||||||
@ -55,6 +54,8 @@ import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable.Score;
|
|||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import static org.sleuthkit.autopsy.datamodel.Bundle.*;
|
import static org.sleuthkit.autopsy.datamodel.Bundle.*;
|
||||||
import static org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode.AbstractFilePropertyType.*;
|
import static org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode.AbstractFilePropertyType.*;
|
||||||
|
import org.sleuthkit.autopsy.datamodel.BaseChildFactory.NoSuchEventBusException;
|
||||||
|
import org.sleuthkit.autopsy.datamodel.BaseChildFactory.RefreshKeysEvent;
|
||||||
import org.sleuthkit.autopsy.ingest.IngestManager;
|
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||||
import org.sleuthkit.autopsy.ingest.ModuleContentEvent;
|
import org.sleuthkit.autopsy.ingest.ModuleContentEvent;
|
||||||
import org.sleuthkit.autopsy.texttranslation.NoServiceProviderException;
|
import org.sleuthkit.autopsy.texttranslation.NoServiceProviderException;
|
||||||
@ -164,17 +165,15 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
|
|||||||
if (getContent().getId() == newContent.getId()) {
|
if (getContent().getId() == newContent.getId()) {
|
||||||
// If so, refresh our children.
|
// If so, refresh our children.
|
||||||
try {
|
try {
|
||||||
Children parentsChildren = getParentNode().getChildren();
|
|
||||||
// We only want to refresh our parents children if we are in the
|
// We only want to refresh our parents children if we are in the
|
||||||
// data sources branch of the tree. The parent nodes in other
|
// data sources branch of the tree. The parent nodes in other
|
||||||
// branches of the tree (e.g. File Types and Deleted Files) do
|
// branches of the tree (e.g. File Types and Deleted Files) do
|
||||||
// not need to be refreshed.
|
// not need to be refreshed.
|
||||||
if (parentsChildren instanceof ContentChildren) {
|
BaseChildFactory.post(getParentNode().getName(), new RefreshKeysEvent());
|
||||||
((ContentChildren) parentsChildren).refreshChildren();
|
|
||||||
parentsChildren.getNodesCount();
|
|
||||||
}
|
|
||||||
} catch (NullPointerException ex) {
|
} catch (NullPointerException ex) {
|
||||||
// Skip
|
// Skip
|
||||||
|
} catch (NoSuchEventBusException ex) {
|
||||||
|
logger.log(Level.WARNING, "Failed to post key refresh event", ex); //NON-NLS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
|
} else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2011-2016 Basis Technology Corp.
|
* Copyright 2011-2019 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -18,228 +18,28 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.datamodel;
|
package org.sleuthkit.autopsy.datamodel;
|
||||||
|
|
||||||
import org.openide.nodes.AbstractNode;
|
|
||||||
import org.openide.nodes.Children.Keys;
|
|
||||||
import org.openide.nodes.Node;
|
import org.openide.nodes.Node;
|
||||||
import org.openide.util.NbBundle;
|
|
||||||
import org.sleuthkit.autopsy.datamodel.FileTypes.FileTypesNode;
|
|
||||||
import org.sleuthkit.autopsy.datamodel.accounts.Accounts;
|
|
||||||
import org.sleuthkit.autopsy.datamodel.accounts.Accounts.AccountsRootNode;
|
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
|
||||||
import org.sleuthkit.datamodel.Content;
|
import org.sleuthkit.datamodel.Content;
|
||||||
import org.sleuthkit.datamodel.DerivedFile;
|
|
||||||
import org.sleuthkit.datamodel.Directory;
|
|
||||||
import org.sleuthkit.datamodel.File;
|
|
||||||
import org.sleuthkit.datamodel.Image;
|
|
||||||
import org.sleuthkit.datamodel.LayoutFile;
|
|
||||||
import org.sleuthkit.datamodel.LocalFile;
|
|
||||||
import org.sleuthkit.datamodel.LocalDirectory;
|
|
||||||
import org.sleuthkit.datamodel.SlackFile;
|
|
||||||
import org.sleuthkit.datamodel.SleuthkitItemVisitor;
|
|
||||||
import org.sleuthkit.datamodel.SleuthkitVisitableItem;
|
import org.sleuthkit.datamodel.SleuthkitVisitableItem;
|
||||||
import org.sleuthkit.datamodel.VirtualDirectory;
|
|
||||||
import org.sleuthkit.datamodel.Volume;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract subclass for ContentChildren and RootContentChildren implementations
|
* Abstract subclass for ContentChildren implementation
|
||||||
* that handles creating Nodes from Content objects.
|
* that handles creating Nodes from Content objects.
|
||||||
*/
|
*/
|
||||||
abstract class AbstractContentChildren<T> extends Keys<T> {
|
abstract class AbstractContentChildren<T extends Content> extends BaseChildFactory<T> {
|
||||||
|
|
||||||
private final CreateSleuthkitNodeVisitor createSleuthkitNodeVisitor = new CreateSleuthkitNodeVisitor();
|
private final CreateSleuthkitNodeVisitor createSleuthkitNodeVisitor = new CreateSleuthkitNodeVisitor();
|
||||||
private final CreateAutopsyNodeVisitor createAutopsyNodeVisitor = new CreateAutopsyNodeVisitor();
|
|
||||||
|
|
||||||
/**
|
AbstractContentChildren(String nodeName) {
|
||||||
* Uses lazy Content.Keys
|
super(nodeName, new DataSourcesKnownAndSlackFilter<>());
|
||||||
*/
|
|
||||||
AbstractContentChildren() {
|
|
||||||
/*
|
|
||||||
* This was turned off because we were getting out of memory errors when
|
|
||||||
* the filter nodes were hiding nodes. Turning this off seemed to help
|
|
||||||
*/
|
|
||||||
super(false); //don't use lazy behavior
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Node[] createNodes(T key) {
|
protected Node createNodeForKey(T key) {
|
||||||
if (key instanceof SleuthkitVisitableItem) {
|
if (key instanceof SleuthkitVisitableItem) {
|
||||||
return new Node[]{((SleuthkitVisitableItem) key).accept(createSleuthkitNodeVisitor)};
|
return ((SleuthkitVisitableItem) key).accept(createSleuthkitNodeVisitor);
|
||||||
} else {
|
} else {
|
||||||
return new Node[]{((AutopsyVisitableItem) key).accept(createAutopsyNodeVisitor)};
|
return null;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates appropriate Node for each sub-class of Content
|
|
||||||
*/
|
|
||||||
public static class CreateSleuthkitNodeVisitor extends SleuthkitItemVisitor.Default<AbstractContentNode<? extends Content>> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AbstractContentNode<? extends Content> visit(Directory drctr) {
|
|
||||||
return new DirectoryNode(drctr);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AbstractContentNode<? extends Content> visit(File file) {
|
|
||||||
return new FileNode(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AbstractContentNode<? extends Content> visit(Image image) {
|
|
||||||
return new ImageNode(image);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AbstractContentNode<? extends Content> visit(Volume volume) {
|
|
||||||
return new VolumeNode(volume);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AbstractContentNode<? extends Content> visit(LayoutFile lf) {
|
|
||||||
return new LayoutFileNode(lf);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AbstractContentNode<? extends Content> visit(DerivedFile df) {
|
|
||||||
return new LocalFileNode(df);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AbstractContentNode<? extends Content> visit(LocalFile lf) {
|
|
||||||
return new LocalFileNode(lf);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AbstractContentNode<? extends Content> visit(VirtualDirectory ld) {
|
|
||||||
return new VirtualDirectoryNode(ld);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AbstractContentNode<? extends Content> visit(LocalDirectory ld) {
|
|
||||||
return new LocalDirectoryNode(ld);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AbstractContentNode<? extends Content> visit(SlackFile sf) {
|
|
||||||
return new SlackFileNode(sf);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AbstractContentNode<? extends Content> visit(BlackboardArtifact art) {
|
|
||||||
return new BlackboardArtifactNode(art);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected AbstractContentNode<? extends Content> defaultVisit(SleuthkitVisitableItem di) {
|
|
||||||
throw new UnsupportedOperationException(NbBundle.getMessage(this.getClass(),
|
|
||||||
"AbstractContentChildren.CreateTSKNodeVisitor.exception.noNodeMsg"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a DisplayableItemNode for use as a subtree root node for the Autopsy
|
|
||||||
* tree view from each type of AutopsyVisitableItem visited. There are
|
|
||||||
* AutopsyVisitableItems for the Data Sources, Views, Results, and Reports
|
|
||||||
* subtrees, and for the subtrees of Results (e.g., Extracted Content, Hash
|
|
||||||
* Set Hits, etc.).
|
|
||||||
*/
|
|
||||||
static class CreateAutopsyNodeVisitor extends AutopsyItemVisitor.Default<AbstractNode> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ExtractedContent.RootNode visit(ExtractedContent ec) {
|
|
||||||
return ec.new RootNode(ec.getSleuthkitCase());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AbstractNode visit(FileTypesByExtension sf) {
|
|
||||||
return sf.new FileTypesByExtNode(sf.getSleuthkitCase(), null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AbstractNode visit(RecentFiles rf) {
|
|
||||||
return new RecentFilesNode(rf.getSleuthkitCase());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AbstractNode visit(DeletedContent dc) {
|
|
||||||
return new DeletedContent.DeletedContentsNode(dc.getSleuthkitCase(), dc.filteringDataSourceObjId());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AbstractNode visit(FileSize dc) {
|
|
||||||
return new FileSize.FileSizeRootNode(dc.getSleuthkitCase(), dc.filteringDataSourceObjId());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AbstractNode visit(KeywordHits kh) {
|
|
||||||
return kh.new RootNode();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AbstractNode visit(HashsetHits hh) {
|
|
||||||
return hh.new RootNode();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AbstractNode visit(InterestingHits ih) {
|
|
||||||
return ih.new RootNode();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AbstractNode visit(EmailExtracted ee) {
|
|
||||||
return ee.new RootNode();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AbstractNode visit(Tags tagsNodeKey) {
|
|
||||||
return tagsNodeKey.new RootNode(tagsNodeKey.filteringDataSourceObjId());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AbstractNode visit(DataSources i) {
|
|
||||||
return new DataSourcesNode(i.filteringDataSourceObjId());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AbstractNode visit(DataSourceGrouping datasourceGrouping) {
|
|
||||||
return new DataSourceGroupingNode(datasourceGrouping.getDataSource());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AbstractNode visit(Views v) {
|
|
||||||
return new ViewsNode(v.getSleuthkitCase(), v.filteringDataSourceObjId());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AbstractNode visit(Results results) {
|
|
||||||
return new ResultsNode(results.getSleuthkitCase(), results.filteringDataSourceObjId() );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AbstractNode visit(FileTypes ft) {
|
|
||||||
return ft.new FileTypesNode();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AbstractNode visit(Reports reportsItem) {
|
|
||||||
return new Reports.ReportsListNode();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AbstractNode visit(Accounts accountsItem) {
|
|
||||||
return accountsItem.new AccountsRootNode();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected AbstractNode defaultVisit(AutopsyVisitableItem di) {
|
|
||||||
throw new UnsupportedOperationException(
|
|
||||||
NbBundle.getMessage(this.getClass(),
|
|
||||||
"AbstractContentChildren.createAutopsyNodeVisitor.exception.noNodeMsg"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AbstractNode visit(FileTypesByMimeType ftByMimeTypeItem) {
|
|
||||||
return ftByMimeTypeItem.new ByMimeTypeNode();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2011-2018 Basis Technology Corp.
|
* Copyright 2011-2019 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -22,6 +22,7 @@ import java.sql.ResultSet;
|
|||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
import org.openide.nodes.Children;
|
||||||
|
|
||||||
import org.openide.util.lookup.Lookups;
|
import org.openide.util.lookup.Lookups;
|
||||||
import org.openide.util.Lookup;
|
import org.openide.util.Lookup;
|
||||||
@ -65,8 +66,7 @@ public abstract class AbstractContentNode<T extends Content> extends ContentNode
|
|||||||
* @param lookup The Lookup object for the node.
|
* @param lookup The Lookup object for the node.
|
||||||
*/
|
*/
|
||||||
AbstractContentNode(T content, Lookup lookup) {
|
AbstractContentNode(T content, Lookup lookup) {
|
||||||
//TODO consider child factory for the content children
|
super(Children.create(new ContentChildren(content), true), lookup);
|
||||||
super(new ContentChildren(content), lookup);
|
|
||||||
this.content = content;
|
this.content = content;
|
||||||
//super.setName(ContentUtils.getSystemName(content));
|
//super.setName(ContentUtils.getSystemName(content));
|
||||||
super.setName("content_" + Long.toString(content.getId())); //NON-NLS
|
super.setName("content_" + Long.toString(content.getId())); //NON-NLS
|
||||||
|
@ -135,7 +135,7 @@ public final class AutopsyTreeChildFactory extends ChildFactory.Detachable<Objec
|
|||||||
protected Node createNodeForKey(Object key) {
|
protected Node createNodeForKey(Object key) {
|
||||||
|
|
||||||
if (key instanceof SleuthkitVisitableItem) {
|
if (key instanceof SleuthkitVisitableItem) {
|
||||||
return ((SleuthkitVisitableItem) key).accept(new RootContentChildren.CreateSleuthkitNodeVisitor());
|
return ((SleuthkitVisitableItem) key).accept(new CreateSleuthkitNodeVisitor());
|
||||||
} else if (key instanceof AutopsyVisitableItem) {
|
} else if (key instanceof AutopsyVisitableItem) {
|
||||||
return ((AutopsyVisitableItem) key).accept(new RootContentChildren.CreateAutopsyNodeVisitor());
|
return ((AutopsyVisitableItem) key).accept(new RootContentChildren.CreateAutopsyNodeVisitor());
|
||||||
}
|
}
|
||||||
|
346
Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java
Normal file
346
Core/src/org/sleuthkit/autopsy/datamodel/BaseChildFactory.java
Normal file
@ -0,0 +1,346 @@
|
|||||||
|
/*
|
||||||
|
* 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.datamodel;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.eventbus.EventBus;
|
||||||
|
import com.google.common.eventbus.Subscribe;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
import java.util.prefs.PreferenceChangeEvent;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import org.openide.nodes.ChildFactory;
|
||||||
|
import org.openide.util.NbBundle.Messages;
|
||||||
|
import org.sleuthkit.autopsy.core.UserPreferences;
|
||||||
|
import org.sleuthkit.datamodel.Content;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract child factory that provides paging and filtering functionality to
|
||||||
|
* subclasses.
|
||||||
|
*
|
||||||
|
* @param <T>
|
||||||
|
*/
|
||||||
|
public abstract class BaseChildFactory<T extends Content> extends ChildFactory.Detachable<T> {
|
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(BaseChildFactory.class.getName());
|
||||||
|
|
||||||
|
private Predicate<T> filter;
|
||||||
|
private boolean isPageChangeEvent;
|
||||||
|
private boolean isPageSizeChangeEvent;
|
||||||
|
|
||||||
|
private final PagingSupport pagingSupport;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This static map is used to facilitate communication between the UI and
|
||||||
|
* the child factory.
|
||||||
|
*/
|
||||||
|
private static Map<String, EventBus> nodeNameToEventBusMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
@Messages({
|
||||||
|
"# {0} - node name", "BaseChildFactory.NoSuchEventBusException.message=No event bus for node: {0}"
|
||||||
|
})
|
||||||
|
public static class NoSuchEventBusException extends Exception {
|
||||||
|
|
||||||
|
public NoSuchEventBusException(String nodeName) {
|
||||||
|
super(Bundle.BaseChildFactory_NoSuchEventBusException_message(nodeName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the given subscriber for the given node name. Will create the
|
||||||
|
* event bus for the given node name if it does not exist.
|
||||||
|
*
|
||||||
|
* @param nodeName The name of the node.
|
||||||
|
* @param subscriber The subscriber to register.
|
||||||
|
*/
|
||||||
|
public static void register(String nodeName, Object subscriber) {
|
||||||
|
EventBus bus = nodeNameToEventBusMap.get(nodeName);
|
||||||
|
if (bus == null) {
|
||||||
|
bus = new EventBus(nodeName);
|
||||||
|
nodeNameToEventBusMap.put(nodeName, bus);
|
||||||
|
}
|
||||||
|
bus.register(subscriber);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Post the given event for the given node name.
|
||||||
|
*
|
||||||
|
* @param nodeName The name of the node.
|
||||||
|
* @param event The event to post.
|
||||||
|
*
|
||||||
|
* @throws
|
||||||
|
* org.sleuthkit.autopsy.datamodel.BaseChildFactory.NoSuchEventBusException
|
||||||
|
*/
|
||||||
|
public static void post(String nodeName, Object event) throws NoSuchEventBusException {
|
||||||
|
EventBus bus = nodeNameToEventBusMap.get(nodeName);
|
||||||
|
if (bus == null) {
|
||||||
|
throw new NoSuchEventBusException(nodeName);
|
||||||
|
}
|
||||||
|
bus.post(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BaseChildFactory(String nodeName) {
|
||||||
|
/**
|
||||||
|
* Initialize a no-op filter that always returns true.
|
||||||
|
*/
|
||||||
|
this(nodeName, x -> true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BaseChildFactory(String nodeName, Predicate<T> filter) {
|
||||||
|
pagingSupport = new PagingSupport(nodeName);
|
||||||
|
pagingSupport.initialize();
|
||||||
|
isPageChangeEvent = false;
|
||||||
|
isPageSizeChangeEvent = false;
|
||||||
|
this.filter = filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void addNotify() {
|
||||||
|
onAdd();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void removeNotify() {
|
||||||
|
onRemove();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subclasses implement this to construct a collection of keys.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected abstract List<T> makeKeys();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subclasses implement this to initialize any required resources.
|
||||||
|
*/
|
||||||
|
protected abstract void onAdd();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subclasses implement this to clean up any resources they acquired in
|
||||||
|
* onAdd()
|
||||||
|
*/
|
||||||
|
protected abstract void onRemove();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean createKeys(List<T> toPopulate) {
|
||||||
|
/**
|
||||||
|
* For page change events and page size change events we simply return
|
||||||
|
* the previously calculated set of keys, otherwise we make a new set of
|
||||||
|
* keys.
|
||||||
|
*/
|
||||||
|
if (!isPageChangeEvent && !isPageSizeChangeEvent) {
|
||||||
|
List<T> allKeys = makeKeys();
|
||||||
|
|
||||||
|
pagingSupport.splitKeysIntoPages(allKeys.stream().filter(filter).collect(Collectors.toList()));
|
||||||
|
}
|
||||||
|
|
||||||
|
toPopulate.addAll(pagingSupport.getCurrentPage());
|
||||||
|
|
||||||
|
// Reset page change and page size change event flags
|
||||||
|
isPageChangeEvent = false;
|
||||||
|
isPageSizeChangeEvent = false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event used to trigger recreation of the keys.
|
||||||
|
*/
|
||||||
|
public static class RefreshKeysEvent {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event used to let subscribers know that the user has navigated to a
|
||||||
|
* different page.
|
||||||
|
*/
|
||||||
|
public static class PageChangeEvent {
|
||||||
|
|
||||||
|
private final int pageNumber;
|
||||||
|
|
||||||
|
public PageChangeEvent(int newPageNumber) {
|
||||||
|
pageNumber = newPageNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPageNumber() {
|
||||||
|
return pageNumber;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event used to let subscribers know that the number of pages has changed.
|
||||||
|
*/
|
||||||
|
public static class PageCountChangeEvent {
|
||||||
|
|
||||||
|
private final int pageCount;
|
||||||
|
|
||||||
|
public PageCountChangeEvent(int newPageCount) {
|
||||||
|
pageCount = newPageCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPageCount() {
|
||||||
|
return pageCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event used to let subscribers know that the page size has changed.
|
||||||
|
*/
|
||||||
|
public static class PageSizeChangeEvent {
|
||||||
|
|
||||||
|
private final int pageSize;
|
||||||
|
|
||||||
|
public PageSizeChangeEvent(int newPageSize) {
|
||||||
|
pageSize = newPageSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPageSize() {
|
||||||
|
return pageSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class that supplies paging related functionality to the base child
|
||||||
|
* factory class.
|
||||||
|
*/
|
||||||
|
private class PagingSupport {
|
||||||
|
|
||||||
|
private final String nodeName;
|
||||||
|
private int pageSize;
|
||||||
|
private int currentPage;
|
||||||
|
private List<List<T>> pages;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct PagingSupport instance for the given node name.
|
||||||
|
*
|
||||||
|
* @param nodeName Name of the node in the tree for which results are
|
||||||
|
* being displayed. The node name is used to allow
|
||||||
|
* communication between the UI and the ChildFactory via
|
||||||
|
* an EventBus.
|
||||||
|
*/
|
||||||
|
PagingSupport(String nodeName) {
|
||||||
|
currentPage = 1;
|
||||||
|
pageSize = UserPreferences.getResultsTablePageSize();
|
||||||
|
pages = new ArrayList<>();
|
||||||
|
this.nodeName = nodeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
void initialize() {
|
||||||
|
/**
|
||||||
|
* Set up a change listener so we know when the user changes the
|
||||||
|
* page size.
|
||||||
|
*/
|
||||||
|
UserPreferences.addChangeListener((PreferenceChangeEvent evt) -> {
|
||||||
|
if (evt.getKey().equals(UserPreferences.RESULTS_TABLE_PAGE_SIZE)) {
|
||||||
|
pageSize = UserPreferences.getResultsTablePageSize();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
register(nodeName, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the list of keys at the current page.
|
||||||
|
*
|
||||||
|
* @return List of keys.
|
||||||
|
*/
|
||||||
|
List<T> getCurrentPage() {
|
||||||
|
if (!pages.isEmpty()) {
|
||||||
|
return pages.get(currentPage - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Split the given collection of keys into pages based on page size.
|
||||||
|
*
|
||||||
|
* @param keys
|
||||||
|
*/
|
||||||
|
void splitKeysIntoPages(List<T> keys) {
|
||||||
|
int oldPageCount = pages.size();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If pageSize is set split keys into pages, otherwise create a
|
||||||
|
* single page containing all keys.
|
||||||
|
*/
|
||||||
|
pages = Lists.partition(keys, pageSize > 0 ? pageSize : keys.size());
|
||||||
|
if (pages.size() != oldPageCount) {
|
||||||
|
try {
|
||||||
|
// Number of pages has changed so we need to send out a notification.
|
||||||
|
post(nodeName, new PageCountChangeEvent(pages.size()));
|
||||||
|
} catch (NoSuchEventBusException ex) {
|
||||||
|
logger.log(Level.WARNING, "Failed to post page change event.", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receives page change events from UI components and triggers a refresh
|
||||||
|
* in the child factory.
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
*/
|
||||||
|
@Subscribe
|
||||||
|
private void subscribeToPageChange(PageChangeEvent event) {
|
||||||
|
if (event != null) {
|
||||||
|
currentPage = event.getPageNumber();
|
||||||
|
isPageChangeEvent = true;
|
||||||
|
refresh(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receives page size change events from UI components and triggers a
|
||||||
|
* refresh in the child factory if necessary.
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
*/
|
||||||
|
@Subscribe
|
||||||
|
private void subscribeToPageSizeChange(PageSizeChangeEvent event) {
|
||||||
|
if (event != null) {
|
||||||
|
int newPageSize = event.getPageSize();
|
||||||
|
if (pageSize == newPageSize) {
|
||||||
|
// No change...nothing to do.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pageSize = newPageSize;
|
||||||
|
splitKeysIntoPages(pages.stream().flatMap(List::stream).collect(Collectors.toList()));
|
||||||
|
|
||||||
|
currentPage = 1;
|
||||||
|
isPageSizeChangeEvent = true;
|
||||||
|
refresh(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
private void subscribeToRefreshKeys(RefreshKeysEvent event) {
|
||||||
|
if (event != null) {
|
||||||
|
refresh(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -43,6 +43,8 @@ ArtifactStringContent.attrsTableHeader.type=Type
|
|||||||
ArtifactStringContent.attrsTableHeader.value=Value
|
ArtifactStringContent.attrsTableHeader.value=Value
|
||||||
ArtifactStringContent.failedToGetAttributes.message=Failed to get some or all attributes from case database
|
ArtifactStringContent.failedToGetAttributes.message=Failed to get some or all attributes from case database
|
||||||
ArtifactStringContent.failedToGetSourcePath.message=Failed to get source file path from case database
|
ArtifactStringContent.failedToGetSourcePath.message=Failed to get source file path from case database
|
||||||
|
# {0} - node name
|
||||||
|
BaseChildFactory.NoSuchEventBusException.message=No event bus for node: {0}
|
||||||
BlackboardArtifactNode.createSheet.artifactDetails.displayName=Result Details
|
BlackboardArtifactNode.createSheet.artifactDetails.displayName=Result Details
|
||||||
BlackboardArtifactNode.createSheet.artifactDetails.name=Result Details
|
BlackboardArtifactNode.createSheet.artifactDetails.name=Result Details
|
||||||
BlackboardArtifactNode.createSheet.artifactMD5.displayName=MD5 Hash
|
BlackboardArtifactNode.createSheet.artifactMD5.displayName=MD5 Hash
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2011-2014 Basis Technology Corp.
|
* Copyright 2011-2019 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -33,17 +33,15 @@ import org.sleuthkit.datamodel.VolumeSystem;
|
|||||||
/**
|
/**
|
||||||
* Makes the children nodes / keys for a given content object. Has knowledge
|
* Makes the children nodes / keys for a given content object. Has knowledge
|
||||||
* about the structure of the directory tree and what levels should be ignored.
|
* about the structure of the directory tree and what levels should be ignored.
|
||||||
* TODO consider a ContentChildren child factory
|
|
||||||
*/
|
*/
|
||||||
class ContentChildren extends AbstractContentChildren<Content> {
|
class ContentChildren extends AbstractContentChildren<Content> {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(ContentChildren.class.getName());
|
private static final Logger logger = Logger.getLogger(ContentChildren.class.getName());
|
||||||
//private static final int MAX_CHILD_COUNT = 1000000;
|
|
||||||
|
|
||||||
private final Content parent;
|
private final Content parent;
|
||||||
|
|
||||||
ContentChildren(Content parent) {
|
ContentChildren(Content parent) {
|
||||||
super(); //initialize lazy behavior
|
super("content_" + Long.toString(parent.getId()));
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,27 +102,13 @@ class ContentChildren extends AbstractContentChildren<Content> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void addNotify() {
|
protected List<Content> makeKeys() {
|
||||||
super.addNotify();
|
return getDisplayChildren(parent);
|
||||||
|
|
||||||
//TODO check global settings
|
|
||||||
//if above limit, query and return subrange
|
|
||||||
//StopWatch s2 = new StopWatch();
|
|
||||||
//s2.start();
|
|
||||||
//logger.log(Level.INFO, "GETTING CHILDREN CONTENT for parent: " + parent.getName());
|
|
||||||
List<Content> children = getDisplayChildren(parent);
|
|
||||||
//s2.stop();
|
|
||||||
//logger.log(Level.INFO, "GOT CHILDREN CONTENTS:" + children.size() + ", took: " + s2.getElapsedTime());
|
|
||||||
|
|
||||||
//limit number children
|
|
||||||
//setKeys(children.subList(0, Math.min(children.size(), MAX_CHILD_COUNT)));
|
|
||||||
setKeys(children);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void removeNotify() {
|
protected void onAdd() {
|
||||||
super.removeNotify();
|
// No-op
|
||||||
setKeys(new ArrayList<>());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -133,7 +117,11 @@ class ContentChildren extends AbstractContentChildren<Content> {
|
|||||||
* them).
|
* them).
|
||||||
*/
|
*/
|
||||||
void refreshChildren() {
|
void refreshChildren() {
|
||||||
List<Content> children = getDisplayChildren(parent);
|
refresh(true);
|
||||||
setKeys(children);
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onRemove() {
|
||||||
|
// No-op
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,102 @@
|
|||||||
|
/*
|
||||||
|
* 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.datamodel;
|
||||||
|
|
||||||
|
import org.openide.util.NbBundle;
|
||||||
|
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||||
|
import org.sleuthkit.datamodel.Content;
|
||||||
|
import org.sleuthkit.datamodel.DerivedFile;
|
||||||
|
import org.sleuthkit.datamodel.Directory;
|
||||||
|
import org.sleuthkit.datamodel.File;
|
||||||
|
import org.sleuthkit.datamodel.Image;
|
||||||
|
import org.sleuthkit.datamodel.LayoutFile;
|
||||||
|
import org.sleuthkit.datamodel.LocalDirectory;
|
||||||
|
import org.sleuthkit.datamodel.LocalFile;
|
||||||
|
import org.sleuthkit.datamodel.SlackFile;
|
||||||
|
import org.sleuthkit.datamodel.SleuthkitItemVisitor;
|
||||||
|
import org.sleuthkit.datamodel.SleuthkitVisitableItem;
|
||||||
|
import org.sleuthkit.datamodel.VirtualDirectory;
|
||||||
|
import org.sleuthkit.datamodel.Volume;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates appropriate Node for each sub-class of Content
|
||||||
|
*/
|
||||||
|
public class CreateSleuthkitNodeVisitor extends SleuthkitItemVisitor.Default<AbstractContentNode<? extends Content>> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractContentNode<? extends Content> visit(Directory drctr) {
|
||||||
|
return new DirectoryNode(drctr);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractContentNode<? extends Content> visit(File file) {
|
||||||
|
return new FileNode(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractContentNode<? extends Content> visit(Image image) {
|
||||||
|
return new ImageNode(image);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractContentNode<? extends Content> visit(Volume volume) {
|
||||||
|
return new VolumeNode(volume);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractContentNode<? extends Content> visit(LayoutFile lf) {
|
||||||
|
return new LayoutFileNode(lf);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractContentNode<? extends Content> visit(DerivedFile df) {
|
||||||
|
return new LocalFileNode(df);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractContentNode<? extends Content> visit(LocalFile lf) {
|
||||||
|
return new LocalFileNode(lf);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractContentNode<? extends Content> visit(VirtualDirectory ld) {
|
||||||
|
return new VirtualDirectoryNode(ld);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractContentNode<? extends Content> visit(LocalDirectory ld) {
|
||||||
|
return new LocalDirectoryNode(ld);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractContentNode<? extends Content> visit(SlackFile sf) {
|
||||||
|
return new SlackFileNode(sf);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractContentNode<? extends Content> visit(BlackboardArtifact art) {
|
||||||
|
return new BlackboardArtifactNode(art);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected AbstractContentNode<? extends Content> defaultVisit(SleuthkitVisitableItem di) {
|
||||||
|
throw new UnsupportedOperationException(NbBundle.getMessage(this.getClass(),
|
||||||
|
"AbstractContentChildren.CreateTSKNodeVisitor.exception.noNodeMsg"));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* 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.datamodel;
|
||||||
|
|
||||||
|
import java.util.prefs.PreferenceChangeEvent;
|
||||||
|
import org.sleuthkit.autopsy.core.UserPreferences;
|
||||||
|
import static org.sleuthkit.autopsy.datamodel.KnownAndSlackFilterBase.filterKnown;
|
||||||
|
import static org.sleuthkit.autopsy.datamodel.KnownAndSlackFilterBase.filterSlack;
|
||||||
|
import org.sleuthkit.datamodel.Content;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Known and Slack filter for Data Sources section of the tree.
|
||||||
|
*
|
||||||
|
* @param <T>
|
||||||
|
*/
|
||||||
|
class DataSourcesKnownAndSlackFilter<T extends Content> extends KnownAndSlackFilterBase<T> {
|
||||||
|
|
||||||
|
static {
|
||||||
|
/**
|
||||||
|
* Watch for user preference changes and update variables inherited from
|
||||||
|
* our parent. The actual filtering is provided by our parent class.
|
||||||
|
*/
|
||||||
|
UserPreferences.addChangeListener((PreferenceChangeEvent evt) -> {
|
||||||
|
if (evt.getKey().equals(UserPreferences.HIDE_KNOWN_FILES_IN_DATA_SRCS_TREE)) {
|
||||||
|
filterKnown = UserPreferences.hideKnownFilesInDataSourcesTree();
|
||||||
|
} else if (evt.getKey().equals(UserPreferences.HIDE_SLACK_FILES_IN_DATA_SRCS_TREE)) {
|
||||||
|
filterSlack = UserPreferences.hideSlackFilesInDataSourcesTree();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
DataSourcesKnownAndSlackFilter() {
|
||||||
|
filterKnown = UserPreferences.hideKnownFilesInDataSourcesTree();
|
||||||
|
filterSlack = UserPreferences.hideSlackFilesInDataSourcesTree();
|
||||||
|
}
|
||||||
|
}
|
@ -27,6 +27,7 @@ import java.util.Comparator;
|
|||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
import org.openide.nodes.Children;
|
||||||
import org.openide.nodes.Sheet;
|
import org.openide.nodes.Sheet;
|
||||||
import org.openide.util.NbBundle;
|
import org.openide.util.NbBundle;
|
||||||
import org.openide.util.lookup.Lookups;
|
import org.openide.util.lookup.Lookups;
|
||||||
@ -56,7 +57,7 @@ public class DataSourcesNode extends DisplayableItemNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public DataSourcesNode(long dsObjId) {
|
public DataSourcesNode(long dsObjId) {
|
||||||
super(new DataSourcesNodeChildren(dsObjId), Lookups.singleton(NAME));
|
super(Children.create(new DataSourcesNodeChildren(dsObjId), true), Lookups.singleton(NAME));
|
||||||
displayName = (dsObjId > 0) ? NbBundle.getMessage(DataSourcesNode.class, "DataSourcesNode.group_by_datasource.name") : NAME;
|
displayName = (dsObjId > 0) ? NbBundle.getMessage(DataSourcesNode.class, "DataSourcesNode.group_by_datasource.name") : NAME;
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
@ -87,7 +88,7 @@ public class DataSourcesNode extends DisplayableItemNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public DataSourcesNodeChildren(long dsObjId) {
|
public DataSourcesNodeChildren(long dsObjId) {
|
||||||
super();
|
super("ds_" + Long.toString(dsObjId));
|
||||||
this.currentKeys = new ArrayList<>();
|
this.currentKeys = new ArrayList<>();
|
||||||
this.datasourceObjId = dsObjId;
|
this.datasourceObjId = dsObjId;
|
||||||
}
|
}
|
||||||
@ -97,25 +98,24 @@ public class DataSourcesNode extends DisplayableItemNode {
|
|||||||
public void propertyChange(PropertyChangeEvent evt) {
|
public void propertyChange(PropertyChangeEvent evt) {
|
||||||
String eventType = evt.getPropertyName();
|
String eventType = evt.getPropertyName();
|
||||||
if (eventType.equals(Case.Events.DATA_SOURCE_ADDED.toString())) {
|
if (eventType.equals(Case.Events.DATA_SOURCE_ADDED.toString())) {
|
||||||
reloadKeys();
|
refresh(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void addNotify() {
|
protected void onAdd() {
|
||||||
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED), pcl);
|
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED), pcl);
|
||||||
reloadKeys();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void removeNotify() {
|
protected void onRemove() {
|
||||||
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED), pcl);
|
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED), pcl);
|
||||||
currentKeys.clear();
|
currentKeys.clear();
|
||||||
setKeys(Collections.<Content>emptySet());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void reloadKeys() {
|
@Override
|
||||||
|
protected List<Content> makeKeys() {
|
||||||
try {
|
try {
|
||||||
if (datasourceObjId == 0) {
|
if (datasourceObjId == 0) {
|
||||||
currentKeys = Case.getCurrentCaseThrows().getDataSources();
|
currentKeys = Case.getCurrentCaseThrows().getDataSources();
|
||||||
@ -135,20 +135,11 @@ public class DataSourcesNode extends DisplayableItemNode {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
setKeys(currentKeys);
|
|
||||||
} catch (TskCoreException | NoCurrentCaseException | TskDataException ex) {
|
} catch (TskCoreException | NoCurrentCaseException | TskDataException ex) {
|
||||||
logger.log(Level.SEVERE, "Error getting data sources: {0}", ex.getMessage()); // NON-NLS
|
logger.log(Level.SEVERE, "Error getting data sources: {0}", ex.getMessage()); // NON-NLS
|
||||||
setKeys(Collections.<Content>emptySet());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
return currentKeys;
|
||||||
* Refresh all content keys This creates new nodes of keys have changed.
|
|
||||||
*/
|
|
||||||
public void refreshContentKeys() {
|
|
||||||
for (Content key : currentKeys) {
|
|
||||||
refreshKey(key);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2011-2018 Basis Technology Corp.
|
* Copyright 2011-2019 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -39,7 +39,6 @@ import org.openide.util.lookup.Lookups;
|
|||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
import org.sleuthkit.autopsy.casemodule.CasePreferences;
|
import org.sleuthkit.autopsy.casemodule.CasePreferences;
|
||||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||||
import org.sleuthkit.autopsy.core.UserPreferences;
|
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.autopsy.ingest.IngestManager;
|
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||||
import org.sleuthkit.datamodel.AbstractFile;
|
import org.sleuthkit.datamodel.AbstractFile;
|
||||||
@ -358,7 +357,7 @@ public class DeletedContent implements AutopsyVisitableItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static class DeletedContentChildren extends ChildFactory.Detachable<AbstractFile> {
|
static class DeletedContentChildren extends BaseChildFactory<AbstractFile> {
|
||||||
|
|
||||||
private final SleuthkitCase skCase;
|
private final SleuthkitCase skCase;
|
||||||
private final DeletedContent.DeletedContentFilter filter;
|
private final DeletedContent.DeletedContentFilter filter;
|
||||||
@ -368,6 +367,7 @@ public class DeletedContent implements AutopsyVisitableItem {
|
|||||||
private final long datasourceObjId;
|
private final long datasourceObjId;
|
||||||
|
|
||||||
DeletedContentChildren(DeletedContent.DeletedContentFilter filter, SleuthkitCase skCase, Observable o, long datasourceObjId) {
|
DeletedContentChildren(DeletedContent.DeletedContentFilter filter, SleuthkitCase skCase, Observable o, long datasourceObjId) {
|
||||||
|
super(filter.getName(), new ViewsKnownAndSlackFilter<>());
|
||||||
this.skCase = skCase;
|
this.skCase = skCase;
|
||||||
this.filter = filter;
|
this.filter = filter;
|
||||||
this.notifier = o;
|
this.notifier = o;
|
||||||
@ -376,6 +376,11 @@ public class DeletedContent implements AutopsyVisitableItem {
|
|||||||
|
|
||||||
private final Observer observer = new DeletedContentChildrenObserver();
|
private final Observer observer = new DeletedContentChildrenObserver();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<AbstractFile> makeKeys() {
|
||||||
|
return runFsQuery();
|
||||||
|
}
|
||||||
|
|
||||||
// Cause refresh of children if there are changes
|
// Cause refresh of children if there are changes
|
||||||
private class DeletedContentChildrenObserver implements Observer {
|
private class DeletedContentChildrenObserver implements Observer {
|
||||||
|
|
||||||
@ -386,25 +391,19 @@ public class DeletedContent implements AutopsyVisitableItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void addNotify() {
|
protected void onAdd() {
|
||||||
if (notifier != null) {
|
if (notifier != null) {
|
||||||
notifier.addObserver(observer);
|
notifier.addObserver(observer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void removeNotify() {
|
protected void onRemove() {
|
||||||
if (notifier != null) {
|
if (notifier != null) {
|
||||||
notifier.deleteObserver(observer);
|
notifier.deleteObserver(observer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean createKeys(List<AbstractFile> list) {
|
|
||||||
list.addAll(runFsQuery());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static private String makeQuery(DeletedContent.DeletedContentFilter filter, long filteringDSObjId) {
|
static private String makeQuery(DeletedContent.DeletedContentFilter filter, long filteringDSObjId) {
|
||||||
String query = "";
|
String query = "";
|
||||||
switch (filter) {
|
switch (filter) {
|
||||||
@ -440,11 +439,6 @@ public class DeletedContent implements AutopsyVisitableItem {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (UserPreferences.hideKnownFilesInViewsTree()) {
|
|
||||||
query += " AND (known != " + TskData.FileKnown.KNOWN.getFileKnownValue() //NON-NLS
|
|
||||||
+ " OR known IS NULL)"; //NON-NLS
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) {
|
if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) {
|
||||||
query += " AND data_source_obj_id = " + filteringDSObjId;
|
query += " AND data_source_obj_id = " + filteringDSObjId;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2012-2018 Basis Technology Corp.
|
* Copyright 2012-2019 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -42,7 +42,6 @@ import org.openide.util.lookup.Lookups;
|
|||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
import org.sleuthkit.autopsy.casemodule.CasePreferences;
|
import org.sleuthkit.autopsy.casemodule.CasePreferences;
|
||||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||||
import org.sleuthkit.autopsy.core.UserPreferences;
|
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.autopsy.ingest.IngestManager;
|
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||||
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
|
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
|
||||||
@ -494,41 +493,52 @@ public class EmailExtracted implements AutopsyVisitableItem {
|
|||||||
/**
|
/**
|
||||||
* Node representing mail folder content (mail messages)
|
* Node representing mail folder content (mail messages)
|
||||||
*/
|
*/
|
||||||
private class MessageFactory extends ChildFactory<Long> implements Observer {
|
private class MessageFactory extends BaseChildFactory<BlackboardArtifact> implements Observer {
|
||||||
|
|
||||||
private final String accountName;
|
private final String accountName;
|
||||||
private final String folderName;
|
private final String folderName;
|
||||||
|
|
||||||
private MessageFactory(String accountName, String folderName) {
|
private MessageFactory(String accountName, String folderName) {
|
||||||
super();
|
super(accountName + "_" + folderName);
|
||||||
this.accountName = accountName;
|
this.accountName = accountName;
|
||||||
this.folderName = folderName;
|
this.folderName = folderName;
|
||||||
emailResults.addObserver(this);
|
emailResults.addObserver(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean createKeys(List<Long> list) {
|
protected Node createNodeForKey(BlackboardArtifact art) {
|
||||||
list.addAll(emailResults.getArtifactIds(accountName, folderName));
|
return new BlackboardArtifactNode(art);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Node createNodeForKey(Long artifactId) {
|
|
||||||
if (skCase == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
BlackboardArtifact artifact = skCase.getBlackboardArtifact(artifactId);
|
|
||||||
return new BlackboardArtifactNode(artifact);
|
|
||||||
} catch (TskCoreException ex) {
|
|
||||||
logger.log(Level.WARNING, "Error creating mail messages nodes", ex); //NON-NLS
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void update(Observable o, Object arg) {
|
public void update(Observable o, Object arg) {
|
||||||
refresh(true);
|
refresh(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<BlackboardArtifact> makeKeys() {
|
||||||
|
List<BlackboardArtifact> keys = new ArrayList<>();
|
||||||
|
|
||||||
|
if (skCase != null) {
|
||||||
|
emailResults.getArtifactIds(accountName, folderName).forEach((id) -> {
|
||||||
|
try {
|
||||||
|
keys.add(skCase.getBlackboardArtifact(id));
|
||||||
|
} catch (TskCoreException ex) {
|
||||||
|
logger.log(Level.WARNING, "Error getting mail messages keys", ex); //NON-NLS
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onAdd() {
|
||||||
|
// No-op
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onRemove() {
|
||||||
|
// No-op
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -423,12 +423,12 @@ public class ExtractedContent implements AutopsyVisitableItem {
|
|||||||
/**
|
/**
|
||||||
* Creates children for a given artifact type
|
* Creates children for a given artifact type
|
||||||
*/
|
*/
|
||||||
private class ArtifactFactory extends ChildFactory.Detachable<BlackboardArtifact> {
|
private class ArtifactFactory extends BaseChildFactory<BlackboardArtifact> {
|
||||||
|
|
||||||
private BlackboardArtifact.Type type;
|
private BlackboardArtifact.Type type;
|
||||||
|
|
||||||
public ArtifactFactory(BlackboardArtifact.Type type) {
|
public ArtifactFactory(BlackboardArtifact.Type type) {
|
||||||
super();
|
super(type.getTypeName());
|
||||||
this.type = type;
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -481,36 +481,34 @@ public class ExtractedContent implements AutopsyVisitableItem {
|
|||||||
};
|
};
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void addNotify() {
|
protected void onAdd() {
|
||||||
IngestManager.getInstance().addIngestJobEventListener(pcl);
|
IngestManager.getInstance().addIngestJobEventListener(pcl);
|
||||||
IngestManager.getInstance().addIngestModuleEventListener(pcl);
|
IngestManager.getInstance().addIngestModuleEventListener(pcl);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void removeNotify() {
|
protected void onRemove() {
|
||||||
IngestManager.getInstance().removeIngestJobEventListener(pcl);
|
IngestManager.getInstance().removeIngestJobEventListener(pcl);
|
||||||
IngestManager.getInstance().removeIngestModuleEventListener(pcl);
|
IngestManager.getInstance().removeIngestModuleEventListener(pcl);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean createKeys(List<BlackboardArtifact> list) {
|
|
||||||
if (skCase != null) {
|
|
||||||
try {
|
|
||||||
List<BlackboardArtifact> arts =
|
|
||||||
Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true) ?
|
|
||||||
blackboard.getArtifacts(type.getTypeID(), datasourceObjId) :
|
|
||||||
skCase.getBlackboardArtifacts(type.getTypeID());
|
|
||||||
list.addAll(arts);
|
|
||||||
} catch (TskException ex) {
|
|
||||||
Logger.getLogger(ArtifactFactory.class.getName()).log(Level.SEVERE, "Couldn't get blackboard artifacts from database", ex); //NON-NLS
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Node createNodeForKey(BlackboardArtifact key) {
|
protected Node createNodeForKey(BlackboardArtifact key) {
|
||||||
return new BlackboardArtifactNode(key);
|
return new BlackboardArtifactNode(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<BlackboardArtifact> makeKeys() {
|
||||||
|
if (skCase != null) {
|
||||||
|
try {
|
||||||
|
return Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)
|
||||||
|
? blackboard.getArtifacts(type.getTypeID(), datasourceObjId)
|
||||||
|
: skCase.getBlackboardArtifacts(type.getTypeID());
|
||||||
|
} catch (TskException ex) {
|
||||||
|
Logger.getLogger(ArtifactFactory.class.getName()).log(Level.SEVERE, "Couldn't get blackboard artifacts from database", ex); //NON-NLS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2013-2018 Basis Technology Corp.
|
* Copyright 2013-2019 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -361,7 +361,7 @@ public class FileSize implements AutopsyVisitableItem {
|
|||||||
/*
|
/*
|
||||||
* Makes children, which are nodes for files of a given range
|
* Makes children, which are nodes for files of a given range
|
||||||
*/
|
*/
|
||||||
static class FileSizeChildren extends ChildFactory.Detachable<AbstractFile> {
|
static class FileSizeChildren extends BaseChildFactory<AbstractFile> {
|
||||||
|
|
||||||
private final SleuthkitCase skCase;
|
private final SleuthkitCase skCase;
|
||||||
private final FileSizeFilter filter;
|
private final FileSizeFilter filter;
|
||||||
@ -377,6 +377,7 @@ public class FileSize implements AutopsyVisitableItem {
|
|||||||
* added to case
|
* added to case
|
||||||
*/
|
*/
|
||||||
FileSizeChildren(FileSizeFilter filter, SleuthkitCase skCase, Observable o, long dsObjId) {
|
FileSizeChildren(FileSizeFilter filter, SleuthkitCase skCase, Observable o, long dsObjId) {
|
||||||
|
super(filter.getName(), new ViewsKnownAndSlackFilter<>());
|
||||||
this.skCase = skCase;
|
this.skCase = skCase;
|
||||||
this.filter = filter;
|
this.filter = filter;
|
||||||
this.notifier = o;
|
this.notifier = o;
|
||||||
@ -385,14 +386,14 @@ public class FileSize implements AutopsyVisitableItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void addNotify() {
|
protected void onAdd() {
|
||||||
if (notifier != null) {
|
if (notifier != null) {
|
||||||
notifier.addObserver(observer);
|
notifier.addObserver(observer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void removeNotify() {
|
protected void onRemove() {
|
||||||
if (notifier != null) {
|
if (notifier != null) {
|
||||||
notifier.deleteObserver(observer);
|
notifier.deleteObserver(observer);
|
||||||
}
|
}
|
||||||
@ -400,6 +401,11 @@ public class FileSize implements AutopsyVisitableItem {
|
|||||||
|
|
||||||
private final Observer observer = new FileSizeChildrenObserver();
|
private final Observer observer = new FileSizeChildrenObserver();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<AbstractFile> makeKeys() {
|
||||||
|
return runFsQuery();
|
||||||
|
}
|
||||||
|
|
||||||
// Cause refresh of children if there are changes
|
// Cause refresh of children if there are changes
|
||||||
private class FileSizeChildrenObserver implements Observer {
|
private class FileSizeChildrenObserver implements Observer {
|
||||||
|
|
||||||
@ -409,12 +415,6 @@ public class FileSize implements AutopsyVisitableItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean createKeys(List<AbstractFile> list) {
|
|
||||||
list.addAll(runFsQuery());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String makeQuery(FileSizeFilter filter, long filteringDSObjId) {
|
private static String makeQuery(FileSizeFilter filter, long filteringDSObjId) {
|
||||||
String query;
|
String query;
|
||||||
switch (filter) {
|
switch (filter) {
|
||||||
@ -436,17 +436,6 @@ public class FileSize implements AutopsyVisitableItem {
|
|||||||
// Ignore unallocated block files.
|
// Ignore unallocated block files.
|
||||||
query = query + " AND (type != " + TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS.getFileType() + ")"; //NON-NLS
|
query = query + " AND (type != " + TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS.getFileType() + ")"; //NON-NLS
|
||||||
|
|
||||||
// Hide known files if indicated in the user preferences.
|
|
||||||
if(UserPreferences.hideKnownFilesInViewsTree()) {
|
|
||||||
query += " AND (known != " + TskData.FileKnown.KNOWN.getFileKnownValue() //NON-NLS
|
|
||||||
+ " OR known IS NULL)"; //NON-NLS
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hide slack files if indicated in the user preferences.
|
|
||||||
if(UserPreferences.hideSlackFilesInViewsTree()) {
|
|
||||||
query += " AND (type != " + TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.getFileType() + ")"; //NON-NLS
|
|
||||||
}
|
|
||||||
|
|
||||||
// filter by datasource if indicated in case preferences
|
// filter by datasource if indicated in case preferences
|
||||||
if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) {
|
if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) {
|
||||||
query += " AND data_source_obj_id = " + filteringDSObjId;
|
query += " AND data_source_obj_id = " + filteringDSObjId;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2011-2018 Basis Technology Corp.
|
* Copyright 2011-2019 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.datamodel;
|
|||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@ -292,7 +293,7 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
|
|||||||
* should refresh
|
* should refresh
|
||||||
*/
|
*/
|
||||||
FileExtensionNode(FileTypesByExtension.SearchFilterInterface filter, SleuthkitCase skCase, FileTypesByExtObservable o) {
|
FileExtensionNode(FileTypesByExtension.SearchFilterInterface filter, SleuthkitCase skCase, FileTypesByExtObservable o) {
|
||||||
super(typesRoot, Children.create(new FileExtensionNodeChildren(filter, skCase, o), true),
|
super(typesRoot, Children.create(new FileExtensionNodeChildren(filter, skCase, o, filter.getDisplayName()), true),
|
||||||
Lookups.singleton(filter.getDisplayName()));
|
Lookups.singleton(filter.getDisplayName()));
|
||||||
this.filter = filter;
|
this.filter = filter;
|
||||||
super.setName(filter.getDisplayName());
|
super.setName(filter.getDisplayName());
|
||||||
@ -377,7 +378,7 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
|
|||||||
/**
|
/**
|
||||||
* Child node factory for a specific file type - does the database query.
|
* Child node factory for a specific file type - does the database query.
|
||||||
*/
|
*/
|
||||||
private class FileExtensionNodeChildren extends ChildFactory.Detachable<FileTypesKey> implements Observer {
|
private class FileExtensionNodeChildren extends BaseChildFactory<FileTypesKey> implements Observer {
|
||||||
|
|
||||||
private final SleuthkitCase skCase;
|
private final SleuthkitCase skCase;
|
||||||
private final FileTypesByExtension.SearchFilterInterface filter;
|
private final FileTypesByExtension.SearchFilterInterface filter;
|
||||||
@ -390,22 +391,22 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
|
|||||||
* @param o Observable that will notify when there could be new
|
* @param o Observable that will notify when there could be new
|
||||||
* data to display
|
* data to display
|
||||||
*/
|
*/
|
||||||
private FileExtensionNodeChildren(FileTypesByExtension.SearchFilterInterface filter, SleuthkitCase skCase, Observable o) {
|
private FileExtensionNodeChildren(FileTypesByExtension.SearchFilterInterface filter, SleuthkitCase skCase, Observable o, String nodeName) {
|
||||||
super();
|
super(nodeName, new ViewsKnownAndSlackFilter<>());
|
||||||
this.filter = filter;
|
this.filter = filter;
|
||||||
this.skCase = skCase;
|
this.skCase = skCase;
|
||||||
notifier = o;
|
notifier = o;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void addNotify() {
|
protected void onAdd() {
|
||||||
if (notifier != null) {
|
if (notifier != null) {
|
||||||
notifier.addObserver(this);
|
notifier.addObserver(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void removeNotify() {
|
protected void onRemove() {
|
||||||
if (notifier != null) {
|
if (notifier != null) {
|
||||||
notifier.deleteObserver(this);
|
notifier.deleteObserver(this);
|
||||||
}
|
}
|
||||||
@ -417,19 +418,19 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean createKeys(List<FileTypesKey> list) {
|
protected Node createNodeForKey(FileTypesKey key) {
|
||||||
try {
|
return key.accept(new FileTypes.FileNodeCreationVisitor());
|
||||||
list.addAll(skCase.findAllFilesWhere(createQuery(filter))
|
|
||||||
.stream().map(f -> new FileTypesKey(f)).collect(Collectors.toList()));
|
|
||||||
} catch (TskCoreException ex) {
|
|
||||||
logger.log(Level.SEVERE, "Couldn't get search results", ex); //NON-NLS
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Node createNodeForKey(FileTypesKey key) {
|
protected List<FileTypesKey> makeKeys() {
|
||||||
return key.accept(new FileTypes.FileNodeCreationVisitor());
|
try {
|
||||||
|
return skCase.findAllFilesWhere(createQuery(filter))
|
||||||
|
.stream().map(f -> new FileTypesKey(f)).collect(Collectors.toList());
|
||||||
|
} catch (TskCoreException ex) {
|
||||||
|
logger.log(Level.SEVERE, "Couldn't get search results", ex); //NON-NLS
|
||||||
|
}
|
||||||
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2011-2018 Basis Technology Corp.
|
* Copyright 2011-2019 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -445,27 +445,16 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
|
|||||||
* files that match MimeType which is represented by this position in the
|
* files that match MimeType which is represented by this position in the
|
||||||
* tree.
|
* tree.
|
||||||
*/
|
*/
|
||||||
private class MediaSubTypeNodeChildren extends ChildFactory.Detachable<FileTypesKey> implements Observer {
|
private class MediaSubTypeNodeChildren extends BaseChildFactory<FileTypesKey> implements Observer {
|
||||||
|
|
||||||
private final String mimeType;
|
private final String mimeType;
|
||||||
|
|
||||||
private MediaSubTypeNodeChildren(String mimeType) {
|
private MediaSubTypeNodeChildren(String mimeType) {
|
||||||
super();
|
super(mimeType, new ViewsKnownAndSlackFilter<>());
|
||||||
addObserver(this);
|
addObserver(this);
|
||||||
this.mimeType = mimeType;
|
this.mimeType = mimeType;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean createKeys(List<FileTypesKey> list) {
|
|
||||||
try {
|
|
||||||
list.addAll(skCase.findAllFilesWhere(createBaseWhereExpr() + " AND mime_type = '" + mimeType + "'")
|
|
||||||
.stream().map(f -> new FileTypesKey(f)).collect(Collectors.toList())); //NON-NLS
|
|
||||||
} catch (TskCoreException ex) {
|
|
||||||
logger.log(Level.SEVERE, "Couldn't get search results", ex); //NON-NLS
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void update(Observable o, Object arg) {
|
public void update(Observable o, Object arg) {
|
||||||
refresh(true);
|
refresh(true);
|
||||||
@ -475,5 +464,26 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
|
|||||||
protected Node createNodeForKey(FileTypesKey key) {
|
protected Node createNodeForKey(FileTypesKey key) {
|
||||||
return key.accept(new FileTypes.FileNodeCreationVisitor());
|
return key.accept(new FileTypes.FileNodeCreationVisitor());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<FileTypesKey> makeKeys() {
|
||||||
|
try {
|
||||||
|
return skCase.findAllFilesWhere(createBaseWhereExpr() + " AND mime_type = '" + mimeType + "'")
|
||||||
|
.stream().map(f -> new FileTypesKey(f)).collect(Collectors.toList()); //NON-NLS
|
||||||
|
} catch (TskCoreException ex) {
|
||||||
|
logger.log(Level.SEVERE, "Couldn't get search results", ex); //NON-NLS
|
||||||
|
}
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onAdd() {
|
||||||
|
// No-op
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onRemove() {
|
||||||
|
// No-op
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2011-2018 Basis Technology Corp.
|
* Copyright 2011-2019 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -44,7 +44,6 @@ import org.openide.util.lookup.Lookups;
|
|||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
import org.sleuthkit.autopsy.casemodule.CasePreferences;
|
import org.sleuthkit.autopsy.casemodule.CasePreferences;
|
||||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||||
import org.sleuthkit.autopsy.core.UserPreferences;
|
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.autopsy.ingest.IngestManager;
|
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||||
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
|
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
|
||||||
@ -378,33 +377,40 @@ public class HashsetHits implements AutopsyVisitableItem {
|
|||||||
/**
|
/**
|
||||||
* Creates the nodes for the hits in a given set.
|
* Creates the nodes for the hits in a given set.
|
||||||
*/
|
*/
|
||||||
private class HitFactory extends ChildFactory.Detachable<Long> implements Observer {
|
private class HitFactory extends BaseChildFactory<BlackboardArtifact> implements Observer {
|
||||||
|
|
||||||
private String hashsetName;
|
private String hashsetName;
|
||||||
private Map<Long, BlackboardArtifact> artifactHits = new HashMap<>();
|
private Map<Long, BlackboardArtifact> artifactHits = new HashMap<>();
|
||||||
|
|
||||||
private HitFactory(String hashsetName) {
|
private HitFactory(String hashsetName) {
|
||||||
super();
|
super(hashsetName);
|
||||||
this.hashsetName = hashsetName;
|
this.hashsetName = hashsetName;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void addNotify() {
|
protected void onAdd() {
|
||||||
hashsetResults.addObserver(this);
|
hashsetResults.addObserver(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void removeNotify() {
|
protected void onRemove() {
|
||||||
hashsetResults.deleteObserver(this);
|
hashsetResults.deleteObserver(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean createKeys(List<Long> list) {
|
protected Node createNodeForKey(BlackboardArtifact key) {
|
||||||
|
return new BlackboardArtifactNode(key);
|
||||||
if (skCase == null) {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(Observable o, Object arg) {
|
||||||
|
refresh(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<BlackboardArtifact> makeKeys() {
|
||||||
|
if (skCase != null) {
|
||||||
|
|
||||||
hashsetResults.getArtifactIds(hashsetName).forEach((id) -> {
|
hashsetResults.getArtifactIds(hashsetName).forEach((id) -> {
|
||||||
try {
|
try {
|
||||||
if (!artifactHits.containsKey(id)) {
|
if (!artifactHits.containsKey(id)) {
|
||||||
@ -415,23 +421,9 @@ public class HashsetHits implements AutopsyVisitableItem {
|
|||||||
logger.log(Level.SEVERE, "TSK Exception occurred", ex); //NON-NLS
|
logger.log(Level.SEVERE, "TSK Exception occurred", ex); //NON-NLS
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
return new ArrayList<>(artifactHits.values());
|
||||||
// Adding all keys at once is more efficient than adding one at a
|
|
||||||
// time because Netbeans triggers internal processing each time an
|
|
||||||
// element is added to the list.
|
|
||||||
list.addAll(artifactHits.keySet());
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
return Collections.emptyList();
|
||||||
@Override
|
|
||||||
protected Node createNodeForKey(Long id) {
|
|
||||||
BlackboardArtifact art = artifactHits.get(id);
|
|
||||||
return (null == art) ? null : new BlackboardArtifactNode(art);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void update(Observable o, Object arg) {
|
|
||||||
refresh(true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2011-2018 Basis Technology Corp.
|
* Copyright 2011-2019 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -28,7 +28,6 @@ import java.util.EnumSet;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import javax.swing.Action;
|
import javax.swing.Action;
|
||||||
import org.openide.nodes.Children;
|
|
||||||
import org.openide.nodes.Sheet;
|
import org.openide.nodes.Sheet;
|
||||||
import org.openide.util.NbBundle;
|
import org.openide.util.NbBundle;
|
||||||
import org.openide.util.NbBundle.Messages;
|
import org.openide.util.NbBundle.Messages;
|
||||||
@ -47,6 +46,7 @@ import org.sleuthkit.datamodel.Image;
|
|||||||
import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery;
|
import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
import org.sleuthkit.datamodel.VirtualDirectory;
|
import org.sleuthkit.datamodel.VirtualDirectory;
|
||||||
|
import org.sleuthkit.autopsy.datamodel.BaseChildFactory.NoSuchEventBusException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class is used to represent the "Node" for the image. The children of
|
* This class is used to represent the "Node" for the image. The children of
|
||||||
@ -224,18 +224,23 @@ public class ImageNode extends AbstractContentNode<Image> {
|
|||||||
if (parent != null) {
|
if (parent != null) {
|
||||||
// Is this a new carved file?
|
// Is this a new carved file?
|
||||||
if (parent.getName().equals(VirtualDirectory.NAME_CARVED)) {
|
if (parent.getName().equals(VirtualDirectory.NAME_CARVED)) {
|
||||||
// Was this new carved file produced from this image?
|
// Is this new carved file for this data source?
|
||||||
if (parent.getParent().getId() == getContent().getId()) {
|
if (newContent.getDataSource().getId() == getContent().getDataSource().getId()) {
|
||||||
Children children = getChildren();
|
// Find the image (if any) associated with the new content and
|
||||||
if (children != null) {
|
// trigger a refresh if it matches the image wrapped by this node.
|
||||||
((ContentChildren) children).refreshChildren();
|
while ((parent = parent.getParent()) != null) {
|
||||||
children.getNodesCount();
|
if (parent.getId() == getContent().getId()) {
|
||||||
|
BaseChildFactory.post(getName(), new BaseChildFactory.RefreshKeysEvent());
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (TskCoreException ex) {
|
} catch (TskCoreException ex) {
|
||||||
// Do nothing.
|
// Do nothing.
|
||||||
|
} catch (NoSuchEventBusException ex) {
|
||||||
|
logger.log(Level.WARNING, "Failed to post key refresh event.", ex); // NON-NLS
|
||||||
}
|
}
|
||||||
} else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
|
} else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
|
||||||
if (evt.getNewValue() == null) {
|
if (evt.getNewValue() == null) {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2011-2018 Basis Technology Corp.
|
* Copyright 2011-2019 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -449,26 +449,23 @@ public class InterestingHits implements AutopsyVisitableItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class HitFactory extends ChildFactory<Long> implements Observer {
|
private class HitFactory extends BaseChildFactory<BlackboardArtifact> implements Observer {
|
||||||
|
|
||||||
private final String setName;
|
private final String setName;
|
||||||
private final String typeName;
|
private final String typeName;
|
||||||
private final Map<Long, BlackboardArtifact> artifactHits = new HashMap<>();
|
private final Map<Long, BlackboardArtifact> artifactHits = new HashMap<>();
|
||||||
|
|
||||||
private HitFactory(String setName, String typeName) {
|
private HitFactory(String setName, String typeName) {
|
||||||
super();
|
super(typeName);
|
||||||
this.setName = setName;
|
this.setName = setName;
|
||||||
this.typeName = typeName;
|
this.typeName = typeName;
|
||||||
interestingResults.addObserver(this);
|
interestingResults.addObserver(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean createKeys(List<Long> list) {
|
protected List<BlackboardArtifact> makeKeys() {
|
||||||
|
|
||||||
if (skCase == null) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (skCase != null) {
|
||||||
interestingResults.getArtifactIds(setName, typeName).forEach((id) -> {
|
interestingResults.getArtifactIds(setName, typeName).forEach((id) -> {
|
||||||
try {
|
try {
|
||||||
if (!artifactHits.containsKey(id)) {
|
if (!artifactHits.containsKey(id)) {
|
||||||
@ -480,20 +477,29 @@ public class InterestingHits implements AutopsyVisitableItem {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
list.addAll(artifactHits.keySet());
|
return new ArrayList<>(artifactHits.values());
|
||||||
|
}
|
||||||
return true;
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Node createNodeForKey(Long l) {
|
protected Node createNodeForKey(BlackboardArtifact art) {
|
||||||
BlackboardArtifact art = artifactHits.get(l);
|
return new BlackboardArtifactNode(art);
|
||||||
return (null == art) ? null : new BlackboardArtifactNode(art);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void update(Observable o, Object arg) {
|
public void update(Observable o, Object arg) {
|
||||||
refresh(true);
|
refresh(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onAdd() {
|
||||||
|
// No-op
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onRemove() {
|
||||||
|
// No-op
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2011-2018 Basis Technology Corp.
|
* Copyright 2011-2019 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -25,6 +25,7 @@ import java.sql.SQLException;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -34,7 +35,6 @@ import java.util.Observable;
|
|||||||
import java.util.Observer;
|
import java.util.Observer;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.openide.nodes.ChildFactory;
|
import org.openide.nodes.ChildFactory;
|
||||||
import org.openide.nodes.Children;
|
import org.openide.nodes.Children;
|
||||||
@ -46,7 +46,6 @@ import org.openide.util.lookup.Lookups;
|
|||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
import org.sleuthkit.autopsy.casemodule.CasePreferences;
|
import org.sleuthkit.autopsy.casemodule.CasePreferences;
|
||||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||||
import org.sleuthkit.autopsy.core.UserPreferences;
|
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import static org.sleuthkit.autopsy.datamodel.Bundle.*;
|
import static org.sleuthkit.autopsy.datamodel.Bundle.*;
|
||||||
import org.sleuthkit.autopsy.ingest.IngestManager;
|
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||||
@ -85,7 +84,6 @@ public class KeywordHits implements AutopsyVisitableItem {
|
|||||||
*/
|
*/
|
||||||
private static final String DEFAULT_INSTANCE_NAME = "DEFAULT_INSTANCE_NAME";
|
private static final String DEFAULT_INSTANCE_NAME = "DEFAULT_INSTANCE_NAME";
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* query attributes table for the ones that we need for the tree
|
* query attributes table for the ones that we need for the tree
|
||||||
*/
|
*/
|
||||||
@ -510,9 +508,11 @@ public class KeywordHits implements AutopsyVisitableItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private abstract class KWHitsNodeBase extends DisplayableItemNode implements Observer {
|
private abstract class KWHitsNodeBase extends DisplayableItemNode implements Observer {
|
||||||
|
private String displayName;
|
||||||
|
|
||||||
private KWHitsNodeBase(Children children, Lookup lookup) {
|
private KWHitsNodeBase(Children children, Lookup lookup, String displayName) {
|
||||||
super(children, lookup);
|
super(children, lookup);
|
||||||
|
this.displayName = displayName;
|
||||||
}
|
}
|
||||||
|
|
||||||
private KWHitsNodeBase(Children children) {
|
private KWHitsNodeBase(Children children) {
|
||||||
@ -530,7 +530,7 @@ public class KeywordHits implements AutopsyVisitableItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final void updateDisplayName() {
|
final void updateDisplayName() {
|
||||||
super.setDisplayName(getName() + " (" + countTotalDescendants() + ")");
|
super.setDisplayName(displayName + " (" + countTotalDescendants() + ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract int countTotalDescendants();
|
abstract int countTotalDescendants();
|
||||||
@ -545,7 +545,7 @@ public class KeywordHits implements AutopsyVisitableItem {
|
|||||||
private final String listName;
|
private final String listName;
|
||||||
|
|
||||||
private ListNode(String listName) {
|
private ListNode(String listName) {
|
||||||
super(Children.create(new TermFactory(listName), true), Lookups.singleton(listName));
|
super(Children.create(new TermFactory(listName), true), Lookups.singleton(listName), listName);
|
||||||
super.setName(listName);
|
super.setName(listName);
|
||||||
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/keyword_hits.png"); //NON-NLS
|
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/keyword_hits.png"); //NON-NLS
|
||||||
this.listName = listName;
|
this.listName = listName;
|
||||||
@ -631,6 +631,24 @@ public class KeywordHits implements AutopsyVisitableItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a ChildFactory object for the given set name and keyword.
|
||||||
|
*
|
||||||
|
* The type of ChildFactory we create is based on whether the node
|
||||||
|
* represents a regular expression keyword search or not. For regular
|
||||||
|
* expression keyword searches there will be an extra layer in the tree that
|
||||||
|
* represents each of the individual terms found by the regular expression.
|
||||||
|
* E.g., for an email regular expression search there will be a node in the
|
||||||
|
* tree for every email address hit.
|
||||||
|
*/
|
||||||
|
ChildFactory<?> createChildFactory(String setName, String keyword) {
|
||||||
|
if (isOnlyDefaultInstance(keywordResults.getKeywordInstances(setName, keyword))) {
|
||||||
|
return new HitsFactory(setName, keyword, DEFAULT_INSTANCE_NAME);
|
||||||
|
} else {
|
||||||
|
return new RegExpInstancesFactory(setName, keyword);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the search term or regexp that user searched for
|
* Represents the search term or regexp that user searched for
|
||||||
*/
|
*/
|
||||||
@ -640,8 +658,16 @@ public class KeywordHits implements AutopsyVisitableItem {
|
|||||||
private final String keyword;
|
private final String keyword;
|
||||||
|
|
||||||
private TermNode(String setName, String keyword) {
|
private TermNode(String setName, String keyword) {
|
||||||
super(Children.create(new RegExpInstancesFactory(setName, keyword), true), Lookups.singleton(keyword));
|
super(Children.create(createChildFactory(setName, keyword), true), Lookups.singleton(keyword), keyword);
|
||||||
super.setName(keyword);
|
|
||||||
|
/**
|
||||||
|
* We differentiate between the programmatic name and the display
|
||||||
|
* name. The programmatic name is used to create an association with
|
||||||
|
* an event bus and must be the same as the node name passed by our
|
||||||
|
* ChildFactory to it's parent constructor. See the HitsFactory
|
||||||
|
* constructor for an example.
|
||||||
|
*/
|
||||||
|
super.setName(setName + "_" + keyword);
|
||||||
this.setName = setName;
|
this.setName = setName;
|
||||||
this.keyword = keyword;
|
this.keyword = keyword;
|
||||||
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/keyword_hits.png"); //NON-NLS
|
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/keyword_hits.png"); //NON-NLS
|
||||||
@ -694,45 +720,11 @@ public class KeywordHits implements AutopsyVisitableItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Allows us to pass in either longs or strings as they keys for different
|
|
||||||
* types of nodes at the same level. Probably a better way to do this, but
|
|
||||||
* it works.
|
|
||||||
*/
|
|
||||||
private class RegExpInstanceKey {
|
|
||||||
|
|
||||||
private final boolean isRegExp;
|
|
||||||
private String strKey;
|
|
||||||
private Long longKey;
|
|
||||||
|
|
||||||
RegExpInstanceKey(String key) {
|
|
||||||
isRegExp = true;
|
|
||||||
strKey = key;
|
|
||||||
}
|
|
||||||
|
|
||||||
RegExpInstanceKey(Long key) {
|
|
||||||
isRegExp = false;
|
|
||||||
longKey = key;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isRegExp() {
|
|
||||||
return isRegExp;
|
|
||||||
}
|
|
||||||
|
|
||||||
Long getIdKey() {
|
|
||||||
return longKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
String getRegExpKey() {
|
|
||||||
return strKey;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the nodes for a given regexp that represent the specific terms
|
* Creates the nodes for a given regexp that represent the specific terms
|
||||||
* that were found
|
* that were found
|
||||||
*/
|
*/
|
||||||
private class RegExpInstancesFactory extends DetachableObserverChildFactory<RegExpInstanceKey> {
|
private class RegExpInstancesFactory extends DetachableObserverChildFactory<String> {
|
||||||
|
|
||||||
private final String keyword;
|
private final String keyword;
|
||||||
private final String setName;
|
private final String setName;
|
||||||
@ -744,35 +736,17 @@ public class KeywordHits implements AutopsyVisitableItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean createKeys(List<RegExpInstanceKey> list) {
|
protected boolean createKeys(List<String> list) {
|
||||||
List<String> instances = keywordResults.getKeywordInstances(setName, keyword);
|
list.addAll(keywordResults.getKeywordInstances(setName, keyword));
|
||||||
// The keys are different depending on what we are displaying.
|
|
||||||
// regexp get another layer to show instances.
|
|
||||||
// Exact/substring matches don't.
|
|
||||||
if (isOnlyDefaultInstance(instances)) {
|
|
||||||
list.addAll(keywordResults.getArtifactIds(setName, keyword, DEFAULT_INSTANCE_NAME).stream()
|
|
||||||
.map(RegExpInstanceKey::new)
|
|
||||||
.collect(Collectors.toList()));
|
|
||||||
} else {
|
|
||||||
list.addAll(instances.stream()
|
|
||||||
.map(RegExpInstanceKey::new)
|
|
||||||
.collect(Collectors.toList()));
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Node createNodeForKey(RegExpInstanceKey key) {
|
protected Node createNodeForKey(String key) {
|
||||||
if (key.isRegExp()) {
|
return new RegExpInstanceNode(setName, keyword, key);
|
||||||
return new RegExpInstanceNode(setName, keyword, key.getRegExpKey());
|
|
||||||
} else {
|
|
||||||
// if it isn't a regexp, then skip the 'instance' layer of the tree
|
|
||||||
return createBlackboardArtifactNode(key.getIdKey());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a specific term that was found from a regexp
|
* Represents a specific term that was found from a regexp
|
||||||
*/
|
*/
|
||||||
@ -783,8 +757,16 @@ public class KeywordHits implements AutopsyVisitableItem {
|
|||||||
private final String instance;
|
private final String instance;
|
||||||
|
|
||||||
private RegExpInstanceNode(String setName, String keyword, String instance) {
|
private RegExpInstanceNode(String setName, String keyword, String instance) {
|
||||||
super(Children.create(new HitsFactory(setName, keyword, instance), true), Lookups.singleton(instance));
|
super(Children.create(new HitsFactory(setName, keyword, instance), true), Lookups.singleton(instance), instance);
|
||||||
super.setName(instance); //the instance represents the name of the keyword hit at this point as the keyword is the regex
|
|
||||||
|
/**
|
||||||
|
* We differentiate between the programmatic name and the display
|
||||||
|
* name. The programmatic name is used to create an association with
|
||||||
|
* an event bus and must be the same as the node name passed by our
|
||||||
|
* ChildFactory to it's parent constructor. See the HitsFactory
|
||||||
|
* constructor for an example.
|
||||||
|
*/
|
||||||
|
super.setName(setName + "_" + keyword + "_" + instance);
|
||||||
this.setName = setName;
|
this.setName = setName;
|
||||||
this.keyword = keyword;
|
this.keyword = keyword;
|
||||||
this.instance = instance;
|
this.instance = instance;
|
||||||
@ -837,7 +819,7 @@ public class KeywordHits implements AutopsyVisitableItem {
|
|||||||
/**
|
/**
|
||||||
* Create a blackboard node for the given Keyword Hit artifact
|
* Create a blackboard node for the given Keyword Hit artifact
|
||||||
*
|
*
|
||||||
* @param artifactId
|
* @param art
|
||||||
*
|
*
|
||||||
* @return Node or null on error
|
* @return Node or null on error
|
||||||
*/
|
*/
|
||||||
@ -850,14 +832,13 @@ public class KeywordHits implements AutopsyVisitableItem {
|
|||||||
"KeywordHits.createNodeForKey.chgTime.name=ChangeTime",
|
"KeywordHits.createNodeForKey.chgTime.name=ChangeTime",
|
||||||
"KeywordHits.createNodeForKey.chgTime.displayName=Change Time",
|
"KeywordHits.createNodeForKey.chgTime.displayName=Change Time",
|
||||||
"KeywordHits.createNodeForKey.chgTime.desc=Change Time"})
|
"KeywordHits.createNodeForKey.chgTime.desc=Change Time"})
|
||||||
private BlackboardArtifactNode createBlackboardArtifactNode(Long artifactId) {
|
private BlackboardArtifactNode createBlackboardArtifactNode(BlackboardArtifact art) {
|
||||||
if (skCase == null) {
|
if (skCase == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
BlackboardArtifactNode n = new BlackboardArtifactNode(art); //NON-NLS
|
||||||
BlackboardArtifact art = skCase.getBlackboardArtifact(artifactId);
|
|
||||||
BlackboardArtifactNode n = new BlackboardArtifactNode(art);
|
|
||||||
// The associated file should be available through the Lookup that
|
// The associated file should be available through the Lookup that
|
||||||
// gets created when the BlackboardArtifactNode is constructed.
|
// gets created when the BlackboardArtifactNode is constructed.
|
||||||
AbstractFile file = n.getLookup().lookup(AbstractFile.class);
|
AbstractFile file = n.getLookup().lookup(AbstractFile.class);
|
||||||
@ -870,14 +851,13 @@ public class KeywordHits implements AutopsyVisitableItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* It is possible to get a keyword hit on artifacts generated for
|
* It is possible to get a keyword hit on artifacts generated for the
|
||||||
* the underlying image in which case MAC times are not
|
* underlying image in which case MAC times are not
|
||||||
* available/applicable/useful.
|
* available/applicable/useful.
|
||||||
*/
|
*/
|
||||||
if (file == null) {
|
if (file == null) {
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
n.addNodeProperty(new NodeProperty<>(
|
n.addNodeProperty(new NodeProperty<>(
|
||||||
KeywordHits_createNodeForKey_modTime_name(),
|
KeywordHits_createNodeForKey_modTime_name(),
|
||||||
KeywordHits_createNodeForKey_modTime_displayName(),
|
KeywordHits_createNodeForKey_modTime_displayName(),
|
||||||
@ -894,37 +874,68 @@ public class KeywordHits implements AutopsyVisitableItem {
|
|||||||
KeywordHits_createNodeForKey_chgTime_desc(),
|
KeywordHits_createNodeForKey_chgTime_desc(),
|
||||||
ContentUtils.getStringTime(file.getCtime(), file)));
|
ContentUtils.getStringTime(file.getCtime(), file)));
|
||||||
return n;
|
return n;
|
||||||
} catch (TskCoreException ex) {
|
|
||||||
logger.log(Level.WARNING, "TSK Exception occurred", ex); //NON-NLS
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates nodes for individual files that had hits
|
* Creates nodes for individual files that had hits
|
||||||
*/
|
*/
|
||||||
private class HitsFactory extends DetachableObserverChildFactory<Long> {
|
private class HitsFactory extends BaseChildFactory<BlackboardArtifact> implements Observer {
|
||||||
|
|
||||||
private final String keyword;
|
private final String keyword;
|
||||||
private final String setName;
|
private final String setName;
|
||||||
private final String instance;
|
private final String instance;
|
||||||
|
private final Map<Long, BlackboardArtifact> artifactHits = new HashMap<>();
|
||||||
|
|
||||||
private HitsFactory(String setName, String keyword, String instance) {
|
private HitsFactory(String setName, String keyword, String instance) {
|
||||||
super();
|
/**
|
||||||
|
* The node name passed to the parent constructor will consist of
|
||||||
|
* the set name, keyword and optionally the instance name (in the
|
||||||
|
* case of regular expression hits. This name must match the name
|
||||||
|
* set in the TermNode or RegExpInstanceNode constructors.
|
||||||
|
*/
|
||||||
|
super(setName + "_" + keyword + (DEFAULT_INSTANCE_NAME.equals(instance) ? "" : "_" + instance));
|
||||||
this.setName = setName;
|
this.setName = setName;
|
||||||
this.keyword = keyword;
|
this.keyword = keyword;
|
||||||
this.instance = instance;
|
this.instance = instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean createKeys(List<Long> list) {
|
protected List<BlackboardArtifact> makeKeys() {
|
||||||
list.addAll(keywordResults.getArtifactIds(setName, keyword, instance));
|
if (skCase != null) {
|
||||||
return true;
|
keywordResults.getArtifactIds(setName, keyword, instance).forEach((id) -> {
|
||||||
|
try {
|
||||||
|
if (!artifactHits.containsKey(id)) {
|
||||||
|
BlackboardArtifact art = skCase.getBlackboardArtifact(id);
|
||||||
|
artifactHits.put(id, art);
|
||||||
|
}
|
||||||
|
} catch (TskCoreException ex) {
|
||||||
|
logger.log(Level.SEVERE, "TSK Exception occurred", ex); //NON-NLS
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return new ArrayList<>(artifactHits.values());
|
||||||
|
}
|
||||||
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Node createNodeForKey(Long artifactId) {
|
protected Node createNodeForKey(BlackboardArtifact art) {
|
||||||
return createBlackboardArtifactNode(artifactId);
|
return createBlackboardArtifactNode(art);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onAdd() {
|
||||||
|
keywordResults.addObserver(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onRemove() {
|
||||||
|
keywordResults.deleteObserver(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(Observable o, Object arg) {
|
||||||
|
refresh(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* 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.datamodel;
|
||||||
|
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import org.sleuthkit.datamodel.AbstractFile;
|
||||||
|
import org.sleuthkit.datamodel.Content;
|
||||||
|
import org.sleuthkit.datamodel.TskData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Predicate that can be used to filter known and/or slack files from
|
||||||
|
* Content collections based on user preferences.
|
||||||
|
*/
|
||||||
|
abstract class KnownAndSlackFilterBase<T extends Content> implements Predicate<T> {
|
||||||
|
protected static boolean filterKnown;
|
||||||
|
protected static boolean filterSlack;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean test(T t) {
|
||||||
|
AbstractFile af = null;
|
||||||
|
|
||||||
|
if (t instanceof AbstractFile) {
|
||||||
|
af = (AbstractFile) t;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (af != null) {
|
||||||
|
if (af.getKnown() == TskData.FileKnown.KNOWN && filterKnown) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (af.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK) && filterSlack) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2011-2017 Basis Technology Corp.
|
* Copyright 2011-2019 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -20,14 +20,20 @@ package org.sleuthkit.autopsy.datamodel;
|
|||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import org.openide.nodes.AbstractNode;
|
||||||
|
import org.openide.nodes.Children;
|
||||||
|
import org.openide.nodes.Node;
|
||||||
|
import org.openide.util.NbBundle;
|
||||||
|
import org.sleuthkit.autopsy.datamodel.accounts.Accounts;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Children implementation for the root node of a ContentNode tree. Accepts a
|
* Children implementation for the root node of a ContentNode tree. Accepts a
|
||||||
* list of root Content objects for the tree.
|
* list of root Content objects for the tree.
|
||||||
*/
|
*/
|
||||||
public class RootContentChildren extends AbstractContentChildren<Object> {
|
public class RootContentChildren extends Children.Keys<Object> {
|
||||||
|
|
||||||
private final Collection<? extends Object> contentKeys;
|
private final Collection<? extends Object> contentKeys;
|
||||||
|
private final CreateAutopsyNodeVisitor createAutopsyNodeVisitor = new CreateAutopsyNodeVisitor();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param contentKeys root Content objects for the Node tree
|
* @param contentKeys root Content objects for the Node tree
|
||||||
@ -56,4 +62,120 @@ public class RootContentChildren extends AbstractContentChildren<Object> {
|
|||||||
public void refreshContentKeys() {
|
public void refreshContentKeys() {
|
||||||
contentKeys.forEach(this::refreshKey);
|
contentKeys.forEach(this::refreshKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Node[] createNodes(Object key) {
|
||||||
|
if (key instanceof AutopsyVisitableItem) {
|
||||||
|
return new Node[] {((AutopsyVisitableItem)key).accept(createAutopsyNodeVisitor)};
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a DisplayableItemNode for use as a subtree root node for the Autopsy
|
||||||
|
* tree view from each type of AutopsyVisitableItem visited. There are
|
||||||
|
* AutopsyVisitableItems for the Data Sources, Views, Results, and Reports
|
||||||
|
* subtrees, and for the subtrees of Results (e.g., Extracted Content, Hash
|
||||||
|
* Set Hits, etc.).
|
||||||
|
*/
|
||||||
|
static class CreateAutopsyNodeVisitor extends AutopsyItemVisitor.Default<AbstractNode> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExtractedContent.RootNode visit(ExtractedContent ec) {
|
||||||
|
return ec.new RootNode(ec.getSleuthkitCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractNode visit(FileTypesByExtension sf) {
|
||||||
|
return sf.new FileTypesByExtNode(sf.getSleuthkitCase(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractNode visit(RecentFiles rf) {
|
||||||
|
return new RecentFilesNode(rf.getSleuthkitCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractNode visit(DeletedContent dc) {
|
||||||
|
return new DeletedContent.DeletedContentsNode(dc.getSleuthkitCase(), dc.filteringDataSourceObjId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractNode visit(FileSize dc) {
|
||||||
|
return new FileSize.FileSizeRootNode(dc.getSleuthkitCase(), dc.filteringDataSourceObjId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractNode visit(KeywordHits kh) {
|
||||||
|
return kh.new RootNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractNode visit(HashsetHits hh) {
|
||||||
|
return hh.new RootNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractNode visit(InterestingHits ih) {
|
||||||
|
return ih.new RootNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractNode visit(EmailExtracted ee) {
|
||||||
|
return ee.new RootNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractNode visit(Tags tagsNodeKey) {
|
||||||
|
return tagsNodeKey.new RootNode(tagsNodeKey.filteringDataSourceObjId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractNode visit(DataSources i) {
|
||||||
|
return new DataSourcesNode(i.filteringDataSourceObjId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractNode visit(DataSourceGrouping datasourceGrouping) {
|
||||||
|
return new DataSourceGroupingNode(datasourceGrouping.getDataSource());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractNode visit(Views v) {
|
||||||
|
return new ViewsNode(v.getSleuthkitCase(), v.filteringDataSourceObjId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractNode visit(Results results) {
|
||||||
|
return new ResultsNode(results.getSleuthkitCase(), results.filteringDataSourceObjId() );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractNode visit(FileTypes ft) {
|
||||||
|
return ft.new FileTypesNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractNode visit(Reports reportsItem) {
|
||||||
|
return new Reports.ReportsListNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractNode visit(Accounts accountsItem) {
|
||||||
|
return accountsItem.new AccountsRootNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected AbstractNode defaultVisit(AutopsyVisitableItem di) {
|
||||||
|
throw new UnsupportedOperationException(
|
||||||
|
NbBundle.getMessage(this.getClass(),
|
||||||
|
"AbstractContentChildren.createAutopsyNodeVisitor.exception.noNodeMsg"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractNode visit(FileTypesByMimeType ftByMimeTypeItem) {
|
||||||
|
return ftByMimeTypeItem.new ByMimeTypeNode();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* 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.datamodel;
|
||||||
|
|
||||||
|
import java.util.prefs.PreferenceChangeEvent;
|
||||||
|
import org.sleuthkit.autopsy.core.UserPreferences;
|
||||||
|
import static org.sleuthkit.autopsy.datamodel.KnownAndSlackFilterBase.filterKnown;
|
||||||
|
import static org.sleuthkit.autopsy.datamodel.KnownAndSlackFilterBase.filterSlack;
|
||||||
|
import org.sleuthkit.datamodel.Content;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Known and Slack filter for Views section of the tree.
|
||||||
|
*
|
||||||
|
* @param <T>
|
||||||
|
*/
|
||||||
|
class ViewsKnownAndSlackFilter<T extends Content> extends KnownAndSlackFilterBase<T> {
|
||||||
|
|
||||||
|
static {
|
||||||
|
/**
|
||||||
|
* Watch for user preference changes and update variables inherited from
|
||||||
|
* our parent. The actual filtering is provided by our parent class.
|
||||||
|
*/
|
||||||
|
UserPreferences.addChangeListener((PreferenceChangeEvent evt) -> {
|
||||||
|
if (evt.getKey().equals(UserPreferences.HIDE_KNOWN_FILES_IN_VIEWS_TREE)) {
|
||||||
|
filterKnown = UserPreferences.hideKnownFilesInViewsTree();
|
||||||
|
} else if (evt.getKey().equals(UserPreferences.HIDE_SLACK_FILES_IN_VIEWS_TREE)) {
|
||||||
|
filterSlack = UserPreferences.hideSlackFilesInViewsTree();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ViewsKnownAndSlackFilter() {
|
||||||
|
filterKnown = UserPreferences.hideKnownFilesInViewsTree();
|
||||||
|
filterSlack = UserPreferences.hideSlackFilesInViewsTree();
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2011-2014 Basis Technology Corp.
|
* Copyright 2011-2019 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -23,11 +23,13 @@ import java.beans.PropertyChangeListener;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.logging.Level;
|
||||||
import javax.swing.Action;
|
import javax.swing.Action;
|
||||||
import org.openide.nodes.Children;
|
|
||||||
import org.openide.nodes.Sheet;
|
import org.openide.nodes.Sheet;
|
||||||
import org.openide.util.NbBundle;
|
import org.openide.util.NbBundle;
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
|
import org.sleuthkit.autopsy.datamodel.BaseChildFactory.NoSuchEventBusException;
|
||||||
import org.sleuthkit.autopsy.directorytree.ExplorerNodeActionVisitor;
|
import org.sleuthkit.autopsy.directorytree.ExplorerNodeActionVisitor;
|
||||||
import org.sleuthkit.autopsy.directorytree.NewWindowViewAction;
|
import org.sleuthkit.autopsy.directorytree.NewWindowViewAction;
|
||||||
import org.sleuthkit.autopsy.ingest.IngestManager;
|
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||||
@ -43,6 +45,7 @@ import org.sleuthkit.autopsy.directorytree.FileSystemDetailsAction;
|
|||||||
* root directory of a file system
|
* root directory of a file system
|
||||||
*/
|
*/
|
||||||
public class VolumeNode extends AbstractContentNode<Volume> {
|
public class VolumeNode extends AbstractContentNode<Volume> {
|
||||||
|
private static final Logger logger = Logger.getLogger(VolumeNode.class.getName());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper so that the display name and the name used in building the path
|
* Helper so that the display name and the name used in building the path
|
||||||
@ -105,18 +108,23 @@ public class VolumeNode extends AbstractContentNode<Volume> {
|
|||||||
if (parent != null) {
|
if (parent != null) {
|
||||||
// Is this a new carved file?
|
// Is this a new carved file?
|
||||||
if (parent.getName().equals(VirtualDirectory.NAME_CARVED)) {
|
if (parent.getName().equals(VirtualDirectory.NAME_CARVED)) {
|
||||||
// Was this new carved file produced from this volume?
|
// Is this new carved file for this data source?
|
||||||
if (parent.getParent().getId() == getContent().getId()) {
|
if (newContent.getDataSource().getId() == getContent().getDataSource().getId()) {
|
||||||
Children children = getChildren();
|
// Find the volume (if any) associated with the new content and
|
||||||
if (children != null) {
|
// trigger a refresh if it matches the volume wrapped by this node.
|
||||||
((ContentChildren) children).refreshChildren();
|
while ((parent = parent.getParent()) != null) {
|
||||||
children.getNodesCount();
|
if (parent.getId() == getContent().getId()) {
|
||||||
|
BaseChildFactory.post(getName(), new BaseChildFactory.RefreshKeysEvent());
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (TskCoreException ex) {
|
} catch (TskCoreException ex) {
|
||||||
// Do nothing.
|
// Do nothing.
|
||||||
|
} catch (NoSuchEventBusException ex) {
|
||||||
|
logger.log(Level.WARNING, eventType, ex);
|
||||||
}
|
}
|
||||||
} else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
|
} else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
|
||||||
if (evt.getNewValue() == null) {
|
if (evt.getNewValue() == null) {
|
||||||
|
@ -26,8 +26,6 @@ import java.util.Collection;
|
|||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.prefs.PreferenceChangeEvent;
|
|
||||||
import java.util.prefs.PreferenceChangeListener;
|
|
||||||
import javax.swing.AbstractAction;
|
import javax.swing.AbstractAction;
|
||||||
import javax.swing.Action;
|
import javax.swing.Action;
|
||||||
import org.openide.explorer.ExplorerManager;
|
import org.openide.explorer.ExplorerManager;
|
||||||
@ -41,7 +39,6 @@ import org.sleuthkit.autopsy.actions.AddBlackboardArtifactTagAction;
|
|||||||
import org.sleuthkit.autopsy.actions.AddContentTagAction;
|
import org.sleuthkit.autopsy.actions.AddContentTagAction;
|
||||||
import org.sleuthkit.autopsy.actions.DeleteFileBlackboardArtifactTagAction;
|
import org.sleuthkit.autopsy.actions.DeleteFileBlackboardArtifactTagAction;
|
||||||
import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction;
|
import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction;
|
||||||
import org.sleuthkit.autopsy.core.UserPreferences;
|
|
||||||
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
|
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode.AbstractFilePropertyType;
|
import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode.AbstractFilePropertyType;
|
||||||
@ -80,7 +77,6 @@ import org.sleuthkit.datamodel.LayoutFile;
|
|||||||
import org.sleuthkit.datamodel.LocalFile;
|
import org.sleuthkit.datamodel.LocalFile;
|
||||||
import org.sleuthkit.datamodel.LocalDirectory;
|
import org.sleuthkit.datamodel.LocalDirectory;
|
||||||
import org.sleuthkit.datamodel.SlackFile;
|
import org.sleuthkit.datamodel.SlackFile;
|
||||||
import org.sleuthkit.datamodel.TskData;
|
|
||||||
import org.sleuthkit.datamodel.TskException;
|
import org.sleuthkit.datamodel.TskException;
|
||||||
import org.sleuthkit.datamodel.VirtualDirectory;
|
import org.sleuthkit.datamodel.VirtualDirectory;
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
|
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
|
||||||
@ -96,33 +92,6 @@ public class DataResultFilterNode extends FilterNode {
|
|||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(DataResultFilterNode.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(DataResultFilterNode.class.getName());
|
||||||
|
|
||||||
private static boolean filterKnownFromDataSources = UserPreferences.hideKnownFilesInDataSourcesTree();
|
|
||||||
private static boolean filterKnownFromViews = UserPreferences.hideKnownFilesInViewsTree();
|
|
||||||
private static boolean filterSlackFromDataSources = UserPreferences.hideSlackFilesInDataSourcesTree();
|
|
||||||
private static boolean filterSlackFromViews = UserPreferences.hideSlackFilesInViewsTree();
|
|
||||||
|
|
||||||
static {
|
|
||||||
UserPreferences.addChangeListener(new PreferenceChangeListener() {
|
|
||||||
@Override
|
|
||||||
public void preferenceChange(PreferenceChangeEvent evt) {
|
|
||||||
switch (evt.getKey()) {
|
|
||||||
case UserPreferences.HIDE_KNOWN_FILES_IN_DATA_SRCS_TREE:
|
|
||||||
filterKnownFromDataSources = UserPreferences.hideKnownFilesInDataSourcesTree();
|
|
||||||
break;
|
|
||||||
case UserPreferences.HIDE_KNOWN_FILES_IN_VIEWS_TREE:
|
|
||||||
filterKnownFromViews = UserPreferences.hideKnownFilesInViewsTree();
|
|
||||||
break;
|
|
||||||
case UserPreferences.HIDE_SLACK_FILES_IN_DATA_SRCS_TREE:
|
|
||||||
filterSlackFromDataSources = UserPreferences.hideSlackFilesInDataSourcesTree();
|
|
||||||
break;
|
|
||||||
case UserPreferences.HIDE_SLACK_FILES_IN_VIEWS_TREE:
|
|
||||||
filterSlackFromViews = UserPreferences.hideSlackFilesInViewsTree();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static private final DisplayableItemNodeVisitor<List<Action>> getActionsDIV = new GetPopupActionsDisplayableItemNodeVisitor();
|
static private final DisplayableItemNodeVisitor<List<Action>> getActionsDIV = new GetPopupActionsDisplayableItemNodeVisitor();
|
||||||
private final DisplayableItemNodeVisitor<AbstractAction> getPreferredActionsDIV = new GetPreferredActionsDisplayableItemNodeVisitor();
|
private final DisplayableItemNodeVisitor<AbstractAction> getPreferredActionsDIV = new GetPreferredActionsDisplayableItemNodeVisitor();
|
||||||
|
|
||||||
@ -155,24 +124,6 @@ public class DataResultFilterNode extends FilterNode {
|
|||||||
this.sourceEm = em;
|
this.sourceEm = em;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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.
|
|
||||||
* @param em The ExplorerManager for the component that is creating
|
|
||||||
* the node.
|
|
||||||
* @param filterKnown Whether or not to filter out children that represent
|
|
||||||
* known files.
|
|
||||||
* @param filterSlack Whether or not to filter out children that represent
|
|
||||||
* virtual slack space files.
|
|
||||||
*/
|
|
||||||
private DataResultFilterNode(Node node, ExplorerManager em, boolean filterKnown, boolean filterSlack) {
|
|
||||||
super(node, new DataResultFilterChildren(node, em, filterKnown, filterSlack));
|
|
||||||
this.sourceEm = em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Right click action for the nodes that we want to pass to the directory
|
* Right click action for the nodes that we want to pass to the directory
|
||||||
* table and the output view.
|
* table and the output view.
|
||||||
@ -294,10 +245,7 @@ public class DataResultFilterNode extends FilterNode {
|
|||||||
private static class DataResultFilterChildren extends FilterNode.Children {
|
private static class DataResultFilterChildren extends FilterNode.Children {
|
||||||
|
|
||||||
private final ExplorerManager sourceEm;
|
private final ExplorerManager sourceEm;
|
||||||
|
private final boolean filterArtifacts; // display message artifacts in the DataSource subtree
|
||||||
private boolean filterKnown;
|
|
||||||
private boolean filterSlack;
|
|
||||||
private boolean filterArtifacts; // display message artifacts in the DataSource subtree
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the constructor
|
* the constructor
|
||||||
@ -305,46 +253,13 @@ public class DataResultFilterNode extends FilterNode {
|
|||||||
private DataResultFilterChildren(Node arg, ExplorerManager sourceEm) {
|
private DataResultFilterChildren(Node arg, ExplorerManager sourceEm) {
|
||||||
super(arg);
|
super(arg);
|
||||||
|
|
||||||
this.filterArtifacts = false;
|
filterArtifacts = SelectionContext.getSelectionContext(arg).equals(SelectionContext.DATA_SOURCES);
|
||||||
switch (SelectionContext.getSelectionContext(arg)) {
|
|
||||||
case DATA_SOURCES:
|
|
||||||
filterSlack = filterSlackFromDataSources;
|
|
||||||
filterKnown = filterKnownFromDataSources;
|
|
||||||
filterArtifacts = true;
|
|
||||||
break;
|
|
||||||
case VIEWS:
|
|
||||||
filterSlack = filterSlackFromViews;
|
|
||||||
filterKnown = filterKnownFromViews;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
filterSlack = false;
|
|
||||||
filterKnown = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
this.sourceEm = sourceEm;
|
|
||||||
}
|
|
||||||
|
|
||||||
private DataResultFilterChildren(Node arg, ExplorerManager sourceEm, boolean filterKnown, boolean filterSlack) {
|
|
||||||
super(arg);
|
|
||||||
this.filterKnown = filterKnown;
|
|
||||||
this.filterSlack = filterSlack;
|
|
||||||
this.sourceEm = sourceEm;
|
this.sourceEm = sourceEm;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Node[] createNodes(Node key) {
|
protected Node[] createNodes(Node key) {
|
||||||
AbstractFile file = key.getLookup().lookup(AbstractFile.class);
|
|
||||||
if (file != null) {
|
|
||||||
if (filterKnown && (file.getKnown() == TskData.FileKnown.KNOWN)) {
|
|
||||||
// Filter out child nodes that represent known files
|
|
||||||
return new Node[]{};
|
|
||||||
}
|
|
||||||
if (filterSlack && file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK)) {
|
|
||||||
// Filter out child nodes that represent slack files
|
|
||||||
return new Node[]{};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// filter out all non-message artifacts, if displaying the results from the Data Source tree
|
// filter out all non-message artifacts, if displaying the results from the Data Source tree
|
||||||
BlackboardArtifact art = key.getLookup().lookup(BlackboardArtifact.class);
|
BlackboardArtifact art = key.getLookup().lookup(BlackboardArtifact.class);
|
||||||
if (art != null
|
if (art != null
|
||||||
@ -354,9 +269,8 @@ public class DataResultFilterNode extends FilterNode {
|
|||||||
return new Node[]{};
|
return new Node[]{};
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Node[]{new DataResultFilterNode(key, sourceEm, filterKnown, filterSlack)};
|
return new Node[]{new DataResultFilterNode(key, sourceEm)};
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NbBundle.Messages("DataResultFilterNode.viewSourceArtifact.text=View Source Result")
|
@NbBundle.Messages("DataResultFilterNode.viewSourceArtifact.text=View Source Result")
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2011 Basis Technology Corp.
|
* Copyright 2011-2019 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -22,7 +22,7 @@ import java.awt.event.ActionEvent;
|
|||||||
import javax.swing.AbstractAction;
|
import javax.swing.AbstractAction;
|
||||||
import org.sleuthkit.autopsy.corecomponents.DataContentTopComponent;
|
import org.sleuthkit.autopsy.corecomponents.DataContentTopComponent;
|
||||||
import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode;
|
import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode;
|
||||||
import org.sleuthkit.autopsy.datamodel.RootContentChildren;
|
import org.sleuthkit.autopsy.datamodel.CreateSleuthkitNodeVisitor;
|
||||||
import org.sleuthkit.datamodel.Content;
|
import org.sleuthkit.datamodel.Content;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -30,7 +30,7 @@ import org.sleuthkit.datamodel.Content;
|
|||||||
*/
|
*/
|
||||||
class ViewAssociatedContentAction extends AbstractAction {
|
class ViewAssociatedContentAction extends AbstractAction {
|
||||||
|
|
||||||
private Content content;
|
private final Content content;
|
||||||
|
|
||||||
public ViewAssociatedContentAction(String title, BlackboardArtifactNode node) {
|
public ViewAssociatedContentAction(String title, BlackboardArtifactNode node) {
|
||||||
super(title);
|
super(title);
|
||||||
@ -39,6 +39,6 @@ class ViewAssociatedContentAction extends AbstractAction {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
DataContentTopComponent.getDefault().setNode(content.accept(new RootContentChildren.CreateSleuthkitNodeVisitor()));
|
DataContentTopComponent.getDefault().setNode(content.accept(new CreateSleuthkitNodeVisitor()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,8 +22,10 @@ import java.io.File;
|
|||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -33,13 +35,11 @@ import org.apache.commons.io.IOUtils;
|
|||||||
import org.apache.poi.hwpf.usermodel.Picture;
|
import org.apache.poi.hwpf.usermodel.Picture;
|
||||||
import org.apache.poi.hslf.usermodel.HSLFPictureData;
|
import org.apache.poi.hslf.usermodel.HSLFPictureData;
|
||||||
import org.apache.poi.hslf.usermodel.HSLFSlideShow;
|
import org.apache.poi.hslf.usermodel.HSLFSlideShow;
|
||||||
import org.apache.poi.hssf.record.RecordInputStream.LeftoverDataException;
|
|
||||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||||
import org.apache.poi.hwpf.HWPFDocument;
|
import org.apache.poi.hwpf.HWPFDocument;
|
||||||
import org.apache.poi.hwpf.model.PicturesTable;
|
import org.apache.poi.hwpf.model.PicturesTable;
|
||||||
import org.apache.poi.sl.usermodel.PictureData.PictureType;
|
import org.apache.poi.sl.usermodel.PictureData.PictureType;
|
||||||
import org.apache.poi.ss.usermodel.Workbook;
|
import org.apache.poi.ss.usermodel.Workbook;
|
||||||
import org.apache.poi.util.RecordFormatException;
|
|
||||||
import org.apache.tika.config.TikaConfig;
|
import org.apache.tika.config.TikaConfig;
|
||||||
import org.apache.tika.detect.Detector;
|
import org.apache.tika.detect.Detector;
|
||||||
import org.apache.tika.exception.TikaException;
|
import org.apache.tika.exception.TikaException;
|
||||||
@ -72,13 +72,13 @@ import org.xml.sax.SAXException;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Extracts embedded content (e.g. images, audio, video) from Microsoft Office
|
* Extracts embedded content (e.g. images, audio, video) from Microsoft Office
|
||||||
* documents (both original and OOXML forms).
|
* documents (both original and OOXML forms) and PDF documents.
|
||||||
*/
|
*/
|
||||||
class MSOfficeEmbeddedContentExtractor {
|
class DocumentEmbeddedContentExtractor {
|
||||||
|
|
||||||
private final FileManager fileManager;
|
private final FileManager fileManager;
|
||||||
private final IngestServices services;
|
private final IngestServices services;
|
||||||
private static final Logger LOGGER = Logger.getLogger(MSOfficeEmbeddedContentExtractor.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(DocumentEmbeddedContentExtractor.class.getName());
|
||||||
private final IngestJobContext context;
|
private final IngestJobContext context;
|
||||||
private String parentFileName;
|
private String parentFileName;
|
||||||
private final String UNKNOWN_IMAGE_NAME_PREFIX = "image_"; //NON-NLS
|
private final String UNKNOWN_IMAGE_NAME_PREFIX = "image_"; //NON-NLS
|
||||||
@ -101,7 +101,8 @@ class MSOfficeEmbeddedContentExtractor {
|
|||||||
PPT("application/vnd.ms-powerpoint"), //NON-NLS
|
PPT("application/vnd.ms-powerpoint"), //NON-NLS
|
||||||
PPTX("application/vnd.openxmlformats-officedocument.presentationml.presentation"), //NON-NLS
|
PPTX("application/vnd.openxmlformats-officedocument.presentationml.presentation"), //NON-NLS
|
||||||
XLS("application/vnd.ms-excel"), //NON-NLS
|
XLS("application/vnd.ms-excel"), //NON-NLS
|
||||||
XLSX("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); //NON-NLS
|
XLSX("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"), //NON-NLS
|
||||||
|
PDF("application/pdf"); //NON-NLS
|
||||||
|
|
||||||
private final String mimeType;
|
private final String mimeType;
|
||||||
|
|
||||||
@ -116,7 +117,7 @@ class MSOfficeEmbeddedContentExtractor {
|
|||||||
}
|
}
|
||||||
private SupportedExtractionFormats abstractFileExtractionFormat;
|
private SupportedExtractionFormats abstractFileExtractionFormat;
|
||||||
|
|
||||||
MSOfficeEmbeddedContentExtractor(IngestJobContext context, FileTypeDetector fileTypeDetector, String moduleDirRelative, String moduleDirAbsolute) throws NoCurrentCaseException {
|
DocumentEmbeddedContentExtractor(IngestJobContext context, FileTypeDetector fileTypeDetector, String moduleDirRelative, String moduleDirAbsolute) throws NoCurrentCaseException {
|
||||||
|
|
||||||
this.fileManager = Case.getCurrentCaseThrows().getServices().getFileManager();
|
this.fileManager = Case.getCurrentCaseThrows().getServices().getFileManager();
|
||||||
this.services = IngestServices.getInstance();
|
this.services = IngestServices.getInstance();
|
||||||
@ -190,6 +191,9 @@ class MSOfficeEmbeddedContentExtractor {
|
|||||||
case XLS:
|
case XLS:
|
||||||
listOfExtractedImages = extractImagesFromXls(abstractFile);
|
listOfExtractedImages = extractImagesFromXls(abstractFile);
|
||||||
break;
|
break;
|
||||||
|
case PDF:
|
||||||
|
listOfExtractedImages = extractEmbeddedContentFromPDF(abstractFile);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -471,6 +475,38 @@ class MSOfficeEmbeddedContentExtractor {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts embedded attachments from PDF files.
|
||||||
|
*
|
||||||
|
* @param abstractFile Input PDF file
|
||||||
|
* @return List of extracted files to be made into derived file instances.
|
||||||
|
*/
|
||||||
|
private List<ExtractedFile> extractEmbeddedContentFromPDF(AbstractFile abstractFile) {
|
||||||
|
PDFAttachmentExtractor pdfExtractor = new PDFAttachmentExtractor(parser);
|
||||||
|
try {
|
||||||
|
Path outputDirectory = Paths.get(getOutputFolderPath(parentFileName));
|
||||||
|
//Get map of attachment name -> location disk.
|
||||||
|
Map<String, Path> extractedAttachments = pdfExtractor.extract(
|
||||||
|
new ReadContentInputStream(abstractFile), abstractFile.getId(),
|
||||||
|
outputDirectory);
|
||||||
|
|
||||||
|
//Convert output to hook into the existing logic for creating derived files
|
||||||
|
List<ExtractedFile> extractedFiles = new ArrayList<>();
|
||||||
|
extractedAttachments.entrySet().forEach((pathEntry) -> {
|
||||||
|
String fileName = pathEntry.getKey();
|
||||||
|
Path writeLocation = pathEntry.getValue();
|
||||||
|
extractedFiles.add(new ExtractedFile(fileName,
|
||||||
|
getFileRelativePath(writeLocation.getFileName().toString()),
|
||||||
|
writeLocation.toFile().length()));
|
||||||
|
});
|
||||||
|
|
||||||
|
return extractedFiles;
|
||||||
|
} catch (IOException | SAXException | TikaException ex) {
|
||||||
|
LOGGER.log(Level.WARNING, "Error attempting to extract attachments from PDFs", ex); //NON-NLS
|
||||||
|
}
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes image to the module output location.
|
* Writes image to the module output location.
|
||||||
*
|
*
|
@ -50,7 +50,7 @@ public final class EmbeddedFileExtractorIngestModule extends FileIngestModuleAda
|
|||||||
//Outer concurrent hashmap with keys of JobID, inner concurrentHashmap with keys of objectID
|
//Outer concurrent hashmap with keys of JobID, inner concurrentHashmap with keys of objectID
|
||||||
private static final ConcurrentHashMap<Long, ConcurrentHashMap<Long, Archive>> mapOfDepthTrees = new ConcurrentHashMap<>();
|
private static final ConcurrentHashMap<Long, ConcurrentHashMap<Long, Archive>> mapOfDepthTrees = new ConcurrentHashMap<>();
|
||||||
private static final IngestModuleReferenceCounter refCounter = new IngestModuleReferenceCounter();
|
private static final IngestModuleReferenceCounter refCounter = new IngestModuleReferenceCounter();
|
||||||
private MSOfficeEmbeddedContentExtractor officeExtractor;
|
private DocumentEmbeddedContentExtractor documentExtractor;
|
||||||
private SevenZipExtractor archiveExtractor;
|
private SevenZipExtractor archiveExtractor;
|
||||||
private FileTypeDetector fileTypeDetector;
|
private FileTypeDetector fileTypeDetector;
|
||||||
private long jobId;
|
private long jobId;
|
||||||
@ -115,10 +115,10 @@ public final class EmbeddedFileExtractorIngestModule extends FileIngestModuleAda
|
|||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* Construct an embedded content extractor for processing Microsoft
|
* Construct an embedded content extractor for processing Microsoft
|
||||||
* Office documents.
|
* Office documents and PDF documents.
|
||||||
*/
|
*/
|
||||||
try {
|
try {
|
||||||
this.officeExtractor = new MSOfficeEmbeddedContentExtractor(context, fileTypeDetector, moduleDirRelative, moduleDirAbsolute);
|
this.documentExtractor = new DocumentEmbeddedContentExtractor(context, fileTypeDetector, moduleDirRelative, moduleDirAbsolute);
|
||||||
} catch (NoCurrentCaseException ex) {
|
} catch (NoCurrentCaseException ex) {
|
||||||
throw new IngestModuleException(Bundle.EmbeddedFileExtractorIngestModule_UnableToGetMSOfficeExtractor_errMsg(), ex);
|
throw new IngestModuleException(Bundle.EmbeddedFileExtractorIngestModule_UnableToGetMSOfficeExtractor_errMsg(), ex);
|
||||||
}
|
}
|
||||||
@ -155,8 +155,8 @@ public final class EmbeddedFileExtractorIngestModule extends FileIngestModuleAda
|
|||||||
*/
|
*/
|
||||||
if (archiveExtractor.isSevenZipExtractionSupported(abstractFile)) {
|
if (archiveExtractor.isSevenZipExtractionSupported(abstractFile)) {
|
||||||
archiveExtractor.unpack(abstractFile, mapOfDepthTrees.get(jobId));
|
archiveExtractor.unpack(abstractFile, mapOfDepthTrees.get(jobId));
|
||||||
} else if (officeExtractor.isContentExtractionSupported(abstractFile)) {
|
} else if (documentExtractor.isContentExtractionSupported(abstractFile)) {
|
||||||
officeExtractor.extractEmbeddedContent(abstractFile);
|
documentExtractor.extractEmbeddedContent(abstractFile);
|
||||||
}
|
}
|
||||||
return ProcessResult.OK;
|
return ProcessResult.OK;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,183 @@
|
|||||||
|
/*
|
||||||
|
* 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.modules.embeddedfileextractor;
|
||||||
|
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import org.apache.commons.io.FilenameUtils;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.tika.exception.TikaException;
|
||||||
|
import org.apache.tika.extractor.EmbeddedDocumentExtractor;
|
||||||
|
import org.apache.tika.metadata.Metadata;
|
||||||
|
import org.apache.tika.parser.AutoDetectParser;
|
||||||
|
import org.apache.tika.parser.ParseContext;
|
||||||
|
import org.apache.tika.parser.Parser;
|
||||||
|
import org.apache.tika.sax.BodyContentHandler;
|
||||||
|
import org.xml.sax.ContentHandler;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
|
import org.sleuthkit.datamodel.EncodedFileOutputStream;
|
||||||
|
import org.sleuthkit.datamodel.TskData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Facility for extracting and storing attachments from PDF documents.
|
||||||
|
* Implementation specifics, however, are generic enough to be used on any
|
||||||
|
* document with embedded resources. The current name reflects the only known
|
||||||
|
* use case for this class.
|
||||||
|
*/
|
||||||
|
final class PDFAttachmentExtractor {
|
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(PDFAttachmentExtractor.class.getName());
|
||||||
|
private final AutoDetectParser parser;
|
||||||
|
|
||||||
|
public PDFAttachmentExtractor() {
|
||||||
|
parser = new AutoDetectParser();
|
||||||
|
}
|
||||||
|
|
||||||
|
public PDFAttachmentExtractor(AutoDetectParser parser) {
|
||||||
|
this.parser = parser;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts PDF attachments from a given input and writes them to the supplied
|
||||||
|
* output directory.
|
||||||
|
*
|
||||||
|
* @param input Input PDF to extract attachments from
|
||||||
|
* @param parentID ID for unique extraction names
|
||||||
|
* @param outputDir Directory to write attachments
|
||||||
|
* @return Map containing file name -> location on disk
|
||||||
|
* @throws IOException
|
||||||
|
* @throws SAXException
|
||||||
|
* @throws TikaException
|
||||||
|
*/
|
||||||
|
public Map<String, Path> extract(InputStream input, long parentID, Path outputDir) throws IOException, SAXException, TikaException {
|
||||||
|
ExtractionPreconditions.checkArgument(Files.exists(outputDir),
|
||||||
|
String.format("Output directory: %s, does not exist.", outputDir.toString())); //NON-NLS
|
||||||
|
|
||||||
|
ParseContext parseContext = new ParseContext();
|
||||||
|
parseContext.set(Parser.class, parser);
|
||||||
|
|
||||||
|
//Keep track of the attachment files as they are being extracted and written to disk.
|
||||||
|
NewResourceWatcher watcher = new NewResourceWatcher();
|
||||||
|
parseContext.set(EmbeddedDocumentExtractor.class, new EmbeddedAttachmentHandler(outputDir, parentID, watcher));
|
||||||
|
|
||||||
|
//Parse input with default params, except for our ParseContext
|
||||||
|
parser.parse(input, new BodyContentHandler(-1), new Metadata(), parseContext);
|
||||||
|
|
||||||
|
return watcher.getSnapshot();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal Tika class that is invoked upon encountering an embedded
|
||||||
|
* resource.
|
||||||
|
*/
|
||||||
|
static class EmbeddedAttachmentHandler implements EmbeddedDocumentExtractor {
|
||||||
|
|
||||||
|
private final Path outputDirectory;
|
||||||
|
private final NewResourceWatcher watcher;
|
||||||
|
private final Long parentID;
|
||||||
|
private Integer attachmentCount;
|
||||||
|
|
||||||
|
public EmbeddedAttachmentHandler(Path outputDirectory, long parentID, NewResourceWatcher watcher) {
|
||||||
|
this.outputDirectory = outputDirectory;
|
||||||
|
this.watcher = watcher;
|
||||||
|
this.parentID = parentID;
|
||||||
|
attachmentCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldParseEmbedded(Metadata mtdt) {
|
||||||
|
//Grab every available attachment
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void parseEmbedded(InputStream in, ContentHandler ch, Metadata mtdt, boolean bln) throws SAXException, IOException {
|
||||||
|
//Resource naming scheme is used internally in autopsy, therefore we can guarentee uniqueness.
|
||||||
|
String uniqueExtractedName = parentID + "_attch_" + attachmentCount++; //NON-NLS
|
||||||
|
|
||||||
|
String name = mtdt.get(Metadata.RESOURCE_NAME_KEY);
|
||||||
|
String ext = FilenameUtils.getExtension(name);
|
||||||
|
|
||||||
|
//Append the extension if we can.
|
||||||
|
if(ext == null) {
|
||||||
|
name = uniqueExtractedName;
|
||||||
|
} else if(!ext.isEmpty()) {
|
||||||
|
uniqueExtractedName += "." + ext;
|
||||||
|
}
|
||||||
|
|
||||||
|
Path outputFile = outputDirectory.resolve(uniqueExtractedName);
|
||||||
|
|
||||||
|
try (EncodedFileOutputStream outputStream = new EncodedFileOutputStream(
|
||||||
|
new FileOutputStream(outputFile.toFile()), TskData.EncodingType.XOR1)){
|
||||||
|
IOUtils.copy(in, outputStream);
|
||||||
|
watcher.notify(name, outputFile);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
logger.log(Level.WARNING, String.format("Could not extract attachment %s into directory %s", //NON-NLS
|
||||||
|
uniqueExtractedName, outputFile), ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenient wrapper for keeping track of new resource paths and the display
|
||||||
|
* name for each of these resources.
|
||||||
|
*
|
||||||
|
* It is necessary to maintain a snapshot of only our changes when the
|
||||||
|
* output directory is shared among other processes/threads.
|
||||||
|
*/
|
||||||
|
static class NewResourceWatcher {
|
||||||
|
|
||||||
|
private final Map<String, Path> newResourcePaths;
|
||||||
|
|
||||||
|
public NewResourceWatcher() {
|
||||||
|
newResourcePaths = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void notify(String name, Path newResource) {
|
||||||
|
newResourcePaths.put(name, newResource);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Path> getSnapshot() {
|
||||||
|
return newResourcePaths;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static convenience methods that ensure the PDF extractor is being invoked
|
||||||
|
* correctly.
|
||||||
|
*/
|
||||||
|
static class ExtractionPreconditions {
|
||||||
|
|
||||||
|
public static void checkArgument(boolean expression, String msg) throws IOException {
|
||||||
|
if (!expression) {
|
||||||
|
throw new IOException(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExtractionPreconditions(){
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -109,7 +109,9 @@ public class FileTypeDetector {
|
|||||||
* Tika, and Autopsy file type definitions take precendence over Tika.
|
* Tika, and Autopsy file type definitions take precendence over Tika.
|
||||||
*
|
*
|
||||||
* @throws FileTypeDetectorInitException If an initialization error occurs,
|
* @throws FileTypeDetectorInitException If an initialization error occurs,
|
||||||
* e.g., user-defined file type definitions exist but cannot be loaded.
|
* e.g., user-defined file type
|
||||||
|
* definitions exist but cannot be
|
||||||
|
* loaded.
|
||||||
*/
|
*/
|
||||||
public FileTypeDetector() throws FileTypeDetectorInitException {
|
public FileTypeDetector() throws FileTypeDetectorInitException {
|
||||||
try {
|
try {
|
||||||
@ -223,7 +225,7 @@ public class FileTypeDetector {
|
|||||||
ReadContentInputStream stream = new ReadContentInputStream(file);
|
ReadContentInputStream stream = new ReadContentInputStream(file);
|
||||||
|
|
||||||
try (TikaInputStream tikaInputStream = TikaInputStream.get(stream)) {
|
try (TikaInputStream tikaInputStream = TikaInputStream.get(stream)) {
|
||||||
String tikaType = tika.detect(tikaInputStream, file.getName());
|
String tikaType = tika.detect(tikaInputStream);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Remove the Tika suffix from the MIME type name.
|
* Remove the Tika suffix from the MIME type name.
|
||||||
@ -234,6 +236,21 @@ public class FileTypeDetector {
|
|||||||
*/
|
*/
|
||||||
mimeType = removeOptionalParameter(mimeType);
|
mimeType = removeOptionalParameter(mimeType);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If Tika recognizes the file signature, then use the file
|
||||||
|
* name to refine the type. In short, this is to exclude the
|
||||||
|
* mime types that are determined solely by file extension.
|
||||||
|
* More details in JIRA-4871.
|
||||||
|
*/
|
||||||
|
if (!mimeType.equals(MimeTypes.OCTET_STREAM)) {
|
||||||
|
ReadContentInputStream secondPassStream = new ReadContentInputStream(file);
|
||||||
|
try (TikaInputStream secondPassTikaStream = TikaInputStream.get(secondPassStream)) {
|
||||||
|
tikaType = tika.detect(secondPassTikaStream, file.getName());
|
||||||
|
mimeType = tikaType.replace("tika-", ""); //NON-NLS
|
||||||
|
mimeType = removeOptionalParameter(mimeType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We cannot trust Tika's audio/mpeg mimetype. Lets verify the
|
* We cannot trust Tika's audio/mpeg mimetype. Lets verify the
|
||||||
* first two bytes and confirm it is not 0xffff. Details in
|
* first two bytes and confirm it is not 0xffff. Details in
|
||||||
@ -275,6 +292,7 @@ public class FileTypeDetector {
|
|||||||
* first 4 bits.
|
* first 4 bits.
|
||||||
*
|
*
|
||||||
* @param x byte
|
* @param x byte
|
||||||
|
*
|
||||||
* @return Flag indicating the byte if 0xFF
|
* @return Flag indicating the byte if 0xFF
|
||||||
*/
|
*/
|
||||||
private boolean byteIs0xFF(byte x) {
|
private boolean byteIs0xFF(byte x) {
|
||||||
@ -287,6 +305,7 @@ public class FileTypeDetector {
|
|||||||
* @param file Abstract file to read
|
* @param file Abstract file to read
|
||||||
* @param offset Offset to begin reading
|
* @param offset Offset to begin reading
|
||||||
* @param n Number of bytes to read
|
* @param n Number of bytes to read
|
||||||
|
*
|
||||||
* @return Byte array of size n
|
* @return Byte array of size n
|
||||||
*
|
*
|
||||||
* @throws TskCoreException
|
* @throws TskCoreException
|
||||||
|
@ -198,6 +198,7 @@ final class HtmlTextExtractor implements TextExtractor {
|
|||||||
renderer.setIncludeHyperlinkURLs(false);
|
renderer.setIncludeHyperlinkURLs(false);
|
||||||
renderer.setDecorateFontStyles(false);
|
renderer.setDecorateFontStyles(false);
|
||||||
renderer.setIncludeAlternateText(false);
|
renderer.setIncludeAlternateText(false);
|
||||||
|
renderer.setMaxLineLength(0); // don't force wrapping
|
||||||
return new StringReader(renderer.toString());
|
return new StringReader(renderer.toString());
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
logger.log(Level.WARNING, "Error extracting HTML from content.", ex);
|
logger.log(Level.WARNING, "Error extracting HTML from content.", ex);
|
||||||
|
@ -46,6 +46,7 @@ import org.apache.tika.Tika;
|
|||||||
import org.apache.tika.exception.TikaException;
|
import org.apache.tika.exception.TikaException;
|
||||||
import org.apache.tika.metadata.Metadata;
|
import org.apache.tika.metadata.Metadata;
|
||||||
import org.apache.tika.parser.AutoDetectParser;
|
import org.apache.tika.parser.AutoDetectParser;
|
||||||
|
import org.apache.tika.parser.EmptyParser;
|
||||||
import org.apache.tika.parser.ParseContext;
|
import org.apache.tika.parser.ParseContext;
|
||||||
import org.apache.tika.parser.Parser;
|
import org.apache.tika.parser.Parser;
|
||||||
import org.apache.tika.parser.ParsingReader;
|
import org.apache.tika.parser.ParsingReader;
|
||||||
@ -125,6 +126,16 @@ final class TikaTextExtractor implements TextExtractor {
|
|||||||
"application/x-z", //NON-NLS
|
"application/x-z", //NON-NLS
|
||||||
"application/x-compress"); //NON-NLS
|
"application/x-compress"); //NON-NLS
|
||||||
|
|
||||||
|
//Tika should ignore types with embedded files that can be handled by the unpacking modules
|
||||||
|
private static final List<String> EMBEDDED_FILE_MIME_TYPES
|
||||||
|
= ImmutableList.of("application/msword", //NON-NLS
|
||||||
|
"application/vnd.openxmlformats-officedocument.wordprocessingml.document", //NON-NLS
|
||||||
|
"application/vnd.ms-powerpoint", //NON-NLS
|
||||||
|
"application/vnd.openxmlformats-officedocument.presentationml.presentation", //NON-NLS
|
||||||
|
"application/vnd.ms-excel", //NON-NLS
|
||||||
|
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", //NON-NLS
|
||||||
|
"application/pdf"); //NON-NLS
|
||||||
|
|
||||||
private static final java.util.logging.Logger TIKA_LOGGER = java.util.logging.Logger.getLogger("Tika"); //NON-NLS
|
private static final java.util.logging.Logger TIKA_LOGGER = java.util.logging.Logger.getLogger("Tika"); //NON-NLS
|
||||||
private static final Logger AUTOPSY_LOGGER = Logger.getLogger(TikaTextExtractor.class.getName());
|
private static final Logger AUTOPSY_LOGGER = Logger.getLogger(TikaTextExtractor.class.getName());
|
||||||
|
|
||||||
@ -184,7 +195,14 @@ final class TikaTextExtractor implements TextExtractor {
|
|||||||
InputStream stream = null;
|
InputStream stream = null;
|
||||||
|
|
||||||
ParseContext parseContext = new ParseContext();
|
ParseContext parseContext = new ParseContext();
|
||||||
|
|
||||||
|
//Disable appending embedded file text to output for EFE supported types
|
||||||
|
//JIRA-4975
|
||||||
|
if(content instanceof AbstractFile && EMBEDDED_FILE_MIME_TYPES.contains(((AbstractFile)content).getMIMEType())) {
|
||||||
|
parseContext.set(Parser.class, new EmptyParser());
|
||||||
|
} else {
|
||||||
parseContext.set(Parser.class, parser);
|
parseContext.set(Parser.class, parser);
|
||||||
|
}
|
||||||
|
|
||||||
if (ocrEnabled() && content instanceof AbstractFile) {
|
if (ocrEnabled() && content instanceof AbstractFile) {
|
||||||
AbstractFile file = ((AbstractFile) content);
|
AbstractFile file = ((AbstractFile) content);
|
||||||
|
@ -52,6 +52,8 @@ import org.sleuthkit.autopsy.texttranslation.NoServiceProviderException;
|
|||||||
import org.sleuthkit.autopsy.texttranslation.TranslationException;
|
import org.sleuthkit.autopsy.texttranslation.TranslationException;
|
||||||
import org.sleuthkit.datamodel.Content;
|
import org.sleuthkit.datamodel.Content;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
|
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
|
||||||
import org.sleuthkit.autopsy.texttranslation.ui.TranslationContentPanel.DisplayDropdownOptions;
|
import org.sleuthkit.autopsy.texttranslation.ui.TranslationContentPanel.DisplayDropdownOptions;
|
||||||
|
|
||||||
@ -61,6 +63,8 @@ import org.sleuthkit.autopsy.texttranslation.ui.TranslationContentPanel.DisplayD
|
|||||||
@ServiceProvider(service = TextViewer.class, position = 4)
|
@ServiceProvider(service = TextViewer.class, position = 4)
|
||||||
public final class TranslatedTextViewer implements TextViewer {
|
public final class TranslatedTextViewer implements TextViewer {
|
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(TranslatedTextViewer.class.getName());
|
||||||
|
|
||||||
private static final boolean OCR_ENABLED = true;
|
private static final boolean OCR_ENABLED = true;
|
||||||
private static final boolean OCR_DISABLED = false;
|
private static final boolean OCR_DISABLED = false;
|
||||||
private static final int MAX_SIZE_1MB = 1024000;
|
private static final int MAX_SIZE_1MB = 1024000;
|
||||||
@ -169,16 +173,20 @@ public final class TranslatedTextViewer implements TextViewer {
|
|||||||
try {
|
try {
|
||||||
return getFileText(node);
|
return getFileText(node);
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
|
logger.log(Level.WARNING, "Error getting text", ex);
|
||||||
return Bundle.TranslatedContentViewer_errorMsg();
|
return Bundle.TranslatedContentViewer_errorMsg();
|
||||||
} catch (TextExtractor.InitReaderException ex) {
|
} catch (TextExtractor.InitReaderException ex) {
|
||||||
|
logger.log(Level.WARNING, "Error getting text", ex);
|
||||||
return Bundle.TranslatedContentViewer_errorExtractingText();
|
return Bundle.TranslatedContentViewer_errorExtractingText();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
return translate(getFileText(node));
|
return translate(getFileText(node));
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
|
logger.log(Level.WARNING, "Error translating text", ex);
|
||||||
return Bundle.TranslatedContentViewer_errorMsg();
|
return Bundle.TranslatedContentViewer_errorMsg();
|
||||||
} catch (TextExtractor.InitReaderException ex) {
|
} catch (TextExtractor.InitReaderException ex) {
|
||||||
|
logger.log(Level.WARNING, "Error translating text", ex);
|
||||||
return Bundle.TranslatedContentViewer_errorExtractingText();
|
return Bundle.TranslatedContentViewer_errorExtractingText();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -247,7 +255,8 @@ public final class TranslatedTextViewer implements TextViewer {
|
|||||||
} catch (NoServiceProviderException ex) {
|
} catch (NoServiceProviderException ex) {
|
||||||
return Bundle.TranslatedContentViewer_noServiceProvider();
|
return Bundle.TranslatedContentViewer_noServiceProvider();
|
||||||
} catch (TranslationException ex) {
|
} catch (TranslationException ex) {
|
||||||
return Bundle.TranslatedContentViewer_translationException();
|
logger.log(Level.WARNING, "Error translating text", ex);
|
||||||
|
return Bundle.TranslatedContentViewer_translationException() + " (" + ex.getMessage() + ")";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -287,7 +296,7 @@ public final class TranslatedTextViewer implements TextViewer {
|
|||||||
|
|
||||||
//Correct for UTF-8
|
//Correct for UTF-8
|
||||||
byte[] resultInUTF8Bytes = result.getBytes("UTF8");
|
byte[] resultInUTF8Bytes = result.getBytes("UTF8");
|
||||||
byte[] trimTo1MB = Arrays.copyOfRange(resultInUTF8Bytes, 0, MAX_SIZE_1MB / 1000);
|
byte[] trimTo1MB = Arrays.copyOfRange(resultInUTF8Bytes, 0, MAX_SIZE_1MB );
|
||||||
return new String(trimTo1MB, "UTF-8");
|
return new String(trimTo1MB, "UTF-8");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -333,7 +342,8 @@ public final class TranslatedTextViewer implements TextViewer {
|
|||||||
bytesRead += read;
|
bytesRead += read;
|
||||||
}
|
}
|
||||||
|
|
||||||
return textBuilder.toString();
|
// The trim is on here because HTML files were observed with nearly 1MB of white space at the end
|
||||||
|
return textBuilder.toString().trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
x
Reference in New Issue
Block a user