mirror of
https://github.com/elisspace/autopsy.git
synced 2026-09-29 05:49:50 +00:00
Merge branch 'develop' of github.com:sleuthkit/autopsy into 4965_paging_ui
This commit is contained in:
+1
-1
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
|
||||
OpenIDE-Module: org.sleuthkit.autopsy.core/10
|
||||
OpenIDE-Module-Localizing-Bundle: org/sleuthkit/autopsy/core/Bundle.properties
|
||||
OpenIDE-Module-Layer: org/sleuthkit/autopsy/core/layer.xml
|
||||
OpenIDE-Module-Implementation-Version: 26
|
||||
OpenIDE-Module-Implementation-Version: 27
|
||||
OpenIDE-Module-Requires: org.openide.windows.WindowManager
|
||||
AutoUpdate-Show-In-Client: true
|
||||
AutoUpdate-Essential-Module: true
|
||||
|
||||
@@ -83,5 +83,5 @@ nbm.homepage=http://www.sleuthkit.org/
|
||||
nbm.module.author=Brian Carrier
|
||||
nbm.needs.restart=true
|
||||
source.reference.curator-recipes-2.8.0.jar=release/modules/ext/curator-recipes-2.8.0-sources.jar
|
||||
spec.version.base=10.14
|
||||
spec.version.base=10.15
|
||||
|
||||
|
||||
@@ -81,6 +81,7 @@ import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.events.DataSourceAddedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.events.DataSourceNameChangedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.events.ReportAddedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData.CaseNodeDataException;
|
||||
import org.sleuthkit.autopsy.casemodule.multiusercases.CoordinationServiceUtils;
|
||||
import org.sleuthkit.autopsy.casemodule.services.Services;
|
||||
import org.sleuthkit.autopsy.commonpropertiessearch.CommonAttributeSearchAction;
|
||||
@@ -1620,11 +1621,10 @@ public class Case {
|
||||
}
|
||||
if (getCaseType() == CaseType.MULTI_USER_CASE && !oldCaseDetails.getCaseDisplayName().equals(caseDetails.getCaseDisplayName())) {
|
||||
try {
|
||||
CoordinationService coordinationService = CoordinationService.getInstance();
|
||||
CaseNodeData nodeData = new CaseNodeData(coordinationService.getNodeData(CategoryNode.CASES, metadata.getCaseDirectory()));
|
||||
CaseNodeData nodeData = CaseNodeData.readCaseNodeData(metadata.getCaseDirectory());
|
||||
nodeData.setDisplayName(caseDetails.getCaseDisplayName());
|
||||
coordinationService.setNodeData(CategoryNode.CASES, metadata.getCaseDirectory(), nodeData.toArray());
|
||||
} catch (CoordinationServiceException | InterruptedException | IOException ex) {
|
||||
CaseNodeData.writeCaseNodeData(nodeData);
|
||||
} catch (CaseNodeDataException | InterruptedException ex) {
|
||||
throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotUpdateCaseNodeData(ex.getLocalizedMessage()), ex);
|
||||
}
|
||||
}
|
||||
@@ -2005,10 +2005,8 @@ public class Case {
|
||||
if (getCaseType() == CaseType.MULTI_USER_CASE) {
|
||||
progressIndicator.progress(Bundle.Case_progressMessage_creatingCaseNodeData());
|
||||
try {
|
||||
CoordinationService coordinationService = CoordinationService.getInstance();
|
||||
CaseNodeData nodeData = new CaseNodeData(metadata);
|
||||
coordinationService.setNodeData(CategoryNode.CASES, metadata.getCaseDirectory(), nodeData.toArray());
|
||||
} catch (CoordinationServiceException | InterruptedException | ParseException | IOException ex) {
|
||||
CaseNodeData.createCaseNodeData(metadata);
|
||||
} catch (CaseNodeDataException | InterruptedException ex) {
|
||||
throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotCreateCaseNodeData(ex.getLocalizedMessage()), ex);
|
||||
}
|
||||
}
|
||||
@@ -2033,27 +2031,10 @@ public class Case {
|
||||
if (getCaseType() == CaseType.MULTI_USER_CASE) {
|
||||
progressIndicator.progress(Bundle.Case_progressMessage_updatingCaseNodeData());
|
||||
try {
|
||||
CaseNodeData nodeData;
|
||||
CoordinationService coordinationService = CoordinationService.getInstance();
|
||||
byte[] nodeBytes = coordinationService.getNodeData(CategoryNode.CASES, metadata.getCaseDirectory());
|
||||
if (nodeBytes != null && nodeBytes.length > 0) {
|
||||
/*
|
||||
* Update the last access date in the coordination service
|
||||
* node data for the case.
|
||||
*/
|
||||
nodeData = new CaseNodeData(nodeBytes);
|
||||
nodeData.setLastAccessDate(new Date());
|
||||
} else {
|
||||
/*
|
||||
* This is a "legacy" case with no data stored in its case
|
||||
* directory coordination service node yet, or the node is
|
||||
* empty due to some error, so create the coordination
|
||||
* service node data from the case metadata.
|
||||
*/
|
||||
nodeData = new CaseNodeData(metadata);
|
||||
}
|
||||
coordinationService.setNodeData(CategoryNode.CASES, metadata.getCaseDirectory(), nodeData.toArray());
|
||||
} catch (CoordinationServiceException | InterruptedException | ParseException | IOException ex) {
|
||||
CaseNodeData nodeData = CaseNodeData.readCaseNodeData(metadata.getCaseDirectory());
|
||||
nodeData.setLastAccessDate(new Date());
|
||||
CaseNodeData.writeCaseNodeData(nodeData);
|
||||
} catch (CaseNodeDataException | InterruptedException ex) {
|
||||
throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotUpdateCaseNodeData(ex.getLocalizedMessage()), ex);
|
||||
}
|
||||
}
|
||||
@@ -2633,9 +2614,8 @@ public class Case {
|
||||
|
||||
progressIndicator.progress(Bundle.Case_progressMessage_fetchingCoordSvcNodeData());
|
||||
try {
|
||||
byte[] nodeBytes = coordinationService.getNodeData(CoordinationService.CategoryNode.CASES, metadata.getCaseDirectory());
|
||||
caseNodeData = new CaseNodeData(nodeBytes);
|
||||
} catch (CoordinationServiceException | InterruptedException | IOException ex) {
|
||||
caseNodeData = CaseNodeData.readCaseNodeData(metadata.getCaseDirectory());
|
||||
} catch (CaseNodeDataException | InterruptedException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Failed to get coordination service node data %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex); //NON-NLS
|
||||
throw new CaseActionException(Bundle.Case_exceptionMessage_failedToFetchCoordSvcNodeData(ex.getLocalizedMessage()));
|
||||
}
|
||||
@@ -2899,9 +2879,8 @@ public class Case {
|
||||
private static void setDeletedItemFlag(CaseNodeData caseNodeData, CaseNodeData.DeletedFlags flag) throws InterruptedException {
|
||||
try {
|
||||
caseNodeData.setDeletedFlag(flag);
|
||||
CoordinationService coordinationService = CoordinationService.getInstance();
|
||||
coordinationService.setNodeData(CategoryNode.CASES, caseNodeData.getDirectory().toString(), caseNodeData.toArray());
|
||||
} catch (IOException | CoordinationServiceException ex) {
|
||||
CaseNodeData.writeCaseNodeData(caseNodeData);
|
||||
} catch (CaseNodeDataException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Error updating deleted item flag %s for %s (%s) in %s", flag.name(), caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory()), ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-2019 Basis Technology Corp.
|
||||
* Copyright 2016-2019 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -38,7 +38,7 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
|
||||
/**
|
||||
* Panel for displaying ingest job history.
|
||||
* Panel for displaying ingest job history for a data source.
|
||||
*/
|
||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||
public final class IngestJobInfoPanel extends javax.swing.JPanel {
|
||||
@@ -79,11 +79,11 @@ public final class IngestJobInfoPanel extends javax.swing.JPanel {
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the data source which ingest jobs are being displayed for
|
||||
* Changes the data source for which ingest jobs are being displayed.
|
||||
*
|
||||
* @param selectedDataSource the data source to display ingest jobs for
|
||||
* @param selectedDataSource The data source.
|
||||
*/
|
||||
public void updateIngestHistoryData(DataSource selectedDataSource) {
|
||||
public void setDataSource(DataSource selectedDataSource) {
|
||||
this.selectedDataSource = selectedDataSource;
|
||||
ingestJobsForSelectedDataSource.clear();
|
||||
if (selectedDataSource != null) {
|
||||
@@ -109,7 +109,7 @@ public final class IngestJobInfoPanel extends javax.swing.JPanel {
|
||||
try {
|
||||
SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
|
||||
this.ingestJobs = skCase.getIngestJobs();
|
||||
updateIngestHistoryData(selectedDataSource);
|
||||
setDataSource(selectedDataSource);
|
||||
} catch (TskCoreException | NoCurrentCaseException ex) {
|
||||
logger.log(Level.SEVERE, "Failed to load ingest jobs.", ex);
|
||||
JOptionPane.showMessageDialog(this, Bundle.IngestJobInfoPanel_loadIngestJob_error_text(), Bundle.IngestJobInfoPanel_loadIngestJob_error_title(), JOptionPane.ERROR_MESSAGE);
|
||||
|
||||
+1
-1
@@ -69,7 +69,7 @@ final class DataSourceSummaryDialog extends javax.swing.JDialog implements Obser
|
||||
DataSource selectedDataSource = dataSourcesPanel.getSelectedDataSource();
|
||||
countsPanel.updateCountsTableData(selectedDataSource);
|
||||
detailsPanel.updateDetailsPanelData(selectedDataSource);
|
||||
ingestHistoryPanel.updateIngestHistoryData(selectedDataSource);
|
||||
ingestHistoryPanel.setDataSource(selectedDataSource);
|
||||
this.repaint();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -22,25 +22,33 @@ import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.text.ParseException;
|
||||
import java.util.Date;
|
||||
import java.util.logging.Level;
|
||||
import org.sleuthkit.autopsy.casemodule.CaseMetadata;
|
||||
import org.sleuthkit.autopsy.casemodule.CaseMetadata.CaseMetadataException;
|
||||
import org.sleuthkit.autopsy.coordinationservice.CoordinationService;
|
||||
import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CoordinationServiceException;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
|
||||
/**
|
||||
* An object that converts data for a case directory lock coordination service
|
||||
* node to and from byte arrays.
|
||||
* Case data stored in a case directory coordination service node.
|
||||
*/
|
||||
public final class CaseNodeData {
|
||||
|
||||
private static final int CURRENT_VERSION = 1;
|
||||
private static final int MAJOR_VERSION = 2;
|
||||
private static final int MINOR_VERSION = 0;
|
||||
private static final Logger logger = Logger.getLogger(CaseNodeData.class.getName());
|
||||
|
||||
/*
|
||||
* Version 0 fields.
|
||||
* Version 0 fields. Note that version 0 node data was only written to the
|
||||
* coordination service node if an auto ingest job error occurred.
|
||||
*/
|
||||
private final int version;
|
||||
private int version;
|
||||
private boolean errorsOccurred;
|
||||
|
||||
/*
|
||||
@@ -53,28 +61,191 @@ public final class CaseNodeData {
|
||||
private String displayName;
|
||||
private short deletedItemFlags;
|
||||
|
||||
/**
|
||||
* Gets the current version of the case directory lock coordination service
|
||||
* node data.
|
||||
*
|
||||
* @return The version number.
|
||||
/*
|
||||
* Version 2 fields.
|
||||
*/
|
||||
public static int getCurrentVersion() {
|
||||
return CaseNodeData.CURRENT_VERSION;
|
||||
private int minorVersion;
|
||||
|
||||
/**
|
||||
* Creates case node data from the metadata for a case and writes it to the
|
||||
* appropriate case directory coordination service node, which must already
|
||||
* exist.
|
||||
*
|
||||
* @param metadata The case metadata.
|
||||
*
|
||||
* @return The case node data that was written to the coordination service
|
||||
* node.
|
||||
*
|
||||
* @throws CaseNodeDataException If there is an error creating or writing
|
||||
* the case node data.
|
||||
* @throws InterruptedException If the current thread is interrupted while
|
||||
* waiting for the coordination service.
|
||||
*/
|
||||
public static CaseNodeData createCaseNodeData(final CaseMetadata metadata) throws CaseNodeDataException, InterruptedException {
|
||||
try {
|
||||
final CaseNodeData nodeData = new CaseNodeData(metadata);
|
||||
CoordinationService.getInstance().setNodeData(CoordinationService.CategoryNode.CASES, nodeData.getDirectory().toString(), nodeData.toArray());
|
||||
return nodeData;
|
||||
|
||||
} catch (ParseException | IOException | CoordinationServiceException ex) {
|
||||
throw new CaseNodeDataException(String.format("Error creating case node data for coordination service node with path %s", metadata.getCaseDirectory().toUpperCase()), ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses a CaseMetadata object to construct an object that converts data for
|
||||
* a case directory lock coordination service node to and from byte arrays.
|
||||
* Reads case data from a case directory coordination service node. If the
|
||||
* data is missing, corrupted, or from an older version of the software, an
|
||||
* attempt is made to remedy the situation using the case metadata.
|
||||
*
|
||||
* @param nodePath The case directory coordination service node path.
|
||||
*
|
||||
* @return The case node data.
|
||||
*
|
||||
* @throws CaseNodeDataException If there is an error reading or writing the
|
||||
* case node data.
|
||||
* @throws InterruptedException If the current thread is interrupted while
|
||||
* waiting for the coordination service.
|
||||
*/
|
||||
public static CaseNodeData readCaseNodeData(String nodePath) throws CaseNodeDataException, InterruptedException {
|
||||
try {
|
||||
CaseNodeData nodeData;
|
||||
final byte[] nodeBytes = CoordinationService.getInstance().getNodeData(CoordinationService.CategoryNode.CASES, nodePath);
|
||||
if (nodeBytes != null && nodeBytes.length > 0) {
|
||||
try {
|
||||
nodeData = new CaseNodeData(nodeBytes);
|
||||
} catch (IOException ex) {
|
||||
/*
|
||||
* The existing case node data is corrupted.
|
||||
*/
|
||||
logger.log(Level.WARNING, String.format("Error reading node data for coordination service node with path %s, will attempt to replace it", nodePath.toUpperCase()), ex); //NON-NLS
|
||||
final CaseMetadata metadata = getCaseMetadata(nodePath);
|
||||
nodeData = createCaseNodeData(metadata);
|
||||
logger.log(Level.INFO, String.format("Replaced corrupt node data for coordination service node with path %s", nodePath.toUpperCase())); //NON-NLS
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* The case node data is missing. Version 0 node data was only
|
||||
* written to the coordination service node if an auto ingest
|
||||
* job error occurred.
|
||||
*/
|
||||
logger.log(Level.INFO, String.format("Missing node data for coordination service node with path %s, will attempt to create it", nodePath.toUpperCase())); //NON-NLS
|
||||
final CaseMetadata metadata = getCaseMetadata(nodePath);
|
||||
nodeData = createCaseNodeData(metadata);
|
||||
logger.log(Level.INFO, String.format("Created node data for coordination service node with path %s", nodePath.toUpperCase())); //NON-NLS
|
||||
}
|
||||
if (nodeData.getVersion() < CaseNodeData.MAJOR_VERSION) {
|
||||
nodeData = upgradeCaseNodeData(nodePath, nodeData);
|
||||
}
|
||||
return nodeData;
|
||||
|
||||
} catch (CaseNodeDataException | CaseMetadataException | ParseException | IOException | CoordinationServiceException ex) {
|
||||
throw new CaseNodeDataException(String.format("Error reading/writing node data coordination service node with path %s", nodePath.toUpperCase()), ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes case data to a case directory coordination service node. Obtain
|
||||
* the case data to be updated and written by calling createCaseNodeData()
|
||||
* or readCaseNodeData().
|
||||
*
|
||||
* @param nodeData The case node data.
|
||||
*
|
||||
* @throws CaseNodeDataException If there is an error writing the case node
|
||||
* data.
|
||||
* @throws InterruptedException If the current thread is interrupted while
|
||||
* waiting for the coordination service.
|
||||
*/
|
||||
public static void writeCaseNodeData(CaseNodeData nodeData) throws CaseNodeDataException, InterruptedException {
|
||||
try {
|
||||
CoordinationService.getInstance().setNodeData(CoordinationService.CategoryNode.CASES, nodeData.getDirectory().toString(), nodeData.toArray());
|
||||
|
||||
} catch (IOException | CoordinationServiceException ex) {
|
||||
throw new CaseNodeDataException(String.format("Error writing node data coordination service node with path %s", nodeData.getDirectory().toString().toUpperCase()), ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Upgrades older versions of node data to the current version and writes
|
||||
* the data back to the case directory coordination service node.
|
||||
*
|
||||
* @param nodePath The case directory coordination service node path.
|
||||
* @param oldNodeData The outdated node data.
|
||||
*
|
||||
* @return The updated node data.
|
||||
*
|
||||
* @throws CaseNodeDataException If the case meta data file or case
|
||||
* directory do not exist.
|
||||
* @throws CaseMetadataException If the case metadata cannot be read.
|
||||
*/
|
||||
private static CaseNodeData upgradeCaseNodeData(String nodePath, CaseNodeData oldNodeData) throws CaseNodeDataException, CaseMetadataException, ParseException, IOException, CoordinationServiceException, InterruptedException {
|
||||
CaseNodeData nodeData;
|
||||
switch (oldNodeData.getVersion()) {
|
||||
case 0:
|
||||
/*
|
||||
* Version 0 node data consisted of only the version number and
|
||||
* the errors occurred flag and was only written when an auto
|
||||
* ingest job error occurred. To upgrade from version 0, the
|
||||
* version 1 fields need to be set from the case metadata and
|
||||
* the errors occurred flag needs to be carried forward. Note
|
||||
* that the last accessed date gets advanced to now, since it is
|
||||
* otherwise unknown.
|
||||
*/
|
||||
final CaseMetadata metadata = getCaseMetadata(nodePath);
|
||||
nodeData = new CaseNodeData(metadata);
|
||||
nodeData.setErrorsOccurred(oldNodeData.getErrorsOccurred());
|
||||
break;
|
||||
case 1:
|
||||
/*
|
||||
* Version 1 node data did not have a minor version number
|
||||
* field.
|
||||
*/
|
||||
oldNodeData.setMinorVersion(MINOR_VERSION);
|
||||
nodeData = oldNodeData;
|
||||
break;
|
||||
default:
|
||||
nodeData = oldNodeData;
|
||||
break;
|
||||
}
|
||||
writeCaseNodeData(nodeData);
|
||||
return nodeData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the metadata for a case.
|
||||
*
|
||||
* @param nodePath The case directory coordination service node path for the
|
||||
* case.
|
||||
*
|
||||
* @return The case metadata.
|
||||
*
|
||||
* @throws CaseNodeDataException If the case metadata file or the case
|
||||
* directory does not exist.
|
||||
* @throws CaseMetadataException If the case metadata cannot be read.
|
||||
*/
|
||||
private static CaseMetadata getCaseMetadata(String nodePath) throws CaseNodeDataException, CaseMetadataException {
|
||||
final Path caseDirectoryPath = Paths.get(nodePath);
|
||||
final File caseDirectory = caseDirectoryPath.toFile();
|
||||
if (!caseDirectory.exists()) {
|
||||
throw new CaseNodeDataException("Case directory does not exist"); // NON-NLS
|
||||
}
|
||||
final Path metadataFilePath = CaseMetadata.getCaseMetadataFilePath(caseDirectoryPath);
|
||||
if (metadataFilePath == null) {
|
||||
throw new CaseNodeDataException("Case meta data file does not exist"); // NON-NLS
|
||||
}
|
||||
return new CaseMetadata(metadataFilePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses case metadata to construct the case data to store in a case
|
||||
* directory coordination service node.
|
||||
*
|
||||
* @param metadata The case meta data.
|
||||
*
|
||||
* @throws java.text.ParseException If there is an error parsing dates from
|
||||
* string representations of dates in the
|
||||
* meta data.
|
||||
* @throws ParseException If there is an error parsing dates from string
|
||||
* representations of dates in the meta data.
|
||||
*/
|
||||
public CaseNodeData(CaseMetadata metadata) throws ParseException {
|
||||
this.version = CURRENT_VERSION;
|
||||
private CaseNodeData(CaseMetadata metadata) throws ParseException {
|
||||
this.version = MAJOR_VERSION;
|
||||
this.errorsOccurred = false;
|
||||
this.directory = Paths.get(metadata.getCaseDirectory());
|
||||
this.createDate = CaseMetadata.getDateFormat().parse(metadata.getCreatedDate());
|
||||
@@ -82,51 +253,64 @@ public final class CaseNodeData {
|
||||
this.name = metadata.getCaseName();
|
||||
this.displayName = metadata.getCaseDisplayName();
|
||||
this.deletedItemFlags = 0;
|
||||
this.minorVersion = MINOR_VERSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses coordination service node data to construct an object that converts
|
||||
* data for a case directory lock coordination service node to and from byte
|
||||
* arrays.
|
||||
* Uses the raw bytes from a case directory coordination service node to
|
||||
* construct a case node data object.
|
||||
*
|
||||
* @param nodeData The raw bytes received from the coordination service.
|
||||
*
|
||||
* @throws IOException If there is an error reading the node data.
|
||||
*/
|
||||
public CaseNodeData(byte[] nodeData) throws IOException {
|
||||
private CaseNodeData(byte[] nodeData) throws IOException {
|
||||
if (nodeData == null || nodeData.length == 0) {
|
||||
throw new IOException(null == nodeData ? "Null node data byte array" : "Zero-length node data byte array");
|
||||
}
|
||||
DataInputStream inputStream = new DataInputStream(new ByteArrayInputStream(nodeData));
|
||||
this.version = inputStream.readInt();
|
||||
if (this.version > 0) {
|
||||
this.errorsOccurred = inputStream.readBoolean();
|
||||
} else {
|
||||
short legacyErrorsOccurred = inputStream.readByte();
|
||||
this.errorsOccurred = (legacyErrorsOccurred < 0);
|
||||
}
|
||||
if (this.version > 0) {
|
||||
this.directory = Paths.get(inputStream.readUTF());
|
||||
this.createDate = new Date(inputStream.readLong());
|
||||
this.lastAccessDate = new Date(inputStream.readLong());
|
||||
this.name = inputStream.readUTF();
|
||||
this.displayName = inputStream.readUTF();
|
||||
this.deletedItemFlags = inputStream.readShort();
|
||||
try (ByteArrayInputStream byteStream = new ByteArrayInputStream(nodeData); DataInputStream inputStream = new DataInputStream(byteStream)) {
|
||||
this.version = inputStream.readInt();
|
||||
if (this.version == 1) {
|
||||
this.errorsOccurred = inputStream.readBoolean();
|
||||
} else {
|
||||
byte errorsOccurredByte = inputStream.readByte();
|
||||
this.errorsOccurred = (errorsOccurredByte < 0);
|
||||
}
|
||||
if (this.version > 0) {
|
||||
this.directory = Paths.get(inputStream.readUTF());
|
||||
this.createDate = new Date(inputStream.readLong());
|
||||
this.lastAccessDate = new Date(inputStream.readLong());
|
||||
this.name = inputStream.readUTF();
|
||||
this.displayName = inputStream.readUTF();
|
||||
this.deletedItemFlags = inputStream.readShort();
|
||||
}
|
||||
if (this.version > 1) {
|
||||
this.minorVersion = inputStream.readInt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the node data version number of this node.
|
||||
* Gets the version number of this node data.
|
||||
*
|
||||
* @return The version number.
|
||||
*/
|
||||
public int getVersion() {
|
||||
private int getVersion() {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the minor version number of this node data.
|
||||
*
|
||||
* @param minorVersion The version number.
|
||||
*/
|
||||
private void setMinorVersion(int minorVersion) {
|
||||
this.minorVersion = minorVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether or not any errors occurred during the processing of any auto
|
||||
* ingest job for the case represented by this node data.
|
||||
* ingest job for the case.
|
||||
*
|
||||
* @return True or false.
|
||||
*/
|
||||
@@ -136,7 +320,7 @@ public final class CaseNodeData {
|
||||
|
||||
/**
|
||||
* Sets whether or not any errors occurred during the processing of any auto
|
||||
* ingest job for the case represented by this node data.
|
||||
* ingest job for the case.
|
||||
*
|
||||
* @param errorsOccurred True or false.
|
||||
*/
|
||||
@@ -145,8 +329,7 @@ public final class CaseNodeData {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the path of the case directory of the case represented by this node
|
||||
* data.
|
||||
* Gets the path of the case directory.
|
||||
*
|
||||
* @return The case directory path.
|
||||
*/
|
||||
@@ -155,17 +338,7 @@ public final class CaseNodeData {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the path of the case directory of the case represented by this node
|
||||
* data.
|
||||
*
|
||||
* @param caseDirectory The case directory path.
|
||||
*/
|
||||
public void setDirectory(Path caseDirectory) {
|
||||
this.directory = caseDirectory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the date the case represented by this node data was created.
|
||||
* Gets the date the case was created.
|
||||
*
|
||||
* @return The create date.
|
||||
*/
|
||||
@@ -174,16 +347,7 @@ public final class CaseNodeData {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the date the case represented by this node data was created.
|
||||
*
|
||||
* @param createDate The create date.
|
||||
*/
|
||||
public void setCreateDate(Date createDate) {
|
||||
this.createDate = new Date(createDate.getTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the date the case represented by this node data last accessed.
|
||||
* Gets the date the case was last accessed.
|
||||
*
|
||||
* @return The last access date.
|
||||
*/
|
||||
@@ -192,7 +356,7 @@ public final class CaseNodeData {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the date the case represented by this node data was last accessed.
|
||||
* Sets the date the case was last accessed.
|
||||
*
|
||||
* @param lastAccessDate The last access date.
|
||||
*/
|
||||
@@ -201,8 +365,7 @@ public final class CaseNodeData {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the unique and immutable (user cannot change it) name of the case
|
||||
* represented by this node data.
|
||||
* Gets the unique and immutable name of the case.
|
||||
*
|
||||
* @return The case name.
|
||||
*/
|
||||
@@ -211,17 +374,7 @@ public final class CaseNodeData {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the unique and immutable (user cannot change it) name of the case
|
||||
* represented by this node data.
|
||||
*
|
||||
* @param name The case name.
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the display name of the case represented by this node data.
|
||||
* Gets the display name of the case.
|
||||
*
|
||||
* @return The case display name.
|
||||
*/
|
||||
@@ -230,7 +383,7 @@ public final class CaseNodeData {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the display name of the case represented by this node data.
|
||||
* Sets the display name of the case.
|
||||
*
|
||||
* @param displayName The case display name.
|
||||
*/
|
||||
@@ -239,19 +392,18 @@ public final class CaseNodeData {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a deleted item flag is set for the case represented by
|
||||
* this node data.
|
||||
* Checks whether a given deleted item flag is set for the case.
|
||||
*
|
||||
* @param flag The flag to check.
|
||||
*
|
||||
* @return
|
||||
* @return True or false.
|
||||
*/
|
||||
public boolean isDeletedFlagSet(DeletedFlags flag) {
|
||||
return (this.deletedItemFlags & flag.getValue()) == flag.getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a deleted item flag for the case represented by this node data.
|
||||
* Sets a given deleted item flag.
|
||||
*
|
||||
* @param flag The flag to set.
|
||||
*/
|
||||
@@ -265,22 +417,24 @@ public final class CaseNodeData {
|
||||
*
|
||||
* @return The node data as a byte array.
|
||||
*
|
||||
* @throws IOException If there is an error writing the node data.
|
||||
* @throws IOException If there is an error writing the node data to the
|
||||
* array.
|
||||
*/
|
||||
public byte[] toArray() throws IOException {
|
||||
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
|
||||
DataOutputStream outputStream = new DataOutputStream(byteStream);
|
||||
outputStream.writeInt(this.version);
|
||||
outputStream.writeBoolean(this.errorsOccurred);
|
||||
outputStream.writeUTF(this.directory.toString());
|
||||
outputStream.writeLong(this.createDate.getTime());
|
||||
outputStream.writeLong(this.lastAccessDate.getTime());
|
||||
outputStream.writeUTF(this.name);
|
||||
outputStream.writeUTF(this.displayName);
|
||||
outputStream.writeShort(this.deletedItemFlags);
|
||||
outputStream.flush();
|
||||
byteStream.flush();
|
||||
return byteStream.toByteArray();
|
||||
private byte[] toArray() throws IOException {
|
||||
try (ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); DataOutputStream outputStream = new DataOutputStream(byteStream)) {
|
||||
outputStream.writeInt(this.version);
|
||||
outputStream.writeByte((byte) (this.errorsOccurred ? 0x80 : 0));
|
||||
outputStream.writeUTF(this.directory.toString());
|
||||
outputStream.writeLong(this.createDate.getTime());
|
||||
outputStream.writeLong(this.lastAccessDate.getTime());
|
||||
outputStream.writeUTF(this.name);
|
||||
outputStream.writeUTF(this.displayName);
|
||||
outputStream.writeShort(this.deletedItemFlags);
|
||||
outputStream.writeInt(this.minorVersion);
|
||||
outputStream.flush();
|
||||
byteStream.flush();
|
||||
return byteStream.toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -316,4 +470,34 @@ public final class CaseNodeData {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Exception thrown when there is an error reading or writing case node
|
||||
* data.
|
||||
*/
|
||||
public static final class CaseNodeDataException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Constructs an exception to throw when there is an error reading or
|
||||
* writing case node data.
|
||||
*
|
||||
* @param message The exception message.
|
||||
*/
|
||||
private CaseNodeDataException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an exception to throw when there is an error reading or
|
||||
* writing case node data.
|
||||
*
|
||||
* @param message The exception message.
|
||||
* @param cause The cause of the exception.
|
||||
*/
|
||||
private CaseNodeDataException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+23
-124
@@ -18,31 +18,28 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.casemodule.multiusercases;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.LinkOption;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import org.sleuthkit.autopsy.casemodule.CaseMetadata;
|
||||
import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData.CaseNodeDataException;
|
||||
import static org.sleuthkit.autopsy.casemodule.multiusercases.CoordinationServiceUtils.isCaseAutoIngestLogNodePath;
|
||||
import static org.sleuthkit.autopsy.casemodule.multiusercases.CoordinationServiceUtils.isCaseNameNodePath;
|
||||
import static org.sleuthkit.autopsy.casemodule.multiusercases.CoordinationServiceUtils.isCaseResourcesNodePath;
|
||||
import org.sleuthkit.autopsy.coordinationservice.CoordinationService;
|
||||
import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CoordinationServiceException;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
|
||||
/**
|
||||
* Queries the coordination service to collect the multi-user case node data
|
||||
* stored in the case directory lock ZooKeeper nodes.
|
||||
* Collects the multi-user case node data stored in the case directory
|
||||
* coordination service nodes.
|
||||
*/
|
||||
final public class CaseNodeDataCollector {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(CaseNodeDataCollector.class.getName());
|
||||
|
||||
/**
|
||||
* Queries the coordination service to collect the multi-user case node data
|
||||
* stored in the case directory lock ZooKeeper nodes.
|
||||
* Collects the multi-user case node data stored in the case directory
|
||||
* coordination service nodes.
|
||||
*
|
||||
* @return The node data for the multi-user cases known to the coordination
|
||||
* service.
|
||||
@@ -54,128 +51,30 @@ final public class CaseNodeDataCollector {
|
||||
* service.
|
||||
*/
|
||||
public static List<CaseNodeData> getNodeData() throws CoordinationServiceException, InterruptedException {
|
||||
final List<CaseNodeData> cases = new ArrayList<>();
|
||||
final List<CaseNodeData> nodeDataList = new ArrayList<>();
|
||||
final CoordinationService coordinationService = CoordinationService.getInstance();
|
||||
final List<String> nodeList = coordinationService.getNodeList(CoordinationService.CategoryNode.CASES);
|
||||
for (String nodeName : nodeList) {
|
||||
if (CoordinationServiceUtils.isCaseNameNodePath(nodeName)
|
||||
|| CoordinationServiceUtils.isCaseResourcesNodePath(nodeName)
|
||||
|| CoordinationServiceUtils.isCaseAutoIngestLogNodePath(nodeName)) {
|
||||
final List<String> nodePaths = coordinationService.getNodeList(CoordinationService.CategoryNode.CASES);
|
||||
for (String nodePath : nodePaths) {
|
||||
/*
|
||||
* Skip the case name, case resources, and case auto ingest log
|
||||
* coordination service nodes. They are not used to store case data.
|
||||
*/
|
||||
if (isCaseNameNodePath(nodePath) || isCaseResourcesNodePath(nodePath) || isCaseAutoIngestLogNodePath(nodePath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the data from the case directory lock node. This data may not
|
||||
* exist or may exist only in an older version. If it is missing or
|
||||
* incomplete, create or update it.
|
||||
* Get the case node data from the case directory coordination service node.
|
||||
*/
|
||||
try {
|
||||
CaseNodeData nodeData;
|
||||
final byte[] nodeBytes = coordinationService.getNodeData(CoordinationService.CategoryNode.CASES, nodeName);
|
||||
if (nodeBytes != null && nodeBytes.length > 0) {
|
||||
nodeData = new CaseNodeData(nodeBytes);
|
||||
if (nodeData.getVersion() < CaseNodeData.getCurrentVersion()) {
|
||||
nodeData = updateNodeData(nodeName, nodeData);
|
||||
}
|
||||
} else {
|
||||
nodeData = updateNodeData(nodeName, null);
|
||||
}
|
||||
if (nodeData != null) {
|
||||
cases.add(nodeData);
|
||||
}
|
||||
|
||||
} catch (CoordinationService.CoordinationServiceException | InterruptedException | IOException | ParseException | CaseMetadata.CaseMetadataException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Error getting coordination service node data for %s", nodeName), ex);
|
||||
final CaseNodeData nodeData = CaseNodeData.readCaseNodeData(nodePath);
|
||||
nodeDataList.add(nodeData);
|
||||
} catch (CaseNodeDataException | InterruptedException ex) {
|
||||
logger.log(Level.WARNING, String.format("Error reading case node data from %s", nodePath), ex);
|
||||
}
|
||||
|
||||
}
|
||||
return cases;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the case directory lock coordination service node data for a
|
||||
* case.
|
||||
*
|
||||
* @param nodeName The coordination service node name, i.e., the case
|
||||
* directory path.
|
||||
* @param oldNodeData The node data to be updated.
|
||||
*
|
||||
* @return A CaseNodedata object or null if the coordination service node is
|
||||
* an "orphan" with no corresponding case directry.
|
||||
*
|
||||
* @throws IOException If there is an error writing the
|
||||
* node data to a byte array.
|
||||
* @throws CaseMetadataException If there is an error reading the
|
||||
* case metadata file.
|
||||
* @throws ParseException If there is an error parsing a date
|
||||
* from the case metadata file.
|
||||
* @throws CoordinationServiceException If there is an error interacting
|
||||
* with the coordination service.
|
||||
* @throws InterruptedException If a coordination service operation
|
||||
* is interrupted.
|
||||
*/
|
||||
private static CaseNodeData updateNodeData(String nodeName, CaseNodeData oldNodeData) throws IOException, CaseMetadata.CaseMetadataException, ParseException, CoordinationService.CoordinationServiceException, InterruptedException {
|
||||
Path caseDirectoryPath = Paths.get(nodeName).toRealPath(LinkOption.NOFOLLOW_LINKS);
|
||||
File caseDirectory = caseDirectoryPath.toFile();
|
||||
if (!caseDirectory.exists()) {
|
||||
logger.log(Level.WARNING, String.format("Found orphan coordination service node %s, attempting clean up", caseDirectoryPath));
|
||||
deleteLockNodes(CoordinationService.getInstance(), caseDirectoryPath);
|
||||
return null;
|
||||
}
|
||||
|
||||
CaseNodeData nodeData = null;
|
||||
if (oldNodeData == null || oldNodeData.getVersion() == 0) {
|
||||
File[] files = caseDirectory.listFiles();
|
||||
for (File file : files) {
|
||||
String name = file.getName().toLowerCase();
|
||||
if (name.endsWith(CaseMetadata.getFileExtension())) {
|
||||
CaseMetadata metadata = new CaseMetadata(Paths.get(file.getAbsolutePath()));
|
||||
nodeData = new CaseNodeData(metadata);
|
||||
if (oldNodeData != null) {
|
||||
/*
|
||||
* Version 0 case node data was only written if errors
|
||||
* occurred during an auto ingest job.
|
||||
*/
|
||||
nodeData.setErrorsOccurred(true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nodeData != null) {
|
||||
CoordinationService.getInstance().setNodeData(CoordinationService.CategoryNode.CASES, nodeName, nodeData.toArray());
|
||||
}
|
||||
|
||||
return nodeData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to delete the coordination service lock nodes for a case,
|
||||
* logging any failures.
|
||||
*
|
||||
* @param coordinationService The coordination service.
|
||||
* @param caseDirectoryPath The case directory path.
|
||||
*/
|
||||
private static void deleteLockNodes(CoordinationService coordinationService, Path caseDirectoryPath) {
|
||||
deleteCoordinationServiceNode(coordinationService, CoordinationServiceUtils.getCaseResourcesNodePath(caseDirectoryPath));
|
||||
deleteCoordinationServiceNode(coordinationService, CoordinationServiceUtils.getCaseAutoIngestLogNodePath(caseDirectoryPath));
|
||||
deleteCoordinationServiceNode(coordinationService, CoordinationServiceUtils.getCaseDirectoryNodePath(caseDirectoryPath));
|
||||
deleteCoordinationServiceNode(coordinationService, CoordinationServiceUtils.getCaseNameNodePath(caseDirectoryPath));
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to delete a coordination service node, logging failure.
|
||||
*
|
||||
* @param coordinationService The coordination service.
|
||||
* @param nodeName A node name.
|
||||
*/
|
||||
private static void deleteCoordinationServiceNode(CoordinationService coordinationService, String nodeName) {
|
||||
try {
|
||||
coordinationService.deleteNode(CoordinationService.CategoryNode.CASES, nodeName);
|
||||
} catch (CoordinationService.CoordinationServiceException | InterruptedException ex) {
|
||||
logger.log(Level.WARNING, String.format("Error deleting coordination service node %s", nodeName), ex);
|
||||
}
|
||||
return nodeDataList;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -183,5 +82,5 @@ final public class CaseNodeDataCollector {
|
||||
*/
|
||||
private CaseNodeDataCollector() {
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
+7
@@ -43,6 +43,8 @@ import org.sleuthkit.autopsy.casemodule.multiusercasesbrowser.MultiUserCaseBrows
|
||||
public final class MultiUserCasesBrowserPanel extends javax.swing.JPanel implements ExplorerManager.Provider {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final int NAME_COLUMN_INDEX = 0;
|
||||
private static final int NAME_COLUMN_WIDTH = 150;
|
||||
private final ExplorerManager explorerManager;
|
||||
private final MultiUserCaseBrowserCustomizer customizer;
|
||||
private final OutlineView outlineView;
|
||||
@@ -103,6 +105,11 @@ public final class MultiUserCasesBrowserPanel extends javax.swing.JPanel impleme
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Give the case name column a greater width.
|
||||
*/
|
||||
outline.getColumnModel().getColumn(NAME_COLUMN_INDEX).setPreferredWidth(NAME_COLUMN_WIDTH);
|
||||
|
||||
/*
|
||||
* Hide the root node and configure the node selection mode.
|
||||
*/
|
||||
|
||||
+23
-7
@@ -26,13 +26,29 @@ DataContentViewerOtherCases.earliestCaseLabel.text=Central Repository Starting D
|
||||
DataContentViewerOtherCases.foundInLabel.text=
|
||||
DataContentViewerOtherCases.title=Other Occurrences
|
||||
DataContentViewerOtherCases.toolTip=Displays instances of the selected file/artifact from other occurrences.
|
||||
DataContentViewerOtherCasesModel.csvHeader.attribute=Matched Attribute
|
||||
DataContentViewerOtherCasesModel.csvHeader.case=Case
|
||||
DataContentViewerOtherCasesModel.csvHeader.comment=Comment
|
||||
DataContentViewerOtherCasesModel.csvHeader.dataSource=Data Source
|
||||
DataContentViewerOtherCasesModel.csvHeader.device=Device
|
||||
DataContentViewerOtherCasesModel.csvHeader.known=Known
|
||||
DataContentViewerOtherCasesModel.csvHeader.path=Path
|
||||
DataContentViewerOtherCasesModel.csvHeader.value=Attribute Value
|
||||
OccurrencePanel.caseCreatedDateLabel.text=Created Date:
|
||||
OccurrencePanel.caseDetails.text=Case Details
|
||||
OccurrencePanel.caseNameLabel.text=Name:
|
||||
OccurrencePanel.commonProperties.text=Common Properties
|
||||
OccurrencePanel.commonPropertyCommentLabel.text=Comment:
|
||||
OccurrencePanel.commonPropertyKnownStatusLabel.text=Known Status:
|
||||
OccurrencePanel.commonPropertyTypeLabel.text=Type:
|
||||
OccurrencePanel.commonPropertyValueLabel.text=Value:
|
||||
OccurrencePanel.dataSourceDetails.text=Data Source Details
|
||||
OccurrencePanel.dataSourceNameLabel.text=Name:
|
||||
OccurrencePanel.fileDetails.text=File Details
|
||||
OccurrencePanel.filePathLabel.text=File Path:
|
||||
OtherOccurrencesCasesTableModel.case=Case
|
||||
OtherOccurrencesCasesTableModel.noData=No Data.
|
||||
OtherOccurrencesFilesTableModel.attribute=Matched Attribute
|
||||
OtherOccurrencesFilesTableModel.comment=Comment
|
||||
OtherOccurrencesFilesTableModel.dataSource=Data Source
|
||||
OtherOccurrencesFilesTableModel.device=Device
|
||||
OtherOccurrencesFilesTableModel.known=Known
|
||||
OtherOccurrencesDataSourcesTableModel.dataSourceName=Data Source Name
|
||||
OtherOccurrencesDataSourcesTableModel.noData=No Data.
|
||||
OtherOccurrencesFilesTableModel.fileName=File Name
|
||||
OtherOccurrencesFilesTableModel.noData=No Data.
|
||||
OtherOccurrencesFilesTableModel.path=Path
|
||||
OtherOccurrencesFilesTableModel.value=Attribute Value
|
||||
|
||||
+101
-88
@@ -46,11 +46,11 @@
|
||||
</NonVisualComponents>
|
||||
<Properties>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[1500, 10]"/>
|
||||
<Dimension value="[600, 10]"/>
|
||||
</Property>
|
||||
<Property name="opaque" type="boolean" value="false"/>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[1500, 44]"/>
|
||||
<Dimension value="[600, 63]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
@@ -68,123 +68,114 @@
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="otherCasesPanel" alignment="0" pref="1500" max="32767" attributes="0"/>
|
||||
<Component id="tableContainerPanel" alignment="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="otherCasesPanel" max="32767" attributes="0"/>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="tableContainerPanel" pref="64" max="32767" attributes="0"/>
|
||||
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JPanel" name="otherCasesPanel">
|
||||
<Container class="javax.swing.JPanel" name="tableContainerPanel">
|
||||
<Properties>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[921, 62]"/>
|
||||
<Dimension value="[600, 63]"/>
|
||||
</Property>
|
||||
<Property name="requestFocusEnabled" type="boolean" value="false"/>
|
||||
</Properties>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<EmptySpace min="0" pref="921" max="32767" attributes="0"/>
|
||||
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="tableContainerPanel" max="32767" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="0" max="-2" attributes="0"/>
|
||||
<Group type="102" attributes="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<Component id="earliestCaseLabel" min="-2" pref="161" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
<Component id="earliestCaseDate" max="32767" attributes="0"/>
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
<Component id="foundInLabel" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<Component id="tablesViewerSplitPane" pref="590" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<EmptySpace min="0" pref="62" max="32767" attributes="0"/>
|
||||
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="tableContainerPanel" pref="62" max="32767" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="0" max="-2" attributes="0"/>
|
||||
<Group type="102" attributes="0">
|
||||
<EmptySpace min="-2" pref="0" max="-2" attributes="0"/>
|
||||
<Component id="tablesViewerSplitPane" pref="33" max="32767" attributes="0"/>
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="1" attributes="0">
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="earliestCaseLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="earliestCaseDate" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Component id="foundInLabel" min="-2" pref="14" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JPanel" name="tableContainerPanel">
|
||||
<Component class="javax.swing.JLabel" name="earliestCaseLabel">
|
||||
<Properties>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[1500, 63]"/>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle.properties" key="DataContentViewerOtherCases.earliestCaseLabel.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/centralrepository/contentviewer/Bundle.properties" key="DataContentViewerOtherCases.earliestCaseLabel.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="earliestCaseDate">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle.properties" key="DataContentViewerOtherCases.earliestCaseDate.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="foundInLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle.properties" key="DataContentViewerOtherCases.foundInLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Container class="javax.swing.JSplitPane" name="tablesViewerSplitPane">
|
||||
<Properties>
|
||||
<Property name="dividerLocation" type="int" value="450"/>
|
||||
<Property name="resizeWeight" type="double" value="0.5"/>
|
||||
</Properties>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<Component id="earliestCaseLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="earliestCaseDate" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="66" max="-2" attributes="0"/>
|
||||
<Component id="foundInLabel" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Component id="jSplitPane2" alignment="0" pref="911" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<Component id="jSplitPane2" pref="31" max="32767" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="earliestCaseLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="earliestCaseDate" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="foundInLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JLabel" name="earliestCaseLabel">
|
||||
<Container class="javax.swing.JSplitPane" name="caseDatasourceFileSplitPane">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle.properties" key="DataContentViewerOtherCases.earliestCaseLabel.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/centralrepository/contentviewer/Bundle.properties" key="DataContentViewerOtherCases.earliestCaseLabel.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="earliestCaseDate">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle.properties" key="DataContentViewerOtherCases.earliestCaseDate.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="foundInLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle.properties" key="DataContentViewerOtherCases.foundInLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Container class="javax.swing.JSplitPane" name="jSplitPane2">
|
||||
<Properties>
|
||||
<Property name="dividerLocation" type="int" value="470"/>
|
||||
<Property name="dividerLocation" type="int" value="300"/>
|
||||
<Property name="resizeWeight" type="double" value="0.6"/>
|
||||
<Property name="toolTipText" type="java.lang.String" value=""/>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
|
||||
<JSplitPaneConstraints position="left"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JSplitPane" name="jSplitPane3">
|
||||
<Container class="javax.swing.JSplitPane" name="caseDatasourceSplitPane">
|
||||
<Properties>
|
||||
<Property name="dividerLocation" type="int" value="150"/>
|
||||
<Property name="resizeWeight" type="double" value="0.5"/>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
|
||||
@@ -195,6 +186,11 @@
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JScrollPane" name="caseScrollPane">
|
||||
<Properties>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[140, 30]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
|
||||
</AuxValues>
|
||||
@@ -217,6 +213,11 @@
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Container class="javax.swing.JScrollPane" name="dataSourceScrollPane">
|
||||
<Properties>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[140, 30]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
|
||||
</AuxValues>
|
||||
@@ -231,11 +232,8 @@
|
||||
<Component class="javax.swing.JTable" name="dataSourcesTable">
|
||||
<Properties>
|
||||
<Property name="autoCreateRowSorter" type="boolean" value="true"/>
|
||||
<Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.editors2.TableModelEditor">
|
||||
<Table columnCount="2" rowCount="0">
|
||||
<Column editable="false" title="Data Source Name" type="java.lang.Object"/>
|
||||
<Column editable="false" title="Device ID" type="java.lang.Object"/>
|
||||
</Table>
|
||||
<Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||
<Connection code="dataSourcesTableModel" type="code"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
@@ -243,10 +241,10 @@
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Container class="javax.swing.JScrollPane" name="propertiesTableScrollPane">
|
||||
<Container class="javax.swing.JScrollPane" name="filesTableScrollPane">
|
||||
<Properties>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[1000, 30]"/>
|
||||
<Dimension value="[140, 30]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
@@ -261,7 +259,7 @@
|
||||
<Properties>
|
||||
<Property name="autoCreateRowSorter" type="boolean" value="true"/>
|
||||
<Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||
<Connection code="tableModel" type="code"/>
|
||||
<Connection code="filesTableModel" type="code"/>
|
||||
</Property>
|
||||
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/contentviewer/Bundle.properties" key="DataContentViewerOtherCases.table.toolTip.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
@@ -273,7 +271,7 @@
|
||||
<ComponentRef name="rightClickPopupMenu"/>
|
||||
</Property>
|
||||
<Property name="selectionModel" type="javax.swing.ListSelectionModel" editor="org.netbeans.modules.form.editors2.JTableSelectionModelEditor">
|
||||
<JTableSelectionModel selectionMode="1"/>
|
||||
<JTableSelectionModel selectionMode="0"/>
|
||||
</Property>
|
||||
<Property name="tableHeader" type="javax.swing.table.JTableHeader" editor="org.netbeans.modules.form.editors2.JTableHeaderEditor">
|
||||
<TableHeader reorderingAllowed="true" resizingAllowed="true"/>
|
||||
@@ -284,6 +282,21 @@
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Container class="javax.swing.JScrollPane" name="detailsPanelScrollPane">
|
||||
<Properties>
|
||||
<Property name="horizontalScrollBarPolicy" type="int" value="31"/>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[200, 100]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
|
||||
<JSplitPaneConstraints position="right"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
|
||||
+267
-223
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Central Repository
|
||||
*
|
||||
* Copyright 2015-2019 Basis Technology Corp.
|
||||
* Copyright 2017-2019 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -21,6 +21,8 @@ package org.sleuthkit.autopsy.centralrepository.contentviewer;
|
||||
import java.awt.Component;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.ComponentAdapter;
|
||||
import java.awt.event.ComponentEvent;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -48,9 +50,6 @@ import static javax.swing.JOptionPane.PLAIN_MESSAGE;
|
||||
import static javax.swing.JOptionPane.ERROR_MESSAGE;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.filechooser.FileNameExtensionFilter;
|
||||
import javax.swing.table.DefaultTableModel;
|
||||
import javax.swing.table.TableCellRenderer;
|
||||
import javax.swing.table.TableColumn;
|
||||
import javax.swing.table.TableModel;
|
||||
import javax.swing.table.TableRowSorter;
|
||||
import org.joda.time.DateTimeZone;
|
||||
@@ -94,49 +93,57 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
private static final Logger LOGGER = Logger.getLogger(DataContentViewerOtherCases.class.getName());
|
||||
private static final CorrelationCaseWrapper NO_ARTIFACTS_CASE = new CorrelationCaseWrapper(Bundle.DataContentViewerOtherCases_table_noArtifacts());
|
||||
private static final CorrelationCaseWrapper NO_RESULTS_CASE = new CorrelationCaseWrapper(Bundle.DataContentViewerOtherCases_table_noResultsFound());
|
||||
private static final int DEFAULT_MIN_CELL_WIDTH = 15;
|
||||
|
||||
private final OtherOccurrencesFilesTableModel tableModel;
|
||||
private final OtherOccurrencesFilesTableModel filesTableModel;
|
||||
private final OtherOccurrencesCasesTableModel casesTableModel;
|
||||
private final OtherOccurrencesDataSourcesTableModel dataSourcesTableModel;
|
||||
private OccurrencePanel occurrencePanel;
|
||||
private final Collection<CorrelationAttributeInstance> correlationAttributes;
|
||||
private String dataSourceName = "";
|
||||
private String deviceId = "";
|
||||
private String dataSourceName = ""; //the data source of the file which the content viewer is being populated for
|
||||
private String deviceId = ""; //the device id of the data source for the file which the content viewer is being populated for
|
||||
/**
|
||||
* Could be null.
|
||||
*/
|
||||
private AbstractFile file;
|
||||
private AbstractFile file; //the file which the content viewer is being populated for
|
||||
|
||||
/**
|
||||
* Creates new form DataContentViewerOtherCases
|
||||
*/
|
||||
public DataContentViewerOtherCases() {
|
||||
this.tableModel = new OtherOccurrencesFilesTableModel();
|
||||
this.filesTableModel = new OtherOccurrencesFilesTableModel();
|
||||
this.casesTableModel = new OtherOccurrencesCasesTableModel();
|
||||
this.dataSourcesTableModel = new OtherOccurrencesDataSourcesTableModel();
|
||||
this.correlationAttributes = new ArrayList<>();
|
||||
|
||||
occurrencePanel = new OccurrencePanel();
|
||||
initComponents();
|
||||
customizeComponents();
|
||||
|
||||
detailsPanelScrollPane.addComponentListener(new ComponentAdapter() {
|
||||
@Override
|
||||
public void componentResized(ComponentEvent componentEvent) {
|
||||
//when its resized make sure the width of the panel resizes to match the scroll pane width to avoid a horizontal scroll bar
|
||||
occurrencePanel.setPreferredSize(new java.awt.Dimension(detailsPanelScrollPane.getPreferredSize().width, occurrencePanel.getPreferredSize().height));
|
||||
detailsPanelScrollPane.setViewportView(occurrencePanel);
|
||||
}
|
||||
});
|
||||
reset();
|
||||
}
|
||||
|
||||
private void customizeComponents() {
|
||||
ActionListener actList = new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
JMenuItem jmi = (JMenuItem) e.getSource();
|
||||
if (jmi.equals(selectAllMenuItem)) {
|
||||
filesTable.selectAll();
|
||||
} else if (jmi.equals(showCaseDetailsMenuItem)) {
|
||||
showCaseDetails(filesTable.getSelectedRow());
|
||||
} else if (jmi.equals(exportToCSVMenuItem)) {
|
||||
try {
|
||||
saveToCSV();
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Exception while getting open case.", ex); // NON-NLS
|
||||
}
|
||||
} else if (jmi.equals(showCommonalityMenuItem)) {
|
||||
showCommonalityDetails();
|
||||
ActionListener actList = (ActionEvent e) -> {
|
||||
JMenuItem jmi = (JMenuItem) e.getSource();
|
||||
if (jmi.equals(selectAllMenuItem)) {
|
||||
filesTable.selectAll();
|
||||
} else if (jmi.equals(showCaseDetailsMenuItem)) {
|
||||
showCaseDetails(filesTable.getSelectedRow());
|
||||
} else if (jmi.equals(exportToCSVMenuItem)) {
|
||||
try {
|
||||
saveToCSV();
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Exception while getting open case.", ex); // NON-NLS
|
||||
}
|
||||
} else if (jmi.equals(showCommonalityMenuItem)) {
|
||||
showCommonalityDetails();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -145,10 +152,6 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
showCaseDetailsMenuItem.addActionListener(actList);
|
||||
showCommonalityMenuItem.addActionListener(actList);
|
||||
|
||||
// Set background of every nth row as light grey.
|
||||
TableCellRenderer renderer = new OtherOccurrencesFilesTableCellRenderer();
|
||||
filesTable.setDefaultRenderer(Object.class, renderer);
|
||||
|
||||
// Configure column sorting.
|
||||
TableRowSorter<TableModel> sorter = new TableRowSorter<>(filesTable.getModel());
|
||||
filesTable.setRowSorter(sorter);
|
||||
@@ -162,8 +165,18 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
updateOnDataSourceSelection();
|
||||
}
|
||||
});
|
||||
|
||||
//alows resizing of the 4th section
|
||||
filesTable.getSelectionModel().addListSelectionListener((e) -> {
|
||||
if (Case.isCaseOpen()) {
|
||||
occurrencePanel = new OccurrencePanel();
|
||||
updateOnFileSelection();
|
||||
}
|
||||
});
|
||||
//sort tables alphabetically initially
|
||||
casesTable.getRowSorter().toggleSortOrder(0);
|
||||
dataSourcesTable.getRowSorter().toggleSortOrder(0);
|
||||
filesTable.getRowSorter().toggleSortOrder(0);
|
||||
}
|
||||
|
||||
@Messages({"DataContentViewerOtherCases.correlatedArtifacts.isEmpty=There are no files or artifacts to correlate.",
|
||||
@@ -218,54 +231,43 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
"DataContentViewerOtherCases.caseDetailsDialog.noCaseNameError=Error",
|
||||
"DataContentViewerOtherCases.noOpenCase.errMsg=No open case available."})
|
||||
private void showCaseDetails(int selectedRowViewIdx) {
|
||||
|
||||
String caseDisplayName = Bundle.DataContentViewerOtherCases_caseDetailsDialog_noCaseNameError();
|
||||
String details = Bundle.DataContentViewerOtherCases_caseDetailsDialog_noDetails();
|
||||
try {
|
||||
if (-1 != selectedRowViewIdx) {
|
||||
EamDb dbManager = EamDb.getInstance();
|
||||
int selectedRowModelIdx = filesTable.convertRowIndexToModel(selectedRowViewIdx);
|
||||
OtherOccurrenceNodeInstanceData nodeData = (OtherOccurrenceNodeInstanceData) tableModel.getRow(selectedRowModelIdx);
|
||||
CorrelationCase eamCasePartial = nodeData.getCorrelationAttributeInstance().getCorrelationCase();
|
||||
if (eamCasePartial == null) {
|
||||
JOptionPane.showConfirmDialog(showCaseDetailsMenuItem,
|
||||
Bundle.DataContentViewerOtherCases_caseDetailsDialog_noDetailsReference(),
|
||||
caseDisplayName,
|
||||
DEFAULT_OPTION, PLAIN_MESSAGE);
|
||||
return;
|
||||
List<OtherOccurrenceNodeData> rowList = filesTableModel.getListOfNodesForFile(selectedRowModelIdx);
|
||||
if (!rowList.isEmpty()) {
|
||||
if (rowList.get(0) instanceof OtherOccurrenceNodeInstanceData) {
|
||||
CorrelationCase eamCasePartial = ((OtherOccurrenceNodeInstanceData) rowList.get(0)).getCorrelationAttributeInstance().getCorrelationCase();
|
||||
caseDisplayName = eamCasePartial.getDisplayName();
|
||||
// query case details
|
||||
CorrelationCase eamCase = dbManager.getCaseByUUID(eamCasePartial.getCaseUUID());
|
||||
if (eamCase != null) {
|
||||
details = eamCase.getCaseDetailsOptionsPaneDialog();
|
||||
} else {
|
||||
details = Bundle.DataContentViewerOtherCases_caseDetailsDialog_noDetails();
|
||||
}
|
||||
} else {
|
||||
details = Bundle.DataContentViewerOtherCases_caseDetailsDialog_notSelected();
|
||||
}
|
||||
} else {
|
||||
details = Bundle.DataContentViewerOtherCases_caseDetailsDialog_noDetailsReference();
|
||||
}
|
||||
caseDisplayName = eamCasePartial.getDisplayName();
|
||||
// query case details
|
||||
CorrelationCase eamCase = dbManager.getCaseByUUID(eamCasePartial.getCaseUUID());
|
||||
if (eamCase == null) {
|
||||
JOptionPane.showConfirmDialog(showCaseDetailsMenuItem,
|
||||
Bundle.DataContentViewerOtherCases_caseDetailsDialog_noDetails(),
|
||||
caseDisplayName,
|
||||
DEFAULT_OPTION, PLAIN_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
// display case details
|
||||
JOptionPane.showConfirmDialog(showCaseDetailsMenuItem,
|
||||
eamCase.getCaseDetailsOptionsPaneDialog(),
|
||||
caseDisplayName,
|
||||
DEFAULT_OPTION, PLAIN_MESSAGE);
|
||||
} else {
|
||||
JOptionPane.showConfirmDialog(showCaseDetailsMenuItem,
|
||||
Bundle.DataContentViewerOtherCases_caseDetailsDialog_notSelected(),
|
||||
caseDisplayName,
|
||||
DEFAULT_OPTION, PLAIN_MESSAGE);
|
||||
}
|
||||
} catch (EamDbException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Error loading case details", ex);
|
||||
} finally {
|
||||
JOptionPane.showConfirmDialog(showCaseDetailsMenuItem,
|
||||
Bundle.DataContentViewerOtherCases_caseDetailsDialog_noDetails(),
|
||||
details,
|
||||
caseDisplayName,
|
||||
DEFAULT_OPTION, PLAIN_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
private void saveToCSV() throws NoCurrentCaseException {
|
||||
if (0 != filesTable.getSelectedRowCount()) {
|
||||
if (casesTableModel.getRowCount() > 0) {
|
||||
Calendar now = Calendar.getInstance();
|
||||
String fileName = String.format("%1$tY%1$tm%1$te%1$tI%1$tM%1$tS_other_data_sources.csv", now);
|
||||
CSVFileChooser.setCurrentDirectory(new File(Case.getCurrentCaseThrows().getExportDirectory()));
|
||||
@@ -279,45 +281,46 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
if (!selectedFile.getName().endsWith(".csv")) { // NON-NLS
|
||||
selectedFile = new File(selectedFile.toString() + ".csv"); // NON-NLS
|
||||
}
|
||||
|
||||
writeSelectedRowsToFileAsCSV(selectedFile);
|
||||
writeOtherOccurrencesToFileAsCSV(selectedFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void writeSelectedRowsToFileAsCSV(File destFile) {
|
||||
StringBuilder content;
|
||||
int[] selectedRowViewIndices = filesTable.getSelectedRows();
|
||||
int colCount = tableModel.getColumnCount();
|
||||
|
||||
@Messages({
|
||||
"DataContentViewerOtherCasesModel.csvHeader.case=Case",
|
||||
"DataContentViewerOtherCasesModel.csvHeader.device=Device",
|
||||
"DataContentViewerOtherCasesModel.csvHeader.dataSource=Data Source",
|
||||
"DataContentViewerOtherCasesModel.csvHeader.attribute=Matched Attribute",
|
||||
"DataContentViewerOtherCasesModel.csvHeader.value=Attribute Value",
|
||||
"DataContentViewerOtherCasesModel.csvHeader.known=Known",
|
||||
"DataContentViewerOtherCasesModel.csvHeader.path=Path",
|
||||
"DataContentViewerOtherCasesModel.csvHeader.comment=Comment"
|
||||
})
|
||||
/**
|
||||
* Write data for all cases in the content viewer to a CSV file
|
||||
*/
|
||||
private void writeOtherOccurrencesToFileAsCSV(File destFile) {
|
||||
try (BufferedWriter writer = Files.newBufferedWriter(destFile.toPath())) {
|
||||
|
||||
// write column names
|
||||
content = new StringBuilder("");
|
||||
for (int colIdx = 0; colIdx < colCount; colIdx++) {
|
||||
content.append('"').append(tableModel.getColumnName(colIdx)).append('"');
|
||||
if (colIdx < (colCount - 1)) {
|
||||
content.append(",");
|
||||
//write headers
|
||||
StringBuilder headers = new StringBuilder("\"");
|
||||
headers.append(Bundle.DataContentViewerOtherCasesModel_csvHeader_case())
|
||||
.append(OtherOccurrenceNodeInstanceData.getCsvItemSeparator()).append(Bundle.DataContentViewerOtherCasesModel_csvHeader_dataSource())
|
||||
.append(OtherOccurrenceNodeInstanceData.getCsvItemSeparator()).append(Bundle.DataContentViewerOtherCasesModel_csvHeader_attribute())
|
||||
.append(OtherOccurrenceNodeInstanceData.getCsvItemSeparator()).append(Bundle.DataContentViewerOtherCasesModel_csvHeader_value())
|
||||
.append(OtherOccurrenceNodeInstanceData.getCsvItemSeparator()).append(Bundle.DataContentViewerOtherCasesModel_csvHeader_known())
|
||||
.append(OtherOccurrenceNodeInstanceData.getCsvItemSeparator()).append(Bundle.DataContentViewerOtherCasesModel_csvHeader_path())
|
||||
.append(OtherOccurrenceNodeInstanceData.getCsvItemSeparator()).append(Bundle.DataContentViewerOtherCasesModel_csvHeader_comment())
|
||||
.append('"').append(System.getProperty("line.separator"));
|
||||
writer.write(headers.toString());
|
||||
//write content
|
||||
for (CorrelationAttributeInstance corAttr : correlationAttributes) {
|
||||
Map<UniquePathKey, OtherOccurrenceNodeInstanceData> correlatedNodeDataMap = new HashMap<>(0);
|
||||
// get correlation and reference set instances from DB
|
||||
correlatedNodeDataMap.putAll(getCorrelatedInstances(corAttr, dataSourceName, deviceId));
|
||||
for (OtherOccurrenceNodeInstanceData nodeData : correlatedNodeDataMap.values()) {
|
||||
writer.write(nodeData.toCsvString());
|
||||
}
|
||||
}
|
||||
|
||||
content.append(System.getProperty("line.separator"));
|
||||
writer.write(content.toString());
|
||||
|
||||
// write rows
|
||||
for (int rowViewIdx : selectedRowViewIndices) {
|
||||
content = new StringBuilder("");
|
||||
for (int colIdx = 0; colIdx < colCount; colIdx++) {
|
||||
int rowModelIdx = filesTable.convertRowIndexToModel(rowViewIdx);
|
||||
content.append('"').append(tableModel.getValueAt(rowModelIdx, colIdx)).append('"');
|
||||
if (colIdx < (colCount - 1)) {
|
||||
content.append(",");
|
||||
}
|
||||
}
|
||||
content.append(System.getProperty("line.separator"));
|
||||
writer.write(content.toString());
|
||||
}
|
||||
|
||||
} catch (IOException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Error writing selected rows to CSV.", ex);
|
||||
}
|
||||
@@ -329,11 +332,15 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
private void reset() {
|
||||
// start with empty table
|
||||
casesTableModel.clearTable();
|
||||
((DefaultTableModel) dataSourcesTable.getModel()).setRowCount(0);
|
||||
tableModel.clearTable();
|
||||
dataSourcesTableModel.clearTable();
|
||||
filesTableModel.clearTable();
|
||||
correlationAttributes.clear();
|
||||
earliestCaseDate.setText(Bundle.DataContentViewerOtherCases_earliestCaseNotAvailable());
|
||||
foundInLabel.setText("");
|
||||
//calling getPreferredSize has a side effect of ensuring it has a preferred size which reflects the contents which are visible
|
||||
occurrencePanel = new OccurrencePanel();
|
||||
occurrencePanel.getPreferredSize();
|
||||
detailsPanelScrollPane.setViewportView(occurrencePanel);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -364,6 +371,7 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
@Override
|
||||
public int isPreferred(Node node) {
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -558,7 +566,6 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
try {
|
||||
final Case openCase = Case.getCurrentCaseThrows();
|
||||
String caseUUID = openCase.getName();
|
||||
|
||||
HashMap<UniquePathKey, OtherOccurrenceNodeInstanceData> nodeDataMap = new HashMap<>();
|
||||
|
||||
if (EamDb.isEnabled()) {
|
||||
@@ -583,7 +590,6 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (corAttr.getCorrelationType().getDisplayName().equals("Files")) {
|
||||
List<AbstractFile> caseDbFiles = getCaseDbMatches(corAttr, openCase);
|
||||
|
||||
@@ -591,7 +597,6 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
addOrUpdateNodeData(openCase, nodeDataMap, caseDbFile);
|
||||
}
|
||||
}
|
||||
|
||||
return nodeDataMap;
|
||||
} catch (EamDbException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Error getting artifact instances from database.", ex); // NON-NLS
|
||||
@@ -748,14 +753,14 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
dataSources.add(makeDataSourceString(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID(), nodeData.getDeviceID(), nodeData.getDataSourceName()));
|
||||
caseNames.put(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID(), nodeData.getCorrelationAttributeInstance().getCorrelationCase());
|
||||
} catch (EamDbException ex) {
|
||||
LOGGER.log(Level.WARNING, "Unable to get correlation case for displaying other occurrence for case: " + nodeData.getCaseName());
|
||||
LOGGER.log(Level.WARNING, "Unable to get correlation case for displaying other occurrence for case: " + nodeData.getCaseName(), ex);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
dataSources.add(makeDataSourceString(Case.getCurrentCaseThrows().getName(), nodeData.getDeviceID(), nodeData.getDataSourceName()));
|
||||
caseNames.put(Case.getCurrentCaseThrows().getName(), new CorrelationCase(Case.getCurrentCaseThrows().getName(), Case.getCurrentCaseThrows().getDisplayName()));
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
LOGGER.log(Level.WARNING, "No current case open for other occurrences");
|
||||
LOGGER.log(Level.WARNING, "No current case open for other occurrences", ex);
|
||||
}
|
||||
}
|
||||
totalCount++;
|
||||
@@ -770,7 +775,6 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
} else if (caseCount == 0) {
|
||||
casesTableModel.addCorrelationCase(NO_RESULTS_CASE);
|
||||
}
|
||||
setColumnWidths();
|
||||
setEarliestCaseDate();
|
||||
foundInLabel.setText(String.format(Bundle.DataContentViewerOtherCases_foundIn_text(), totalCount, caseCount, dataSources.size()));
|
||||
if (caseCount > 0) {
|
||||
@@ -791,33 +795,40 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
*/
|
||||
private void updateOnCaseSelection() {
|
||||
int[] selectedCaseIndexes = casesTable.getSelectedRows();
|
||||
DefaultTableModel dataSourceModel = (DefaultTableModel) dataSourcesTable.getModel();
|
||||
dataSourceModel.setRowCount(0);
|
||||
tableModel.clearTable();
|
||||
for (CorrelationAttributeInstance corAttr : correlationAttributes) {
|
||||
Map<UniquePathKey, OtherOccurrenceNodeInstanceData> correlatedNodeDataMap = new HashMap<>(0);
|
||||
dataSourcesTableModel.clearTable();
|
||||
filesTableModel.clearTable();
|
||||
|
||||
// get correlation and reference set instances from DB
|
||||
correlatedNodeDataMap.putAll(getCorrelatedInstances(corAttr, dataSourceName, deviceId));
|
||||
for (OtherOccurrenceNodeInstanceData nodeData : correlatedNodeDataMap.values()) {
|
||||
for (int selectedRow : selectedCaseIndexes) {
|
||||
try {
|
||||
if (nodeData.isCentralRepoNode()) {
|
||||
if (casesTableModel.getCorrelationCase(casesTable.convertRowIndexToModel(selectedRow)) != null
|
||||
&& ((CorrelationCase) casesTableModel.getCorrelationCase(casesTable.convertRowIndexToModel(selectedRow))).getCaseUUID().equals(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID())) {
|
||||
dataSourceModel.addRow(new Object[]{nodeData.getDataSourceName(), nodeData.getDeviceID()});
|
||||
if (selectedCaseIndexes.length == 0) {
|
||||
//special case when no cases are selected
|
||||
occurrencePanel = new OccurrencePanel();
|
||||
occurrencePanel.getPreferredSize();
|
||||
detailsPanelScrollPane.setViewportView(occurrencePanel);
|
||||
} else {
|
||||
for (CorrelationAttributeInstance corAttr : correlationAttributes) {
|
||||
Map<UniquePathKey, OtherOccurrenceNodeInstanceData> correlatedNodeDataMap = new HashMap<>(0);
|
||||
|
||||
// get correlation and reference set instances from DB
|
||||
correlatedNodeDataMap.putAll(getCorrelatedInstances(corAttr, dataSourceName, deviceId));
|
||||
for (OtherOccurrenceNodeInstanceData nodeData : correlatedNodeDataMap.values()) {
|
||||
for (int selectedRow : selectedCaseIndexes) {
|
||||
try {
|
||||
if (nodeData.isCentralRepoNode()) {
|
||||
if (casesTableModel.getCorrelationCase(casesTable.convertRowIndexToModel(selectedRow)) != null
|
||||
&& casesTableModel.getCorrelationCase(casesTable.convertRowIndexToModel(selectedRow)).getCaseUUID().equals(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID())) {
|
||||
dataSourcesTableModel.addNodeData(nodeData);
|
||||
}
|
||||
} else {
|
||||
dataSourcesTableModel.addNodeData(nodeData);
|
||||
}
|
||||
} else {
|
||||
dataSourceModel.addRow(new Object[]{nodeData.getDataSourceName(), nodeData.getDeviceID()});
|
||||
} catch (EamDbException ex) {
|
||||
LOGGER.log(Level.WARNING, "Unable to get correlation attribute instance from OtherOccurrenceNodeInstanceData for case " + nodeData.getCaseName(), ex);
|
||||
}
|
||||
} catch (EamDbException ex) {
|
||||
LOGGER.log(Level.WARNING, "Unable to get correlation attribute instance from OtherOccurrenceNodeInstanceData for case " + nodeData.getCaseName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dataSourcesTable.getRowCount() > 0) {
|
||||
dataSourcesTable.setRowSelectionInterval(0, 0);
|
||||
if (dataSourcesTable.getRowCount() > 0) {
|
||||
dataSourcesTable.setRowSelectionInterval(0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -827,9 +838,8 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
*/
|
||||
private void updateOnDataSourceSelection() {
|
||||
int[] selectedCaseIndexes = casesTable.getSelectedRows();
|
||||
DefaultTableModel dataSourceModel = (DefaultTableModel) dataSourcesTable.getModel();
|
||||
int[] selectedDataSources = dataSourcesTable.getSelectedRows();
|
||||
tableModel.clearTable();
|
||||
filesTableModel.clearTable();
|
||||
for (CorrelationAttributeInstance corAttr : correlationAttributes) {
|
||||
Map<UniquePathKey, OtherOccurrenceNodeInstanceData> correlatedNodeDataMap = new HashMap<>(0);
|
||||
|
||||
@@ -841,43 +851,94 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
try {
|
||||
if (nodeData.isCentralRepoNode()) {
|
||||
if (casesTableModel.getCorrelationCase(casesTable.convertRowIndexToModel(selectedCaseRow)) != null
|
||||
&& ((CorrelationCase) casesTableModel.getCorrelationCase(casesTable.convertRowIndexToModel(selectedCaseRow))).getCaseUUID().equals(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID())
|
||||
&& dataSourceModel.getValueAt(dataSourcesTable.convertRowIndexToModel(selectedDataSourceRow), 1).toString().equals(nodeData.getDeviceID())) {
|
||||
tableModel.addNodeData(nodeData);
|
||||
&& casesTableModel.getCorrelationCase(casesTable.convertRowIndexToModel(selectedCaseRow)).getCaseUUID().equals(nodeData.getCorrelationAttributeInstance().getCorrelationCase().getCaseUUID())
|
||||
&& dataSourcesTableModel.getDeviceIdForRow(dataSourcesTable.convertRowIndexToModel(selectedDataSourceRow)).equals(nodeData.getDeviceID())) {
|
||||
filesTableModel.addNodeData(nodeData);
|
||||
}
|
||||
} else {
|
||||
if (dataSourceModel.getValueAt(dataSourcesTable.convertRowIndexToModel(selectedDataSourceRow), 1).toString().equals(nodeData.getDeviceID())) {
|
||||
tableModel.addNodeData(nodeData);
|
||||
if (dataSourcesTableModel.getDeviceIdForRow(dataSourcesTable.convertRowIndexToModel(selectedDataSourceRow)).equals(nodeData.getDeviceID())) {
|
||||
filesTableModel.addNodeData(nodeData);
|
||||
}
|
||||
}
|
||||
} catch (EamDbException ex) {
|
||||
LOGGER.log(Level.WARNING, "Unable to get correlation attribute instance from OtherOccurrenceNodeInstanceData for case " + nodeData.getCaseName());
|
||||
LOGGER.log(Level.WARNING, "Unable to get correlation attribute instance from OtherOccurrenceNodeInstanceData for case " + nodeData.getCaseName(), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (filesTable.getRowCount() > 0) {
|
||||
filesTable.setRowSelectionInterval(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust column widths to their preferred values.
|
||||
* Update the data displayed in the details section to be correct for the
|
||||
* currently selected File
|
||||
*/
|
||||
private void setColumnWidths() {
|
||||
for (int idx = 0; idx < tableModel.getColumnCount(); idx++) {
|
||||
TableColumn column = filesTable.getColumnModel().getColumn(idx);
|
||||
column.setMinWidth(DEFAULT_MIN_CELL_WIDTH);
|
||||
int columnWidth = tableModel.getColumnPreferredWidth(idx);
|
||||
if (columnWidth > 0) {
|
||||
column.setPreferredWidth(columnWidth);
|
||||
private void updateOnFileSelection() {
|
||||
if (filesTable.getSelectedRowCount() == 1) {
|
||||
//if there is one file selected update the deatils to show the data for that file
|
||||
occurrencePanel = new OccurrencePanel(filesTableModel.getListOfNodesForFile(filesTable.convertRowIndexToModel(filesTable.getSelectedRow())));
|
||||
} else if (dataSourcesTable.getSelectedRowCount() == 1) {
|
||||
//if no files were selected and only one data source is selected update the information to reflect the data source
|
||||
String caseName = dataSourcesTableModel.getCaseNameForRow(dataSourcesTable.convertRowIndexToModel(dataSourcesTable.getSelectedRow()));
|
||||
String dsName = dataSourcesTableModel.getValueAt(dataSourcesTable.convertRowIndexToModel(dataSourcesTable.getSelectedRow()), 0).toString();
|
||||
String caseCreatedDate = "";
|
||||
for (int row : casesTable.getSelectedRows()) {
|
||||
if (casesTableModel.getValueAt(casesTable.convertRowIndexToModel(row), 0).toString().equals(caseName)) {
|
||||
caseCreatedDate = getCaseCreatedDate(row);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int idx = 0; idx < dataSourcesTable.getColumnCount(); idx++) {
|
||||
if (dataSourcesTable.getColumnModel().getColumn(idx).getHeaderValue().toString().equals(Bundle.DataContentViewerOtherCases_dataSources_header_text())) {
|
||||
dataSourcesTable.getColumnModel().getColumn(idx).setPreferredWidth(100);
|
||||
occurrencePanel = new OccurrencePanel(caseName, caseCreatedDate, dsName);
|
||||
} else if (casesTable.getSelectedRowCount() == 1) {
|
||||
//if no files were selected and a number of data source other than 1 are selected
|
||||
//update the information to reflect the case
|
||||
String createdDate = "";
|
||||
String caseName = "";
|
||||
if (casesTable.getRowCount() > 0) {
|
||||
caseName = casesTableModel.getValueAt(casesTable.convertRowIndexToModel(casesTable.getSelectedRow()), 0).toString();
|
||||
}
|
||||
if (caseName.isEmpty()) {
|
||||
occurrencePanel = new OccurrencePanel();
|
||||
} else {
|
||||
dataSourcesTable.getColumnModel().getColumn(idx).setPreferredWidth(210);
|
||||
createdDate = getCaseCreatedDate(casesTable.getSelectedRow());
|
||||
occurrencePanel = new OccurrencePanel(caseName, createdDate);
|
||||
}
|
||||
} else {
|
||||
//else display an empty details area
|
||||
occurrencePanel = new OccurrencePanel();
|
||||
}
|
||||
//calling getPreferredSize has a side effect of ensuring it has a preferred size which reflects the contents which are visible
|
||||
occurrencePanel.getPreferredSize();
|
||||
detailsPanelScrollPane.setViewportView(occurrencePanel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the date a case was created
|
||||
*
|
||||
* @param caseTableRowIdx the row from the casesTable representing the case
|
||||
*
|
||||
* @return A string representing the date the case was created or an empty
|
||||
* string if the date could not be determined
|
||||
*/
|
||||
private String getCaseCreatedDate(int caseTableRowIdx) {
|
||||
try {
|
||||
if (EamDb.isEnabled()) {
|
||||
CorrelationCase partialCase;
|
||||
partialCase = casesTableModel.getCorrelationCase(casesTable.convertRowIndexToModel(caseTableRowIdx));
|
||||
if (partialCase == null){
|
||||
return "";
|
||||
}
|
||||
return EamDb.getInstance().getCaseByUUID(partialCase.getCaseUUID()).getCreationDate();
|
||||
} else {
|
||||
return Case.getCurrentCase().getCreatedDate();
|
||||
}
|
||||
} catch (EamDbException ex) {
|
||||
LOGGER.log(Level.WARNING, "Error getting case created date for row: " + caseTableRowIdx, ex);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -895,19 +956,20 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
showCaseDetailsMenuItem = new javax.swing.JMenuItem();
|
||||
showCommonalityMenuItem = new javax.swing.JMenuItem();
|
||||
CSVFileChooser = new javax.swing.JFileChooser();
|
||||
otherCasesPanel = new javax.swing.JPanel();
|
||||
tableContainerPanel = new javax.swing.JPanel();
|
||||
earliestCaseLabel = new javax.swing.JLabel();
|
||||
earliestCaseDate = new javax.swing.JLabel();
|
||||
foundInLabel = new javax.swing.JLabel();
|
||||
jSplitPane2 = new javax.swing.JSplitPane();
|
||||
jSplitPane3 = new javax.swing.JSplitPane();
|
||||
tablesViewerSplitPane = new javax.swing.JSplitPane();
|
||||
caseDatasourceFileSplitPane = new javax.swing.JSplitPane();
|
||||
caseDatasourceSplitPane = new javax.swing.JSplitPane();
|
||||
caseScrollPane = new javax.swing.JScrollPane();
|
||||
casesTable = new javax.swing.JTable();
|
||||
dataSourceScrollPane = new javax.swing.JScrollPane();
|
||||
dataSourcesTable = new javax.swing.JTable();
|
||||
propertiesTableScrollPane = new javax.swing.JScrollPane();
|
||||
filesTableScrollPane = new javax.swing.JScrollPane();
|
||||
filesTable = new javax.swing.JTable();
|
||||
detailsPanelScrollPane = new javax.swing.JScrollPane();
|
||||
|
||||
rightClickPopupMenu.addPopupMenuListener(new javax.swing.event.PopupMenuListener() {
|
||||
public void popupMenuCanceled(javax.swing.event.PopupMenuEvent evt) {
|
||||
@@ -931,13 +993,12 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
org.openide.awt.Mnemonics.setLocalizedText(showCommonalityMenuItem, org.openide.util.NbBundle.getMessage(DataContentViewerOtherCases.class, "DataContentViewerOtherCases.showCommonalityMenuItem.text")); // NOI18N
|
||||
rightClickPopupMenu.add(showCommonalityMenuItem);
|
||||
|
||||
setMinimumSize(new java.awt.Dimension(1500, 10));
|
||||
setMinimumSize(new java.awt.Dimension(600, 10));
|
||||
setOpaque(false);
|
||||
setPreferredSize(new java.awt.Dimension(1500, 44));
|
||||
setPreferredSize(new java.awt.Dimension(600, 63));
|
||||
|
||||
otherCasesPanel.setPreferredSize(new java.awt.Dimension(921, 62));
|
||||
|
||||
tableContainerPanel.setPreferredSize(new java.awt.Dimension(1500, 63));
|
||||
tableContainerPanel.setPreferredSize(new java.awt.Dimension(600, 63));
|
||||
tableContainerPanel.setRequestFocusEnabled(false);
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(earliestCaseLabel, org.openide.util.NbBundle.getMessage(DataContentViewerOtherCases.class, "DataContentViewerOtherCases.earliestCaseLabel.text")); // NOI18N
|
||||
earliestCaseLabel.setToolTipText(org.openide.util.NbBundle.getMessage(DataContentViewerOtherCases.class, "DataContentViewerOtherCases.earliestCaseLabel.toolTipText")); // NOI18N
|
||||
@@ -946,49 +1007,50 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(foundInLabel, org.openide.util.NbBundle.getMessage(DataContentViewerOtherCases.class, "DataContentViewerOtherCases.foundInLabel.text")); // NOI18N
|
||||
|
||||
jSplitPane2.setDividerLocation(470);
|
||||
tablesViewerSplitPane.setDividerLocation(450);
|
||||
tablesViewerSplitPane.setResizeWeight(0.5);
|
||||
|
||||
jSplitPane3.setDividerLocation(150);
|
||||
caseDatasourceFileSplitPane.setDividerLocation(300);
|
||||
caseDatasourceFileSplitPane.setResizeWeight(0.6);
|
||||
caseDatasourceFileSplitPane.setToolTipText("");
|
||||
|
||||
caseDatasourceSplitPane.setDividerLocation(150);
|
||||
caseDatasourceSplitPane.setResizeWeight(0.5);
|
||||
|
||||
caseScrollPane.setPreferredSize(new java.awt.Dimension(140, 30));
|
||||
|
||||
casesTable.setAutoCreateRowSorter(true);
|
||||
casesTable.setModel(casesTableModel);
|
||||
caseScrollPane.setViewportView(casesTable);
|
||||
|
||||
jSplitPane3.setLeftComponent(caseScrollPane);
|
||||
caseDatasourceSplitPane.setLeftComponent(caseScrollPane);
|
||||
|
||||
dataSourceScrollPane.setPreferredSize(new java.awt.Dimension(140, 30));
|
||||
|
||||
dataSourcesTable.setAutoCreateRowSorter(true);
|
||||
dataSourcesTable.setModel(new javax.swing.table.DefaultTableModel(
|
||||
new Object [][] {
|
||||
|
||||
},
|
||||
new String [] {
|
||||
"Data Source Name", "Device ID"
|
||||
}
|
||||
) {
|
||||
boolean[] canEdit = new boolean [] {
|
||||
false, false
|
||||
};
|
||||
|
||||
public boolean isCellEditable(int rowIndex, int columnIndex) {
|
||||
return canEdit [columnIndex];
|
||||
}
|
||||
});
|
||||
dataSourcesTable.setModel(dataSourcesTableModel);
|
||||
dataSourceScrollPane.setViewportView(dataSourcesTable);
|
||||
|
||||
jSplitPane3.setRightComponent(dataSourceScrollPane);
|
||||
caseDatasourceSplitPane.setRightComponent(dataSourceScrollPane);
|
||||
|
||||
jSplitPane2.setLeftComponent(jSplitPane3);
|
||||
caseDatasourceFileSplitPane.setLeftComponent(caseDatasourceSplitPane);
|
||||
|
||||
propertiesTableScrollPane.setPreferredSize(new java.awt.Dimension(1000, 30));
|
||||
filesTableScrollPane.setPreferredSize(new java.awt.Dimension(140, 30));
|
||||
|
||||
filesTable.setAutoCreateRowSorter(true);
|
||||
filesTable.setModel(tableModel);
|
||||
filesTable.setModel(filesTableModel);
|
||||
filesTable.setToolTipText(org.openide.util.NbBundle.getMessage(DataContentViewerOtherCases.class, "DataContentViewerOtherCases.table.toolTip.text")); // NOI18N
|
||||
filesTable.setComponentPopupMenu(rightClickPopupMenu);
|
||||
filesTable.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_INTERVAL_SELECTION);
|
||||
propertiesTableScrollPane.setViewportView(filesTable);
|
||||
filesTable.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
|
||||
filesTableScrollPane.setViewportView(filesTable);
|
||||
|
||||
jSplitPane2.setRightComponent(propertiesTableScrollPane);
|
||||
caseDatasourceFileSplitPane.setRightComponent(filesTableScrollPane);
|
||||
|
||||
tablesViewerSplitPane.setLeftComponent(caseDatasourceFileSplitPane);
|
||||
|
||||
detailsPanelScrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
|
||||
detailsPanelScrollPane.setPreferredSize(new java.awt.Dimension(200, 100));
|
||||
tablesViewerSplitPane.setRightComponent(detailsPanelScrollPane);
|
||||
|
||||
javax.swing.GroupLayout tableContainerPanelLayout = new javax.swing.GroupLayout(tableContainerPanel);
|
||||
tableContainerPanel.setLayout(tableContainerPanelLayout);
|
||||
@@ -997,65 +1059,49 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
.addGroup(tableContainerPanelLayout.createSequentialGroup()
|
||||
.addGroup(tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(tableContainerPanelLayout.createSequentialGroup()
|
||||
.addComponent(earliestCaseLabel)
|
||||
.addComponent(earliestCaseLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 161, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(earliestCaseDate)
|
||||
.addGap(66, 66, 66)
|
||||
.addComponent(foundInLabel))
|
||||
.addComponent(jSplitPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 911, Short.MAX_VALUE))
|
||||
.addComponent(earliestCaseDate, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(foundInLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
.addComponent(tablesViewerSplitPane, javax.swing.GroupLayout.DEFAULT_SIZE, 590, Short.MAX_VALUE))
|
||||
.addContainerGap())
|
||||
);
|
||||
tableContainerPanelLayout.setVerticalGroup(
|
||||
tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, tableContainerPanelLayout.createSequentialGroup()
|
||||
.addComponent(jSplitPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 31, Short.MAX_VALUE)
|
||||
.addGroup(tableContainerPanelLayout.createSequentialGroup()
|
||||
.addGap(0, 0, 0)
|
||||
.addComponent(tablesViewerSplitPane, javax.swing.GroupLayout.DEFAULT_SIZE, 33, Short.MAX_VALUE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(earliestCaseLabel)
|
||||
.addComponent(earliestCaseDate)
|
||||
.addComponent(foundInLabel))
|
||||
.addGroup(tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
|
||||
.addGroup(tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(earliestCaseLabel)
|
||||
.addComponent(earliestCaseDate))
|
||||
.addComponent(foundInLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 14, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addContainerGap())
|
||||
);
|
||||
|
||||
javax.swing.GroupLayout otherCasesPanelLayout = new javax.swing.GroupLayout(otherCasesPanel);
|
||||
otherCasesPanel.setLayout(otherCasesPanelLayout);
|
||||
otherCasesPanelLayout.setHorizontalGroup(
|
||||
otherCasesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 921, Short.MAX_VALUE)
|
||||
.addGroup(otherCasesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(otherCasesPanelLayout.createSequentialGroup()
|
||||
.addComponent(tableContainerPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addGap(0, 0, 0)))
|
||||
);
|
||||
otherCasesPanelLayout.setVerticalGroup(
|
||||
otherCasesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 62, Short.MAX_VALUE)
|
||||
.addGroup(otherCasesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(otherCasesPanelLayout.createSequentialGroup()
|
||||
.addComponent(tableContainerPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 62, Short.MAX_VALUE)
|
||||
.addGap(0, 0, 0)))
|
||||
);
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(otherCasesPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 1500, Short.MAX_VALUE)
|
||||
.addComponent(tableContainerPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(otherCasesPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(tableContainerPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 64, Short.MAX_VALUE)
|
||||
.addGap(0, 0, 0))
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
private void rightClickPopupMenuPopupMenuWillBecomeVisible(javax.swing.event.PopupMenuEvent evt) {//GEN-FIRST:event_rightClickPopupMenuPopupMenuWillBecomeVisible
|
||||
boolean enableCentralRepoActions = false;
|
||||
|
||||
if (EamDb.isEnabled() && filesTable.getSelectedRowCount() == 1) {
|
||||
int rowIndex = filesTable.getSelectedRow();
|
||||
OtherOccurrenceNodeData selectedNode = (OtherOccurrenceNodeData) tableModel.getRow(rowIndex);
|
||||
if (selectedNode instanceof OtherOccurrenceNodeInstanceData) {
|
||||
OtherOccurrenceNodeInstanceData instanceData = (OtherOccurrenceNodeInstanceData) selectedNode;
|
||||
List<OtherOccurrenceNodeData> selectedFile = filesTableModel.getListOfNodesForFile(rowIndex);
|
||||
if (!selectedFile.isEmpty() && selectedFile.get(0) instanceof OtherOccurrenceNodeInstanceData) {
|
||||
OtherOccurrenceNodeInstanceData instanceData = (OtherOccurrenceNodeInstanceData) selectedFile.get(0);
|
||||
enableCentralRepoActions = instanceData.isCentralRepoNode();
|
||||
}
|
||||
}
|
||||
@@ -1065,24 +1111,25 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JFileChooser CSVFileChooser;
|
||||
private javax.swing.JSplitPane caseDatasourceFileSplitPane;
|
||||
private javax.swing.JSplitPane caseDatasourceSplitPane;
|
||||
private javax.swing.JScrollPane caseScrollPane;
|
||||
private javax.swing.JTable casesTable;
|
||||
private javax.swing.JScrollPane dataSourceScrollPane;
|
||||
private javax.swing.JTable dataSourcesTable;
|
||||
private javax.swing.JScrollPane detailsPanelScrollPane;
|
||||
private javax.swing.JLabel earliestCaseDate;
|
||||
private javax.swing.JLabel earliestCaseLabel;
|
||||
private javax.swing.JMenuItem exportToCSVMenuItem;
|
||||
private javax.swing.JTable filesTable;
|
||||
private javax.swing.JScrollPane filesTableScrollPane;
|
||||
private javax.swing.JLabel foundInLabel;
|
||||
private javax.swing.JSplitPane jSplitPane2;
|
||||
private javax.swing.JSplitPane jSplitPane3;
|
||||
private javax.swing.JPanel otherCasesPanel;
|
||||
private javax.swing.JScrollPane propertiesTableScrollPane;
|
||||
private javax.swing.JPopupMenu rightClickPopupMenu;
|
||||
private javax.swing.JMenuItem selectAllMenuItem;
|
||||
private javax.swing.JMenuItem showCaseDetailsMenuItem;
|
||||
private javax.swing.JMenuItem showCommonalityMenuItem;
|
||||
private javax.swing.JPanel tableContainerPanel;
|
||||
private javax.swing.JSplitPane tablesViewerSplitPane;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
/**
|
||||
@@ -1119,9 +1166,6 @@ public class DataContentViewerOtherCases extends JPanel implements DataContentVi
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
//int hash = 7;
|
||||
//hash = 67 * hash + this.dataSourceID.hashCode();
|
||||
//hash = 67 * hash + this.filePath.hashCode();
|
||||
return Objects.hash(getDataSourceID(), getFilePath(), getType());
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.4" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
||||
<Properties>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[50, 30]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
||||
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,0,68,0,0,0,-116"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
|
||||
</Form>
|
||||
@@ -0,0 +1,370 @@
|
||||
/*
|
||||
* Central Repository
|
||||
*
|
||||
* 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.centralrepository.contentviewer;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
|
||||
/**
|
||||
* Panel for displaying other occurrence details.
|
||||
*/
|
||||
final class OccurrencePanel extends javax.swing.JPanel {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(OccurrencePanel.class.getName());
|
||||
private static final int LEFT_INSET = 10;
|
||||
private static final int RIGHT_INSET = 10;
|
||||
private static final int TOP_INSET = 10;
|
||||
private static final int BOTTOM_INSET = 10;
|
||||
private static final int VERTICAL_GAP = 6;
|
||||
private static final int HORIZONTAL_GAP = 4;
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private int gridY = 0;
|
||||
private final List<OtherOccurrenceNodeData> nodeDataList;
|
||||
private final Map<String, String> caseNamesAndDates = new HashMap<>();
|
||||
private final Set<String> dataSourceNames = new HashSet<>();
|
||||
private final Set<String> filePaths = new HashSet<>();
|
||||
|
||||
/**
|
||||
* Construct an empty OccurrencePanel
|
||||
*/
|
||||
OccurrencePanel() {
|
||||
nodeDataList = new ArrayList<>();
|
||||
customizeComponents();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an OccurrencePanel which will display only Case information
|
||||
*
|
||||
* @param caseName the name of the case
|
||||
* @param caseCreatedDate the date the case was created
|
||||
*/
|
||||
OccurrencePanel(String caseName, String caseCreatedDate) {
|
||||
nodeDataList = new ArrayList<>();
|
||||
caseNamesAndDates.put(caseName, caseCreatedDate);
|
||||
customizeComponents();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an OccurrencePanel which will display only case and data source
|
||||
* information
|
||||
*
|
||||
* @param caseName the name of the case
|
||||
* @param caseCreatedDate the date the case was created
|
||||
* @param dataSourceName the name of the data source
|
||||
*/
|
||||
OccurrencePanel(String caseName, String caseCreatedDate, String dataSourceName) {
|
||||
nodeDataList = new ArrayList<>();
|
||||
caseNamesAndDates.put(caseName, caseCreatedDate);
|
||||
dataSourceNames.add(dataSourceName);
|
||||
customizeComponents();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a OccurrencePanel which will display details for all other
|
||||
* occurrences associated with a file
|
||||
*
|
||||
* @param nodeDataList the list of OtherOccurrenceNodeData representing
|
||||
* common properties for the file
|
||||
*/
|
||||
OccurrencePanel(List<OtherOccurrenceNodeData> nodeDataList) {
|
||||
this.nodeDataList = nodeDataList;
|
||||
customizeComponents();
|
||||
}
|
||||
|
||||
/**
|
||||
* Do all the construction of gui elements and adding of the appropriate
|
||||
* elements to the gridbaglayout
|
||||
*/
|
||||
private void customizeComponents() {
|
||||
initComponents();
|
||||
if (!this.nodeDataList.isEmpty()) {
|
||||
//if addInstanceDetails is going to be called it should be called
|
||||
// before addFileDetails, addDataSourceDetails, and addCaseDetails
|
||||
//because it also collects the information they display
|
||||
addInstanceDetails();
|
||||
if (!filePaths.isEmpty()) {
|
||||
addFileDetails();
|
||||
}
|
||||
}
|
||||
if (!dataSourceNames.isEmpty()) {
|
||||
addDataSourceDetails();
|
||||
}
|
||||
if (!caseNamesAndDates.keySet().isEmpty()) {
|
||||
addCaseDetails();
|
||||
}
|
||||
//add filler to keep everything else at the top
|
||||
addItemToBag(gridY, 0, 0, 0, new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 32767)));
|
||||
}
|
||||
|
||||
@Messages({
|
||||
"OccurrencePanel.commonProperties.text=Common Properties",
|
||||
"OccurrencePanel.commonPropertyTypeLabel.text=Type:",
|
||||
"OccurrencePanel.commonPropertyValueLabel.text=Value:",
|
||||
"OccurrencePanel.commonPropertyKnownStatusLabel.text=Known Status:",
|
||||
"OccurrencePanel.commonPropertyCommentLabel.text=Comment:"
|
||||
})
|
||||
/**
|
||||
* Add the Common Property instance details to the gridbaglayout supports
|
||||
* adding multiple common properties
|
||||
*
|
||||
* Also collects the case, data source, and file path information to be
|
||||
* displayed
|
||||
*/
|
||||
private void addInstanceDetails() {
|
||||
javax.swing.JLabel commonPropertiesLabel = new javax.swing.JLabel();
|
||||
org.openide.awt.Mnemonics.setLocalizedText(commonPropertiesLabel, Bundle.OccurrencePanel_commonProperties_text());
|
||||
commonPropertiesLabel.setFont(commonPropertiesLabel.getFont().deriveFont(Font.BOLD, commonPropertiesLabel.getFont().getSize()));
|
||||
addItemToBag(gridY, 0, TOP_INSET, 0, commonPropertiesLabel);
|
||||
gridY++;
|
||||
//for each other occurrence
|
||||
for (OtherOccurrenceNodeData occurrence : nodeDataList) {
|
||||
if (occurrence instanceof OtherOccurrenceNodeInstanceData) {
|
||||
String type = ((OtherOccurrenceNodeInstanceData) occurrence).getType();
|
||||
if (!type.isEmpty()) {
|
||||
javax.swing.JLabel typeLabel = new javax.swing.JLabel();
|
||||
org.openide.awt.Mnemonics.setLocalizedText(typeLabel, Bundle.OccurrencePanel_commonPropertyTypeLabel_text());
|
||||
addItemToBag(gridY, 0, VERTICAL_GAP, 0, typeLabel);
|
||||
javax.swing.JLabel typeFieldValue = new javax.swing.JLabel();
|
||||
typeFieldValue.setText(type);
|
||||
addItemToBag(gridY, 1, VERTICAL_GAP, 0, typeFieldValue);
|
||||
gridY++;
|
||||
}
|
||||
String value = ((OtherOccurrenceNodeInstanceData) occurrence).getValue();
|
||||
if (!value.isEmpty()) {
|
||||
javax.swing.JLabel valueLabel = new javax.swing.JLabel();
|
||||
org.openide.awt.Mnemonics.setLocalizedText(valueLabel, Bundle.OccurrencePanel_commonPropertyValueLabel_text());
|
||||
addItemToBag(gridY, 0, 0, 0, valueLabel);
|
||||
javax.swing.JLabel valueFieldValue = new javax.swing.JLabel();
|
||||
valueFieldValue.setText(value);
|
||||
addItemToBag(gridY, 1, 0, 0, valueFieldValue);
|
||||
gridY++;
|
||||
}
|
||||
TskData.FileKnown knownStatus = ((OtherOccurrenceNodeInstanceData) occurrence).getKnown();
|
||||
javax.swing.JLabel knownStatusLabel = new javax.swing.JLabel();
|
||||
org.openide.awt.Mnemonics.setLocalizedText(knownStatusLabel, Bundle.OccurrencePanel_commonPropertyKnownStatusLabel_text());
|
||||
addItemToBag(gridY, 0, 0, 0, knownStatusLabel);
|
||||
javax.swing.JLabel knownStatusValue = new javax.swing.JLabel();
|
||||
knownStatusValue.setText(knownStatus.toString());
|
||||
if (knownStatus == TskData.FileKnown.BAD) {
|
||||
knownStatusValue.setForeground(Color.RED);
|
||||
}
|
||||
addItemToBag(gridY, 1, 0, 0, knownStatusValue);
|
||||
gridY++;
|
||||
String comment = ((OtherOccurrenceNodeInstanceData) occurrence).getComment();
|
||||
if (!comment.isEmpty()) {
|
||||
javax.swing.JLabel commentLabel = new javax.swing.JLabel();
|
||||
org.openide.awt.Mnemonics.setLocalizedText(commentLabel, Bundle.OccurrencePanel_commonPropertyCommentLabel_text());
|
||||
addItemToBag(gridY, 0, 0, VERTICAL_GAP, commentLabel);
|
||||
javax.swing.JTextArea commentValue = new javax.swing.JTextArea();
|
||||
commentValue.setText(comment);
|
||||
commentValue.setEditable(false);
|
||||
commentValue.setColumns(20);
|
||||
commentValue.setLineWrap(true);
|
||||
commentValue.setRows(3);
|
||||
commentValue.setTabSize(4);
|
||||
commentValue.setWrapStyleWord(true);
|
||||
commentValue.setBorder(javax.swing.BorderFactory.createEtchedBorder());
|
||||
commentValue.setBackground(javax.swing.UIManager.getDefaults().getColor("TextArea.disabledBackground"));
|
||||
addItemToBag(gridY, 1, 0, VERTICAL_GAP, commentValue);
|
||||
gridY++;
|
||||
}
|
||||
String caseDate = "";
|
||||
try {
|
||||
OtherOccurrenceNodeInstanceData nodeData = ((OtherOccurrenceNodeInstanceData) occurrence);
|
||||
if (nodeData.isCentralRepoNode()) {
|
||||
if (EamDb.isEnabled()) {
|
||||
CorrelationCase partialCase = nodeData.getCorrelationAttributeInstance().getCorrelationCase();
|
||||
caseDate = EamDb.getInstance().getCaseByUUID(partialCase.getCaseUUID()).getCreationDate();
|
||||
}
|
||||
} else {
|
||||
caseDate = Case.getCurrentCase().getCreatedDate();
|
||||
}
|
||||
} catch (EamDbException ex) {
|
||||
LOGGER.log(Level.WARNING, "Error getting case created date for other occurrence content viewer", ex);
|
||||
}
|
||||
//Collect the data that is necessary for the other sections
|
||||
caseNamesAndDates.put(((OtherOccurrenceNodeInstanceData) occurrence).getCaseName(), caseDate);
|
||||
dataSourceNames.add(((OtherOccurrenceNodeInstanceData) occurrence).getDataSourceName());
|
||||
filePaths.add(((OtherOccurrenceNodeInstanceData) occurrence).getFilePath());
|
||||
}
|
||||
}
|
||||
//end for each
|
||||
}
|
||||
|
||||
@Messages({
|
||||
"OccurrencePanel.fileDetails.text=File Details",
|
||||
"OccurrencePanel.filePathLabel.text=File Path:"
|
||||
})
|
||||
/**
|
||||
* Add the File specific details such as file path to the gridbaglayout
|
||||
*/
|
||||
private void addFileDetails() {
|
||||
String filePath = filePaths.size() > 1 ? "" : filePaths.iterator().next();
|
||||
if (!filePath.isEmpty()) {
|
||||
javax.swing.JLabel fileDetailsLabel = new javax.swing.JLabel();
|
||||
org.openide.awt.Mnemonics.setLocalizedText(fileDetailsLabel, Bundle.OccurrencePanel_fileDetails_text());
|
||||
fileDetailsLabel.setFont(fileDetailsLabel.getFont().deriveFont(Font.BOLD, fileDetailsLabel.getFont().getSize()));
|
||||
addItemToBag(gridY, 0, TOP_INSET, 0, fileDetailsLabel);
|
||||
gridY++;
|
||||
javax.swing.JLabel filePathLabel = new javax.swing.JLabel();
|
||||
org.openide.awt.Mnemonics.setLocalizedText(filePathLabel, Bundle.OccurrencePanel_filePathLabel_text());
|
||||
addItemToBag(gridY, 0, VERTICAL_GAP, VERTICAL_GAP, filePathLabel);
|
||||
javax.swing.JTextArea filePathValue = new javax.swing.JTextArea();
|
||||
filePathValue.setText(filePath);
|
||||
filePathValue.setEditable(false);
|
||||
filePathValue.setColumns(20);
|
||||
filePathValue.setLineWrap(true);
|
||||
filePathValue.setRows(3);
|
||||
filePathValue.setTabSize(4);
|
||||
filePathValue.setWrapStyleWord(true);
|
||||
filePathValue.setBorder(javax.swing.BorderFactory.createEtchedBorder());
|
||||
filePathValue.setBackground(javax.swing.UIManager.getDefaults().getColor("TextArea.disabledBackground"));
|
||||
addItemToBag(gridY, 1, VERTICAL_GAP, VERTICAL_GAP, filePathValue);
|
||||
gridY++;
|
||||
}
|
||||
}
|
||||
|
||||
@Messages({
|
||||
"OccurrencePanel.dataSourceDetails.text=Data Source Details",
|
||||
"OccurrencePanel.dataSourceNameLabel.text=Name:"
|
||||
})
|
||||
/**
|
||||
* Add the data source specific details such as data source name to the
|
||||
* gridbaglayout
|
||||
*/
|
||||
private void addDataSourceDetails() {
|
||||
String dataSourceName = dataSourceNames.size() > 1 ? "" : dataSourceNames.iterator().next();
|
||||
if (!dataSourceName.isEmpty()) {
|
||||
javax.swing.JLabel dataSourceDetailsLabel = new javax.swing.JLabel();
|
||||
org.openide.awt.Mnemonics.setLocalizedText(dataSourceDetailsLabel, Bundle.OccurrencePanel_dataSourceDetails_text());
|
||||
dataSourceDetailsLabel.setFont(dataSourceDetailsLabel.getFont().deriveFont(Font.BOLD, dataSourceDetailsLabel.getFont().getSize()));
|
||||
addItemToBag(gridY, 0, TOP_INSET, 0, dataSourceDetailsLabel);
|
||||
gridY++;
|
||||
javax.swing.JLabel dataSourceNameLabel = new javax.swing.JLabel();
|
||||
org.openide.awt.Mnemonics.setLocalizedText(dataSourceNameLabel, Bundle.OccurrencePanel_dataSourceNameLabel_text());
|
||||
addItemToBag(gridY, 0, VERTICAL_GAP, VERTICAL_GAP, dataSourceNameLabel);
|
||||
javax.swing.JLabel dataSourceNameValue = new javax.swing.JLabel();
|
||||
dataSourceNameValue.setText(dataSourceName);
|
||||
addItemToBag(gridY, 1, VERTICAL_GAP, VERTICAL_GAP, dataSourceNameValue);
|
||||
gridY++;
|
||||
}
|
||||
}
|
||||
|
||||
@Messages({
|
||||
"OccurrencePanel.caseDetails.text=Case Details",
|
||||
"OccurrencePanel.caseNameLabel.text=Name:",
|
||||
"OccurrencePanel.caseCreatedDateLabel.text=Created Date:"
|
||||
})
|
||||
/**
|
||||
* Add the case specific details such as case name to the gridbaglayout
|
||||
*/
|
||||
private void addCaseDetails() {
|
||||
javax.swing.JLabel caseDetailsLabel = new javax.swing.JLabel();
|
||||
org.openide.awt.Mnemonics.setLocalizedText(caseDetailsLabel, Bundle.OccurrencePanel_caseDetails_text());
|
||||
caseDetailsLabel.setFont(caseDetailsLabel.getFont().deriveFont(Font.BOLD, caseDetailsLabel.getFont().getSize()));
|
||||
addItemToBag(gridY, 0, TOP_INSET, 0, caseDetailsLabel);
|
||||
gridY++;
|
||||
String caseName = caseNamesAndDates.keySet().size() > 1 ? "" : caseNamesAndDates.keySet().iterator().next();
|
||||
if (!caseName.isEmpty()) {
|
||||
javax.swing.JLabel caseNameLabel = new javax.swing.JLabel();
|
||||
org.openide.awt.Mnemonics.setLocalizedText(caseNameLabel, Bundle.OccurrencePanel_caseNameLabel_text());
|
||||
addItemToBag(gridY, 0, VERTICAL_GAP, 0, caseNameLabel);
|
||||
javax.swing.JLabel caseNameValue = new javax.swing.JLabel();
|
||||
caseNameValue.setText(caseName);
|
||||
addItemToBag(gridY, 1, VERTICAL_GAP, 0, caseNameValue);
|
||||
gridY++;
|
||||
}
|
||||
String caseCreatedDate = caseNamesAndDates.keySet().size() > 1 ? "" : caseNamesAndDates.get(caseName);
|
||||
if (caseCreatedDate != null && !caseCreatedDate.isEmpty()) {
|
||||
javax.swing.JLabel caseCreatedLabel = new javax.swing.JLabel();
|
||||
org.openide.awt.Mnemonics.setLocalizedText(caseCreatedLabel, Bundle.OccurrencePanel_caseCreatedDateLabel_text());
|
||||
addItemToBag(gridY, 0, 0, BOTTOM_INSET, caseCreatedLabel);
|
||||
javax.swing.JLabel caseCreatedValue = new javax.swing.JLabel();
|
||||
caseCreatedValue.setText(caseCreatedDate);
|
||||
addItemToBag(gridY, 1, 0, BOTTOM_INSET, caseCreatedValue);
|
||||
gridY++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a JComponent to the gridbaglayout
|
||||
*
|
||||
* @param gridYLocation the row number the item should be added at
|
||||
* @param gridXLocation the column number the item should be added at
|
||||
* @param topInset the gap from the top of the cell which should exist
|
||||
* @param bottomInset the gap from the bottom of the cell which should
|
||||
* exist
|
||||
* @param item the JComponent to add to the gridbaglayout
|
||||
*/
|
||||
private void addItemToBag(int gridYLocation, int gridXLocation, int topInset, int bottomInset, javax.swing.JComponent item) {
|
||||
java.awt.GridBagConstraints gridBagConstraints;
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.gridx = gridXLocation;
|
||||
gridBagConstraints.gridy = gridYLocation;
|
||||
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
|
||||
gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START;
|
||||
int leftInset = LEFT_INSET;
|
||||
int rightInset = HORIZONTAL_GAP;
|
||||
//change formating a bit if it is the value instead of the label
|
||||
if (gridXLocation == 1) {
|
||||
leftInset = 0;
|
||||
rightInset = RIGHT_INSET;
|
||||
gridBagConstraints.weightx = 0.1;
|
||||
gridBagConstraints.gridwidth = 2;
|
||||
}
|
||||
gridBagConstraints.insets = new java.awt.Insets(topInset, leftInset, bottomInset, rightInset);
|
||||
//if the item is a filler item ensure it will resize vertically
|
||||
if (item instanceof javax.swing.Box.Filler) {
|
||||
gridBagConstraints.weighty = 0.1;
|
||||
}
|
||||
add(item, gridBagConstraints);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called from within the constructor to initialize the form.
|
||||
* WARNING: Do NOT modify this code. The content of this method is always
|
||||
* regenerated by the Form Editor.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
setMinimumSize(new java.awt.Dimension(50, 30));
|
||||
setPreferredSize(null);
|
||||
setLayout(new java.awt.GridBagLayout());
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
// End of variables declaration//GEN-END:variables
|
||||
}
|
||||
+80
-31
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Central Repository
|
||||
*
|
||||
* Copyright 2018 Basis Technology Corp.
|
||||
* Copyright 2018-2019 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -31,11 +31,12 @@ import org.sleuthkit.datamodel.TskDataException;
|
||||
* Class for populating the Other Occurrences tab
|
||||
*/
|
||||
class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData {
|
||||
|
||||
|
||||
// For now hard code the string for the central repo files type, since
|
||||
// getting it dynamically can fail.
|
||||
private static final String FILE_TYPE_STR = "Files";
|
||||
|
||||
private static final String CSV_ITEM_SEPARATOR = "\",\"";
|
||||
|
||||
private final String caseName;
|
||||
private String deviceID;
|
||||
private String dataSourceName;
|
||||
@@ -44,12 +45,13 @@ class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData {
|
||||
private final String value;
|
||||
private TskData.FileKnown known;
|
||||
private String comment;
|
||||
|
||||
|
||||
private AbstractFile originalAbstractFile = null;
|
||||
private CorrelationAttributeInstance originalCorrelationInstance = null;
|
||||
|
||||
|
||||
/**
|
||||
* Create a node from a central repo instance.
|
||||
*
|
||||
* @param instance The central repo instance
|
||||
* @param type The type of the instance
|
||||
* @param value The value of the instance
|
||||
@@ -63,15 +65,17 @@ class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData {
|
||||
this.value = value;
|
||||
known = instance.getKnownStatus();
|
||||
comment = instance.getComment();
|
||||
|
||||
|
||||
originalCorrelationInstance = instance;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a node from an abstract file.
|
||||
*
|
||||
* @param newFile The abstract file
|
||||
* @param autopsyCase The current case
|
||||
* @throws EamDbException
|
||||
*
|
||||
* @throws EamDbException
|
||||
*/
|
||||
OtherOccurrenceNodeInstanceData(AbstractFile newFile, Case autopsyCase) throws EamDbException {
|
||||
caseName = autopsyCase.getDisplayName();
|
||||
@@ -82,115 +86,129 @@ class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData {
|
||||
} catch (TskDataException | TskCoreException ex) {
|
||||
throw new EamDbException("Error loading data source for abstract file ID " + newFile.getId(), ex);
|
||||
}
|
||||
|
||||
|
||||
filePath = newFile.getParentPath() + newFile.getName();
|
||||
typeStr = FILE_TYPE_STR;
|
||||
value = newFile.getMd5Hash();
|
||||
known = newFile.getKnown();
|
||||
comment = "";
|
||||
|
||||
|
||||
originalAbstractFile = newFile;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if this node is a "file" type
|
||||
*
|
||||
* @return true if it is a file type
|
||||
*/
|
||||
boolean isFileType() {
|
||||
return FILE_TYPE_STR.equals(typeStr);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update the known status for this node
|
||||
*
|
||||
* @param newKnownStatus The new known status
|
||||
*/
|
||||
void updateKnown(TskData.FileKnown newKnownStatus) {
|
||||
known = newKnownStatus;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update the comment for this node
|
||||
*
|
||||
* @param newComment The new comment
|
||||
*/
|
||||
void updateComment(String newComment) {
|
||||
comment = newComment;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if this is a central repo node.
|
||||
* @return true if this node was created from a central repo instance, false otherwise
|
||||
*
|
||||
* @return true if this node was created from a central repo instance, false
|
||||
* otherwise
|
||||
*/
|
||||
boolean isCentralRepoNode() {
|
||||
return (originalCorrelationInstance != null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the case name
|
||||
*
|
||||
* @return the case name
|
||||
*/
|
||||
String getCaseName() {
|
||||
return caseName;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the device ID
|
||||
*
|
||||
* @return the device ID
|
||||
*/
|
||||
String getDeviceID() {
|
||||
return deviceID;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the data source name
|
||||
*
|
||||
* @return the data source name
|
||||
*/
|
||||
String getDataSourceName() {
|
||||
return dataSourceName;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the file path
|
||||
*
|
||||
* @return the file path
|
||||
*/
|
||||
String getFilePath() {
|
||||
return filePath;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the type (as a string)
|
||||
*
|
||||
* @return the type
|
||||
*/
|
||||
String getType() {
|
||||
return typeStr;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the value (MD5 hash for files)
|
||||
*
|
||||
* @return the value
|
||||
*/
|
||||
String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the known status
|
||||
*
|
||||
* @return the known status
|
||||
*/
|
||||
TskData.FileKnown getKnown() {
|
||||
return known;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the comment
|
||||
*
|
||||
* @return the comment
|
||||
*/
|
||||
String getComment() {
|
||||
return comment;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the backing abstract file.
|
||||
* Should only be called if isCentralRepoNode() is false
|
||||
* Get the backing abstract file. Should only be called if
|
||||
* isCentralRepoNode() is false
|
||||
*
|
||||
* @return the original abstract file
|
||||
*/
|
||||
AbstractFile getAbstractFile() throws EamDbException {
|
||||
@@ -199,12 +217,14 @@ class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData {
|
||||
}
|
||||
return originalAbstractFile;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the backing CorrelationAttributeInstance.
|
||||
* Should only be called if isCentralRepoNode() is true
|
||||
* Get the backing CorrelationAttributeInstance. Should only be called if
|
||||
* isCentralRepoNode() is true
|
||||
*
|
||||
* @return the original CorrelationAttributeInstance
|
||||
* @throws EamDbException
|
||||
*
|
||||
* @throws EamDbException
|
||||
*/
|
||||
CorrelationAttributeInstance getCorrelationAttributeInstance() throws EamDbException {
|
||||
if (originalCorrelationInstance == null) {
|
||||
@@ -212,4 +232,33 @@ class OtherOccurrenceNodeInstanceData implements OtherOccurrenceNodeData {
|
||||
}
|
||||
return originalCorrelationInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the string to append between elements when writing the node instance
|
||||
* data to a CSV
|
||||
*
|
||||
* @return the CSV_ITEM_SEPARATOR string
|
||||
*/
|
||||
static String getCsvItemSeparator() {
|
||||
return CSV_ITEM_SEPARATOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a string representation of the node's data comma separated with a
|
||||
* line separator ending
|
||||
*
|
||||
* @return a comma separated string representation of the node's data
|
||||
*/
|
||||
String toCsvString() {
|
||||
StringBuilder line = new StringBuilder("\"");
|
||||
line.append(getCaseName()).append(CSV_ITEM_SEPARATOR)
|
||||
.append(getDataSourceName()).append(CSV_ITEM_SEPARATOR)
|
||||
.append(getType()).append(CSV_ITEM_SEPARATOR)
|
||||
.append(getValue()).append(CSV_ITEM_SEPARATOR)
|
||||
.append(getKnown().toString()).append(CSV_ITEM_SEPARATOR)
|
||||
.append(getFilePath()).append(CSV_ITEM_SEPARATOR)
|
||||
.append(getComment()).append('"')
|
||||
.append(System.getProperty("line.separator"));
|
||||
return line.toString();
|
||||
}
|
||||
}
|
||||
|
||||
+29
-63
@@ -22,6 +22,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase;
|
||||
|
||||
/**
|
||||
* Model for cells in the cases section of the other occurrences data content
|
||||
@@ -32,26 +33,16 @@ public class OtherOccurrencesCasesTableModel extends AbstractTableModel {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final List<CorrelationCaseWrapper> correlationCaseList = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Create a table model for displaying case names
|
||||
*/
|
||||
OtherOccurrencesCasesTableModel() {
|
||||
// This constructor is intentionally empty.
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount() {
|
||||
return TableColumns.values().length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the preferred width that has been configured for this column.
|
||||
*
|
||||
* A value of 0 means that no preferred width has been defined for this
|
||||
* column.
|
||||
*
|
||||
* @param colIdx Column index
|
||||
*
|
||||
* @return preferred column width >= 0
|
||||
*/
|
||||
public int getColumnPreferredWidth(int colIdx) {
|
||||
return TableColumns.values()[colIdx].columnWidth();
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -59,45 +50,43 @@ public class OtherOccurrencesCasesTableModel extends AbstractTableModel {
|
||||
return correlationCaseList.size();
|
||||
}
|
||||
|
||||
@Messages({"OtherOccurrencesCasesTableModel.case=Case",})
|
||||
@Override
|
||||
public String getColumnName(int colIdx) {
|
||||
return TableColumns.values()[colIdx].columnName();
|
||||
return Bundle.OtherOccurrencesCasesTableModel_case();
|
||||
}
|
||||
|
||||
@Messages({"OtherOccurrencesCasesTableModel.noData=No Data."})
|
||||
@Override
|
||||
public Object getValueAt(int rowIdx, int colIdx) {
|
||||
if (0 == correlationCaseList.size()) {
|
||||
//if anything would prevent this from working we will consider it no data for the sake of simplicity
|
||||
if (correlationCaseList.isEmpty() || rowIdx < 0
|
||||
|| rowIdx >= correlationCaseList.size()
|
||||
|| correlationCaseList.get(rowIdx) == null
|
||||
|| correlationCaseList.get(rowIdx).getMessage() == null
|
||||
|| correlationCaseList.get(rowIdx).getMessage().isEmpty()) {
|
||||
return Bundle.OtherOccurrencesCasesTableModel_noData();
|
||||
}
|
||||
|
||||
CorrelationCaseWrapper caseWrapper = correlationCaseList.get(rowIdx);
|
||||
TableColumns columnId = TableColumns.values()[colIdx];
|
||||
return mapCorrelationCase(caseWrapper, columnId);
|
||||
return correlationCaseList.get(rowIdx).getMessage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a column ID to the value in that cell for correlation case wrapper.
|
||||
* Get a correlation case for the selected index. Does not query the Central
|
||||
* Repository so CorrelationCase will be partial missing CR case ID and
|
||||
* other information that is stored in the CR.
|
||||
*
|
||||
* @param correlationCaseWrapper The correlation case wrapper
|
||||
* @param columnId The ID of the cell column.
|
||||
* @param rowIdx the row from the table model which corresponds to the case
|
||||
*
|
||||
* @return The value in the cell.
|
||||
* @return CorrelationCase for the table item specified or null if no
|
||||
* correlation could be found for any reason
|
||||
*/
|
||||
@Messages({"OtherOccurrencesCasesTableModel.noData=No Data."})
|
||||
private Object mapCorrelationCase(CorrelationCaseWrapper correlationCaseWrapper, TableColumns columnId) {
|
||||
String value = Bundle.OtherOccurrencesCasesTableModel_noData();
|
||||
|
||||
switch (columnId) {
|
||||
case CASE_NAME:
|
||||
value = correlationCaseWrapper.getMessage();
|
||||
break;
|
||||
default: //Use default "No data" value.
|
||||
break;
|
||||
CorrelationCase getCorrelationCase(int rowIdx) {
|
||||
//if anything would prevent this from working we will return null
|
||||
if (correlationCaseList.isEmpty() || rowIdx < 0
|
||||
|| rowIdx >= correlationCaseList.size()
|
||||
|| correlationCaseList.get(rowIdx) == null) {
|
||||
return null;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
Object getCorrelationCase(int rowIdx) {
|
||||
return correlationCaseList.get(rowIdx).getCorrelationCase();
|
||||
}
|
||||
|
||||
@@ -107,7 +96,7 @@ public class OtherOccurrencesCasesTableModel extends AbstractTableModel {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add one correlated instance object to the table
|
||||
* Add one correlation case wrapper object to the table
|
||||
*
|
||||
* @param newCorrelationCaseWrapper data to add to the table
|
||||
*/
|
||||
@@ -123,27 +112,4 @@ public class OtherOccurrencesCasesTableModel extends AbstractTableModel {
|
||||
correlationCaseList.clear();
|
||||
fireTableDataChanged();
|
||||
}
|
||||
|
||||
@Messages({"OtherOccurrencesCasesTableModel.case=Case",})
|
||||
enum TableColumns {
|
||||
// Ordering here determines displayed column order in Content Viewer.
|
||||
// If order is changed, update the CellRenderer to ensure correct row coloring.
|
||||
CASE_NAME(Bundle.OtherOccurrencesCasesTableModel_case(), 100);
|
||||
|
||||
private final String columnName;
|
||||
private final int columnWidth;
|
||||
|
||||
TableColumns(String columnName, int columnWidth) {
|
||||
this.columnName = columnName;
|
||||
this.columnWidth = columnWidth;
|
||||
}
|
||||
|
||||
public String columnName() {
|
||||
return columnName;
|
||||
}
|
||||
|
||||
public int columnWidth() {
|
||||
return columnWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+206
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* Central Repository
|
||||
*
|
||||
* 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.centralrepository.contentviewer;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
import org.openide.util.NbBundle;
|
||||
|
||||
/**
|
||||
* Model for cells in the data sources section of the other occurrences data
|
||||
* content viewer
|
||||
*/
|
||||
final class OtherOccurrencesDataSourcesTableModel extends AbstractTableModel {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final Set<DataSourceColumnItem> dataSourceSet = new LinkedHashSet<>();
|
||||
|
||||
/**
|
||||
* Create a table model for displaying data source names
|
||||
*/
|
||||
OtherOccurrencesDataSourcesTableModel() {
|
||||
// This constructor is intentionally empty.
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
return dataSourceSet.size();
|
||||
}
|
||||
|
||||
@NbBundle.Messages({"OtherOccurrencesDataSourcesTableModel.dataSourceName=Data Source Name",
|
||||
"OtherOccurrencesDataSourcesTableModel.noData=No Data."})
|
||||
@Override
|
||||
public String getColumnName(int colIdx) {
|
||||
return Bundle.OtherOccurrencesDataSourcesTableModel_dataSourceName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValueAt(int rowIdx, int colIdx) {
|
||||
//if anything would prevent this from working we will consider it no data for the sake of simplicity
|
||||
if (dataSourceSet.isEmpty() || rowIdx < 0
|
||||
|| rowIdx >= dataSourceSet.size()
|
||||
|| !(dataSourceSet.toArray()[rowIdx] instanceof DataSourceColumnItem)) {
|
||||
return Bundle.OtherOccurrencesDataSourcesTableModel_noData();
|
||||
}
|
||||
return ((DataSourceColumnItem) dataSourceSet.toArray()[rowIdx]).getDataSourceName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the device id of the data source shown at the specified row index
|
||||
*
|
||||
* @param rowIdx the row index of the data source you want the device id for
|
||||
*
|
||||
* @return the device id of the specified data source or an empty string if
|
||||
* a device id could not be retrieved
|
||||
*/
|
||||
String getDeviceIdForRow(int rowIdx) {
|
||||
//if anything would prevent this from working we will return an empty string
|
||||
if (dataSourceSet.isEmpty() || rowIdx < 0
|
||||
|| rowIdx >= dataSourceSet.size()
|
||||
|| !(dataSourceSet.toArray()[rowIdx] instanceof DataSourceColumnItem)) {
|
||||
return "";
|
||||
}
|
||||
return ((DataSourceColumnItem) dataSourceSet.toArray()[rowIdx]).getDeviceId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the case name of the data source shown at the specified row index
|
||||
*
|
||||
* @param rowIdx the row index of the data source you want the case name for
|
||||
*
|
||||
* @return the case name of the specified data source or an empty string if
|
||||
* a case name could not be retrieved
|
||||
*/
|
||||
String getCaseNameForRow(int rowIdx) {
|
||||
//if anything would prevent this from working we will return an empty string
|
||||
if (dataSourceSet.isEmpty() || rowIdx < 0
|
||||
|| rowIdx >= dataSourceSet.size()
|
||||
|| !(dataSourceSet.toArray()[rowIdx] instanceof DataSourceColumnItem)) {
|
||||
return "";
|
||||
}
|
||||
return ((DataSourceColumnItem) dataSourceSet.toArray()[rowIdx]).getCaseName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<String> getColumnClass(int colIdx) {
|
||||
return String.class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add data source information to the table of unique data sources
|
||||
*
|
||||
* @param newNodeData data to add to the table
|
||||
*/
|
||||
void addNodeData(OtherOccurrenceNodeData newNodeData) {
|
||||
dataSourceSet.add(new DataSourceColumnItem((OtherOccurrenceNodeInstanceData) newNodeData));
|
||||
fireTableDataChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the node data table.
|
||||
*/
|
||||
void clearTable() {
|
||||
dataSourceSet.clear();
|
||||
fireTableDataChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Private class for storing data source information in a way that
|
||||
* facilitates de-duping.
|
||||
*/
|
||||
private final class DataSourceColumnItem {
|
||||
|
||||
private final String caseName;
|
||||
private final String deviceId;
|
||||
private final String dataSourceName;
|
||||
|
||||
/**
|
||||
* Create a DataSourceColumnItem given an
|
||||
* OtherOccurrenceNodeInstanceData object
|
||||
*
|
||||
* @param nodeData the OtherOccurrenceNodeInstanceData which contains
|
||||
* the data source information
|
||||
*/
|
||||
private DataSourceColumnItem(OtherOccurrenceNodeInstanceData nodeData) {
|
||||
this(nodeData.getCaseName(), nodeData.getDeviceID(), nodeData.getDataSourceName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a DataSourceColumnItem given a case name, device id, and data
|
||||
* source name
|
||||
*
|
||||
* @param caseName the name of the case the data source exists in
|
||||
* @param deviceId the name of the device id for the data source
|
||||
* @param dataSourceName the name of the data source
|
||||
*/
|
||||
private DataSourceColumnItem(String caseName, String deviceId, String dataSourceName) {
|
||||
this.caseName = caseName;
|
||||
this.deviceId = deviceId;
|
||||
this.dataSourceName = dataSourceName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the device id
|
||||
*
|
||||
* @return the data source's device id
|
||||
*/
|
||||
private String getDeviceId() {
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data source name
|
||||
*
|
||||
* @return the data source's name
|
||||
*/
|
||||
private String getDataSourceName() {
|
||||
return dataSourceName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the case the data source exists in
|
||||
*
|
||||
* @return the name of the case the data source is in
|
||||
*/
|
||||
private String getCaseName() {
|
||||
return caseName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return other instanceof DataSourceColumnItem
|
||||
&& caseName.equals(((DataSourceColumnItem) other).getCaseName())
|
||||
&& dataSourceName.equals(((DataSourceColumnItem) other).getDataSourceName())
|
||||
&& deviceId.equals(((DataSourceColumnItem) other).getDeviceId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(caseName, deviceId, dataSourceName);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-69
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* Central Repository
|
||||
*
|
||||
* Copyright 2015-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.centralrepository.contentviewer;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.table.DefaultTableCellRenderer;
|
||||
import javax.swing.table.TableCellRenderer;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
|
||||
/**
|
||||
* Renderer for cells in the files section of the other occurrences data content viewer
|
||||
*/
|
||||
public class OtherOccurrencesFilesTableCellRenderer implements TableCellRenderer {
|
||||
|
||||
public static final DefaultTableCellRenderer DEFAULT_RENDERER = new DefaultTableCellRenderer();
|
||||
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(
|
||||
JTable table,
|
||||
Object value,
|
||||
boolean isSelected,
|
||||
boolean hasFocus,
|
||||
int row,
|
||||
int column) {
|
||||
Component renderer = DEFAULT_RENDERER.getTableCellRendererComponent(
|
||||
table, value, isSelected, hasFocus, row, column);
|
||||
((JComponent) renderer).setOpaque(true);
|
||||
Color foreground, background;
|
||||
if (isSelected) {
|
||||
foreground = Color.WHITE;
|
||||
background = new Color(51,153,255);
|
||||
} else {
|
||||
String known_status = (String) table.getModel().getValueAt(table.convertRowIndexToModel(row),
|
||||
table.getColumn(OtherOccurrencesFilesTableModel.TableColumns.KNOWN.columnName()).getModelIndex());
|
||||
if (known_status.equals(TskData.FileKnown.BAD.getName())) {
|
||||
foreground = Color.WHITE;
|
||||
background = Color.RED;
|
||||
} else if (known_status.equals(TskData.FileKnown.UNKNOWN.getName())) {
|
||||
foreground = Color.BLACK;
|
||||
background = Color.WHITE;
|
||||
} else {
|
||||
foreground = Color.BLACK;
|
||||
background = Color.WHITE;
|
||||
}
|
||||
}
|
||||
renderer.setForeground(foreground);
|
||||
renderer.setBackground(background);
|
||||
return renderer;
|
||||
}
|
||||
}
|
||||
+48
-110
@@ -19,150 +19,76 @@
|
||||
package org.sleuthkit.autopsy.centralrepository.contentviewer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
|
||||
/**
|
||||
* Model for cells in the files section of the other occurrences data content viewer
|
||||
* Model for cells in the files section of the other occurrences data content
|
||||
* viewer
|
||||
*/
|
||||
public class OtherOccurrencesFilesTableModel extends AbstractTableModel {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final List<String> nodeKeys = new ArrayList<>();
|
||||
private final Map<String, List<OtherOccurrenceNodeData>> nodeMap = new HashMap<>();
|
||||
|
||||
@Messages({"OtherOccurrencesFilesTableModel.device=Device",
|
||||
"OtherOccurrencesFilesTableModel.dataSource=Data Source",
|
||||
"OtherOccurrencesFilesTableModel.path=Path",
|
||||
"OtherOccurrencesFilesTableModel.attribute=Matched Attribute",
|
||||
"OtherOccurrencesFilesTableModel.value=Attribute Value",
|
||||
"OtherOccurrencesFilesTableModel.known=Known",
|
||||
"OtherOccurrencesFilesTableModel.comment=Comment",
|
||||
"OtherOccurrencesFilesTableModel.noData=No Data.",})
|
||||
enum TableColumns {
|
||||
// Ordering here determines displayed column order in Content Viewer.
|
||||
// If order is changed, update the CellRenderer to ensure correct row coloring.
|
||||
ATTRIBUTE(Bundle.OtherOccurrencesFilesTableModel_attribute(), 75),
|
||||
VALUE(Bundle.OtherOccurrencesFilesTableModel_value(), 190),
|
||||
KNOWN(Bundle.OtherOccurrencesFilesTableModel_known(), 25),
|
||||
FILE_PATH(Bundle.OtherOccurrencesFilesTableModel_path(), 470),
|
||||
COMMENT(Bundle.OtherOccurrencesFilesTableModel_comment(), 190);
|
||||
|
||||
private final String columnName;
|
||||
private final int columnWidth;
|
||||
|
||||
TableColumns(String columnName, int columnWidth) {
|
||||
this.columnName = columnName;
|
||||
this.columnWidth = columnWidth;
|
||||
}
|
||||
|
||||
public String columnName() {
|
||||
return columnName;
|
||||
}
|
||||
|
||||
public int columnWidth() {
|
||||
return columnWidth;
|
||||
}
|
||||
};
|
||||
|
||||
private final List<OtherOccurrenceNodeData> nodeDataList = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Create a table model for displaying file names
|
||||
*/
|
||||
OtherOccurrencesFilesTableModel() {
|
||||
|
||||
// This constructor is intentionally empty.
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount() {
|
||||
return TableColumns.values().length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the preferred width that has been configured for this column.
|
||||
*
|
||||
* A value of 0 means that no preferred width has been defined for this
|
||||
* column.
|
||||
*
|
||||
* @param colIdx Column index
|
||||
*
|
||||
* @return preferred column width >= 0
|
||||
*/
|
||||
public int getColumnPreferredWidth(int colIdx) {
|
||||
return TableColumns.values()[colIdx].columnWidth();
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
return nodeDataList.size();
|
||||
return nodeKeys.size();
|
||||
}
|
||||
|
||||
@Messages({"OtherOccurrencesFilesTableModel.fileName=File Name",
|
||||
"OtherOccurrencesFilesTableModel.noData=No Data."})
|
||||
@Override
|
||||
public String getColumnName(int colIdx) {
|
||||
return TableColumns.values()[colIdx].columnName();
|
||||
return Bundle.OtherOccurrencesFilesTableModel_fileName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValueAt(int rowIdx, int colIdx) {
|
||||
if (0 == nodeDataList.size()) {
|
||||
//if anything would prevent this from working we will consider it no data for the sake of simplicity
|
||||
if (nodeMap.isEmpty() || nodeKeys.isEmpty() || rowIdx < 0
|
||||
|| rowIdx >= nodeKeys.size() || nodeKeys.get(rowIdx) == null
|
||||
|| nodeMap.get(nodeKeys.get(rowIdx)) == null
|
||||
|| nodeMap.get(nodeKeys.get(rowIdx)).isEmpty()) {
|
||||
return Bundle.OtherOccurrencesFilesTableModel_noData();
|
||||
}
|
||||
|
||||
OtherOccurrenceNodeData nodeData = nodeDataList.get(rowIdx);
|
||||
TableColumns columnId = TableColumns.values()[colIdx];
|
||||
if (nodeData instanceof OtherOccurrenceNodeMessageData) {
|
||||
return mapNodeMessageData((OtherOccurrenceNodeMessageData) nodeData, columnId);
|
||||
}
|
||||
return mapNodeInstanceData((OtherOccurrenceNodeInstanceData) nodeData, columnId);
|
||||
return FilenameUtils.getName(((OtherOccurrenceNodeInstanceData) nodeMap.get(nodeKeys.get(rowIdx)).get(0)).getFilePath());
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a column ID to the value in that cell for node message data.
|
||||
* Get a list of OtherOccurrenceNodeData that exist for the file which
|
||||
* corresponds to the Index
|
||||
*
|
||||
* @param nodeData The node message data.
|
||||
* @param columnId The ID of the cell column.
|
||||
* @param rowIdx the index of the file to get data for
|
||||
*
|
||||
* @return The value in the cell.
|
||||
* @return a list of OtherOccurrenceNodeData for the specified index or an
|
||||
* empty list if no data was found
|
||||
*/
|
||||
private Object mapNodeMessageData(OtherOccurrenceNodeMessageData nodeData, TableColumns columnId) {
|
||||
if (columnId == TableColumns.ATTRIBUTE) {
|
||||
return nodeData.getDisplayMessage();
|
||||
List<OtherOccurrenceNodeData> getListOfNodesForFile(int rowIdx) {
|
||||
//if anything would prevent this from working return an empty list
|
||||
if (nodeMap.isEmpty() || nodeKeys.isEmpty() || rowIdx < 0
|
||||
|| rowIdx >= nodeKeys.size() || nodeKeys.get(rowIdx) == null
|
||||
|| nodeMap.get(nodeKeys.get(rowIdx)) == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a column ID to the value in that cell for node instance data.
|
||||
*
|
||||
* @param nodeData The node instance data.
|
||||
* @param columnId The ID of the cell column.
|
||||
*
|
||||
* @return The value in the cell.
|
||||
*/
|
||||
private Object mapNodeInstanceData(OtherOccurrenceNodeInstanceData nodeData, TableColumns columnId) {
|
||||
String value = Bundle.OtherOccurrencesFilesTableModel_noData();
|
||||
|
||||
switch (columnId) {
|
||||
case FILE_PATH:
|
||||
value = nodeData.getFilePath();
|
||||
break;
|
||||
case ATTRIBUTE:
|
||||
value = nodeData.getType();
|
||||
break;
|
||||
case VALUE:
|
||||
value = nodeData.getValue();
|
||||
break;
|
||||
case KNOWN:
|
||||
value = nodeData.getKnown().getName();
|
||||
break;
|
||||
case COMMENT:
|
||||
value = nodeData.getComment();
|
||||
break;
|
||||
default: //Use default "No data" value.
|
||||
break;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
Object getRow(int rowIdx) {
|
||||
return nodeDataList.get(rowIdx);
|
||||
return nodeMap.get(nodeKeys.get(rowIdx));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -176,15 +102,27 @@ public class OtherOccurrencesFilesTableModel extends AbstractTableModel {
|
||||
* @param newNodeData data to add to the table
|
||||
*/
|
||||
void addNodeData(OtherOccurrenceNodeData newNodeData) {
|
||||
nodeDataList.add(newNodeData);
|
||||
String newNodeKey = createNodeKey((OtherOccurrenceNodeInstanceData) newNodeData);//FilenameUtils.getName(((OtherOccurrenceNodeInstanceData)newNodeData).getFilePath());
|
||||
List<OtherOccurrenceNodeData> nodeList = nodeMap.get(newNodeKey);
|
||||
if (nodeList == null) {
|
||||
nodeKeys.add(newNodeKey);
|
||||
nodeList = new ArrayList<>();
|
||||
}
|
||||
nodeList.add(newNodeData);
|
||||
nodeMap.put(newNodeKey, nodeList);
|
||||
fireTableDataChanged();
|
||||
}
|
||||
|
||||
private String createNodeKey(OtherOccurrenceNodeInstanceData nodeData) {
|
||||
return nodeData.getCaseName() + nodeData.getDataSourceName() + nodeData.getDeviceID() + nodeData.getFilePath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the node data table.
|
||||
*/
|
||||
void clearTable() {
|
||||
nodeDataList.clear();
|
||||
nodeKeys.clear();
|
||||
nodeMap.clear();
|
||||
fireTableDataChanged();
|
||||
}
|
||||
|
||||
|
||||
@@ -170,13 +170,13 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
|
||||
"GstVideoPanel.cannotProcFile.err");
|
||||
|
||||
//Video playback components
|
||||
private PlayBin gstPlayBin;
|
||||
private volatile PlayBin gstPlayBin;
|
||||
private JavaFxAppSink fxAppSink;
|
||||
private JFXPanel fxPanel;
|
||||
private volatile boolean livePlayBin;
|
||||
private volatile boolean hasError;
|
||||
private Bus.ERROR errorListener;
|
||||
private Bus.STATE_CHANGED stateChangeListener;
|
||||
private Bus.EOS endOfStreamListener;
|
||||
|
||||
//When a video is playing, update the UI every 75 ms
|
||||
//Update progress bar and time label during video playback
|
||||
private final Timer timer = new Timer(75, new VideoPanelUpdater());
|
||||
private static final int PROGRESS_SLIDER_SIZE = 2000;
|
||||
|
||||
@@ -196,7 +196,6 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
|
||||
progressSlider.setMinimum(0);
|
||||
progressSlider.setMaximum(PROGRESS_SLIDER_SIZE);
|
||||
progressSlider.setValue(0);
|
||||
|
||||
//Manage the gstreamer video position when a user is dragging the slider in the panel.
|
||||
progressSlider.addChangeListener(new ChangeListener() {
|
||||
@Override
|
||||
@@ -212,24 +211,49 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//Manage the audio level when the user is adjusting the volumn slider
|
||||
audioSlider.addChangeListener((ChangeEvent event) -> {
|
||||
if (audioSlider.getValueIsAdjusting()) {
|
||||
int audioPercent = audioSlider.getValue() * 2;
|
||||
gstPlayBin.setVolumePercent(audioPercent);
|
||||
double audioPercent = (audioSlider.getValue() * 2.0) / 100.0;
|
||||
gstPlayBin.setVolume(audioPercent);
|
||||
}
|
||||
});
|
||||
|
||||
videoPanel.setLayout(new BoxLayout(videoPanel, BoxLayout.Y_AXIS));
|
||||
fxPanel = new JFXPanel();
|
||||
videoPanel.add(fxPanel);//add jfx ui to JPanel
|
||||
errorListener = new Bus.ERROR() {
|
||||
@Override
|
||||
public void errorMessage(GstObject go, int i, String string) {
|
||||
enableComponents(false);
|
||||
infoLabel.setText(String.format(
|
||||
"<html><font color='red'>%s</font></html>",
|
||||
MEDIA_PLAYER_ERROR_STRING));
|
||||
timer.stop();
|
||||
}
|
||||
};
|
||||
stateChangeListener = new Bus.STATE_CHANGED() {
|
||||
@Override
|
||||
public void stateChanged(GstObject go, State oldState, State currentState, State pendingState) {
|
||||
if (State.PLAYING.equals(currentState)) {
|
||||
playButton.setText("||");
|
||||
} else {
|
||||
playButton.setText("►");
|
||||
}
|
||||
}
|
||||
};
|
||||
endOfStreamListener = new Bus.EOS() {
|
||||
@Override
|
||||
public void endOfStream(GstObject go) {
|
||||
gstPlayBin.seek(ClockTime.ZERO);
|
||||
progressSlider.setValue(0);
|
||||
/**
|
||||
* Keep the video from automatically playing
|
||||
*/
|
||||
Gst.getExecutor().submit(() -> gstPlayBin.pause());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void initGst() throws GstException, UnsatisfiedLinkError {
|
||||
logger.log(Level.INFO, "Attempting initializing of gstreamer for video/audio viewing"); //NON-NLS
|
||||
Gst.init();
|
||||
gstPlayBin = new PlayBin("VideoPlayer");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -241,8 +265,6 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
|
||||
@NbBundle.Messages({"GstVideoPanel.noOpenCase.errMsg=No open case available."})
|
||||
void loadFile(final AbstractFile file) {
|
||||
//Ensure everything is back in the initial state
|
||||
reset();
|
||||
|
||||
infoLabel.setText("");
|
||||
if (file.isDirNameFlagSet(TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC)) {
|
||||
infoLabel.setText(NbBundle.getMessage(this.getClass(), "GstVideoPanel.setupVideo.infoLabel.text"));
|
||||
@@ -252,7 +274,7 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
|
||||
try {
|
||||
//Pushing off initialization to the background
|
||||
extractMediaWorker = new ExtractMedia(file, VideoUtils.getVideoFileInTempDir(file));
|
||||
extractMediaWorker.execute();
|
||||
extractMediaWorker.execute();
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS
|
||||
infoLabel.setText(String.format("<html><font color='red'>%s</font></html>", Bundle.GstVideoPanel_noOpenCase_errMsg()));
|
||||
@@ -261,14 +283,14 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
|
||||
}
|
||||
|
||||
/**
|
||||
* Assume no support on a fresh reset until we begin loading the file
|
||||
* for play.
|
||||
* Assume no support on a fresh reset until we begin loading the file for
|
||||
* play.
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"MediaPlayerPanel.noSupport=File not supported."
|
||||
})
|
||||
void resetComponents() {
|
||||
progressLabel.setText(String.format("%s/%s", Bundle.MediaPlayerPanel_unknownTime(),
|
||||
progressLabel.setText(String.format("%s/%s", Bundle.MediaPlayerPanel_unknownTime(),
|
||||
Bundle.MediaPlayerPanel_unknownTime()));
|
||||
infoLabel.setText(Bundle.MediaPlayerPanel_noSupport());
|
||||
progressSlider.setValue(0);
|
||||
@@ -278,39 +300,24 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
|
||||
* Return this panel to its initial state.
|
||||
*/
|
||||
void reset() {
|
||||
timer.stop();
|
||||
if(livePlayBin && !hasError) {
|
||||
gstPlayBin.stop();
|
||||
}
|
||||
|
||||
hasError = false;
|
||||
livePlayBin = false;
|
||||
gstPlayBin.dispose();
|
||||
|
||||
if (fxAppSink != null) {
|
||||
fxAppSink.clear();
|
||||
}
|
||||
|
||||
videoPanel.removeAll();
|
||||
|
||||
if (extractMediaWorker != null) {
|
||||
extractMediaWorker.cancel(true);
|
||||
}
|
||||
|
||||
timer.stop();
|
||||
if (gstPlayBin != null) {
|
||||
gstPlayBin.stop();
|
||||
gstPlayBin.getBus().disconnect(endOfStreamListener);
|
||||
gstPlayBin.getBus().disconnect(endOfStreamListener);
|
||||
gstPlayBin.getBus().disconnect(endOfStreamListener);
|
||||
gstPlayBin.dispose();
|
||||
fxAppSink.clear();
|
||||
gstPlayBin = null;
|
||||
}
|
||||
videoPanel.removeAll();
|
||||
resetComponents();
|
||||
enableComponents(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the node has been reset but messages from the previous PlayBin are
|
||||
* still firing, ignore them.
|
||||
*/
|
||||
synchronized void setLabelText(String msg) {
|
||||
if (livePlayBin) {
|
||||
infoLabel.setText(msg);
|
||||
}
|
||||
}
|
||||
|
||||
private void enableComponents(boolean isEnabled) {
|
||||
playButton.setEnabled(isEnabled);
|
||||
progressSlider.setEnabled(isEnabled);
|
||||
@@ -442,31 +449,37 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
|
||||
protected void done() {
|
||||
try {
|
||||
super.get();
|
||||
|
||||
//Video is ready for playback. Clean up previous components and create new ones
|
||||
|
||||
if(this.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
//Video is ready for playback. Create new components
|
||||
gstPlayBin = new PlayBin("VideoPlayer", tempFile.toURI());
|
||||
//Create a custom AppSink that hooks into JavaFx panels for video display
|
||||
fxPanel = new JFXPanel();
|
||||
//Configure event handling
|
||||
Bus playBinBus = gstPlayBin.getBus();
|
||||
playBinBus.connect(endOfStreamListener);
|
||||
playBinBus.connect(stateChangeListener);
|
||||
playBinBus.connect(errorListener);
|
||||
|
||||
if(this.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
JFXPanel fxPanel = new JFXPanel();
|
||||
videoPanel.removeAll();
|
||||
videoPanel.setLayout(new BoxLayout(videoPanel, BoxLayout.Y_AXIS));
|
||||
videoPanel.add(fxPanel);
|
||||
fxAppSink = new JavaFxAppSink("JavaFxAppSink", fxPanel);
|
||||
gstPlayBin.setVideoSink(fxAppSink);
|
||||
|
||||
videoPanel.setLayout(new BoxLayout(videoPanel, BoxLayout.Y_AXIS));
|
||||
videoPanel.add(fxPanel);//add jfx ui to JPanel
|
||||
if(this.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Configure event handling
|
||||
attachEOSListener(gstPlayBin); //Handle end of video events
|
||||
attachStateListener(gstPlayBin); //Handle syncing play/pause button to the stream state
|
||||
attachErrorListener(gstPlayBin); //Handle errors gracefully when they are encountered
|
||||
|
||||
//Customize components
|
||||
gstPlayBin.setVolumePercent(audioSlider.getValue() * 2);
|
||||
|
||||
/**
|
||||
* Prepare the PlayBin for playback.
|
||||
*/
|
||||
gstPlayBin.ready();
|
||||
livePlayBin = true;
|
||||
//Customize components
|
||||
gstPlayBin.setVolume((audioSlider.getValue() * 2.0) / 100.0);
|
||||
gstPlayBin.pause();
|
||||
|
||||
timer.start();
|
||||
enableComponents(true);
|
||||
} catch (CancellationException ex) {
|
||||
logger.log(Level.INFO, "Media buffering was canceled."); //NON-NLS
|
||||
@@ -476,69 +489,6 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
|
||||
logger.log(Level.SEVERE, "Fatal error during media buffering.", ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens for the end of stream event, in which case we conveniently
|
||||
* reset the video for the user.
|
||||
*/
|
||||
private void attachEOSListener(PlayBin gstPlayBin) {
|
||||
gstPlayBin.getBus().connect(new Bus.EOS() {
|
||||
@Override
|
||||
public void endOfStream(GstObject go) {
|
||||
gstPlayBin.seek(ClockTime.ZERO);
|
||||
progressSlider.setValue(0);
|
||||
/**
|
||||
* Keep the video from automatically playing
|
||||
*/
|
||||
Gst.getExecutorService().submit(() -> gstPlayBin.pause());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen for state changes and update the play/pause button
|
||||
* accordingly. In addition, handle the state transition from
|
||||
* READY -> PAUSED.
|
||||
*/
|
||||
private void attachStateListener(PlayBin gstPlayBin) {
|
||||
gstPlayBin.getBus().connect(new Bus.STATE_CHANGED() {
|
||||
@Override
|
||||
public void stateChanged(GstObject go, State oldState, State currentState, State pendingState) {
|
||||
/**
|
||||
* If we are ready, it is safe to transition to the pause state
|
||||
* to initiate data-flow for pre-roll frame and duration
|
||||
* information.
|
||||
*/
|
||||
if (State.READY.equals(currentState)) {
|
||||
Gst.getExecutorService().submit(() -> gstPlayBin.pause());
|
||||
timer.start();
|
||||
}
|
||||
|
||||
if (State.PLAYING.equals(currentState)) {
|
||||
playButton.setText("||");
|
||||
} else {
|
||||
playButton.setText("►");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* On error messages disable the UI and show the user an error was
|
||||
* encountered.
|
||||
*/
|
||||
private void attachErrorListener(PlayBin gstPlayBin) {
|
||||
gstPlayBin.getBus().connect(new Bus.ERROR() {
|
||||
@Override
|
||||
public void errorMessage(GstObject go, int i, String string) {
|
||||
enableComponents(false);
|
||||
setLabelText(String.format("<html><font color='red'>%s</font></html>",
|
||||
MEDIA_PLAYER_ERROR_STRING));
|
||||
timer.stop();
|
||||
hasError = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -549,21 +499,19 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
if (!progressSlider.getValueIsAdjusting()) {
|
||||
if(livePlayBin) {
|
||||
long position = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
|
||||
long duration = gstPlayBin.queryDuration(TimeUnit.NANOSECONDS);
|
||||
/**
|
||||
* Duration may not be known until there is video data in the
|
||||
* pipeline. We start this updater when data-flow has just been
|
||||
* initiated so buffering may still be in progress.
|
||||
*/
|
||||
if (duration != -1) {
|
||||
double relativePosition = (double) position / duration;
|
||||
progressSlider.setValue((int) (relativePosition * PROGRESS_SLIDER_SIZE));
|
||||
}
|
||||
|
||||
updateTimeLabel(position, duration);
|
||||
long position = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
|
||||
long duration = gstPlayBin.queryDuration(TimeUnit.NANOSECONDS);
|
||||
/**
|
||||
* Duration may not be known until there is video data in the
|
||||
* pipeline. We start this updater when data-flow has just been
|
||||
* initiated so buffering may still be in progress.
|
||||
*/
|
||||
if (duration != -1) {
|
||||
double relativePosition = (double) position / duration;
|
||||
progressSlider.setValue((int) (relativePosition * PROGRESS_SLIDER_SIZE));
|
||||
}
|
||||
|
||||
updateTimeLabel(position, duration);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -681,7 +629,7 @@ public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaVie
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
private void playButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_playButtonActionPerformed
|
||||
if(gstPlayBin.isPlaying()) {
|
||||
if (gstPlayBin.isPlaying()) {
|
||||
gstPlayBin.pause();
|
||||
} else {
|
||||
gstPlayBin.play();
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
</Property>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new BeanTreeView()"/>
|
||||
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new ExpansionBeanTreeView()"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-2019 Basis Technology Corp.
|
||||
* Copyright 2012-2019 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -26,7 +26,6 @@ import java.beans.PropertyVetoException;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
@@ -47,6 +46,7 @@ import javax.swing.tree.TreeSelectionModel;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.openide.explorer.ExplorerManager;
|
||||
import org.openide.explorer.ExplorerUtils;
|
||||
import org.openide.explorer.view.BeanTreeView;
|
||||
import org.openide.explorer.view.Visualizer;
|
||||
import org.openide.nodes.AbstractNode;
|
||||
import org.openide.nodes.Children;
|
||||
@@ -128,7 +128,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
getTree().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
|
||||
//Hook into the JTree and pre-expand the Views Node and Results node when a user
|
||||
//expands an item in the tree that makes these nodes visible.
|
||||
getTree().addTreeExpansionListener(new TreeExpansionListener() {
|
||||
((ExpansionBeanTreeView )getTree()).addTreeExpansionListener(new TreeExpansionListener() {
|
||||
@Override
|
||||
public void treeExpanded(TreeExpansionEvent event) {
|
||||
//Bail immediately if we are not in the Group By view.
|
||||
@@ -192,7 +192,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
* @param rootChildren Children node containing Results node and Views node.
|
||||
*/
|
||||
private void preExpandNodes(Children rootChildren) {
|
||||
ExpansionBeanTreeView tree = getTree();
|
||||
BeanTreeView tree = getTree();
|
||||
|
||||
Node results = rootChildren.findChild(ResultsNode.NAME);
|
||||
if (!Objects.isNull(results)) {
|
||||
@@ -930,8 +930,8 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
||||
*
|
||||
* @return tree the BeanTreeView
|
||||
*/
|
||||
public ExpansionBeanTreeView getTree() {
|
||||
return (ExpansionBeanTreeView) this.treeView;
|
||||
BeanTreeView getTree() {
|
||||
return (BeanTreeView) this.treeView;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,7 +24,7 @@ import org.openide.explorer.view.BeanTreeView;
|
||||
/**
|
||||
* Adds the ability to listen to user-driven JTree expansion events.
|
||||
*/
|
||||
class ExpansionBeanTreeView extends BeanTreeView {
|
||||
final class ExpansionBeanTreeView extends BeanTreeView {
|
||||
public void addTreeExpansionListener(TreeExpansionListener listener) {
|
||||
this.tree.addTreeExpansionListener(listener);
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ public class HealthMonitorDashboard {
|
||||
|
||||
private final static Logger logger = Logger.getLogger(HealthMonitorDashboard.class.getName());
|
||||
|
||||
private final static String ADMIN_ACCESS_FILE_NAME = "_aiaa"; // NON-NLS
|
||||
private final static String ADMIN_ACCESS_FILE_NAME = "admin"; // NON-NLS
|
||||
private final static String ADMIN_ACCESS_FILE_PATH = Paths.get(PlatformUtil.getUserConfigDirectory(), ADMIN_ACCESS_FILE_NAME).toString();
|
||||
|
||||
Map<String, List<HealthMonitor.DatabaseTimingResult>> timingData;
|
||||
|
||||
+21
-3
@@ -52,6 +52,7 @@ final class EncryptionDetectionDataSourceIngestModule implements DataSourceInges
|
||||
private Blackboard blackboard;
|
||||
private double calculatedEntropy;
|
||||
private final double minimumEntropy;
|
||||
private IngestJobContext context;
|
||||
|
||||
/**
|
||||
* Create an EncryptionDetectionDataSourceIngestModule object that will
|
||||
@@ -67,6 +68,7 @@ final class EncryptionDetectionDataSourceIngestModule implements DataSourceInges
|
||||
public void startUp(IngestJobContext context) throws IngestModule.IngestModuleException {
|
||||
validateSettings();
|
||||
blackboard = Case.getCurrentCase().getServices().getBlackboard();
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Messages({
|
||||
@@ -77,8 +79,6 @@ final class EncryptionDetectionDataSourceIngestModule implements DataSourceInges
|
||||
@Override
|
||||
public ProcessResult process(Content dataSource, DataSourceIngestModuleProgress progressBar) {
|
||||
|
||||
|
||||
|
||||
try {
|
||||
if (dataSource instanceof Image) {
|
||||
|
||||
@@ -92,10 +92,23 @@ final class EncryptionDetectionDataSourceIngestModule implements DataSourceInges
|
||||
int numVolSystemsChecked = 0;
|
||||
progressBar.progress(Bundle.EncryptionDetectionDataSourceIngestModule_processing_message(), 0);
|
||||
for (VolumeSystem volumeSystem : volumeSystems) {
|
||||
|
||||
if (context.dataSourceIngestIsCancelled()) {
|
||||
return ProcessResult.OK;
|
||||
}
|
||||
|
||||
for (Volume volume : volumeSystem.getVolumes()) {
|
||||
|
||||
if (context.dataSourceIngestIsCancelled()) {
|
||||
return ProcessResult.OK;
|
||||
}
|
||||
if (BitlockerDetection.isBitlockerVolume(volume)) {
|
||||
return flagVolume(volume, BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED, Bundle.EncryptionDetectionDataSourceIngestModule_artifactComment_bitlocker());
|
||||
}
|
||||
|
||||
if (context.dataSourceIngestIsCancelled()) {
|
||||
return ProcessResult.OK;
|
||||
}
|
||||
if (isVolumeEncrypted(volume)) {
|
||||
return flagVolume(volume, BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_SUSPECTED, String.format(Bundle.EncryptionDetectionDataSourceIngestModule_artifactComment_suspected(), calculatedEntropy));
|
||||
}
|
||||
@@ -139,6 +152,11 @@ final class EncryptionDetectionDataSourceIngestModule implements DataSourceInges
|
||||
* there was a problem.
|
||||
*/
|
||||
private IngestModule.ProcessResult flagVolume(Volume volume, BlackboardArtifact.ARTIFACT_TYPE artifactType, String comment) {
|
||||
|
||||
if (context.dataSourceIngestIsCancelled()) {
|
||||
return ProcessResult.OK;
|
||||
}
|
||||
|
||||
try {
|
||||
BlackboardArtifact artifact = volume.newArtifact(artifactType);
|
||||
artifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT, EncryptionDetectionModuleFactory.getModuleName(), comment));
|
||||
@@ -198,7 +216,7 @@ final class EncryptionDetectionDataSourceIngestModule implements DataSourceInges
|
||||
* http://www.forensicswiki.org/wiki/TrueCrypt#Detection
|
||||
*/
|
||||
if (volume.getFileSystems().isEmpty()) {
|
||||
calculatedEntropy = EncryptionDetectionTools.calculateEntropy(volume);
|
||||
calculatedEntropy = EncryptionDetectionTools.calculateEntropy(volume, context);
|
||||
if (calculatedEntropy >= minimumEntropy) {
|
||||
return true;
|
||||
}
|
||||
|
||||
+7
-1
@@ -82,6 +82,7 @@ final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter
|
||||
private final Logger logger = services.getLogger(EncryptionDetectionModuleFactory.getModuleName());
|
||||
private FileTypeDetector fileTypeDetector;
|
||||
private Blackboard blackboard;
|
||||
private IngestJobContext context;
|
||||
private double calculatedEntropy;
|
||||
|
||||
private final double minimumEntropy;
|
||||
@@ -107,6 +108,7 @@ final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter
|
||||
public void startUp(IngestJobContext context) throws IngestModule.IngestModuleException {
|
||||
try {
|
||||
validateSettings();
|
||||
this.context = context;
|
||||
blackboard = Case.getCurrentCaseThrows().getServices().getBlackboard();
|
||||
fileTypeDetector = new FileTypeDetector();
|
||||
} catch (FileTypeDetector.FileTypeDetectorInitException ex) {
|
||||
@@ -194,6 +196,10 @@ final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter
|
||||
*/
|
||||
private IngestModule.ProcessResult flagFile(AbstractFile file, BlackboardArtifact.ARTIFACT_TYPE artifactType, String comment) {
|
||||
try {
|
||||
if (context.fileIngestIsCancelled()) {
|
||||
return IngestModule.ProcessResult.OK;
|
||||
}
|
||||
|
||||
BlackboardArtifact artifact = file.newArtifact(artifactType);
|
||||
artifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT,
|
||||
EncryptionDetectionModuleFactory.getModuleName(), comment));
|
||||
@@ -397,7 +403,7 @@ final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter
|
||||
/*
|
||||
* Qualify the entropy.
|
||||
*/
|
||||
calculatedEntropy = EncryptionDetectionTools.calculateEntropy(file);
|
||||
calculatedEntropy = EncryptionDetectionTools.calculateEntropy(file, context);
|
||||
if (calculatedEntropy >= minimumEntropy) {
|
||||
possiblyEncrypted = true;
|
||||
}
|
||||
|
||||
+12
-1
@@ -22,6 +22,7 @@ import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.ingest.IngestJobContext;
|
||||
import org.sleuthkit.autopsy.ingest.IngestModule;
|
||||
import org.sleuthkit.datamodel.ReadContentInputStream;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
@@ -69,6 +70,7 @@ final class EncryptionDetectionTools {
|
||||
* content as possibly encrypted.
|
||||
*
|
||||
* @param content The content to be calculated against.
|
||||
* @param context The ingest job context for cancellation checks
|
||||
*
|
||||
* @return The entropy of the content.
|
||||
*
|
||||
@@ -77,7 +79,7 @@ final class EncryptionDetectionTools {
|
||||
* @throws IOException If there is a failure closing or
|
||||
* reading from the InputStream.
|
||||
*/
|
||||
static double calculateEntropy(Content content) throws ReadContentInputStream.ReadContentInputStreamException, IOException {
|
||||
static double calculateEntropy(Content content, IngestJobContext context) throws ReadContentInputStream.ReadContentInputStreamException, IOException {
|
||||
/*
|
||||
* Logic in this method is based on
|
||||
* https://github.com/willjasen/entropy/blob/master/entropy.java
|
||||
@@ -95,8 +97,17 @@ final class EncryptionDetectionTools {
|
||||
*/
|
||||
int[] byteOccurences = new int[BYTE_OCCURENCES_BUFFER_SIZE];
|
||||
int readByte;
|
||||
long bytesRead = 0;
|
||||
while ((readByte = bin.read()) != -1) {
|
||||
byteOccurences[readByte]++;
|
||||
|
||||
// Do a cancellation check every 10,000 bytes
|
||||
bytesRead++;
|
||||
if (bytesRead % 10000 == 0) {
|
||||
if (context.dataSourceIngestIsCancelled() || context.fileIngestIsCancelled()) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -37,7 +37,7 @@ FileReportDataTypes.knownStatus.text=Known Status
|
||||
FileReportDataTypes.perms.text=Permissions
|
||||
FileReportDataTypes.path.text=Full Path
|
||||
FileReportText.getName.text=Files - Text
|
||||
FileReportText.getDesc.text=A tab delimited text file containing information about individual files in the case.
|
||||
FileReportText.getDesc.text=A delimited text file containing information about individual files in the case.
|
||||
ReportBodyFile.progress.querying=Querying files...
|
||||
ReportBodyFile.ingestWarning.text=Warning, this report was run before ingest services completed\!
|
||||
ReportBodyFile.progress.loading=Loading files...
|
||||
@@ -258,3 +258,5 @@ CreatePortableCasePanel.outputFolderTextField.text=jTextField1
|
||||
CreatePortableCasePanel.chooseOutputFolderButton.text=Choose folder
|
||||
CreatePortableCasePanel.jLabel1.text=Export files tagged as:
|
||||
CreatePortableCasePanel.jLabel2.text=Select output folder:
|
||||
ReportFileTextConfigurationPanel.tabDelimitedButton.text=Tab delimited
|
||||
ReportFileTextConfigurationPanel.commaDelimitedButton.text=Comma delimited
|
||||
|
||||
@@ -85,7 +85,7 @@ FileReportDataTypes.knownStatus.text=Known Status
|
||||
FileReportDataTypes.perms.text=Permissions
|
||||
FileReportDataTypes.path.text=Full Path
|
||||
FileReportText.getName.text=Files - Text
|
||||
FileReportText.getDesc.text=A tab delimited text file containing information about individual files in the case.
|
||||
FileReportText.getDesc.text=A delimited text file containing information about individual files in the case.
|
||||
ReportBodyFile.progress.querying=Querying files...
|
||||
ReportBodyFile.ingestWarning.text=Warning, this report was run before ingest services completed\!
|
||||
ReportBodyFile.progress.loading=Loading files...
|
||||
@@ -306,4 +306,6 @@ CreatePortableCasePanel.outputFolderTextField.text=jTextField1
|
||||
CreatePortableCasePanel.chooseOutputFolderButton.text=Choose folder
|
||||
CreatePortableCasePanel.jLabel1.text=Export files tagged as:
|
||||
CreatePortableCasePanel.jLabel2.text=Select output folder:
|
||||
ReportFileTextConfigurationPanel.tabDelimitedButton.text=Tab delimited
|
||||
ReportFileTextConfigurationPanel.commaDelimitedButton.text=Comma delimited
|
||||
TableReportGenerator.StatusColumn.Header=Review Status
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2013 - 2018 Basis Technology Corp.
|
||||
* Copyright 2013 - 2019 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -19,6 +19,7 @@
|
||||
package org.sleuthkit.autopsy.report;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
@@ -27,6 +28,7 @@ import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.JPanel;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
|
||||
import org.openide.util.NbBundle;
|
||||
@@ -36,18 +38,18 @@ import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* A Tab-delimited text report of the files in the case.
|
||||
* A delimited text report of the files in the case.
|
||||
*
|
||||
* @author jwallace
|
||||
*/
|
||||
class FileReportText implements FileReportModule {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(FileReportText.class.getName());
|
||||
private static final String FILE_NAME = "file-report.txt"; //NON-NLS
|
||||
private static FileReportText instance;
|
||||
private String reportPath;
|
||||
private Writer out;
|
||||
private static final String FILE_NAME = "file-report.txt"; //NON-NLS
|
||||
|
||||
private static FileReportText instance;
|
||||
private ReportFileTextConfigurationPanel configPanel;
|
||||
|
||||
// Get the default implementation of this report
|
||||
public static synchronized FileReportText getDefault() {
|
||||
@@ -62,7 +64,7 @@ class FileReportText implements FileReportModule {
|
||||
this.reportPath = baseReportDir + FILE_NAME;
|
||||
try {
|
||||
out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(this.reportPath)));
|
||||
} catch (IOException ex) {
|
||||
} catch (FileNotFoundException ex) {
|
||||
logger.log(Level.WARNING, "Failed to create report text file", ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
@@ -85,11 +87,12 @@ class FileReportText implements FileReportModule {
|
||||
}
|
||||
}
|
||||
|
||||
private String getTabDelimitedList(List<String> list) {
|
||||
StringBuilder output = new StringBuilder();
|
||||
private String getDelimitedList(List<String> list, String delimiter) {
|
||||
StringBuilder output;
|
||||
output = new StringBuilder();
|
||||
Iterator<String> it = list.iterator();
|
||||
while (it.hasNext()) {
|
||||
output.append(it.next()).append((it.hasNext() ? "\t" : System.lineSeparator()));
|
||||
output.append('"').append(it.next()).append('"').append((it.hasNext() ? delimiter : System.lineSeparator()));
|
||||
}
|
||||
return output.toString();
|
||||
}
|
||||
@@ -101,7 +104,7 @@ class FileReportText implements FileReportModule {
|
||||
titles.add(col.getName());
|
||||
}
|
||||
try {
|
||||
out.write(getTabDelimitedList(titles));
|
||||
out.write(getDelimitedList(titles, configPanel.getDelimiter()));
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.WARNING, "Error when writing headers to report file: {0}", ex); //NON-NLS
|
||||
}
|
||||
@@ -114,7 +117,7 @@ class FileReportText implements FileReportModule {
|
||||
cells.add(type.getValue(toAdd));
|
||||
}
|
||||
try {
|
||||
out.write(getTabDelimitedList(cells));
|
||||
out.write(getDelimitedList(cells, configPanel.getDelimiter()));
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.WARNING, "Error when writing row to report file: {0}", ex); //NON-NLS
|
||||
}
|
||||
@@ -143,4 +146,12 @@ class FileReportText implements FileReportModule {
|
||||
public String getRelativeFilePath() {
|
||||
return FILE_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JPanel getConfigurationPanel() {
|
||||
if (configPanel == null) {
|
||||
configPanel = new ReportFileTextConfigurationPanel();
|
||||
}
|
||||
return configPanel;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
||||
<NonVisualComponents>
|
||||
<Component class="javax.swing.ButtonGroup" name="delimiterGroup">
|
||||
</Component>
|
||||
</NonVisualComponents>
|
||||
<AuxValues>
|
||||
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
<Component id="tabDelimitedButton" min="-2" pref="116" max="-2" attributes="0"/>
|
||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||
<Component id="commaDelimitedButton" min="-2" pref="133" max="-2" attributes="0"/>
|
||||
<EmptySpace pref="166" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="tabDelimitedButton" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="commaDelimitedButton" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace pref="78" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JRadioButton" name="tabDelimitedButton">
|
||||
<Properties>
|
||||
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
|
||||
<ComponentRef name="delimiterGroup"/>
|
||||
</Property>
|
||||
<Property name="selected" type="boolean" value="true"/>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/report/Bundle.properties" key="ReportFileTextConfigurationPanel.tabDelimitedButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JRadioButton" name="commaDelimitedButton">
|
||||
<Properties>
|
||||
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
|
||||
<ComponentRef name="delimiterGroup"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/report/Bundle.properties" key="ReportFileTextConfigurationPanel.commaDelimitedButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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.report;
|
||||
|
||||
/**
|
||||
* Panel for configuring settings for the file text report
|
||||
*/
|
||||
class ReportFileTextConfigurationPanel extends javax.swing.JPanel {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final String TAB_DELIMITER = "\t"; //NON-NLS
|
||||
private static final String COMMA_DELIMITER = ","; //NON-NLS
|
||||
|
||||
/**
|
||||
* Creates new form ReportFileTextConfigurationPanel
|
||||
*/
|
||||
ReportFileTextConfigurationPanel() {
|
||||
initComponents();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the delimiter that was selected on this panel
|
||||
*
|
||||
* @return the selected delimiter
|
||||
*/
|
||||
String getDelimiter() {
|
||||
if (commaDelimitedButton.isSelected()) {
|
||||
return COMMA_DELIMITER;
|
||||
} else {
|
||||
//if the comma button is not selected default to tab since it was previously the only option
|
||||
return TAB_DELIMITER;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called from within the constructor to initialize the form.
|
||||
* WARNING: Do NOT modify this code. The content of this method is always
|
||||
* regenerated by the Form Editor.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
delimiterGroup = new javax.swing.ButtonGroup();
|
||||
tabDelimitedButton = new javax.swing.JRadioButton();
|
||||
commaDelimitedButton = new javax.swing.JRadioButton();
|
||||
|
||||
delimiterGroup.add(tabDelimitedButton);
|
||||
tabDelimitedButton.setSelected(true);
|
||||
org.openide.awt.Mnemonics.setLocalizedText(tabDelimitedButton, org.openide.util.NbBundle.getMessage(ReportFileTextConfigurationPanel.class, "ReportFileTextConfigurationPanel.tabDelimitedButton.text")); // NOI18N
|
||||
|
||||
delimiterGroup.add(commaDelimitedButton);
|
||||
org.openide.awt.Mnemonics.setLocalizedText(commaDelimitedButton, org.openide.util.NbBundle.getMessage(ReportFileTextConfigurationPanel.class, "ReportFileTextConfigurationPanel.commaDelimitedButton.text")); // NOI18N
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(tabDelimitedButton, javax.swing.GroupLayout.PREFERRED_SIZE, 116, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addComponent(commaDelimitedButton, javax.swing.GroupLayout.PREFERRED_SIZE, 133, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addContainerGap(166, Short.MAX_VALUE))
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(tabDelimitedButton)
|
||||
.addComponent(commaDelimitedButton))
|
||||
.addContainerGap(78, Short.MAX_VALUE))
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JRadioButton commaDelimitedButton;
|
||||
private javax.swing.ButtonGroup delimiterGroup;
|
||||
private javax.swing.JRadioButton tabDelimitedButton;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
}
|
||||
@@ -72,6 +72,7 @@ import org.sleuthkit.autopsy.timeline.zooming.ZoomParams;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifactTag;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.ContentTag;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.Tag;
|
||||
@@ -711,10 +712,10 @@ public class EventsRepository {
|
||||
// if the time is legitimate ( greater than zero ) insert it into the db
|
||||
if (eventDescription != null && eventDescription.getTime() > 0) {
|
||||
long objectID = bbart.getObjectID();
|
||||
AbstractFile f = skCase.getAbstractFileById(objectID);
|
||||
long datasourceID = f.getDataSource().getId();
|
||||
Content content = skCase.getContentById(objectID);
|
||||
long datasourceID = content.getDataSource().getId();
|
||||
long artifactID = bbart.getArtifactID();
|
||||
Set<String> hashSets = f.getHashSetNames();
|
||||
Set<String> hashSets = content.getHashSetNames();
|
||||
List<BlackboardArtifactTag> tags = tagsManager.getBlackboardArtifactTagsByArtifact(bbart);
|
||||
String fullDescription = eventDescription.getFullDescription();
|
||||
String medDescription = eventDescription.getMedDescription();
|
||||
|
||||
Reference in New Issue
Block a user