Merge pull request #7240 from eugene7646/new_cr_artifacts

Merged develop and resolved conflicts
This commit is contained in:
eugene7646 2021-09-07 16:50:53 -04:00 committed by GitHub
commit e74d948572
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
161 changed files with 5879 additions and 4255 deletions

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2017 Basis Technology Corp.
* Copyright 2011-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -43,6 +43,7 @@ import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
import org.sleuthkit.autopsy.coreutils.Version;
import org.sleuthkit.autopsy.guiutils.JFileChooserFactory;
/**
* The action associated with the Case/Open Case menu item via the layer.xml
@ -64,6 +65,8 @@ public final class CaseOpenAction extends CallableSystemAction implements Action
private static final Logger LOGGER = Logger.getLogger(CaseOpenAction.class.getName());
private final FileFilter caseMetadataFileFilter;
private final JFileChooserFactory fileChooserHelper;
/**
* Constructs the action associated with the Case/Open Case menu item via
* the layer.xml file, a toolbar button, and the Open Case button of the
@ -72,6 +75,7 @@ public final class CaseOpenAction extends CallableSystemAction implements Action
*/
public CaseOpenAction() {
caseMetadataFileFilter = new FileNameExtensionFilter(NbBundle.getMessage(CaseOpenAction.class, "CaseOpenAction.autFilter.title", Version.getName(), CaseMetadata.getFileExtension()), CaseMetadata.getFileExtension().substring(1));
fileChooserHelper = new JFileChooserFactory();
}
/**
@ -80,7 +84,7 @@ public final class CaseOpenAction extends CallableSystemAction implements Action
* to open the case described by the file.
*/
void openCaseSelectionWindow() {
JFileChooser fileChooser = new JFileChooser();
JFileChooser fileChooser = fileChooserHelper.getChooser();
fileChooser.setDragEnabled(false);
fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
fileChooser.setMultiSelectionEnabled(false);

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2018 Basis Technology Corp.
* Copyright 2011-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -35,6 +35,7 @@ import org.sleuthkit.autopsy.coreutils.DriveUtils;
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
import org.sleuthkit.autopsy.coreutils.PathValidator;
import org.sleuthkit.autopsy.coreutils.TimeZoneUtils;
import org.sleuthkit.autopsy.guiutils.JFileChooserFactory;
import org.sleuthkit.datamodel.HashUtility;
/**
@ -48,8 +49,10 @@ public class ImageFilePanel extends JPanel implements DocumentListener {
private static final long serialVersionUID = 1L;
private static final String PROP_LASTIMAGE_PATH = "LBL_LastImage_PATH"; //NON-NLS
private static final String[] SECTOR_SIZE_CHOICES = {"Auto Detect", "512", "1024", "2048", "4096"};
private final JFileChooser fileChooser = new JFileChooser();
private final JFileChooserFactory fileChooserHelper = new JFileChooserFactory();
private JFileChooser fileChooser;
private final String contextName;
private final List<FileFilter> fileChooserFilters;
/**
* Creates new form ImageFilePanel
@ -73,14 +76,7 @@ public class ImageFilePanel extends JPanel implements DocumentListener {
sectorSizeComboBox.setSelectedIndex(0);
errorLabel.setVisible(false);
fileChooser.setDragEnabled(false);
fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
fileChooser.setMultiSelectionEnabled(false);
fileChooserFilters.forEach(fileChooser::addChoosableFileFilter);
if (fileChooserFilters.isEmpty() == false) {
fileChooser.setFileFilter(fileChooserFilters.get(0));
}
this.fileChooserFilters = fileChooserFilters;
}
/**
@ -132,6 +128,21 @@ public class ImageFilePanel extends JPanel implements DocumentListener {
private JTextField getSha256TextField() {
return sha256HashTextField;
}
private JFileChooser getChooser() {
if(fileChooser == null) {
fileChooser = fileChooserHelper.getChooser();
fileChooser.setDragEnabled(false);
fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
fileChooser.setMultiSelectionEnabled(false);
fileChooserFilters.forEach(fileChooser::addChoosableFileFilter);
if (fileChooserFilters.isEmpty() == false) {
fileChooser.setFileFilter(fileChooserFilters.get(0));
}
}
return fileChooser;
}
/**
* This method is called from within the constructor to initialize the form.
@ -298,12 +309,13 @@ public class ImageFilePanel extends JPanel implements DocumentListener {
String oldText = getContentPaths();
// set the current directory of the FileChooser if the ImagePath Field is valid
File currentDir = new File(oldText);
JFileChooser chooser = getChooser();
if (currentDir.exists()) {
fileChooser.setCurrentDirectory(currentDir);
chooser.setCurrentDirectory(currentDir);
}
if (fileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
String path = fileChooser.getSelectedFile().getPath();
if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
String path = chooser.getSelectedFile().getPath();
if (path.endsWith(".001")) {
String zeroX3_path = StringUtils.removeEnd(path, ".001") + ".000";
if (new File(zeroX3_path).exists()) {

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2018 Basis Technology Corp.
* Copyright 2011-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -32,6 +32,7 @@ import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
import org.sleuthkit.autopsy.coreutils.TimeZoneUtils;
import org.sleuthkit.autopsy.guiutils.JFileChooserFactory;
import org.sleuthkit.autopsy.imagewriter.ImageWriterSettings;
/**
@ -58,7 +59,8 @@ final class LocalDiskPanel extends JPanel {
private static final long serialVersionUID = 1L;
private LocalDisk localDisk;
private boolean enableNext = false;
private final JFileChooser fc = new JFileChooser();
private JFileChooser fc;
private final JFileChooserFactory chooserHelper;
/**
* Creates new form LocalDiskPanel
@ -68,6 +70,7 @@ final class LocalDiskPanel extends JPanel {
customInit();
createTimeZoneList();
createSectorSizeList();
chooserHelper = new JFileChooserFactory();
}
/**
@ -261,6 +264,7 @@ final class LocalDiskPanel extends JPanel {
}//GEN-LAST:event_pathTextFieldKeyReleased
private void browseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browseButtonActionPerformed
fc = chooserHelper.getChooser();
String oldText = pathTextField.getText();
// set the current directory of the FileChooser if the ImagePath Field is valid
File currentFile = new File(oldText);

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2012-2018 Basis Technology Corp.
* Copyright 2012-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -32,6 +32,7 @@ import org.openide.util.NbBundle;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.coreutils.DriveUtils;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.guiutils.JFileChooserFactory;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
@ -45,7 +46,8 @@ class MissingImageDialog extends javax.swing.JDialog {
long obj_id;
SleuthkitCase db;
private final JFileChooser fileChooser = new JFileChooser();
private JFileChooser fileChooser;
private final JFileChooserFactory chooserHelper;
/**
* Instantiate a MissingImageDialog.
@ -58,17 +60,8 @@ class MissingImageDialog extends javax.swing.JDialog {
this.obj_id = obj_id;
this.db = db;
initComponents();
fileChooser.setDragEnabled(false);
fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
fileChooser.setMultiSelectionEnabled(false);
List<FileFilter> fileFiltersList = ImageDSProcessor.getFileFiltersList();
for (FileFilter fileFilter : fileFiltersList) {
fileChooser.addChoosableFileFilter(fileFilter);
}
fileChooser.setFileFilter(fileFiltersList.get(0));
chooserHelper = new JFileChooserFactory();
selectButton.setEnabled(false);
}
@ -270,6 +263,19 @@ class MissingImageDialog extends javax.swing.JDialog {
private void browseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browseButtonActionPerformed
if(fileChooser == null) {
fileChooser = chooserHelper.getChooser();
fileChooser.setDragEnabled(false);
fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
fileChooser.setMultiSelectionEnabled(false);
List<FileFilter> fileFiltersList = ImageDSProcessor.getFileFiltersList();
for (FileFilter fileFilter : fileFiltersList) {
fileChooser.addChoosableFileFilter(fileFilter);
}
fileChooser.setFileFilter(fileFiltersList.get(0));
}
String oldText = pathNameTextField.getText();
lbWarning.setText("");
// set the current directory of the FileChooser if the ImagePath Field is valid

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2020 Basis Technology Corp.
* Copyright 2011-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -30,6 +30,7 @@ import javax.swing.event.DocumentListener;
import org.sleuthkit.autopsy.casemodule.Case.CaseType;
import org.sleuthkit.autopsy.coreutils.PathValidator;
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
import org.sleuthkit.autopsy.guiutils.JFileChooserFactory;
/**
* The JPanel for the first page of the new case wizard.
@ -37,7 +38,7 @@ import org.sleuthkit.autopsy.coreutils.PlatformUtil;
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
final class NewCaseVisualPanel1 extends JPanel implements DocumentListener {
private final JFileChooser fileChooser = new JFileChooser();
private final JFileChooserFactory fileChooserHelper = new JFileChooserFactory();
private final NewCaseWizardPanel1 wizPanel;
/**
@ -353,8 +354,9 @@ final class NewCaseVisualPanel1 extends JPanel implements DocumentListener {
* @param evt the action event
*/
private void caseDirBrowseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_caseDirBrowseButtonActionPerformed
JFileChooser fileChooser = fileChooserHelper.getChooser();
fileChooser.setDragEnabled(false);
if (!caseParentDirTextField.getText().trim().equals("")) {
if (!caseParentDirTextField.getText().trim().isEmpty()) {
fileChooser.setCurrentDirectory(new File(caseParentDirTextField.getText()));
}
fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 Basis Technology Corp.
* Copyright 2019-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");

View File

@ -87,27 +87,10 @@ public final class OtherOccurrences {
if (osAccountAddr.isPresent()) {
try {
for (OsAccountInstance instance : osAccount.getOsAccountInstances()) {
DataSource osAccountDataSource = instance.getDataSource();
try {
CorrelationCase correlationCase = CentralRepository.getInstance().getCase(Case.getCurrentCaseThrows());
CorrelationAttributeInstance correlationAttributeInstance = new CorrelationAttributeInstance(
CentralRepository.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.OSACCOUNT_TYPE_ID),
osAccountAddr.get(),
correlationCase,
CorrelationDataSource.fromTSKDataSource(correlationCase, instance.getDataSource()),
"",
"",
TskData.FileKnown.KNOWN,
osAccount.getId());
for (OsAccountInstance instance : osAccount.getOsAccountInstances()) {
CorrelationAttributeInstance correlationAttributeInstance = CorrelationAttributeUtil.makeCorrAttr(instance.getOsAccount(), instance.getDataSource());
if (correlationAttributeInstance != null) {
ret.add(correlationAttributeInstance);
} catch (CentralRepoException ex) {
logger.log(Level.SEVERE, String.format("Cannot get central repository for OsAccount: %s.", osAccountAddr.get()), ex); //NON-NLS
} catch (NoCurrentCaseException ex) {
logger.log(Level.WARNING, String.format("Exception while getting open case looking up osAccount %s.", osAccountAddr.get()), ex); //NON-NLS
} catch (CorrelationAttributeNormalizationException ex) {
logger.log(Level.SEVERE, String.format("Exception with Correlation Attribute Normalization for osAccount %s.", osAccountAddr.get()), ex); //NON-NLS
}
}
} catch (TskCoreException ex) {

View File

@ -40,6 +40,8 @@ import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.DataArtifact;
import org.sleuthkit.datamodel.HashUtility;
import org.sleuthkit.datamodel.InvalidAccountIDException;
import org.sleuthkit.datamodel.OsAccount;
import org.sleuthkit.datamodel.OsAccountInstance;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
@ -568,6 +570,51 @@ public class CorrelationAttributeUtil {
}
}
/**
* Makes a correlation attribute instance of a given type from an OS
* account. Checks address if it is null, or one of the ones always present
* on a windows system and thus not unique.
*
* @param osAccoun The OS account.
* @param dataSource The data source content object.
*
* @return The correlation attribute instance or null, if an error occurred.
*/
public static CorrelationAttributeInstance makeCorrAttr(OsAccount osAccount, Content dataSource) {
Optional<String> accountAddr = osAccount.getAddr();
// Check address if it is null or one of the ones below we want to ignore it since they will always be one a windows system
// and they are not unique
if (!accountAddr.isPresent() || accountAddr.get().equals("S-1-5-18") || accountAddr.get().equals("S-1-5-19") || accountAddr.get().equals("S-1-5-20")) {
return null;
}
try {
CorrelationCase correlationCase = CentralRepository.getInstance().getCase(Case.getCurrentCaseThrows());
CorrelationAttributeInstance correlationAttributeInstance = new CorrelationAttributeInstance(
CentralRepository.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.OSACCOUNT_TYPE_ID),
accountAddr.get(),
correlationCase,
CorrelationDataSource.fromTSKDataSource(correlationCase, dataSource),
"",
"",
TskData.FileKnown.KNOWN,
osAccount.getId());
return correlationAttributeInstance;
} catch (CentralRepoException ex) {
logger.log(Level.SEVERE, String.format("Cannot get central repository for OsAccount: %s.", accountAddr.get()), ex); //NON-NLS
return null;
} catch (NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS
return null;
} catch (CorrelationAttributeNormalizationException ex) {
logger.log(Level.SEVERE, "Exception with Correlation Attribute Normalization.", ex); //NON-NLS
return null;
}
}
/**
* Gets the correlation attribute instance for a file.
*

View File

@ -664,7 +664,8 @@ public final class CaseEventListener implements PropertyChangeListener {
"CaseEventsListener.prevCaseComment.text=Users seen in previous cases",
"CaseEventsListener.prevExists.text=Previously Seen Users (Central Repository)"})
/**
* Add OsAccount Instance to CR and find interesting items based on the OsAccount
* Add OsAccount Instance to CR and find interesting items based on the
* OsAccount
*/
private final class OsAccountInstancesAddedTask implements Runnable {
@ -680,7 +681,7 @@ public final class CaseEventListener implements PropertyChangeListener {
@Override
public void run() {
//Nothing to do here if the central repo is not enabled or if ingest is running but is set to not save data/make artifacts
if (!CentralRepository.isEnabled()
if (!CentralRepository.isEnabled()
|| (IngestManager.getInstance().isIngestRunning() && !(IngestEventsListener.isFlagSeenDevices() || IngestEventsListener.shouldCreateCrProperties()))) {
return;
}
@ -690,27 +691,15 @@ public final class CaseEventListener implements PropertyChangeListener {
for (OsAccountInstance osAccountInstance : addedOsAccountNew) {
try {
OsAccount osAccount = osAccountInstance.getOsAccount();
Optional<String> accountAddr = osAccount.getAddr();
// Check address if it is null or one of the ones below we want to ignore it since they will always be one a windows system
// and they are not unique
if (!accountAddr.isPresent() || accountAddr.get().equals("S-1-5-18") || accountAddr.get().equals("S-1-5-19") || accountAddr.get().equals("S-1-5-20")) {
CorrelationAttributeInstance correlationAttributeInstance = CorrelationAttributeUtil.makeCorrAttr(osAccount, osAccountInstance.getDataSource());
if (correlationAttributeInstance == null) {
return;
}
Optional<String> accountAddr = osAccount.getAddr();
try {
CorrelationCase correlationCase = CentralRepository.getInstance().getCase(Case.getCurrentCaseThrows());
CorrelationAttributeInstance correlationAttributeInstance = new CorrelationAttributeInstance(
CentralRepository.getInstance().getCorrelationTypeById(CorrelationAttributeInstance.OSACCOUNT_TYPE_ID),
accountAddr.get(),
correlationCase,
CorrelationDataSource.fromTSKDataSource(correlationCase, osAccountInstance.getDataSource()),
"",
"",
TskData.FileKnown.KNOWN,
osAccount.getId());
// Save to the database if requested
if(IngestEventsListener.shouldCreateCrProperties()) {
if (IngestEventsListener.shouldCreateCrProperties()) {
dbManager.addArtifactInstance(correlationAttributeInstance);
}
@ -766,14 +755,11 @@ public final class CaseEventListener implements PropertyChangeListener {
}
}
} catch (CentralRepoException ex) {
LOGGER.log(Level.SEVERE, String.format("Cannot get central repository for OsAccount: %s.", accountAddr.get()), ex); //NON-NLS
} catch (NoCurrentCaseException ex) {
LOGGER.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS
} catch (CorrelationAttributeNormalizationException ex) {
LOGGER.log(Level.SEVERE, "Exception with Correlation Attribute Normalization.", ex); //NON-NLS
} catch (CentralRepoException ex) {
LOGGER.log(Level.SEVERE, String.format("Cannot get central repository for OsAccount: %s.", accountAddr.get()), ex); //NON-NLS
}
} catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Cannot get central repository for OsAccount: " + "OsAccount", ex);
}

View File

@ -75,6 +75,7 @@ import org.sleuthkit.datamodel.TskData;
*/
@NbBundle.Messages({"IngestEventsListener.ingestmodule.name=Central Repository"})
public class IngestEventsListener {
private static final Logger LOGGER = Logger.getLogger(CorrelationAttributeInstance.class.getName());
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.DATA_SOURCE_ANALYSIS_COMPLETED);
private static final Set<IngestManager.IngestModuleEvent> INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(DATA_ADDED);
@ -513,8 +514,8 @@ public class IngestEventsListener {
try {
dataSource = ((DataSourceAnalysisEvent) event).getDataSource();
/*
* We only care about Images for the purpose of
* updating hash values.
* We only care about Images for the purpose of updating hash
* values.
*/
if (!(dataSource instanceof Image)) {
return;

View File

@ -13,4 +13,3 @@ IngestSettingsPanel.createCorrelationPropertiesCheckbox.text=\u30a2\u30a4\u30c6\
IngestSettingsPanel.flagPreviouslySeenDevicesCheckbox.text=\u4ee5\u524d\u306e\u30b1\u30fc\u30b9\u3067\u898b\u3089\u308c\u305f\u30c7\u30d0\u30a4\u30b9\u306b\u30d5\u30e9\u30b0\u3092\u4ed8\u3051\u308b
IngestSettingsPanel.flagTaggedNotableItemsCheckbox.text=\u4ee5\u524d\u306b\u6ce8\u76ee\u3059\u3079\u304d\u30bf\u30b0\u4ed8\u3051\u305f\u30a2\u30a4\u30c6\u30e0\u306b\u30d5\u30e9\u30b0\u3092\u4ed8\u3051\u308b
IngestSettingsPanel.ingestSettingsLabel.text=\u53d6\u8fbc\u307f\u8a2d\u5b9a
IngestSettingsPanel.flagUniqueAppsCheckbox.text=\u4ee5\u524d\u306e\u30b1\u30fc\u30b9\u3067\u898b\u3089\u308c\u305f\u30c7\u30d0\u30a4\u30b9\u306b\u30d5\u30e9\u30b0\u3092\u4ed8\u3051\u308b

View File

@ -1,8 +1,9 @@
CommandLineIngestSettingPanel_empty_report_name_mgs=Report profile name was empty, no profile created.
CommandLineIngestSettingPanel_existing_report_name_mgs=Report profile name was already exists, no profile created.
CommandLineIngestSettingPanel_invalid_report_name_mgs=Report profile name contained illegal characters, no profile created.
CommandListIngestSettingsPanel_Default_Report_DisplayName=Default
CommandListIngestSettingsPanel_Make_Config=Make new profile...
CommandListIngestSettingsPanel_Report_Name_Msg=Please supply a report profile name (commas not allowed):
CommandListIngestSettingsPanel_Report_Name_Msg=Please supply a report profile name (letters, digits, and underscore characters only):
OpenIDE-Module-Name=CommandLineAutopsy
OptionsCategory_Keywords_Command_Line_Ingest_Settings=Command Line Ingest Settings
OptionsCategory_Keywords_General=Options

View File

@ -280,18 +280,15 @@ public class CommandLineIngestSettingsPanel extends javax.swing.JPanel {
add(nodePanel, java.awt.BorderLayout.CENTER);
}// </editor-fold>//GEN-END:initComponents
@Messages({
"CommandListIngestSettingsPanel_Report_Name_Msg=Please supply a report profile name (commas not allowed):",
"CommandListIngestSettingsPanel_Report_Name_Msg=Please supply a report profile name (letters, digits, and underscore characters only):",
"CommandLineIngestSettingPanel_empty_report_name_mgs=Report profile name was empty, no profile created.",
"CommandLineIngestSettingPanel_existing_report_name_mgs=Report profile name was already exists, no profile created."
"CommandLineIngestSettingPanel_existing_report_name_mgs=Report profile name was already exists, no profile created.",
"CommandLineIngestSettingPanel_invalid_report_name_mgs=Report profile name contained illegal characters, no profile created."
})
private void bnEditReportSettingsActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnEditReportSettingsActionPerformed
String reportName = getReportName();
if (reportName.equals(Bundle.CommandListIngestSettingsPanel_Make_Config())) {
reportName = JOptionPane.showInputDialog(this, Bundle.CommandListIngestSettingsPanel_Report_Name_Msg());
// sanitize report name. Remove all commas because in CommandLineOptionProcessor we use commas
// to separate multiple report names
reportName = reportName.replaceAll(",", "");
// User hit cancel
if (reportName == null) {
@ -302,6 +299,15 @@ public class CommandLineIngestSettingsPanel extends javax.swing.JPanel {
} else if (doesReportProfileNameExist(reportName)) {
JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), Bundle.CommandLineIngestSettingPanel_existing_report_name_mgs());
return;
} else {
// sanitize report name
String originalReportName = reportName;
reportName = reportName.replaceAll("[^A-Za-z0-9_]", "");
if (reportName.isEmpty() || (!(originalReportName.equals(reportName)))) {
// report name contained only invalid characters, display error
JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), Bundle.CommandLineIngestSettingPanel_invalid_report_name_mgs());
return;
}
}
}

View File

@ -24,6 +24,7 @@ import java.awt.KeyboardFocusManager;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
@ -38,6 +39,7 @@ import org.openide.nodes.AbstractNode;
import org.openide.nodes.Node;
import org.openide.util.Lookup;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.communications.ModifiableProxyLookup;
import org.sleuthkit.autopsy.corecomponents.TableFilterNode;
import org.sleuthkit.autopsy.coreutils.Logger;
@ -46,8 +48,10 @@ import org.sleuthkit.autopsy.directorytree.DataResultFilterNode;
import org.sleuthkit.datamodel.AbstractContent;
import org.sleuthkit.datamodel.Account;
import org.sleuthkit.datamodel.BlackboardArtifact;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.SleuthkitCase;
/**
* A Panel that shows the media (thumbnails) for the selected account.
@ -65,6 +69,7 @@ final class MediaViewer extends JPanel implements RelationshipsViewer, ExplorerM
private final MessageDataContent contentViewer;
private MediaViewerWorker worker;
private SelectionWorker selectionWorker;
@Messages({
"MediaViewer_Name=Media Attachments"
@ -106,11 +111,15 @@ final class MediaViewer extends JPanel implements RelationshipsViewer, ExplorerM
@Override
public void setSelectionInfo(SelectionInfo info) {
contentViewer.setNode(null);
thumbnailViewer.resetComponent();
thumbnailViewer.setNode(null);
if (worker != null) {
worker.cancel(true);
}
if(selectionWorker != null) {
selectionWorker.cancel(true);
}
worker = new MediaViewerWorker(info);
@ -181,21 +190,66 @@ final class MediaViewer extends JPanel implements RelationshipsViewer, ExplorerM
*/
private void handleNodeSelectionChange() {
final Node[] nodes = tableEM.getSelectedNodes();
contentViewer.setNode(null);
if(selectionWorker != null) {
selectionWorker.cancel(true);
}
if (nodes != null && nodes.length == 1) {
AbstractContent thumbnail = nodes[0].getLookup().lookup(AbstractContent.class);
if (thumbnail != null) {
try {
Content parentContent = thumbnail.getParent();
if (parentContent != null && parentContent instanceof BlackboardArtifact) {
contentViewer.setNode(new BlackboardArtifactNode((BlackboardArtifact) parentContent));
}
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Unable to get parent Content from AbstraceContent instance.", ex); //NON-NLS
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
selectionWorker = new SelectionWorker(thumbnail);
selectionWorker.execute();
}
}
}
/**
* A SwingWorker to get the artifact associated with the selected thumbnail.
*/
private class SelectionWorker extends SwingWorker<BlackboardArtifact, Void> {
private final AbstractContent thumbnail;
// Construct a SelectionWorker.
SelectionWorker(AbstractContent thumbnail) {
this.thumbnail = thumbnail;
}
@Override
protected BlackboardArtifact doInBackground() throws Exception {
SleuthkitCase skCase = Case.getCurrentCase().getSleuthkitCase();
List<BlackboardArtifact> artifactsList = skCase.getBlackboardArtifacts(TSK_ASSOCIATED_OBJECT, thumbnail.getId());
for (BlackboardArtifact contextArtifact : artifactsList) {
BlackboardAttribute associatedArtifactAttribute = contextArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT));
if (associatedArtifactAttribute != null) {
long artifactId = associatedArtifactAttribute.getValueLong();
return contextArtifact.getSleuthkitCase().getBlackboardArtifact(artifactId);
}
}
} else {
contentViewer.setNode(null);
return null;
}
@Override
protected void done() {
if (isCancelled()) {
return;
}
try {
BlackboardArtifact artifact = get();
if (artifact != null) {
contentViewer.setNode(new BlackboardArtifactNode(artifact));
} else {
contentViewer.setNode(null);
}
} catch (InterruptedException | ExecutionException ex) {
logger.log(Level.SEVERE, "Failed message viewer based on thumbnail selection. thumbnailID = " + thumbnail.getId(), ex);
} finally {
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
}
}
}

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2018-2019 Basis Technology Corp.
* Copyright 2018-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -56,6 +56,7 @@ import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.guiutils.JFileChooserFactory;
import org.sleuthkit.datamodel.TskCoreException;
import org.xml.sax.SAXException;
@ -75,6 +76,8 @@ class PListViewer extends javax.swing.JPanel implements FileTypeViewer, Explorer
private ExplorerManager explorerManager;
private NSObject rootDict;
private final JFileChooserFactory fileChooserHelper = new JFileChooserFactory();
/**
* Creates new form PListViewer
@ -203,7 +206,7 @@ class PListViewer extends javax.swing.JPanel implements FileTypeViewer, Explorer
return;
}
final JFileChooser fileChooser = new JFileChooser();
final JFileChooser fileChooser = fileChooserHelper.getChooser();
fileChooser.setCurrentDirectory(new File(openCase.getExportDirectory()));
fileChooser.setFileFilter(new FileNameExtensionFilter("XML file", "xml"));

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2018-2019 Basis Technology Corp.
* Copyright 2018-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -49,6 +49,7 @@ import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.autopsy.coreutils.SQLiteTableReader;
import org.sleuthkit.autopsy.guiutils.JFileChooserFactory;
/**
* A file content viewer for SQLite database files.
@ -74,6 +75,8 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
private int currPage = 0; // curr page of rows being displayed
SwingWorker<?, ?> worker;
private final JFileChooserFactory chooserHelper = new JFileChooserFactory();
/**
* Constructs a file content viewer for SQLite database files.
@ -280,7 +283,7 @@ class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer {
private void exportCsvButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_exportCsvButtonActionPerformed
Case openCase = Case.getCurrentCase();
File caseDirectory = new File(openCase.getExportDirectory());
JFileChooser fileChooser = new JFileChooser();
JFileChooser fileChooser = chooserHelper.getChooser();
fileChooser.setDragEnabled(false);
fileChooser.setCurrentDirectory(caseDirectory);
//Set a filter to let the filechooser only work for csv files

View File

@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.contentviewers.analysisresults;
import java.awt.Component;
import java.util.Objects;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.SwingWorker;
@ -28,30 +29,26 @@ import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
import org.sleuthkit.autopsy.datamodel.TskContentItem;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
import org.sleuthkit.datamodel.AnalysisResult;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.autopsy.datamodel.AnalysisResultItem;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Displays a list of analysis results as a content viewer.
* A content viewer that displays the analysis results for a Content object.
*/
@ServiceProvider(service = DataContentViewer.class, position = 7)
public class AnalysisResultsContentViewer implements DataContentViewer {
private static final Logger logger = Logger.getLogger(AnalysisResultsContentPanel.class.getName());
// isPreferred value
private static final int PREFERRED_VALUE = 3;
private final AnalysisResultsViewModel viewModel = new AnalysisResultsViewModel();
private final AnalysisResultsContentPanel panel = new AnalysisResultsContentPanel();
private SwingWorker<?, ?> worker = null;
@NbBundle.Messages({
"AnalysisResultsContentViewer_title=Analysis Results"
})
@ -126,29 +123,27 @@ public class AnalysisResultsContentViewer implements DataContentViewer {
@Override
public boolean isSupported(Node node) {
if (node == null) {
if (Objects.isNull(node)) {
return false;
}
// There needs to either be a file with an AnalysisResult or an AnalysisResult in the lookup.
for (Content content : node.getLookup().lookupAll(Content.class)) {
if (content instanceof AnalysisResult) {
return true;
}
if (content == null || content instanceof BlackboardArtifact) {
continue;
}
AnalysisResultItem analysisResultItem = node.getLookup().lookup(AnalysisResultItem.class);
if (Objects.nonNull(analysisResultItem)) {
return true;
}
TskContentItem contentItem = node.getLookup().lookup(TskContentItem.class);
if (!Objects.isNull(contentItem)) {
Content content = contentItem.getTskContent();
try {
if (Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard().hasAnalysisResults(content.getId())) {
return true;
}
} catch (NoCurrentCaseException | TskCoreException ex) {
logger.log(Level.SEVERE, "Unable to get analysis results for file with obj id " + content.getId(), ex);
logger.log(Level.SEVERE, String.format("Error getting analysis results for Content (object ID = %d)", content.getId()), ex);
}
}
return false;
}
@ -156,4 +151,5 @@ public class AnalysisResultsContentViewer implements DataContentViewer {
public int isPreferred(Node node) {
return PREFERRED_VALUE;
}
}

View File

@ -20,9 +20,8 @@ package org.sleuthkit.autopsy.contentviewers.analysisresults;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -31,14 +30,14 @@ import java.util.stream.Stream;
import org.apache.commons.lang3.tuple.Pair;
import org.openide.nodes.Node;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.datamodel.AnalysisResultItem;
import org.sleuthkit.autopsy.datamodel.TskContentItem;
import org.sleuthkit.datamodel.AnalysisResult;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.Score;
import org.sleuthkit.datamodel.TskCoreException;
/**
*
* Creates a representation of a list of analysis results gathered from a node.
*/
public class AnalysisResultsViewModel {
@ -72,7 +71,7 @@ public class AnalysisResultsViewModel {
* @return The attributes to display.
*/
List<Pair<String, String>> getAttributesToDisplay() {
return attributesToDisplay;
return Collections.unmodifiableList(attributesToDisplay);
}
/**
@ -118,7 +117,7 @@ public class AnalysisResultsViewModel {
* @return The analysis results to be displayed.
*/
List<ResultDisplayAttributes> getAnalysisResults() {
return analysisResults;
return Collections.unmodifiableList(analysisResults);
}
/**
@ -238,58 +237,44 @@ public class AnalysisResultsViewModel {
return new NodeResults(Collections.emptyList(), Optional.empty(), Optional.empty(), Optional.empty());
}
Optional<Score> aggregateScore = Optional.empty();
Optional<Content> nodeContent = Optional.empty();
// maps id of analysis result to analysis result to prevent duplication
Map<Long, AnalysisResult> allAnalysisResults = new HashMap<>();
Optional<AnalysisResult> selectedResult = Optional.empty();
// Find first content that is not an artifact within node
for (Content content : node.getLookup().lookupAll(Content.class)) {
if (content == null || content instanceof BlackboardArtifact) {
continue;
}
try {
nodeContent = Optional.of(content);
// get the aggregate score of that content
aggregateScore = Optional.ofNullable(content.getAggregateScore());
// and add all analysis results to mapping
content.getAllAnalysisResults().stream()
.forEach((ar) -> allAnalysisResults.put(ar.getArtifactID(), ar));
break;
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Unable to get analysis results for content with obj id " + content.getId(), ex);
Content analyzedContent = null;
AnalysisResult selectedAnalysisResult = null;
Score aggregateScore = null;
List<AnalysisResult> analysisResults = Collections.emptyList();
long selectedObjectId = 0;
try {
AnalysisResultItem analysisResultItem = node.getLookup().lookup(AnalysisResultItem.class);
if (Objects.nonNull(analysisResultItem)) {
/*
* The content represented by the Node is an analysis result.
* Set this analysis result as the analysis result to be
* selected in the content viewer and get the analyzed content
* as the source of the analysis results to display.
*/
selectedAnalysisResult = analysisResultItem.getAnalysisResult();
selectedObjectId = selectedAnalysisResult.getId();
analyzedContent = selectedAnalysisResult.getParent();
} else {
/*
* The content represented by the Node is something other than
* an analysis result. Use it as the source of the analysis
* results to display.
*/
TskContentItem contentItem = node.getLookup().lookup(TskContentItem.class);
analyzedContent = contentItem.getTskContent();
selectedObjectId = analyzedContent.getId();
}
aggregateScore = analyzedContent.getAggregateScore();
analysisResults = analyzedContent.getAllAnalysisResults();
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Error getting analysis result data for selected Content (object ID=%d)", selectedObjectId), ex);
}
// Find any analysis results in the node
Collection<? extends AnalysisResult> analysisResults = node.getLookup().lookupAll(AnalysisResult.class);
if (analysisResults.size() > 0) {
// get any items with a score
List<AnalysisResult> filteredResults = analysisResults.stream()
.collect(Collectors.toList());
// add them to the map to display
filteredResults.forEach((ar) -> allAnalysisResults.put(ar.getArtifactID(), ar));
// the selected result will be the highest scored analysis result in the node.
selectedResult = filteredResults.stream()
.max((a, b) -> a.getScore().compareTo(b.getScore()));
// if no aggregate score determined at this point, use the selected result score.
if (!aggregateScore.isPresent()) {
aggregateScore = selectedResult.flatMap(selectedRes -> Optional.ofNullable(selectedRes.getScore()));
}
}
// get view model representation
List<ResultDisplayAttributes> displayAttributes = getOrderedDisplayAttributes(allAnalysisResults.values());
return new NodeResults(displayAttributes, selectedResult, aggregateScore, nodeContent);
/*
* Use the data collected above to construct the view model.
*/
List<ResultDisplayAttributes> displayAttributes = getOrderedDisplayAttributes(analysisResults);
return new NodeResults(displayAttributes, Optional.ofNullable(selectedAnalysisResult), Optional.ofNullable(aggregateScore), Optional.ofNullable(analyzedContent));
}
}

View File

@ -55,6 +55,7 @@ import org.sleuthkit.autopsy.machinesettings.UserMachinePreferencesException;
import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
import org.sleuthkit.autopsy.coreutils.Version;
import org.sleuthkit.autopsy.guiutils.JFileChooserFactory;
import org.sleuthkit.autopsy.machinesettings.UserMachinePreferences.TempDirChoice;
import org.sleuthkit.autopsy.report.ReportBranding;
@ -82,8 +83,8 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
private static final long serialVersionUID = 1L;
private static final String DEFAULT_HEAP_DUMP_FILE_FIELD = "";
private final JFileChooser logoFileChooser;
private final JFileChooser tempDirChooser;
private JFileChooser logoFileChooser;
private JFileChooser tempDirChooser;
private static final String ETC_FOLDER_NAME = "etc";
private static final String CONFIG_FILE_EXTENSION = ".conf";
private static final long ONE_BILLION = 1000000000L; //used to roughly convert system memory from bytes to gigabytes
@ -94,27 +95,17 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
private String initialMemValue = Long.toString(Runtime.getRuntime().maxMemory() / ONE_BILLION);
private final ReportBranding reportBranding;
private final JFileChooser heapFileChooser;
private JFileChooser heapFileChooser;
private final JFileChooserFactory logoChooserHelper = new JFileChooserFactory();
private final JFileChooserFactory heapChooserHelper = new JFileChooserFactory();
private final JFileChooserFactory tempChooserHelper = new JFileChooserFactory();
/**
* Instantiate the Autopsy options panel.
*/
AutopsyOptionsPanel() {
initComponents();
logoFileChooser = new JFileChooser();
logoFileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
logoFileChooser.setMultiSelectionEnabled(false);
logoFileChooser.setAcceptAllFileFilterUsed(false);
logoFileChooser.setFileFilter(new GeneralFilter(GeneralFilter.GRAPHIC_IMAGE_EXTS, GeneralFilter.GRAPHIC_IMG_DECR));
tempDirChooser = new JFileChooser();
tempDirChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
tempDirChooser.setMultiSelectionEnabled(false);
heapFileChooser = new JFileChooser();
heapFileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
heapFileChooser.setMultiSelectionEnabled(false);
if (!isJVMHeapSettingsCapable()) {
//32 bit JVM has a max heap size of 1.4 gb to 4 gb depending on OS
//So disabling the setting of heap size when the JVM is not 64 bit
@ -1242,6 +1233,11 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
"# {0} - path",
"AutopsyOptionsPanel_tempDirectoryBrowseButtonActionPerformed_onInvalidPath_description=Unable to create temporary directory within specified path: {0}",})
private void tempDirectoryBrowseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_tempDirectoryBrowseButtonActionPerformed
if(tempDirChooser == null) {
tempDirChooser = tempChooserHelper.getChooser();
tempDirChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
tempDirChooser.setMultiSelectionEnabled(false);
}
int returnState = tempDirChooser.showOpenDialog(this);
if (returnState == JFileChooser.APPROVE_OPTION) {
String specifiedPath = tempDirChooser.getSelectedFile().getPath();
@ -1318,6 +1314,13 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
}//GEN-LAST:event_defaultLogoRBActionPerformed
private void browseLogosButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browseLogosButtonActionPerformed
if(logoFileChooser == null) {
logoFileChooser = logoChooserHelper.getChooser();
logoFileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
logoFileChooser.setMultiSelectionEnabled(false);
logoFileChooser.setAcceptAllFileFilterUsed(false);
logoFileChooser.setFileFilter(new GeneralFilter(GeneralFilter.GRAPHIC_IMAGE_EXTS, GeneralFilter.GRAPHIC_IMG_DECR));
}
String oldLogoPath = agencyLogoPathField.getText();
int returnState = logoFileChooser.showOpenDialog(this);
if (returnState == JFileChooser.APPROVE_OPTION) {
@ -1360,6 +1363,11 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
"AutopsyOptionsPanel_heapDumpBrowseButtonActionPerformed_fileAlreadyExistsMessage=File already exists. Please select a new location."
})
private void heapDumpBrowseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_heapDumpBrowseButtonActionPerformed
if(heapFileChooser == null) {
heapFileChooser = heapChooserHelper.getChooser();
heapFileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
heapFileChooser.setMultiSelectionEnabled(false);
}
String oldHeapPath = heapDumpFileField.getText();
if (!StringUtils.isBlank(oldHeapPath)) {
heapFileChooser.setCurrentDirectory(new File(oldHeapPath));

View File

@ -102,7 +102,7 @@ public abstract class AbstractContentNode<T extends Content> extends ContentNode
* @param content Underlying Content instances
*/
AbstractContentNode(T content) {
this(content, Lookups.singleton(content));
this(content, Lookups.fixed(content, new TskContentItem(content)));
}
/**

View File

@ -0,0 +1,51 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2021-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.datamodel;
import com.google.common.annotations.Beta;
import org.sleuthkit.datamodel.AnalysisResult;
/**
* An Autopsy Data Model item with an underlying analysis result Sleuth Kit Data
* Model object.
*/
public class AnalysisResultItem extends TskContentItem {
/**
* Constructs an Autopsy Data Model item with an underlying AnalysisResult
* Sleuth Kit Data Model object.
*
* @param analysisResult The analysis result.
*/
@Beta
AnalysisResultItem(AnalysisResult analysisResult) {
super(analysisResult);
}
/**
* Gets the underlying analysis result.
*
* @return The analysis result.
*/
@Beta
public AnalysisResult getAnalysisResult() {
return (AnalysisResult) (getTskContent());
}
}

View File

@ -84,8 +84,8 @@ import org.sleuthkit.datamodel.BlackboardArtifact.Category;
import org.sleuthkit.datamodel.Score;
/**
* A BlackboardArtifactNode is an AbstractNode implementation that can be used
* to represent an artifact of any type.
* An AbstractNode implementation that can be used to represent an data artifact
* or analysis result of any type.
*/
public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifact> {
@ -219,11 +219,14 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
private final PropertyChangeListener weakListener = WeakListeners.propertyChange(listener, null);
/**
* Constructs a BlackboardArtifactNode, an AbstractNode implementation that
* can be used to represent an artifact of any type.
* Constructs an AbstractNode implementation that can be used to represent a
* data artifact or analysis result of any type. The Lookup of the Node will
* contain the data artifact or analysis result and its parent content as
* its source content.
*
* @param artifact The artifact to represent.
* @param iconPath The path to the icon for the artifact type.
* @param artifact The data artifact or analysis result.
* @param iconPath The path to the icon for the data artifact or analysis
* result type.
*/
public BlackboardArtifactNode(BlackboardArtifact artifact, String iconPath) {
super(artifact, createLookup(artifact, false));
@ -256,26 +259,28 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
}
/**
* Constructs a BlackboardArtifactNode, an AbstractNode implementation that
* can be used to represent an artifact of any type.
* Constructs an AbstractNode implementation that can be used to represent a
* data artifact or analysis result of any type. The Lookup of the Node will
* contain the data artifact or analysis result and its source content,
* either the parent content or the associated file.
*
* @param artifact The artifact to represent.
* @param lookupIsAssociatedFile True if the Content lookup should be made
* for the associated file instead of the
* parent file.
* @param artifact The data artifact or analysis result.
* @param useAssociatedFileInLookup True if the source content in the Lookup
* should be the associated file instead of
* the parent content.
*/
@Beta
public BlackboardArtifactNode(BlackboardArtifact artifact, boolean lookupIsAssociatedFile) {
super(artifact, createLookup(artifact, lookupIsAssociatedFile));
public BlackboardArtifactNode(BlackboardArtifact artifact, boolean useAssociatedFileInLookup) {
super(artifact, createLookup(artifact, useAssociatedFileInLookup));
this.artifact = artifact;
this.artifactType = getType(artifact);
try {
//The lookup for a file may or may not exist so we define the srcContent as the parent.
srcContent = artifact.getParent();
} catch (TskCoreException ex) {
logger.log(Level.WARNING, MessageFormat.format("Error getting the parent of the artifact for (artifact objID={0})", artifact.getId()), ex);
}
if (srcContent != null) {
try {
/*
@ -291,6 +296,7 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
} else {
throw new IllegalArgumentException(MessageFormat.format("Artifact missing source content (artifact objID={0})", artifact));
}
setName(Long.toString(artifact.getArtifactID()));
String displayName = srcContent.getName();
setDisplayName(displayName);
@ -301,18 +307,22 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
}
/**
* Constructs a BlackboardArtifactNode, an AbstractNode implementation that
* can be used to represent an artifact of any type.
* Constructs an AbstractNode implementation that can be used to represent a
* data artifact or analysis result of any type. The Lookup of the Node will
* contain the data artifact or analysis result and its parent content as
* its source content.
*
* @param artifact The artifact to represent.
* @param artifact The data artifact or analysis result.
*/
public BlackboardArtifactNode(BlackboardArtifact artifact) {
this(artifact, IconsUtil.getIconFilePath(artifact.getArtifactTypeID()));
}
/**
* Returns the artifact type of the artifact.
*
* @param artifact The artifact.
*
* @return The artifact type or null if no type could be retrieved.
*/
private static BlackboardArtifact.Type getType(BlackboardArtifact artifact) {
@ -328,54 +338,52 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
* Creates a Lookup object for this node and populates it with both the
* artifact this node represents and its source content.
*
* @param artifact The artifact this node represents.
* @param artifact The artifact this node represents.
* @param useAssociatedFile True if the source content in the Lookup should
* be the associated file instead of the parent
* content.
*
* @return The Lookup.
*/
private static Lookup createLookup(BlackboardArtifact artifact) {
final long objectID = artifact.getObjectID();
private static Lookup createLookup(BlackboardArtifact artifact, boolean useAssociatedFile) {
/*
* Get the source content.
*/
Content content = null;
try {
Content content = contentCache.get(objectID, () -> artifact.getSleuthkitCase().getContentById(objectID));
if (content == null) {
return Lookups.fixed(artifact);
if (useAssociatedFile) {
content = getPathIdFile(artifact);
} else {
return Lookups.fixed(artifact, content);
long srcObjectID = artifact.getObjectID();
content = contentCache.get(srcObjectID, () -> artifact.getSleuthkitCase().getContentById(srcObjectID));
}
} catch (ExecutionException ex) {
logger.log(Level.SEVERE, MessageFormat.format("Error getting source content (artifact objID={0}", artifact.getId()), ex); //NON-NLS
return Lookups.fixed(artifact);
logger.log(Level.SEVERE, MessageFormat.format("Error getting source/associated content (artifact object ID={0})", artifact.getId()), ex); //NON-NLS
}
}
/**
* Creates a Lookup object for this node and populates it with both the
* artifact this node represents and its source content.
*
* @param artifact The artifact this node represents.
* @param lookupIsAssociatedFile True if the Content lookup should be made
* for the associated file instead of the
* parent file.
*
* @return The Lookup.
*/
private static Lookup createLookup(BlackboardArtifact artifact, boolean lookupIsAssociatedFile) {
Content content = null;
if (lookupIsAssociatedFile) {
try {
content = getPathIdFile(artifact);
} catch (ExecutionException ex) {
logger.log(Level.SEVERE, MessageFormat.format("Error getting source content (artifact objID={0}", artifact.getId()), ex); //NON-NLS
content = null;
}
if (content == null) {
return Lookups.fixed(artifact);
} else {
return Lookups.fixed(artifact, content);
}
/*
* Make an Autopsy Data Model wrapper for the artifact.
*
* NOTE: The creation of an Autopsy Data Model independent of the
* NetBeans nodes is a work in progress. At the time this comment is
* being written, this object is only used by the analysis content
* viewer.
*/
TskContentItem artifactItem;
if (artifact instanceof AnalysisResult) {
artifactItem = new AnalysisResultItem((AnalysisResult) artifact);
} else {
return createLookup(artifact);
artifactItem = new TskContentItem(artifact);
}
/*
* Create the Lookup.
*/
if (content == null) {
return Lookups.fixed(artifact, artifactItem);
} else {
return Lookups.fixed(artifact, artifactItem, content);
}
}
/**
@ -470,10 +478,10 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
* action to view it in the timeline.
*/
try {
if (ViewArtifactInTimelineAction.hasSupportedTimeStamp(artifact) &&
// don't show ViewArtifactInTimelineAction for AnalysisResults.
if (ViewArtifactInTimelineAction.hasSupportedTimeStamp(artifact)
&& // don't show ViewArtifactInTimelineAction for AnalysisResults.
(!(this.artifact instanceof AnalysisResult))) {
actionsList.add(new ViewArtifactInTimelineAction(artifact));
}
} catch (TskCoreException ex) {

View File

@ -99,6 +99,7 @@ public abstract class DisplayableItemNode extends AbstractNode {
* operation on this artifact type and return some object as the result of
* the operation.
*
* @param <T> The return type.
* @param visitor The visitor, where the type parameter of the visitor is
* the type of the object that will be returned as the result
* of the visit operation.

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2012-2019 Basis Technology Corp.
* Copyright 2012-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -110,7 +110,7 @@ public class ImageNode extends AbstractContentNode<Image> {
actionsList.add(a);
}
actionsList.addAll(ExplorerNodeActionVisitor.getActions(content));
actionsList.add(new FileSearchAction(Bundle.ImageNode_getActions_openFileSearchByAttr_text()));
actionsList.add(new FileSearchAction(Bundle.ImageNode_getActions_openFileSearchByAttr_text(), content.getId()));
actionsList.add(new ViewSummaryInformationAction(content.getId()));
actionsList.add(new RunIngestModulesAction(Collections.<Content>singletonList(content)));
actionsList.add(new NewWindowViewAction(NbBundle.getMessage(this.getClass(), "ImageNode.getActions.viewInNewWin.text"), this));

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2017-2019 Basis Technology Corp.
* Copyright 2017-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -65,7 +65,7 @@ public abstract class SpecialDirectoryNode extends AbstractAbstractFileNode<Spec
actions.add(ExtractAction.getInstance());
actions.add(ExportCSVAction.getInstance());
actions.add(null); // creates a menu separator
actions.add(new FileSearchAction(Bundle.ImageNode_getActions_openFileSearchByAttr_text()));
actions.add(new FileSearchAction(Bundle.ImageNode_getActions_openFileSearchByAttr_text(), content.getId()));
if (content.isDataSource()) {
actions.add(new ViewSummaryInformationAction(content.getId()));
actions.add(new RunIngestModulesAction(Collections.<Content>singletonList(content)));

View File

@ -0,0 +1,56 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2021-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.datamodel;
import com.google.common.annotations.Beta;
import org.sleuthkit.datamodel.Content;
/**
* An Autopsy Data Model item with an underlying Sleuth Kit Data Model object
* that implements the Sleuth Kit Data Model's Content interface.
*/
@Beta
public class TskContentItem {
private final Content tskContent;
/**
* Constructs an Autopsy Data Model item with an underlying Sleuth Kit Data
* Model object that implements the Sleuth Kit Data Model's Content
* interface.
*
* @param content The underlying Sleuth Kit Data Model object.
*
*/
@Beta
TskContentItem(Content sleuthKitContent) {
this.tskContent = sleuthKitContent;
}
/**
* Gets the underlying Sleuth Kit Data Model object.
*
* @return The Sleuth Kit Data Model object.
*/
@Beta
public Content getTskContent() {
return tskContent;
}
}

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2018 Basis Technology Corp.
* Copyright 2011-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -32,6 +32,7 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
import org.sleuthkit.autopsy.coreutils.PathValidator;
import org.sleuthkit.autopsy.coreutils.TimeZoneUtils;
import org.sleuthkit.autopsy.guiutils.JFileChooserFactory;
/**
* Allows examiner to supply a raw data source.
@ -41,7 +42,8 @@ final class RawDSInputPanel extends JPanel implements DocumentListener {
private static final long TWO_GB = 2000000000L;
private static final long serialVersionUID = 1L; //default
private final String PROP_LASTINPUT_PATH = "LBL_LastInputFile_PATH";
private final JFileChooser fc = new JFileChooser();
private JFileChooser fc;
private JFileChooserFactory chooserHelper = new JFileChooserFactory();
// Externally supplied name is used to store settings
private final String contextName;
/**
@ -51,11 +53,6 @@ final class RawDSInputPanel extends JPanel implements DocumentListener {
initComponents();
errorLabel.setVisible(false);
fc.setDragEnabled(false);
fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
fc.setMultiSelectionEnabled(false);
this.contextName = context;
}
@ -200,18 +197,25 @@ final class RawDSInputPanel extends JPanel implements DocumentListener {
}// </editor-fold>//GEN-END:initComponents
@SuppressWarnings("deprecation")
private void browseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browseButtonActionPerformed
String oldText = pathTextField.getText();
// set the current directory of the FileChooser if the ImagePath Field is valid
File currentDir = new File(oldText);
if (currentDir.exists()) {
fc.setCurrentDirectory(currentDir);
}
if (fc == null) {
fc = chooserHelper.getChooser();
fc.setDragEnabled(false);
fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
fc.setMultiSelectionEnabled(false);
}
int retval = fc.showOpenDialog(this);
if (retval == JFileChooser.APPROVE_OPTION) {
String path = fc.getSelectedFile().getPath();
pathTextField.setText(path);
}
String oldText = pathTextField.getText();
// set the current directory of the FileChooser if the ImagePath Field is valid
File currentDir = new File(oldText);
if (currentDir.exists()) {
fc.setCurrentDirectory(currentDir);
}
int retval = fc.showOpenDialog(this);
if (retval == JFileChooser.APPROVE_OPTION) {
String path = fc.getSelectedFile().getPath();
pathTextField.setText(path);
}
}//GEN-LAST:event_browseButtonActionPerformed
private void j2GBBreakupRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_j2GBBreakupRadioButtonActionPerformed

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 Basis Technology Corp.
* Copyright 2019-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -30,6 +30,7 @@ import javax.swing.JPanel;
import org.apache.commons.lang3.StringUtils;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
import org.sleuthkit.autopsy.guiutils.JFileChooserFactory;
/**
* Allows an examiner to configure the XRY Data source processor.
@ -49,6 +50,8 @@ final class XRYDataSourceProcessorConfigPanel extends JPanel {
//panel will indicate when it is ready for an update.
private final PropertyChangeSupport pcs;
private final JFileChooserFactory chooserHelper = new JFileChooserFactory();
/**
* Creates new form XRYDataSourceConfigPanel.
* Prevent direct instantiation.
@ -191,7 +194,7 @@ final class XRYDataSourceProcessorConfigPanel extends JPanel {
* report folder.
*/
private void fileBrowserButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_fileBrowserButtonActionPerformed
JFileChooser fileChooser = new JFileChooser();
JFileChooser fileChooser = chooserHelper.getChooser();
fileChooser.setMultiSelectionEnabled(false);
fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
Optional<Path> lastUsedPath = getLastUsedPath();

View File

@ -18,9 +18,7 @@
*/
package org.sleuthkit.autopsy.datasourcesummary.datamodel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultArtifactUpdateGovernor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@ -31,30 +29,23 @@ import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Providing data for the data source analysis tab.
* Helper class for getting hash set hits, keyword hits, and interesting item
* hits within a datasource.
*/
public class AnalysisSummary implements DefaultArtifactUpdateGovernor {
public class AnalysisSummary {
private static final BlackboardAttribute.Type TYPE_SET_NAME = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_SET_NAME);
private static final Set<String> EXCLUDED_KEYWORD_SEARCH_ITEMS = new HashSet<>();
private static final Set<Integer> ARTIFACT_UPDATE_TYPE_IDS = new HashSet<>(Arrays.asList(
ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID(),
ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID(),
ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID(),
ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()
));
private final SleuthkitCaseProvider provider;
/**
@ -73,11 +64,6 @@ public class AnalysisSummary implements DefaultArtifactUpdateGovernor {
this.provider = provider;
}
@Override
public Set<Integer> getArtifactTypeIdsForRefresh() {
return ARTIFACT_UPDATE_TYPE_IDS;
}
/**
* Gets counts for hashset hits.
*

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -64,6 +64,7 @@ class ClosestCityMapper {
* Retrieves singleton instance of this class.
*
* @return The singleton instance of this class.
*
* @throws IOException
*/
static ClosestCityMapper getInstance() throws IOException {
@ -95,8 +96,9 @@ class ClosestCityMapper {
* Main Constructor loading from an input stream.
*
* @param citiesInputStream The input stream for the csv text file
* containing the cities.
* @param logger The logger to be used with this.
* containing the cities.
* @param logger The logger to be used with this.
*
* @throws IOException
*/
private ClosestCityMapper(InputStream citiesInputStream, java.util.logging.Logger logger) throws IOException {
@ -109,6 +111,7 @@ class ClosestCityMapper {
* city can be determined.
*
* @param point The point to locate.
*
* @return The closest city or null if no close city can be found.
*/
CityRecord findClosest(CityRecord point) {
@ -120,6 +123,7 @@ class ClosestCityMapper {
* returned.
*
* @param s The string to parse.
*
* @return The double value or null if value cannot be parsed.
*/
private Double tryParse(String s) {
@ -138,8 +142,9 @@ class ClosestCityMapper {
* Parses a country name and transforms values like "last, first" to "first
* last" (i.e. "Korea, South" becomes "South Korea").
*
* @param orig The original string value.
* @param orig The original string value.
* @param lineNum The line number that this country was found.
*
* @return The country name.
*/
private String parseCountryName(String orig, int lineNum) {
@ -159,9 +164,10 @@ class ClosestCityMapper {
/**
* Parses a row from the csv creating a city record.
*
* @param csvRow The row of data where each item in the list is each column
* in the row.
* @param csvRow The row of data where each item in the list is each column
* in the row.
* @param lineNum The line number for this csv row.
*
* @return The parsed CityRecord or null if none can be determined.
*/
private CityRecord getCsvCityRecord(List<String> csvRow, int lineNum) {
@ -199,8 +205,9 @@ class ClosestCityMapper {
/**
* Parses a row of the csv into individual column values.
*
* @param line The line to parse.
* @param line The line to parse.
* @param lineNum The line number in the csv where this line is.
*
* @return The list of column values.
*/
private List<String> parseCsvLine(String line, int lineNum) {
@ -222,10 +229,12 @@ class ClosestCityMapper {
* Parses all lines in the csv file input stream into a list of city
* records.
*
* @param csvInputStream The csv file input stream.
* @param csvInputStream The csv file input stream.
* @param ignoreHeaderRow Whether or not there is a header row in the csv
* file.
* file.
*
* @return The list of city records.
*
* @throws IOException
*/
private List<CityRecord> parseCsvLines(InputStream csvInputStream, boolean ignoreHeaderRow) throws IOException {

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -18,29 +18,25 @@
*/
package org.sleuthkit.autopsy.datasourcesummary.datamodel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultArtifactUpdateGovernor;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
import org.sleuthkit.autopsy.ingest.ModuleContentEvent;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.Image;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
/**
* Provides methods to query for data source overview details.
*/
public class ContainerSummary implements DefaultArtifactUpdateGovernor {
private static final Set<Integer> ARTIFACT_UPDATE_TYPE_IDS = new HashSet<>(Arrays.asList(
BlackboardArtifact.ARTIFACT_TYPE.TSK_OS_INFO.getTypeID(),
BlackboardArtifact.ARTIFACT_TYPE.TSK_DATA_SOURCE_USAGE.getTypeID()
));
public class ContainerSummary {
private final SleuthkitCaseProvider provider;
@ -59,22 +55,7 @@ public class ContainerSummary implements DefaultArtifactUpdateGovernor {
public ContainerSummary(SleuthkitCaseProvider provider) {
this.provider = provider;
}
@Override
public boolean isRefreshRequired(ModuleContentEvent evt) {
return true;
}
@Override
public boolean isRefreshRequired(AbstractFile file) {
return true;
}
@Override
public Set<Integer> getArtifactTypeIdsForRefresh() {
return ARTIFACT_UPDATE_TYPE_IDS;
}
/**
* Gets the size of unallocated files in a particular datasource.
*
@ -231,4 +212,228 @@ public class ContainerSummary implements DefaultArtifactUpdateGovernor {
String separator = ", ";
return getConcattedStringsResult(query, valueParam, separator);
}
/**
* Data model data for data source images.
*/
public static class ImageDetails {
private final long unallocatedSize;
private final long size;
private final long sectorSize;
private final String timeZone;
private final String imageType;
private final List<String> paths;
private final String md5Hash;
private final String sha1Hash;
private final String sha256Hash;
/**
* Main constructor.
*
* @param unallocatedSize Size in bytes of unallocated space.
* @param size Total size in bytes.
* @param sectorSize Sector size in bytes.
* @param timeZone The time zone.
* @param imageType The type of image.
* @param paths The source paths for the image.
* @param md5Hash The md5 hash or null.
* @param sha1Hash The sha1 hash or null.
* @param sha256Hash The sha256 hash or null.
*/
ImageDetails(long unallocatedSize, long size, long sectorSize,
String timeZone, String imageType, List<String> paths, String md5Hash,
String sha1Hash, String sha256Hash) {
this.unallocatedSize = unallocatedSize;
this.size = size;
this.sectorSize = sectorSize;
this.timeZone = timeZone;
this.imageType = imageType;
this.paths = paths == null ? Collections.emptyList() : new ArrayList<>(paths);
this.md5Hash = md5Hash;
this.sha1Hash = sha1Hash;
this.sha256Hash = sha256Hash;
}
/**
* @return Size in bytes of unallocated space.
*/
public long getUnallocatedSize() {
return unallocatedSize;
}
/**
* @return Total size in bytes.
*/
public long getSize() {
return size;
}
/**
* @return Sector size in bytes.
*/
public long getSectorSize() {
return sectorSize;
}
/**
* @return The time zone.
*/
public String getTimeZone() {
return timeZone;
}
/**
* @return The type of image.
*/
public String getImageType() {
return imageType;
}
/**
* @return The source paths for the image.
*/
public List<String> getPaths() {
return Collections.unmodifiableList(paths);
}
/**
* @return The md5 hash or null.
*/
public String getMd5Hash() {
return md5Hash;
}
/**
* @return The sha1 hash or null.
*/
public String getSha1Hash() {
return sha1Hash;
}
/**
* @return The sha256 hash or null.
*/
public String getSha256Hash() {
return sha256Hash;
}
}
/**
* Data model for container data.
*/
public static class ContainerDetails {
private final String displayName;
private final String originalName;
private final String deviceIdValue;
private final String acquisitionDetails;
private final ImageDetails imageDetails;
/**
* Main constructor.
*
* @param displayName The display name for this data source.
* @param originalName The original name for this data source.
* @param deviceIdValue The device id value for this data source.
* @param acquisitionDetails The acquisition details for this data
* source or null.
* @param imageDetails If the data source is an image, the image
* data model for this data source or null if
* non-image.
*/
ContainerDetails(String displayName, String originalName, String deviceIdValue,
String acquisitionDetails, ImageDetails imageDetails) {
this.displayName = displayName;
this.originalName = originalName;
this.deviceIdValue = deviceIdValue;
this.acquisitionDetails = acquisitionDetails;
this.imageDetails = imageDetails;
}
/**
* @return The display name for this data source.
*/
public String getDisplayName() {
return displayName;
}
/**
* @return The original name for this data source.
*/
public String getOriginalName() {
return originalName;
}
/**
* @return The device id value for this data source.
*/
public String getDeviceId() {
return deviceIdValue;
}
/**
* @return The acquisition details for this data source or null.
*/
public String getAcquisitionDetails() {
return acquisitionDetails;
}
/**
* @return If the data source is an image, the image details for this
* data source or null if non-image.
*/
public ImageDetails getImageDetails() {
return imageDetails;
}
}
/**
* Generates a container data model object containing data about the data
* source.
*
* @param ds The data source.
*
* @return The generated view model.
*/
public ContainerDetails getContainerDetails(DataSource ds) throws TskCoreException, SQLException, SleuthkitCaseProvider.SleuthkitCaseProviderException {
if (ds == null) {
return null;
}
return new ContainerDetails(
ds.getName(),
ds.getName(),
ds.getDeviceId(),
ds.getAcquisitionDetails(),
ds instanceof Image ? getImageDetails((Image) ds) : null
);
}
/**
* Generates an image data model object containing data about the image.
*
* @param image The image.
*
* @return The generated view model.
*/
public ImageDetails getImageDetails(Image image) throws TskCoreException, SQLException, SleuthkitCaseProvider.SleuthkitCaseProviderException {
if (image == null) {
return null;
}
Long unallocSize = getSizeOfUnallocatedFiles(image);
String imageType = image.getType().getName();
Long size = image.getSize();
Long sectorSize = image.getSsize();
String timeZone = image.getTimeZone();
List<String> paths = image.getPaths() == null ? Collections.emptyList() : Arrays.asList(image.getPaths());
String md5 = image.getMd5();
String sha1 = image.getSha1();
String sha256 = image.getSha256();
return new ImageDetails(unallocSize, size, sectorSize, timeZone, imageType, paths, md5, sha1, sha256);
}
}

View File

@ -16,7 +16,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.datasourcesummary.uiutils;
package org.sleuthkit.autopsy.datasourcesummary.datamodel;
/**
* A function that accepts input of type I and outputs type O. This function is

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 - 2020 Basis Technology Corp.
* Copyright 2019 - 2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.datasourcesummary.datamodel;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
@ -41,7 +42,10 @@ import org.sleuthkit.datamodel.TskData.TSK_FS_META_TYPE_ENUM;
* Utilities for getting information about a data source or all data sources
* from the case database.
*/
final class DataSourceInfoUtilities {
public final class DataSourceInfoUtilities {
public static final String COMMA_FORMAT_STR = "#,###";
public static final DecimalFormat COMMA_FORMATTER = new DecimalFormat(COMMA_FORMAT_STR);
/**
* Gets a count of tsk_files for a particular datasource.
@ -100,7 +104,7 @@ final class DataSourceInfoUtilities {
* @throws TskCoreException
* @throws SQLException
*/
static Long getCountOfRegNonSlackFiles(SleuthkitCase skCase, DataSource currentDataSource, String additionalWhere)
public static Long getCountOfRegNonSlackFiles(SleuthkitCase skCase, DataSource currentDataSource, String additionalWhere)
throws TskCoreException, SQLException {
String whereClause = "meta_type=" + TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG.getValue()
+ " AND type<>" + TSK_DB_FILES_TYPE_ENUM.SLACK.getFileType();
@ -115,7 +119,7 @@ final class DataSourceInfoUtilities {
/**
* An interface for handling a result set and returning a value.
*/
interface ResultSetHandler<T> {
public interface ResultSetHandler<T> {
T process(ResultSet resultset) throws SQLException;
}
@ -149,14 +153,14 @@ final class DataSourceInfoUtilities {
*
* @return The clause.
*/
static String getMetaFlagsContainsStatement(TSK_FS_META_FLAG_ENUM flag) {
public static String getMetaFlagsContainsStatement(TSK_FS_META_FLAG_ENUM flag) {
return "meta_flags & " + flag.getValue() + " > 0";
}
/**
* Enum for specifying the sort order for getAttributes.
*/
enum SortOrder {
public enum SortOrder {
DESCENDING,
ASCENDING
}
@ -181,7 +185,7 @@ final class DataSourceInfoUtilities {
*
* @throws TskCoreException
*/
static List<BlackboardArtifact> getArtifacts(SleuthkitCase skCase, BlackboardArtifact.Type artifactType, DataSource dataSource, BlackboardAttribute.Type attributeType, SortOrder sortOrder) throws TskCoreException {
public static List<BlackboardArtifact> getArtifacts(SleuthkitCase skCase, BlackboardArtifact.Type artifactType, DataSource dataSource, BlackboardAttribute.Type attributeType, SortOrder sortOrder) throws TskCoreException {
return getArtifacts(skCase, artifactType, dataSource, attributeType, sortOrder, 0);
}
@ -207,7 +211,7 @@ final class DataSourceInfoUtilities {
*
* @throws TskCoreException
*/
static List<BlackboardArtifact> getArtifacts(SleuthkitCase skCase, BlackboardArtifact.Type artifactType, DataSource dataSource, BlackboardAttribute.Type attributeType, SortOrder sortOrder, int maxCount) throws TskCoreException {
public static List<BlackboardArtifact> getArtifacts(SleuthkitCase skCase, BlackboardArtifact.Type artifactType, DataSource dataSource, BlackboardAttribute.Type attributeType, SortOrder sortOrder, int maxCount) throws TskCoreException {
if (maxCount < 0) {
throw new IllegalArgumentException("Invalid maxCount passed to getArtifacts, value must be equal to or greater than 0");
}
@ -380,7 +384,7 @@ final class DataSourceInfoUtilities {
* @return The 'getValueString()' value or null if the attribute or String
* could not be retrieved.
*/
static String getStringOrNull(BlackboardArtifact artifact, Type attributeType) {
public static String getStringOrNull(BlackboardArtifact artifact, Type attributeType) {
BlackboardAttribute attr = getAttributeOrNull(artifact, attributeType);
return (attr == null) ? null : attr.getValueString();
}
@ -394,11 +398,11 @@ final class DataSourceInfoUtilities {
* @return The 'getValueLong()' value or null if the attribute could not be
* retrieved.
*/
static Long getLongOrNull(BlackboardArtifact artifact, Type attributeType) {
public static Long getLongOrNull(BlackboardArtifact artifact, Type attributeType) {
BlackboardAttribute attr = getAttributeOrNull(artifact, attributeType);
return (attr == null) ? null : attr.getValueLong();
}
/**
* Retrieves the int value of a certain attribute type from an artifact.
*
@ -408,7 +412,7 @@ final class DataSourceInfoUtilities {
* @return The 'getValueInt()' value or null if the attribute could not be
* retrieved.
*/
static Integer getIntOrNull(BlackboardArtifact artifact, Type attributeType) {
public static Integer getIntOrNull(BlackboardArtifact artifact, Type attributeType) {
BlackboardAttribute attr = getAttributeOrNull(artifact, attributeType);
return (attr == null) ? null : attr.getValueInt();
}
@ -423,8 +427,31 @@ final class DataSourceInfoUtilities {
* @return The date determined from the 'getValueLong()' as seconds from
* epoch or null if the attribute could not be retrieved or is 0.
*/
static Date getDateOrNull(BlackboardArtifact artifact, Type attributeType) {
public static Date getDateOrNull(BlackboardArtifact artifact, Type attributeType) {
Long longVal = getLongOrNull(artifact, attributeType);
return (longVal == null || longVal == 0) ? null : new Date(longVal * 1000);
}
/**
* Returns the long value or zero if longVal is null.
*
* @param longVal The long value.
*
* @return The long value or 0 if provided value is null.
*/
public static long getLongOrZero(Long longVal) {
return longVal == null ? 0 : longVal;
}
/**
* Returns string value of long with comma separators. If null returns a
* string of '0'.
*
* @param longVal The long value.
*
* @return The string value of the long.
*/
public static String getStringOrZero(Long longVal) {
return longVal == null ? "0" : COMMA_FORMATTER.format(longVal);
}
}

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -32,7 +32,6 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.lang3.tuple.Pair;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultArtifactUpdateGovernor;
import org.sleuthkit.autopsy.geolocation.AbstractWaypointFetcher;
import org.sleuthkit.autopsy.geolocation.GeoFilter;
import org.sleuthkit.autopsy.geolocation.MapWaypoint;
@ -45,7 +44,7 @@ import org.sleuthkit.datamodel.DataSource;
/**
* Gathers summary data about Geolocation information for a data source.
*/
public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
public class GeolocationSummary {
/**
* A count of hits for a particular city.
@ -59,8 +58,8 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
* Main constructor.
*
* @param cityRecord The record for the city including name, country,
* and location.
* @param count The number of hits in proximity to that city.
* and location.
* @param count The number of hits in proximity to that city.
*/
CityRecordCount(CityRecord cityRecord, int count) {
this.cityRecord = cityRecord;
@ -69,7 +68,7 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
/**
* @return The record for the city including name, country, and
* location.
* location.
*/
public CityRecord getCityRecord() {
return cityRecord;
@ -96,8 +95,8 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
/**
* Main constructor.
*
* @param mostCommon The list of most common cities seen.
* @param mostRecent The list of most recent cities seen.
* @param mostCommon The list of most common cities seen.
* @param mostRecent The list of most recent cities seen.
* @param mostRecentSeen
*/
CityData(CityCountsList mostCommon, CityCountsList mostRecent, Long mostRecentSeen) {
@ -122,7 +121,7 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
/**
* @return The time stamp in seconds from epoch of the most recently
* seen city
* seen city
*/
public Long getMostRecentSeen() {
return mostRecentSeen;
@ -142,10 +141,10 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
/**
* Main constructor.
*
* @param counts The list of cities and the count of how many points are
* closest to that city.
* @param counts The list of cities and the count of how many points
* are closest to that city.
* @param otherCount The count of points where no closest city was
* determined due to not being close enough.
* determined due to not being close enough.
*/
CityCountsList(List<CityRecordCount> counts, int otherCount) {
this.counts = Collections.unmodifiableList(new ArrayList<>(counts));
@ -154,7 +153,7 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
/**
* @return The list of cities and the count of how many points are
* closest to that city.
* closest to that city.
*/
public List<CityRecordCount> getCounts() {
return counts;
@ -162,7 +161,7 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
/**
* @return The count of points where no closest city was determined due
* to not being close enough.
* to not being close enough.
*/
public int getOtherCount() {
return otherCount;
@ -183,10 +182,10 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
* Main constructor.
*
* @param mapWaypoints The way points found for the data source.
* @param tracks A list of sets where each set is a track in the data
* source.
* @param areas A list of areas where each set is an area in the data
* source.
* @param tracks A list of sets where each set is a track in the
* data source.
* @param areas A list of areas where each set is an area in the
* data source.
*/
private GeoResult(Set<MapWaypoint> mapWaypoints, List<Set<MapWaypoint>> tracks, List<Set<MapWaypoint>> areas) {
this.mapWaypoints = mapWaypoints;
@ -250,6 +249,7 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
* A supplier method that can throw an exception of E.
*
* @return The object type.
*
* @throws E The exception type.
*/
T get() throws E;
@ -277,13 +277,12 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
/**
* @return Returns all the geolocation artifact types.
*/
public List<ARTIFACT_TYPE> getGeoTypes() {
public static List<ARTIFACT_TYPE> getGeoTypes() {
return GPS_ARTIFACT_TYPES;
}
@Override
public Set<Integer> getArtifactTypeIdsForRefresh() {
return GPS_ARTIFACT_TYPE_IDS;
public static Set<Integer> getArtifactTypeIdsForRefresh() {
return Collections.unmodifiableSet(GPS_ARTIFACT_TYPE_IDS);
}
/**
@ -291,13 +290,14 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
* the event where either time is null.
*
* @param minTime The minimum time. If null is provided, this function will
* return false.
* @param time The time to check. If null is provided and the min time is
* non-null, then this function will return false.
* return false.
* @param time The time to check. If null is provided and the min time is
* non-null, then this function will return false.
*
* @return If minTime == null then false. If minTime != null && time == null
* then false. Otherwise time >= minTime.
* then false. Otherwise time >= minTime.
*/
private boolean greaterThanOrEqual(Long minTime, Long time) {
private static boolean greaterThanOrEqual(Long minTime, Long time) {
if (minTime != null && time != null && time >= minTime) {
return true;
} else {
@ -310,12 +310,13 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
* a total of waypoints whose time stamp is greater than or equal to
* minTime.
*
* @param points The list of way points.
* @param points The list of way points.
* @param minTime The minimum time for most recent points count.
*
* @return A pair where the left value is the total count of way points and
* the right is the total list of way points that are >= minTime.
* the right is the total list of way points that are >= minTime.
*/
private Pair<Integer, Integer> getCounts(List<Long> points, Long minTime) {
private static Pair<Integer, Integer> getCounts(List<Long> points, Long minTime) {
if (points == null) {
return EMPTY_COUNT;
}
@ -332,7 +333,8 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
* the point is null, null is returned.
*
* @param cityMapper The means of mapping a point to the closest city.
* @param pt The geolocation point.
* @param pt The geolocation point.
*
* @return A tuple of the closest city and timestamp in seconds from epoch.
*/
private Pair<CityRecord, Long> getClosestWithTime(ClosestCityMapper cityMapper, MapWaypoint pt) {
@ -351,10 +353,12 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
* into a stream of the unique cities identified in this grouping and the
* latest time stamp for each grouping.
*
* @param points The points in the grouping.
* @param points The points in the grouping.
* @param cityMapper The means of mapping a point to the closest city.
*
* @return A stream of tuples where each tuple will be a unique city (or
* null if a closest is not determined) and the latest timestamp for each.
* null if a closest is not determined) and the latest timestamp for
* each.
*/
private Stream<Pair<CityRecord, Long>> reduceGrouping(Set<MapWaypoint> points, ClosestCityMapper cityMapper) {
if (points == null) {
@ -367,7 +371,7 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
if (pair == null) {
continue;
}
CityRecord city = pair.getLeft();
Long prevTime = timeMapping.get(city);
Long curTime = pair.getRight();
@ -375,7 +379,7 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
timeMapping.put(city, curTime);
}
}
return timeMapping.entrySet().stream()
.map(e -> Pair.of(e.getKey(), e.getValue()));
}
@ -385,10 +389,12 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
* of tuples where each tuple represents a point with the closest city and
* the time stamp in seconds from epoch.
*
* @param geoResult The result from the Geolocation API.
* @param geoResult The result from the Geolocation API.
* @param cityMapper The means of mapping a point to the closest city.
*
* @return A list of tuples where each tuple represents a point to be
* counted with a combination of the closest city and the timestamp.
* counted with a combination of the closest city and the timestamp.
*
* @throws IOException
*/
private Stream<Pair<CityRecord, Long>> processGeoResult(GeoResult geoResult, ClosestCityMapper cityMapper) {
@ -398,7 +404,7 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
List<Set<MapWaypoint>> areas = (geoResult.getAreas() == null) ? Collections.emptyList() : geoResult.getAreas();
List<Set<MapWaypoint>> tracks = (geoResult.getTracks() == null) ? Collections.emptyList() : geoResult.getTracks();
Stream<Pair<CityRecord, Long>> reducedGroupings = Stream.of(areas, tracks)
.flatMap((groupingList) -> groupingList.stream())
.flatMap((grouping) -> reduceGrouping(grouping, cityMapper));
@ -407,7 +413,7 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
.flatMap((groupingList) -> groupingList.stream())
.flatMap((group) -> group.stream())
.collect(Collectors.toSet());
Set<MapWaypoint> pointSet = geoResult.getMapWaypoints() == null ? Collections.emptySet() : geoResult.getMapWaypoints();
Stream<Pair<CityRecord, Long>> citiesForPoints = pointSet.stream()
// it appears that AbstractWaypointFetcher.handleFilteredWaypointSet returns all points
@ -423,8 +429,8 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
* number of found hits (i.e. most hits is first index).
*
* @param dataSource The data source.
* @param daysCount Number of days to go back.
* @param maxCount Maximum number of results.
* @param daysCount Number of days to go back.
* @param maxCount Maximum number of results.
*
* @return The sorted list.
*
@ -507,11 +513,12 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
* Main constructor.
*
* @param asyncResult Geolocation fetches results in a callback which is
* already handled by other mechanisms in data source summary. The
* BlockingQueue blocks until a result is received from geolocation.
* @param filters The applicable filters for geolocation.
* already handled by other mechanisms in data source
* summary. The BlockingQueue blocks until a result
* is received from geolocation.
* @param filters The applicable filters for geolocation.
*/
public PointFetcher(BlockingQueue<GeoResult> asyncResult, GeoFilter filters) {
PointFetcher(BlockingQueue<GeoResult> asyncResult, GeoFilter filters) {
super(filters);
this.asyncResult = asyncResult;
}
@ -531,6 +538,7 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
* Fetches all GPS data for the data source from the current case.
*
* @param dataSource The data source.
*
* @return The GPS data pertaining to the data source.
* @throws SleuthkitCaseProviderException
* @throws GeoLocationDataException

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -64,8 +64,8 @@ class LatLngMap<E extends KdTree.XYZPoint> {
* Main contructor.
*
* @param pointsToAdd The points to be added to the data structure.
* @param bucketSize The size of a grid square in kilometers. So, if this
* value is 100, each sqaure will be a 100 x 100 km.
* @param bucketSize The size of a grid square in kilometers. So, if this
* value is 100, each sqaure will be a 100 x 100 km.
*/
LatLngMap(List<E> pointsToAdd, double bucketSize) {
this.bucketSize = bucketSize;
@ -86,6 +86,7 @@ class LatLngMap<E extends KdTree.XYZPoint> {
* closest neighboring buckets.
*
* @param point The point to calculate the bucket location pair.
*
* @return The pair that was determined.
*/
private Pair<Double, Double> getBucketLocation(XYZPoint point) {
@ -106,6 +107,7 @@ class LatLngMap<E extends KdTree.XYZPoint> {
* Finds closest point within (.5 * bucketSize) distance.
*
* @param point The point for which to find closest.
*
* @return Returns the found point.
*/
E findClosest(E point) {
@ -132,9 +134,10 @@ class LatLngMap<E extends KdTree.XYZPoint> {
/**
* Within the specific bucket, finds the closest point if any exists.
*
* @param x The x axis bucket.
* @param y The y axis bucket.
* @param x The x axis bucket.
* @param y The y axis bucket.
* @param point The point to search for.
*
* @return The point, if any, that was found.
*/
private E findClosestInBucket(int x, int y, E point) {

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -18,30 +18,22 @@
*/
package org.sleuthkit.autopsy.datasourcesummary.datamodel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultUpdateGovernor;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.ModuleContentEvent;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Provides methods to query for datasource files by mime type.
* Class to export summary information used by TypesPanel tab on the known files
* present in the specified DataSource.
*/
public class MimeTypeSummary implements DefaultUpdateGovernor {
public class MimeTypeSummary {
private final SleuthkitCaseProvider provider;
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS = new HashSet<>(
Arrays.asList(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED));
/**
* Main constructor.
*/
@ -58,26 +50,6 @@ public class MimeTypeSummary implements DefaultUpdateGovernor {
this.provider = provider;
}
@Override
public boolean isRefreshRequired(ModuleContentEvent evt) {
return true;
}
@Override
public boolean isRefreshRequired(AbstractFile file) {
return true;
}
@Override
public boolean isRefreshRequired(IngestManager.IngestJobEvent evt) {
return (evt != null && INGEST_JOB_EVENTS.contains(evt));
}
@Override
public Set<IngestManager.IngestJobEvent> getIngestJobEventUpdates() {
return INGEST_JOB_EVENTS;
}
/**
* Get the number of files in the case database for the current data source
* which have the specified mimetypes.
@ -98,12 +70,7 @@ public class MimeTypeSummary implements DefaultUpdateGovernor {
*/
public Long getCountOfFilesForMimeTypes(DataSource currentDataSource, Set<String> setOfMimeTypes)
throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException {
return DataSourceInfoUtilities.getCountOfRegNonSlackFiles(
provider.get(),
currentDataSource,
"mime_type IN " + getSqlSet(setOfMimeTypes)
);
return DataSourceInfoUtilities.getCountOfRegNonSlackFiles(provider.get(), currentDataSource, "mime_type IN " + getSqlSet(setOfMimeTypes));
}
/**
@ -124,13 +91,9 @@ public class MimeTypeSummary implements DefaultUpdateGovernor {
*/
public Long getCountOfFilesNotInMimeTypes(DataSource currentDataSource, Set<String> setOfMimeTypes)
throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException {
return DataSourceInfoUtilities.getCountOfRegNonSlackFiles(
provider.get(),
currentDataSource,
return DataSourceInfoUtilities.getCountOfRegNonSlackFiles(provider.get(), currentDataSource,
"mime_type NOT IN " + getSqlSet(setOfMimeTypes)
+ " AND mime_type IS NOT NULL AND mime_type <> '' "
);
+ " AND mime_type IS NOT NULL AND mime_type <> '' ");
}
/**
@ -146,7 +109,6 @@ public class MimeTypeSummary implements DefaultUpdateGovernor {
*/
public Long getCountOfAllRegularFiles(DataSource dataSource)
throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException {
return DataSourceInfoUtilities.getCountOfRegNonSlackFiles(provider.get(), dataSource, null);
}
@ -164,12 +126,7 @@ public class MimeTypeSummary implements DefaultUpdateGovernor {
*/
public Long getCountOfFilesWithNoMimeType(DataSource currentDataSource)
throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException {
return DataSourceInfoUtilities.getCountOfRegNonSlackFiles(
provider.get(),
currentDataSource,
"(mime_type IS NULL OR mime_type = '') "
);
return DataSourceInfoUtilities.getCountOfRegNonSlackFiles(provider.get(), currentDataSource, "(mime_type IS NULL OR mime_type = '') ");
}
/**

View File

@ -32,7 +32,6 @@ import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.centralrepository.ingestmodule.CentralRepoIngestModuleFactory;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultArtifactUpdateGovernor;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import org.sleuthkit.datamodel.BlackboardAttribute;
@ -64,7 +63,7 @@ import org.sleuthkit.datamodel.TskCoreException;
* d) The content of that TSK_OTHER_CASES attribute will be of the form
* "case1,case2...caseN"
*/
public class PastCasesSummary implements DefaultArtifactUpdateGovernor {
public class PastCasesSummary {
/**
* Return type for results items in the past cases tab.
@ -89,14 +88,14 @@ public class PastCasesSummary implements DefaultArtifactUpdateGovernor {
* @return Data for the cases with same id table.
*/
public List<Pair<String, Long>> getSameIdsResults() {
return sameIdsResults;
return Collections.unmodifiableList(sameIdsResults);
}
/**
* @return Data for the tagged notable table.
*/
public List<Pair<String, Long>> getTaggedNotable() {
return taggedNotable;
return Collections.unmodifiableList(taggedNotable);
}
}
@ -147,11 +146,6 @@ public class PastCasesSummary implements DefaultArtifactUpdateGovernor {
this.logger = logger;
}
@Override
public Set<Integer> getArtifactTypeIdsForRefresh() {
return ARTIFACT_UPDATE_TYPE_IDS;
}
/**
* Given the provided sources for an attribute, aims to determine if one of
* those sources is the Central Repository Ingest Module.
@ -216,7 +210,7 @@ public class PastCasesSummary implements DefaultArtifactUpdateGovernor {
* @return The list of unique cases and their occurrences sorted from max to
* min.
*/
private List<Pair<String, Long>> getCaseCounts(Stream<String> cases) {
private static List<Pair<String, Long>> getCaseCounts(Stream<String> cases) {
Collection<List<String>> groupedCases = cases
// group by case insensitive compare of cases
.collect(Collectors.groupingBy((caseStr) -> caseStr.toUpperCase().trim()))
@ -243,10 +237,11 @@ public class PastCasesSummary implements DefaultArtifactUpdateGovernor {
* @throws TskCoreException
* @throws NoCurrentCaseException
*/
private BlackboardArtifact getParentArtifact(BlackboardArtifact artifact) throws TskCoreException, NoCurrentCaseException {
private BlackboardArtifact getParentArtifact(BlackboardArtifact artifact) throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException {
BlackboardArtifact sourceArtifact = null;
Content content = Case.getCurrentCaseThrows().getSleuthkitCase().getContentById(artifact.getObjectID());
SleuthkitCase skCase = caseProvider.get();
Content content = skCase.getContentById(artifact.getObjectID());
if (content instanceof BlackboardArtifact) {
sourceArtifact = (BlackboardArtifact) content;
}
@ -263,7 +258,7 @@ public class PastCasesSummary implements DefaultArtifactUpdateGovernor {
* @throws TskCoreException
* @throws NoCurrentCaseException
*/
private boolean hasDeviceAssociatedArtifact(BlackboardArtifact artifact) throws TskCoreException, NoCurrentCaseException {
private boolean hasDeviceAssociatedArtifact(BlackboardArtifact artifact) throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException {
BlackboardArtifact parent = getParentArtifact(artifact);
if (parent == null) {
return false;
@ -284,7 +279,7 @@ public class PastCasesSummary implements DefaultArtifactUpdateGovernor {
* @throws NoCurrentCaseException
*/
public PastCasesResult getPastCasesData(DataSource dataSource)
throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, NoCurrentCaseException {
throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException {
if (dataSource == null) {
return null;
@ -294,32 +289,20 @@ public class PastCasesSummary implements DefaultArtifactUpdateGovernor {
List<String> deviceArtifactCases = new ArrayList<>();
List<String> nonDeviceArtifactCases = new ArrayList<>();
for (BlackboardArtifact artifact : skCase.getBlackboard().getArtifacts(ARTIFACT_TYPE.TSK_PREVIOUSLY_SEEN.getTypeID(), dataSource.getId())) {
List<String> cases = getCasesFromArtifact(artifact);
if (cases == null || cases.isEmpty()) {
continue;
}
for (Integer typeId : ARTIFACT_UPDATE_TYPE_IDS) {
for (BlackboardArtifact artifact : skCase.getBlackboard().getArtifacts(typeId, dataSource.getId())) {
List<String> cases = getCasesFromArtifact(artifact);
if (cases == null || cases.isEmpty()) {
continue;
}
if (hasDeviceAssociatedArtifact(artifact)) {
deviceArtifactCases.addAll(cases);
} else {
nonDeviceArtifactCases.addAll(cases);
if (hasDeviceAssociatedArtifact(artifact)) {
deviceArtifactCases.addAll(cases);
} else {
nonDeviceArtifactCases.addAll(cases);
}
}
}
for (BlackboardArtifact artifact : skCase.getBlackboard().getArtifacts(ARTIFACT_TYPE.TSK_PREVIOUSLY_NOTABLE.getTypeID(), dataSource.getId())) {
List<String> cases = getCasesFromArtifact(artifact);
if (cases == null || cases.isEmpty()) {
continue;
}
if (hasDeviceAssociatedArtifact(artifact)) {
deviceArtifactCases.addAll(cases);
} else {
nonDeviceArtifactCases.addAll(cases);
}
}
}
return new PastCasesResult(
getCaseCounts(deviceArtifactCases.stream()),

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Copyright 2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -18,21 +18,18 @@
*/
package org.sleuthkit.autopsy.datasourcesummary.datamodel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultArtifactUpdateGovernor;
import java.nio.file.Paths;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.commons.lang.StringUtils;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
@ -40,13 +37,12 @@ import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
/**
* Helper class for getting data for the Recent Files Data Summary tab.
* Helper class for getting Recent Activity data.
*/
public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
public class RecentFilesSummary {
private final static BlackboardAttribute.Type DATETIME_ACCESSED_ATT = new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED);
private final static BlackboardAttribute.Type DOMAIN_ATT = new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN);
@ -58,14 +54,6 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
private static final DateFormat DATETIME_FORMAT = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.getDefault());
private static final Set<Integer> ARTIFACT_UPDATE_TYPE_IDS = new HashSet<>(Arrays.asList(
ARTIFACT_TYPE.TSK_RECENT_OBJECT.getTypeID(),
ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID(),
ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT.getTypeID(),
ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID(),
ARTIFACT_TYPE.TSK_MESSAGE.getTypeID()
));
private final SleuthkitCaseProvider provider;
/**
@ -88,11 +76,6 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
this.provider = provider;
}
@Override
public Set<Integer> getArtifactTypeIdsForRefresh() {
return ARTIFACT_UPDATE_TYPE_IDS;
}
/**
* Removes fileDetails entries with redundant paths, sorts by date
* descending and limits to the limit provided.
@ -101,7 +84,7 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
* @param limit The maximum number of entries to return.
* @return The sorted limited list with unique paths.
*/
private <T extends RecentFileDetails> List<T> getSortedLimited(List<T> fileDetails, int limit) {
private static <T extends RecentFileDetails> List<T> getSortedLimited(List<T> fileDetails, int limit) {
Map<String, T> fileDetailsMap = fileDetails.stream()
.filter(details -> details != null)
.collect(Collectors.toMap(
@ -122,7 +105,7 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
* @param artifact The artifact.
* @return The derived object or null if artifact is invalid.
*/
private RecentFileDetails getRecentlyOpenedDocument(BlackboardArtifact artifact) {
private static RecentFileDetails getRecentlyOpenedDocument(BlackboardArtifact artifact) {
String path = DataSourceInfoUtilities.getStringOrNull(artifact, PATH_ATT);
Long lastOpened = DataSourceInfoUtilities.getLongOrNull(artifact, DATETIME_ACCESSED_ATT);
@ -170,7 +153,7 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
* @param artifact The artifact.
* @return The derived object or null if artifact is invalid.
*/
private RecentDownloadDetails getRecentDownload(BlackboardArtifact artifact) {
private static RecentDownloadDetails getRecentDownload(BlackboardArtifact artifact) {
Long accessedTime = DataSourceInfoUtilities.getLongOrNull(artifact, DATETIME_ACCESSED_ATT);
String domain = DataSourceInfoUtilities.getStringOrNull(artifact, DOMAIN_ATT);
String path = DataSourceInfoUtilities.getStringOrNull(artifact, PATH_ATT);
@ -187,7 +170,7 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
*
* @param count The count.
*/
private void throwOnNonPositiveCount(int count) {
private static void throwOnNonPositiveCount(int count) {
if (count < 1) {
throw new IllegalArgumentException("Invalid count: value must be greater than 0.");
}
@ -268,7 +251,7 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
* @return The derived object or null.
* @throws TskCoreException
*/
private RecentAttachmentDetails getRecentAttachment(BlackboardArtifact artifact, SleuthkitCase skCase) throws TskCoreException {
private static RecentAttachmentDetails getRecentAttachment(BlackboardArtifact artifact, SleuthkitCase skCase) throws TskCoreException {
// get associated artifact or return no result
BlackboardAttribute attribute = artifact.getAttribute(ASSOCATED_ATT);
if (attribute == null) {
@ -309,7 +292,7 @@ public class RecentFilesSummary implements DefaultArtifactUpdateGovernor {
*
* @return True if the given artifact is a message artifact
*/
private boolean isMessageArtifact(BlackboardArtifact nodeArtifact) {
private static boolean isMessageArtifact(BlackboardArtifact nodeArtifact) {
final int artifactTypeID = nodeArtifact.getArtifactTypeID();
return artifactTypeID == ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()
|| artifactTypeID == ARTIFACT_TYPE.TSK_MESSAGE.getTypeID();

View File

@ -18,7 +18,6 @@
*/
package org.sleuthkit.autopsy.datasourcesummary.datamodel;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.timeline.TimeLineController;
import org.sleuthkit.autopsy.timeline.TimeLineModule;
import org.sleuthkit.autopsy.timeline.ui.filtering.datamodel.FilterState;
@ -60,10 +59,9 @@ public class TimelineDataSourceUtils {
* @param dataSource The data source.
* @return The root filter representing a default filter with only this data
* source selected.
* @throws NoCurrentCaseException
* @throws TskCoreException
*/
public RootFilter getDataSourceFilter(DataSource dataSource) throws NoCurrentCaseException, TskCoreException {
public RootFilter getDataSourceFilter(DataSource dataSource) throws TskCoreException {
RootFilterState filterState = getDataSourceFilterState(dataSource);
return filterState == null ? null : filterState.getActiveFilter();
}
@ -75,10 +73,9 @@ public class TimelineDataSourceUtils {
* @param dataSource The data source.
* @return The root filter state representing a default filter with only
* this data source selected.
* @throws NoCurrentCaseException
* @throws TskCoreException
*/
public RootFilterState getDataSourceFilterState(DataSource dataSource) throws NoCurrentCaseException, TskCoreException {
public RootFilterState getDataSourceFilterState(DataSource dataSource) throws TskCoreException {
TimeLineController controller = TimeLineModule.getController();
RootFilterState dataSourceState = controller.getEventsModel().getDefaultEventFilterState().copyOf();

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -18,6 +18,8 @@
*/
package org.sleuthkit.autopsy.datasourcesummary.datamodel;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
@ -26,29 +28,25 @@ import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import org.joda.time.Interval;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultUpdateGovernor;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.ModuleContentEvent;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.TimelineEvent;
import org.sleuthkit.datamodel.TimelineEventType;
import org.sleuthkit.datamodel.TimelineFilter.RootFilter;
import org.sleuthkit.datamodel.TimelineManager;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
import java.util.function.Supplier;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.core.UserPreferences;
/**
* Provides data source summary information pertaining to Timeline data.
*/
public class TimelineSummary implements DefaultUpdateGovernor {
public class TimelineSummary {
/**
* A function for obtaining a Timeline RootFilter filtered to the specific
@ -61,16 +59,13 @@ public class TimelineSummary implements DefaultUpdateGovernor {
*
* @param dataSource The data source.
* @return The timeline root filter.
* @throws NoCurrentCaseException
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
*/
RootFilter apply(DataSource dataSource) throws NoCurrentCaseException, TskCoreException;
RootFilter apply(DataSource dataSource) throws SleuthkitCaseProviderException, TskCoreException;
}
private static final long DAY_SECS = 24 * 60 * 60;
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS = new HashSet<>(
Arrays.asList(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED));
private static final Set<TimelineEventType> FILE_SYSTEM_EVENTS
= new HashSet<>(Arrays.asList(
TimelineEventType.FILE_MODIFIED,
@ -105,39 +100,19 @@ public class TimelineSummary implements DefaultUpdateGovernor {
this.filterFunction = filterFunction;
}
@Override
public boolean isRefreshRequired(ModuleContentEvent evt) {
return true;
}
@Override
public boolean isRefreshRequired(AbstractFile file) {
return true;
}
@Override
public boolean isRefreshRequired(IngestManager.IngestJobEvent evt) {
return (evt != null && INGEST_JOB_EVENTS.contains(evt));
}
@Override
public Set<IngestManager.IngestJobEvent> getIngestJobEventUpdates() {
return INGEST_JOB_EVENTS;
}
/**
* Retrieves timeline summary data.
*
* @param dataSource The data source for which timeline data will be
* retrieved.
* @param dataSource The data source for which timeline data will be
* retrieved.
* @param recentDaysNum The maximum number of most recent days' activity to
* include.
* include.
*
* @return The retrieved data.
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
* @throws NoCurrentCaseException
*/
public TimelineSummaryData getData(DataSource dataSource, int recentDaysNum) throws SleuthkitCaseProviderException, TskCoreException, NoCurrentCaseException {
public TimelineSummaryData getTimelineSummaryData(DataSource dataSource, int recentDaysNum) throws SleuthkitCaseProviderException, TskCoreException {
TimeZone timeZone = this.timeZoneProvider.get();
TimelineManager timelineManager = this.caseProvider.get().getTimelineManager();
@ -174,10 +149,11 @@ public class TimelineSummary implements DefaultUpdateGovernor {
* Given activity by day, converts to most recent days' activity handling
* empty values.
*
* @param dateCounts The day from epoch mapped to activity amounts for that
* day.
* @param dateCounts The day from epoch mapped to activity amounts for
* that day.
* @param minRecentDay The minimum recent day in days from epoch.
* @param maxDay The maximum recent day in days from epoch;
* @param maxDay The maximum recent day in days from epoch;
*
* @return The most recent daily activity amounts.
*/
private List<DailyActivityAmount> getMostRecentActivityAmounts(Map<Long, DailyActivityAmount> dateCounts, long minRecentDay, long maxDay) {
@ -197,17 +173,18 @@ public class TimelineSummary implements DefaultUpdateGovernor {
/**
* Fetches timeline events per day for a particular data source.
*
* @param dataSource The data source.
* @param dataSource The data source.
* @param timelineManager The timeline manager to use while fetching the
* data.
* @param timeZone The time zone to use to determine which day activity
* belongs.
* data.
* @param timeZone The time zone to use to determine which day
* activity belongs.
*
* @return A Map mapping days from epoch to the activity for that day.
*
* @throws TskCoreException
* @throws NoCurrentCaseException
*/
private Map<Long, DailyActivityAmount> getTimelineEventsByDay(DataSource dataSource, TimelineManager timelineManager, TimeZone timeZone)
throws TskCoreException, NoCurrentCaseException {
throws TskCoreException, SleuthkitCaseProviderException {
RootFilter rootFilter = this.filterFunction.apply(dataSource);
// get events for data source
@ -251,12 +228,14 @@ public class TimelineSummary implements DefaultUpdateGovernor {
/**
* Main constructor.
*
* @param minDate Earliest usage date recorded for the data source.
* @param maxDate Latest usage date recorded for the data source.
* @param minDate Earliest usage date recorded for the data
* source.
* @param maxDate Latest usage date recorded for the data
* source.
* @param recentDaysActivity A list of activity prior to and including
* max date sorted by min to max date.
* @param dataSource The data source for which this data applies. the
* latest usage date by day.
* max date sorted by min to max date.
* @param dataSource The data source for which this data
* applies. the latest usage date by day.
*/
TimelineSummaryData(Date minDate, Date maxDate, List<DailyActivityAmount> recentDaysActivity, DataSource dataSource) {
this.minDate = minDate;
@ -281,7 +260,7 @@ public class TimelineSummary implements DefaultUpdateGovernor {
/**
* @return A list of activity prior to and including the latest usage
* date by day sorted min to max date.
* date by day sorted min to max date.
*/
public List<DailyActivityAmount> getMostRecentDaysActivity() {
return histogramActivity;
@ -307,8 +286,10 @@ public class TimelineSummary implements DefaultUpdateGovernor {
/**
* Main constructor.
*
* @param day The day for which activity is being measured.
* @param fileActivityCount The amount of file activity timeline events.
* @param day The day for which activity is being
* measured.
* @param fileActivityCount The amount of file activity timeline
* events.
* @param artifactActivityCount The amount of artifact timeline events.
*/
DailyActivityAmount(Date day, long fileActivityCount, long artifactActivityCount) {
@ -337,6 +318,29 @@ public class TimelineSummary implements DefaultUpdateGovernor {
public long getArtifactActivityCount() {
return artifactActivityCount;
}
}
/**
* Creates a DateFormat formatter that uses UTC for time zone.
*
* @param formatString The date format string.
* @return The data format.
*/
public static DateFormat getUtcFormat(String formatString) {
return new SimpleDateFormat(formatString, Locale.getDefault());
}
/**
* Formats a date using a DateFormat. In the event that the date is null,
* returns a null string.
*
* @param date The date to format.
* @param formatter The DateFormat to use to format the date.
*
* @return The formatted string generated from the formatter or null if the
* date is null.
*/
public static String formatDate(Date date, DateFormat formatter) {
return date == null ? null : formatter.format(date);
}
}

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 - 2020 Basis Technology Corp.
* Copyright 2019 - 2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -18,26 +18,19 @@
*/
package org.sleuthkit.autopsy.datasourcesummary.datamodel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultUpdateGovernor;
import java.awt.Color;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.ModuleContentEvent;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.autopsy.coreutils.FileTypeUtils;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
/**
* Provides information for the DataSourceSummaryCountsPanel.
* Helper class for getting summary information on the known files present in the
* specified DataSource..
*/
public class TypesSummary implements DefaultUpdateGovernor {
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS = new HashSet<>(
Arrays.asList(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED));
public class TypesSummary {
private final SleuthkitCaseProvider provider;
@ -57,25 +50,6 @@ public class TypesSummary implements DefaultUpdateGovernor {
this.provider = provider;
}
@Override
public boolean isRefreshRequired(ModuleContentEvent evt) {
return true;
}
@Override
public boolean isRefreshRequired(AbstractFile file) {
return true;
}
@Override
public boolean isRefreshRequired(IngestManager.IngestJobEvent evt) {
return (evt != null && INGEST_JOB_EVENTS.contains(evt));
}
@Override
public Set<IngestManager.IngestJobEvent> getIngestJobEventUpdates() {
return INGEST_JOB_EVENTS;
}
/**
* Get count of regular files (not directories) in a data source.
@ -169,4 +143,59 @@ public class TypesSummary implements DefaultUpdateGovernor {
return DataSourceInfoUtilities.getCountOfRegularFiles(provider.get(), currentDataSource,
"type=" + TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.getFileType());
}
/**
* Information concerning a particular file type category.
*/
public static class FileTypeCategoryData {
private final String label;
private final Set<String> mimeTypes;
private final Color color;
/**
* Main constructor.
*
* @param label The label for this slice.
* @param mimeTypes The mime types associated with this slice.
* @param color The color associated with this slice.
*/
public FileTypeCategoryData(String label, Set<String> mimeTypes, Color color) {
this.label = label;
this.mimeTypes = mimeTypes;
this.color = color;
}
/**
* Constructor that accepts FileTypeCategory.
*
* @param label The label for this slice.
* @param mimeTypes The mime types associated with this slice.
* @param color The color associated with this slice.
*/
public FileTypeCategoryData(String label, FileTypeUtils.FileTypeCategory fileCategory, Color color) {
this(label, fileCategory.getMediaTypes(), color);
}
/**
* @return The label for this category.
*/
public String getLabel() {
return label;
}
/**
* @return The mime types associated with this category.
*/
public Set<String> getMimeTypes() {
return mimeTypes;
}
/**
* @return The color associated with this category.
*/
public Color getColor() {
return color;
}
}
}

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -19,7 +19,6 @@
package org.sleuthkit.autopsy.datasourcesummary.datamodel;
import java.io.File;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultArtifactUpdateGovernor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@ -54,7 +53,7 @@ import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
* time, the data being provided for domains is fictitious and is done as a
* placeholder.
*/
public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
public class UserActivitySummary {
/**
* Functions that determine the folder name of a list of path elements. If
@ -138,16 +137,6 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
.compareToIgnoreCase((b.getProgramName() == null ? "" : b.getProgramName()));
};
private static final Set<Integer> ARTIFACT_UPDATE_TYPE_IDS = new HashSet<>(Arrays.asList(
ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY.getTypeID(),
ARTIFACT_TYPE.TSK_MESSAGE.getTypeID(),
ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID(),
ARTIFACT_TYPE.TSK_CALLLOG.getTypeID(),
ARTIFACT_TYPE.TSK_DEVICE_ATTACHED.getTypeID(),
ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID(),
ARTIFACT_TYPE.TSK_PROG_RUN.getTypeID()
));
private static final Set<String> DEVICE_EXCLUDE_LIST = new HashSet<>(Arrays.asList("ROOT_HUB", "ROOT_HUB20"));
private static final Set<String> DOMAIN_EXCLUDE_LIST = new HashSet<>(Arrays.asList("127.0.0.1", "LOCALHOST"));
@ -186,27 +175,55 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
this.logger = logger;
}
@Override
public Set<Integer> getArtifactTypeIdsForRefresh() {
return ARTIFACT_UPDATE_TYPE_IDS;
}
/**
* Throws an IllegalArgumentException if count <= 0.
*
* @param count The count being checked.
*/
private void assertValidCount(int count) {
private static void assertValidCount(int count) {
if (count <= 0) {
throw new IllegalArgumentException("Count must be greater than 0");
}
}
/**
* Determines a short folder name if any. Otherwise, returns empty string.
*
* @param strPath The string path.
* @param applicationName The application name.
*
* @return The short folder name or empty string if not found.
*/
public static String getShortFolderName(String strPath, String applicationName) {
if (strPath == null) {
return "";
}
List<String> pathEls = new ArrayList<>(Arrays.asList(applicationName));
File file = new File(strPath);
while (file != null && org.apache.commons.lang.StringUtils.isNotBlank(file.getName())) {
pathEls.add(file.getName());
file = file.getParentFile();
}
Collections.reverse(pathEls);
for (Function<List<String>, String> matchEntry : SHORT_FOLDER_MATCHERS) {
String result = matchEntry.apply(pathEls);
if (org.apache.commons.lang.StringUtils.isNotBlank(result)) {
return result;
}
}
return "";
}
/**
* Gets a list of recent domains based on the datasource.
*
* @param dataSource The datasource to query for recent domains.
* @param count The max count of items to return.
* @param count The max count of items to return.
*
* @return The list of items retrieved from the database.
*
@ -242,13 +259,13 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* Creates a TopDomainsResult from data or null if no visit date exists
* within DOMAIN_WINDOW_MS of mostRecentMs.
*
* @param domain The domain.
* @param visits The list of the artifact and its associated time in
* milliseconds.
* @param domain The domain.
* @param visits The list of the artifact and its associated time in
* milliseconds.
* @param mostRecentMs The most recent visit of any domain.
*
* @return The TopDomainsResult or null if no visits to this domain within
* 30 days of mostRecentMs.
* 30 days of mostRecentMs.
*/
private TopDomainsResult getDomainsResult(String domain, List<Pair<BlackboardArtifact, Long>> visits, long mostRecentMs) {
long visitCount = 0;
@ -288,9 +305,9 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* @param dataSource The datasource.
*
* @return A tuple where the first value is the latest web history accessed
* date in milliseconds and the second value maps normalized (lowercase;
* trimmed) domain names to when those domains were visited and the relevant
* artifact.
* date in milliseconds and the second value maps normalized
* (lowercase; trimmed) domain names to when those domains were
* visited and the relevant artifact.
*
* @throws TskCoreException
* @throws SleuthkitCaseProviderException
@ -357,7 +374,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* @param artifact The artifact.
*
* @return The TopWebSearchResult or null if the search string or date
* accessed cannot be determined.
* accessed cannot be determined.
*/
private static TopWebSearchResult getWebSearchResult(BlackboardArtifact artifact) {
String searchString = DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_TEXT);
@ -372,10 +389,11 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* term.
*
* @param dataSource The data source.
* @param count The maximum number of records to be shown (must be > 0).
* @param count The maximum number of records to be shown (must be >
* 0).
*
* @return The list of most recent web searches where most recent search
* appears first.
* appears first.
*
* @throws
* org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException
@ -462,6 +480,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
*
* @param r1 A result.
* @param r2 Another result.
*
* @return The most recent one with a non-null date.
*/
private TopDeviceAttachedResult getMostRecentDevice(TopDeviceAttachedResult r1, TopDeviceAttachedResult r2) {
@ -480,10 +499,11 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* Retrieves most recent devices used by most recent date attached.
*
* @param dataSource The data source.
* @param count The maximum number of records to be shown (must be > 0).
* @param count The maximum number of records to be shown (must be >
* 0).
*
* @return The list of most recent devices attached where most recent device
* attached appears first.
* attached appears first.
*
* @throws
* org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException
@ -528,7 +548,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* @param artifact The artifact.
*
* @return The TopAccountResult or null if the account type or message date
* cannot be determined.
* cannot be determined.
*/
private static TopAccountResult getMessageAccountResult(BlackboardArtifact artifact) {
String type = DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_MESSAGE_TYPE);
@ -542,12 +562,12 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* Obtains a TopAccountResult from a blackboard artifact. The date is
* maximum of any found dates for attribute types provided.
*
* @param artifact The artifact.
* @param artifact The artifact.
* @param messageType The type of message this is.
* @param dateAttrs The date attribute types.
* @param dateAttrs The date attribute types.
*
* @return The TopAccountResult or null if the account type or max date are
* not provided.
* not provided.
*/
private static TopAccountResult getAccountResult(BlackboardArtifact artifact, String messageType, BlackboardAttribute.Type... dateAttrs) {
String type = messageType;
@ -638,39 +658,6 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
.collect(Collectors.toList());
}
/**
* Determines a short folder name if any. Otherwise, returns empty string.
*
* @param strPath The string path.
* @param applicationName The application name.
*
* @return The short folder name or empty string if not found.
*/
public String getShortFolderName(String strPath, String applicationName) {
if (strPath == null) {
return "";
}
List<String> pathEls = new ArrayList<>(Arrays.asList(applicationName));
File file = new File(strPath);
while (file != null && org.apache.commons.lang.StringUtils.isNotBlank(file.getName())) {
pathEls.add(file.getName());
file = file.getParentFile();
}
Collections.reverse(pathEls);
for (Function<List<String>, String> matchEntry : SHORT_FOLDER_MATCHERS) {
String result = matchEntry.apply(pathEls);
if (org.apache.commons.lang.StringUtils.isNotBlank(result)) {
return result;
}
}
return "";
}
/**
* Creates a TopProgramsResult from a TSK_PROG_RUN blackboard artifact.
*
@ -764,12 +751,12 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* be ignored and all items will be returned.
*
* @param dataSource The datasource. If the datasource is null, an empty
* list will be returned.
* @param count The number of results to return. This value must be > 0 or
* an IllegalArgumentException will be thrown.
* list will be returned.
* @param count The number of results to return. This value must be > 0
* or an IllegalArgumentException will be thrown.
*
* @return The sorted list and limited to the count if last run or run count
* information is available on any item.
* information is available on any item.
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
@ -840,7 +827,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* Main constructor.
*
* @param lastAccessed The date of last access.
* @param artifact The relevant blackboard artifact.
* @param artifact The relevant blackboard artifact.
*/
public LastAccessedArtifact(Date lastAccessed, BlackboardArtifact artifact) {
this.lastAccessed = lastAccessed;
@ -875,7 +862,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
*
* @param searchString The search string.
* @param dateAccessed The latest date searched.
* @param artifact The relevant blackboard artifact.
* @param artifact The relevant blackboard artifact.
*/
public TopWebSearchResult(String searchString, Date dateAccessed, BlackboardArtifact artifact) {
super(dateAccessed, artifact);
@ -918,11 +905,11 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
/**
* Main constructor.
*
* @param deviceId The device id.
* @param deviceId The device id.
* @param dateAccessed The date last attached.
* @param deviceMake The device make.
* @param deviceModel The device model.
* @param artifact The relevant blackboard artifact.
* @param deviceMake The device make.
* @param deviceModel The device model.
* @param artifact The relevant blackboard artifact.
*/
public TopDeviceAttachedResult(String deviceId, Date dateAccessed, String deviceMake, String deviceModel, BlackboardArtifact artifact) {
super(dateAccessed, artifact);
@ -965,8 +952,8 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* Main constructor.
*
* @param accountType The account type.
* @param lastAccess The date the account was last accessed.
* @param artifact The artifact indicating last access.
* @param lastAccess The date the account was last accessed.
* @param artifact The artifact indicating last access.
*/
public TopAccountResult(String accountType, Date lastAccess, BlackboardArtifact artifact) {
super(lastAccess, artifact);
@ -992,10 +979,10 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
/**
* Describes a top domain result.
*
* @param domain The domain.
* @param domain The domain.
* @param visitTimes The number of times it was visited.
* @param lastVisit The date of the last visit.
* @param artifact The relevant blackboard artifact.
* @param lastVisit The date of the last visit.
* @param artifact The relevant blackboard artifact.
*/
public TopDomainsResult(String domain, Long visitTimes, Date lastVisit, BlackboardArtifact artifact) {
super(lastVisit, artifact);
@ -1032,8 +1019,8 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
*
* @param programName The name of the program.
* @param programPath The path of the program.
* @param runTimes The number of runs.
* @param artifact The relevant blackboard artifact.
* @param runTimes The number of runs.
* @param artifact The relevant blackboard artifact.
*/
TopProgramsResult(String programName, String programPath, Long runTimes, Date lastRun, BlackboardArtifact artifact) {
super(lastRun, artifact);

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -21,16 +21,12 @@ package org.sleuthkit.autopsy.datasourcesummary.ui;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.lang3.tuple.Pair;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.AnalysisSummary;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ColumnModel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetcher;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataFetcher;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultCellModel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport.ExcelSheetExport;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel;
import org.sleuthkit.datamodel.DataSource;
@ -101,10 +97,10 @@ public class AnalysisPanel extends BaseDataSourceSummaryPanel {
* Creates a new DataSourceUserActivityPanel.
*/
public AnalysisPanel() {
this(new AnalysisSummary());
this(new AnalysisSummaryGetter());
}
public AnalysisPanel(AnalysisSummary analysisData) {
public AnalysisPanel(AnalysisSummaryGetter analysisData) {
super(analysisData);
hashsetsFetcher = (dataSource) -> analysisData.getHashsetCounts(dataSource);
@ -229,17 +225,6 @@ public class AnalysisPanel extends BaseDataSourceSummaryPanel {
);
}// </editor-fold>//GEN-END:initComponents
@Override
List<ExcelSheetExport> getExports(DataSource dataSource) {
return Stream.of(
getTableExport(hashsetsFetcher, DEFAULT_COLUMNS, Bundle.AnalysisPanel_hashsetHits_tabName(), dataSource),
getTableExport(keywordsFetcher, DEFAULT_COLUMNS, Bundle.AnalysisPanel_keywordHits_tabName(), dataSource),
getTableExport(interestingItemsFetcher, DEFAULT_COLUMNS, Bundle.AnalysisPanel_interestingItemHits_tabName(), dataSource))
.filter(sheet -> sheet != null)
.collect(Collectors.toList());
}
// Variables declaration - do not modify//GEN-BEGIN:variables
// End of variables declaration//GEN-END:variables
}

View File

@ -0,0 +1,105 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020-2021 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.datasourcesummary.ui;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultArtifactUpdateGovernor;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.lang3.tuple.Pair;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.AnalysisSummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Wrapper class for converting
* org.sleuthkit.autopsy.contentutils.AnalysisSummary functionality into a
* DefaultArtifactUpdateGovernor used by data source analysis tab.
*/
public class AnalysisSummaryGetter implements DefaultArtifactUpdateGovernor {
private static final Set<Integer> ARTIFACT_UPDATE_TYPE_IDS = new HashSet<>(Arrays.asList(
ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID(),
ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID(),
ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID(),
ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()
));
private final AnalysisSummary analysisSummary;
/**
* Main constructor.
*/
public AnalysisSummaryGetter() {
analysisSummary = new AnalysisSummary();
}
@Override
public Set<Integer> getArtifactTypeIdsForRefresh() {
return Collections.unmodifiableSet(ARTIFACT_UPDATE_TYPE_IDS);
}
/**
* Gets counts for hashset hits.
*
* @param dataSource The datasource for which to identify hashset hits.
*
* @return The hashset set name with the number of hits in descending order.
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
*/
public List<Pair<String, Long>> getHashsetCounts(DataSource dataSource) throws SleuthkitCaseProviderException, TskCoreException {
return analysisSummary.getHashsetCounts(dataSource);
}
/**
* Gets counts for keyword hits.
*
* @param dataSource The datasource for which to identify keyword hits.
*
* @return The keyword set name with the number of hits in descending order.
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
*/
public List<Pair<String, Long>> getKeywordCounts(DataSource dataSource) throws SleuthkitCaseProviderException, TskCoreException {
return analysisSummary.getKeywordCounts(dataSource);
}
/**
* Gets counts for interesting item hits.
*
* @param dataSource The datasource for which to identify interesting item
* hits.
*
* @return The interesting item set name with the number of hits in
* descending order.
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
*/
public List<Pair<String, Long>> getInterestingItemCounts(DataSource dataSource) throws SleuthkitCaseProviderException, TskCoreException {
return analysisSummary.getInterestingItemCounts(dataSource);
}
}

View File

@ -38,16 +38,11 @@ import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ColumnModel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetcher;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataFetcher;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.EventUpdateHandler;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport.ExcelExportException;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport.ExcelSheetExport;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelTableExport;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelCellModel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.GuiCellModel.DefaultMenuItem;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.GuiCellModel.MenuItem;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableComponent;
@ -453,14 +448,6 @@ abstract class BaseDataSourceSummaryPanel extends JPanel {
*/
protected abstract void onNewDataSource(DataSource dataSource);
/**
* Returns all the excel exportable items associated with the tab.
*
* @param dataSource The data source that results should be filtered.
* @return The excel exportable objects.
*/
abstract List<ExcelSheetExport> getExports(DataSource dataSource);
/**
* Runs a data fetcher and returns the result handling any possible errors
* with a log message.
@ -485,100 +472,6 @@ abstract class BaseDataSourceSummaryPanel extends JPanel {
}
}
/**
* Function that converts data into a excel sheet data.
*/
protected interface ExcelExportFunction<T> {
/**
* Function that converts data into an excel sheet.
*
* @param data The data.
* @return The excel sheet export.
* @throws ExcelExportException
*/
ExcelSheetExport convert(T data) throws ExcelExportException;
}
/**
* Helper method that converts data into an excel sheet export handling
* possible excel exceptions.
*
* @param excelConverter Function to convert data to an excel sheet export.
* @param data The data. If data is null, null will be returned.
* @param sheetName The name(s) of the sheet (to be used in the error
* message).
* @return The excel sheet export.
*/
protected static <T> ExcelSheetExport convertToExcel(ExcelExportFunction<T> excelConverter, T data, String sheetName) {
if (data == null) {
return null;
}
try {
return excelConverter.convert(data);
} catch (ExcelExportException ex) {
logger.log(Level.WARNING,
String.format("There was an error while preparing export of worksheet(s): '%s'",
sheetName == null ? "<null>" : sheetName), ex);
return null;
}
}
/**
* Returns an excel sheet export given the fetching of data or null if no
* export created.
*
* @param dataFetcher The means of fetching data.
* @param excelConverter The means of converting data to excel.
* @param sheetName The name of the sheet (for error handling reporting).
* @param ds The data source to use for fetching data.
* @return The excel sheet export or null if no export could be generated.
*/
protected static <T> ExcelSheetExport getExport(
DataFetcher<DataSource, T> dataFetcher, ExcelExportFunction<T> excelConverter,
String sheetName, DataSource ds) {
T data = getFetchResult(dataFetcher, sheetName, ds);
return convertToExcel(excelConverter, data, sheetName);
}
/**
* Returns an excel table export of the data or null if no export created.
*
* @param columnsModel The model for the columns.
* @param sheetName The name for the sheet.
* @param data The data to be exported.
* @return The excel table export or null if no export could be generated.
*/
protected static <T, C extends ExcelCellModel> ExcelSheetExport getTableExport(List<ColumnModel<T, C>> columnsModel,
String sheetName, List<T> data) {
return convertToExcel((dataList) -> new ExcelTableExport<T, C>(sheetName, columnsModel, dataList),
data,
sheetName);
}
/**
* Returns an excel table export of the data or null if no export created.
*
* @param dataFetcher The means of fetching data for the data source and the
* export.
* @param columnsModel The model for the columns.
* @param sheetName The name for the sheet.
* @param ds The data source.
* @return The excel export or null if no export created.
*/
protected static <T, C extends ExcelCellModel> ExcelSheetExport getTableExport(
DataFetcher<DataSource, List<T>> dataFetcher, List<ColumnModel<T, C>> columnsModel,
String sheetName, DataSource ds) {
return getExport(dataFetcher,
(dataList) -> new ExcelTableExport<T, C>(sheetName, columnsModel, dataList),
sheetName,
ds);
}
/**
* Utility method that shows a loading screen with loadable components,
* create swing workers from the datafetch components and data source

View File

@ -6,19 +6,6 @@ AnalysisPanel_keywordHits_tabName=Keyword Hits
AnalysisPanel_keywordSearchModuleName=Keyword Search
BaseDataSourceSummaryPanel_goToArtifact=View Source Result
BaseDataSourceSummaryPanel_goToFile=View Source File in Directory
ContainerPanel_export_acquisitionDetails=Acquisition Details:
ContainerPanel_export_deviceId=Device ID:
ContainerPanel_export_displayName=Display Name:
ContainerPanel_export_filePaths=File Paths:
ContainerPanel_export_imageType=Image Type:
ContainerPanel_export_md5=MD5:
ContainerPanel_export_originalName=Name:
ContainerPanel_export_sectorSize=Sector Size:
ContainerPanel_export_sha1=SHA1:
ContainerPanel_export_sha256=SHA256:
ContainerPanel_export_size=Size:
ContainerPanel_export_timeZone=Time Zone:
ContainerPanel_export_unallocatedSize=Unallocated Space:
ContainerPanel_setFieldsForNonImageDataSource_na=N/A
ContainerPanel_tabName=Container
CTL_DataSourceSummaryAction=Data Source Summary
@ -62,7 +49,6 @@ DataSourceSummaryNode.column.type.header=Type
DataSourceSummaryNode.viewDataSourceAction.text=Go to Data Source
DataSourceSummaryTabbedPane_analysisTab_title=Analysis
DataSourceSummaryTabbedPane_detailsTab_title=Container
DataSourceSummaryTabbedPane_exportTab_title=Export
DataSourceSummaryTabbedPane_geolocationTab_title=Geolocation
DataSourceSummaryTabbedPane_ingestHistoryTab_title=Ingest History
DataSourceSummaryTabbedPane_pastCasesTab_title=Past Cases
@ -70,45 +56,27 @@ DataSourceSummaryTabbedPane_recentFileTab_title=Recent Files
DataSourceSummaryTabbedPane_timelineTab_title=Timeline
DataSourceSummaryTabbedPane_typesTab_title=Types
DataSourceSummaryTabbedPane_userActivityTab_title=User Activity
ExcelExportAction_exportToXLSX_beginExport=Beginning Export...
# {0} - tabName
ExcelExportAction_exportToXLSX_gatheringTabData=Fetching Data for {0} Tab...
ExcelExportAction_exportToXLSX_writingToFile=Writing to File...
ExcelExportAction_getXLSXPath_directory=DataSourceSummary
ExcelExportAction_moduleName=Data Source Summary
ExcelExportAction_runXLSXExport_errorMessage=There was an error while exporting.
ExcelExportAction_runXLSXExport_errorTitle=Error While Exporting
ExcelExportAction_runXLSXExport_progressCancelActionTitle=Cancelling...
ExcelExportAction_runXLSXExport_progressCancelTitle=Cancel
# {0} - dataSource
ExcelExportAction_runXLSXExport_progressTitle=Exporting {0} to XLSX
ExcelExportDialog_title=Data Source Summary Exported
DataSourceUserActivitySummary_getRecentAccounts_calllogMessage=Call Log
DataSourceUserActivitySummary_getRecentAccounts_emailMessage=Email Message
GeolocationPanel_cityColumn_title=Closest City
GeolocationPanel_countColumn_title=Count
GeolocationPanel_mostCommon_tabName=Most Common Cities
GeolocationPanel_mostRecent_tabName=Most Recent Cities
GeolocationPanel_onNoCrIngest_message=No results will be shown because the GPX Parser was not run.
GeolocationPanel_unknownRow_title=Unknown
IngestJobExcelExport_endTimeColumn=End Time
IngestJobExcelExport_ingestStatusTimeColumn=Ingest Status
IngestJobExcelExport_moduleNameTimeColumn=Module Name
IngestJobExcelExport_sheetName=Ingest History
IngestJobExcelExport_startTimeColumn=Start Time
IngestJobExcelExport_versionColumn=Module Version
PastCasesPanel_caseColumn_title=Case
PastCasesPanel_countColumn_title=Count
PastCasesPanel_notableFileTable_tabName=Cases with Common Notable
PastCasesPanel_onNoCrIngest_message=No results will be shown because the Central Repository module was not run.
PastCasesPanel_sameIdsTable_tabName=Past Cases with the Same Devices
RecentFilePanel_col_header_domain=Domain
RecentFilePanel_col_header_path=Path
RecentFilePanel_col_header_sender=Sender
RecentFilePanel_emailParserModuleName=Email Parser
RecentFilePanel_no_open_documents=No recently open documents found.
RecentFilesPanel_attachmentsTable_tabName=Recent Attachments
RecentFilesPanel_col_head_date=Date
RecentFilesPanel_col_header_domain=Domain
RecentFilesPanel_col_header_path=Path
RecentFilesPanel_col_header_sender=Sender
RecentFilesPanel_docsTable_tabName=Recently Opened Documents
RecentFilesPanel_downloadsTable_tabName=Recently Downloads
RecentFilesPanel_no_open_documents=No recently open documents found.
SizeRepresentationUtil_units_bytes=bytes
SizeRepresentationUtil_units_gigabytes=GB
SizeRepresentationUtil_units_kilobytes=KB
@ -116,12 +84,6 @@ SizeRepresentationUtil_units_megabytes=MB
SizeRepresentationUtil_units_petabytes=PB
SizeRepresentationUtil_units_terabytes=TB
TimelinePanel_earliestLabel_title=Earliest
TimelinePanel_getExports_activityRange=Activity Range
TimelinePanel_getExports_chartName=Last 30 Days
TimelinePanel_getExports_dateColumnHeader=Date
TimelinePanel_getExports_earliest=Earliest:
TimelinePanel_getExports_latest=Latest:
TimelinePanel_getExports_sheetName=Timeline
TimelinePanel_latestLabel_title=Latest
TimlinePanel_last30DaysChart_artifactEvts_title=Result Events
TimlinePanel_last30DaysChart_fileEvts_title=File Events

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 Basis Technology Corp.
* Copyright 2019-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -19,39 +19,23 @@
package org.sleuthkit.autopsy.datasourcesummary.ui;
import java.beans.PropertyChangeEvent;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.sleuthkit.autopsy.coreutils.Logger;
import javax.swing.table.DefaultTableModel;
import org.apache.commons.lang.StringUtils;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.ContainerSummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
import static org.sleuthkit.autopsy.datasourcesummary.ui.BaseDataSourceSummaryPanel.getFetchResult;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.ContainerSummary.ContainerDetails;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.ContainerSummary.ImageDetails;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult.ResultType;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetcher;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultCellModel;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataFetcher;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultUpdateGovernor;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport.ExcelSheetExport;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport.ExcelItemExportable;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport.KeyValueItemExportable;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport.SingleCellExportable;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport.TitledExportable;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.UpdateGovernor;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.Image;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Panel to display additional details associated with a specific DataSource
@ -61,182 +45,6 @@ import org.sleuthkit.datamodel.TskCoreException;
})
class ContainerPanel extends BaseDataSourceSummaryPanel {
/**
* View model data for data source images.
*/
private static class ImageViewModel {
private final long unallocatedSize;
private final long size;
private final long sectorSize;
private final String timeZone;
private final String imageType;
private final List<String> paths;
private final String md5Hash;
private final String sha1Hash;
private final String sha256Hash;
/**
* Main constructor.
*
* @param unallocatedSize Size in bytes of unallocated space.
* @param size Total size in bytes.
* @param sectorSize Sector size in bytes.
* @param timeZone The time zone.
* @param imageType The type of image.
* @param paths The source paths for the image.
* @param md5Hash The md5 hash or null.
* @param sha1Hash The sha1 hash or null.
* @param sha256Hash The sha256 hash or null.
*/
ImageViewModel(long unallocatedSize, long size, long sectorSize,
String timeZone, String imageType, List<String> paths, String md5Hash,
String sha1Hash, String sha256Hash) {
this.unallocatedSize = unallocatedSize;
this.size = size;
this.sectorSize = sectorSize;
this.timeZone = timeZone;
this.imageType = imageType;
this.paths = paths == null ? Collections.emptyList() : new ArrayList<>(paths);
this.md5Hash = md5Hash;
this.sha1Hash = sha1Hash;
this.sha256Hash = sha256Hash;
}
/**
* @return Size in bytes of unallocated space.
*/
long getUnallocatedSize() {
return unallocatedSize;
}
/**
* @return Total size in bytes.
*/
long getSize() {
return size;
}
/**
* @return Sector size in bytes.
*/
long getSectorSize() {
return sectorSize;
}
/**
* @return The time zone.
*/
String getTimeZone() {
return timeZone;
}
/**
* @return The type of image.
*/
String getImageType() {
return imageType;
}
/**
* @return The source paths for the image.
*/
List<String> getPaths() {
return paths;
}
/**
* @return The md5 hash or null.
*/
String getMd5Hash() {
return md5Hash;
}
/**
* @return The sha1 hash or null.
*/
String getSha1Hash() {
return sha1Hash;
}
/**
* @return The sha256 hash or null.
*/
String getSha256Hash() {
return sha256Hash;
}
}
/**
* View model for container data.
*/
private static class ContainerViewModel {
private final String displayName;
private final String originalName;
private final String deviceIdValue;
private final String acquisitionDetails;
private final ImageViewModel imageViewModel;
/**
* Main constructor.
*
* @param displayName The display name for this data source.
* @param originalName The original name for this data source.
* @param deviceIdValue The device id value for this data source.
* @param acquisitionDetails The acquisition details for this data
* source or null.
* @param imageViewModel If the data source is an image, the image view
* model for this data source or null if non-image.
*/
ContainerViewModel(String displayName, String originalName, String deviceIdValue,
String acquisitionDetails, ImageViewModel imageViewModel) {
this.displayName = displayName;
this.originalName = originalName;
this.deviceIdValue = deviceIdValue;
this.acquisitionDetails = acquisitionDetails;
this.imageViewModel = imageViewModel;
}
/**
* @return The display name for this data source.
*/
String getDisplayName() {
return displayName;
}
/**
* @return The original name for this data source.
*/
String getOriginalName() {
return originalName;
}
/**
* @return The device id value for this data source.
*/
String getDeviceId() {
return deviceIdValue;
}
/**
* @return The acquisition details for this data source or null.
*/
String getAcquisitionDetails() {
return acquisitionDetails;
}
/**
* @return If the data source is an image, the image view model for this
* data source or null if non-image.
*/
ImageViewModel getImageViewModel() {
return imageViewModel;
}
}
// set of case events for which to call update (if the name changes, that will impact data shown)
private static final Set<Case.Events> CASE_EVENT_SET = new HashSet<>(Arrays.asList(
Case.Events.DATA_SOURCE_NAME_CHANGED
@ -262,29 +70,29 @@ class ContainerPanel extends BaseDataSourceSummaryPanel {
private static final Logger logger = Logger.getLogger(ContainerPanel.class.getName());
private final List<DataFetchComponents<DataSource, ?>> dataFetchComponents;
private final DataFetcher<DataSource, ContainerViewModel> containerDataFetcher;
private final DataFetcher<DataSource, ContainerDetails> containerDataFetcher;
/**
* Creates a new form ContainerPanel.
*/
ContainerPanel() {
this(new ContainerSummary());
this(new ContainerSummaryGetter());
}
/**
* Creates new form ContainerPanel.
*/
ContainerPanel(ContainerSummary containerSummary) {
ContainerPanel(ContainerSummaryGetter containerSummary) {
super(containerSummary, CONTAINER_UPDATES);
containerDataFetcher = (dataSource) -> getContainerViewModel(containerSummary, dataSource);
containerDataFetcher = (dataSource) -> containerSummary.getContainerDetails(dataSource);
dataFetchComponents = Arrays.asList(
new DataFetchComponents<>(
containerDataFetcher,
(result) -> {
if (result != null && result.getResultType() == ResultType.SUCCESS) {
ContainerViewModel data = result.getData();
ContainerDetails data = result.getData();
updateDetailsPanelData(data);
} else {
if (result == null) {
@ -313,92 +121,12 @@ class ContainerPanel extends BaseDataSourceSummaryPanel {
fetchInformation(dataFetchComponents, dataSource);
}
/**
* A means of retrieving data that could potentially throw an exception.
*/
private interface Retriever<O> {
/**
* Retrieves data of a certain type and possibly throws an exception.
*
* @return The data type.
* @throws TskCoreException
* @throws SleuthkitCaseProviderException
* @throws SQLException
*/
O retrieve() throws TskCoreException, SleuthkitCaseProviderException, SQLException;
}
/**
* Retrieves data of a particular type and handles any exceptions that may
* be thrown by logging.
*
* @param retriever The retrieving function.
* @return The retrieved data.
*/
private static <O> O retrieve(Retriever<O> retriever) {
try {
return retriever.retrieve();
} catch (TskCoreException | SleuthkitCaseProviderException | SQLException ex) {
logger.log(Level.WARNING, "Error while retrieving data.", ex);
return null;
}
}
/**
* Generates a container view model object containing data to display about
* the data source.
*
* @param containerSummary The service providing data about the data source.
* @param ds The data source.
* @return The generated view model.
*/
private static ContainerViewModel getContainerViewModel(ContainerSummary containerSummary, DataSource ds) {
if (ds == null) {
return null;
}
return new ContainerViewModel(
ds.getName(),
ds.getName(),
ds.getDeviceId(),
retrieve(() -> ds.getAcquisitionDetails()),
ds instanceof Image ? getImageViewModel(containerSummary, (Image) ds) : null
);
}
/**
* Generates an image view model object containing data to display about the
* image.
*
* @param containerSummary The service providing data about the image.
* @param image The image.
* @return The generated view model.
*/
private static ImageViewModel getImageViewModel(ContainerSummary containerSummary, Image image) {
if (image == null) {
return null;
}
Long unallocSize = retrieve(() -> containerSummary.getSizeOfUnallocatedFiles(image));
String imageType = image.getType().getName();
Long size = image.getSize();
Long sectorSize = image.getSsize();
String timeZone = image.getTimeZone();
List<String> paths = image.getPaths() == null ? Collections.emptyList() : Arrays.asList(image.getPaths());
String md5 = retrieve(() -> image.getMd5());
String sha1 = retrieve(() -> image.getSha1());
String sha256 = retrieve(() -> image.getSha256());
return new ImageViewModel(unallocSize, size, sectorSize, timeZone, imageType, paths, md5, sha1, sha256);
}
/**
* Update the swing components with fetched data.
*
* @param viewModel The data source view model data.
*/
private void updateDetailsPanelData(ContainerViewModel viewModel) {
private void updateDetailsPanelData(ContainerDetails viewModel) {
clearTableValues();
if (viewModel == null) {
return;
@ -409,8 +137,8 @@ class ContainerPanel extends BaseDataSourceSummaryPanel {
deviceIdValue.setText(viewModel.getDeviceId());
acquisitionDetailsTextArea.setText(viewModel.getAcquisitionDetails());
if (viewModel.getImageViewModel() != null) {
setFieldsForImage(viewModel.getImageViewModel());
if (viewModel.getImageDetails() != null) {
setFieldsForImage(viewModel.getImageDetails());
} else {
setFieldsForNonImageDataSource();
}
@ -445,7 +173,7 @@ class ContainerPanel extends BaseDataSourceSummaryPanel {
*
* @param viewModel The image view model data.
*/
private void setFieldsForImage(ImageViewModel viewModel) {
private void setFieldsForImage(ImageDetails viewModel) {
unallocatedSizeValue.setText(SizeRepresentationUtil.getSizeString(viewModel.getUnallocatedSize()));
imageTypeValue.setText(viewModel.getImageType());
sizeValue.setText(SizeRepresentationUtil.getSizeString(viewModel.getSize()));
@ -480,84 +208,6 @@ class ContainerPanel extends BaseDataSourceSummaryPanel {
((DefaultTableModel) filePathsTable.getModel()).setRowCount(0);
}
/**
* Divides acquisition details into key/value pairs to be displayed in
* separate cells in an excel export.
*
* @param acquisitionDetails The acquisition details.
* @return The list of key value pairs that can be incorporated into the
* excel export.
*/
private static List<? extends ExcelItemExportable> getAcquisitionDetails(String acquisitionDetails) {
if (StringUtils.isBlank(acquisitionDetails)) {
return Collections.emptyList();
} else {
return Stream.of(acquisitionDetails.split("\\r?\\n"))
.map((line) -> (StringUtils.isBlank(line)) ? null : new SingleCellExportable(line))
.filter(item -> item != null)
.collect(Collectors.toList());
}
}
@Override
@Messages({
"ContainerPanel_export_displayName=Display Name:",
"ContainerPanel_export_originalName=Name:",
"ContainerPanel_export_deviceId=Device ID:",
"ContainerPanel_export_timeZone=Time Zone:",
"ContainerPanel_export_acquisitionDetails=Acquisition Details:",
"ContainerPanel_export_imageType=Image Type:",
"ContainerPanel_export_size=Size:",
"ContainerPanel_export_sectorSize=Sector Size:",
"ContainerPanel_export_md5=MD5:",
"ContainerPanel_export_sha1=SHA1:",
"ContainerPanel_export_sha256=SHA256:",
"ContainerPanel_export_unallocatedSize=Unallocated Space:",
"ContainerPanel_export_filePaths=File Paths:",})
protected List<ExcelSheetExport> getExports(DataSource ds) {
ContainerViewModel result = getFetchResult(containerDataFetcher, "Container sheets", ds);
if (ds == null || result == null) {
return Collections.emptyList();
}
String NA = Bundle.ContainerPanel_setFieldsForNonImageDataSource_na();
DefaultCellModel<?> NACell = new DefaultCellModel<>(NA);
ImageViewModel imageModel = result.getImageViewModel();
boolean hasImage = imageModel != null;
DefaultCellModel<?> timeZone = hasImage ? new DefaultCellModel<>(imageModel.getTimeZone()) : NACell;
DefaultCellModel<?> imageType = hasImage ? new DefaultCellModel<>(imageModel.getImageType()) : NACell;
DefaultCellModel<?> size = hasImage ? SizeRepresentationUtil.getBytesCell(imageModel.getSize()) : NACell;
DefaultCellModel<?> sectorSize = hasImage ? SizeRepresentationUtil.getBytesCell(imageModel.getSectorSize()) : NACell;
DefaultCellModel<?> md5 = hasImage ? new DefaultCellModel<>(imageModel.getMd5Hash()) : NACell;
DefaultCellModel<?> sha1 = hasImage ? new DefaultCellModel<>(imageModel.getSha1Hash()) : NACell;
DefaultCellModel<?> sha256 = hasImage ? new DefaultCellModel<>(imageModel.getSha256Hash()) : NACell;
DefaultCellModel<?> unallocatedSize = hasImage ? SizeRepresentationUtil.getBytesCell(imageModel.getUnallocatedSize()) : NACell;
List<String> paths = result.getImageViewModel() == null ? Collections.singletonList(NA) : result.getImageViewModel().getPaths();
List<SingleCellExportable> cellPaths = paths.stream()
.map(SingleCellExportable::new)
.collect(Collectors.toList());
return Arrays.asList(
new ExcelSpecialFormatExport(Bundle.ContainerPanel_tabName(), Arrays.asList(
new KeyValueItemExportable(Bundle.ContainerPanel_export_displayName(), new DefaultCellModel<>(result.getDisplayName())),
new KeyValueItemExportable(Bundle.ContainerPanel_export_originalName(), new DefaultCellModel<>(result.getOriginalName())),
new KeyValueItemExportable(Bundle.ContainerPanel_export_deviceId(), new DefaultCellModel<>(result.getDeviceId())),
new KeyValueItemExportable(Bundle.ContainerPanel_export_timeZone(), timeZone),
new TitledExportable(Bundle.ContainerPanel_export_acquisitionDetails(), getAcquisitionDetails(result.getAcquisitionDetails())),
new KeyValueItemExportable(Bundle.ContainerPanel_export_imageType(), imageType),
new KeyValueItemExportable(Bundle.ContainerPanel_export_size(), size),
new KeyValueItemExportable(Bundle.ContainerPanel_export_sectorSize(), sectorSize),
new KeyValueItemExportable(Bundle.ContainerPanel_export_md5(), md5),
new KeyValueItemExportable(Bundle.ContainerPanel_export_sha1(), sha1),
new KeyValueItemExportable(Bundle.ContainerPanel_export_sha256(), sha256),
new KeyValueItemExportable(Bundle.ContainerPanel_export_unallocatedSize(), unallocatedSize),
new TitledExportable(Bundle.ContainerPanel_export_filePaths(), cellPaths)
)));
}
/**
* 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

View File

@ -0,0 +1,140 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020-2021 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.datasourcesummary.ui;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultArtifactUpdateGovernor;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.ContainerSummary;
import org.sleuthkit.autopsy.ingest.ModuleContentEvent;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Wrapper class for converting
* org.sleuthkit.autopsy.contentutils.ContainerSummary functionality into a
* DefaultArtifactUpdateGovernor used by Container tab.
*/
public class ContainerSummaryGetter implements DefaultArtifactUpdateGovernor {
private static final Set<Integer> ARTIFACT_UPDATE_TYPE_IDS = new HashSet<>(Arrays.asList(
BlackboardArtifact.ARTIFACT_TYPE.TSK_OS_INFO.getTypeID(),
BlackboardArtifact.ARTIFACT_TYPE.TSK_DATA_SOURCE_USAGE.getTypeID()
));
private final ContainerSummary containerSummary;
/**
* Main constructor.
*/
public ContainerSummaryGetter() {
containerSummary = new ContainerSummary();
}
@Override
public boolean isRefreshRequired(ModuleContentEvent evt) {
return true;
}
@Override
public boolean isRefreshRequired(AbstractFile file) {
return true;
}
@Override
public Set<Integer> getArtifactTypeIdsForRefresh() {
return Collections.unmodifiableSet(ARTIFACT_UPDATE_TYPE_IDS);
}
/**
* Gets the size of unallocated files in a particular datasource.
*
* @param currentDataSource The data source.
*
* @return The size or null if the query could not be executed.
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
* @throws SQLException
*/
public Long getSizeOfUnallocatedFiles(DataSource currentDataSource)
throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException {
return containerSummary.getSizeOfUnallocatedFiles(currentDataSource);
}
/**
* Retrieves the concatenation of operating system attributes for a
* particular data source.
*
* @param dataSource The data source.
*
* @return The concatenated value or null if the query could not be
* executed.
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
* @throws SQLException
*/
public String getOperatingSystems(DataSource dataSource)
throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException {
return containerSummary.getOperatingSystems(dataSource);
}
/**
* Retrieves the concatenation of data source usage for a particular data
* source.
*
* @param dataSource The data source.
*
* @return The concatenated value or null if the query could not be
* executed.
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
* @throws SQLException
*/
public String getDataSourceType(DataSource dataSource)
throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException {
return containerSummary.getDataSourceType(dataSource);
}
/**
* Retrieves a container data model object containing data about the data
* source.
*
* @param dataSource The data source.
*
* @return The concatenated value or null if the query could not be
* executed.
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
* @throws SQLException
*/
public ContainerSummary.ContainerDetails getContainerDetails(DataSource dataSource)
throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException {
return containerSummary.getContainerDetails(dataSource);
}
}

View File

@ -18,6 +18,7 @@
*/
package org.sleuthkit.autopsy.datasourcesummary.ui;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.CaseDataSourcesSummary;
import java.awt.Cursor;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.RightAlignedTableCellRenderer;
import java.awt.EventQueue;
@ -42,7 +43,6 @@ import static javax.swing.SwingConstants.RIGHT;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.table.TableColumn;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.CaseDataSourcesSummary;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.IngestJobInfo;
import org.sleuthkit.datamodel.SleuthkitCase;

View File

@ -25,12 +25,9 @@ import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.IngestJobInfoPanel;
import org.sleuthkit.autopsy.datasourcesummary.ui.ExcelExportAction.ExportableTab;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport.ExcelSheetExport;
import org.sleuthkit.datamodel.DataSource;
/**
@ -46,8 +43,7 @@ import org.sleuthkit.datamodel.DataSource;
"DataSourceSummaryTabbedPane_pastCasesTab_title=Past Cases",
"DataSourceSummaryTabbedPane_analysisTab_title=Analysis",
"DataSourceSummaryTabbedPane_geolocationTab_title=Geolocation",
"DataSourceSummaryTabbedPane_timelineTab_title=Timeline",
"DataSourceSummaryTabbedPane_exportTab_title=Export"
"DataSourceSummaryTabbedPane_timelineTab_title=Timeline"
})
public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
@ -55,12 +51,11 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
* Records of tab information (i.e. title, component, function to call on
* new data source).
*/
private class DataSourceTab implements ExportableTab {
private class DataSourceTab {
private final String tabTitle;
private final Component component;
private final Consumer<DataSource> onDataSource;
private final Function<DataSource, List<ExcelSheetExport>> excelExporter;
private final Runnable onClose;
private final Runnable onInit;
@ -71,7 +66,7 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
* @param panel The component to be displayed in the tab.
*/
DataSourceTab(String tabTitle, BaseDataSourceSummaryPanel panel) {
this(tabTitle, panel, panel::setDataSource, panel::getExports, panel::close, panel::init);
this(tabTitle, panel, panel::setDataSource, panel::close, panel::init);
panel.setParentCloseListener(() -> notifyParentClose());
}
@ -90,12 +85,10 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
* added to the tabbed pane.
*/
DataSourceTab(String tabTitle, Component component, Consumer<DataSource> onDataSource,
Function<DataSource, List<ExcelSheetExport>> excelExporter, Runnable onClose,
Runnable onInit) {
Runnable onClose, Runnable onInit) {
this.tabTitle = tabTitle;
this.component = component;
this.onDataSource = onDataSource;
this.excelExporter = excelExporter;
this.onClose = onClose;
this.onInit = onInit;
}
@ -103,7 +96,6 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
/**
* @return The title for the tab.
*/
@Override
public String getTabTitle() {
return tabTitle;
}
@ -122,11 +114,6 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
return onDataSource;
}
@Override
public List<ExcelSheetExport> getExcelExports(DataSource dataSource) {
return excelExporter == null ? null : excelExporter.apply(dataSource);
}
/**
* @return The action for closing resources in the tab.
*/
@ -152,9 +139,6 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
private Runnable notifyParentClose = null;
private final IngestJobInfoPanel ingestHistoryPanel = new IngestJobInfoPanel();
// create an export panel whose button triggers the export to XLSX action
private final ExportPanel exportPanel = new ExportPanel();
private final List<DataSourceTab> tabs = Arrays.asList(
new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_typesTab_title(), new TypesPanel()),
new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_userActivityTab_title(), new UserActivityPanel()),
@ -168,22 +152,11 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
Bundle.DataSourceSummaryTabbedPane_ingestHistoryTab_title(),
ingestHistoryPanel,
ingestHistoryPanel::setDataSource,
IngestJobExcelExport::getExports,
null,
null),
new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_detailsTab_title(), new ContainerPanel()),
new DataSourceTab(
Bundle.DataSourceSummaryTabbedPane_exportTab_title(),
exportPanel,
null,
null,
null,
null)
new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_detailsTab_title(), new ContainerPanel())
);
// the action that does the export
private final ExcelExportAction exportAction = new ExcelExportAction(tabs);
private DataSource dataSource = null;
private CardLayout cardLayout;
@ -243,9 +216,6 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
// set this to no datasource initially
cardLayout.show(this, NO_DATASOURCE_PANE);
// set action for when user requests xlsx export
exportPanel.setXlsxExportAction(() -> exportAction.accept(getDataSource()));
}
/**

View File

@ -1,301 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2021 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.datasourcesummary.ui;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
import java.util.logging.Level;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.FileUtil;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport.ExcelExportException;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport.ExcelSheetExport;
import org.sleuthkit.autopsy.progress.ModalDialogProgressIndicator;
import org.sleuthkit.autopsy.progress.ProgressIndicator;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Action that exports tab data to an excel workbook.
*/
@Messages({
"ExcelExportAction_moduleName=Data Source Summary",})
class ExcelExportAction implements Consumer<DataSource> {
private static final Logger logger = Logger.getLogger(ExcelExportAction.class.getName());
/**
* A tab that can be exported.
*/
interface ExportableTab {
/**
* Returns the name of the tab.
*
* @return The tab name.
*/
String getTabTitle();
/**
* Given the data source, provides the excel exports for this tab.
*
* @param dataSource The data source.
* @return The excel exports or null.
*/
List<ExcelSheetExport> getExcelExports(DataSource dataSource);
}
private final ExcelExport excelExport = ExcelExport.getInstance();
private final List<? extends ExportableTab> tabExports;
/**
* Main constructor.
*
* @param tabExports The different tabs that may have excel exports.
*/
ExcelExportAction(List<? extends ExportableTab> tabExports) {
this.tabExports = Collections.unmodifiableList(new ArrayList<>(tabExports));
}
/**
* Accepts the data source for which this export pertains, prompts user for
* output location, and exports the data.
*
* @param ds The data source.
*/
@Override
public void accept(DataSource ds) {
if (ds == null) {
return;
}
File outputLoc = getXLSXPath(ds.getName());
if (outputLoc == null) {
return;
}
runXLSXExport(ds, outputLoc);
}
/**
* Generates an xlsx path for the data source summary export.
*
* @param dataSourceName The name of the data source.
* @return The file to which the excel document should be written or null if
* file already exists or cancellation.
*/
@NbBundle.Messages({
"ExcelExportAction_getXLSXPath_directory=DataSourceSummary",})
private File getXLSXPath(String dataSourceName) {
// set initial path to reports directory with filename that is
// a combination of the data source name and time stamp
DateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy-HH-mm-ss");
String fileName = String.format("%s-%s.xlsx", dataSourceName == null ? "" : FileUtil.escapeFileName(dataSourceName), dateFormat.format(new Date()));
try {
String reportsDir = Case.getCurrentCaseThrows().getReportDirectory();
File reportsDirFile = Paths.get(reportsDir, Bundle.ExcelExportAction_getXLSXPath_directory()).toFile();
if (!reportsDirFile.exists()) {
reportsDirFile.mkdirs();
}
return Paths.get(reportsDirFile.getAbsolutePath(), fileName).toFile();
} catch (NoCurrentCaseException ex) {
logger.log(Level.WARNING, "Unable to find reports directory.", ex);
}
return null;
}
/**
* An action listener that handles cancellation of the export process.
*/
private class CancelExportListener implements ActionListener {
private SwingWorker<Boolean, Void> worker = null;
@Override
public void actionPerformed(ActionEvent e) {
if (worker != null && !worker.isCancelled() && !worker.isDone()) {
worker.cancel(true);
}
}
/**
* Returns the swing worker that could be cancelled.
*
* @return The swing worker that could be cancelled.
*/
SwingWorker<Boolean, Void> getWorker() {
return worker;
}
/**
* Sets the swing worker that could be cancelled.
*
* @param worker The swing worker that could be cancelled.
*/
void setWorker(SwingWorker<Boolean, Void> worker) {
this.worker = worker;
}
}
/**
* Handles managing the gui and exporting data from the tabs into an excel
* document.
*
* @param dataSource The data source.
* @param path The output path.
*/
@NbBundle.Messages({
"# {0} - dataSource",
"ExcelExportAction_runXLSXExport_progressTitle=Exporting {0} to XLSX",
"ExcelExportAction_runXLSXExport_progressCancelTitle=Cancel",
"ExcelExportAction_runXLSXExport_progressCancelActionTitle=Cancelling...",
"ExcelExportAction_runXLSXExport_errorTitle=Error While Exporting",
"ExcelExportAction_runXLSXExport_errorMessage=There was an error while exporting.",
})
private void runXLSXExport(DataSource dataSource, File path) {
CancelExportListener cancelButtonListener = new CancelExportListener();
ProgressIndicator progressIndicator = new ModalDialogProgressIndicator(
WindowManager.getDefault().getMainWindow(),
Bundle.ExcelExportAction_runXLSXExport_progressTitle(dataSource.getName()),
new String[]{Bundle.ExcelExportAction_runXLSXExport_progressCancelTitle()},
Bundle.ExcelExportAction_runXLSXExport_progressCancelTitle(),
cancelButtonListener
);
SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() {
@Override
protected Boolean doInBackground() throws Exception {
exportToXLSX(progressIndicator, dataSource, path);
return true;
}
@Override
protected void done() {
try {
get();
} catch (ExecutionException ex) {
logger.log(Level.WARNING, "Error while trying to export data source summary to xlsx.", ex);
JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
Bundle.ExcelExportAction_runXLSXExport_errorMessage(),
Bundle.ExcelExportAction_runXLSXExport_errorTitle(),
JOptionPane.ERROR_MESSAGE);
} catch (InterruptedException | CancellationException ex) {
// no op on cancellation
} finally {
progressIndicator.finish();
}
}
};
cancelButtonListener.setWorker(worker);
worker.execute();
}
/**
* Action that handles updating progress and exporting data from the tabs.
*
* @param progressIndicator The progress indicator.
* @param dataSource The data source to be exported.
* @param path The path of the excel export.
* @throws InterruptedException
* @throws IOException
* @throws ExcelExportException
*/
@NbBundle.Messages({
"ExcelExportAction_exportToXLSX_beginExport=Beginning Export...",
"# {0} - tabName",
"ExcelExportAction_exportToXLSX_gatheringTabData=Fetching Data for {0} Tab...",
"ExcelExportAction_exportToXLSX_writingToFile=Writing to File...",})
private void exportToXLSX(ProgressIndicator progressIndicator, DataSource dataSource, File path)
throws InterruptedException, IOException, ExcelExport.ExcelExportException {
int exportWeight = 3;
int totalWeight = tabExports.size() + exportWeight;
progressIndicator.start(Bundle.ExcelExportAction_exportToXLSX_beginExport(), totalWeight);
List<ExcelExport.ExcelSheetExport> sheetExports = new ArrayList<>();
for (int i = 0; i < tabExports.size(); i++) {
if (Thread.interrupted()) {
throw new InterruptedException("Export has been cancelled.");
}
ExportableTab tab = tabExports.get(i);
progressIndicator.progress(Bundle.ExcelExportAction_exportToXLSX_gatheringTabData(tab == null ? "" : tab.getTabTitle()), i);
List<ExcelExport.ExcelSheetExport> exports = tab.getExcelExports(dataSource);
if (exports != null) {
sheetExports.addAll(exports);
}
}
if (Thread.interrupted()) {
throw new InterruptedException("Export has been cancelled.");
}
progressIndicator.progress(Bundle.ExcelExportAction_exportToXLSX_writingToFile(), tabExports.size());
excelExport.writeExcel(sheetExports, path);
progressIndicator.finish();
try {
// add to reports
Case curCase = Case.getCurrentCaseThrows();
curCase.addReport(path.getParent(),
Bundle.ExcelExportAction_moduleName(),
path.getName(),
dataSource);
// and show finished dialog
SwingUtilities.invokeLater(() -> {
ExcelExportDialog dialog = new ExcelExportDialog(WindowManager.getDefault().getMainWindow(), path);
dialog.setResizable(false);
dialog.setLocationRelativeTo(WindowManager.getDefault().getMainWindow());
dialog.setVisible(true);
dialog.toFront();
});
} catch (NoCurrentCaseException | TskCoreException ex) {
logger.log(Level.WARNING, "There was an error attaching report to case.", ex);
}
}
}

View File

@ -1,114 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JDialogFormInfo">
<Properties>
<Property name="defaultCloseOperation" type="int" value="2"/>
</Properties>
<SyntheticProperties>
<SyntheticProperty name="formSizePolicy" type="int" value="1"/>
<SyntheticProperty name="generateCenter" type="boolean" value="false"/>
</SyntheticProperties>
<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="1" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="1" attributes="0">
<Component id="linkTextScrollPane" max="32767" attributes="0"/>
<Group type="102" alignment="1" attributes="0">
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
<Component id="okButton" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<Component id="titleLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="116" max="32767" attributes="0"/>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="titleLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="linkTextScrollPane" min="-2" pref="39" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="okButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="titleLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="ExcelExportDialog.titleLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
</Component>
<Component class="javax.swing.JButton" name="okButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="ExcelExportDialog.okButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="okButtonActionPerformed"/>
</Events>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
</Component>
<Container class="javax.swing.JScrollPane" name="linkTextScrollPane">
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JTextArea" name="linkText">
<Properties>
<Property name="editable" type="boolean" value="false"/>
<Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color type="null"/>
</Property>
<Property name="columns" type="int" value="20"/>
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="java.awt.Color.BLUE" type="code"/>
</Property>
<Property name="lineWrap" type="boolean" value="true"/>
<Property name="rows" type="int" value="1"/>
<Property name="wrapStyleWord" type="boolean" value="true"/>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="null"/>
</Property>
<Property name="opaque" type="boolean" value="false"/>
</Properties>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Form>

View File

@ -1,143 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2021 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.datasourcesummary.ui;
import java.awt.Cursor;
import java.awt.Desktop;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import javax.swing.SwingUtilities;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.coreutils.Logger;
/**
* Dialog showing where the data source summary excel export can be located.
*/
@Messages({
"ExcelExportDialog_title=Data Source Summary Exported"
})
public class ExcelExportDialog extends javax.swing.JDialog {
private static final Logger logger = Logger.getLogger(ExcelExportDialog.class.getName());
/**
* Creates new form ExcelExportDialog
*/
public ExcelExportDialog(java.awt.Frame parent, File filePath) {
super(parent, true);
initComponents();
setTitle(Bundle.ExcelExportDialog_title());
this.linkText.setText(filePath.getAbsolutePath());
this.linkText.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
SwingUtilities.invokeLater(() -> {
try {
Desktop.getDesktop().open(filePath);
} catch (IOException ex) {
logger.log(Level.WARNING, "Unable to open: " + filePath.getAbsolutePath(), ex);
}
});
}
});
this.linkText.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
javax.swing.JLabel titleLabel = new javax.swing.JLabel();
javax.swing.JButton okButton = new javax.swing.JButton();
javax.swing.JScrollPane linkTextScrollPane = new javax.swing.JScrollPane();
linkText = new javax.swing.JTextArea();
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
org.openide.awt.Mnemonics.setLocalizedText(titleLabel, org.openide.util.NbBundle.getMessage(ExcelExportDialog.class, "ExcelExportDialog.titleLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(okButton, org.openide.util.NbBundle.getMessage(ExcelExportDialog.class, "ExcelExportDialog.okButton.text")); // NOI18N
okButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
okButtonActionPerformed(evt);
}
});
linkText.setEditable(false);
linkText.setBackground(null);
linkText.setColumns(20);
linkText.setForeground(java.awt.Color.BLUE);
linkText.setLineWrap(true);
linkText.setRows(1);
linkText.setWrapStyleWord(true);
linkText.setBorder(null);
linkText.setOpaque(false);
linkTextScrollPane.setViewportView(linkText);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(linkTextScrollPane)
.addGroup(layout.createSequentialGroup()
.addGap(0, 0, Short.MAX_VALUE)
.addComponent(okButton))
.addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
.addComponent(titleLabel)
.addGap(0, 116, Short.MAX_VALUE)))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(titleLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(linkTextScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 39, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(okButton)
.addContainerGap())
);
pack();
}// </editor-fold>//GEN-END:initComponents
private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed
dispose();
}//GEN-LAST:event_okButtonActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JTextArea linkText;
// End of variables declaration//GEN-END:variables
}

View File

@ -1,68 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="xlsxExportMessage" min="-2" max="-2" attributes="0"/>
<Component id="xlsxExportButton" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace pref="62" 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"/>
<Component id="xlsxExportMessage" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="xlsxExportButton" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="250" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JButton" name="xlsxExportButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="ExportPanel.xlsxExportButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="xlsxExportButtonActionPerformed"/>
</Events>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
</Component>
<Component class="javax.swing.JLabel" name="xlsxExportMessage">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties" key="ExportPanel.xlsxExportMessage.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
</Component>
</SubComponents>
</Form>

View File

@ -1,105 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2021 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.datasourcesummary.ui;
/**
* The panel that provides options for exporting data source summary data.
*/
public class ExportPanel extends javax.swing.JPanel {
private Runnable xlsxExportAction;
/**
* Creates new form ExportPanel
*/
public ExportPanel() {
initComponents();
}
/**
* Returns the action that handles exporting to excel.
*
* @return The action that handles exporting to excel.
*/
public Runnable getXlsxExportAction() {
return xlsxExportAction;
}
/**
* Sets the action that handles exporting to excel.
*
* @param onXlsxExport The action that handles exporting to excel.
*/
public void setXlsxExportAction(Runnable onXlsxExport) {
this.xlsxExportAction = onXlsxExport;
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
javax.swing.JButton xlsxExportButton = new javax.swing.JButton();
javax.swing.JLabel xlsxExportMessage = new javax.swing.JLabel();
org.openide.awt.Mnemonics.setLocalizedText(xlsxExportButton, org.openide.util.NbBundle.getMessage(ExportPanel.class, "ExportPanel.xlsxExportButton.text")); // NOI18N
xlsxExportButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
xlsxExportButtonActionPerformed(evt);
}
});
org.openide.awt.Mnemonics.setLocalizedText(xlsxExportMessage, org.openide.util.NbBundle.getMessage(ExportPanel.class, "ExportPanel.xlsxExportMessage.text")); // NOI18N
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(xlsxExportMessage)
.addComponent(xlsxExportButton))
.addContainerGap(62, Short.MAX_VALUE))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(xlsxExportMessage)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(xlsxExportButton)
.addContainerGap(250, Short.MAX_VALUE))
);
}// </editor-fold>//GEN-END:initComponents
private void xlsxExportButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_xlsxExportButtonActionPerformed
if (this.xlsxExportAction != null) {
xlsxExportAction.run();
}
}//GEN-LAST:event_xlsxExportButtonActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
// End of variables declaration//GEN-END:variables
}

View File

@ -34,7 +34,6 @@ import org.openide.util.NbBundle.Messages;
import org.openide.util.actions.CallableSystemAction;
import org.openide.windows.TopComponent;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.GeolocationSummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.GeolocationSummary.CityCountsList;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.GeolocationSummary.CityData;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.GeolocationSummary.CityRecordCount;
@ -43,9 +42,8 @@ import org.sleuthkit.autopsy.datasourcesummary.uiutils.ColumnModel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetcher;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataFetcher;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultCellModel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel;
import org.sleuthkit.autopsy.geolocation.GeoFilter;
@ -79,9 +77,9 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel {
* Main constructor.
*
* @param mostRecentData The data to be displayed in the most recent
* table.
* table.
* @param mostCommonData The data to be displayed in the most common
* table.
* table.
*/
GeolocationViewModel(List<Pair<String, Integer>> mostRecentData, List<Pair<String, Integer>> mostCommonData) {
this.mostRecentData = mostRecentData;
@ -147,7 +145,7 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel {
private final IngestRunningLabel ingestRunningLabel = new IngestRunningLabel();
private final GeolocationSummary whereUsedData;
private final GeolocationSummaryGetter whereUsedData;
private final DataFetcher<DataSource, GeolocationViewModel> geolocationFetcher;
@ -155,15 +153,15 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel {
* Main constructor.
*/
public GeolocationPanel() {
this(new GeolocationSummary());
this(new GeolocationSummaryGetter());
}
/**
* Main constructor.
*
* @param whereUsedData The GeolocationSummary instance to use.
* @param whereUsedData The GeolocationSummaryGetter instance to use.
*/
public GeolocationPanel(GeolocationSummary whereUsedData) {
public GeolocationPanel(GeolocationSummaryGetter whereUsedData) {
super(whereUsedData);
this.whereUsedData = whereUsedData;
@ -183,7 +181,7 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel {
* Means of rendering data to be shown in the tables.
*
* @param result The result of fetching data for a data source and
* processing into view model data.
* processing into view model data.
*/
private void handleData(DataFetchResult<GeolocationViewModel> result) {
showCityContent(DataFetchResult.getSubResult(result, (dr) -> dr.getMostCommonData()), mostCommonTable, commonViewInGeolocationBtn);
@ -194,6 +192,7 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel {
* Retrieves the city name to display from the record.
*
* @param record The record for the city to display.
*
* @return The display name (city, country).
*/
private static String getCityName(CityRecord record) {
@ -221,6 +220,7 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel {
* formats the city name).
*
* @param cityCount The CityRecordCount representing a row.
*
* @return The city/count pair to be displayed as a row.
*/
private Pair<String, Integer> formatRecord(CityRecordCount cityCount) {
@ -239,7 +239,8 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel {
* 'unknown').
*
* @param countsList The CityCountsList object representing the data to be
* displayed in the table.
* displayed in the table.
*
* @return The list of city/count tuples to be displayed as a row.
*/
private List<Pair<String, Integer>> formatList(CityCountsList countsList) {
@ -263,10 +264,11 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel {
}
/**
* Converts CityData from GeolocationSummary into data that can be directly
* put into table in this panel.
* Converts CityData from GeolocationSummaryGetter into data that can be
* directly put into table in this panel.
*
* @param cityData The city data.
*
* @return The view model data.
*/
private GeolocationViewModel convertToViewModel(CityData cityData) {
@ -280,8 +282,8 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel {
/**
* Shows data in a particular table.
*
* @param result The result to be displayed in the table.
* @param table The table where the data will be displayed.
* @param result The result to be displayed in the table.
* @param table The table where the data will be displayed.
* @param goToGeolocation The corresponding geolocation navigation button.
*/
private void showCityContent(DataFetchResult<List<Pair<String, Integer>>> result, JTablePanel<Pair<String, Integer>> table, JButton goToGeolocation) {
@ -296,9 +298,9 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel {
* Action to open the geolocation window.
*
* @param dataSource The data source for which the window should filter.
* @param daysLimit The limit for how recently the waypoints should be (for
* most recent table) or null for most recent filter to not be set (for most
* common table).
* @param daysLimit The limit for how recently the waypoints should be (for
* most recent table) or null for most recent filter to
* not be set (for most common table).
*/
private void openGeolocationWindow(DataSource dataSource, Integer daysLimit) {
// notify dialog (if in dialog) should close.
@ -349,19 +351,6 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel {
onNewDataSource(dataFetchComponents, tables, dataSource);
}
@Override
List<ExcelExport.ExcelSheetExport> getExports(DataSource dataSource) {
GeolocationViewModel model = getFetchResult(geolocationFetcher, "Geolocation sheets", dataSource);
if (model == null) {
return Collections.emptyList();
}
return Arrays.asList(
getTableExport(DEFAULT_TEMPLATE, Bundle.GeolocationPanel_mostRecent_tabName(), model.getMostRecentData()),
getTableExport(DEFAULT_TEMPLATE, Bundle.GeolocationPanel_mostCommon_tabName(), model.getMostCommonData())
);
}
@Override
public void close() {
ingestRunningLabel.unregister();

View File

@ -0,0 +1,78 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020-2021 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.datasourcesummary.ui;
import java.io.IOException;
import java.util.List;
import java.util.Set;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.GeolocationSummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.GeolocationSummary.CityData;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultArtifactUpdateGovernor;
import org.sleuthkit.autopsy.geolocation.datamodel.GeoLocationDataException;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import org.sleuthkit.datamodel.DataSource;
/**
* Wrapper class for converting
* org.sleuthkit.autopsy.contentutils.GeolocationSummary functionality into a
* DefaultArtifactUpdateGovernor used by GeolocationPanel tab.
*/
public class GeolocationSummaryGetter implements DefaultArtifactUpdateGovernor {
private final GeolocationSummary geoSummary;
/**
* Default constructor.
*/
public GeolocationSummaryGetter() {
geoSummary = new GeolocationSummary();
}
/**
* @return Returns all the geolocation artifact types.
*/
public List<ARTIFACT_TYPE> getGeoTypes() {
return GeolocationSummary.getGeoTypes();
}
@Override
public Set<Integer> getArtifactTypeIdsForRefresh() {
return GeolocationSummary.getArtifactTypeIdsForRefresh();
}
/**
* Get this list of hits per city where the list is sorted descending by
* number of found hits (i.e. most hits is first index).
*
* @param dataSource The data source.
* @param daysCount Number of days to go back.
* @param maxCount Maximum number of results.
*
* @return The sorted list.
*
* @throws SleuthkitCaseProviderException
* @throws GeoLocationDataException
* @throws InterruptedException
*/
public CityData getCityCounts(DataSource dataSource, int daysCount, int maxCount)
throws SleuthkitCaseProviderException, GeoLocationDataException, InterruptedException, IOException {
return geoSummary.getCityCounts(dataSource, daysCount, maxCount);
}
}

View File

@ -0,0 +1,150 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020-2021 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.datasourcesummary.ui;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultUpdateGovernor;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.MimeTypeSummary;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.ModuleContentEvent;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Wrapper class for converting org.sleuthkit.autopsy.contentutils.TypesSummary
* functionality into a DefaultArtifactUpdateGovernor used by TypesPanel tab.
*/
public class MimeTypeSummaryGetter implements DefaultUpdateGovernor {
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS = new HashSet<>(
Arrays.asList(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED));
private final MimeTypeSummary mimeTypeSummary;
/**
* Main constructor.
*/
public MimeTypeSummaryGetter() {
mimeTypeSummary = new MimeTypeSummary();
}
@Override
public boolean isRefreshRequired(ModuleContentEvent evt) {
return true;
}
@Override
public boolean isRefreshRequired(AbstractFile file) {
return true;
}
@Override
public boolean isRefreshRequired(IngestManager.IngestJobEvent evt) {
return (evt != null && INGEST_JOB_EVENTS.contains(evt));
}
@Override
public Set<IngestManager.IngestJobEvent> getIngestJobEventUpdates() {
return Collections.unmodifiableSet(INGEST_JOB_EVENTS);
}
/**
* Get the number of files in the case database for the current data source
* which have the specified mimetypes.
*
* @param currentDataSource the data source which we are finding a file
* count
*
* @param setOfMimeTypes the set of mime types which we are finding the
* number of occurences of
*
* @return a Long value which represents the number of occurrences of the
* specified mime types in the current case for the specified data
* source, null if no count was retrieved
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
* @throws SQLException
*/
public Long getCountOfFilesForMimeTypes(DataSource currentDataSource, Set<String> setOfMimeTypes)
throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException {
return mimeTypeSummary.getCountOfFilesForMimeTypes(currentDataSource, setOfMimeTypes);
}
/**
* Get the number of files in the case database for the current data source
* which do not have the specified mimetypes.
*
* @param currentDataSource the data source which we are finding a file
* count
*
* @param setOfMimeTypes the set of mime types that should be excluded.
*
* @return a Long value which represents the number of files that do not
* have the specific mime type, but do have a mime type.
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
* @throws SQLException
*/
public Long getCountOfFilesNotInMimeTypes(DataSource currentDataSource, Set<String> setOfMimeTypes)
throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException {
return mimeTypeSummary.getCountOfFilesNotInMimeTypes(currentDataSource, setOfMimeTypes);
}
/**
* Get a count of all regular files in a datasource.
*
* @param dataSource The datasource.
*
* @return The count of regular files.
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
* @throws SQLException
*/
public Long getCountOfAllRegularFiles(DataSource dataSource)
throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException {
return mimeTypeSummary.getCountOfAllRegularFiles(dataSource);
}
/**
* Gets the number of files in the data source with no assigned mime type.
*
* @param currentDataSource The data source.
*
* @return The number of files with no mime type or null if there is an
* issue searching the data source.
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
* @throws SQLException
*/
public Long getCountOfFilesWithNoMimeType(DataSource currentDataSource)
throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException {
return mimeTypeSummary.getCountOfFilesWithNoMimeType(currentDataSource);
}
}

View File

@ -19,21 +19,16 @@
package org.sleuthkit.autopsy.datasourcesummary.ui;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.apache.commons.lang3.tuple.Pair;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.PastCasesSummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.PastCasesSummary.PastCasesResult;
import static org.sleuthkit.autopsy.datasourcesummary.ui.BaseDataSourceSummaryPanel.getFetchResult;
import static org.sleuthkit.autopsy.datasourcesummary.ui.BaseDataSourceSummaryPanel.getTableExport;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ColumnModel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetcher;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataFetcher;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultCellModel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel;
import org.sleuthkit.datamodel.DataSource;
@ -84,19 +79,19 @@ public class PastCasesPanel extends BaseDataSourceSummaryPanel {
private final IngestRunningLabel ingestRunningLabel = new IngestRunningLabel();
private final DataFetcher<DataSource, PastCasesResult> pastCasesFetcher;
public PastCasesPanel() {
this(new PastCasesSummary());
this(new PastCasesSummaryGetter());
}
/**
* Creates new form PastCasesPanel
*/
public PastCasesPanel(PastCasesSummary pastCaseData) {
public PastCasesPanel(PastCasesSummaryGetter pastCaseData) {
super(pastCaseData);
this.pastCasesFetcher = (dataSource) -> pastCaseData.getPastCasesData(dataSource);
// set up data acquisition methods
dataFetchComponents = Arrays.asList(
new DataFetchWorker.DataFetchComponents<>(
@ -128,19 +123,6 @@ public class PastCasesPanel extends BaseDataSourceSummaryPanel {
onNewDataSource(dataFetchComponents, tables, dataSource);
}
@Override
List<ExcelExport.ExcelSheetExport> getExports(DataSource dataSource) {
PastCasesResult result = getFetchResult(pastCasesFetcher, "Past cases sheets", dataSource);
if (result == null) {
return Collections.emptyList();
}
return Arrays.asList(
getTableExport(DEFAULT_TEMPLATE, Bundle.PastCasesPanel_notableFileTable_tabName(), result.getTaggedNotable()),
getTableExport(DEFAULT_TEMPLATE, Bundle.PastCasesPanel_sameIdsTable_tabName(), result.getSameIdsResults())
);
}
@Override
public void close() {
ingestRunningLabel.unregister();

View File

@ -0,0 +1,71 @@
/*
* 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.datasourcesummary.ui;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultArtifactUpdateGovernor;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.PastCasesSummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.PastCasesSummary.PastCasesResult;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Wrapper class for converting
* org.sleuthkit.autopsy.contentutils.PastCasesSummary functionality into a
* DefaultArtifactUpdateGovernor used by PastCases tab.
*/
public class PastCasesSummaryGetter implements DefaultArtifactUpdateGovernor {
private static final Set<Integer> ARTIFACT_UPDATE_TYPE_IDS = new HashSet<>(Arrays.asList(
ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID(),
ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()
));
private final PastCasesSummary pastSummary;
public PastCasesSummaryGetter() {
pastSummary = new PastCasesSummary();
}
@Override
public Set<Integer> getArtifactTypeIdsForRefresh() {
return Collections.unmodifiableSet(ARTIFACT_UPDATE_TYPE_IDS);
}
/**
* Returns the past cases data to be shown in the past cases tab.
*
* @param dataSource The data source.
*
* @return The retrieved data or null if null dataSource.
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
*/
public PastCasesResult getPastCasesData(DataSource dataSource)
throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException {
return pastSummary.getPastCasesData(dataSource);
}
}

View File

@ -0,0 +1,116 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020-2021 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.datasourcesummary.ui;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultArtifactUpdateGovernor;
import java.util.List;
import java.util.Set;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary.RecentAttachmentDetails;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary.RecentDownloadDetails;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary.RecentFileDetails;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
import org.sleuthkit.datamodel.BlackboardArtifact;
/**
* Wrapper class for converting
* org.sleuthkit.autopsy.contentutils.RecentFilesSummary functionality into a
* DefaultArtifactUpdateGovernor used by Recent Files Data Summary tab.
*/
public class RecentFilesGetter implements DefaultArtifactUpdateGovernor {
private static final Set<Integer> ARTIFACT_UPDATE_TYPE_IDS = new HashSet<>(Arrays.asList(
BlackboardArtifact.ARTIFACT_TYPE.TSK_RECENT_OBJECT.getTypeID(),
BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID(),
BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT.getTypeID(),
BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID(),
BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE.getTypeID()
));
private final RecentFilesSummary recentSummary;
/**
* Default constructor.
*/
public RecentFilesGetter() {
recentSummary = new RecentFilesSummary();
}
@Override
public Set<Integer> getArtifactTypeIdsForRefresh() {
return Collections.unmodifiableSet(ARTIFACT_UPDATE_TYPE_IDS);
}
/**
* Return a list of the most recently opened documents based on the
* TSK_RECENT_OBJECT artifact.
*
* @param dataSource The data source to query.
* @param maxCount The maximum number of results to return, pass 0 to get
* a list of all results.
*
* @return A list RecentFileDetails representing the most recently opened
* documents or an empty list if none were found.
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
*/
public List<RecentFileDetails> getRecentlyOpenedDocuments(DataSource dataSource, int maxCount) throws SleuthkitCaseProviderException, TskCoreException {
return recentSummary.getRecentlyOpenedDocuments(dataSource, maxCount);
}
/**
* Return a list of the most recent downloads based on the value of the the
* artifact TSK_DATETIME_ACCESSED attribute.
*
* @param dataSource Data source to query.
* @param maxCount Maximum number of results to return, passing 0 will
* return all results.
*
* @return A list of RecentFileDetails objects or empty list if none were
* found.
*
* @throws TskCoreException
* @throws SleuthkitCaseProviderException
*/
public List<RecentDownloadDetails> getRecentDownloads(DataSource dataSource, int maxCount) throws TskCoreException, SleuthkitCaseProviderException {
return recentSummary.getRecentDownloads(dataSource, maxCount);
}
/**
* Returns a list of the most recent message attachments.
*
* @param dataSource Data source to query.
* @param maxCount Maximum number of results to return, passing 0 will
* return all results.
*
* @return A list of RecentFileDetails of the most recent attachments.
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
*/
public List<RecentAttachmentDetails> getRecentAttachments(DataSource dataSource, int maxCount) throws SleuthkitCaseProviderException, TskCoreException {
return recentSummary.getRecentAttachments(dataSource, maxCount);
}
}

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -27,20 +27,15 @@ import java.util.List;
import java.util.Locale;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary.RecentAttachmentDetails;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary.RecentDownloadDetails;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary.RecentFileDetails;
import static org.sleuthkit.autopsy.datasourcesummary.ui.BaseDataSourceSummaryPanel.getTableExport;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ColumnModel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetcher;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataFetcher;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultCellModel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.GuiCellModel.MenuItem;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel;
@ -70,7 +65,7 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
private final DataFetcher<DataSource, List<RecentAttachmentDetails>> attachmentsFetcher;
private final List<ColumnModel<RecentFileDetails, DefaultCellModel<?>>> docsTemplate = Arrays.asList(
new ColumnModel<>(Bundle.RecentFilePanel_col_header_path(),
new ColumnModel<>(Bundle.RecentFilesPanel_col_header_path(),
(prog) -> {
return new DefaultCellModel<>(prog.getPath())
.setPopupMenuRetriever(getPopupFunct(prog));
@ -80,12 +75,12 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
80));
private final List<ColumnModel<RecentDownloadDetails, DefaultCellModel<?>>> downloadsTemplate = Arrays.asList(
new ColumnModel<>(Bundle.RecentFilePanel_col_header_domain(),
new ColumnModel<>(Bundle.RecentFilesPanel_col_header_domain(),
(prog) -> {
return new DefaultCellModel<>(prog.getWebDomain())
.setPopupMenuRetriever(getPopupFunct(prog));
}, 100),
new ColumnModel<>(Bundle.RecentFilePanel_col_header_path(),
new ColumnModel<>(Bundle.RecentFilesPanel_col_header_path(),
(prog) -> {
return new DefaultCellModel<>(prog.getPath())
.setPopupMenuRetriever(getPopupFunct(prog));
@ -95,7 +90,7 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
80));
private final List<ColumnModel<RecentAttachmentDetails, DefaultCellModel<?>>> attachmentsTemplate = Arrays.asList(
new ColumnModel<>(Bundle.RecentFilePanel_col_header_path(),
new ColumnModel<>(Bundle.RecentFilesPanel_col_header_path(),
(prog) -> {
return new DefaultCellModel<>(prog.getPath())
.setPopupMenuRetriever(getPopupFunct(prog));
@ -103,7 +98,7 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
new ColumnModel<>(Bundle.RecentFilesPanel_col_head_date(),
getDateFunct(),
80),
new ColumnModel<>(Bundle.RecentFilePanel_col_header_sender(),
new ColumnModel<>(Bundle.RecentFilesPanel_col_header_sender(),
(prog) -> {
return new DefaultCellModel<>(prog.getSender())
.setPopupMenuRetriever(getPopupFunct(prog));
@ -114,19 +109,18 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
*/
@Messages({
"RecentFilesPanel_col_head_date=Date",
"RecentFilePanel_col_header_domain=Domain",
"RecentFilePanel_col_header_path=Path",
"RecentFilePanel_col_header_sender=Sender",
"RecentFilePanel_emailParserModuleName=Email Parser"
"RecentFilesPanel_col_header_domain=Domain",
"RecentFilesPanel_col_header_path=Path",
"RecentFilesPanel_col_header_sender=Sender"
})
public RecentFilesPanel() {
this(new RecentFilesSummary());
this(new RecentFilesGetter());
}
/**
* Creates new form RecentFilesPanel
*/
public RecentFilesPanel(RecentFilesSummary dataHandler) {
public RecentFilesPanel(RecentFilesGetter dataHandler) {
super(dataHandler);
docsFetcher = (dataSource) -> dataHandler.getRecentlyOpenedDocuments(dataSource, 10);
downloadsFetcher = (dataSource) -> dataHandler.getRecentDownloads(dataSource, 10);
@ -137,15 +131,16 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
}
/**
* Returns a function that gets the date from the RecentFileDetails object and
* converts into a DefaultCellModel to be displayed in a table.
* Returns a function that gets the date from the RecentFileDetails object
* and converts into a DefaultCellModel to be displayed in a table.
*
* @return The function that determines the date cell from a RecentFileDetails object.
* @return The function that determines the date cell from a
* RecentFileDetails object.
*/
private <T extends RecentFileDetails> Function<T, DefaultCellModel<?>> getDateFunct() {
return (T lastAccessed) -> {
Function<Date, String> dateParser = (dt) -> dt == null ? "" : DATETIME_FORMAT.format(dt);
return new DefaultCellModel<>(new Date(lastAccessed.getDateAsLong() * 1000), dateParser, DATETIME_FORMAT_STR)
return new DefaultCellModel<>(new Date(lastAccessed.getDateAsLong() * 1000), dateParser)
.setPopupMenuRetriever(getPopupFunct(lastAccessed));
};
}
@ -155,9 +150,10 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
* items.
*
* @param record The RecentFileDetails instance.
*
* @return The menu items list containing one action or navigating to the
* appropriate artifact/file and closing the data source summary dialog if
* open.
* appropriate artifact/file and closing the data source summary
* dialog if open.
*/
private Supplier<List<MenuItem>> getPopupFunct(RecentFileDetails record) {
return () -> {
@ -190,16 +186,6 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
onNewDataSource(dataFetchComponents, tablePanelList, dataSource);
}
@Override
List<ExcelExport.ExcelSheetExport> getExports(DataSource dataSource) {
return Stream.of(
getTableExport(docsFetcher, docsTemplate, Bundle.RecentFilesPanel_docsTable_tabName(), dataSource),
getTableExport(downloadsFetcher, downloadsTemplate, Bundle.RecentFilesPanel_downloadsTable_tabName(), dataSource),
getTableExport(attachmentsFetcher, attachmentsTemplate, Bundle.RecentFilesPanel_attachmentsTable_tabName(), dataSource))
.filter(sheet -> sheet != null)
.collect(Collectors.toList());
}
@Override
public void close() {
ingestRunningLabel.unregister();
@ -216,7 +202,7 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
}
@Messages({
"RecentFilePanel_no_open_documents=No recently open documents found."
"RecentFilesPanel_no_open_documents=No recently open documents found."
})
/**
* Setup the data model and columns for the recently open table.

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -19,8 +19,6 @@
package org.sleuthkit.autopsy.datasourcesummary.ui;
import java.text.DecimalFormat;
import java.util.Arrays;
import java.util.List;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultCellModel;
@ -45,28 +43,23 @@ public final class SizeRepresentationUtil {
"SizeRepresentationUtil_units_petabytes=PB"
})
enum SizeUnit {
BYTES(Bundle.SizeRepresentationUtil_units_bytes(), "#", 0),
KB(Bundle.SizeRepresentationUtil_units_kilobytes(), "#,##0.00,", 1),
MB(Bundle.SizeRepresentationUtil_units_megabytes(), "#,##0.00,,", 2),
GB(Bundle.SizeRepresentationUtil_units_gigabytes(), "#,##0.00,,,", 3),
TB(Bundle.SizeRepresentationUtil_units_terabytes(), "#,##0.00,,,,", 4),
PB(Bundle.SizeRepresentationUtil_units_petabytes(), "#,##0.00,,,,,", 5);
BYTES(Bundle.SizeRepresentationUtil_units_bytes(), 0),
KB(Bundle.SizeRepresentationUtil_units_kilobytes(), 1),
MB(Bundle.SizeRepresentationUtil_units_megabytes(), 2),
GB(Bundle.SizeRepresentationUtil_units_gigabytes(), 3),
TB(Bundle.SizeRepresentationUtil_units_terabytes(), 4),
PB(Bundle.SizeRepresentationUtil_units_petabytes(), 5);
private final String suffix;
private final String excelFormatString;
private final long divisor;
/**
* Main constructor.
* @param suffix The string suffix to use for size unit.
* @param excelFormatString The excel format string to use for this size unit.
* @param power The power of 1000 of bytes for this size unit.
*/
SizeUnit(String suffix, String excelFormatString, int power) {
SizeUnit(String suffix, int power) {
this.suffix = suffix;
// based on https://www.mrexcel.com/board/threads/how-do-i-format-cells-to-show-gb-mb-kb.140135/
this.excelFormatString = String.format("%s \"%s\"", excelFormatString, suffix);
this.divisor = (long) Math.pow(SIZE_CONVERSION_CONSTANT, power);
}
@ -77,13 +70,6 @@ public final class SizeRepresentationUtil {
return suffix;
}
/**
* @return The excel format string to use for this size unit.
*/
public String getExcelFormatString() {
return excelFormatString;
}
/**
* @return The divisor to convert from bytes to this unit.
*/
@ -114,8 +100,7 @@ public final class SizeRepresentationUtil {
return SizeUnit.values()[0];
}
for (int unitsIndex = 0; unitsIndex < SizeUnit.values().length; unitsIndex++) {
SizeUnit unit = SizeUnit.values()[unitsIndex];
for (SizeUnit unit : SizeUnit.values()) {
long result = size / unit.getDivisor();
if (result < SIZE_CONVERSION_CONSTANT) {
return unit;
@ -126,14 +111,14 @@ public final class SizeRepresentationUtil {
}
/**
* Get a long size in bytes as a string formated to be read by users.
* Get a long size in bytes as a string formatted to be read by users.
*
* @param size Long value representing a size in byte.s
* @param format The means of formatting the number.
* @param showFullSize Optionally show the number of bytes in the
* datasource.
*
* @return Return a string formated with a user friendly version of the size
* @return Return a string formatted with a user friendly version of the size
* as a string, returns empty String when provided empty size.
*/
static String getSizeString(Long size, DecimalFormat format, boolean showFullSize) {
@ -168,12 +153,7 @@ public final class SizeRepresentationUtil {
if (bytes == null) {
return new DefaultCellModel<>("");
} else {
SizeUnit unit = SizeRepresentationUtil.getSizeUnit(bytes);
if (unit == null) {
unit = SizeUnit.BYTES;
}
return new DefaultCellModel<Long>(bytes, SizeRepresentationUtil::getSizeString, unit.getExcelFormatString());
return new DefaultCellModel<>(bytes, SizeRepresentationUtil::getSizeString);
}
}

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -20,39 +20,29 @@ package org.sleuthkit.autopsy.datasourcesummary.ui;
import java.awt.Color;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.logging.Level;
import org.apache.commons.collections.CollectionUtils;
import org.joda.time.DateTime;
import org.joda.time.Interval;
import org.openide.util.NbBundle.Messages;
import org.openide.util.actions.CallableSystemAction;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineDataSourceUtils;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineSummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineSummary.DailyActivityAmount;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineSummary.TimelineSummaryData;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartExport;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartPanel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartSeries;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartPanel.OrderedKey;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartSeries.OrderedKey;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartSeries.BarChartItem;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetcher;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultCellModel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport.KeyValueItemExportable;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport.TitledExportable;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataFetcher;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartSeries;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableComponent;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableLabel;
@ -78,20 +68,10 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
private static final long serialVersionUID = 1L;
private static final String EARLIEST_LATEST_FORMAT_STR = "MMM d, yyyy";
private static final DateFormat EARLIEST_LATEST_FORMAT = getUtcFormat(EARLIEST_LATEST_FORMAT_STR);
private static final DateFormat CHART_FORMAT = getUtcFormat("MMM d, yyyy");
private static final DateFormat EARLIEST_LATEST_FORMAT = TimelineSummary.getUtcFormat(EARLIEST_LATEST_FORMAT_STR);
private static final DateFormat CHART_FORMAT = TimelineSummary.getUtcFormat("MMM d, yyyy");
private static final int MOST_RECENT_DAYS_COUNT = 30;
/**
* Creates a DateFormat formatter that uses UTC for time zone.
*
* @param formatString The date format string.
* @return The data format.
*/
private static DateFormat getUtcFormat(String formatString) {
return new SimpleDateFormat(formatString, Locale.getDefault());
}
// components displayed in the tab
private final IngestRunningLabel ingestRunningLabel = new IngestRunningLabel();
private final LoadableLabel earliestLabel = new LoadableLabel(Bundle.TimelinePanel_earliestLabel_title());
@ -108,13 +88,13 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
private final List<DataFetchComponents<DataSource, ?>> dataFetchComponents;
public TimelinePanel() {
this(new TimelineSummary());
this(new TimelineSummaryGetter());
}
/**
* Creates new form PastCasesPanel
*/
public TimelinePanel(TimelineSummary timelineData) {
public TimelinePanel(TimelineSummaryGetter timelineData) {
super(timelineData);
dataFetcher = (dataSource) -> timelineData.getData(dataSource, MOST_RECENT_DAYS_COUNT);
@ -126,29 +106,18 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
initComponents();
}
/**
* Formats a date using a DateFormat. In the event that the date is null,
* returns a null string.
*
* @param date The date to format.
* @param formatter The DateFormat to use to format the date.
* @return The formatted string generated from the formatter or null if the
* date is null.
*/
private static String formatDate(Date date, DateFormat formatter) {
return date == null ? null : formatter.format(date);
}
private static final Color FILE_EVT_COLOR = new Color(228, 22, 28);
private static final Color ARTIFACT_EVT_COLOR = new Color(21, 227, 100);
/**
* Converts DailyActivityAmount data retrieved from TimelineSummary into
* data to be displayed as a bar chart.
* Converts DailyActivityAmount data retrieved from TimelineSummaryGetter
* into data to be displayed as a bar chart.
*
* @param recentDaysActivity The data retrieved from TimelineSummary.
* @param recentDaysActivity The data retrieved from
* TimelineSummaryGetter.
* @param showIntermediateDates If true, shows all dates. If false, shows
* only first and last date.
* only first and last date.
*
* @return The data to be displayed in the BarChart.
*/
private List<BarChartSeries> parseChartData(List<DailyActivityAmount> recentDaysActivity, boolean showIntermediateDates) {
@ -167,7 +136,7 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
long fileAmt = curItem.getFileActivityCount();
long artifactAmt = curItem.getArtifactActivityCount() * 100;
String formattedDate = (showIntermediateDates || i == 0 || i == recentDaysActivity.size() - 1)
? formatDate(curItem.getDay(), CHART_FORMAT) : "";
? TimelineSummary.formatDate(curItem.getDay(), CHART_FORMAT) : "";
OrderedKey thisKey = new OrderedKey(formattedDate, i);
fileEvtCounts.add(new BarChartItem(thisKey, fileAmt));
@ -191,8 +160,8 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
* @param result The result to be displayed on this tab.
*/
private void handleResult(DataFetchResult<TimelineSummaryData> result) {
earliestLabel.showDataFetchResult(DataFetchResult.getSubResult(result, r -> formatDate(r.getMinDate(), EARLIEST_LATEST_FORMAT)));
latestLabel.showDataFetchResult(DataFetchResult.getSubResult(result, r -> formatDate(r.getMaxDate(), EARLIEST_LATEST_FORMAT)));
earliestLabel.showDataFetchResult(DataFetchResult.getSubResult(result, r -> TimelineSummary.formatDate(r.getMinDate(), EARLIEST_LATEST_FORMAT)));
latestLabel.showDataFetchResult(DataFetchResult.getSubResult(result, r -> TimelineSummary.formatDate(r.getMaxDate(), EARLIEST_LATEST_FORMAT)));
last30DaysChart.showDataFetchResult(DataFetchResult.getSubResult(result, r -> parseChartData(r.getMostRecentDaysActivity(), false)));
if (result != null
@ -242,8 +211,8 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
* Action that occurs when 'View in Timeline' button is pressed.
*
* @param dataSource The data source to filter to.
* @param minDate The min date for the zoom of the window.
* @param maxDate The max date for the zoom of the window.
* @param minDate The min date for the zoom of the window.
* @param maxDate The max date for the zoom of the window.
*/
private void openFilteredChart(DataSource dataSource, Date minDate, Date maxDate) {
OpenTimelineAction openTimelineAction = CallableSystemAction.get(OpenTimelineAction.class);
@ -266,7 +235,7 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
if (minDate != null && maxDate != null) {
timeSpan = new Interval(new DateTime(minDate), new DateTime(maxDate));
}
} catch (NoCurrentCaseException | TskCoreException ex) {
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Unable to view time range in Timeline view", ex);
}
@ -293,43 +262,6 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
super.close();
}
/**
* Create a default cell model to be use with excel export in the earliest /
* latest date format.
*
* @param date The date.
* @return The cell model.
*/
private static DefaultCellModel<?> getEarliestLatestCell(Date date) {
return new DefaultCellModel<>(date, (dt) -> dt == null ? "" : EARLIEST_LATEST_FORMAT.format(dt), EARLIEST_LATEST_FORMAT_STR);
}
@Messages({
"TimelinePanel_getExports_sheetName=Timeline",
"TimelinePanel_getExports_activityRange=Activity Range",
"TimelinePanel_getExports_earliest=Earliest:",
"TimelinePanel_getExports_latest=Latest:",
"TimelinePanel_getExports_dateColumnHeader=Date",
"TimelinePanel_getExports_chartName=Last 30 Days",})
@Override
List<ExcelExport.ExcelSheetExport> getExports(DataSource dataSource) {
TimelineSummaryData summaryData = getFetchResult(dataFetcher, "Timeline", dataSource);
if (summaryData == null) {
return Collections.emptyList();
}
return Arrays.asList(
new ExcelSpecialFormatExport(Bundle.TimelinePanel_getExports_sheetName(),
Arrays.asList(
new TitledExportable(Bundle.TimelinePanel_getExports_activityRange(), Collections.emptyList()),
new KeyValueItemExportable(Bundle.TimelinePanel_getExports_earliest(), getEarliestLatestCell(summaryData.getMinDate())),
new KeyValueItemExportable(Bundle.TimelinePanel_getExports_latest(), getEarliestLatestCell(summaryData.getMaxDate())),
new BarChartExport(Bundle.TimelinePanel_getExports_dateColumnHeader(),
"#,###",
Bundle.TimelinePanel_getExports_chartName(),
parseChartData(summaryData.getMostRecentDaysActivity(), true)))));
}
/**
* 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

View File

@ -0,0 +1,88 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 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.datasourcesummary.ui;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultUpdateGovernor;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineSummary;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.ModuleContentEvent;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineSummary.TimelineSummaryData;
/**
* Provides data source summary information pertaining to Timeline data.
*/
public class TimelineSummaryGetter implements DefaultUpdateGovernor {
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS = new HashSet<>(
Arrays.asList(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED));
private final TimelineSummary timelineSummary;
/**
* Default constructor.
*/
public TimelineSummaryGetter() {
timelineSummary = new TimelineSummary();
}
@Override
public boolean isRefreshRequired(ModuleContentEvent evt) {
return true;
}
@Override
public boolean isRefreshRequired(AbstractFile file) {
return true;
}
@Override
public boolean isRefreshRequired(IngestManager.IngestJobEvent evt) {
return (evt != null && INGEST_JOB_EVENTS.contains(evt));
}
@Override
public Set<IngestManager.IngestJobEvent> getIngestJobEventUpdates() {
return Collections.unmodifiableSet(INGEST_JOB_EVENTS);
}
/**
* Retrieves timeline summary data.
*
* @param dataSource The data source for which timeline data will be
* retrieved.
* @param recentDaysNum The maximum number of most recent days' activity to
* include.
*
* @return The retrieved data.
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
*/
public TimelineSummaryData getData(DataSource dataSource, int recentDaysNum) throws SleuthkitCaseProviderException, TskCoreException {
return timelineSummary.getTimelineSummaryData(dataSource, recentDaysNum);
}
}

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 Basis Technology Corp.
* Copyright 2019-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -23,31 +23,23 @@ import java.sql.SQLException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.coreutils.FileTypeUtils.FileTypeCategory;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TypesSummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.ContainerSummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.MimeTypeSummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataSourceInfoUtilities;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TypesSummary.FileTypeCategoryData;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult.ResultType;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetcher;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultCellModel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport.KeyValueItemExportable;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataFetcher;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableComponent;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableLabel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.PieChartExport;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.PieChartPanel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.PieChartItem;
@ -95,7 +87,7 @@ class TypesPanel extends BaseDataSourceSummaryPanel {
* @param usefulContent True if this is useful content; false if there
* is 0 mime type information.
*/
public TypesPieChartData(List<PieChartItem> pieSlices, boolean usefulContent) {
TypesPieChartData(List<PieChartItem> pieSlices, boolean usefulContent) {
this.pieSlices = pieSlices;
this.usefulContent = usefulContent;
}
@ -103,78 +95,20 @@ class TypesPanel extends BaseDataSourceSummaryPanel {
/**
* @return The pie chart data.
*/
public List<PieChartItem> getPieSlices() {
List<PieChartItem> getPieSlices() {
return pieSlices;
}
/**
* @return Whether or not the data is usefulContent.
*/
public boolean isUsefulContent() {
boolean isUsefulContent() {
return usefulContent;
}
}
/**
* Information concerning a particular category in the file types pie chart.
*/
private static class TypesPieCategory {
private final String label;
private final Set<String> mimeTypes;
private final Color color;
/**
* Main constructor.
*
* @param label The label for this slice.
* @param mimeTypes The mime types associated with this slice.
* @param color The color associated with this slice.
*/
TypesPieCategory(String label, Set<String> mimeTypes, Color color) {
this.label = label;
this.mimeTypes = mimeTypes;
this.color = color;
}
/**
* Constructor that accepts FileTypeCategory.
*
* @param label The label for this slice.
* @param mimeTypes The mime types associated with this slice.
* @param color The color associated with this slice.
*/
TypesPieCategory(String label, FileTypeCategory fileCategory, Color color) {
this(label, fileCategory.getMediaTypes(), color);
}
/**
* @return The label for this category.
*/
String getLabel() {
return label;
}
/**
* @return The mime types associated with this category.
*/
Set<String> getMimeTypes() {
return mimeTypes;
}
/**
* @return The color associated with this category.
*/
Color getColor() {
return color;
}
}
private static final long serialVersionUID = 1L;
private static final DecimalFormat INTEGER_SIZE_FORMAT = new DecimalFormat("#");
private static final String COMMA_FORMAT_STR = "#,###";
private static final DecimalFormat COMMA_FORMATTER = new DecimalFormat(COMMA_FORMAT_STR);
private static final Color IMAGES_COLOR = new Color(156, 39, 176);
private static final Color VIDEOS_COLOR = Color.YELLOW;
@ -186,13 +120,13 @@ class TypesPanel extends BaseDataSourceSummaryPanel {
private static final Color NOT_ANALYZED_COLOR = Color.WHITE;
// All file type categories.
private static final List<TypesPieCategory> FILE_MIME_TYPE_CATEGORIES = Arrays.asList(
new TypesPieCategory(Bundle.TypesPanel_fileMimeTypesChart_images_title(), FileTypeCategory.IMAGE.getMediaTypes(), IMAGES_COLOR),
new TypesPieCategory(Bundle.TypesPanel_fileMimeTypesChart_videos_title(), FileTypeCategory.VIDEO.getMediaTypes(), VIDEOS_COLOR),
new TypesPieCategory(Bundle.TypesPanel_fileMimeTypesChart_audio_title(), FileTypeCategory.AUDIO.getMediaTypes(), AUDIO_COLOR),
new TypesPieCategory(Bundle.TypesPanel_fileMimeTypesChart_documents_title(), FileTypeCategory.DOCUMENTS.getMediaTypes(), DOCUMENTS_COLOR),
new TypesPieCategory(Bundle.TypesPanel_fileMimeTypesChart_executables_title(), FileTypeCategory.EXECUTABLE.getMediaTypes(), EXECUTABLES_COLOR),
new TypesPieCategory(Bundle.TypesPanel_fileMimeTypesChart_unknown_title(), new HashSet<>(Arrays.asList("application/octet-stream")), UNKNOWN_COLOR)
private static final List<FileTypeCategoryData> FILE_MIME_TYPE_CATEGORIES = Arrays.asList(
new FileTypeCategoryData(Bundle.TypesPanel_fileMimeTypesChart_images_title(), FileTypeCategory.IMAGE.getMediaTypes(), IMAGES_COLOR),
new FileTypeCategoryData(Bundle.TypesPanel_fileMimeTypesChart_videos_title(), FileTypeCategory.VIDEO.getMediaTypes(), VIDEOS_COLOR),
new FileTypeCategoryData(Bundle.TypesPanel_fileMimeTypesChart_audio_title(), FileTypeCategory.AUDIO.getMediaTypes(), AUDIO_COLOR),
new FileTypeCategoryData(Bundle.TypesPanel_fileMimeTypesChart_documents_title(), FileTypeCategory.DOCUMENTS.getMediaTypes(), DOCUMENTS_COLOR),
new FileTypeCategoryData(Bundle.TypesPanel_fileMimeTypesChart_executables_title(), FileTypeCategory.EXECUTABLE.getMediaTypes(), EXECUTABLES_COLOR),
new FileTypeCategoryData(Bundle.TypesPanel_fileMimeTypesChart_unknown_title(), new HashSet<>(Arrays.asList("application/octet-stream")), UNKNOWN_COLOR)
);
private final DataFetcher<DataSource, String> usageFetcher;
@ -237,8 +171,8 @@ class TypesPanel extends BaseDataSourceSummaryPanel {
/**
* Creates a new TypesPanel.
*/
public TypesPanel() {
this(new MimeTypeSummary(), new TypesSummary(), new ContainerSummary());
TypesPanel() {
this(new MimeTypeSummaryGetter(), new TypesSummaryGetter(), new ContainerSummaryGetter());
}
@Override
@ -254,10 +188,10 @@ class TypesPanel extends BaseDataSourceSummaryPanel {
* @param typeData The service for file types data.
* @param containerData The service for container information.
*/
public TypesPanel(
MimeTypeSummary mimeTypeData,
TypesSummary typeData,
ContainerSummary containerData) {
TypesPanel(
MimeTypeSummaryGetter mimeTypeData,
TypesSummaryGetter typeData,
ContainerSummaryGetter containerData) {
super(mimeTypeData, typeData, containerData);
@ -282,13 +216,13 @@ class TypesPanel extends BaseDataSourceSummaryPanel {
size -> SizeRepresentationUtil.getSizeString(size, INTEGER_SIZE_FORMAT, false)))),
new DataFetchWorker.DataFetchComponents<>(typesFetcher, this::showMimeTypeCategories),
new DataFetchWorker.DataFetchComponents<>(allocatedFetcher,
countRes -> allocatedLabel.showDataFetchResult(DataFetchResult.getSubResult(countRes, (count) -> getStringOrZero(count)))),
countRes -> allocatedLabel.showDataFetchResult(DataFetchResult.getSubResult(countRes, (count) -> DataSourceInfoUtilities.getStringOrZero(count)))),
new DataFetchWorker.DataFetchComponents<>(unallocatedFetcher,
countRes -> unallocatedLabel.showDataFetchResult(DataFetchResult.getSubResult(countRes, (count) -> getStringOrZero(count)))),
countRes -> unallocatedLabel.showDataFetchResult(DataFetchResult.getSubResult(countRes, (count) -> DataSourceInfoUtilities.getStringOrZero(count)))),
new DataFetchWorker.DataFetchComponents<>(slackFetcher,
countRes -> slackLabel.showDataFetchResult(DataFetchResult.getSubResult(countRes, (count) -> getStringOrZero(count)))),
countRes -> slackLabel.showDataFetchResult(DataFetchResult.getSubResult(countRes, (count) -> DataSourceInfoUtilities.getStringOrZero(count)))),
new DataFetchWorker.DataFetchComponents<>(directoriesFetcher,
countRes -> directoriesLabel.showDataFetchResult(DataFetchResult.getSubResult(countRes, (count) -> getStringOrZero(count))))
countRes -> directoriesLabel.showDataFetchResult(DataFetchResult.getSubResult(countRes, (count) -> DataSourceInfoUtilities.getStringOrZero(count))))
);
initComponents();
@ -312,7 +246,7 @@ class TypesPanel extends BaseDataSourceSummaryPanel {
*
* @return The pie chart items.
*/
private TypesPieChartData getMimeTypeCategoriesModel(MimeTypeSummary mimeTypeData, DataSource dataSource)
private TypesPieChartData getMimeTypeCategoriesModel(MimeTypeSummaryGetter mimeTypeData, DataSource dataSource)
throws SQLException, SleuthkitCaseProviderException, TskCoreException {
if (dataSource == null) {
@ -323,8 +257,8 @@ class TypesPanel extends BaseDataSourceSummaryPanel {
List<PieChartItem> fileCategoryItems = new ArrayList<>();
long categoryTotalCount = 0;
for (TypesPieCategory cat : FILE_MIME_TYPE_CATEGORIES) {
long thisValue = getLongOrZero(mimeTypeData.getCountOfFilesForMimeTypes(dataSource, cat.getMimeTypes()));
for (FileTypeCategoryData cat : FILE_MIME_TYPE_CATEGORIES) {
long thisValue = DataSourceInfoUtilities.getLongOrZero(mimeTypeData.getCountOfFilesForMimeTypes(dataSource, cat.getMimeTypes()));
categoryTotalCount += thisValue;
fileCategoryItems.add(new PieChartItem(
@ -334,10 +268,10 @@ class TypesPanel extends BaseDataSourceSummaryPanel {
}
// get a count of all files with no mime type
long noMimeTypeCount = getLongOrZero(mimeTypeData.getCountOfFilesWithNoMimeType(dataSource));
long noMimeTypeCount = DataSourceInfoUtilities.getLongOrZero(mimeTypeData.getCountOfFilesWithNoMimeType(dataSource));
// get a count of all regular files
long allRegularFiles = getLongOrZero(mimeTypeData.getCountOfAllRegularFiles(dataSource));
long allRegularFiles = DataSourceInfoUtilities.getLongOrZero(mimeTypeData.getCountOfAllRegularFiles(dataSource));
// create entry for mime types in other category
long otherCount = allRegularFiles - (categoryTotalCount + noMimeTypeCount);
@ -390,89 +324,6 @@ class TypesPanel extends BaseDataSourceSummaryPanel {
}
}
/**
* Returns the long value or zero if longVal is null.
*
* @param longVal The long value.
*
* @return The long value or 0 if provided value is null.
*/
private static long getLongOrZero(Long longVal) {
return longVal == null ? 0 : longVal;
}
/**
* Returns string value of long with comma separators. If null returns a
* string of '0'.
*
* @param longVal The long value.
*
* @return The string value of the long.
*/
private static String getStringOrZero(Long longVal) {
return longVal == null ? "0" : COMMA_FORMATTER.format(longVal);
}
/**
* Returns a key value pair to be exported in a sheet.
*
* @param fetcher The means of fetching the data.
* @param key The key to use.
* @param dataSource The data source containing the data.
* @return The key value pair to be exported.
*/
private static KeyValueItemExportable getStrExportable(DataFetcher<DataSource, String> fetcher, String key, DataSource dataSource) {
String result = getFetchResult(fetcher, "Types", dataSource);
return (result == null) ? null : new KeyValueItemExportable(key, new DefaultCellModel<>(result));
}
/**
* Returns a key value pair to be exported in a sheet formatting the long
* with commas separated by orders of 1000.
*
* @param fetcher The means of fetching the data.
* @param key The string key for this key value pair.
* @param dataSource The data source.
* @return The key value pair.
*/
private static KeyValueItemExportable getCountExportable(DataFetcher<DataSource, Long> fetcher, String key, DataSource dataSource) {
Long count = getFetchResult(fetcher, "Types", dataSource);
return (count == null) ? null : new KeyValueItemExportable(key,
new DefaultCellModel<Long>(count, COMMA_FORMATTER::format, COMMA_FORMAT_STR));
}
@Override
List<ExcelExport.ExcelSheetExport> getExports(DataSource dataSource) {
if (dataSource == null) {
return Collections.emptyList();
}
// Retrieve data to create the types pie chart
TypesPieChartData typesData = TypesPanel.getFetchResult(typesFetcher, "Types", dataSource);
PieChartExport typesChart = (typesData == null || !typesData.isUsefulContent()) ? null :
new PieChartExport(
Bundle.TypesPanel_fileMimeTypesChart_title(),
Bundle.TypesPanel_fileMimeTypesChart_valueLabel(),
"#,###",
Bundle.TypesPanel_fileMimeTypesChart_title(),
typesData.getPieSlices());
return Arrays.asList(new ExcelSpecialFormatExport(Bundle.TypesPanel_excelTabName(),
Stream.of(
getStrExportable(usageFetcher, Bundle.TypesPanel_usageLabel_title(), dataSource),
getStrExportable(osFetcher, Bundle.TypesPanel_osLabel_title(), dataSource),
new KeyValueItemExportable(Bundle.TypesPanel_sizeLabel_title(),
SizeRepresentationUtil.getBytesCell(getFetchResult(sizeFetcher, "Types", dataSource))),
typesChart,
getCountExportable(allocatedFetcher, Bundle.TypesPanel_filesByCategoryTable_allocatedRow_title(), dataSource),
getCountExportable(unallocatedFetcher, Bundle.TypesPanel_filesByCategoryTable_unallocatedRow_title(), dataSource),
getCountExportable(slackFetcher, Bundle.TypesPanel_filesByCategoryTable_slackRow_title(), dataSource),
getCountExportable(directoriesFetcher, Bundle.TypesPanel_filesByCategoryTable_directoryRow_title(), dataSource))
.filter(sheet -> sheet != null)
.collect(Collectors.toList())
));
}
/**
* 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

View File

@ -0,0 +1,154 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 - 2020 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.datasourcesummary.ui;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultUpdateGovernor;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TypesSummary;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.ModuleContentEvent;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Wrapper class for converting org.sleuthkit.autopsy.contentutils.TypesSummary
* functionality into a DefaultArtifactUpdateGovernor used by
* DataSourceSummaryCountsPanel.
*/
public class TypesSummaryGetter implements DefaultUpdateGovernor {
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS = new HashSet<>(
Arrays.asList(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED));
private final TypesSummary typesSummary;
/**
* Main constructor.
*/
public TypesSummaryGetter() {
typesSummary = new TypesSummary();
}
@Override
public boolean isRefreshRequired(ModuleContentEvent evt) {
return true;
}
@Override
public boolean isRefreshRequired(AbstractFile file) {
return true;
}
@Override
public boolean isRefreshRequired(IngestManager.IngestJobEvent evt) {
return (evt != null && INGEST_JOB_EVENTS.contains(evt));
}
@Override
public Set<IngestManager.IngestJobEvent> getIngestJobEventUpdates() {
return Collections.unmodifiableSet(INGEST_JOB_EVENTS);
}
/**
* Get count of regular files (not directories) in a data source.
*
* @param currentDataSource The data source.
*
* @return The count.
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
* @throws SQLException
*/
public Long getCountOfFiles(DataSource currentDataSource)
throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException {
return typesSummary.getCountOfFiles(currentDataSource);
}
/**
* Get count of allocated files in a data source.
*
* @param currentDataSource The data source.
*
* @return The count.
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
* @throws SQLException
*/
public Long getCountOfAllocatedFiles(DataSource currentDataSource)
throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException {
return typesSummary.getCountOfAllocatedFiles(currentDataSource);
}
/**
* Get count of unallocated files in a data source.
*
* @param currentDataSource The data source.
*
* @return The count.
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
* @throws SQLException
*/
public Long getCountOfUnallocatedFiles(DataSource currentDataSource)
throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException {
return typesSummary.getCountOfUnallocatedFiles(currentDataSource);
}
/**
* Get count of directories in a data source.
*
* @param currentDataSource The data source.
*
* @return The count.
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
* @throws SQLException
*/
public Long getCountOfDirectories(DataSource currentDataSource)
throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException {
return typesSummary.getCountOfDirectories(currentDataSource);
}
/**
* Get count of slack files in a data source.
*
* @param currentDataSource The data source.
*
* @return The count.
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
* @throws SQLException
*/
public Long getCountOfSlackFiles(DataSource currentDataSource)
throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException {
return typesSummary.getCountOfSlackFiles(currentDataSource);
}
}

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -26,23 +26,19 @@ import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.lang.StringUtils;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.LastAccessedArtifact;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopAccountResult;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopDeviceAttachedResult;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopWebSearchResult;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopDomainsResult;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopProgramsResult;
import static org.sleuthkit.autopsy.datasourcesummary.ui.BaseDataSourceSummaryPanel.getTableExport;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopWebSearchResult;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ColumnModel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetcher;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataFetcher;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultCellModel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.GuiCellModel.MenuItem;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel;
@ -265,22 +261,22 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
private final IngestRunningLabel ingestRunningLabel = new IngestRunningLabel();
private final List<DataFetchComponents<DataSource, ?>> dataFetchComponents;
private final UserActivitySummary userActivityData;
private final UserActivitySummaryGetter userActivityData;
/**
* Creates a new UserActivityPanel.
*/
public UserActivityPanel() {
this(new UserActivitySummary());
this(new UserActivitySummaryGetter());
}
/**
* Creates a new UserActivityPanel.
*
* @param userActivityData Class from which to obtain remaining user
* activity data.
* activity data.
*/
public UserActivityPanel(UserActivitySummary userActivityData) {
public UserActivityPanel(UserActivitySummaryGetter userActivityData) {
super(userActivityData);
this.userActivityData = userActivityData;
@ -320,7 +316,7 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
private <T extends LastAccessedArtifact> Function<T, DefaultCellModel<?>> getDateFunct() {
return (T lastAccessed) -> {
Function<Date, String> dateParser = (dt) -> dt == null ? "" : DATETIME_FORMAT.format(dt);
return new DefaultCellModel<>(lastAccessed.getLastAccessed(), dateParser, DATETIME_FORMAT_STR)
return new DefaultCellModel<>(lastAccessed.getLastAccessed(), dateParser)
.setPopupMenu(getPopup(lastAccessed));
};
}
@ -332,7 +328,8 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
* @param record The LastAccessedArtifact instance.
*
* @return The menu items list containing one action or navigating to the
* appropriate artifact and closing the data source summary dialog if open.
* appropriate artifact and closing the data source summary dialog
* if open.
*/
private List<MenuItem> getPopup(LastAccessedArtifact record) {
return record == null ? null : Arrays.asList(getArtifactNavigateItem(record.getArtifact()));
@ -341,13 +338,13 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
/**
* Queries DataSourceTopProgramsSummary instance for short folder name.
*
* @param path The path for the application.
* @param path The path for the application.
* @param appName The application name.
*
* @return The underlying short folder name if one exists.
*/
private String getShortFolderName(String path, String appName) {
return this.userActivityData.getShortFolderName(path, appName);
private static String getShortFolderName(String path, String appName) {
return UserActivitySummary.getShortFolderName(path, appName);
}
@Override
@ -366,18 +363,6 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
super.close();
}
@Override
List<ExcelExport.ExcelSheetExport> getExports(DataSource dataSource) {
return Stream.of(
getTableExport(topProgramsFetcher, topProgramsTemplate, Bundle.UserActivityPanel_TopProgramsTableModel_tabName(), dataSource),
getTableExport(topDomainsFetcher, topDomainsTemplate, Bundle.UserActivityPanel_TopDomainsTableModel_tabName(), dataSource),
getTableExport(topWebSearchesFetcher, topWebSearchesTemplate, Bundle.UserActivityPanel_TopWebSearchTableModel_tabName(), dataSource),
getTableExport(topDevicesAttachedFetcher, topDevicesTemplate, Bundle.UserActivityPanel_TopDeviceAttachedTableModel_tabName(), dataSource),
getTableExport(topAccountsFetcher, topAccountsTemplate, Bundle.UserActivityPanel_TopAccountTableModel_tabName(), dataSource))
.filter(sheet -> sheet != null)
.collect(Collectors.toList());
}
/**
* 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

View File

@ -0,0 +1,162 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020-2021 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.datasourcesummary.ui;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultArtifactUpdateGovernor;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopAccountResult;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopDeviceAttachedResult;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopDomainsResult;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopProgramsResult;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopWebSearchResult;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
/**
* Wrapper class for converting
* org.sleuthkit.autopsy.contentutils.UserActivitySummary functionality into a
* DefaultArtifactUpdateGovernor used by UserActivityPanel tab.
*/
public class UserActivitySummaryGetter implements DefaultArtifactUpdateGovernor {
private static final Set<Integer> ARTIFACT_UPDATE_TYPE_IDS = new HashSet<>(Arrays.asList(
ARTIFACT_TYPE.TSK_WEB_SEARCH_QUERY.getTypeID(),
ARTIFACT_TYPE.TSK_MESSAGE.getTypeID(),
ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID(),
ARTIFACT_TYPE.TSK_CALLLOG.getTypeID(),
ARTIFACT_TYPE.TSK_DEVICE_ATTACHED.getTypeID(),
ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID(),
ARTIFACT_TYPE.TSK_PROG_RUN.getTypeID()
));
private final UserActivitySummary userActivity;
public UserActivitySummaryGetter() {
userActivity = new UserActivitySummary();
}
@Override
public Set<Integer> getArtifactTypeIdsForRefresh() {
return Collections.unmodifiableSet(ARTIFACT_UPDATE_TYPE_IDS);
}
/**
* Gets a list of recent domains based on the datasource.
*
* @param dataSource The datasource to query for recent domains.
* @param count The max count of items to return.
*
* @return The list of items retrieved from the database.
*
* @throws InterruptedException
*/
public List<TopDomainsResult> getRecentDomains(DataSource dataSource, int count) throws TskCoreException, SleuthkitCaseProviderException {
return userActivity.getRecentDomains(dataSource, count);
}
/**
* Retrieves most recent web searches by most recent date grouped by search
* term.
*
* @param dataSource The data source.
* @param count The maximum number of records to be shown (must be >
* 0).
*
* @return The list of most recent web searches where most recent search
* appears first.
*
* @throws
* org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException
* @throws TskCoreException
*/
public List<TopWebSearchResult> getMostRecentWebSearches(DataSource dataSource, int count) throws SleuthkitCaseProviderException, TskCoreException {
return userActivity.getMostRecentWebSearches(dataSource, count);
}
/**
* Retrieves most recent devices used by most recent date attached.
*
* @param dataSource The data source.
* @param count The maximum number of records to be shown (must be >
* 0).
*
* @return The list of most recent devices attached where most recent device
* attached appears first.
*
* @throws
* org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException
* @throws TskCoreException
*/
public List<TopDeviceAttachedResult> getRecentDevices(DataSource dataSource, int count) throws SleuthkitCaseProviderException, TskCoreException {
return userActivity.getRecentDevices(dataSource, count);
}
/**
* Retrieves most recent account used by most recent date for a message
* sent.
*
* @param dataSource The data source.
* @param count The maximum number of records to be shown (must be >
* 0).
*
* @return The list of most recent accounts used where the most recent
* account by last message sent occurs first.
*
* @throws
* org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException
* @throws TskCoreException
*/
@Messages({
"DataSourceUserActivitySummary_getRecentAccounts_emailMessage=Email Message",
"DataSourceUserActivitySummary_getRecentAccounts_calllogMessage=Call Log",})
public List<TopAccountResult> getRecentAccounts(DataSource dataSource, int count) throws SleuthkitCaseProviderException, TskCoreException {
return userActivity.getRecentAccounts(dataSource, count);
}
/**
* Retrieves the top programs results for the given data source limited to
* the count provided as a parameter. The highest run times are at the top
* of the list. If that information isn't available the last run date is
* used. If both, the last run date and the number of run times are
* unavailable, the programs will be sorted alphabetically, the count will
* be ignored and all items will be returned.
*
* @param dataSource The datasource. If the datasource is null, an empty
* list will be returned.
* @param count The number of results to return. This value must be > 0
* or an IllegalArgumentException will be thrown.
*
* @return The sorted list and limited to the count if last run or run count
* information is available on any item.
*
* @throws SleuthkitCaseProviderException
* @throws TskCoreException
*/
public List<TopProgramsResult> getTopPrograms(DataSource dataSource, int count) throws SleuthkitCaseProviderException, TskCoreException {
return userActivity.getTopPrograms(dataSource, count);
}
}

View File

@ -39,84 +39,6 @@ import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartSeries.BarChartIt
*/
public class BarChartPanel extends AbstractLoadableComponent<List<BarChartSeries>> {
/**
* JFreeChart bar charts don't preserve the order of bars provided to the
* chart, but instead uses the comparable nature to order items. This
* provides order using a provided index as well as the value for the axis.
*/
public static class OrderedKey implements Comparable<OrderedKey> {
private final Object keyValue;
private final int keyIndex;
/**
* Main constructor.
*
* @param keyValue The value for the key to be displayed in the domain
* axis.
* @param keyIndex The index at which it will be displayed.
*/
public OrderedKey(Object keyValue, int keyIndex) {
this.keyValue = keyValue;
this.keyIndex = keyIndex;
}
/**
* @return The value for the key to be displayed in the domain axis.
*/
Object getKeyValue() {
return keyValue;
}
/**
* @return The index at which it will be displayed.
*/
int getKeyIndex() {
return keyIndex;
}
@Override
public int compareTo(OrderedKey o) {
// this will have a higher value than null.
if (o == null) {
return 1;
}
// compare by index
return Integer.compare(this.getKeyIndex(), o.getKeyIndex());
}
@Override
public int hashCode() {
int hash = 3;
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final OrderedKey other = (OrderedKey) obj;
if (this.keyIndex != other.keyIndex) {
return false;
}
return true;
}
@Override
public String toString() {
// use toString on the key.
return this.getKeyValue() == null ? null : this.getKeyValue().toString();
}
}
private static final long serialVersionUID = 1L;
private static final Font DEFAULT_FONT = new JLabel().getFont();

View File

@ -98,4 +98,82 @@ public class BarChartSeries {
return key;
}
/**
* JFreeChart bar charts don't preserve the order of bars provided to the
* chart, but instead uses the comparable nature to order items. This
* provides order using a provided index as well as the value for the axis.
*/
public static class OrderedKey implements Comparable<OrderedKey> {
private final Object keyValue;
private final int keyIndex;
/**
* Main constructor.
*
* @param keyValue The value for the key to be displayed in the domain
* axis.
* @param keyIndex The index at which it will be displayed.
*/
public OrderedKey(Object keyValue, int keyIndex) {
this.keyValue = keyValue;
this.keyIndex = keyIndex;
}
/**
* @return The value for the key to be displayed in the domain axis.
*/
Object getKeyValue() {
return keyValue;
}
/**
* @return The index at which it will be displayed.
*/
int getKeyIndex() {
return keyIndex;
}
@Override
public int compareTo(OrderedKey o) {
// this will have a higher value than null.
if (o == null) {
return 1;
}
// compare by index
return Integer.compare(this.getKeyIndex(), o.getKeyIndex());
}
@Override
public int hashCode() {
int hash = 3;
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final OrderedKey other = (OrderedKey) obj;
if (this.keyIndex != other.keyIndex) {
return false;
}
return true;
}
@Override
public String toString() {
// use toString on the key.
return this.getKeyValue() == null ? null : this.getKeyValue().toString();
}
}
}

View File

@ -1,7 +1,5 @@
AbstractLoadableComponent_errorMessage_defaultText=There was an error loading results.
AbstractLoadableComponent_loadingMessage_defaultText=Loading results...
AbstractLoadableComponent_noDataExists_defaultText=No data exists.
# {0} - sheetNumber
ExcelExport_writeExcel_noSheetName=Sheet {0}
IngestRunningLabel_defaultMessage=Ingest is currently running.
PieChartPanel_noDataLabel=No Data

View File

@ -18,6 +18,7 @@
*/
package org.sleuthkit.autopsy.datasourcesummary.uiutils;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataFetcher;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
import javax.swing.SwingWorker;

View File

@ -27,7 +27,7 @@ import java.util.function.Supplier;
/**
* The default cell model.
*/
public class DefaultCellModel<T> implements GuiCellModel, ExcelCellModel {
public class DefaultCellModel<T> implements GuiCellModel {
private final T data;
private final String text;
@ -35,7 +35,6 @@ public class DefaultCellModel<T> implements GuiCellModel, ExcelCellModel {
private CellModel.HorizontalAlign horizontalAlignment;
private List<MenuItem> popupMenu;
private Supplier<List<MenuItem>> menuItemSupplier;
private final String excelFormatString;
/**
* Main constructor.
@ -43,18 +42,7 @@ public class DefaultCellModel<T> implements GuiCellModel, ExcelCellModel {
* @param data The data to be displayed in the cell.
*/
public DefaultCellModel(T data) {
this(data, null, null);
}
/**
* Constructor.
*
* @param data The data to be displayed in the cell.
* @param stringConverter The means of converting that data to a string or
* null to use .toString method on object.
*/
public DefaultCellModel(T data, Function<T, String> stringConverter) {
this(data, stringConverter, null);
this(data, null);
}
/**
@ -63,15 +51,9 @@ public class DefaultCellModel<T> implements GuiCellModel, ExcelCellModel {
* @param data The data to be displayed in the cell.
* @param stringConverter The means of converting that data to a string or
* null to use .toString method on object.
* @param excelFormatString The apache poi excel format string to use with
* the data.
*
* NOTE: Only certain data types can be exported. See
* ExcelTableExport.createCell() for types.
*/
public DefaultCellModel(T data, Function<T, String> stringConverter, String excelFormatString) {
public DefaultCellModel(T data, Function<T, String> stringConverter) {
this.data = data;
this.excelFormatString = excelFormatString;
if (stringConverter == null) {
text = this.data == null ? "" : this.data.toString();
@ -86,11 +68,6 @@ public class DefaultCellModel<T> implements GuiCellModel, ExcelCellModel {
return this.data;
}
@Override
public String getExcelFormatString() {
return this.excelFormatString;
}
@Override
public String getText() {
return text;

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2018 Basis Technology Corp.
* Copyright 2011-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -26,6 +26,7 @@ import javax.swing.event.DocumentListener;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.GeneralFilter;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.guiutils.JFileChooserFactory;
import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector;
/**
@ -35,7 +36,9 @@ import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector;
class AddExternalViewerRulePanel extends javax.swing.JPanel {
private static final Logger logger = Logger.getLogger(AddExternalViewerRulePanel.class.getName());
private final JFileChooser fc = new JFileChooser();
private static final long serialVersionUID = 1L;
private JFileChooser fc;
private final JFileChooserFactory chooserHelper = new JFileChooserFactory();
private static final GeneralFilter exeFilter = new GeneralFilter(GeneralFilter.EXECUTABLE_EXTS, GeneralFilter.EXECUTABLE_DESC);
enum EVENT {
@ -47,10 +50,6 @@ class AddExternalViewerRulePanel extends javax.swing.JPanel {
*/
AddExternalViewerRulePanel() {
initComponents();
fc.setDragEnabled(false);
fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
fc.setMultiSelectionEnabled(false);
fc.setFileFilter(exeFilter);
customize();
}
@ -260,6 +259,13 @@ class AddExternalViewerRulePanel extends javax.swing.JPanel {
}// </editor-fold>//GEN-END:initComponents
private void browseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browseButtonActionPerformed
if(fc == null) {
fc = chooserHelper.getChooser();
fc.setDragEnabled(false);
fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
fc.setMultiSelectionEnabled(false);
fc.setFileFilter(exeFilter);
}
int returnState = fc.showOpenDialog(this);
if (returnState == JFileChooser.APPROVE_OPTION) {
String path = fc.getSelectedFile().getPath();

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 Basis Technology Corp.
* Copyright 2019-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -51,6 +51,7 @@ import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode.AbstractFileProp
import org.openide.nodes.Node;
import org.openide.nodes.Node.PropertySet;
import org.openide.nodes.Node.Property;
import org.sleuthkit.autopsy.guiutils.JFileChooserFactory;
/**
* Exports CSV version of result nodes to a location selected by the user.
@ -68,6 +69,8 @@ public final class ExportCSVAction extends AbstractAction {
// org.openide.nodes.NodeOp.findActions(Node[] nodes) will only pick up an Action if every
// node in the array returns a reference to the same action object from Node.getActions(boolean).
private static ExportCSVAction instance;
private static final JFileChooserFactory chooserHelper = new JFileChooserFactory();
/**
* Get an instance of the Action. See above for why
@ -125,7 +128,7 @@ public final class ExportCSVAction extends AbstractAction {
// Set up the file chooser with a default name and either the Export
// folder or the last used folder.
String fileName = getDefaultOutputFileName(nodesToExport.iterator().next().getParentNode());
JFileChooser fileChooser = new JFileChooser();
JFileChooser fileChooser = chooserHelper.getChooser();
fileChooser.setCurrentDirectory(new File(getExportDirectory(Case.getCurrentCaseThrows())));
fileChooser.setSelectedFile(new File(fileName));
fileChooser.setFileFilter(new FileNameExtensionFilter("csv file", "csv"));

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2018 Basis Technology Corp.
* Copyright 2011-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -33,6 +33,7 @@ import org.netbeans.spi.options.OptionsPanelController;
import org.sleuthkit.autopsy.casemodule.GeneralFilter;
import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
import org.sleuthkit.autopsy.guiutils.JFileChooserFactory;
/**
* An options panel for the user to create, edit, and delete associations for
@ -42,9 +43,13 @@ import org.sleuthkit.autopsy.coreutils.PlatformUtil;
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
final class ExternalViewerGlobalSettingsPanel extends javax.swing.JPanel implements OptionsPanel {
private ExternalViewerGlobalSettingsTableModel tableModel;
private static final long serialVersionUID = 1L;
public ExternalViewerGlobalSettingsPanel() {
private ExternalViewerGlobalSettingsTableModel tableModel;
private final JFileChooserFactory chooserHelper = new JFileChooserFactory();
ExternalViewerGlobalSettingsPanel() {
this(new ExternalViewerGlobalSettingsTableModel(new String[] {
"Mime type/Extension", "Application"}));
}
@ -52,7 +57,7 @@ final class ExternalViewerGlobalSettingsPanel extends javax.swing.JPanel impleme
/**
* Creates new form ExternalViewerGlobalSettingsPanel
*/
public ExternalViewerGlobalSettingsPanel(ExternalViewerGlobalSettingsTableModel tableModel) {
ExternalViewerGlobalSettingsPanel(ExternalViewerGlobalSettingsTableModel tableModel) {
initComponents();
this.tableModel = tableModel;
customizeComponents(tableModel);
@ -335,7 +340,7 @@ final class ExternalViewerGlobalSettingsPanel extends javax.swing.JPanel impleme
}//GEN-LAST:event_deleteRuleButtonActionPerformed
private void browseHxDDirectoryActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browseHxDDirectoryActionPerformed
JFileChooser fileWindow = new JFileChooser();
JFileChooser fileWindow = chooserHelper.getChooser();
fileWindow.setFileSelectionMode(JFileChooser.FILES_ONLY);
GeneralFilter exeFilter = new GeneralFilter(GeneralFilter.EXECUTABLE_EXTS, GeneralFilter.EXECUTABLE_DESC);
File HxDPathFile = new File(HxDPath.getText());

View File

@ -33,9 +33,6 @@ import java.util.List;
import java.util.Set;
import java.util.HashSet;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.logging.Level;
import javax.swing.AbstractAction;
import javax.swing.JFileChooser;
@ -50,6 +47,7 @@ import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.guiutils.JFileChooserFactory;
import org.sleuthkit.datamodel.AbstractContent;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Content;
@ -78,9 +76,7 @@ final class ExtractUnallocAction extends AbstractAction {
private final Volume volume;
private final Image image;
private final FutureTask<JFileChooser> futureFileChooser = new FutureTask<>(CustomFileChooser::new);
private JFileChooser fileChooser = null;
private final JFileChooserFactory chooserFactory;
/**
* Create an instance of ExtractUnallocAction with a volume.
@ -90,7 +86,7 @@ final class ExtractUnallocAction extends AbstractAction {
*/
ExtractUnallocAction(String title, Volume volume) {
this(title, null, volume);
}
/**
@ -110,9 +106,8 @@ final class ExtractUnallocAction extends AbstractAction {
this.volume = null;
this.image = image;
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(futureFileChooser);
chooserFactory = new JFileChooserFactory(CustomFileChooser.class);
}
/**
@ -138,13 +133,7 @@ final class ExtractUnallocAction extends AbstractAction {
return;
}
if (fileChooser == null) {
try {
fileChooser = futureFileChooser.get();
} catch (InterruptedException | ExecutionException ex) {
fileChooser = new CustomFileChooser();
}
}
JFileChooser fileChooser = chooserFactory.getChooser();
fileChooser.setCurrentDirectory(new File(getExportDirectory(openCase)));
if (JFileChooser.APPROVE_OPTION != fileChooser.showSaveDialog((Component) event.getSource())) {
@ -753,11 +742,11 @@ final class ExtractUnallocAction extends AbstractAction {
}
// A Custome JFileChooser for this Action Class.
private class CustomFileChooser extends JFileChooser {
public static class CustomFileChooser extends JFileChooser {
private static final long serialVersionUID = 1L;
CustomFileChooser() {
public CustomFileChooser() {
initalize();
}

View File

@ -1,15 +1,15 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011 Basis Technology Corp.
*
* Copyright 2011-2021 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.
@ -28,14 +28,26 @@ import org.openide.util.Lookup;
*/
public class FileSearchAction extends AbstractAction {
private final Long dataSourceId;
public FileSearchAction(String title, long dataSourceID) {
super(title);
dataSourceId = dataSourceID;
}
public FileSearchAction(String title) {
super(title);
dataSourceId = null;
}
@Override
public void actionPerformed(ActionEvent e) {
FileSearchProvider searcher = Lookup.getDefault().lookup(FileSearchProvider.class);
searcher.showDialog();
if (dataSourceId == null) {
searcher.showDialog();
} else {
searcher.showDialog(dataSourceId);
}
}
}

View File

@ -1,15 +1,15 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011 Basis Technology Corp.
*
* Copyright 2011-2021 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.
@ -23,5 +23,8 @@ package org.sleuthkit.autopsy.directorytree;
*/
public interface FileSearchProvider {
public void showDialog(Long dataSourceID);
@Deprecated
public void showDialog();
}

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2013-2019 Basis Technology Corp.
* Copyright 2013-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -42,6 +42,7 @@ import org.sleuthkit.autopsy.coreutils.FileUtil;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.autopsy.guiutils.JFileChooserFactory;
import org.sleuthkit.datamodel.AbstractFile;
/**
@ -51,6 +52,9 @@ public class ExtractActionHelper {
private final Logger logger = Logger.getLogger(ExtractActionHelper.class.getName());
private String userDefinedExportPath;
private final JFileChooserFactory extractFileHelper = new JFileChooserFactory();
private final JFileChooserFactory extractFilesHelper = new JFileChooserFactory();
/**
* Extract the specified collection of files with an event specified for
@ -89,7 +93,7 @@ public class ExtractActionHelper {
logger.log(Level.INFO, "Exception while getting open case.", ex); //NON-NLS
return;
}
JFileChooser fileChooser = new JFileChooser();
JFileChooser fileChooser = extractFileHelper.getChooser();
fileChooser.setCurrentDirectory(new File(getExportDirectory(openCase)));
// If there is an attribute name, change the ":". Otherwise the extracted file will be hidden
fileChooser.setSelectedFile(new File(FileUtil.escapeFileName(selectedFile.getName())));
@ -117,7 +121,7 @@ public class ExtractActionHelper {
logger.log(Level.INFO, "Exception while getting open case.", ex); //NON-NLS
return;
}
JFileChooser folderChooser = new JFileChooser();
JFileChooser folderChooser = extractFilesHelper.getChooser();
folderChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
folderChooser.setCurrentDirectory(new File(getExportDirectory(openCase)));
if (folderChooser.showSaveDialog((Component) event.getSource()) == JFileChooser.APPROVE_OPTION) {

View File

@ -1,7 +1,7 @@
/*
* Autopsy
*
* Copyright 2020 Basis Technology Corp.
* Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -54,14 +54,18 @@ public abstract class AbstractFilter {
* @param caseDb The case database
* @param centralRepoDb The central repo database. Can be null if the
* filter does not require it.
* @param context The SearchContext the search which is applying this
* filter is being performed from.
*
* @return The list of results that match this filter (and any that came
* before it)
*
* @throws DiscoveryException
* @throws SearchCancellationException Thrown when the user has cancelled
* the search.
*/
public List<Result> applyAlternateFilter(List<Result> currentResults, SleuthkitCase caseDb,
CentralRepository centralRepoDb) throws DiscoveryException {
CentralRepository centralRepoDb, SearchContext context) throws DiscoveryException, SearchCancellationException {
return new ArrayList<>();
}

View File

@ -1,7 +1,7 @@
/*
* Autopsy
*
* Copyright 2020 Basis Technology Corp.
* Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -78,10 +78,14 @@ public class DiscoveryAttributes {
* @param caseDb The case database.
* @param centralRepoDb The central repository database. Can be null if
* not needed.
* @param context The SearchContext the search which is applying
* this filter is being performed from.
*
* @throws DiscoveryException
* @throws SearchCancellationException - Thrown when the user has
* cancelled the search.
*/
public void addAttributeToResults(List<Result> results, SleuthkitCase caseDb, CentralRepository centralRepoDb) throws DiscoveryException {
public void addAttributeToResults(List<Result> results, SleuthkitCase caseDb, CentralRepository centralRepoDb, SearchContext context) throws DiscoveryException, SearchCancellationException {
// Default is to do nothing
}
}
@ -154,10 +158,13 @@ public class DiscoveryAttributes {
@Override
public void addAttributeToResults(List<Result> results, SleuthkitCase caseDb,
CentralRepository centralRepoDb) throws DiscoveryException {
CentralRepository centralRepoDb, SearchContext context) throws DiscoveryException, SearchCancellationException {
try {
Map<String, Set<String>> domainsToCategories = getDomainsWithWebCategories(caseDb);
Map<String, Set<String>> domainsToCategories = getDomainsWithWebCategories(caseDb, context);
for (Result result : results) {
if (context.searchIsCancelled()) {
throw new SearchCancellationException("The search was cancelled while Domain Category Attribute was being added.");
}
if (result instanceof ResultDomain) {
ResultDomain domain = (ResultDomain) result;
domain.addWebCategories(domainsToCategories.get(domain.getDomain()));
@ -172,14 +179,29 @@ public class DiscoveryAttributes {
* Loads all TSK_WEB_CATEGORY artifacts and maps the domain attribute to
* the category name attribute. Each ResultDomain is then parsed and
* matched against this map of values.
*
* @param caseDb The case database.
* @param context The SearchContext the search which is applying this
* filter is being performed from.
*
* @return domainToCategory - A map of the domain names to the category
* name attribute they are classified as.
*
* @throws TskCoreException
* @throws InterruptedException
* @throws SearchCancellationException - Thrown when the user has
* cancelled the search.
*/
private Map<String, Set<String>> getDomainsWithWebCategories(SleuthkitCase caseDb) throws TskCoreException, InterruptedException {
private Map<String, Set<String>> getDomainsWithWebCategories(SleuthkitCase caseDb, SearchContext context) throws TskCoreException, InterruptedException, SearchCancellationException {
Map<String, Set<String>> domainToCategory = new HashMap<>();
for (BlackboardArtifact artifact : caseDb.getBlackboardArtifacts(TSK_WEB_CATEGORIZATION)) {
if (Thread.currentThread().isInterrupted()) {
throw new InterruptedException();
}
if (context.searchIsCancelled()) {
throw new SearchCancellationException("Search was cancelled while getting domains for artifact type: " + artifact.getDisplayName());
}
BlackboardAttribute webCategory = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME));
BlackboardAttribute domain = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN));
if (webCategory != null && domain != null) {
@ -206,14 +228,16 @@ public class DiscoveryAttributes {
@Override
public void addAttributeToResults(List<Result> results, SleuthkitCase caseDb,
CentralRepository centralRepoDb) throws DiscoveryException {
CentralRepository centralRepoDb, SearchContext context) throws DiscoveryException, SearchCancellationException {
// Get pairs of (object ID, keyword list name) for all files in the list of files that have
// keyword list hits.
String selectQuery = createSetNameClause(results, BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID(),
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID());
SetKeywordListNamesCallback callback = new SetKeywordListNamesCallback(results);
if (context.searchIsCancelled()) {
throw new SearchCancellationException("The search was cancelled while Keyword List Attribute was being added.");
}
try {
caseDb.getCaseDbAccessManager().select(selectQuery, callback);
} catch (TskCoreException ex) {
@ -278,8 +302,20 @@ public class DiscoveryAttributes {
* Example: query for notable status of google.com. Result: notable With
* this map, all domain instances that represent google.com can be updated
* after one simple lookup.
*
* @param domainsBatch The list of ResultDomains to organize.
* @param attributeType The type of correlation attribute being organized.
* @param context The SearchContext the search which is applying this
* filter is being performed from.
*
* @return resultDomainTable - A map of the normalized domain name to the
* list of ResultDomain objects which are part of that normalized
* domain.
*
* @throws SearchCancellationException - Thrown when the user has cancelled
* the search.
*/
private static Map<String, List<ResultDomain>> organizeByValue(List<ResultDomain> domainsBatch, CorrelationAttributeInstance.Type attributeType) {
private static Map<String, List<ResultDomain>> organizeByValue(List<ResultDomain> domainsBatch, CorrelationAttributeInstance.Type attributeType, SearchContext context) throws SearchCancellationException {
final Map<String, List<ResultDomain>> resultDomainTable = new HashMap<>();
for (ResultDomain domainInstance : domainsBatch) {
try {
@ -288,6 +324,9 @@ public class DiscoveryAttributes {
final List<ResultDomain> bucket = resultDomainTable.getOrDefault(normalizedDomain, new ArrayList<>());
bucket.add(domainInstance);
resultDomainTable.put(normalizedDomain, bucket);
if (context.searchIsCancelled()) {
throw new SearchCancellationException("Search was cancelled while orgainizing domains by their normalized value.");
}
} catch (CorrelationAttributeNormalizationException ex) {
logger.log(Level.INFO, String.format("Domain [%s] failed normalization, skipping...", domainInstance.getDomain()));
}
@ -322,39 +361,73 @@ public class DiscoveryAttributes {
@Override
public void addAttributeToResults(List<Result> results, SleuthkitCase caseDb,
CentralRepository centralRepoDb) throws DiscoveryException {
CentralRepository centralRepoDb, SearchContext context) throws DiscoveryException, SearchCancellationException {
if (centralRepoDb != null) {
processFilesWithCr(results, centralRepoDb);
processFilesWithCr(results, centralRepoDb, context);
}
}
private void processFilesWithCr(List<Result> results, CentralRepository centralRepo) throws DiscoveryException {
/**
* Helper method to batch the domain results and check for notability.
*
* @param results The results which are being checked for previously
* being notable in the CR.
* @param centralRepo The central repository being used to check for
* notability.
* @param context The SearchContext the search which is applying
* this filter is being performed from.
*
* @throws DiscoveryException
* @throws SearchCancellationException - Thrown when the user has
* cancelled the search.
*/
private void processFilesWithCr(List<Result> results, CentralRepository centralRepo, SearchContext context) throws DiscoveryException, SearchCancellationException {
List<ResultDomain> domainsBatch = new ArrayList<>();
for (Result result : results) {
if (context.searchIsCancelled()) {
throw new SearchCancellationException("The search was cancelled while Previously Notable attribute was being calculated with the CR.");
}
if (result.getType() == SearchData.Type.DOMAIN) {
domainsBatch.add((ResultDomain) result);
if (domainsBatch.size() == DOMAIN_BATCH_SIZE) {
queryPreviouslyNotable(domainsBatch, centralRepo);
queryPreviouslyNotable(domainsBatch, centralRepo, context);
domainsBatch.clear();
}
}
}
queryPreviouslyNotable(domainsBatch, centralRepo);
queryPreviouslyNotable(domainsBatch, centralRepo, context);
}
private void queryPreviouslyNotable(List<ResultDomain> domainsBatch, CentralRepository centralRepo) throws DiscoveryException {
/**
* Helper method to check a batch of domains for notability.
*
*
* @param domainsBatch The list of ResultDomains to check for
* notability.
* @param centralRepo The central repository being used to check for
* notability.
* @param context The SearchContext the search which is applying
* this filter is being performed from.
*
* @throws DiscoveryException
* @throws SearchCancellationException - Thrown when the user has
* cancelled the search.
*/
private void queryPreviouslyNotable(List<ResultDomain> domainsBatch, CentralRepository centralRepo, SearchContext context) throws DiscoveryException, SearchCancellationException {
if (domainsBatch.isEmpty()) {
return;
}
try {
final CorrelationAttributeInstance.Type attributeType = centralRepo.getCorrelationTypeById(CorrelationAttributeInstance.DOMAIN_TYPE_ID);
final Map<String, List<ResultDomain>> resultDomainTable = organizeByValue(domainsBatch, attributeType);
final Map<String, List<ResultDomain>> resultDomainTable = organizeByValue(domainsBatch, attributeType, context);
final String values = createCSV(resultDomainTable.keySet());
if (context.searchIsCancelled()) {
throw new SearchCancellationException("Search was cancelled while checking for previously notable domains.");
}
final String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(attributeType);
final String domainFrequencyQuery = " value AS domain_name "
+ "FROM " + tableName + " "
@ -421,7 +494,7 @@ public class DiscoveryAttributes {
@Override
public void addAttributeToResults(List<Result> results, SleuthkitCase caseDb,
CentralRepository centralRepoDb) throws DiscoveryException {
CentralRepository centralRepoDb, SearchContext context) throws DiscoveryException, SearchCancellationException {
if (centralRepoDb == null) {
for (Result result : results) {
if (result.getFrequency() == SearchData.Frequency.UNKNOWN && result.getKnown() == TskData.FileKnown.KNOWN) {
@ -429,7 +502,7 @@ public class DiscoveryAttributes {
}
}
} else {
processResultFilesForCR(results, centralRepoDb);
processResultFilesForCR(results, centralRepoDb, context);
}
}
@ -437,16 +510,26 @@ public class DiscoveryAttributes {
* Private helper method for adding Frequency attribute when CR is
* enabled.
*
* @param files The list of ResultFiles to caluclate frequency
* for.
* @param centralRepoDb The central repository currently in use.
* @param results The results which are having their frequency
* checked.
* @param centralRepoDb The central repository being used to check
* frequency.
* @param context The SearchContext the search which is applying
* this filter is being performed from.
*
* @throws DiscoveryException
* @throws SearchCancellationException - Thrown when the user has
* cancelled the search.
*/
private void processResultFilesForCR(List<Result> results,
CentralRepository centralRepoDb) throws DiscoveryException {
CentralRepository centralRepoDb, SearchContext context) throws DiscoveryException, SearchCancellationException {
List<ResultFile> currentFiles = new ArrayList<>();
Set<String> hashesToLookUp = new HashSet<>();
List<ResultDomain> domainsToQuery = new ArrayList<>();
for (Result result : results) {
if (context.searchIsCancelled()) {
throw new SearchCancellationException("The search was cancelled while Frequency attribute was being calculated with the CR.");
}
// If frequency was already calculated, skip...
if (result.getFrequency() == SearchData.Frequency.UNKNOWN) {
if (result.getKnown() == TskData.FileKnown.KNOWN) {
@ -462,7 +545,7 @@ public class DiscoveryAttributes {
}
if (hashesToLookUp.size() >= BATCH_SIZE) {
computeFrequency(hashesToLookUp, currentFiles, centralRepoDb);
computeFrequency(hashesToLookUp, currentFiles, centralRepoDb, context);
hashesToLookUp.clear();
currentFiles.clear();
@ -470,16 +553,15 @@ public class DiscoveryAttributes {
} else {
domainsToQuery.add((ResultDomain) result);
if (domainsToQuery.size() == DOMAIN_BATCH_SIZE) {
queryDomainFrequency(domainsToQuery, centralRepoDb);
queryDomainFrequency(domainsToQuery, centralRepoDb, context);
domainsToQuery.clear();
}
}
}
}
queryDomainFrequency(domainsToQuery, centralRepoDb);
computeFrequency(hashesToLookUp, currentFiles, centralRepoDb);
queryDomainFrequency(domainsToQuery, centralRepoDb, context);
computeFrequency(hashesToLookUp, currentFiles, centralRepoDb, context);
}
}
@ -487,17 +569,22 @@ public class DiscoveryAttributes {
* Query to get the frequency of a domain.
*
* @param domainsToQuery List of domains to check the frequency of.
* @param centralRepository The central repository to query.
* @param centralRepository The central repository being used to check
* frequency.
* @param context The SearchContext the search which is applying
* this filter is being performed from.
*
* @throws DiscoveryException
* @throws SearchCancellationException - Thrown when the user has cancelled
* the search.
*/
private static void queryDomainFrequency(List<ResultDomain> domainsToQuery, CentralRepository centralRepository) throws DiscoveryException {
private static void queryDomainFrequency(List<ResultDomain> domainsToQuery, CentralRepository centralRepository, SearchContext context) throws DiscoveryException, SearchCancellationException {
if (domainsToQuery.isEmpty()) {
return;
}
try {
final CorrelationAttributeInstance.Type attributeType = centralRepository.getCorrelationTypeById(CorrelationAttributeInstance.DOMAIN_TYPE_ID);
final Map<String, List<ResultDomain>> resultDomainTable = organizeByValue(domainsToQuery, attributeType);
final Map<String, List<ResultDomain>> resultDomainTable = organizeByValue(domainsToQuery, attributeType, context);
final String values = createCSV(resultDomainTable.keySet());
final String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(attributeType);
final String domainFrequencyQuery = " value AS domain_name, COUNT(value) AS frequency FROM"
@ -508,8 +595,11 @@ public class DiscoveryAttributes {
+ ")) AS foo GROUP BY value";
final DomainFrequencyCallback frequencyCallback = new DomainFrequencyCallback(resultDomainTable);
centralRepository.processSelectClause(domainFrequencyQuery, frequencyCallback);
centralRepository.processSelectClause(domainFrequencyQuery, frequencyCallback);
if (context.searchIsCancelled()) {
throw new SearchCancellationException("The search was cancelled while Domain frequency was being queried with the CR.");
}
if (frequencyCallback.getCause() != null) {
throw frequencyCallback.getCause();
}
@ -620,7 +710,7 @@ public class DiscoveryAttributes {
@Override
public void addAttributeToResults(List<Result> results, SleuthkitCase caseDb,
CentralRepository centralRepoDb) throws DiscoveryException {
CentralRepository centralRepoDb, SearchContext context) throws DiscoveryException, SearchCancellationException {
// Get pairs of (object ID, hash set name) for all files in the list of files that have
// hash set hits.
@ -628,6 +718,9 @@ public class DiscoveryAttributes {
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID());
HashSetNamesCallback callback = new HashSetNamesCallback(results);
if (context.searchIsCancelled()) {
throw new SearchCancellationException("The search was cancelled while Hash Hit attribute was being added.");
}
try {
caseDb.getCaseDbAccessManager().select(selectQuery, callback);
} catch (TskCoreException ex) {
@ -695,7 +788,7 @@ public class DiscoveryAttributes {
@Override
public void addAttributeToResults(List<Result> results, SleuthkitCase caseDb,
CentralRepository centralRepoDb) throws DiscoveryException {
CentralRepository centralRepoDb, SearchContext context) throws DiscoveryException, SearchCancellationException {
// Get pairs of (object ID, interesting item set name) for all files in the list of files that have
// interesting file set hits.
@ -703,6 +796,9 @@ public class DiscoveryAttributes {
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID());
InterestingFileSetNamesCallback callback = new InterestingFileSetNamesCallback(results);
if (context.searchIsCancelled()) {
throw new SearchCancellationException("The search was cancelled while Interesting Item attribute was being added.");
}
try {
caseDb.getCaseDbAccessManager().select(selectQuery, callback);
} catch (TskCoreException ex) {
@ -808,7 +904,7 @@ public class DiscoveryAttributes {
@Override
public void addAttributeToResults(List<Result> results, SleuthkitCase caseDb,
CentralRepository centralRepoDb) throws DiscoveryException {
CentralRepository centralRepoDb, SearchContext context) throws DiscoveryException, SearchCancellationException {
// Get pairs of (object ID, object type name) for all files in the list of files that have
// objects detected
@ -816,6 +912,9 @@ public class DiscoveryAttributes {
BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION.getTypeID());
ObjectDetectedNamesCallback callback = new ObjectDetectedNamesCallback(results);
if (context.searchIsCancelled()) {
throw new SearchCancellationException("The search was cancelled while Object Detected attribute was being added.");
}
try {
caseDb.getCaseDbAccessManager().select(selectQuery, callback);
} catch (TskCoreException ex) {
@ -884,10 +983,13 @@ public class DiscoveryAttributes {
@Override
public void addAttributeToResults(List<Result> results, SleuthkitCase caseDb,
CentralRepository centralRepoDb) throws DiscoveryException {
CentralRepository centralRepoDb, SearchContext context) throws DiscoveryException, SearchCancellationException {
try {
for (Result result : results) {
if (context.searchIsCancelled()) {
throw new SearchCancellationException("The search was cancelled while File Tag attribute was being added.");
}
if (result.getType() == SearchData.Type.DOMAIN) {
return;
}
@ -995,14 +1097,20 @@ public class DiscoveryAttributes {
}
/**
*
* Computes the CR frequency of all the given hashes and updates the list of
* files.
*
* @param hashesToLookUp Hashes to find the frequency of.
* @param currentFiles List of files to update with frequencies.
* @param centralRepoDb The central repository being used.
* @param context The SearchContext the search which is applying this
* filter is being performed from.
*
* @throws SearchCancellationException - Thrown when the user has cancelled
* the search.
*/
private static void computeFrequency(Set<String> hashesToLookUp, List<ResultFile> currentFiles, CentralRepository centralRepoDb) {
private static void computeFrequency(Set<String> hashesToLookUp, List<ResultFile> currentFiles, CentralRepository centralRepoDb, SearchContext context) throws SearchCancellationException {
if (hashesToLookUp.isEmpty()) {
return;
@ -1022,7 +1130,9 @@ public class DiscoveryAttributes {
FrequencyCallback callback = new FrequencyCallback(currentFiles);
centralRepoDb.processSelectClause(selectClause, callback);
if (context.searchIsCancelled()) {
throw new SearchCancellationException("The search was cancelled while Domain frequency was being queried with the CR.");
}
} catch (CentralRepoException ex) {
logger.log(Level.WARNING, "Error getting frequency counts from Central Repository", ex); // NON-NLS
}

View File

@ -59,6 +59,7 @@ public class DiscoveryKeyUtils {
private final List<AbstractFilter> filters;
private final SleuthkitCase sleuthkitCase;
private final CentralRepository centralRepository;
private final SearchContext context;
/**
* Construct a new SearchKey with all information that defines a search.
@ -70,16 +71,20 @@ public class DiscoveryKeyUtils {
* @param sortingMethod The method to sort the results by.
* @param sleuthkitCase The SleuthkitCase being searched.
* @param centralRepository The Central Repository being searched.
* @param context The SearchContext which reflects the search
* being performed to get results for this
* key.
*/
SearchKey(String userName, List<AbstractFilter> filters,
DiscoveryAttributes.AttributeType groupAttributeType,
Group.GroupSortingAlgorithm groupSortingType,
ResultsSorter.SortingMethod sortingMethod,
SleuthkitCase sleuthkitCase, CentralRepository centralRepository) {
SleuthkitCase sleuthkitCase, CentralRepository centralRepository, SearchContext context) {
this.groupAttributeType = groupAttributeType;
this.groupSortingType = groupSortingType;
this.sortingMethod = sortingMethod;
this.filters = filters;
this.context = context;
StringBuilder searchStringBuilder = new StringBuilder();
searchStringBuilder.append(userName);
@ -93,8 +98,8 @@ public class DiscoveryKeyUtils {
}
/**
* Construct a SearchKey without a SleuthkitCase or CentralRepositry
* instance.
* Construct a SearchKey without a SearchContext, SleuthkitCase or
* CentralRepositry instance.
*
* @param userName The name of the user performing the search.
* @param filters The Filters being used for the search.
@ -107,7 +112,8 @@ public class DiscoveryKeyUtils {
Group.GroupSortingAlgorithm groupSortingType,
ResultsSorter.SortingMethod sortingMethod) {
this(userName, filters, groupAttributeType, groupSortingType,
sortingMethod, null, null);
sortingMethod, null, null, null);
//this constructor should only be used putting things directly into a map or getting if present since casedb, cr, and search context will be null
}
@Override
@ -141,6 +147,23 @@ public class DiscoveryKeyUtils {
return hash;
}
/**
* Get the SearchContext for the search this key is being used in.
*
* @return The SearchContext the search key is being used in.
*
* @throws DiscoveryException Thrown when the key being used has a null
* context indicating it was not created with
* knowledge of the case or central
* repository databases.
*/
SearchContext getContext() throws DiscoveryException {
if (context == null) {
throw new DiscoveryException("The key in use was created without a context and does not support retrieving information from the databases.");
}
return context;
}
/**
* Get the String representation of this key.
*

View File

@ -78,24 +78,31 @@ public class DomainSearch {
* @param caseDb The case database.
* @param centralRepoDb The central repository database. Can be null
* if not needed.
* @param context The SearchContext the search is being performed from.
*
* @return A LinkedHashMap grouped and sorted according to the parameters.
*
* @throws DiscoveryException
* @throws SearchCancellationException - Thrown when the user has cancelled
* the search.
*/
public Map<GroupKey, Integer> getGroupSizes(String userName,
List<AbstractFilter> filters,
DiscoveryAttributes.AttributeType groupAttributeType,
Group.GroupSortingAlgorithm groupSortingType,
ResultsSorter.SortingMethod domainSortingMethod,
SleuthkitCase caseDb, CentralRepository centralRepoDb) throws DiscoveryException {
SleuthkitCase caseDb, CentralRepository centralRepoDb, SearchContext context) throws DiscoveryException, SearchCancellationException {
final Map<GroupKey, List<Result>> searchResults = searchCache.get(
userName, filters, groupAttributeType, groupSortingType,
domainSortingMethod, caseDb, centralRepoDb);
domainSortingMethod, caseDb, centralRepoDb, context);
// Transform the cached results into a map of group key to group size.
final LinkedHashMap<GroupKey, Integer> groupSizes = new LinkedHashMap<>();
for (GroupKey groupKey : searchResults.keySet()) {
if (context.searchIsCancelled()) {
throw new SearchCancellationException("The search was cancelled before group sizes were finished being calculated");
}
groupSizes.put(groupKey, searchResults.get(groupKey).size());
}
@ -130,11 +137,11 @@ public class DomainSearch {
Group.GroupSortingAlgorithm groupSortingType,
ResultsSorter.SortingMethod domainSortingMethod,
GroupKey groupKey, int startingEntry, int numberOfEntries,
SleuthkitCase caseDb, CentralRepository centralRepoDb) throws DiscoveryException {
SleuthkitCase caseDb, CentralRepository centralRepoDb, SearchContext context) throws DiscoveryException, SearchCancellationException {
final Map<GroupKey, List<Result>> searchResults = searchCache.get(
userName, filters, groupAttributeType, groupSortingType,
domainSortingMethod, caseDb, centralRepoDb);
domainSortingMethod, caseDb, centralRepoDb, context);
final List<Result> domainsInGroup = searchResults.get(groupKey);
final List<Result> page = new ArrayList<>();
for (int i = startingEntry; (i < startingEntry + numberOfEntries)

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -55,20 +55,24 @@ class DomainSearchCache {
* @param caseDb The case database.
* @param centralRepoDb The central repository database. Can be null if
* not needed.
* @param context The SearchContext the search is being performed
* from.
*
* @return Domain search results matching the given parameters.
*
* @throws DiscoveryException
* @throws SearchCancellationException - Thrown when the user has cancelled
* the search.
*/
Map<GroupKey, List<Result>> get(String userName,
List<AbstractFilter> filters,
DiscoveryAttributes.AttributeType groupAttributeType,
Group.GroupSortingAlgorithm groupSortingType,
ResultsSorter.SortingMethod domainSortingMethod,
SleuthkitCase caseDb, CentralRepository centralRepoDb) throws DiscoveryException {
SleuthkitCase caseDb, CentralRepository centralRepoDb, SearchContext context) throws DiscoveryException, SearchCancellationException {
try {
final SearchKey searchKey = new SearchKey(userName, filters, groupAttributeType,
groupSortingType, domainSortingMethod, caseDb, centralRepoDb);
groupSortingType, domainSortingMethod, caseDb, centralRepoDb, context);
return cache.get(searchKey);
} catch (ExecutionException ex) {
throw new DiscoveryException("Error fetching results from cache", ex.getCause());

View File

@ -73,7 +73,7 @@ class DomainSearchCacheLoader extends CacheLoader<SearchKey, Map<GroupKey, List<
throw new InterruptedException();
}
attr.addAttributeToResults(domainResults,
key.getSleuthkitCase(), key.getCentralRepository());
key.getSleuthkitCase(), key.getCentralRepository(), key.getContext());
}
// Apply secondary in memory filters
for (AbstractFilter filter : key.getFilters()) {
@ -81,7 +81,7 @@ class DomainSearchCacheLoader extends CacheLoader<SearchKey, Map<GroupKey, List<
throw new InterruptedException();
}
if (filter.useAlternateFilter()) {
domainResults = filter.applyAlternateFilter(domainResults, key.getSleuthkitCase(), key.getCentralRepository());
domainResults = filter.applyAlternateFilter(domainResults, key.getSleuthkitCase(), key.getCentralRepository(), key.getContext());
}
}
// Sort the ResultDomains by the requested criteria.

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019-2020 Basis Technology Corp.
* Copyright 2019-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -62,17 +62,21 @@ public class FileSearch {
* @param caseDb The case database
* @param centralRepoDb The central repository database. Can be null if
* not needed.
* @param context The SearchContext the search is being performed
* from.
*
* @return The raw search results
*
* @throws DiscoveryException
* @throws SearchCancellationException - Thrown when the user has cancelled
* the search.
*/
static SearchResults runFileSearchDebug(String userName,
List<AbstractFilter> filters,
AttributeType groupAttributeType,
Group.GroupSortingAlgorithm groupSortingType,
ResultsSorter.SortingMethod fileSortingMethod,
SleuthkitCase caseDb, CentralRepository centralRepoDb) throws DiscoveryException {
SleuthkitCase caseDb, CentralRepository centralRepoDb, SearchContext context) throws DiscoveryException, SearchCancellationException {
// Make a list of attributes that we want to add values for. This ensures the
// ResultFile objects will have all needed fields set when it's time to group
// and sort them. For example, if we're grouping by central repo frequency, we need
@ -82,10 +86,10 @@ public class FileSearch {
attributesNeededForGroupingOrSorting.addAll(fileSortingMethod.getRequiredAttributes());
// Run the queries for each filter
List<Result> results = SearchFiltering.runQueries(filters, caseDb, centralRepoDb);
List<Result> results = SearchFiltering.runQueries(filters, caseDb, centralRepoDb, context);
// Add the data to resultFiles for any attributes needed for sorting and grouping
addAttributes(attributesNeededForGroupingOrSorting, results, caseDb, centralRepoDb);
addAttributes(attributesNeededForGroupingOrSorting, results, caseDb, centralRepoDb, context);
// Collect everything in the search results
SearchResults searchResults = new SearchResults(groupSortingType, groupAttributeType, fileSortingMethod);
@ -114,21 +118,28 @@ public class FileSearch {
* @param caseDb The case database
* @param centralRepoDb The central repository database. Can be null if
* not needed.
* @param context The SearchContext the search is being performed
* from.
*
* @return A LinkedHashMap grouped and sorted according to the parameters
*
* @throws DiscoveryException
* @throws SearchCancellationException - Thrown when the user has cancelled
* the search.
*/
public static Map<GroupKey, Integer> getGroupSizes(String userName,
List<AbstractFilter> filters,
AttributeType groupAttributeType,
Group.GroupSortingAlgorithm groupSortingType,
ResultsSorter.SortingMethod fileSortingMethod,
SleuthkitCase caseDb, CentralRepository centralRepoDb) throws DiscoveryException {
SleuthkitCase caseDb, CentralRepository centralRepoDb, SearchContext context) throws DiscoveryException, SearchCancellationException {
Map<GroupKey, List<Result>> searchResults = runFileSearch(userName, filters,
groupAttributeType, groupSortingType, fileSortingMethod, caseDb, centralRepoDb);
groupAttributeType, groupSortingType, fileSortingMethod, caseDb, centralRepoDb, context);
LinkedHashMap<GroupKey, Integer> groupSizes = new LinkedHashMap<>();
for (GroupKey groupKey : searchResults.keySet()) {
if (context.searchIsCancelled()) {
throw new SearchCancellationException("The search was cancelled before group sizes were finished being calculated");
}
groupSizes.put(groupKey, searchResults.get(groupKey).size());
}
return groupSizes;
@ -151,10 +162,14 @@ public class FileSearch {
* @param caseDb The case database
* @param centralRepoDb The central repository database. Can be null if
* not needed.
* @param context The SearchContext the search is being performed
* from.
*
* @return A LinkedHashMap grouped and sorted according to the parameters
*
* @throws DiscoveryException
* @throws SearchCancellationException - Thrown when the user has cancelled
* the search.
*/
public static List<Result> getFilesInGroup(String userName,
List<AbstractFilter> filters,
@ -164,7 +179,7 @@ public class FileSearch {
GroupKey groupKey,
int startingEntry,
int numberOfEntries,
SleuthkitCase caseDb, CentralRepository centralRepoDb) throws DiscoveryException {
SleuthkitCase caseDb, CentralRepository centralRepoDb, SearchContext context) throws DiscoveryException, SearchCancellationException {
//the group should be in the cache at this point
List<Result> filesInGroup = null;
SearchKey searchKey = new SearchKey(userName, filters, groupAttributeType, groupSortingType, fileSortingMethod);
@ -178,7 +193,7 @@ public class FileSearch {
List<Result> page = new ArrayList<>();
if (filesInGroup == null) {
logger.log(Level.INFO, "Group {0} was not cached, performing search to cache all groups again", groupKey);
runFileSearch(userName, filters, groupAttributeType, groupSortingType, fileSortingMethod, caseDb, centralRepoDb);
runFileSearch(userName, filters, groupAttributeType, groupSortingType, fileSortingMethod, caseDb, centralRepoDb, context);
synchronized (searchCache) {
resultsMap = searchCache.getIfPresent(searchKey.getKeyString());
}
@ -218,7 +233,6 @@ public class FileSearch {
TextSummarizer localSummarizer;
synchronized (searchCache) {
localSummarizer = SummaryHelpers.getLocalSummarizer();
}
if (localSummarizer != null) {
try {
@ -247,17 +261,21 @@ public class FileSearch {
* @param caseDb The case database
* @param centralRepoDb The central repository database. Can be null if
* not needed.
* @param context The SearchContext the search is being performed
* from.
*
* @return A LinkedHashMap grouped and sorted according to the parameters
*
* @throws DiscoveryException
* @throws SearchCancellationException - Thrown when the user has cancelled
* the search.
*/
private static Map<GroupKey, List<Result>> runFileSearch(String userName,
public static Map<GroupKey, List<Result>> runFileSearch(String userName,
List<AbstractFilter> filters,
AttributeType groupAttributeType,
Group.GroupSortingAlgorithm groupSortingType,
ResultsSorter.SortingMethod fileSortingMethod,
SleuthkitCase caseDb, CentralRepository centralRepoDb) throws DiscoveryException {
SleuthkitCase caseDb, CentralRepository centralRepoDb, SearchContext context) throws DiscoveryException, SearchCancellationException {
// Make a list of attributes that we want to add values for. This ensures the
// ResultFile objects will have all needed fields set when it's time to group
@ -268,10 +286,10 @@ public class FileSearch {
attributesNeededForGroupingOrSorting.addAll(fileSortingMethod.getRequiredAttributes());
// Run the queries for each filter
List<Result> results = SearchFiltering.runQueries(filters, caseDb, centralRepoDb);
List<Result> results = SearchFiltering.runQueries(filters, caseDb, centralRepoDb, context);
// Add the data to resultFiles for any attributes needed for sorting and grouping
addAttributes(attributesNeededForGroupingOrSorting, results, caseDb, centralRepoDb);
addAttributes(attributesNeededForGroupingOrSorting, results, caseDb, centralRepoDb, context);
// Collect everything in the search results
SearchResults searchResults = new SearchResults(groupSortingType, groupAttributeType, fileSortingMethod);
@ -295,13 +313,17 @@ public class FileSearch {
* @param caseDb The case database
* @param centralRepoDb The central repository database. Can be null if not
* needed.
* @param context The SearchContext the search is being performed
* from.
*
* @throws DiscoveryException
* @throws SearchCancellationException - Thrown when the user has cancelled
* the search.
*/
private static void addAttributes(List<AttributeType> attrs, List<Result> results, SleuthkitCase caseDb, CentralRepository centralRepoDb)
throws DiscoveryException {
private static void addAttributes(List<AttributeType> attrs, List<Result> results, SleuthkitCase caseDb, CentralRepository centralRepoDb, SearchContext context)
throws DiscoveryException, SearchCancellationException {
for (AttributeType attr : attrs) {
attr.addAttributeToResults(results, caseDb, centralRepoDb);
attr.addAttributeToResults(results, caseDb, centralRepoDb, context);
}
}

View File

@ -0,0 +1,40 @@
/*
* Autopsy
*
* Copyright 2021 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.discovery.search;
import java.util.concurrent.CancellationException;
/**
* Exception to be thrown when the search has been intentionally cancelled to
* provide information on where the code was when the cancellation took place.
*/
public class SearchCancellationException extends CancellationException {
private static final long serialVersionUID = 1L;
/**
* Construct a new SearchCancellationException with the specified message.
*
* @param message The text to use as the message for the exception.
*/
SearchCancellationException(String message) {
super(message);
}
}

View File

@ -1,5 +1,5 @@
/*
* Autopsy Forensic Browser
* Autopsy
*
* Copyright 2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
@ -16,17 +16,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.datasourcesummary.uiutils;
package org.sleuthkit.autopsy.discovery.search;
/**
* Basic interface for a cell model.
* Interface for providing feedback on if a search has been cancelled.
*
*/
public interface ExcelCellModel extends CellModel {
public interface SearchContext {
/**
* @return The format string to be used with Apache POI during excel
* export or null if none necessary.
* Returns true if the search has been cancelled, false otherwise.
*
* @return True if the search has been cancelled, false otherwise.
*/
String getExcelFormatString();
boolean searchIsCancelled();
}

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019-2020 Basis Technology Corp.
* Copyright 2019-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -60,10 +60,16 @@ public class SearchFiltering {
* @param caseDb The case database.
* @param centralRepoDb The central repo. Can be null as long as no filters
* need it.
* @param context The SearchContext the search is being performed
* from.
*
* @return List of Results from the search performed.
*
* @throws DiscoveryException
* @throws SearchCancellationException - Thrown when the user has cancelled
* the search.
*/
static List<Result> runQueries(List<AbstractFilter> filters, SleuthkitCase caseDb, CentralRepository centralRepoDb) throws DiscoveryException {
static List<Result> runQueries(List<AbstractFilter> filters, SleuthkitCase caseDb, CentralRepository centralRepoDb, SearchContext context) throws DiscoveryException, SearchCancellationException {
if (caseDb == null) {
throw new DiscoveryException("Case DB parameter is null"); // NON-NLS
}
@ -82,8 +88,11 @@ public class SearchFiltering {
// The file search filter is required, so this should never be empty.
throw new DiscoveryException("Selected filters do not include a case database query");
}
if (context.searchIsCancelled()) {
throw new SearchCancellationException("The search was cancelled before result list could be retrieved.");
}
try {
return getResultList(filters, combinedQuery, caseDb, centralRepoDb);
return getResultList(filters, combinedQuery, caseDb, centralRepoDb, context);
} catch (TskCoreException ex) {
throw new DiscoveryException("Error querying case database", ex); // NON-NLS
}
@ -97,17 +106,23 @@ public class SearchFiltering {
* @param caseDb The case database.
* @param centralRepoDb The central repo. Can be null as long as no filters
* need it.
* @param context The SearchContext the search is being performed
* from.
*
* @return An ArrayList of Results returned by the query.
*
* @throws TskCoreException
* @throws DiscoveryException
* @throws SearchCancellationException - Thrown when the user has cancelled
* the search.
*/
private static List<Result> getResultList(List<AbstractFilter> filters, String combinedQuery, SleuthkitCase caseDb, CentralRepository centralRepoDb) throws TskCoreException, DiscoveryException {
private static List<Result> getResultList(List<AbstractFilter> filters, String combinedQuery, SleuthkitCase caseDb, CentralRepository centralRepoDb, SearchContext context) throws TskCoreException, DiscoveryException, SearchCancellationException {
// Get all matching abstract files
List<Result> resultList = new ArrayList<>();
List<AbstractFile> sqlResults = caseDb.findAllFilesWhere(combinedQuery);
if (context.searchIsCancelled()) {
throw new SearchCancellationException("The search was cancelled while the case database query was being performed.");
}
// If there are no results, return now
if (sqlResults.isEmpty()) {
return resultList;
@ -120,8 +135,11 @@ public class SearchFiltering {
// Now run any non-SQL filters.
for (AbstractFilter filter : filters) {
if (context.searchIsCancelled()) {
throw new SearchCancellationException("The search was cancelled while alternate filters were being applied.");
}
if (filter.useAlternateFilter()) {
resultList = filter.applyAlternateFilter(resultList, caseDb, centralRepoDb);
resultList = filter.applyAlternateFilter(resultList, caseDb, centralRepoDb, context);
}
// There are no matches for the filters run so far, so return
if (resultList.isEmpty()) {
@ -227,7 +245,7 @@ public class SearchFiltering {
public Collection<ARTIFACT_TYPE> getTypes() {
return Collections.unmodifiableCollection(types);
}
private StringJoiner joinStandardArtifactTypes() {
StringJoiner joiner = new StringJoiner(",");
for (ARTIFACT_TYPE type : types) {
@ -241,9 +259,10 @@ public class SearchFiltering {
StringJoiner joiner = joinStandardArtifactTypes();
return "artifact_type_id IN (" + joiner + ")";
}
/**
* Used by backend domain search code to query for additional artifact types.
* Used by backend domain search code to query for additional artifact
* types.
*/
String getWhereClause(List<ARTIFACT_TYPE> nonVisibleArtifactTypesToInclude) {
StringJoiner joiner = joinStandardArtifactTypes();
@ -674,14 +693,17 @@ public class SearchFiltering {
@Override
public List<Result> applyAlternateFilter(List<Result> currentResults, SleuthkitCase caseDb,
CentralRepository centralRepoDb) throws DiscoveryException {
CentralRepository centralRepoDb, SearchContext context) throws DiscoveryException, SearchCancellationException {
// Set the frequency for each file
DiscoveryAttributes.FrequencyAttribute freqAttr = new DiscoveryAttributes.FrequencyAttribute();
freqAttr.addAttributeToResults(currentResults, caseDb, centralRepoDb);
freqAttr.addAttributeToResults(currentResults, caseDb, centralRepoDb, context);
// If the frequency matches the filter, add the file to the results
List<Result> frequencyResults = new ArrayList<>();
for (Result file : currentResults) {
if (context.searchIsCancelled()) {
throw new SearchCancellationException("The search was cancelled while Frequency alternate filter was being applied.");
}
if (frequencies.contains(file.getFrequency())) {
frequencyResults.add(file);
}
@ -705,7 +727,7 @@ public class SearchFiltering {
return Bundle.SearchFiltering_FrequencyFilter_desc(desc);
}
}
/**
* A filter for domains with known account types.
*/
@ -715,17 +737,20 @@ public class SearchFiltering {
public String getWhereClause() {
throw new UnsupportedOperationException("Not supported, this is an alternative filter.");
}
@Override
public boolean useAlternateFilter() {
return true;
}
@Override
public List<Result> applyAlternateFilter(List<Result> currentResults, SleuthkitCase caseDb,
CentralRepository centralRepoDb) throws DiscoveryException {
CentralRepository centralRepoDb, SearchContext context) throws DiscoveryException, SearchCancellationException {
List<Result> filteredResults = new ArrayList<>();
for (Result result : currentResults) {
if (context.searchIsCancelled()) {
throw new SearchCancellationException("The search was cancelled while Known Account Type alternate filter was being applied.");
}
if (result instanceof ResultDomain) {
ResultDomain domain = (ResultDomain) result;
if (domain.hasKnownAccountType()) {
@ -745,9 +770,9 @@ public class SearchFiltering {
public String getDesc() {
return Bundle.SearchFiltering_KnownAccountTypeFilter_desc();
}
}
/**
* A filter for previously notable content in the central repository.
*/
@ -757,19 +782,22 @@ public class SearchFiltering {
public String getWhereClause() {
throw new UnsupportedOperationException("Not supported, this is an alternative filter.");
}
@Override
public boolean useAlternateFilter() {
return true;
}
@Override
public List<Result> applyAlternateFilter(List<Result> currentResults, SleuthkitCase caseDb,
CentralRepository centralRepoDb) throws DiscoveryException {
CentralRepository centralRepoDb, SearchContext context) throws DiscoveryException, SearchCancellationException {
DiscoveryAttributes.PreviouslyNotableAttribute previouslyNotableAttr = new DiscoveryAttributes.PreviouslyNotableAttribute();
previouslyNotableAttr.addAttributeToResults(currentResults, caseDb, centralRepoDb);
previouslyNotableAttr.addAttributeToResults(currentResults, caseDb, centralRepoDb, context);
List<Result> filteredResults = new ArrayList<>();
for (Result file : currentResults) {
if (context.searchIsCancelled()) {
throw new SearchCancellationException("The search was cancelled while Previously Notable alternate filter was being applied.");
}
if (file.getPreviouslyNotableInCR() == SearchData.PreviouslyNotable.PREVIOUSLY_NOTABLE) {
filteredResults.add(file);
}
@ -784,7 +812,7 @@ public class SearchFiltering {
public String getDesc() {
return Bundle.SearchFiltering_PreviouslyNotableFilter_desc();
}
}
/**
@ -1068,7 +1096,7 @@ public class SearchFiltering {
@Override
public List<Result> applyAlternateFilter(List<Result> currentResults, SleuthkitCase caseDb,
CentralRepository centralRepoDb) throws DiscoveryException {
CentralRepository centralRepoDb, SearchContext context) throws DiscoveryException, SearchCancellationException {
if (centralRepoDb == null) {
throw new DiscoveryException("Can not run Previously Notable filter with null Central Repository DB"); // NON-NLS
@ -1087,6 +1115,9 @@ public class SearchFiltering {
CorrelationAttributeInstance.Type type = CorrelationAttributeInstance.getDefaultCorrelationTypes().get(CorrelationAttributeInstance.FILES_TYPE_ID);
for (Result result : currentResults) {
if (context.searchIsCancelled()) {
throw new SearchCancellationException("The search was cancelled while Notable alternate filter was being applied.");
}
ResultFile file = (ResultFile) result;
if (result.getType() == SearchData.Type.DOMAIN) {
break;

View File

@ -1,7 +1,7 @@
/*
* Autopsy
*
* Copyright 2020 Basis Technology Corp.
* Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -31,7 +31,6 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import javax.swing.SwingUtilities;
import org.apache.commons.lang.StringUtils;
import org.openide.util.NbBundle.Messages;
import org.openide.windows.WindowManager;
@ -574,7 +573,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
}
private void searchButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchButtonActionPerformed
// Get the selected filters
setVisible(false); //set visible used here instead of dispose incase dispose code changes
final DiscoveryTopComponent tc = DiscoveryTopComponent.getTopComponent();
if (tc == null) {
setValid("No Top Component Found");
@ -584,6 +583,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
tc.open();
}
tc.resetTopComponent();
// Get the selected filters
List<AbstractFilter> filters;
if (videosButton.isSelected()) {
filters = videoFilterPanel.getFilters();
@ -617,7 +617,6 @@ final class DiscoveryDialog extends javax.swing.JDialog {
}
searchWorker = new SearchWorker(centralRepoDb, type, filters, groupingAttr, groupSortAlgorithm, fileSort);
searchWorker.execute();
dispose();
tc.toFront();
tc.requestActive();
}//GEN-LAST:event_searchButtonActionPerformed
@ -651,6 +650,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
void cancelSearch() {
if (searchWorker != null) {
searchWorker.cancel(true);
searchWorker = null;
}
}
@ -750,7 +750,6 @@ final class DiscoveryDialog extends javax.swing.JDialog {
|| eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()) {
shouldUpdate = shouldUpdateFilters(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID(), eventData, interestingItems);
}
}
} catch (NoCurrentCaseException notUsed) {
// Case is closed, do nothing.

View File

@ -1,7 +1,7 @@
/*
* Autopsy
*
* Copyright 2019-2020 Basis Technology Corp.
* Copyright 2019-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -260,7 +260,6 @@ public final class DiscoveryTopComponent extends TopComponent {
private void newSearchButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_newSearchButtonActionPerformed
close();
final DiscoveryDialog discDialog = DiscoveryDialog.getDiscoveryDialogInstance();
discDialog.cancelSearch();
discDialog.setVisible(true);
discDialog.validateDialog();
}//GEN-LAST:event_newSearchButtonActionPerformed

View File

@ -1,7 +1,7 @@
/*
* Autopsy
*
* Copyright 2019 Basis Technology Corp.
* Copyright 2019-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -36,6 +36,8 @@ import org.sleuthkit.autopsy.discovery.search.DiscoveryException;
import org.sleuthkit.autopsy.discovery.search.DomainSearch;
import org.sleuthkit.autopsy.discovery.search.ResultsSorter;
import org.sleuthkit.autopsy.discovery.search.Result;
import org.sleuthkit.autopsy.discovery.search.SearchCancellationException;
import org.sleuthkit.autopsy.discovery.search.SearchContext;
/**
* SwingWorker to retrieve the contents of a page.
@ -87,7 +89,7 @@ final class PageWorker extends SwingWorker<Void, Void> {
@Override
protected Void doInBackground() throws Exception {
SearchContext context = new SwingWorkerSearchContext(this);
try {
// Run the search
if (resultType == SearchData.Type.DOMAIN) {
@ -96,17 +98,22 @@ final class PageWorker extends SwingWorker<Void, Void> {
groupingAttribute,
groupSort,
fileSortMethod, groupKey, startingEntry, pageSize,
Case.getCurrentCase().getSleuthkitCase(), centralRepo));
Case.getCurrentCase().getSleuthkitCase(), centralRepo, context));
} else {
results.addAll(FileSearch.getFilesInGroup(System.getProperty(USER_NAME_PROPERTY), searchfilters,
groupingAttribute,
groupSort,
fileSortMethod, groupKey, startingEntry, pageSize,
Case.getCurrentCase().getSleuthkitCase(), centralRepo));
Case.getCurrentCase().getSleuthkitCase(), centralRepo, context));
}
} catch (DiscoveryException ex) {
logger.log(Level.SEVERE, "Error running file search test", ex);
cancel(true);
} catch (SearchCancellationException ex) {
//The user does not explicitly have a way to cancel the loading of a page
//but they could have cancelled the search during the loading of the first page
//So this may or may not be an issue depending on when this occurred.
logger.log(Level.WARNING, "Search was cancelled while retrieving data for results page with starting entry: " + startingEntry, ex);
}
return null;
}

View File

@ -1,7 +1,7 @@
/*
* Autopsy
*
* Copyright 2019 Basis Technology Corp.
* Copyright 2019-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -35,6 +35,8 @@ import org.sleuthkit.autopsy.discovery.search.FileSearch;
import org.sleuthkit.autopsy.discovery.search.DiscoveryException;
import org.sleuthkit.autopsy.discovery.search.DomainSearch;
import org.sleuthkit.autopsy.discovery.search.ResultsSorter;
import org.sleuthkit.autopsy.discovery.search.SearchCancellationException;
import org.sleuthkit.autopsy.discovery.search.SearchContext;
import org.sleuthkit.autopsy.discovery.search.SearchData;
/**
@ -75,23 +77,28 @@ final class SearchWorker extends SwingWorker<Void, Void> {
protected Void doInBackground() throws Exception {
try {
// Run the search
SearchContext context = new SwingWorkerSearchContext(this);
if (searchType == SearchData.Type.DOMAIN) {
DomainSearch domainSearch = new DomainSearch();
results.putAll(domainSearch.getGroupSizes(System.getProperty(USER_NAME_PROPERTY), filters,
groupingAttr,
groupSortAlgorithm,
fileSort,
Case.getCurrentCase().getSleuthkitCase(), centralRepoDb));
Case.getCurrentCase().getSleuthkitCase(), centralRepoDb, context));
} else {
results.putAll(FileSearch.getGroupSizes(System.getProperty(USER_NAME_PROPERTY), filters,
groupingAttr,
groupSortAlgorithm,
fileSort,
Case.getCurrentCase().getSleuthkitCase(), centralRepoDb));
Case.getCurrentCase().getSleuthkitCase(), centralRepoDb, context));
}
} catch (DiscoveryException ex) {
logger.log(Level.SEVERE, "Error running file search test", ex);
logger.log(Level.SEVERE, "Error running file search test.", ex);
cancel(true);
} catch (SearchCancellationException ex) {
//search cancellation exceptions should indicate that the user chose to cancell this search
//so would not be a problem but we might be curious what was being done when it was cancelled
logger.log(Level.INFO, "Discovery search was cancelled.", ex);
}
return null;
}

View File

@ -0,0 +1,45 @@
/*
* Autopsy
*
* Copyright 2021 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.discovery.ui;
import javax.swing.SwingWorker;
import org.sleuthkit.autopsy.discovery.search.SearchContext;
/**
* Implementation of SearchContext for searches being performed in the
* background thread of a SwingWorker.
*/
class SwingWorkerSearchContext implements SearchContext {
private final SwingWorker<Void, Void> searchWorker;
/**
* Construct a new SwingWorkerSearchContext.
*
* @param worker The SwingWorker the search is being performed in.
*/
SwingWorkerSearchContext(SwingWorker<Void, Void> worker) {
searchWorker = worker;
}
@Override
public boolean searchIsCancelled() {
return searchWorker.isCancelled();
}
}

Some files were not shown because too many files have changed in this diff Show More