Merge branch 'develop' into 7937-timeline-name-fix

This commit is contained in:
Kelly Kelly 2021-09-01 13:05:12 -04:00
commit 0000ef154d
129 changed files with 5219 additions and 3927 deletions

View File

@ -87,27 +87,10 @@ public final class OtherOccurrences {
if (osAccountAddr.isPresent()) { if (osAccountAddr.isPresent()) {
try { try {
for (OsAccountInstance instance : osAccount.getOsAccountInstances()) { for (OsAccountInstance instance : osAccount.getOsAccountInstances()) {
DataSource osAccountDataSource = instance.getDataSource(); CorrelationAttributeInstance correlationAttributeInstance = CorrelationAttributeUtil.makeCorrAttr(instance.getOsAccount(), instance.getDataSource());
try { if (correlationAttributeInstance != null) {
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());
ret.add(correlationAttributeInstance); 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) { } catch (TskCoreException ex) {

View File

@ -40,6 +40,8 @@ import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.DataArtifact; import org.sleuthkit.datamodel.DataArtifact;
import org.sleuthkit.datamodel.HashUtility; import org.sleuthkit.datamodel.HashUtility;
import org.sleuthkit.datamodel.InvalidAccountIDException; import org.sleuthkit.datamodel.InvalidAccountIDException;
import org.sleuthkit.datamodel.OsAccount;
import org.sleuthkit.datamodel.OsAccountInstance;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData; 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. * Gets the correlation attribute instance for a file.
* *

View File

@ -661,7 +661,8 @@ public final class CaseEventListener implements PropertyChangeListener {
"CaseEventsListener.prevCaseComment.text=Users seen in previous cases", "CaseEventsListener.prevCaseComment.text=Users seen in previous cases",
"CaseEventsListener.prevExists.text=Previously Seen Users (Central Repository)"}) "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 { private final class OsAccountInstancesAddedTask implements Runnable {
@ -677,7 +678,7 @@ public final class CaseEventListener implements PropertyChangeListener {
@Override @Override
public void run() { 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 //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()))) { || (IngestManager.getInstance().isIngestRunning() && !(IngestEventsListener.isFlagSeenDevices() || IngestEventsListener.shouldCreateCrProperties()))) {
return; return;
} }
@ -687,27 +688,15 @@ public final class CaseEventListener implements PropertyChangeListener {
for (OsAccountInstance osAccountInstance : addedOsAccountNew) { for (OsAccountInstance osAccountInstance : addedOsAccountNew) {
try { try {
OsAccount osAccount = osAccountInstance.getOsAccount(); OsAccount osAccount = osAccountInstance.getOsAccount();
Optional<String> accountAddr = osAccount.getAddr(); CorrelationAttributeInstance correlationAttributeInstance = CorrelationAttributeUtil.makeCorrAttr(osAccount, osAccountInstance.getDataSource());
// 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 if (correlationAttributeInstance == null) {
// 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; return;
} }
Optional<String> accountAddr = osAccount.getAddr();
try { 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 // Save to the database if requested
if(IngestEventsListener.shouldCreateCrProperties()) { if (IngestEventsListener.shouldCreateCrProperties()) {
dbManager.addArtifactInstance(correlationAttributeInstance); dbManager.addArtifactInstance(correlationAttributeInstance);
} }
@ -740,14 +729,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) { } catch (CorrelationAttributeNormalizationException ex) {
LOGGER.log(Level.SEVERE, "Exception with Correlation Attribute Normalization.", ex); //NON-NLS 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) { } catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE, "Cannot get central repository for OsAccount: " + "OsAccount", ex); LOGGER.log(Level.SEVERE, "Cannot get central repository for OsAccount: " + "OsAccount", ex);
} }

View File

@ -1,8 +1,9 @@
CommandLineIngestSettingPanel_empty_report_name_mgs=Report profile name was empty, no profile created. 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.
CommandListIngestSettingsPanel_Default_Report_DisplayName=Default CommandListIngestSettingsPanel_Default_Report_DisplayName=Default
CommandListIngestSettingsPanel_Make_Config=Make new profile... 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 OpenIDE-Module-Name=CommandLineAutopsy
OptionsCategory_Keywords_Command_Line_Ingest_Settings=Command Line Ingest Settings OptionsCategory_Keywords_Command_Line_Ingest_Settings=Command Line Ingest Settings
OptionsCategory_Keywords_General=Options OptionsCategory_Keywords_General=Options

View File

@ -280,18 +280,15 @@ public class CommandLineIngestSettingsPanel extends javax.swing.JPanel {
add(nodePanel, java.awt.BorderLayout.CENTER); add(nodePanel, java.awt.BorderLayout.CENTER);
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
@Messages({ @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_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 private void bnEditReportSettingsActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnEditReportSettingsActionPerformed
String reportName = getReportName(); String reportName = getReportName();
if (reportName.equals(Bundle.CommandListIngestSettingsPanel_Make_Config())) { if (reportName.equals(Bundle.CommandListIngestSettingsPanel_Make_Config())) {
reportName = JOptionPane.showInputDialog(this, Bundle.CommandListIngestSettingsPanel_Report_Name_Msg()); 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 // User hit cancel
if (reportName == null) { if (reportName == null) {
@ -302,6 +299,15 @@ public class CommandLineIngestSettingsPanel extends javax.swing.JPanel {
} else if (doesReportProfileNameExist(reportName)) { } else if (doesReportProfileNameExist(reportName)) {
JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), Bundle.CommandLineIngestSettingPanel_existing_report_name_mgs()); JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), Bundle.CommandLineIngestSettingPanel_existing_report_name_mgs());
return; 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.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.logging.Level; import java.util.logging.Level;
@ -38,6 +39,7 @@ import org.openide.nodes.AbstractNode;
import org.openide.nodes.Node; import org.openide.nodes.Node;
import org.openide.util.Lookup; import org.openide.util.Lookup;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.communications.ModifiableProxyLookup; import org.sleuthkit.autopsy.communications.ModifiableProxyLookup;
import org.sleuthkit.autopsy.corecomponents.TableFilterNode; import org.sleuthkit.autopsy.corecomponents.TableFilterNode;
import org.sleuthkit.autopsy.coreutils.Logger; 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.AbstractContent;
import org.sleuthkit.datamodel.Account; import org.sleuthkit.datamodel.Account;
import org.sleuthkit.datamodel.BlackboardArtifact; 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.Content;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.SleuthkitCase;
/** /**
* A Panel that shows the media (thumbnails) for the selected account. * 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 final MessageDataContent contentViewer;
private MediaViewerWorker worker; private MediaViewerWorker worker;
private SelectionWorker selectionWorker;
@Messages({ @Messages({
"MediaViewer_Name=Media Attachments" "MediaViewer_Name=Media Attachments"
@ -106,11 +111,15 @@ final class MediaViewer extends JPanel implements RelationshipsViewer, ExplorerM
@Override @Override
public void setSelectionInfo(SelectionInfo info) { public void setSelectionInfo(SelectionInfo info) {
contentViewer.setNode(null); contentViewer.setNode(null);
thumbnailViewer.resetComponent(); thumbnailViewer.setNode(null);
if (worker != null) { if (worker != null) {
worker.cancel(true); worker.cancel(true);
} }
if(selectionWorker != null) {
selectionWorker.cancel(true);
}
worker = new MediaViewerWorker(info); worker = new MediaViewerWorker(info);
@ -181,21 +190,66 @@ final class MediaViewer extends JPanel implements RelationshipsViewer, ExplorerM
*/ */
private void handleNodeSelectionChange() { private void handleNodeSelectionChange() {
final Node[] nodes = tableEM.getSelectedNodes(); final Node[] nodes = tableEM.getSelectedNodes();
contentViewer.setNode(null);
if(selectionWorker != null) {
selectionWorker.cancel(true);
}
if (nodes != null && nodes.length == 1) { if (nodes != null && nodes.length == 1) {
AbstractContent thumbnail = nodes[0].getLookup().lookup(AbstractContent.class); AbstractContent thumbnail = nodes[0].getLookup().lookup(AbstractContent.class);
if (thumbnail != null) { if (thumbnail != null) {
try { setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
Content parentContent = thumbnail.getParent(); selectionWorker = new SelectionWorker(thumbnail);
if (parentContent != null && parentContent instanceof BlackboardArtifact) { selectionWorker.execute();
contentViewer.setNode(new BlackboardArtifactNode((BlackboardArtifact) parentContent)); }
} }
} catch (TskCoreException ex) { }
logger.log(Level.WARNING, "Unable to get parent Content from AbstraceContent instance.", ex); //NON-NLS
/**
* 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 { return null;
contentViewer.setNode(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

@ -40,18 +40,17 @@ import org.sleuthkit.datamodel.TskCoreException;
*/ */
@ServiceProvider(service = DataContentViewer.class, position = 7) @ServiceProvider(service = DataContentViewer.class, position = 7)
public class AnalysisResultsContentViewer implements DataContentViewer { public class AnalysisResultsContentViewer implements DataContentViewer {
private static final Logger logger = Logger.getLogger(AnalysisResultsContentPanel.class.getName()); private static final Logger logger = Logger.getLogger(AnalysisResultsContentPanel.class.getName());
// isPreferred value // isPreferred value
private static final int PREFERRED_VALUE = 3; private static final int PREFERRED_VALUE = 3;
private final AnalysisResultsViewModel viewModel = new AnalysisResultsViewModel(); private final AnalysisResultsViewModel viewModel = new AnalysisResultsViewModel();
private final AnalysisResultsContentPanel panel = new AnalysisResultsContentPanel(); private final AnalysisResultsContentPanel panel = new AnalysisResultsContentPanel();
private SwingWorker<?, ?> worker = null; private SwingWorker<?, ?> worker = null;
@NbBundle.Messages({ @NbBundle.Messages({
"AnalysisResultsContentViewer_title=Analysis Results" "AnalysisResultsContentViewer_title=Analysis Results"
}) })
@ -135,11 +134,11 @@ public class AnalysisResultsContentViewer implements DataContentViewer {
if (content instanceof AnalysisResult) { if (content instanceof AnalysisResult) {
return true; return true;
} }
if (content == null || content instanceof BlackboardArtifact) { if (content == null || content instanceof BlackboardArtifact) {
continue; continue;
} }
try { try {
if (Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard().hasAnalysisResults(content.getId())) { if (Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard().hasAnalysisResults(content.getId())) {
return true; return true;
@ -148,7 +147,7 @@ public class AnalysisResultsContentViewer implements DataContentViewer {
logger.log(Level.SEVERE, "Unable to get analysis results for file with obj id " + content.getId(), ex); logger.log(Level.SEVERE, "Unable to get analysis results for file with obj id " + content.getId(), ex);
} }
} }
return false; return false;
} }

View File

@ -252,7 +252,7 @@ public class AnalysisResultsViewModel {
try { try {
nodeContent = Optional.of(content); nodeContent = Optional.of(content);
// get the aggregate score of that content // get the aggregate score of that content
aggregateScore = Optional.ofNullable(content.getAggregateScore()); aggregateScore = Optional.ofNullable(content.getAggregateScore());

View File

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

View File

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

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2020 Basis Technology Corp. * Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -18,9 +18,7 @@
*/ */
package org.sleuthkit.autopsy.datasourcesummary.datamodel; package org.sleuthkit.autopsy.datasourcesummary.datamodel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultArtifactUpdateGovernor;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@ -31,30 +29,23 @@ import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException; import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException; 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 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<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; private final SleuthkitCaseProvider provider;
/** /**
@ -73,11 +64,6 @@ public class AnalysisSummary implements DefaultArtifactUpdateGovernor {
this.provider = provider; this.provider = provider;
} }
@Override
public Set<Integer> getArtifactTypeIdsForRefresh() {
return ARTIFACT_UPDATE_TYPE_IDS;
}
/** /**
* Gets counts for hashset hits. * Gets counts for hashset hits.
* *

View File

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

View File

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

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2020 Basis Technology Corp. * Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -18,29 +18,25 @@
*/ */
package org.sleuthkit.autopsy.datasourcesummary.datamodel; package org.sleuthkit.autopsy.datasourcesummary.datamodel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultArtifactUpdateGovernor;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Set; import java.util.Set;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.ingest.ModuleContentEvent;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.Image;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.TskData;
/** /**
* Provides methods to query for data source overview details. * Provides methods to query for data source overview details.
*/ */
public class ContainerSummary implements DefaultArtifactUpdateGovernor { public class ContainerSummary {
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 SleuthkitCaseProvider provider; private final SleuthkitCaseProvider provider;
@ -59,22 +55,7 @@ public class ContainerSummary implements DefaultArtifactUpdateGovernor {
public ContainerSummary(SleuthkitCaseProvider provider) { public ContainerSummary(SleuthkitCaseProvider provider) {
this.provider = 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. * Gets the size of unallocated files in a particular datasource.
* *
@ -231,4 +212,228 @@ public class ContainerSummary implements DefaultArtifactUpdateGovernor {
String separator = ", "; String separator = ", ";
return getConcattedStringsResult(query, valueParam, 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 * See the License for the specific language governing permissions and
* limitations under the License. * 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 * A function that accepts input of type I and outputs type O. This function is

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2019 - 2020 Basis Technology Corp. * Copyright 2019 - 2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.datasourcesummary.datamodel;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.text.DecimalFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator; import java.util.Comparator;
import java.util.Date; 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 * Utilities for getting information about a data source or all data sources
* from the case database. * 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. * Gets a count of tsk_files for a particular datasource.
@ -100,7 +104,7 @@ final class DataSourceInfoUtilities {
* @throws TskCoreException * @throws TskCoreException
* @throws SQLException * @throws SQLException
*/ */
static Long getCountOfRegNonSlackFiles(SleuthkitCase skCase, DataSource currentDataSource, String additionalWhere) public static Long getCountOfRegNonSlackFiles(SleuthkitCase skCase, DataSource currentDataSource, String additionalWhere)
throws TskCoreException, SQLException { throws TskCoreException, SQLException {
String whereClause = "meta_type=" + TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG.getValue() String whereClause = "meta_type=" + TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG.getValue()
+ " AND type<>" + TSK_DB_FILES_TYPE_ENUM.SLACK.getFileType(); + " 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. * An interface for handling a result set and returning a value.
*/ */
interface ResultSetHandler<T> { public interface ResultSetHandler<T> {
T process(ResultSet resultset) throws SQLException; T process(ResultSet resultset) throws SQLException;
} }
@ -149,14 +153,14 @@ final class DataSourceInfoUtilities {
* *
* @return The clause. * @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"; return "meta_flags & " + flag.getValue() + " > 0";
} }
/** /**
* Enum for specifying the sort order for getAttributes. * Enum for specifying the sort order for getAttributes.
*/ */
enum SortOrder { public enum SortOrder {
DESCENDING, DESCENDING,
ASCENDING ASCENDING
} }
@ -181,7 +185,7 @@ final class DataSourceInfoUtilities {
* *
* @throws TskCoreException * @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); return getArtifacts(skCase, artifactType, dataSource, attributeType, sortOrder, 0);
} }
@ -207,7 +211,7 @@ final class DataSourceInfoUtilities {
* *
* @throws TskCoreException * @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) { if (maxCount < 0) {
throw new IllegalArgumentException("Invalid maxCount passed to getArtifacts, value must be equal to or greater than 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 * @return The 'getValueString()' value or null if the attribute or String
* could not be retrieved. * could not be retrieved.
*/ */
static String getStringOrNull(BlackboardArtifact artifact, Type attributeType) { public static String getStringOrNull(BlackboardArtifact artifact, Type attributeType) {
BlackboardAttribute attr = getAttributeOrNull(artifact, attributeType); BlackboardAttribute attr = getAttributeOrNull(artifact, attributeType);
return (attr == null) ? null : attr.getValueString(); 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 * @return The 'getValueLong()' value or null if the attribute could not be
* retrieved. * retrieved.
*/ */
static Long getLongOrNull(BlackboardArtifact artifact, Type attributeType) { public static Long getLongOrNull(BlackboardArtifact artifact, Type attributeType) {
BlackboardAttribute attr = getAttributeOrNull(artifact, attributeType); BlackboardAttribute attr = getAttributeOrNull(artifact, attributeType);
return (attr == null) ? null : attr.getValueLong(); return (attr == null) ? null : attr.getValueLong();
} }
/** /**
* Retrieves the int value of a certain attribute type from an artifact. * 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 * @return The 'getValueInt()' value or null if the attribute could not be
* retrieved. * retrieved.
*/ */
static Integer getIntOrNull(BlackboardArtifact artifact, Type attributeType) { public static Integer getIntOrNull(BlackboardArtifact artifact, Type attributeType) {
BlackboardAttribute attr = getAttributeOrNull(artifact, attributeType); BlackboardAttribute attr = getAttributeOrNull(artifact, attributeType);
return (attr == null) ? null : attr.getValueInt(); return (attr == null) ? null : attr.getValueInt();
} }
@ -423,8 +427,31 @@ final class DataSourceInfoUtilities {
* @return The date determined from the 'getValueLong()' as seconds from * @return The date determined from the 'getValueLong()' as seconds from
* epoch or null if the attribute could not be retrieved or is 0. * 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); Long longVal = getLongOrNull(artifact, attributeType);
return (longVal == null || longVal == 0) ? null : new Date(longVal * 1000); 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 * Autopsy Forensic Browser
* *
* Copyright 2020 Basis Technology Corp. * Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -32,7 +32,6 @@ import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException; 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.AbstractWaypointFetcher;
import org.sleuthkit.autopsy.geolocation.GeoFilter; import org.sleuthkit.autopsy.geolocation.GeoFilter;
import org.sleuthkit.autopsy.geolocation.MapWaypoint; 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. * 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. * A count of hits for a particular city.
@ -59,8 +58,8 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
* Main constructor. * Main constructor.
* *
* @param cityRecord The record for the city including name, country, * @param cityRecord The record for the city including name, country,
* and location. * and location.
* @param count The number of hits in proximity to that city. * @param count The number of hits in proximity to that city.
*/ */
CityRecordCount(CityRecord cityRecord, int count) { CityRecordCount(CityRecord cityRecord, int count) {
this.cityRecord = cityRecord; this.cityRecord = cityRecord;
@ -69,7 +68,7 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
/** /**
* @return The record for the city including name, country, and * @return The record for the city including name, country, and
* location. * location.
*/ */
public CityRecord getCityRecord() { public CityRecord getCityRecord() {
return cityRecord; return cityRecord;
@ -96,8 +95,8 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
/** /**
* Main constructor. * Main constructor.
* *
* @param mostCommon The list of most common cities seen. * @param mostCommon The list of most common cities seen.
* @param mostRecent The list of most recent cities seen. * @param mostRecent The list of most recent cities seen.
* @param mostRecentSeen * @param mostRecentSeen
*/ */
CityData(CityCountsList mostCommon, CityCountsList mostRecent, Long 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 * @return The time stamp in seconds from epoch of the most recently
* seen city * seen city
*/ */
public Long getMostRecentSeen() { public Long getMostRecentSeen() {
return mostRecentSeen; return mostRecentSeen;
@ -142,10 +141,10 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
/** /**
* Main constructor. * Main constructor.
* *
* @param counts The list of cities and the count of how many points are * @param counts The list of cities and the count of how many points
* closest to that city. * are closest to that city.
* @param otherCount The count of points where no closest city was * @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) { CityCountsList(List<CityRecordCount> counts, int otherCount) {
this.counts = Collections.unmodifiableList(new ArrayList<>(counts)); 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 * @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() { public List<CityRecordCount> getCounts() {
return counts; return counts;
@ -162,7 +161,7 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
/** /**
* @return The count of points where no closest city was determined due * @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() { public int getOtherCount() {
return otherCount; return otherCount;
@ -183,10 +182,10 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
* Main constructor. * Main constructor.
* *
* @param mapWaypoints The way points found for the data source. * @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 * @param tracks A list of sets where each set is a track in the
* source. * data source.
* @param areas A list of areas where each set is an area in the data * @param areas A list of areas where each set is an area in the
* source. * data source.
*/ */
private GeoResult(Set<MapWaypoint> mapWaypoints, List<Set<MapWaypoint>> tracks, List<Set<MapWaypoint>> areas) { private GeoResult(Set<MapWaypoint> mapWaypoints, List<Set<MapWaypoint>> tracks, List<Set<MapWaypoint>> areas) {
this.mapWaypoints = mapWaypoints; this.mapWaypoints = mapWaypoints;
@ -250,6 +249,7 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
* A supplier method that can throw an exception of E. * A supplier method that can throw an exception of E.
* *
* @return The object type. * @return The object type.
*
* @throws E The exception type. * @throws E The exception type.
*/ */
T get() throws E; T get() throws E;
@ -277,13 +277,12 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
/** /**
* @return Returns all the geolocation artifact types. * @return Returns all the geolocation artifact types.
*/ */
public List<ARTIFACT_TYPE> getGeoTypes() { public static List<ARTIFACT_TYPE> getGeoTypes() {
return GPS_ARTIFACT_TYPES; return GPS_ARTIFACT_TYPES;
} }
@Override public static Set<Integer> getArtifactTypeIdsForRefresh() {
public Set<Integer> getArtifactTypeIdsForRefresh() { return Collections.unmodifiableSet(GPS_ARTIFACT_TYPE_IDS);
return GPS_ARTIFACT_TYPE_IDS;
} }
/** /**
@ -291,13 +290,14 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
* the event where either time is null. * the event where either time is null.
* *
* @param minTime The minimum time. If null is provided, this function will * @param minTime The minimum time. If null is provided, this function will
* return false. * return false.
* @param time The time to check. If null is provided and the min time is * @param time The time to check. If null is provided and the min time is
* non-null, then this function will return false. * non-null, then this function will return false.
*
* @return If minTime == null then false. If minTime != null && time == null * @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) { if (minTime != null && time != null && time >= minTime) {
return true; return true;
} else { } else {
@ -310,12 +310,13 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
* a total of waypoints whose time stamp is greater than or equal to * a total of waypoints whose time stamp is greater than or equal to
* minTime. * 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. * @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 * @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) { if (points == null) {
return EMPTY_COUNT; return EMPTY_COUNT;
} }
@ -332,7 +333,8 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
* the point is null, null is returned. * the point is null, null is returned.
* *
* @param cityMapper The means of mapping a point to the closest city. * @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. * @return A tuple of the closest city and timestamp in seconds from epoch.
*/ */
private Pair<CityRecord, Long> getClosestWithTime(ClosestCityMapper cityMapper, MapWaypoint pt) { 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 * into a stream of the unique cities identified in this grouping and the
* latest time stamp for each grouping. * 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. * @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 * @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) { private Stream<Pair<CityRecord, Long>> reduceGrouping(Set<MapWaypoint> points, ClosestCityMapper cityMapper) {
if (points == null) { if (points == null) {
@ -367,7 +371,7 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
if (pair == null) { if (pair == null) {
continue; continue;
} }
CityRecord city = pair.getLeft(); CityRecord city = pair.getLeft();
Long prevTime = timeMapping.get(city); Long prevTime = timeMapping.get(city);
Long curTime = pair.getRight(); Long curTime = pair.getRight();
@ -375,7 +379,7 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
timeMapping.put(city, curTime); timeMapping.put(city, curTime);
} }
} }
return timeMapping.entrySet().stream() return timeMapping.entrySet().stream()
.map(e -> Pair.of(e.getKey(), e.getValue())); .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 * of tuples where each tuple represents a point with the closest city and
* the time stamp in seconds from epoch. * 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. * @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 * @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 * @throws IOException
*/ */
private Stream<Pair<CityRecord, Long>> processGeoResult(GeoResult geoResult, ClosestCityMapper cityMapper) { 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>> areas = (geoResult.getAreas() == null) ? Collections.emptyList() : geoResult.getAreas();
List<Set<MapWaypoint>> tracks = (geoResult.getTracks() == null) ? Collections.emptyList() : geoResult.getTracks(); List<Set<MapWaypoint>> tracks = (geoResult.getTracks() == null) ? Collections.emptyList() : geoResult.getTracks();
Stream<Pair<CityRecord, Long>> reducedGroupings = Stream.of(areas, tracks) Stream<Pair<CityRecord, Long>> reducedGroupings = Stream.of(areas, tracks)
.flatMap((groupingList) -> groupingList.stream()) .flatMap((groupingList) -> groupingList.stream())
.flatMap((grouping) -> reduceGrouping(grouping, cityMapper)); .flatMap((grouping) -> reduceGrouping(grouping, cityMapper));
@ -407,7 +413,7 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
.flatMap((groupingList) -> groupingList.stream()) .flatMap((groupingList) -> groupingList.stream())
.flatMap((group) -> group.stream()) .flatMap((group) -> group.stream())
.collect(Collectors.toSet()); .collect(Collectors.toSet());
Set<MapWaypoint> pointSet = geoResult.getMapWaypoints() == null ? Collections.emptySet() : geoResult.getMapWaypoints(); Set<MapWaypoint> pointSet = geoResult.getMapWaypoints() == null ? Collections.emptySet() : geoResult.getMapWaypoints();
Stream<Pair<CityRecord, Long>> citiesForPoints = pointSet.stream() Stream<Pair<CityRecord, Long>> citiesForPoints = pointSet.stream()
// it appears that AbstractWaypointFetcher.handleFilteredWaypointSet returns all points // 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). * number of found hits (i.e. most hits is first index).
* *
* @param dataSource The data source. * @param dataSource The data source.
* @param daysCount Number of days to go back. * @param daysCount Number of days to go back.
* @param maxCount Maximum number of results. * @param maxCount Maximum number of results.
* *
* @return The sorted list. * @return The sorted list.
* *
@ -507,11 +513,12 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
* Main constructor. * Main constructor.
* *
* @param asyncResult Geolocation fetches results in a callback which is * @param asyncResult Geolocation fetches results in a callback which is
* already handled by other mechanisms in data source summary. The * already handled by other mechanisms in data source
* BlockingQueue blocks until a result is received from geolocation. * summary. The BlockingQueue blocks until a result
* @param filters The applicable filters for geolocation. * 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); super(filters);
this.asyncResult = asyncResult; this.asyncResult = asyncResult;
} }
@ -531,6 +538,7 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
* Fetches all GPS data for the data source from the current case. * Fetches all GPS data for the data source from the current case.
* *
* @param dataSource The data source. * @param dataSource The data source.
*
* @return The GPS data pertaining to the data source. * @return The GPS data pertaining to the data source.
* @throws SleuthkitCaseProviderException * @throws SleuthkitCaseProviderException
* @throws GeoLocationDataException * @throws GeoLocationDataException

View File

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

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2020 Basis Technology Corp. * Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -18,30 +18,22 @@
*/ */
package org.sleuthkit.autopsy.datasourcesummary.datamodel; package org.sleuthkit.autopsy.datasourcesummary.datamodel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultUpdateGovernor;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException; 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.DataSource;
import org.sleuthkit.datamodel.TskCoreException; 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 final SleuthkitCaseProvider provider;
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS = new HashSet<>(
Arrays.asList(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED));
/** /**
* Main constructor. * Main constructor.
*/ */
@ -58,26 +50,6 @@ public class MimeTypeSummary implements DefaultUpdateGovernor {
this.provider = provider; 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 * Get the number of files in the case database for the current data source
* which have the specified mimetypes. * which have the specified mimetypes.
@ -98,12 +70,7 @@ public class MimeTypeSummary implements DefaultUpdateGovernor {
*/ */
public Long getCountOfFilesForMimeTypes(DataSource currentDataSource, Set<String> setOfMimeTypes) public Long getCountOfFilesForMimeTypes(DataSource currentDataSource, Set<String> setOfMimeTypes)
throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException { 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) public Long getCountOfFilesNotInMimeTypes(DataSource currentDataSource, Set<String> setOfMimeTypes)
throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException { throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException {
return DataSourceInfoUtilities.getCountOfRegNonSlackFiles(provider.get(), currentDataSource,
return DataSourceInfoUtilities.getCountOfRegNonSlackFiles(
provider.get(),
currentDataSource,
"mime_type NOT IN " + getSqlSet(setOfMimeTypes) "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) public Long getCountOfAllRegularFiles(DataSource dataSource)
throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException { throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException {
return DataSourceInfoUtilities.getCountOfRegNonSlackFiles(provider.get(), dataSource, null); return DataSourceInfoUtilities.getCountOfRegNonSlackFiles(provider.get(), dataSource, null);
} }
@ -164,12 +126,7 @@ public class MimeTypeSummary implements DefaultUpdateGovernor {
*/ */
public Long getCountOfFilesWithNoMimeType(DataSource currentDataSource) public Long getCountOfFilesWithNoMimeType(DataSource currentDataSource)
throws SleuthkitCaseProvider.SleuthkitCaseProviderException, TskCoreException, SQLException { 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

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2019 Basis Technology Corp. * Copyright 2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -31,7 +31,6 @@ import java.util.stream.Stream;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import org.sleuthkit.autopsy.centralrepository.ingestmodule.CentralRepoIngestModuleFactory; import org.sleuthkit.autopsy.centralrepository.ingestmodule.CentralRepoIngestModuleFactory;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException; 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;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute;
@ -62,7 +61,7 @@ import org.sleuthkit.datamodel.TskCoreException;
* d) The content of that TSK_COMMENT attribute will be of the form "Previous * d) The content of that TSK_COMMENT attribute will be of the form "Previous
* Case: case1,case2...caseN" * Case: case1,case2...caseN"
*/ */
public class PastCasesSummary implements DefaultArtifactUpdateGovernor { public class PastCasesSummary {
/** /**
* Return type for results items in the past cases tab. * Return type for results items in the past cases tab.
@ -87,22 +86,17 @@ public class PastCasesSummary implements DefaultArtifactUpdateGovernor {
* @return Data for the cases with same id table. * @return Data for the cases with same id table.
*/ */
public List<Pair<String, Long>> getSameIdsResults() { public List<Pair<String, Long>> getSameIdsResults() {
return sameIdsResults; return Collections.unmodifiableList(sameIdsResults);
} }
/** /**
* @return Data for the tagged notable table. * @return Data for the tagged notable table.
*/ */
public List<Pair<String, Long>> getTaggedNotable() { public List<Pair<String, Long>> getTaggedNotable() {
return taggedNotable; return Collections.unmodifiableList(taggedNotable);
} }
} }
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 static final String CENTRAL_REPO_INGEST_NAME = CentralRepoIngestModuleFactory.getModuleName().toUpperCase().trim(); private static final String CENTRAL_REPO_INGEST_NAME = CentralRepoIngestModuleFactory.getModuleName().toUpperCase().trim();
private static final BlackboardAttribute.Type TYPE_COMMENT = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_COMMENT); private static final BlackboardAttribute.Type TYPE_COMMENT = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_COMMENT);
private static final BlackboardAttribute.Type TYPE_ASSOCIATED_ARTIFACT = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT); private static final BlackboardAttribute.Type TYPE_ASSOCIATED_ARTIFACT = new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT);
@ -147,11 +141,6 @@ public class PastCasesSummary implements DefaultArtifactUpdateGovernor {
this.logger = logger; 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 * Given the provided sources for an attribute, aims to determine if one of
* those sources is the Central Repository Ingest Module. * those sources is the Central Repository Ingest Module.
@ -224,7 +213,7 @@ public class PastCasesSummary implements DefaultArtifactUpdateGovernor {
* @return The list of unique cases and their occurrences sorted from max to * @return The list of unique cases and their occurrences sorted from max to
* min. * 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 Collection<List<String>> groupedCases = cases
// group by case insensitive compare of cases // group by case insensitive compare of cases
.collect(Collectors.groupingBy((caseStr) -> caseStr.toUpperCase().trim())) .collect(Collectors.groupingBy((caseStr) -> caseStr.toUpperCase().trim()))

View File

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

View File

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

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2020 Basis Technology Corp. * Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -18,6 +18,8 @@
*/ */
package org.sleuthkit.autopsy.datasourcesummary.datamodel; package org.sleuthkit.autopsy.datasourcesummary.datamodel;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.Instant; import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -26,29 +28,25 @@ import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.TimeZone; import java.util.TimeZone;
import org.joda.time.Interval; 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.DataSource;
import org.sleuthkit.datamodel.TimelineEvent; import org.sleuthkit.datamodel.TimelineEvent;
import org.sleuthkit.datamodel.TimelineEventType; import org.sleuthkit.datamodel.TimelineEventType;
import org.sleuthkit.datamodel.TimelineFilter.RootFilter; import org.sleuthkit.datamodel.TimelineFilter.RootFilter;
import org.sleuthkit.datamodel.TimelineManager; import org.sleuthkit.datamodel.TimelineManager;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.core.UserPreferences;
/** /**
* Provides data source summary information pertaining to Timeline data. * 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 * 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. * @param dataSource The data source.
* @return The timeline root filter. * @return The timeline root filter.
* @throws NoCurrentCaseException * @throws SleuthkitCaseProviderException
* @throws TskCoreException * @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 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 private static final Set<TimelineEventType> FILE_SYSTEM_EVENTS
= new HashSet<>(Arrays.asList( = new HashSet<>(Arrays.asList(
TimelineEventType.FILE_MODIFIED, TimelineEventType.FILE_MODIFIED,
@ -105,39 +100,19 @@ public class TimelineSummary implements DefaultUpdateGovernor {
this.filterFunction = filterFunction; 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. * Retrieves timeline summary data.
* *
* @param dataSource The data source for which timeline data will be * @param dataSource The data source for which timeline data will be
* retrieved. * retrieved.
* @param recentDaysNum The maximum number of most recent days' activity to * @param recentDaysNum The maximum number of most recent days' activity to
* include. * include.
*
* @return The retrieved data. * @return The retrieved data.
* @throws SleuthkitCaseProviderException * @throws SleuthkitCaseProviderException
* @throws TskCoreException * @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(); TimeZone timeZone = this.timeZoneProvider.get();
TimelineManager timelineManager = this.caseProvider.get().getTimelineManager(); 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 * Given activity by day, converts to most recent days' activity handling
* empty values. * empty values.
* *
* @param dateCounts The day from epoch mapped to activity amounts for that * @param dateCounts The day from epoch mapped to activity amounts for
* day. * that day.
* @param minRecentDay The minimum recent day in days from epoch. * @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. * @return The most recent daily activity amounts.
*/ */
private List<DailyActivityAmount> getMostRecentActivityAmounts(Map<Long, DailyActivityAmount> dateCounts, long minRecentDay, long maxDay) { 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. * 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 * @param timelineManager The timeline manager to use while fetching the
* data. * data.
* @param timeZone The time zone to use to determine which day activity * @param timeZone The time zone to use to determine which day
* belongs. * activity belongs.
*
* @return A Map mapping days from epoch to the activity for that day. * @return A Map mapping days from epoch to the activity for that day.
*
* @throws TskCoreException * @throws TskCoreException
* @throws NoCurrentCaseException
*/ */
private Map<Long, DailyActivityAmount> getTimelineEventsByDay(DataSource dataSource, TimelineManager timelineManager, TimeZone timeZone) private Map<Long, DailyActivityAmount> getTimelineEventsByDay(DataSource dataSource, TimelineManager timelineManager, TimeZone timeZone)
throws TskCoreException, NoCurrentCaseException { throws TskCoreException, SleuthkitCaseProviderException {
RootFilter rootFilter = this.filterFunction.apply(dataSource); RootFilter rootFilter = this.filterFunction.apply(dataSource);
// get events for data source // get events for data source
@ -251,12 +228,14 @@ public class TimelineSummary implements DefaultUpdateGovernor {
/** /**
* Main constructor. * Main constructor.
* *
* @param minDate Earliest usage date recorded for the data source. * @param minDate Earliest usage date recorded for the data
* @param maxDate Latest usage date recorded for the data source. * source.
* @param maxDate Latest usage date recorded for the data
* source.
* @param recentDaysActivity A list of activity prior to and including * @param recentDaysActivity A list of activity prior to and including
* max date sorted by min to max date. * max date sorted by min to max date.
* @param dataSource The data source for which this data applies. the * @param dataSource The data source for which this data
* latest usage date by day. * applies. the latest usage date by day.
*/ */
TimelineSummaryData(Date minDate, Date maxDate, List<DailyActivityAmount> recentDaysActivity, DataSource dataSource) { TimelineSummaryData(Date minDate, Date maxDate, List<DailyActivityAmount> recentDaysActivity, DataSource dataSource) {
this.minDate = minDate; this.minDate = minDate;
@ -281,7 +260,7 @@ public class TimelineSummary implements DefaultUpdateGovernor {
/** /**
* @return A list of activity prior to and including the latest usage * @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() { public List<DailyActivityAmount> getMostRecentDaysActivity() {
return histogramActivity; return histogramActivity;
@ -307,8 +286,10 @@ public class TimelineSummary implements DefaultUpdateGovernor {
/** /**
* Main constructor. * Main constructor.
* *
* @param day The day for which activity is being measured. * @param day The day for which activity is being
* @param fileActivityCount The amount of file activity timeline events. * measured.
* @param fileActivityCount The amount of file activity timeline
* events.
* @param artifactActivityCount The amount of artifact timeline events. * @param artifactActivityCount The amount of artifact timeline events.
*/ */
DailyActivityAmount(Date day, long fileActivityCount, long artifactActivityCount) { DailyActivityAmount(Date day, long fileActivityCount, long artifactActivityCount) {
@ -337,6 +318,29 @@ public class TimelineSummary implements DefaultUpdateGovernor {
public long getArtifactActivityCount() { public long getArtifactActivityCount() {
return artifactActivityCount; 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 * Autopsy Forensic Browser
* *
* Copyright 2019 - 2020 Basis Technology Corp. * Copyright 2019 - 2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -18,26 +18,19 @@
*/ */
package org.sleuthkit.autopsy.datasourcesummary.datamodel; package org.sleuthkit.autopsy.datasourcesummary.datamodel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultUpdateGovernor; import java.awt.Color;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set; import java.util.Set;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException; import org.sleuthkit.autopsy.coreutils.FileTypeUtils;
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.DataSource;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData; 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 { public class TypesSummary {
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS = new HashSet<>(
Arrays.asList(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED));
private final SleuthkitCaseProvider provider; private final SleuthkitCaseProvider provider;
@ -57,25 +50,6 @@ public class TypesSummary implements DefaultUpdateGovernor {
this.provider = provider; 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. * 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, return DataSourceInfoUtilities.getCountOfRegularFiles(provider.get(), currentDataSource,
"type=" + TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.getFileType()); "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 * Autopsy Forensic Browser
* *
* Copyright 2020 Basis Technology Corp. * Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -19,7 +19,6 @@
package org.sleuthkit.autopsy.datasourcesummary.datamodel; package org.sleuthkit.autopsy.datasourcesummary.datamodel;
import java.io.File; import java.io.File;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DefaultArtifactUpdateGovernor;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; 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 * time, the data being provided for domains is fictitious and is done as a
* placeholder. * placeholder.
*/ */
public class UserActivitySummary implements DefaultArtifactUpdateGovernor { public class UserActivitySummary {
/** /**
* Functions that determine the folder name of a list of path elements. If * 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())); .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> 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")); 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; this.logger = logger;
} }
@Override
public Set<Integer> getArtifactTypeIdsForRefresh() {
return ARTIFACT_UPDATE_TYPE_IDS;
}
/** /**
* Throws an IllegalArgumentException if count <= 0. * Throws an IllegalArgumentException if count <= 0.
* *
* @param count The count being checked. * @param count The count being checked.
*/ */
private void assertValidCount(int count) { private static void assertValidCount(int count) {
if (count <= 0) { if (count <= 0) {
throw new IllegalArgumentException("Count must be greater than 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. * Gets a list of recent domains based on the datasource.
* *
* @param dataSource The datasource to query for recent domains. * @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. * @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 * Creates a TopDomainsResult from data or null if no visit date exists
* within DOMAIN_WINDOW_MS of mostRecentMs. * within DOMAIN_WINDOW_MS of mostRecentMs.
* *
* @param domain The domain. * @param domain The domain.
* @param visits The list of the artifact and its associated time in * @param visits The list of the artifact and its associated time in
* milliseconds. * milliseconds.
* @param mostRecentMs The most recent visit of any domain. * @param mostRecentMs The most recent visit of any domain.
* *
* @return The TopDomainsResult or null if no visits to this domain within * @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) { private TopDomainsResult getDomainsResult(String domain, List<Pair<BlackboardArtifact, Long>> visits, long mostRecentMs) {
long visitCount = 0; long visitCount = 0;
@ -288,9 +305,9 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* @param dataSource The datasource. * @param dataSource The datasource.
* *
* @return A tuple where the first value is the latest web history accessed * @return A tuple where the first value is the latest web history accessed
* date in milliseconds and the second value maps normalized (lowercase; * date in milliseconds and the second value maps normalized
* trimmed) domain names to when those domains were visited and the relevant * (lowercase; trimmed) domain names to when those domains were
* artifact. * visited and the relevant artifact.
* *
* @throws TskCoreException * @throws TskCoreException
* @throws SleuthkitCaseProviderException * @throws SleuthkitCaseProviderException
@ -357,7 +374,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* @param artifact The artifact. * @param artifact The artifact.
* *
* @return The TopWebSearchResult or null if the search string or date * @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) { private static TopWebSearchResult getWebSearchResult(BlackboardArtifact artifact) {
String searchString = DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_TEXT); String searchString = DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_TEXT);
@ -372,10 +389,11 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* term. * term.
* *
* @param dataSource The data source. * @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 * @return The list of most recent web searches where most recent search
* appears first. * appears first.
* *
* @throws * @throws
* org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException * org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException
@ -462,6 +480,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* *
* @param r1 A result. * @param r1 A result.
* @param r2 Another result. * @param r2 Another result.
*
* @return The most recent one with a non-null date. * @return The most recent one with a non-null date.
*/ */
private TopDeviceAttachedResult getMostRecentDevice(TopDeviceAttachedResult r1, TopDeviceAttachedResult r2) { 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. * Retrieves most recent devices used by most recent date attached.
* *
* @param dataSource The data source. * @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 * @return The list of most recent devices attached where most recent device
* attached appears first. * attached appears first.
* *
* @throws * @throws
* org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException * org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException
@ -528,7 +548,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* @param artifact The artifact. * @param artifact The artifact.
* *
* @return The TopAccountResult or null if the account type or message date * @return The TopAccountResult or null if the account type or message date
* cannot be determined. * cannot be determined.
*/ */
private static TopAccountResult getMessageAccountResult(BlackboardArtifact artifact) { private static TopAccountResult getMessageAccountResult(BlackboardArtifact artifact) {
String type = DataSourceInfoUtilities.getStringOrNull(artifact, TYPE_MESSAGE_TYPE); 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 * Obtains a TopAccountResult from a blackboard artifact. The date is
* maximum of any found dates for attribute types provided. * 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 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 * @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) { private static TopAccountResult getAccountResult(BlackboardArtifact artifact, String messageType, BlackboardAttribute.Type... dateAttrs) {
String type = messageType; String type = messageType;
@ -638,39 +658,6 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
.collect(Collectors.toList()); .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. * 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. * be ignored and all items will be returned.
* *
* @param dataSource The datasource. If the datasource is null, an empty * @param dataSource The datasource. If the datasource is null, an empty
* list will be returned. * list will be returned.
* @param count The number of results to return. This value must be > 0 or * @param count The number of results to return. This value must be > 0
* an IllegalArgumentException will be thrown. * or an IllegalArgumentException will be thrown.
* *
* @return The sorted list and limited to the count if last run or run count * @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 SleuthkitCaseProviderException
* @throws TskCoreException * @throws TskCoreException
@ -840,7 +827,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* Main constructor. * Main constructor.
* *
* @param lastAccessed The date of last access. * @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) { public LastAccessedArtifact(Date lastAccessed, BlackboardArtifact artifact) {
this.lastAccessed = lastAccessed; this.lastAccessed = lastAccessed;
@ -875,7 +862,7 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* *
* @param searchString The search string. * @param searchString The search string.
* @param dateAccessed The latest date searched. * @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) { public TopWebSearchResult(String searchString, Date dateAccessed, BlackboardArtifact artifact) {
super(dateAccessed, artifact); super(dateAccessed, artifact);
@ -918,11 +905,11 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
/** /**
* Main constructor. * Main constructor.
* *
* @param deviceId The device id. * @param deviceId The device id.
* @param dateAccessed The date last attached. * @param dateAccessed The date last attached.
* @param deviceMake The device make. * @param deviceMake The device make.
* @param deviceModel The device model. * @param deviceModel The device model.
* @param artifact The relevant blackboard artifact. * @param artifact The relevant blackboard artifact.
*/ */
public TopDeviceAttachedResult(String deviceId, Date dateAccessed, String deviceMake, String deviceModel, BlackboardArtifact artifact) { public TopDeviceAttachedResult(String deviceId, Date dateAccessed, String deviceMake, String deviceModel, BlackboardArtifact artifact) {
super(dateAccessed, artifact); super(dateAccessed, artifact);
@ -965,8 +952,8 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* Main constructor. * Main constructor.
* *
* @param accountType The account type. * @param accountType The account type.
* @param lastAccess The date the account was last accessed. * @param lastAccess The date the account was last accessed.
* @param artifact The artifact indicating last access. * @param artifact The artifact indicating last access.
*/ */
public TopAccountResult(String accountType, Date lastAccess, BlackboardArtifact artifact) { public TopAccountResult(String accountType, Date lastAccess, BlackboardArtifact artifact) {
super(lastAccess, artifact); super(lastAccess, artifact);
@ -992,10 +979,10 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
/** /**
* Describes a top domain result. * Describes a top domain result.
* *
* @param domain The domain. * @param domain The domain.
* @param visitTimes The number of times it was visited. * @param visitTimes The number of times it was visited.
* @param lastVisit The date of the last visit. * @param lastVisit The date of the last visit.
* @param artifact The relevant blackboard artifact. * @param artifact The relevant blackboard artifact.
*/ */
public TopDomainsResult(String domain, Long visitTimes, Date lastVisit, BlackboardArtifact artifact) { public TopDomainsResult(String domain, Long visitTimes, Date lastVisit, BlackboardArtifact artifact) {
super(lastVisit, artifact); super(lastVisit, artifact);
@ -1032,8 +1019,8 @@ public class UserActivitySummary implements DefaultArtifactUpdateGovernor {
* *
* @param programName The name of the program. * @param programName The name of the program.
* @param programPath The path of the program. * @param programPath The path of the program.
* @param runTimes The number of runs. * @param runTimes The number of runs.
* @param artifact The relevant blackboard artifact. * @param artifact The relevant blackboard artifact.
*/ */
TopProgramsResult(String programName, String programPath, Long runTimes, Date lastRun, BlackboardArtifact artifact) { TopProgramsResult(String programName, String programPath, Long runTimes, Date lastRun, BlackboardArtifact artifact) {
super(lastRun, artifact); super(lastRun, artifact);

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2020 Basis Technology Corp. * Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -21,16 +21,12 @@ package org.sleuthkit.autopsy.datasourcesummary.ui;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import org.openide.util.NbBundle.Messages; 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.ColumnModel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker; 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.DefaultCellModel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport.ExcelSheetExport;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel;
import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.datamodel.DataSource;
@ -101,10 +97,10 @@ public class AnalysisPanel extends BaseDataSourceSummaryPanel {
* Creates a new DataSourceUserActivityPanel. * Creates a new DataSourceUserActivityPanel.
*/ */
public AnalysisPanel() { public AnalysisPanel() {
this(new AnalysisSummary()); this(new AnalysisSummaryGetter());
} }
public AnalysisPanel(AnalysisSummary analysisData) { public AnalysisPanel(AnalysisSummaryGetter analysisData) {
super(analysisData); super(analysisData);
hashsetsFetcher = (dataSource) -> analysisData.getHashsetCounts(dataSource); hashsetsFetcher = (dataSource) -> analysisData.getHashsetCounts(dataSource);
@ -229,17 +225,6 @@ public class AnalysisPanel extends BaseDataSourceSummaryPanel {
); );
}// </editor-fold>//GEN-END:initComponents }// </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 // Variables declaration - do not modify//GEN-BEGIN:variables
// End of variables declaration//GEN-END: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.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger; 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.DataFetchResult;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents; 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.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.DefaultMenuItem;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.GuiCellModel.MenuItem; import org.sleuthkit.autopsy.datasourcesummary.uiutils.GuiCellModel.MenuItem;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableComponent; import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableComponent;
@ -453,14 +448,6 @@ abstract class BaseDataSourceSummaryPanel extends JPanel {
*/ */
protected abstract void onNewDataSource(DataSource dataSource); 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 * Runs a data fetcher and returns the result handling any possible errors
* with a log message. * 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, * Utility method that shows a loading screen with loadable components,
* create swing workers from the datafetch components and data source * 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 AnalysisPanel_keywordSearchModuleName=Keyword Search
BaseDataSourceSummaryPanel_goToArtifact=View Source Result BaseDataSourceSummaryPanel_goToArtifact=View Source Result
BaseDataSourceSummaryPanel_goToFile=View Source File in Directory 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_setFieldsForNonImageDataSource_na=N/A
ContainerPanel_tabName=Container ContainerPanel_tabName=Container
CTL_DataSourceSummaryAction=Data Source Summary CTL_DataSourceSummaryAction=Data Source Summary
@ -62,7 +49,6 @@ DataSourceSummaryNode.column.type.header=Type
DataSourceSummaryNode.viewDataSourceAction.text=Go to Data Source DataSourceSummaryNode.viewDataSourceAction.text=Go to Data Source
DataSourceSummaryTabbedPane_analysisTab_title=Analysis DataSourceSummaryTabbedPane_analysisTab_title=Analysis
DataSourceSummaryTabbedPane_detailsTab_title=Container DataSourceSummaryTabbedPane_detailsTab_title=Container
DataSourceSummaryTabbedPane_exportTab_title=Export
DataSourceSummaryTabbedPane_geolocationTab_title=Geolocation DataSourceSummaryTabbedPane_geolocationTab_title=Geolocation
DataSourceSummaryTabbedPane_ingestHistoryTab_title=Ingest History DataSourceSummaryTabbedPane_ingestHistoryTab_title=Ingest History
DataSourceSummaryTabbedPane_pastCasesTab_title=Past Cases DataSourceSummaryTabbedPane_pastCasesTab_title=Past Cases
@ -70,45 +56,27 @@ DataSourceSummaryTabbedPane_recentFileTab_title=Recent Files
DataSourceSummaryTabbedPane_timelineTab_title=Timeline DataSourceSummaryTabbedPane_timelineTab_title=Timeline
DataSourceSummaryTabbedPane_typesTab_title=Types DataSourceSummaryTabbedPane_typesTab_title=Types
DataSourceSummaryTabbedPane_userActivityTab_title=User Activity DataSourceSummaryTabbedPane_userActivityTab_title=User Activity
ExcelExportAction_exportToXLSX_beginExport=Beginning Export... DataSourceUserActivitySummary_getRecentAccounts_calllogMessage=Call Log
# {0} - tabName DataSourceUserActivitySummary_getRecentAccounts_emailMessage=Email Message
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
GeolocationPanel_cityColumn_title=Closest City GeolocationPanel_cityColumn_title=Closest City
GeolocationPanel_countColumn_title=Count GeolocationPanel_countColumn_title=Count
GeolocationPanel_mostCommon_tabName=Most Common Cities GeolocationPanel_mostCommon_tabName=Most Common Cities
GeolocationPanel_mostRecent_tabName=Most Recent Cities GeolocationPanel_mostRecent_tabName=Most Recent Cities
GeolocationPanel_onNoCrIngest_message=No results will be shown because the GPX Parser was not run. GeolocationPanel_onNoCrIngest_message=No results will be shown because the GPX Parser was not run.
GeolocationPanel_unknownRow_title=Unknown 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_caseColumn_title=Case
PastCasesPanel_countColumn_title=Count PastCasesPanel_countColumn_title=Count
PastCasesPanel_notableFileTable_tabName=Cases with Common Notable PastCasesPanel_notableFileTable_tabName=Cases with Common Notable
PastCasesPanel_onNoCrIngest_message=No results will be shown because the Central Repository module was not run. 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 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_attachmentsTable_tabName=Recent Attachments
RecentFilesPanel_col_head_date=Date 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_docsTable_tabName=Recently Opened Documents
RecentFilesPanel_downloadsTable_tabName=Recently Downloads RecentFilesPanel_downloadsTable_tabName=Recently Downloads
RecentFilesPanel_no_open_documents=No recently open documents found.
SizeRepresentationUtil_units_bytes=bytes SizeRepresentationUtil_units_bytes=bytes
SizeRepresentationUtil_units_gigabytes=GB SizeRepresentationUtil_units_gigabytes=GB
SizeRepresentationUtil_units_kilobytes=KB SizeRepresentationUtil_units_kilobytes=KB
@ -116,12 +84,6 @@ SizeRepresentationUtil_units_megabytes=MB
SizeRepresentationUtil_units_petabytes=PB SizeRepresentationUtil_units_petabytes=PB
SizeRepresentationUtil_units_terabytes=TB SizeRepresentationUtil_units_terabytes=TB
TimelinePanel_earliestLabel_title=Earliest 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 TimelinePanel_latestLabel_title=Latest
TimlinePanel_last30DaysChart_artifactEvts_title=Result Events TimlinePanel_last30DaysChart_artifactEvts_title=Result Events
TimlinePanel_last30DaysChart_fileEvts_title=File Events TimlinePanel_last30DaysChart_fileEvts_title=File Events

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2019 Basis Technology Corp. * Copyright 2019-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -19,39 +19,23 @@
package org.sleuthkit.autopsy.datasourcesummary.ui; package org.sleuthkit.autopsy.datasourcesummary.ui;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import javax.swing.table.DefaultTableModel; import javax.swing.table.DefaultTableModel;
import org.apache.commons.lang.StringUtils;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.ContainerSummary; import org.sleuthkit.autopsy.datasourcesummary.datamodel.ContainerSummary.ContainerDetails;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException; import org.sleuthkit.autopsy.datasourcesummary.datamodel.ContainerSummary.ImageDetails;
import static org.sleuthkit.autopsy.datasourcesummary.ui.BaseDataSourceSummaryPanel.getFetchResult;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult.ResultType; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult.ResultType;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents; 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.DefaultUpdateGovernor; 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.autopsy.datasourcesummary.uiutils.UpdateGovernor;
import org.sleuthkit.datamodel.DataSource; 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 * Panel to display additional details associated with a specific DataSource
@ -61,182 +45,6 @@ import org.sleuthkit.datamodel.TskCoreException;
}) })
class ContainerPanel extends BaseDataSourceSummaryPanel { 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) // 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( private static final Set<Case.Events> CASE_EVENT_SET = new HashSet<>(Arrays.asList(
Case.Events.DATA_SOURCE_NAME_CHANGED 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 static final Logger logger = Logger.getLogger(ContainerPanel.class.getName());
private final List<DataFetchComponents<DataSource, ?>> dataFetchComponents; private final List<DataFetchComponents<DataSource, ?>> dataFetchComponents;
private final DataFetcher<DataSource, ContainerViewModel> containerDataFetcher; private final DataFetcher<DataSource, ContainerDetails> containerDataFetcher;
/** /**
* Creates a new form ContainerPanel. * Creates a new form ContainerPanel.
*/ */
ContainerPanel() { ContainerPanel() {
this(new ContainerSummary()); this(new ContainerSummaryGetter());
} }
/** /**
* Creates new form ContainerPanel. * Creates new form ContainerPanel.
*/ */
ContainerPanel(ContainerSummary containerSummary) { ContainerPanel(ContainerSummaryGetter containerSummary) {
super(containerSummary, CONTAINER_UPDATES); super(containerSummary, CONTAINER_UPDATES);
containerDataFetcher = (dataSource) -> getContainerViewModel(containerSummary, dataSource); containerDataFetcher = (dataSource) -> containerSummary.getContainerDetails(dataSource);
dataFetchComponents = Arrays.asList( dataFetchComponents = Arrays.asList(
new DataFetchComponents<>( new DataFetchComponents<>(
containerDataFetcher, containerDataFetcher,
(result) -> { (result) -> {
if (result != null && result.getResultType() == ResultType.SUCCESS) { if (result != null && result.getResultType() == ResultType.SUCCESS) {
ContainerViewModel data = result.getData(); ContainerDetails data = result.getData();
updateDetailsPanelData(data); updateDetailsPanelData(data);
} else { } else {
if (result == null) { if (result == null) {
@ -313,92 +121,12 @@ class ContainerPanel extends BaseDataSourceSummaryPanel {
fetchInformation(dataFetchComponents, dataSource); 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. * Update the swing components with fetched data.
* *
* @param viewModel The data source view model data. * @param viewModel The data source view model data.
*/ */
private void updateDetailsPanelData(ContainerViewModel viewModel) { private void updateDetailsPanelData(ContainerDetails viewModel) {
clearTableValues(); clearTableValues();
if (viewModel == null) { if (viewModel == null) {
return; return;
@ -409,8 +137,8 @@ class ContainerPanel extends BaseDataSourceSummaryPanel {
deviceIdValue.setText(viewModel.getDeviceId()); deviceIdValue.setText(viewModel.getDeviceId());
acquisitionDetailsTextArea.setText(viewModel.getAcquisitionDetails()); acquisitionDetailsTextArea.setText(viewModel.getAcquisitionDetails());
if (viewModel.getImageViewModel() != null) { if (viewModel.getImageDetails() != null) {
setFieldsForImage(viewModel.getImageViewModel()); setFieldsForImage(viewModel.getImageDetails());
} else { } else {
setFieldsForNonImageDataSource(); setFieldsForNonImageDataSource();
} }
@ -445,7 +173,7 @@ class ContainerPanel extends BaseDataSourceSummaryPanel {
* *
* @param viewModel The image view model data. * @param viewModel The image view model data.
*/ */
private void setFieldsForImage(ImageViewModel viewModel) { private void setFieldsForImage(ImageDetails viewModel) {
unallocatedSizeValue.setText(SizeRepresentationUtil.getSizeString(viewModel.getUnallocatedSize())); unallocatedSizeValue.setText(SizeRepresentationUtil.getSizeString(viewModel.getUnallocatedSize()));
imageTypeValue.setText(viewModel.getImageType()); imageTypeValue.setText(viewModel.getImageType());
sizeValue.setText(SizeRepresentationUtil.getSizeString(viewModel.getSize())); sizeValue.setText(SizeRepresentationUtil.getSizeString(viewModel.getSize()));
@ -480,84 +208,6 @@ class ContainerPanel extends BaseDataSourceSummaryPanel {
((DefaultTableModel) filePathsTable.getModel()).setRowCount(0); ((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. * 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 * 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; package org.sleuthkit.autopsy.datasourcesummary.ui;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.CaseDataSourcesSummary;
import java.awt.Cursor; import java.awt.Cursor;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.RightAlignedTableCellRenderer; import org.sleuthkit.autopsy.datasourcesummary.uiutils.RightAlignedTableCellRenderer;
import java.awt.EventQueue; import java.awt.EventQueue;
@ -42,7 +43,6 @@ import static javax.swing.SwingConstants.RIGHT;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import javax.swing.SwingWorker; import javax.swing.SwingWorker;
import javax.swing.table.TableColumn; import javax.swing.table.TableColumn;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.CaseDataSourcesSummary;
import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.IngestJobInfo; import org.sleuthkit.datamodel.IngestJobInfo;
import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.SleuthkitCase;

View File

@ -25,12 +25,9 @@ import java.util.Arrays;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.List; import java.util.List;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.IngestJobInfoPanel; 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; import org.sleuthkit.datamodel.DataSource;
/** /**
@ -46,8 +43,7 @@ import org.sleuthkit.datamodel.DataSource;
"DataSourceSummaryTabbedPane_pastCasesTab_title=Past Cases", "DataSourceSummaryTabbedPane_pastCasesTab_title=Past Cases",
"DataSourceSummaryTabbedPane_analysisTab_title=Analysis", "DataSourceSummaryTabbedPane_analysisTab_title=Analysis",
"DataSourceSummaryTabbedPane_geolocationTab_title=Geolocation", "DataSourceSummaryTabbedPane_geolocationTab_title=Geolocation",
"DataSourceSummaryTabbedPane_timelineTab_title=Timeline", "DataSourceSummaryTabbedPane_timelineTab_title=Timeline"
"DataSourceSummaryTabbedPane_exportTab_title=Export"
}) })
public class DataSourceSummaryTabbedPane extends javax.swing.JPanel { 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 * Records of tab information (i.e. title, component, function to call on
* new data source). * new data source).
*/ */
private class DataSourceTab implements ExportableTab { private class DataSourceTab {
private final String tabTitle; private final String tabTitle;
private final Component component; private final Component component;
private final Consumer<DataSource> onDataSource; private final Consumer<DataSource> onDataSource;
private final Function<DataSource, List<ExcelSheetExport>> excelExporter;
private final Runnable onClose; private final Runnable onClose;
private final Runnable onInit; 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. * @param panel The component to be displayed in the tab.
*/ */
DataSourceTab(String tabTitle, BaseDataSourceSummaryPanel panel) { 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()); panel.setParentCloseListener(() -> notifyParentClose());
} }
@ -90,12 +85,10 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
* added to the tabbed pane. * added to the tabbed pane.
*/ */
DataSourceTab(String tabTitle, Component component, Consumer<DataSource> onDataSource, DataSourceTab(String tabTitle, Component component, Consumer<DataSource> onDataSource,
Function<DataSource, List<ExcelSheetExport>> excelExporter, Runnable onClose, Runnable onClose, Runnable onInit) {
Runnable onInit) {
this.tabTitle = tabTitle; this.tabTitle = tabTitle;
this.component = component; this.component = component;
this.onDataSource = onDataSource; this.onDataSource = onDataSource;
this.excelExporter = excelExporter;
this.onClose = onClose; this.onClose = onClose;
this.onInit = onInit; this.onInit = onInit;
} }
@ -103,7 +96,6 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
/** /**
* @return The title for the tab. * @return The title for the tab.
*/ */
@Override
public String getTabTitle() { public String getTabTitle() {
return tabTitle; return tabTitle;
} }
@ -122,11 +114,6 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
return onDataSource; 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. * @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 Runnable notifyParentClose = null;
private final IngestJobInfoPanel ingestHistoryPanel = new IngestJobInfoPanel(); 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( private final List<DataSourceTab> tabs = Arrays.asList(
new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_typesTab_title(), new TypesPanel()), new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_typesTab_title(), new TypesPanel()),
new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_userActivityTab_title(), new UserActivityPanel()), new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_userActivityTab_title(), new UserActivityPanel()),
@ -168,22 +152,11 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
Bundle.DataSourceSummaryTabbedPane_ingestHistoryTab_title(), Bundle.DataSourceSummaryTabbedPane_ingestHistoryTab_title(),
ingestHistoryPanel, ingestHistoryPanel,
ingestHistoryPanel::setDataSource, ingestHistoryPanel::setDataSource,
IngestJobExcelExport::getExports,
null, null,
null), null),
new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_detailsTab_title(), new ContainerPanel()), new DataSourceTab(Bundle.DataSourceSummaryTabbedPane_detailsTab_title(), new ContainerPanel())
new DataSourceTab(
Bundle.DataSourceSummaryTabbedPane_exportTab_title(),
exportPanel,
null,
null,
null,
null)
); );
// the action that does the export
private final ExcelExportAction exportAction = new ExcelExportAction(tabs);
private DataSource dataSource = null; private DataSource dataSource = null;
private CardLayout cardLayout; private CardLayout cardLayout;
@ -243,9 +216,6 @@ public class DataSourceSummaryTabbedPane extends javax.swing.JPanel {
// set this to no datasource initially // set this to no datasource initially
cardLayout.show(this, NO_DATASOURCE_PANE); 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.util.actions.CallableSystemAction;
import org.openide.windows.TopComponent; import org.openide.windows.TopComponent;
import org.openide.windows.WindowManager; 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.CityCountsList;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.GeolocationSummary.CityData; import org.sleuthkit.autopsy.datasourcesummary.datamodel.GeolocationSummary.CityData;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.GeolocationSummary.CityRecordCount; 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.DataFetchResult;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents; 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.DefaultCellModel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel;
import org.sleuthkit.autopsy.geolocation.GeoFilter; import org.sleuthkit.autopsy.geolocation.GeoFilter;
@ -79,9 +77,9 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel {
* Main constructor. * Main constructor.
* *
* @param mostRecentData The data to be displayed in the most recent * @param mostRecentData The data to be displayed in the most recent
* table. * table.
* @param mostCommonData The data to be displayed in the most common * @param mostCommonData The data to be displayed in the most common
* table. * table.
*/ */
GeolocationViewModel(List<Pair<String, Integer>> mostRecentData, List<Pair<String, Integer>> mostCommonData) { GeolocationViewModel(List<Pair<String, Integer>> mostRecentData, List<Pair<String, Integer>> mostCommonData) {
this.mostRecentData = mostRecentData; this.mostRecentData = mostRecentData;
@ -147,7 +145,7 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel {
private final IngestRunningLabel ingestRunningLabel = new IngestRunningLabel(); private final IngestRunningLabel ingestRunningLabel = new IngestRunningLabel();
private final GeolocationSummary whereUsedData; private final GeolocationSummaryGetter whereUsedData;
private final DataFetcher<DataSource, GeolocationViewModel> geolocationFetcher; private final DataFetcher<DataSource, GeolocationViewModel> geolocationFetcher;
@ -155,15 +153,15 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel {
* Main constructor. * Main constructor.
*/ */
public GeolocationPanel() { public GeolocationPanel() {
this(new GeolocationSummary()); this(new GeolocationSummaryGetter());
} }
/** /**
* Main constructor. * 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); super(whereUsedData);
this.whereUsedData = whereUsedData; this.whereUsedData = whereUsedData;
@ -183,7 +181,7 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel {
* Means of rendering data to be shown in the tables. * Means of rendering data to be shown in the tables.
* *
* @param result The result of fetching data for a data source and * @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) { private void handleData(DataFetchResult<GeolocationViewModel> result) {
showCityContent(DataFetchResult.getSubResult(result, (dr) -> dr.getMostCommonData()), mostCommonTable, commonViewInGeolocationBtn); 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. * Retrieves the city name to display from the record.
* *
* @param record The record for the city to display. * @param record The record for the city to display.
*
* @return The display name (city, country). * @return The display name (city, country).
*/ */
private static String getCityName(CityRecord record) { private static String getCityName(CityRecord record) {
@ -221,6 +220,7 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel {
* formats the city name). * formats the city name).
* *
* @param cityCount The CityRecordCount representing a row. * @param cityCount The CityRecordCount representing a row.
*
* @return The city/count pair to be displayed as a row. * @return The city/count pair to be displayed as a row.
*/ */
private Pair<String, Integer> formatRecord(CityRecordCount cityCount) { private Pair<String, Integer> formatRecord(CityRecordCount cityCount) {
@ -239,7 +239,8 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel {
* 'unknown'). * 'unknown').
* *
* @param countsList The CityCountsList object representing the data to be * @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. * @return The list of city/count tuples to be displayed as a row.
*/ */
private List<Pair<String, Integer>> formatList(CityCountsList countsList) { 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 * Converts CityData from GeolocationSummaryGetter into data that can be
* put into table in this panel. * directly put into table in this panel.
* *
* @param cityData The city data. * @param cityData The city data.
*
* @return The view model data. * @return The view model data.
*/ */
private GeolocationViewModel convertToViewModel(CityData cityData) { private GeolocationViewModel convertToViewModel(CityData cityData) {
@ -280,8 +282,8 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel {
/** /**
* Shows data in a particular table. * Shows data in a particular table.
* *
* @param result The result to be displayed in the table. * @param result The result to be displayed in the table.
* @param table The table where the data will be displayed. * @param table The table where the data will be displayed.
* @param goToGeolocation The corresponding geolocation navigation button. * @param goToGeolocation The corresponding geolocation navigation button.
*/ */
private void showCityContent(DataFetchResult<List<Pair<String, Integer>>> result, JTablePanel<Pair<String, Integer>> table, JButton goToGeolocation) { 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. * Action to open the geolocation window.
* *
* @param dataSource The data source for which the window should filter. * @param dataSource The data source for which the window should filter.
* @param daysLimit The limit for how recently the waypoints should be (for * @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 * most recent table) or null for most recent filter to
* common table). * not be set (for most common table).
*/ */
private void openGeolocationWindow(DataSource dataSource, Integer daysLimit) { private void openGeolocationWindow(DataSource dataSource, Integer daysLimit) {
// notify dialog (if in dialog) should close. // notify dialog (if in dialog) should close.
@ -349,19 +351,6 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel {
onNewDataSource(dataFetchComponents, tables, dataSource); 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 @Override
public void close() { public void close() {
ingestRunningLabel.unregister(); 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; package org.sleuthkit.autopsy.datasourcesummary.ui;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.List; import java.util.List;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.PastCasesSummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.PastCasesSummary.PastCasesResult; 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.ColumnModel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents; 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.DefaultCellModel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel;
import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.datamodel.DataSource;
@ -84,19 +79,19 @@ public class PastCasesPanel extends BaseDataSourceSummaryPanel {
private final IngestRunningLabel ingestRunningLabel = new IngestRunningLabel(); private final IngestRunningLabel ingestRunningLabel = new IngestRunningLabel();
private final DataFetcher<DataSource, PastCasesResult> pastCasesFetcher; private final DataFetcher<DataSource, PastCasesResult> pastCasesFetcher;
public PastCasesPanel() { public PastCasesPanel() {
this(new PastCasesSummary()); this(new PastCasesSummaryGetter());
} }
/** /**
* Creates new form PastCasesPanel * Creates new form PastCasesPanel
*/ */
public PastCasesPanel(PastCasesSummary pastCaseData) { public PastCasesPanel(PastCasesSummaryGetter pastCaseData) {
super(pastCaseData); super(pastCaseData);
this.pastCasesFetcher = (dataSource) -> pastCaseData.getPastCasesData(dataSource); this.pastCasesFetcher = (dataSource) -> pastCaseData.getPastCasesData(dataSource);
// set up data acquisition methods // set up data acquisition methods
dataFetchComponents = Arrays.asList( dataFetchComponents = Arrays.asList(
new DataFetchWorker.DataFetchComponents<>( new DataFetchWorker.DataFetchComponents<>(
@ -128,19 +123,6 @@ public class PastCasesPanel extends BaseDataSourceSummaryPanel {
onNewDataSource(dataFetchComponents, tables, dataSource); 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 @Override
public void close() { public void close() {
ingestRunningLabel.unregister(); 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 * Autopsy Forensic Browser
* *
* Copyright 2020 Basis Technology Corp. * Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -27,20 +27,15 @@ import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.openide.util.NbBundle.Messages; 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.RecentAttachmentDetails;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary.RecentDownloadDetails; import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary.RecentDownloadDetails;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary.RecentFileDetails; 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.CellModelTableCellRenderer;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ColumnModel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.ColumnModel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker; 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.DefaultCellModel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.GuiCellModel.MenuItem; import org.sleuthkit.autopsy.datasourcesummary.uiutils.GuiCellModel.MenuItem;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel; 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 DataFetcher<DataSource, List<RecentAttachmentDetails>> attachmentsFetcher;
private final List<ColumnModel<RecentFileDetails, DefaultCellModel<?>>> docsTemplate = Arrays.asList( private final List<ColumnModel<RecentFileDetails, DefaultCellModel<?>>> docsTemplate = Arrays.asList(
new ColumnModel<>(Bundle.RecentFilePanel_col_header_path(), new ColumnModel<>(Bundle.RecentFilesPanel_col_header_path(),
(prog) -> { (prog) -> {
return new DefaultCellModel<>(prog.getPath()) return new DefaultCellModel<>(prog.getPath())
.setPopupMenuRetriever(getPopupFunct(prog)); .setPopupMenuRetriever(getPopupFunct(prog));
@ -80,12 +75,12 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
80)); 80));
private final List<ColumnModel<RecentDownloadDetails, DefaultCellModel<?>>> downloadsTemplate = Arrays.asList( private final List<ColumnModel<RecentDownloadDetails, DefaultCellModel<?>>> downloadsTemplate = Arrays.asList(
new ColumnModel<>(Bundle.RecentFilePanel_col_header_domain(), new ColumnModel<>(Bundle.RecentFilesPanel_col_header_domain(),
(prog) -> { (prog) -> {
return new DefaultCellModel<>(prog.getWebDomain()) return new DefaultCellModel<>(prog.getWebDomain())
.setPopupMenuRetriever(getPopupFunct(prog)); .setPopupMenuRetriever(getPopupFunct(prog));
}, 100), }, 100),
new ColumnModel<>(Bundle.RecentFilePanel_col_header_path(), new ColumnModel<>(Bundle.RecentFilesPanel_col_header_path(),
(prog) -> { (prog) -> {
return new DefaultCellModel<>(prog.getPath()) return new DefaultCellModel<>(prog.getPath())
.setPopupMenuRetriever(getPopupFunct(prog)); .setPopupMenuRetriever(getPopupFunct(prog));
@ -95,7 +90,7 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
80)); 80));
private final List<ColumnModel<RecentAttachmentDetails, DefaultCellModel<?>>> attachmentsTemplate = Arrays.asList( private final List<ColumnModel<RecentAttachmentDetails, DefaultCellModel<?>>> attachmentsTemplate = Arrays.asList(
new ColumnModel<>(Bundle.RecentFilePanel_col_header_path(), new ColumnModel<>(Bundle.RecentFilesPanel_col_header_path(),
(prog) -> { (prog) -> {
return new DefaultCellModel<>(prog.getPath()) return new DefaultCellModel<>(prog.getPath())
.setPopupMenuRetriever(getPopupFunct(prog)); .setPopupMenuRetriever(getPopupFunct(prog));
@ -103,7 +98,7 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
new ColumnModel<>(Bundle.RecentFilesPanel_col_head_date(), new ColumnModel<>(Bundle.RecentFilesPanel_col_head_date(),
getDateFunct(), getDateFunct(),
80), 80),
new ColumnModel<>(Bundle.RecentFilePanel_col_header_sender(), new ColumnModel<>(Bundle.RecentFilesPanel_col_header_sender(),
(prog) -> { (prog) -> {
return new DefaultCellModel<>(prog.getSender()) return new DefaultCellModel<>(prog.getSender())
.setPopupMenuRetriever(getPopupFunct(prog)); .setPopupMenuRetriever(getPopupFunct(prog));
@ -114,19 +109,18 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
*/ */
@Messages({ @Messages({
"RecentFilesPanel_col_head_date=Date", "RecentFilesPanel_col_head_date=Date",
"RecentFilePanel_col_header_domain=Domain", "RecentFilesPanel_col_header_domain=Domain",
"RecentFilePanel_col_header_path=Path", "RecentFilesPanel_col_header_path=Path",
"RecentFilePanel_col_header_sender=Sender", "RecentFilesPanel_col_header_sender=Sender"
"RecentFilePanel_emailParserModuleName=Email Parser"
}) })
public RecentFilesPanel() { public RecentFilesPanel() {
this(new RecentFilesSummary()); this(new RecentFilesGetter());
} }
/** /**
* Creates new form RecentFilesPanel * Creates new form RecentFilesPanel
*/ */
public RecentFilesPanel(RecentFilesSummary dataHandler) { public RecentFilesPanel(RecentFilesGetter dataHandler) {
super(dataHandler); super(dataHandler);
docsFetcher = (dataSource) -> dataHandler.getRecentlyOpenedDocuments(dataSource, 10); docsFetcher = (dataSource) -> dataHandler.getRecentlyOpenedDocuments(dataSource, 10);
downloadsFetcher = (dataSource) -> dataHandler.getRecentDownloads(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 * Returns a function that gets the date from the RecentFileDetails object
* converts into a DefaultCellModel to be displayed in a table. * 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() { private <T extends RecentFileDetails> Function<T, DefaultCellModel<?>> getDateFunct() {
return (T lastAccessed) -> { return (T lastAccessed) -> {
Function<Date, String> dateParser = (dt) -> dt == null ? "" : DATETIME_FORMAT.format(dt); 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)); .setPopupMenuRetriever(getPopupFunct(lastAccessed));
}; };
} }
@ -155,9 +150,10 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
* items. * items.
* *
* @param record The RecentFileDetails instance. * @param record The RecentFileDetails instance.
*
* @return The menu items list containing one action or navigating to the * @return The menu items list containing one action or navigating to the
* appropriate artifact/file and closing the data source summary dialog if * appropriate artifact/file and closing the data source summary
* open. * dialog if open.
*/ */
private Supplier<List<MenuItem>> getPopupFunct(RecentFileDetails record) { private Supplier<List<MenuItem>> getPopupFunct(RecentFileDetails record) {
return () -> { return () -> {
@ -190,16 +186,6 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
onNewDataSource(dataFetchComponents, tablePanelList, dataSource); 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 @Override
public void close() { public void close() {
ingestRunningLabel.unregister(); ingestRunningLabel.unregister();
@ -216,7 +202,7 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
} }
@Messages({ @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. * Setup the data model and columns for the recently open table.

View File

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

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2020 Basis Technology Corp. * Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -20,39 +20,29 @@ package org.sleuthkit.autopsy.datasourcesummary.ui;
import java.awt.Color; import java.awt.Color;
import java.text.DateFormat; import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.logging.Level; import java.util.logging.Level;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.joda.time.Interval; import org.joda.time.Interval;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.openide.util.actions.CallableSystemAction; import org.openide.util.actions.CallableSystemAction;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineDataSourceUtils; import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineDataSourceUtils;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineSummary; import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineSummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineSummary.DailyActivityAmount; import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineSummary.DailyActivityAmount;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TimelineSummary.TimelineSummaryData; 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.BarChartPanel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartSeries; import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartSeries.OrderedKey;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartPanel.OrderedKey;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartSeries.BarChartItem; import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartSeries.BarChartItem;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents; 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.BarChartSeries;
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.uiutils.IngestRunningLabel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableComponent; import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableComponent;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableLabel; 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 long serialVersionUID = 1L;
private static final String EARLIEST_LATEST_FORMAT_STR = "MMM d, yyyy"; 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 EARLIEST_LATEST_FORMAT = TimelineSummary.getUtcFormat(EARLIEST_LATEST_FORMAT_STR);
private static final DateFormat CHART_FORMAT = getUtcFormat("MMM d, yyyy"); private static final DateFormat CHART_FORMAT = TimelineSummary.getUtcFormat("MMM d, yyyy");
private static final int MOST_RECENT_DAYS_COUNT = 30; 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 // components displayed in the tab
private final IngestRunningLabel ingestRunningLabel = new IngestRunningLabel(); private final IngestRunningLabel ingestRunningLabel = new IngestRunningLabel();
private final LoadableLabel earliestLabel = new LoadableLabel(Bundle.TimelinePanel_earliestLabel_title()); 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; private final List<DataFetchComponents<DataSource, ?>> dataFetchComponents;
public TimelinePanel() { public TimelinePanel() {
this(new TimelineSummary()); this(new TimelineSummaryGetter());
} }
/** /**
* Creates new form PastCasesPanel * Creates new form PastCasesPanel
*/ */
public TimelinePanel(TimelineSummary timelineData) { public TimelinePanel(TimelineSummaryGetter timelineData) {
super(timelineData); super(timelineData);
dataFetcher = (dataSource) -> timelineData.getData(dataSource, MOST_RECENT_DAYS_COUNT); dataFetcher = (dataSource) -> timelineData.getData(dataSource, MOST_RECENT_DAYS_COUNT);
@ -126,29 +106,18 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
initComponents(); 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 FILE_EVT_COLOR = new Color(228, 22, 28);
private static final Color ARTIFACT_EVT_COLOR = new Color(21, 227, 100); private static final Color ARTIFACT_EVT_COLOR = new Color(21, 227, 100);
/** /**
* Converts DailyActivityAmount data retrieved from TimelineSummary into * Converts DailyActivityAmount data retrieved from TimelineSummaryGetter
* data to be displayed as a bar chart. * 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 * @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. * @return The data to be displayed in the BarChart.
*/ */
private List<BarChartSeries> parseChartData(List<DailyActivityAmount> recentDaysActivity, boolean showIntermediateDates) { private List<BarChartSeries> parseChartData(List<DailyActivityAmount> recentDaysActivity, boolean showIntermediateDates) {
@ -167,7 +136,7 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
long fileAmt = curItem.getFileActivityCount(); long fileAmt = curItem.getFileActivityCount();
long artifactAmt = curItem.getArtifactActivityCount() * 100; long artifactAmt = curItem.getArtifactActivityCount() * 100;
String formattedDate = (showIntermediateDates || i == 0 || i == recentDaysActivity.size() - 1) 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); OrderedKey thisKey = new OrderedKey(formattedDate, i);
fileEvtCounts.add(new BarChartItem(thisKey, fileAmt)); 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. * @param result The result to be displayed on this tab.
*/ */
private void handleResult(DataFetchResult<TimelineSummaryData> result) { private void handleResult(DataFetchResult<TimelineSummaryData> result) {
earliestLabel.showDataFetchResult(DataFetchResult.getSubResult(result, r -> formatDate(r.getMinDate(), EARLIEST_LATEST_FORMAT))); earliestLabel.showDataFetchResult(DataFetchResult.getSubResult(result, r -> TimelineSummary.formatDate(r.getMinDate(), EARLIEST_LATEST_FORMAT)));
latestLabel.showDataFetchResult(DataFetchResult.getSubResult(result, r -> formatDate(r.getMaxDate(), 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))); last30DaysChart.showDataFetchResult(DataFetchResult.getSubResult(result, r -> parseChartData(r.getMostRecentDaysActivity(), false)));
if (result != null if (result != null
@ -242,8 +211,8 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
* Action that occurs when 'View in Timeline' button is pressed. * Action that occurs when 'View in Timeline' button is pressed.
* *
* @param dataSource The data source to filter to. * @param dataSource The data source to filter to.
* @param minDate The min 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. * @param maxDate The max date for the zoom of the window.
*/ */
private void openFilteredChart(DataSource dataSource, Date minDate, Date maxDate) { private void openFilteredChart(DataSource dataSource, Date minDate, Date maxDate) {
OpenTimelineAction openTimelineAction = CallableSystemAction.get(OpenTimelineAction.class); OpenTimelineAction openTimelineAction = CallableSystemAction.get(OpenTimelineAction.class);
@ -266,7 +235,7 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
if (minDate != null && maxDate != null) { if (minDate != null && maxDate != null) {
timeSpan = new Interval(new DateTime(minDate), new DateTime(maxDate)); 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); logger.log(Level.WARNING, "Unable to view time range in Timeline view", ex);
} }
@ -293,43 +262,6 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
super.close(); 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. * 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 * 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 * Autopsy Forensic Browser
* *
* Copyright 2019 Basis Technology Corp. * Copyright 2019-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -23,31 +23,23 @@ import java.sql.SQLException;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.coreutils.FileTypeUtils.FileTypeCategory; import org.sleuthkit.autopsy.coreutils.FileTypeUtils.FileTypeCategory;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TypesSummary; import org.sleuthkit.autopsy.datasourcesummary.datamodel.DataSourceInfoUtilities;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.ContainerSummary; import org.sleuthkit.autopsy.datasourcesummary.datamodel.TypesSummary.FileTypeCategoryData;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.MimeTypeSummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException; import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult.ResultType; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult.ResultType;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker; import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents; 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.ExcelSpecialFormatExport;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport.KeyValueItemExportable;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableComponent; import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableComponent;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.LoadableLabel; 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.PieChartPanel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.PieChartItem; 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 * @param usefulContent True if this is useful content; false if there
* is 0 mime type information. * is 0 mime type information.
*/ */
public TypesPieChartData(List<PieChartItem> pieSlices, boolean usefulContent) { TypesPieChartData(List<PieChartItem> pieSlices, boolean usefulContent) {
this.pieSlices = pieSlices; this.pieSlices = pieSlices;
this.usefulContent = usefulContent; this.usefulContent = usefulContent;
} }
@ -103,78 +95,20 @@ class TypesPanel extends BaseDataSourceSummaryPanel {
/** /**
* @return The pie chart data. * @return The pie chart data.
*/ */
public List<PieChartItem> getPieSlices() { List<PieChartItem> getPieSlices() {
return pieSlices; return pieSlices;
} }
/** /**
* @return Whether or not the data is usefulContent. * @return Whether or not the data is usefulContent.
*/ */
public boolean isUsefulContent() { boolean isUsefulContent() {
return usefulContent; 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 long serialVersionUID = 1L;
private static final DecimalFormat INTEGER_SIZE_FORMAT = new DecimalFormat("#"); 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 IMAGES_COLOR = new Color(156, 39, 176);
private static final Color VIDEOS_COLOR = Color.YELLOW; 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; private static final Color NOT_ANALYZED_COLOR = Color.WHITE;
// All file type categories. // All file type categories.
private static final List<TypesPieCategory> FILE_MIME_TYPE_CATEGORIES = Arrays.asList( private static final List<FileTypeCategoryData> FILE_MIME_TYPE_CATEGORIES = Arrays.asList(
new TypesPieCategory(Bundle.TypesPanel_fileMimeTypesChart_images_title(), FileTypeCategory.IMAGE.getMediaTypes(), IMAGES_COLOR), new FileTypeCategoryData(Bundle.TypesPanel_fileMimeTypesChart_images_title(), FileTypeCategory.IMAGE.getMediaTypes(), IMAGES_COLOR),
new TypesPieCategory(Bundle.TypesPanel_fileMimeTypesChart_videos_title(), FileTypeCategory.VIDEO.getMediaTypes(), VIDEOS_COLOR), new FileTypeCategoryData(Bundle.TypesPanel_fileMimeTypesChart_videos_title(), FileTypeCategory.VIDEO.getMediaTypes(), VIDEOS_COLOR),
new TypesPieCategory(Bundle.TypesPanel_fileMimeTypesChart_audio_title(), FileTypeCategory.AUDIO.getMediaTypes(), AUDIO_COLOR), new FileTypeCategoryData(Bundle.TypesPanel_fileMimeTypesChart_audio_title(), FileTypeCategory.AUDIO.getMediaTypes(), AUDIO_COLOR),
new TypesPieCategory(Bundle.TypesPanel_fileMimeTypesChart_documents_title(), FileTypeCategory.DOCUMENTS.getMediaTypes(), DOCUMENTS_COLOR), new FileTypeCategoryData(Bundle.TypesPanel_fileMimeTypesChart_documents_title(), FileTypeCategory.DOCUMENTS.getMediaTypes(), DOCUMENTS_COLOR),
new TypesPieCategory(Bundle.TypesPanel_fileMimeTypesChart_executables_title(), FileTypeCategory.EXECUTABLE.getMediaTypes(), EXECUTABLES_COLOR), new FileTypeCategoryData(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) new FileTypeCategoryData(Bundle.TypesPanel_fileMimeTypesChart_unknown_title(), new HashSet<>(Arrays.asList("application/octet-stream")), UNKNOWN_COLOR)
); );
private final DataFetcher<DataSource, String> usageFetcher; private final DataFetcher<DataSource, String> usageFetcher;
@ -237,8 +171,8 @@ class TypesPanel extends BaseDataSourceSummaryPanel {
/** /**
* Creates a new TypesPanel. * Creates a new TypesPanel.
*/ */
public TypesPanel() { TypesPanel() {
this(new MimeTypeSummary(), new TypesSummary(), new ContainerSummary()); this(new MimeTypeSummaryGetter(), new TypesSummaryGetter(), new ContainerSummaryGetter());
} }
@Override @Override
@ -254,10 +188,10 @@ class TypesPanel extends BaseDataSourceSummaryPanel {
* @param typeData The service for file types data. * @param typeData The service for file types data.
* @param containerData The service for container information. * @param containerData The service for container information.
*/ */
public TypesPanel( TypesPanel(
MimeTypeSummary mimeTypeData, MimeTypeSummaryGetter mimeTypeData,
TypesSummary typeData, TypesSummaryGetter typeData,
ContainerSummary containerData) { ContainerSummaryGetter containerData) {
super(mimeTypeData, typeData, containerData); super(mimeTypeData, typeData, containerData);
@ -282,13 +216,13 @@ class TypesPanel extends BaseDataSourceSummaryPanel {
size -> SizeRepresentationUtil.getSizeString(size, INTEGER_SIZE_FORMAT, false)))), size -> SizeRepresentationUtil.getSizeString(size, INTEGER_SIZE_FORMAT, false)))),
new DataFetchWorker.DataFetchComponents<>(typesFetcher, this::showMimeTypeCategories), new DataFetchWorker.DataFetchComponents<>(typesFetcher, this::showMimeTypeCategories),
new DataFetchWorker.DataFetchComponents<>(allocatedFetcher, 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, 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, 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, new DataFetchWorker.DataFetchComponents<>(directoriesFetcher,
countRes -> directoriesLabel.showDataFetchResult(DataFetchResult.getSubResult(countRes, (count) -> getStringOrZero(count)))) countRes -> directoriesLabel.showDataFetchResult(DataFetchResult.getSubResult(countRes, (count) -> DataSourceInfoUtilities.getStringOrZero(count))))
); );
initComponents(); initComponents();
@ -312,7 +246,7 @@ class TypesPanel extends BaseDataSourceSummaryPanel {
* *
* @return The pie chart items. * @return The pie chart items.
*/ */
private TypesPieChartData getMimeTypeCategoriesModel(MimeTypeSummary mimeTypeData, DataSource dataSource) private TypesPieChartData getMimeTypeCategoriesModel(MimeTypeSummaryGetter mimeTypeData, DataSource dataSource)
throws SQLException, SleuthkitCaseProviderException, TskCoreException { throws SQLException, SleuthkitCaseProviderException, TskCoreException {
if (dataSource == null) { if (dataSource == null) {
@ -323,8 +257,8 @@ class TypesPanel extends BaseDataSourceSummaryPanel {
List<PieChartItem> fileCategoryItems = new ArrayList<>(); List<PieChartItem> fileCategoryItems = new ArrayList<>();
long categoryTotalCount = 0; long categoryTotalCount = 0;
for (TypesPieCategory cat : FILE_MIME_TYPE_CATEGORIES) { for (FileTypeCategoryData cat : FILE_MIME_TYPE_CATEGORIES) {
long thisValue = getLongOrZero(mimeTypeData.getCountOfFilesForMimeTypes(dataSource, cat.getMimeTypes())); long thisValue = DataSourceInfoUtilities.getLongOrZero(mimeTypeData.getCountOfFilesForMimeTypes(dataSource, cat.getMimeTypes()));
categoryTotalCount += thisValue; categoryTotalCount += thisValue;
fileCategoryItems.add(new PieChartItem( fileCategoryItems.add(new PieChartItem(
@ -334,10 +268,10 @@ class TypesPanel extends BaseDataSourceSummaryPanel {
} }
// get a count of all files with no mime type // 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 // 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 // create entry for mime types in other category
long otherCount = allRegularFiles - (categoryTotalCount + noMimeTypeCount); 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. * 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 * 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 * Autopsy Forensic Browser
* *
* Copyright 2020 Basis Technology Corp. * Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -26,23 +26,19 @@ import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary; import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.LastAccessedArtifact; import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.LastAccessedArtifact;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopAccountResult; import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopAccountResult;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopDeviceAttachedResult; 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.TopDomainsResult;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopProgramsResult; 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.ColumnModel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents; 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.DefaultCellModel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.GuiCellModel.MenuItem; import org.sleuthkit.autopsy.datasourcesummary.uiutils.GuiCellModel.MenuItem;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel; import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel;
@ -265,22 +261,22 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
private final IngestRunningLabel ingestRunningLabel = new IngestRunningLabel(); private final IngestRunningLabel ingestRunningLabel = new IngestRunningLabel();
private final List<DataFetchComponents<DataSource, ?>> dataFetchComponents; private final List<DataFetchComponents<DataSource, ?>> dataFetchComponents;
private final UserActivitySummary userActivityData; private final UserActivitySummaryGetter userActivityData;
/** /**
* Creates a new UserActivityPanel. * Creates a new UserActivityPanel.
*/ */
public UserActivityPanel() { public UserActivityPanel() {
this(new UserActivitySummary()); this(new UserActivitySummaryGetter());
} }
/** /**
* Creates a new UserActivityPanel. * Creates a new UserActivityPanel.
* *
* @param userActivityData Class from which to obtain remaining user * @param userActivityData Class from which to obtain remaining user
* activity data. * activity data.
*/ */
public UserActivityPanel(UserActivitySummary userActivityData) { public UserActivityPanel(UserActivitySummaryGetter userActivityData) {
super(userActivityData); super(userActivityData);
this.userActivityData = userActivityData; this.userActivityData = userActivityData;
@ -320,7 +316,7 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
private <T extends LastAccessedArtifact> Function<T, DefaultCellModel<?>> getDateFunct() { private <T extends LastAccessedArtifact> Function<T, DefaultCellModel<?>> getDateFunct() {
return (T lastAccessed) -> { return (T lastAccessed) -> {
Function<Date, String> dateParser = (dt) -> dt == null ? "" : DATETIME_FORMAT.format(dt); 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)); .setPopupMenu(getPopup(lastAccessed));
}; };
} }
@ -332,7 +328,8 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
* @param record The LastAccessedArtifact instance. * @param record The LastAccessedArtifact instance.
* *
* @return The menu items list containing one action or navigating to the * @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) { private List<MenuItem> getPopup(LastAccessedArtifact record) {
return record == null ? null : Arrays.asList(getArtifactNavigateItem(record.getArtifact())); 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. * 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. * @param appName The application name.
* *
* @return The underlying short folder name if one exists. * @return The underlying short folder name if one exists.
*/ */
private String getShortFolderName(String path, String appName) { private static String getShortFolderName(String path, String appName) {
return this.userActivityData.getShortFolderName(path, appName); return UserActivitySummary.getShortFolderName(path, appName);
} }
@Override @Override
@ -366,18 +363,6 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
super.close(); 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. * 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 * 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>> { 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 long serialVersionUID = 1L;
private static final Font DEFAULT_FONT = new JLabel().getFont(); private static final Font DEFAULT_FONT = new JLabel().getFont();

View File

@ -98,4 +98,82 @@ public class BarChartSeries {
return key; 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_errorMessage_defaultText=There was an error loading results.
AbstractLoadableComponent_loadingMessage_defaultText=Loading results... AbstractLoadableComponent_loadingMessage_defaultText=Loading results...
AbstractLoadableComponent_noDataExists_defaultText=No data exists. AbstractLoadableComponent_noDataExists_defaultText=No data exists.
# {0} - sheetNumber
ExcelExport_writeExcel_noSheetName=Sheet {0}
IngestRunningLabel_defaultMessage=Ingest is currently running. IngestRunningLabel_defaultMessage=Ingest is currently running.
PieChartPanel_noDataLabel=No Data PieChartPanel_noDataLabel=No Data

View File

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

View File

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

View File

@ -1,15 +1,15 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011 Basis Technology Corp. * Copyright 2011-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 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 { public class FileSearchAction extends AbstractAction {
private final Long dataSourceId;
public FileSearchAction(String title, long dataSourceID) {
super(title);
dataSourceId = dataSourceID;
}
public FileSearchAction(String title) { public FileSearchAction(String title) {
super(title); super(title);
dataSourceId = null;
} }
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
FileSearchProvider searcher = Lookup.getDefault().lookup(FileSearchProvider.class); 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 * Autopsy Forensic Browser
* *
* Copyright 2011 Basis Technology Corp. * Copyright 2011-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -23,5 +23,8 @@ package org.sleuthkit.autopsy.directorytree;
*/ */
public interface FileSearchProvider { public interface FileSearchProvider {
public void showDialog(Long dataSourceID);
@Deprecated
public void showDialog(); public void showDialog();
} }

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2020 Basis Technology Corp. * Copyright 2020-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -55,20 +55,24 @@ class DomainSearchCache {
* @param caseDb The case database. * @param caseDb The case database.
* @param centralRepoDb The central repository database. Can be null if * @param centralRepoDb The central repository database. Can be null if
* not needed. * not needed.
* @param context The SearchContext the search is being performed
* from.
* *
* @return Domain search results matching the given parameters. * @return Domain search results matching the given parameters.
* *
* @throws DiscoveryException * @throws DiscoveryException
* @throws SearchCancellationException - Thrown when the user has cancelled
* the search.
*/ */
Map<GroupKey, List<Result>> get(String userName, Map<GroupKey, List<Result>> get(String userName,
List<AbstractFilter> filters, List<AbstractFilter> filters,
DiscoveryAttributes.AttributeType groupAttributeType, DiscoveryAttributes.AttributeType groupAttributeType,
Group.GroupSortingAlgorithm groupSortingType, Group.GroupSortingAlgorithm groupSortingType,
ResultsSorter.SortingMethod domainSortingMethod, ResultsSorter.SortingMethod domainSortingMethod,
SleuthkitCase caseDb, CentralRepository centralRepoDb) throws DiscoveryException { SleuthkitCase caseDb, CentralRepository centralRepoDb, SearchContext context) throws DiscoveryException, SearchCancellationException {
try { try {
final SearchKey searchKey = new SearchKey(userName, filters, groupAttributeType, final SearchKey searchKey = new SearchKey(userName, filters, groupAttributeType,
groupSortingType, domainSortingMethod, caseDb, centralRepoDb); groupSortingType, domainSortingMethod, caseDb, centralRepoDb, context);
return cache.get(searchKey); return cache.get(searchKey);
} catch (ExecutionException ex) { } catch (ExecutionException ex) {
throw new DiscoveryException("Error fetching results from cache", ex.getCause()); 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(); throw new InterruptedException();
} }
attr.addAttributeToResults(domainResults, attr.addAttributeToResults(domainResults,
key.getSleuthkitCase(), key.getCentralRepository()); key.getSleuthkitCase(), key.getCentralRepository(), key.getContext());
} }
// Apply secondary in memory filters // Apply secondary in memory filters
for (AbstractFilter filter : key.getFilters()) { for (AbstractFilter filter : key.getFilters()) {
@ -81,7 +81,7 @@ class DomainSearchCacheLoader extends CacheLoader<SearchKey, Map<GroupKey, List<
throw new InterruptedException(); throw new InterruptedException();
} }
if (filter.useAlternateFilter()) { 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. // Sort the ResultDomains by the requested criteria.

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2019-2020 Basis Technology Corp. * Copyright 2019-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -62,17 +62,21 @@ public class FileSearch {
* @param caseDb The case database * @param caseDb The case database
* @param centralRepoDb The central repository database. Can be null if * @param centralRepoDb The central repository database. Can be null if
* not needed. * not needed.
* @param context The SearchContext the search is being performed
* from.
* *
* @return The raw search results * @return The raw search results
* *
* @throws DiscoveryException * @throws DiscoveryException
* @throws SearchCancellationException - Thrown when the user has cancelled
* the search.
*/ */
static SearchResults runFileSearchDebug(String userName, static SearchResults runFileSearchDebug(String userName,
List<AbstractFilter> filters, List<AbstractFilter> filters,
AttributeType groupAttributeType, AttributeType groupAttributeType,
Group.GroupSortingAlgorithm groupSortingType, Group.GroupSortingAlgorithm groupSortingType,
ResultsSorter.SortingMethod fileSortingMethod, 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 // 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 // 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 // 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()); attributesNeededForGroupingOrSorting.addAll(fileSortingMethod.getRequiredAttributes());
// Run the queries for each filter // 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 // 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 // Collect everything in the search results
SearchResults searchResults = new SearchResults(groupSortingType, groupAttributeType, fileSortingMethod); SearchResults searchResults = new SearchResults(groupSortingType, groupAttributeType, fileSortingMethod);
@ -114,21 +118,28 @@ public class FileSearch {
* @param caseDb The case database * @param caseDb The case database
* @param centralRepoDb The central repository database. Can be null if * @param centralRepoDb The central repository database. Can be null if
* not needed. * not needed.
* @param context The SearchContext the search is being performed
* from.
* *
* @return A LinkedHashMap grouped and sorted according to the parameters * @return A LinkedHashMap grouped and sorted according to the parameters
* *
* @throws DiscoveryException * @throws DiscoveryException
* @throws SearchCancellationException - Thrown when the user has cancelled
* the search.
*/ */
public static Map<GroupKey, Integer> getGroupSizes(String userName, public static Map<GroupKey, Integer> getGroupSizes(String userName,
List<AbstractFilter> filters, List<AbstractFilter> filters,
AttributeType groupAttributeType, AttributeType groupAttributeType,
Group.GroupSortingAlgorithm groupSortingType, Group.GroupSortingAlgorithm groupSortingType,
ResultsSorter.SortingMethod fileSortingMethod, 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, 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<>(); LinkedHashMap<GroupKey, Integer> groupSizes = new LinkedHashMap<>();
for (GroupKey groupKey : searchResults.keySet()) { 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()); groupSizes.put(groupKey, searchResults.get(groupKey).size());
} }
return groupSizes; return groupSizes;
@ -151,10 +162,14 @@ public class FileSearch {
* @param caseDb The case database * @param caseDb The case database
* @param centralRepoDb The central repository database. Can be null if * @param centralRepoDb The central repository database. Can be null if
* not needed. * not needed.
* @param context The SearchContext the search is being performed
* from.
* *
* @return A LinkedHashMap grouped and sorted according to the parameters * @return A LinkedHashMap grouped and sorted according to the parameters
* *
* @throws DiscoveryException * @throws DiscoveryException
* @throws SearchCancellationException - Thrown when the user has cancelled
* the search.
*/ */
public static List<Result> getFilesInGroup(String userName, public static List<Result> getFilesInGroup(String userName,
List<AbstractFilter> filters, List<AbstractFilter> filters,
@ -164,7 +179,7 @@ public class FileSearch {
GroupKey groupKey, GroupKey groupKey,
int startingEntry, int startingEntry,
int numberOfEntries, 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 //the group should be in the cache at this point
List<Result> filesInGroup = null; List<Result> filesInGroup = null;
SearchKey searchKey = new SearchKey(userName, filters, groupAttributeType, groupSortingType, fileSortingMethod); SearchKey searchKey = new SearchKey(userName, filters, groupAttributeType, groupSortingType, fileSortingMethod);
@ -178,7 +193,7 @@ public class FileSearch {
List<Result> page = new ArrayList<>(); List<Result> page = new ArrayList<>();
if (filesInGroup == null) { if (filesInGroup == null) {
logger.log(Level.INFO, "Group {0} was not cached, performing search to cache all groups again", groupKey); 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) { synchronized (searchCache) {
resultsMap = searchCache.getIfPresent(searchKey.getKeyString()); resultsMap = searchCache.getIfPresent(searchKey.getKeyString());
} }
@ -218,7 +233,6 @@ public class FileSearch {
TextSummarizer localSummarizer; TextSummarizer localSummarizer;
synchronized (searchCache) { synchronized (searchCache) {
localSummarizer = SummaryHelpers.getLocalSummarizer(); localSummarizer = SummaryHelpers.getLocalSummarizer();
} }
if (localSummarizer != null) { if (localSummarizer != null) {
try { try {
@ -247,17 +261,21 @@ public class FileSearch {
* @param caseDb The case database * @param caseDb The case database
* @param centralRepoDb The central repository database. Can be null if * @param centralRepoDb The central repository database. Can be null if
* not needed. * not needed.
* @param context The SearchContext the search is being performed
* from.
* *
* @return A LinkedHashMap grouped and sorted according to the parameters * @return A LinkedHashMap grouped and sorted according to the parameters
* *
* @throws DiscoveryException * @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, List<AbstractFilter> filters,
AttributeType groupAttributeType, AttributeType groupAttributeType,
Group.GroupSortingAlgorithm groupSortingType, Group.GroupSortingAlgorithm groupSortingType,
ResultsSorter.SortingMethod fileSortingMethod, 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 // 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 // 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()); attributesNeededForGroupingOrSorting.addAll(fileSortingMethod.getRequiredAttributes());
// Run the queries for each filter // 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 // 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 // Collect everything in the search results
SearchResults searchResults = new SearchResults(groupSortingType, groupAttributeType, fileSortingMethod); SearchResults searchResults = new SearchResults(groupSortingType, groupAttributeType, fileSortingMethod);
@ -295,13 +313,17 @@ public class FileSearch {
* @param caseDb The case database * @param caseDb The case database
* @param centralRepoDb The central repository database. Can be null if not * @param centralRepoDb The central repository database. Can be null if not
* needed. * needed.
* @param context The SearchContext the search is being performed
* from.
* *
* @throws DiscoveryException * @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) private static void addAttributes(List<AttributeType> attrs, List<Result> results, SleuthkitCase caseDb, CentralRepository centralRepoDb, SearchContext context)
throws DiscoveryException { throws DiscoveryException, SearchCancellationException {
for (AttributeType attr : attrs) { 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. * Copyright 2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
@ -16,17 +16,18 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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 * Returns true if the search has been cancelled, false otherwise.
* export or null if none necessary. *
* @return True if the search has been cancelled, false otherwise.
*/ */
String getExcelFormatString(); boolean searchIsCancelled();
} }

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy * Autopsy
* *
* Copyright 2019 Basis Technology Corp. * Copyright 2019-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -36,6 +36,8 @@ import org.sleuthkit.autopsy.discovery.search.DiscoveryException;
import org.sleuthkit.autopsy.discovery.search.DomainSearch; import org.sleuthkit.autopsy.discovery.search.DomainSearch;
import org.sleuthkit.autopsy.discovery.search.ResultsSorter; import org.sleuthkit.autopsy.discovery.search.ResultsSorter;
import org.sleuthkit.autopsy.discovery.search.Result; 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. * SwingWorker to retrieve the contents of a page.
@ -87,7 +89,7 @@ final class PageWorker extends SwingWorker<Void, Void> {
@Override @Override
protected Void doInBackground() throws Exception { protected Void doInBackground() throws Exception {
SearchContext context = new SwingWorkerSearchContext(this);
try { try {
// Run the search // Run the search
if (resultType == SearchData.Type.DOMAIN) { if (resultType == SearchData.Type.DOMAIN) {
@ -96,17 +98,22 @@ final class PageWorker extends SwingWorker<Void, Void> {
groupingAttribute, groupingAttribute,
groupSort, groupSort,
fileSortMethod, groupKey, startingEntry, pageSize, fileSortMethod, groupKey, startingEntry, pageSize,
Case.getCurrentCase().getSleuthkitCase(), centralRepo)); Case.getCurrentCase().getSleuthkitCase(), centralRepo, context));
} else { } else {
results.addAll(FileSearch.getFilesInGroup(System.getProperty(USER_NAME_PROPERTY), searchfilters, results.addAll(FileSearch.getFilesInGroup(System.getProperty(USER_NAME_PROPERTY), searchfilters,
groupingAttribute, groupingAttribute,
groupSort, groupSort,
fileSortMethod, groupKey, startingEntry, pageSize, fileSortMethod, groupKey, startingEntry, pageSize,
Case.getCurrentCase().getSleuthkitCase(), centralRepo)); Case.getCurrentCase().getSleuthkitCase(), centralRepo, context));
} }
} catch (DiscoveryException ex) { } 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); 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; return null;
} }

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy * Autopsy
* *
* Copyright 2019 Basis Technology Corp. * Copyright 2019-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -35,6 +35,8 @@ import org.sleuthkit.autopsy.discovery.search.FileSearch;
import org.sleuthkit.autopsy.discovery.search.DiscoveryException; import org.sleuthkit.autopsy.discovery.search.DiscoveryException;
import org.sleuthkit.autopsy.discovery.search.DomainSearch; import org.sleuthkit.autopsy.discovery.search.DomainSearch;
import org.sleuthkit.autopsy.discovery.search.ResultsSorter; 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; import org.sleuthkit.autopsy.discovery.search.SearchData;
/** /**
@ -75,23 +77,28 @@ final class SearchWorker extends SwingWorker<Void, Void> {
protected Void doInBackground() throws Exception { protected Void doInBackground() throws Exception {
try { try {
// Run the search // Run the search
SearchContext context = new SwingWorkerSearchContext(this);
if (searchType == SearchData.Type.DOMAIN) { if (searchType == SearchData.Type.DOMAIN) {
DomainSearch domainSearch = new DomainSearch(); DomainSearch domainSearch = new DomainSearch();
results.putAll(domainSearch.getGroupSizes(System.getProperty(USER_NAME_PROPERTY), filters, results.putAll(domainSearch.getGroupSizes(System.getProperty(USER_NAME_PROPERTY), filters,
groupingAttr, groupingAttr,
groupSortAlgorithm, groupSortAlgorithm,
fileSort, fileSort,
Case.getCurrentCase().getSleuthkitCase(), centralRepoDb)); Case.getCurrentCase().getSleuthkitCase(), centralRepoDb, context));
} else { } else {
results.putAll(FileSearch.getGroupSizes(System.getProperty(USER_NAME_PROPERTY), filters, results.putAll(FileSearch.getGroupSizes(System.getProperty(USER_NAME_PROPERTY), filters,
groupingAttr, groupingAttr,
groupSortAlgorithm, groupSortAlgorithm,
fileSort, fileSort,
Case.getCurrentCase().getSleuthkitCase(), centralRepoDb)); Case.getCurrentCase().getSleuthkitCase(), centralRepoDb, context));
} }
} catch (DiscoveryException ex) { } 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); 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; 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();
}
}

View File

@ -48,6 +48,15 @@ class DataSourceFilter extends AbstractFileSearchFilter<DataSourcePanel> {
return this.getComponent().isSelected(); return this.getComponent().isSelected();
} }
/**
* Set the data source filter to select the specified data source initially.
*
* @param dataSourceId - The data source to select.
*/
void setSelectedDataSource(long dataSourceId) {
this.getComponent().setDataSourceSelected(dataSourceId);
}
/** /**
* Reset the data source filter to be up to date with the current case. * Reset the data source filter to be up to date with the current case.
*/ */

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2018 Basis Technology Corp. * Copyright 2018-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -152,6 +152,19 @@ public class DataSourcePanel extends javax.swing.JPanel {
this.dataSourceNoteLabel.setEnabled(enabled); this.dataSourceNoteLabel.setEnabled(enabled);
} }
/**
* Set the data source initially selected in this filter.
*
* @param dataSourceId - The object ID of the data source which will be
* selected.
*/
void setDataSourceSelected(long dataSourceId) {
this.dataSourceCheckBox.setSelected(true);
setComponentsEnabled();
String dataSourceName = dataSourceMap.get(dataSourceId);
dataSourceList.setSelectedValue(dataSourceName, true);
}
/** /**
* This method is called from within the constructor to initialize the form. * 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 * WARNING: Do NOT modify this code. The content of this method is always

View File

@ -32,6 +32,7 @@ final class FileSearchAction extends CallableSystemAction implements FileSearchP
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static FileSearchAction instance = null; private static FileSearchAction instance = null;
private static FileSearchDialog searchDialog; private static FileSearchDialog searchDialog;
private static Long selectedDataSourceId;
FileSearchAction() { FileSearchAction() {
super(); super();
@ -39,7 +40,7 @@ final class FileSearchAction extends CallableSystemAction implements FileSearchP
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), (PropertyChangeEvent evt) -> { Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), (PropertyChangeEvent evt) -> {
if (evt.getPropertyName().equals(Case.Events.CURRENT_CASE.toString())) { if (evt.getPropertyName().equals(Case.Events.CURRENT_CASE.toString())) {
setEnabled(evt.getNewValue() != null); setEnabled(evt.getNewValue() != null);
if (searchDialog != null && evt.getNewValue() != null){ if (searchDialog != null && evt.getNewValue() != null) {
searchDialog.resetCaseDependentFilters(); searchDialog.resetCaseDependentFilters();
} }
} }
@ -57,7 +58,9 @@ final class FileSearchAction extends CallableSystemAction implements FileSearchP
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
if (searchDialog == null) { if (searchDialog == null) {
searchDialog = new FileSearchDialog(); searchDialog = new FileSearchDialog();
} }
//Preserve whatever the previously selected data source was
selectedDataSourceId = null;
searchDialog.setVisible(true); searchDialog.setVisible(true);
} }
@ -66,6 +69,8 @@ final class FileSearchAction extends CallableSystemAction implements FileSearchP
if (searchDialog == null) { if (searchDialog == null) {
searchDialog = new FileSearchDialog(); searchDialog = new FileSearchDialog();
} }
//
searchDialog.setSelectedDataSourceFilter(selectedDataSourceId);
searchDialog.setVisible(true); searchDialog.setVisible(true);
} }
@ -85,7 +90,15 @@ final class FileSearchAction extends CallableSystemAction implements FileSearchP
} }
@Override @Override
public void showDialog() { public void showDialog(Long dataSourceId) {
selectedDataSourceId = dataSourceId;
performAction(); performAction();
}
@Override
@Deprecated
public void showDialog() {
showDialog(null);
} }
} }

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2018 Basis Technology Corp. * Copyright 2011-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -29,6 +29,8 @@ import org.openide.windows.WindowManager;
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
final class FileSearchDialog extends javax.swing.JDialog { final class FileSearchDialog extends javax.swing.JDialog {
private static final long serialVersionUID = 1L;
/** /**
* Creates new form FileSearchDialog * Creates new form FileSearchDialog
*/ */
@ -48,6 +50,15 @@ final class FileSearchDialog extends javax.swing.JDialog {
}); });
} }
/**
* Set the data source filter to select the specified data source initially.
*
* @param dataSourceId - The data source to select.
*/
void setSelectedDataSourceFilter(long dataSourceId) {
fileSearchPanel1.setDataSourceFilter(dataSourceId);
}
/** /**
* Reset the filters which are populated with options based on the contents * Reset the filters which are populated with options based on the contents
* of the current case. * of the current case.

View File

@ -57,13 +57,13 @@ import org.sleuthkit.datamodel.TskCoreException;
*/ */
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
class FileSearchPanel extends javax.swing.JPanel { class FileSearchPanel extends javax.swing.JPanel {
private static final Logger logger = Logger.getLogger(FileSearchPanel.class.getName()); private static final Logger logger = Logger.getLogger(FileSearchPanel.class.getName());
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private final List<FileSearchFilter> filters = new ArrayList<>(); private final List<FileSearchFilter> filters = new ArrayList<>();
private static int resultWindowCount = 0; //keep track of result windows so they get unique names private static int resultWindowCount = 0; //keep track of result windows so they get unique names
private static MimeTypeFilter mimeTypeFilter = new MimeTypeFilter(); private static final MimeTypeFilter mimeTypeFilter = new MimeTypeFilter();
private static DataSourceFilter dataSourceFilter = new DataSourceFilter(); private static final DataSourceFilter dataSourceFilter = new DataSourceFilter();
private static final String EMPTY_WHERE_CLAUSE = NbBundle.getMessage(DateSearchFilter.class, "FileSearchPanel.emptyWhereClause.text"); private static final String EMPTY_WHERE_CLAUSE = NbBundle.getMessage(DateSearchFilter.class, "FileSearchPanel.emptyWhereClause.text");
private static SwingWorker<TableFilterNode, Void> searchWorker = null; private static SwingWorker<TableFilterNode, Void> searchWorker = null;
@ -106,7 +106,6 @@ class FileSearchPanel extends javax.swing.JPanel {
DateSearchFilter dateFilter = new DateSearchFilter(); DateSearchFilter dateFilter = new DateSearchFilter();
KnownStatusSearchFilter knowStatusFilter = new KnownStatusSearchFilter(); KnownStatusSearchFilter knowStatusFilter = new KnownStatusSearchFilter();
HashSearchFilter hashFilter = new HashSearchFilter(); HashSearchFilter hashFilter = new HashSearchFilter();
panel2.add(new FilterArea(NbBundle.getMessage(this.getClass(), "FileSearchPanel.filterTitle.name"), nameFilter)); panel2.add(new FilterArea(NbBundle.getMessage(this.getClass(), "FileSearchPanel.filterTitle.name"), nameFilter));
panel3.add(new FilterArea(NbBundle.getMessage(this.getClass(), "FileSearchPanel.filterTitle.metadata"), sizeFilter)); panel3.add(new FilterArea(NbBundle.getMessage(this.getClass(), "FileSearchPanel.filterTitle.metadata"), sizeFilter));
@ -149,6 +148,15 @@ class FileSearchPanel extends javax.swing.JPanel {
searchButton.setEnabled(isValidSearch()); searchButton.setEnabled(isValidSearch());
} }
/**
* Set the data source filter to select the specified data source initially.
*
* @param dataSourceId - The data source to select.
*/
void setDataSourceFilter(long dataSourceId) {
dataSourceFilter.setSelectedDataSource(dataSourceId);
}
/** /**
* @return true if any of the filters in the panel are enabled (checked) * @return true if any of the filters in the panel are enabled (checked)
*/ */
@ -334,7 +342,7 @@ class FileSearchPanel extends javax.swing.JPanel {
return enabledFilters; return enabledFilters;
} }
/** /**
* Reset the filters which are populated with options based on the contents * Reset the filters which are populated with options based on the contents
* of the current case. * of the current case.

View File

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

View File

@ -115,7 +115,7 @@ IngestJobSettingsPanel.jButtonSelectAll.text=Select All
IngestJobSettingsPanel.jButtonDeselectAll.text=Deselect All IngestJobSettingsPanel.jButtonDeselectAll.text=Deselect All
IngestManager.cancellingIngest.msgDlg.text=Cancelling all currently running ingest jobs IngestManager.cancellingIngest.msgDlg.text=Cancelling all currently running ingest jobs
IngestManager.serviceIsDown.msgDlg.text={0} is down IngestManager.serviceIsDown.msgDlg.text={0} is down
ProfilePanel.messages.profileNameContainsIllegalCharacter=Profile name contains an illegal character ProfilePanel.messages.profileNameContainsIllegalCharacter=Profile name contains an illegal character. Only \nletters, digits, and underscore characters are allowed.
ProfilePanel.messages.profilesMustBeNamed=Ingest profile must be named. ProfilePanel.messages.profilesMustBeNamed=Ingest profile must be named.
ProfilePanel.newProfileText=NewEmptyProfile ProfilePanel.newProfileText=NewEmptyProfile
ProfilePanel.profileDescLabel.text=Description: ProfilePanel.profileDescLabel.text=Description:

View File

@ -135,7 +135,6 @@ ModuleTableModel.colName.module=\u30e2\u30b8\u30e5\u30fc\u30eb
OpenIDE-Module-Name=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8 OpenIDE-Module-Name=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8
OptionsCategory_Keywords_IngestOptions=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8 OptionsCategory_Keywords_IngestOptions=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8
OptionsCategory_Name_IngestOptions=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8 OptionsCategory_Name_IngestOptions=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8
ProfilePanel.messages.profileNameContainsIllegalCharacter=\u30d7\u30ed\u30d5\u30a1\u30a4\u30eb\u540d\u306b\u4e0d\u6b63\u306a\u6587\u5b57\u304c\u542b\u307e\u308c\u3066\u3044\u307e\u3059
ProfilePanel.messages.profilesMustBeNamed=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30d7\u30ed\u30d5\u30a1\u30a4\u30eb\u306b\u540d\u524d\u3092\u4ed8\u3051\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002 ProfilePanel.messages.profilesMustBeNamed=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u30d7\u30ed\u30d5\u30a1\u30a4\u30eb\u306b\u540d\u524d\u3092\u4ed8\u3051\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002
ProfilePanel.newProfileText=NewEmptyProfile ProfilePanel.newProfileText=NewEmptyProfile
ProfilePanel.profileDescLabel.text=\u8aac\u660e\: ProfilePanel.profileDescLabel.text=\u8aac\u660e\:

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2018 Basis Technology Corp. * Copyright 2011-2021 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -19,10 +19,6 @@
package org.sleuthkit.autopsy.ingest; package org.sleuthkit.autopsy.ingest;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.openide.DialogDisplayer; import org.openide.DialogDisplayer;
import org.openide.NotifyDescriptor; import org.openide.NotifyDescriptor;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
@ -39,13 +35,12 @@ class ProfilePanel extends IngestModuleGlobalSettingsPanel {
"ProfilePanel.profileNameLabel.text=Profile Name:", "ProfilePanel.profileNameLabel.text=Profile Name:",
"ProfilePanel.newProfileText=NewEmptyProfile", "ProfilePanel.newProfileText=NewEmptyProfile",
"ProfilePanel.messages.profilesMustBeNamed=Ingest profile must be named.", "ProfilePanel.messages.profilesMustBeNamed=Ingest profile must be named.",
"ProfilePanel.messages.profileNameContainsIllegalCharacter=Profile name contains an illegal character"}) "ProfilePanel.messages.profileNameContainsIllegalCharacter=Profile name contains an illegal character. Only \nletters, digits, and underscore characters are allowed."})
private final IngestJobSettingsPanel ingestSettingsPanel; private final IngestJobSettingsPanel ingestSettingsPanel;
private final IngestJobSettings settings; private final IngestJobSettings settings;
private IngestProfile profile; private IngestProfile profile;
private final static String NEW_PROFILE_NAME = NbBundle.getMessage(ProfilePanel.class, "ProfilePanel.newProfileText"); private final static String NEW_PROFILE_NAME = NbBundle.getMessage(ProfilePanel.class, "ProfilePanel.newProfileText");
private static final List<String> ILLEGAL_NAME_CHARS = Collections.unmodifiableList(new ArrayList<>(Arrays.asList("\\", "/", ":", "*", "?", "\"", "<", ">")));
/** /**
* Creates new form ProfilePanel * Creates new form ProfilePanel
@ -231,8 +226,12 @@ class ProfilePanel extends IngestModuleGlobalSettingsPanel {
/** /**
* Save a new or edited profile. * Save a new or edited profile.
*/ */
void store() { boolean store() {
if (!isValidDefinition(false)) {
return false;
}
saveSettings(); saveSettings();
return true;
} }
void load() { void load() {
@ -240,41 +239,33 @@ class ProfilePanel extends IngestModuleGlobalSettingsPanel {
/** /**
* Checks that information entered constitutes a valid ingest profile. * Checks that information entered constitutes a valid ingest profile.
*
* @param dispayWarnings boolean flag whether to display warnings if an error occurred.
* *
* @return true for valid, false for invalid. * @return true for valid, false for invalid.
*/ */
boolean isValidDefinition() { boolean isValidDefinition(boolean dispayWarnings) {
if (getProfileName().isEmpty()) { String profileName = getProfileName();
NotifyDescriptor notifyDesc = new NotifyDescriptor.Message( if (profileName.isEmpty()) {
NbBundle.getMessage(ProfilePanel.class, "ProfilePanel.messages.profilesMustBeNamed"), if (dispayWarnings) {
NotifyDescriptor.WARNING_MESSAGE); NotifyDescriptor notifyDesc = new NotifyDescriptor.Message(
DialogDisplayer.getDefault().notify(notifyDesc); NbBundle.getMessage(ProfilePanel.class, "ProfilePanel.messages.profilesMustBeNamed"),
return false; NotifyDescriptor.WARNING_MESSAGE);
} DialogDisplayer.getDefault().notify(notifyDesc);
if (!containsOnlyLegalChars(getProfileName(), ILLEGAL_NAME_CHARS)) {
NotifyDescriptor notifyDesc = new NotifyDescriptor.Message(
NbBundle.getMessage(ProfilePanel.class, "ProfilePanel.messages.profileNameContainsIllegalCharacter"),
NotifyDescriptor.WARNING_MESSAGE);
DialogDisplayer.getDefault().notify(notifyDesc);
return false;
}
return true;
}
/**
* Checks an input string for the use of illegal characters.
*
* @param toBeChecked The input string.
* @param illegalChars The characters deemed to be illegal.
*
* @return True if the string does not contain illegal characters, false
* otherwise.
*/
private static boolean containsOnlyLegalChars(String toBeChecked, List<String> illegalChars) {
for (String illegalChar : illegalChars) {
if (toBeChecked.contains(illegalChar)) {
return false;
} }
return false;
}
// check if the name contains illegal characters
String sanitizedName = profileName.replaceAll("[^A-Za-z0-9_]", "");
if (!(profileName.equals(sanitizedName))) {
if (dispayWarnings) {
NotifyDescriptor notifyDesc = new NotifyDescriptor.Message(
NbBundle.getMessage(ProfilePanel.class, "ProfilePanel.messages.profileNameContainsIllegalCharacter"),
NotifyDescriptor.WARNING_MESSAGE);
DialogDisplayer.getDefault().notify(notifyDesc);
}
return false;
} }
return true; return true;
} }

View File

@ -416,7 +416,7 @@ class ProfileSettingsPanel extends IngestModuleGlobalSettingsPanel implements Op
do { do {
option = JOptionPane.CANCEL_OPTION; option = JOptionPane.CANCEL_OPTION;
dialog.display(panel); dialog.display(panel);
} while (option == JOptionPane.OK_OPTION && !panel.isValidDefinition()); } while (option == JOptionPane.OK_OPTION && !panel.isValidDefinition(true));
if (option == JOptionPane.OK_OPTION) { if (option == JOptionPane.OK_OPTION) {

View File

@ -10,7 +10,6 @@ OpenIDE-Module-Short-Description=Embedded File Extraction Ingest Module
EmbeddedFileExtractorIngestModule.SevenZipContentReadStream.seek.exception.invalidOrigin=Invalid seek origin: {0} EmbeddedFileExtractorIngestModule.SevenZipContentReadStream.seek.exception.invalidOrigin=Invalid seek origin: {0}
EmbeddedFileExtractorIngestModule.SevenZipContentReadStream.read.exception.errReadStream=Error reading content stream. EmbeddedFileExtractorIngestModule.SevenZipContentReadStream.read.exception.errReadStream=Error reading content stream.
EmbeddedFileExtractorIngestModule.ArchiveExtractor.encryptionFileLevel=Content-only Encryption (Archive File) EmbeddedFileExtractorIngestModule.ArchiveExtractor.encryptionFileLevel=Content-only Encryption (Archive File)
EmbeddedFileExtractorIngestModule.ArchiveExtractor.encryptionFull=Full Encryption (Archive File)
EmbeddedFileExtractorIngestModule.ArchiveExtractor.init.errInitModule.details=Error initializing output dir: {0}: {1} EmbeddedFileExtractorIngestModule.ArchiveExtractor.init.errInitModule.details=Error initializing output dir: {0}: {1}
EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnMsg=Possible ZIP bomb detected in archive: {0}, item: {1} EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnMsg=Possible ZIP bomb detected in archive: {0}, item: {1}
EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnDetails=Compression ratio is {0}, skipping items in {1}. EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnDetails=Compression ratio is {0}, skipping items in {1}.

View File

@ -23,7 +23,6 @@ OpenIDE-Module-Short-Description=Embedded File Extraction Ingest Module
EmbeddedFileExtractorIngestModule.SevenZipContentReadStream.seek.exception.invalidOrigin=Invalid seek origin: {0} EmbeddedFileExtractorIngestModule.SevenZipContentReadStream.seek.exception.invalidOrigin=Invalid seek origin: {0}
EmbeddedFileExtractorIngestModule.SevenZipContentReadStream.read.exception.errReadStream=Error reading content stream. EmbeddedFileExtractorIngestModule.SevenZipContentReadStream.read.exception.errReadStream=Error reading content stream.
EmbeddedFileExtractorIngestModule.ArchiveExtractor.encryptionFileLevel=Content-only Encryption (Archive File) EmbeddedFileExtractorIngestModule.ArchiveExtractor.encryptionFileLevel=Content-only Encryption (Archive File)
EmbeddedFileExtractorIngestModule.ArchiveExtractor.encryptionFull=Full Encryption (Archive File)
EmbeddedFileExtractorIngestModule.ArchiveExtractor.init.errInitModule.details=Error initializing output dir: {0}: {1} EmbeddedFileExtractorIngestModule.ArchiveExtractor.init.errInitModule.details=Error initializing output dir: {0}: {1}
EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnMsg=Possible ZIP bomb detected in archive: {0}, item: {1} EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnMsg=Possible ZIP bomb detected in archive: {0}, item: {1}
EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnDetails=Compression ratio is {0}, skipping items in {1}. EmbeddedFileExtractorIngestModule.ArchiveExtractor.isZipBombCheck.warnDetails=Compression ratio is {0}, skipping items in {1}.

View File

@ -58,6 +58,7 @@ import org.sleuthkit.autopsy.casemodule.services.FileManager;
import org.sleuthkit.autopsy.coreutils.FileUtil; import org.sleuthkit.autopsy.coreutils.FileUtil;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.modules.encryptiondetection.EncryptionDetectionModuleFactory;
import org.sleuthkit.autopsy.ingest.IngestJobContext; import org.sleuthkit.autopsy.ingest.IngestJobContext;
import org.sleuthkit.autopsy.ingest.IngestMessage; import org.sleuthkit.autopsy.ingest.IngestMessage;
import org.sleuthkit.autopsy.ingest.IngestMonitor; import org.sleuthkit.autopsy.ingest.IngestMonitor;
@ -95,8 +96,7 @@ class SevenZipExtractor {
//encryption type strings //encryption type strings
private static final String ENCRYPTION_FILE_LEVEL = NbBundle.getMessage(EmbeddedFileExtractorIngestModule.class, private static final String ENCRYPTION_FILE_LEVEL = NbBundle.getMessage(EmbeddedFileExtractorIngestModule.class,
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.encryptionFileLevel"); "EmbeddedFileExtractorIngestModule.ArchiveExtractor.encryptionFileLevel");
private static final String ENCRYPTION_FULL = NbBundle.getMessage(EmbeddedFileExtractorIngestModule.class, private static final String ENCRYPTION_FULL = EncryptionDetectionModuleFactory.PASSWORD_PROTECT_MESSAGE;
"EmbeddedFileExtractorIngestModule.ArchiveExtractor.encryptionFull");
//zip bomb detection //zip bomb detection
private static final int MAX_DEPTH = 4; private static final int MAX_DEPTH = 4;

View File

@ -83,7 +83,7 @@ final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter
private Blackboard blackboard; private Blackboard blackboard;
private IngestJobContext context; private IngestJobContext context;
private double calculatedEntropy; private double calculatedEntropy;
private final double minimumEntropy; private final double minimumEntropy;
private final int minimumFileSize; private final int minimumFileSize;
private final boolean fileSizeMultipleEnforced; private final boolean fileSizeMultipleEnforced;
@ -119,7 +119,6 @@ final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter
} }
@Messages({ @Messages({
"EncryptionDetectionFileIngestModule.artifactComment.password=Password protection detected.",
"EncryptionDetectionFileIngestModule.artifactComment.suspected=Suspected encryption due to high entropy (%f)." "EncryptionDetectionFileIngestModule.artifactComment.suspected=Suspected encryption due to high entropy (%f)."
}) })
@Override @Override
@ -160,7 +159,7 @@ final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter
String.format(Bundle.EncryptionDetectionFileIngestModule_artifactComment_suspected(), calculatedEntropy)); String.format(Bundle.EncryptionDetectionFileIngestModule_artifactComment_suspected(), calculatedEntropy));
} else if (isFilePasswordProtected(file)) { } else if (isFilePasswordProtected(file)) {
return flagFile(file, BlackboardArtifact.Type.TSK_ENCRYPTION_DETECTED, Score.SCORE_NOTABLE, return flagFile(file, BlackboardArtifact.Type.TSK_ENCRYPTION_DETECTED, Score.SCORE_NOTABLE,
Bundle.EncryptionDetectionFileIngestModule_artifactComment_password()); EncryptionDetectionModuleFactory.PASSWORD_PROTECT_MESSAGE);
} }
} }
} catch (ReadContentInputStreamException | SAXException | TikaException | UnsupportedCodecException ex) { } catch (ReadContentInputStreamException | SAXException | TikaException | UnsupportedCodecException ex) {

View File

@ -36,15 +36,19 @@ import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel;
@ServiceProvider(service = IngestModuleFactory.class) @ServiceProvider(service = IngestModuleFactory.class)
@Messages({ @Messages({
"EncryptionDetectionFileIngestModule.moduleName.text=Encryption Detection", "EncryptionDetectionFileIngestModule.moduleName.text=Encryption Detection",
"EncryptionDetectionFileIngestModule.getDesc.text=Looks for files with the specified minimum entropy." "EncryptionDetectionFileIngestModule.getDesc.text=Looks for files with the specified minimum entropy.",
"EncryptionDetectionFileIngestModule.artifactComment.password=Password protection detected.",
}) })
public class EncryptionDetectionModuleFactory implements IngestModuleFactory { public class EncryptionDetectionModuleFactory implements IngestModuleFactory {
public static final String PASSWORD_PROTECT_MESSAGE = Bundle.EncryptionDetectionFileIngestModule_artifactComment_password();
@Override @Override
public String getModuleDisplayName() { public String getModuleDisplayName() {
return getModuleName(); return getModuleName();
} }
/** /**
* Get the name of the module. * Get the name of the module.
* *

View File

@ -16,7 +16,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.sleuthkit.autopsy.datasourcesummary.uiutils; package org.sleuthkit.autopsy.report.modules.datasourcesummaryexport;
import java.awt.Color; import java.awt.Color;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@ -49,15 +49,16 @@ import org.apache.poi.xssf.usermodel.XSSFChart;
import org.apache.poi.xssf.usermodel.XSSFClientAnchor; import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
import org.apache.poi.xssf.usermodel.XSSFDrawing; import org.apache.poi.xssf.usermodel.XSSFDrawing;
import org.apache.poi.xssf.usermodel.XSSFSheet; import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport.ExcelExportException; import org.sleuthkit.autopsy.datasourcesummary.uiutils.BarChartSeries;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelExport.ExcelSheetExport; import org.sleuthkit.autopsy.report.modules.datasourcesummaryexport.ExcelExport.ExcelExportException;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport.ExcelItemExportable; import org.sleuthkit.autopsy.report.modules.datasourcesummaryexport.ExcelExport.ExcelSheetExport;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.ExcelSpecialFormatExport.ItemDimensions; import org.sleuthkit.autopsy.report.modules.datasourcesummaryexport.ExcelSpecialFormatExport.ExcelItemExportable;
import org.sleuthkit.autopsy.report.modules.datasourcesummaryexport.ExcelSpecialFormatExport.ItemDimensions;
/** /**
* Class that creates an excel stacked bar chart along with data table. * Class that creates an excel stacked bar chart along with data table.
*/ */
public class BarChartExport implements ExcelItemExportable, ExcelSheetExport { class BarChartExport implements ExcelItemExportable, ExcelSheetExport {
/** /**
* Creates an excel table model to be written to an excel sheet and used as * Creates an excel table model to be written to an excel sheet and used as
@ -70,7 +71,7 @@ public class BarChartExport implements ExcelItemExportable, ExcelSheetExport {
* @return An excel table export to be used as the data source for the chart * @return An excel table export to be used as the data source for the chart
* in the excel document. * in the excel document.
*/ */
private static ExcelTableExport<Pair<Object, List<Double>>, ? extends ExcelCellModel> getTableModel( private static ExcelTableExport<Pair<Object, List<Double>>, ? extends CellModel> getTableModel(
List<BarChartSeries> categories, String keyColumnHeader, String chartTitle) { List<BarChartSeries> categories, String keyColumnHeader, String chartTitle) {
// get the row keys by finding the series with the largest set of bar items // get the row keys by finding the series with the largest set of bar items
@ -134,7 +135,7 @@ public class BarChartExport implements ExcelItemExportable, ExcelSheetExport {
private static final int DEFAULT_ROW_PADDING = 1; private static final int DEFAULT_ROW_PADDING = 1;
private static final int DEFAULT_COL_OFFSET = 1; private static final int DEFAULT_COL_OFFSET = 1;
private final ExcelTableExport<Pair<Object, List<Double>>, ? extends ExcelCellModel> tableExport; private final ExcelTableExport<Pair<Object, List<Double>>, ? extends CellModel> tableExport;
private final int colOffset; private final int colOffset;
private final int rowPadding; private final int rowPadding;
private final int colSize; private final int colSize;
@ -154,7 +155,7 @@ public class BarChartExport implements ExcelItemExportable, ExcelSheetExport {
* @param chartTitle The title for the chart. * @param chartTitle The title for the chart.
* @param categories The categories along with data. * @param categories The categories along with data.
*/ */
public BarChartExport(String keyColumnHeader, BarChartExport(String keyColumnHeader,
String valueFormatString, String valueFormatString,
String chartTitle, String chartTitle,
List<BarChartSeries> categories) { List<BarChartSeries> categories) {
@ -177,7 +178,7 @@ public class BarChartExport implements ExcelItemExportable, ExcelSheetExport {
* @param colSize The column size of the chart. * @param colSize The column size of the chart.
* @param rowSize The row size of the chart. * @param rowSize The row size of the chart.
*/ */
public BarChartExport(String keyColumnHeader, String valueFormatString, BarChartExport(String keyColumnHeader, String valueFormatString,
String chartTitle, String sheetName, String chartTitle, String sheetName,
List<BarChartSeries> categories, List<BarChartSeries> categories,
int colOffset, int rowPadding, int colSize, int rowSize) { int colOffset, int rowPadding, int colSize, int rowSize) {

View File

@ -0,0 +1,3 @@
DataSourceSummaryReport.getName.text=Data Source Summary Report
DataSourceSummaryReport.getDesc.text=Data source summary report in Excel (XLS) format.
DataSourceSummaryReport.endReport.srcModuleName.text=Excel Report

View File

@ -0,0 +1,123 @@
DataSourceSummaryReport.error.noDataSources=No data sources selected for report.
DataSourceSummaryReport.error.noOpenCase=No currently open case.
DataSourceSummaryReport.excelFileWriteError=Could not write the KML file.
DataSourceSummaryReport.failedToCompleteReport=Failed to complete report.
DataSourceSummaryReport.getName.text=Data Source Summary Report
DataSourceSummaryReport.getDesc.text=Data source summary report in Excel (XLS) format.
DataSourceSummaryReport.endReport.srcModuleName.text=Excel Report
# {0} - sheetNumber
ExcelExport_writeExcel_noSheetName=Sheet {0}
ExcelExportAction_exportToXLSX_beginExport=Beginning Export...
ExcelExportAction_exportToXLSX_gatheringAnalysisData=Fetching Analysis Data
ExcelExportAction_exportToXLSX_gatheringContainerData=Fetching Container & Image Data
ExcelExportAction_exportToXLSX_gatheringFileData=Fetching File and MIME Type Data
ExcelExportAction_exportToXLSX_gatheringGeoData=Fetching Geolocation Data
ExcelExportAction_exportToXLSX_gatheringIngestData=Fetching Ingest History Data
ExcelExportAction_exportToXLSX_gatheringPastData=Fetching Historical Data
ExcelExportAction_exportToXLSX_gatheringRecentActivityData=Fetching Recent Activity Data
ExcelExportAction_exportToXLSX_gatheringTimelineData=Fetching Timeline Data
ExcelExportAction_exportToXLSX_gatheringUserData=Fetching User Activity Data
ExcelExportAction_exportToXLSX_writingToFile=Writing to File...
ExcelExportAction_getXLSXPath_directory=DataSourceSummary
ExcelExportAction_moduleName=Data Source Summary
ExportAnalysisResults_countColumn_title=Count
ExportAnalysisResults_hashsetHits_tabName=Hashset Hits
ExportAnalysisResults_interestingItemHits_tabName=Interesting Item Hits
ExportAnalysisResults_keyColumn_title=Name
ExportAnalysisResults_keywordHits_tabName=Keyword Hits
ExportAnalysisResults_keywordSearchModuleName=Keyword Search
ExportContainerInfo_export_acquisitionDetails=Acquisition Details:
ExportContainerInfo_export_deviceId=Device ID:
ExportContainerInfo_export_displayName=Display Name:
ExportContainerInfo_export_filePaths=File Paths:
ExportContainerInfo_export_imageType=Image Type:
ExportContainerInfo_export_md5=MD5:
ExportContainerInfo_export_originalName=Name:
ExportContainerInfo_export_sectorSize=Sector Size:
ExportContainerInfo_export_sha1=SHA1:
ExportContainerInfo_export_sha256=SHA256:
ExportContainerInfo_export_size=Size:
ExportContainerInfo_export_timeZone=Time Zone:
ExportContainerInfo_export_unallocatedSize=Unallocated Space:
ExportContainerInfo_setFieldsForNonImageDataSource_na=N/A
ExportContainerInfo_tabName=Container
ExportGeolocation_cityColumn_title=Closest City
ExportGeolocation_countColumn_title=Count
ExportGeolocation_mostCommon_tabName=Most Common Cities
ExportGeolocation_mostRecent_tabName=Most Recent Cities
ExportGeolocation_unknownRow_title=Unknown
ExportIngestHistory_endTimeColumn=End Time
ExportIngestHistory_ingestStatusTimeColumn=Ingest Status
ExportIngestHistory_moduleNameTimeColumn=Module Name
ExportIngestHistory_sheetName=Ingest History
ExportIngestHistory_startTimeColumn=Start Time
ExportIngestHistory_versionColumn=Module Version
ExportPastCases_caseColumn_title=Case
ExportPastCases_countColumn_title=Count
ExportPastCases_notableFileTable_tabName=Cases with Common Notable
ExportPastCases_sameIdsTable_tabName=Past Cases with the Same Devices
ExportRecentFiles_attachmentsTable_tabName=Recent Attachments
ExportRecentFiles_col_head_date=Date
ExportRecentFiles_col_header_domain=Domain
ExportRecentFiles_col_header_path=Path
ExportRecentFiles_col_header_sender=Sender
ExportRecentFiles_docsTable_tabName=Recently Opened Documents
ExportRecentFiles_downloadsTable_tabName=Recently Downloads
ExportTypes_artifactsTypesPieChart_title=Artifact Types
ExportTypes_excelTabName=Types
ExportTypes_fileMimeTypesChart_audio_title=Audio
ExportTypes_fileMimeTypesChart_documents_title=Documents
ExportTypes_fileMimeTypesChart_executables_title=Executables
ExportTypes_fileMimeTypesChart_images_title=Images
ExportTypes_fileMimeTypesChart_notAnalyzed_title=Not Analyzed
ExportTypes_fileMimeTypesChart_other_title=Other
ExportTypes_fileMimeTypesChart_title=File Types
ExportTypes_fileMimeTypesChart_unknown_title=Unknown
ExportTypes_fileMimeTypesChart_valueLabel=Count
ExportTypes_fileMimeTypesChart_videos_title=Videos
ExportTypes_filesByCategoryTable_allocatedRow_title=Allocated Files
ExportTypes_filesByCategoryTable_directoryRow_title=Directories
ExportTypes_filesByCategoryTable_slackRow_title=Slack Files
ExportTypes_filesByCategoryTable_unallocatedRow_title=Unallocated Files
ExportTypes_osLabel_title=OS
ExportTypes_sizeLabel_title=Size
ExportTypes_usageLabel_title=Usage
ExportUserActivity_noDataExists=No communication data exists
ExportUserActivity_tab_title=User Activity
ExportUserActivity_TopAccountTableModel_accountType_header=Account Type
ExportUserActivity_TopAccountTableModel_lastAccess_header=Last Accessed
ExportUserActivity_TopAccountTableModel_tabName=Recent Account Types Used
ExportUserActivity_TopDeviceAttachedTableModel_dateAccessed_header=Last Accessed
ExportUserActivity_TopDeviceAttachedTableModel_deviceId_header=Device Id
ExportUserActivity_TopDeviceAttachedTableModel_makeModel_header=Make and Model
ExportUserActivity_TopDeviceAttachedTableModel_tabName=Recent Devices Attached
ExportUserActivity_TopDomainsTableModel_count_header=Visits
ExportUserActivity_TopDomainsTableModel_domain_header=Domain
ExportUserActivity_TopDomainsTableModel_lastAccess_header=Last Accessed
ExportUserActivity_TopDomainsTableModel_tabName=Recent Domains
ExportUserActivity_TopProgramsTableModel_count_header=Run Times
ExportUserActivity_TopProgramsTableModel_folder_header=Folder
ExportUserActivity_TopProgramsTableModel_lastrun_header=Last Run
ExportUserActivity_TopProgramsTableModel_name_header=Program
ExportUserActivity_TopProgramsTableModel_tabName=Recent Programs
ExportUserActivity_TopWebSearchTableModel_dateAccessed_header=Date Accessed
ExportUserActivity_TopWebSearchTableModel_searchString_header=Search String
ExportUserActivity_TopWebSearchTableModel_tabName=Recent Web Searches
ExportUserActivity_TopWebSearchTableModel_translatedResult_header=Translated
SizeRepresentationUtil_units_bytes=bytes
SizeRepresentationUtil_units_gigabytes=GB
SizeRepresentationUtil_units_kilobytes=KB
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
TimlinePanel_last30DaysChart_title=Last 30 Days

View File

@ -0,0 +1,91 @@
/*
* 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.report.modules.datasourcesummaryexport;
import javax.swing.JLabel;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
/**
* Basic interface for a cell model.
*/
interface CellModel {
/**
* Describes the horizontal alignment.
*/
enum HorizontalAlign {
LEFT(JLabel.LEFT, HorizontalAlignment.LEFT),
CENTER(JLabel.CENTER, HorizontalAlignment.CENTER),
RIGHT(JLabel.RIGHT, HorizontalAlignment.RIGHT);
private final int jlabelAlignment;
private final HorizontalAlignment poiAlignment;
/**
* Constructor for a HorizontalAlign enum.
*
* @param jlabelAlignment The corresponding JLabel horizontal alignment
* number.
* @param poiAlignment Horizontal alignment for Apache POI.
*/
HorizontalAlign(int jlabelAlignment, HorizontalAlignment poiAlignment) {
this.jlabelAlignment = jlabelAlignment;
this.poiAlignment = poiAlignment;
}
/**
* @return The corresponding JLabel horizontal alignment (i.e.
* JLabel.LEFT).
*/
int getJLabelAlignment() {
return this.jlabelAlignment;
}
/**
* @return Horizontal alignment for Apache POI.
*/
HorizontalAlignment getPoiAlignment() {
return poiAlignment;
}
}
/**
* @return The root data object.
*/
Object getData();
/**
* @return The text to be shown in the cell.
*/
default String getText() {
Object data = getData();
return (data == null) ? null : data.toString();
}
/**
* @return The horizontal alignment for the text in the cell.
*/
HorizontalAlign getHorizontalAlignment();
/**
* @return The format string to be used with Apache POI during excel
* export or null if none necessary.
*/
String getExcelFormatString();
}

View File

@ -0,0 +1,80 @@
/*
* 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.report.modules.datasourcesummaryexport;
import java.util.function.Function;
/**
* Describes aspects of a column which can be used with getTableModel or
* getJTablePanel. 'T' represents the object that will represent rows in the
* table.
*/
class ColumnModel<T, C extends CellModel> {
private final String headerTitle;
private final Function<T, ? extends C> cellRenderer;
private final Integer width;
/**
* Constructor for a DataResultColumnModel.
*
* @param headerTitle The title for the column.
* @param cellRenderer The method that generates a CellModel for the column
* based on the data.
*/
ColumnModel(String headerTitle, Function<T, ? extends C> cellRenderer) {
this(headerTitle, cellRenderer, null);
}
/**
* Constructor for a DataResultColumnModel.
*
* @param headerTitle The title for the column.
* @param cellRenderer The method that generates a CellModel for the column
* based on the data.
* @param width The preferred width of the column.
*/
ColumnModel(String headerTitle, Function<T, ? extends C> cellRenderer, Integer width) {
this.headerTitle = headerTitle;
this.cellRenderer = cellRenderer;
this.width = width;
}
/**
* @return The title for the column.
*/
String getHeaderTitle() {
return headerTitle;
}
/**
* @return The method that generates a CellModel for the column based on the
* data.
*/
Function<T, ? extends C> getCellRenderer() {
return cellRenderer;
}
/**
* @return The preferred width of the column (can be null).
*/
Integer getWidth() {
return width;
}
}

View File

@ -0,0 +1,162 @@
/*
* 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.report.modules.datasourcesummaryexport;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.stream.Collectors;
import javax.swing.JPanel;
import org.openide.util.NbBundle;
import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.report.GeneralReportModule;
import org.sleuthkit.autopsy.report.GeneralReportSettings;
import org.sleuthkit.autopsy.report.ReportProgressPanel;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Instances of this class plug in to the reporting infrastructure to provide a
* convenient way to extract data source summary information into Excel.
*/
@ServiceProvider(service = GeneralReportModule.class)
public class DataSourceSummaryReport implements GeneralReportModule {
private static final Logger logger = Logger.getLogger(DataSourceSummaryReport.class.getName());
private static DataSourceSummaryReport instance;
// Get the default instance of this report
public static synchronized DataSourceSummaryReport getDefault() {
if (instance == null) {
instance = new DataSourceSummaryReport();
}
return instance;
}
public DataSourceSummaryReport() {
}
@Override
public String getName() {
String name = NbBundle.getMessage(this.getClass(), "DataSourceSummaryReport.getName.text");
return name;
}
@Override
public String getRelativeFilePath() {
return "";
}
@Override
public String getDescription() {
String desc = NbBundle.getMessage(this.getClass(), "DataSourceSummaryReport.getDesc.text");
return desc;
}
@Override
public JPanel getConfigurationPanel() {
return null;
}
@Override
public boolean supportsDataSourceSelection() {
return true;
}
@NbBundle.Messages({
"DataSourceSummaryReport.error.noOpenCase=No currently open case.",
"DataSourceSummaryReport.error.noDataSources=No data sources selected for report.",
"DataSourceSummaryReport.failedToCompleteReport=Failed to complete report.",
"DataSourceSummaryReport.excelFileWriteError=Could not write the KML file.",})
@Override
public void generateReport(GeneralReportSettings settings, ReportProgressPanel progressPanel) {
progressPanel.start();
Case currentCase;
try {
currentCase = Case.getCurrentCaseThrows();
} catch (NoCurrentCaseException ex) {
progressPanel.complete(ReportProgressPanel.ReportStatus.ERROR, Bundle.DataSourceSummaryReport_error_noOpenCase());
logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS
return;
}
String errorMessage = "";
ReportProgressPanel.ReportStatus result = ReportProgressPanel.ReportStatus.COMPLETE;
List<Content> selectedDataSources = new ArrayList<>();
if(settings.getSelectedDataSources() == null) {
// Process all data sources if the list is null.
try {
selectedDataSources = currentCase.getDataSources();
List<Long> dsIDs = selectedDataSources
.stream()
.map(Content::getId)
.collect(Collectors.toList());
settings.setSelectedDataSources(dsIDs);
} catch (TskCoreException ex) {
result = ReportProgressPanel.ReportStatus.ERROR;
errorMessage = Bundle.DataSourceSummaryReport_failedToCompleteReport();
logger.log(Level.SEVERE, "Could not get the datasources from the case", ex);
progressPanel.complete(result, errorMessage);
return;
}
} else {
for (Long dsID : settings.getSelectedDataSources()) {
try {
selectedDataSources.add(currentCase.getSleuthkitCase().getContentById(dsID));
} catch (TskCoreException ex) {
result = ReportProgressPanel.ReportStatus.ERROR;
errorMessage = Bundle.DataSourceSummaryReport_failedToCompleteReport();
logger.log(Level.SEVERE, "Could not get the datasources from the case", ex);
progressPanel.complete(result, errorMessage);
return;
}
}
}
if (selectedDataSources.isEmpty()) {
result = ReportProgressPanel.ReportStatus.ERROR;
progressPanel.complete(result, Bundle.DataSourceSummaryReport_error_noDataSources());
logger.log(Level.SEVERE, "No data sources selected for report."); //NON-NLS
return;
}
// looop over all selected data sources
for (Content dataSource : selectedDataSources){
if (dataSource instanceof DataSource) {
try {
new ExcelExportAction().exportToXLSX(progressPanel, (DataSource) dataSource, settings.getReportDirectoryPath());
} catch (IOException | ExcelExport.ExcelExportException ex) {
errorMessage = Bundle.DataSourceSummaryReport_excelFileWriteError();
logger.log(Level.SEVERE, errorMessage, ex); //NON-NLS
progressPanel.complete(ReportProgressPanel.ReportStatus.ERROR, errorMessage);
return;
}
}
}
progressPanel.complete(result, errorMessage);
}
}

View File

@ -0,0 +1,111 @@
/*
* 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.report.modules.datasourcesummaryexport;
import java.util.function.Function;
/**
* The default cell model.
*/
class DefaultCellModel<T> implements CellModel {
private final T data;
private final String text;
private CellModel.HorizontalAlign horizontalAlignment;
private final String excelFormatString;
/**
* Main constructor.
*
* @param data The data to be displayed in the cell.
*/
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.
*/
DefaultCellModel(T data, Function<T, String> stringConverter) {
this(data, stringConverter, 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.
* @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.
*/
DefaultCellModel(T data, Function<T, String> stringConverter, String excelFormatString) {
this.data = data;
this.excelFormatString = excelFormatString;
if (stringConverter == null) {
text = this.data == null ? "" : this.data.toString();
} else {
text = stringConverter.apply(this.data);
}
}
@Override
public T getData() {
return this.data;
}
@Override
public String getText() {
return text;
}
@Override
public HorizontalAlign getHorizontalAlignment() {
return horizontalAlignment;
}
@Override
public String getExcelFormatString() {
return this.excelFormatString;
}
/**
* Sets the horizontal alignment for this cell model.
*
* @param alignment The horizontal alignment for the cell model.
*
* @return As a utility, returns this.
*/
DefaultCellModel<T> setHorizontalAlignment(CellModel.HorizontalAlign alignment) {
this.horizontalAlignment = alignment;
return this;
}
@Override
public String toString() {
return getText();
}
}

View File

@ -16,7 +16,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.sleuthkit.autopsy.datasourcesummary.uiutils; package org.sleuthkit.autopsy.report.modules.datasourcesummaryexport;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
@ -37,24 +37,24 @@ import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModel.HorizontalAlign; import org.sleuthkit.autopsy.report.modules.datasourcesummaryexport.CellModel.HorizontalAlign;
/** /**
* Class for handling Excel exporting. * Class for handling Excel exporting.
*/ */
public class ExcelExport { class ExcelExport {
/** /**
* Exception thrown in the event of an excel export issue. * Exception thrown in the event of an excel export issue.
*/ */
public static class ExcelExportException extends Exception { static class ExcelExportException extends Exception {
/** /**
* Constructor. * Constructor.
* *
* @param string The message. * @param string The message.
*/ */
public ExcelExportException(String string) { ExcelExportException(String string) {
super(string); super(string);
} }
@ -64,7 +64,7 @@ public class ExcelExport {
* @param string The message. * @param string The message.
* @param thrwbl The inner exception. * @param thrwbl The inner exception.
*/ */
public ExcelExportException(String string, Throwable thrwbl) { ExcelExportException(String string, Throwable thrwbl) {
super(string, thrwbl); super(string, thrwbl);
} }
} }
@ -153,7 +153,7 @@ public class ExcelExport {
/** /**
* Class detailing aspects of the worksheet. * Class detailing aspects of the worksheet.
*/ */
public static class WorksheetEnv { static class WorksheetEnv {
private final CellStyle headerStyle; private final CellStyle headerStyle;
private final Workbook parentWorkbook; private final Workbook parentWorkbook;
@ -182,7 +182,7 @@ public class ExcelExport {
* @param cellStyleKey The key. * @param cellStyleKey The key.
* @return The cell style representing this key. * @return The cell style representing this key.
*/ */
public CellStyle getCellStyle(CellStyleKey cellStyleKey) { CellStyle getCellStyle(CellStyleKey cellStyleKey) {
return cellStyleCache.computeIfAbsent(cellStyleKey, (pair) -> { return cellStyleCache.computeIfAbsent(cellStyleKey, (pair) -> {
CellStyle computed = this.parentWorkbook.createCellStyle(); CellStyle computed = this.parentWorkbook.createCellStyle();
computed.cloneStyleFrom(cellStyleKey.getCellStyle() == null ? defaultStyle : cellStyleKey.getCellStyle()); computed.cloneStyleFrom(cellStyleKey.getCellStyle() == null ? defaultStyle : cellStyleKey.getCellStyle());
@ -203,7 +203,7 @@ public class ExcelExport {
* *
* @return The cell style to use for headers. * @return The cell style to use for headers.
*/ */
public CellStyle getHeaderStyle() { CellStyle getHeaderStyle() {
return headerStyle; return headerStyle;
} }
@ -212,7 +212,7 @@ public class ExcelExport {
* *
* @return The cell style for default items. * @return The cell style for default items.
*/ */
public CellStyle getDefaultCellStyle() { CellStyle getDefaultCellStyle() {
return defaultStyle; return defaultStyle;
} }
@ -221,7 +221,7 @@ public class ExcelExport {
* *
* @return The parent workbook. * @return The parent workbook.
*/ */
public Workbook getParentWorkbook() { Workbook getParentWorkbook() {
return parentWorkbook; return parentWorkbook;
} }
} }
@ -229,7 +229,7 @@ public class ExcelExport {
/** /**
* An item to be exported as a sheet during export. * An item to be exported as a sheet during export.
*/ */
public static interface ExcelSheetExport { static interface ExcelSheetExport {
/** /**
* Returns the name of the sheet to use with this item. * Returns the name of the sheet to use with this item.
@ -250,23 +250,7 @@ public class ExcelExport {
void renderSheet(Sheet sheet, WorksheetEnv env) throws ExcelExportException; void renderSheet(Sheet sheet, WorksheetEnv env) throws ExcelExportException;
} }
private static ExcelExport instance = null;
/**
* Retrieves a singleton instance of this class.
*
* @return The instance.
*/
public static ExcelExport getInstance() {
if (instance == null) {
instance = new ExcelExport();
}
return instance;
}
private ExcelExport() { private ExcelExport() {
} }
/** /**
@ -281,7 +265,7 @@ public class ExcelExport {
"# {0} - sheetNumber", "# {0} - sheetNumber",
"ExcelExport_writeExcel_noSheetName=Sheet {0}" "ExcelExport_writeExcel_noSheetName=Sheet {0}"
}) })
public void writeExcel(List<ExcelSheetExport> exports, File path) throws IOException, ExcelExportException { static void writeExcel(List<ExcelSheetExport> exports, File path) throws IOException, ExcelExportException {
// Create a Workbook // Create a Workbook
Workbook workbook = new XSSFWorkbook(); // new HSSFWorkbook() for generating `.xls` file Workbook workbook = new XSSFWorkbook(); // new HSSFWorkbook() for generating `.xls` file
@ -337,7 +321,7 @@ public class ExcelExport {
* @param cellStyle The style to use. * @param cellStyle The style to use.
* @return The created cell. * @return The created cell.
*/ */
static Cell createCell(WorksheetEnv env, Row row, int colNum, ExcelCellModel cellModel, Optional<CellStyle> cellStyle) { static Cell createCell(WorksheetEnv env, Row row, int colNum, CellModel cellModel, Optional<CellStyle> cellStyle) {
CellStyle cellStyleToUse = cellStyle.orElse(env.getDefaultCellStyle()); CellStyle cellStyleToUse = cellStyle.orElse(env.getDefaultCellStyle());
if (cellModel.getExcelFormatString() != null || cellModel.getHorizontalAlignment() != null) { if (cellModel.getExcelFormatString() != null || cellModel.getHorizontalAlignment() != null) {

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