mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-16 01:37:43 +00:00
Merge develop into artifact pipeline branch
This commit is contained in:
commit
15af4bce1a
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,6 +3,7 @@
|
||||
/*/build/
|
||||
*/nbproject/private/*
|
||||
/nbproject/private/*
|
||||
/apidiff_output/
|
||||
|
||||
/Core/release/
|
||||
/Core/src/org/sleuthkit/autopsy/coreutils/Version.properties
|
||||
|
@ -31,6 +31,17 @@
|
||||
</target>
|
||||
|
||||
<target name="get-thirdparty-dependencies" description="get third-party dependencies">
|
||||
<!--
|
||||
Copy netbeans localization jars:
|
||||
This contains jars provided in Netbeans 8 RCP that provide localization bundles.
|
||||
They do not appear to be included in Netbeans >= 9.
|
||||
See VIK-7434 for more information.
|
||||
-->
|
||||
<mkdir dir="${modules.dir}/locale"/>
|
||||
<copy todir="${modules.dir}/locale" >
|
||||
<fileset dir="${thirdparty.dir}/NetbeansLocalization"/>
|
||||
</copy>
|
||||
|
||||
<!--Copy photorec to release-->
|
||||
<copy todir="${basedir}/release/photorec_exec" >
|
||||
<fileset dir="${thirdparty.dir}/photorec_exec"/>
|
||||
@ -89,6 +100,11 @@
|
||||
<fileset dir="${thirdparty.dir}/ImageMagick-7.0.10-27-portable-Q16-x64"/>
|
||||
</copy>
|
||||
|
||||
<!--Copy DomainCategorization to release-->
|
||||
<copy todir="${basedir}/release/DomainCategorization" >
|
||||
<fileset dir="${thirdparty.dir}/DomainCategorization"/>
|
||||
</copy>
|
||||
|
||||
<!-- The 'libgstlibav.dll' file is too big to store on GitHub, so we
|
||||
have it stored in a ZIP file. We'll extract it in place and remove
|
||||
the ZIP file afterward. -->
|
||||
|
@ -83,7 +83,6 @@ file.reference.jgraphx-4.1.0.jar=release\\modules\\ext\\jgraphx-4.1.0.jar
|
||||
file.reference.jline-0.9.94.jar=release\\modules\\ext\\jline-0.9.94.jar
|
||||
file.reference.jsoup-1.10.3.jar=release\\modules\\ext\\jsoup-1.10.3.jar
|
||||
file.reference.jsr305-3.0.2.jar=release\\modules\\ext\\jsr305-3.0.2.jar
|
||||
file.reference.junit-3.8.1.jar=release\\modules\\ext\\junit-3.8.1.jar
|
||||
file.reference.jutf7-1.0.0.jar=release\\modules\\ext\\jutf7-1.0.0.jar
|
||||
file.reference.jxmapviewer2-2.4.jar=release\\modules\\ext\\jxmapviewer2-2.4.jar
|
||||
file.reference.jython-standalone-2.7.0.jar=release\\modules\\ext\\jython-standalone-2.7.0.jar
|
||||
|
@ -737,10 +737,6 @@
|
||||
<runtime-relative-path>ext/jai_imageio-1.1.jar</runtime-relative-path>
|
||||
<binary-origin>release\modules\ext\jai_imageio-1.1.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/junit-3.8.1.jar</runtime-relative-path>
|
||||
<binary-origin>release\modules\ext\junit-3.8.1.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/curator-client-2.8.0.jar</runtime-relative-path>
|
||||
<binary-origin>release\modules\ext\curator-client-2.8.0.jar</binary-origin>
|
||||
|
@ -45,3 +45,4 @@ OpenPythonModulesFolderAction.actionName.text=Python Plugins
|
||||
OpenPythonModulesFolderAction.errorMsg.folderNotFound=Python plugins folder not found: {0}
|
||||
CTL_OpenPythonModulesFolderAction=Python Plugins
|
||||
GetTagNameAndCommentDialog.tagCombo.toolTipText=Select tag to use
|
||||
CTL_ExitAction=Exit
|
@ -96,3 +96,4 @@ OpenPythonModulesFolderAction.actionName.text=Python Plugins
|
||||
OpenPythonModulesFolderAction.errorMsg.folderNotFound=Python plugins folder not found: {0}
|
||||
CTL_OpenPythonModulesFolderAction=Python Plugins
|
||||
GetTagNameAndCommentDialog.tagCombo.toolTipText=Select tag to use
|
||||
CTL_ExitAction=Exit
|
||||
|
@ -40,7 +40,7 @@ import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
||||
* The action associated with the Case/Exit menu item. It closes the current
|
||||
* case, if any, and shuts down the application.
|
||||
*/
|
||||
@ActionRegistration(displayName = "Exit", iconInMenu = true)
|
||||
@ActionRegistration(displayName = "#CTL_ExitAction", iconInMenu = true)
|
||||
@ActionReference(path = "Menu/Case", position = 1000, separatorBefore = 999)
|
||||
@ActionID(id = "org.sleuthkit.autopsy.casemodule.ExitAction", category = "Case")
|
||||
final public class ExitAction implements ActionListener {
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2020 Basis Technology Corp.
|
||||
* Copyright 2020-2021 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -24,18 +24,10 @@ import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.ThreadInfo;
|
||||
import java.lang.management.ThreadMXBean;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.swing.SwingWorker;
|
||||
import org.openide.awt.ActionID;
|
||||
import org.openide.awt.ActionReference;
|
||||
@ -46,6 +38,8 @@ import org.openide.util.actions.CallableSystemAction;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
|
||||
import org.sleuthkit.autopsy.coreutils.ThreadUtils;
|
||||
import org.sleuthkit.autopsy.coreutils.TimeStampUtils;
|
||||
|
||||
/**
|
||||
* Action class for the Thread Dump help menu item. If there is no case open the
|
||||
@ -63,8 +57,6 @@ public final class ThreadDumpAction extends CallableSystemAction implements Acti
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Logger logger = Logger.getLogger(ThreadDumpAction.class.getName());
|
||||
|
||||
private static final DateFormat DATE_FORMAT = new SimpleDateFormat("MM-dd-yyyy-HH-mm-ss-SSSS");
|
||||
|
||||
@Override
|
||||
public void performAction() {
|
||||
(new ThreadDumper()).run();
|
||||
@ -114,26 +106,13 @@ public final class ThreadDumpAction extends CallableSystemAction implements Acti
|
||||
* @throws IOException
|
||||
*/
|
||||
private File createThreadDump() throws IOException {
|
||||
|
||||
// generate thread dump
|
||||
String threadDump = ThreadUtils.generateThreadDump();
|
||||
|
||||
File dumpFile = createFilePath().toFile();
|
||||
try (BufferedWriter writer = new BufferedWriter(new FileWriter(dumpFile, true))) {
|
||||
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
|
||||
ThreadInfo[] threadInfos = threadMXBean.getThreadInfo(threadMXBean.getAllThreadIds(), 100);
|
||||
for (ThreadInfo threadInfo : threadInfos) {
|
||||
writer.write(threadInfo.toString());
|
||||
writer.write("\n");
|
||||
}
|
||||
|
||||
long[] deadlockThreadIds = threadMXBean.findDeadlockedThreads();
|
||||
if (deadlockThreadIds != null) {
|
||||
writer.write("-------------------List of Deadlocked Thread IDs ---------------------");
|
||||
String idsList = (Arrays
|
||||
.stream(deadlockThreadIds)
|
||||
.boxed()
|
||||
.collect(Collectors.toList()))
|
||||
.stream().map(n -> String.valueOf(n))
|
||||
.collect(Collectors.joining("-", "{", "}"));
|
||||
writer.write(idsList);
|
||||
}
|
||||
writer.write(threadDump);
|
||||
}
|
||||
|
||||
return dumpFile;
|
||||
@ -145,7 +124,7 @@ public final class ThreadDumpAction extends CallableSystemAction implements Acti
|
||||
* @return Path for dump file.
|
||||
*/
|
||||
private Path createFilePath() {
|
||||
String fileName = "ThreadDump_" + DATE_FORMAT.format(new Date()) + ".txt";
|
||||
String fileName = "ThreadDump_" + TimeStampUtils.createTimeStamp() + ".txt";
|
||||
if (Case.isCaseOpen()) {
|
||||
return Paths.get(Case.getCurrentCase().getLogDirectoryPath(), fileName);
|
||||
}
|
||||
|
@ -0,0 +1,4 @@
|
||||
CTL_ResetWindowsAction=Reset Windows
|
||||
ResetWindowAction.caseCloseFailure.text=Unable to close the current case, the software will restart and the windows locations will reset the next time the software is closed.
|
||||
ResetWindowAction.caseSaveMetadata.text=Unable to save current case path, the software will restart and the windows locations will reset but the current case will not be opened upon restart.
|
||||
ResetWindowAction.confirm.text=In order to perform the resetting of window locations the software will close and restart. If a case is currently open, it will be closed. If ingest or a search is currently running, it will be terminated. Are you sure you want to restart the software to reset all window locations?
|
141
Core/src/org/sleuthkit/autopsy/apputils/ResetWindowsAction.java
Normal file
141
Core/src/org/sleuthkit/autopsy/apputils/ResetWindowsAction.java
Normal file
@ -0,0 +1,141 @@
|
||||
/*
|
||||
* 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.apputils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.SwingUtilities;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.openide.LifecycleManager;
|
||||
import org.openide.awt.ActionID;
|
||||
import org.openide.awt.ActionReference;
|
||||
import org.openide.awt.ActionReferences;
|
||||
import org.openide.awt.ActionRegistration;
|
||||
import org.openide.util.HelpCtx;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.actions.CallableSystemAction;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.CaseActionException;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
||||
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
|
||||
|
||||
/**
|
||||
* Class to open the Discovery dialog. Allows the user to run searches and see
|
||||
* results in the DiscoveryTopComponent.
|
||||
*/
|
||||
@ActionID(category = "Tools", id = "org.sleuthkit.autopsy.apputils.ResetWindowsAction")
|
||||
@ActionReferences(value = {
|
||||
@ActionReference(path = "Menu/Window", position = 205)})
|
||||
@ActionRegistration(displayName = "#CTL_ResetWindowsAction", lazy = false)
|
||||
@NbBundle.Messages({"CTL_ResetWindowsAction=Reset Windows"})
|
||||
public final class ResetWindowsAction extends CallableSystemAction {
|
||||
|
||||
private static final String DISPLAY_NAME = Bundle.CTL_ResetWindowsAction();
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final static Logger logger = Logger.getLogger(ResetWindowsAction.class.getName());
|
||||
private final static String WINDOWS2LOCAL = "Windows2Local";
|
||||
private final static String CASE_TO_REOPEN_FILE = "caseToOpen.txt";
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@NbBundle.Messages({"ResetWindowAction.confirm.text=In order to perform the resetting of window locations the software will close and restart. "
|
||||
+ "If a case is currently open, it will be closed. If ingest or a search is currently running, it will be terminated. "
|
||||
+ "Are you sure you want to restart the software to reset all window locations?",
|
||||
"ResetWindowAction.caseCloseFailure.text=Unable to close the current case, "
|
||||
+ "the software will restart and the windows locations will reset the next time the software is closed.",
|
||||
"ResetWindowAction.caseSaveMetadata.text=Unable to save current case path, "
|
||||
+ "the software will restart and the windows locations will reset but the current case will not be opened upon restart."})
|
||||
|
||||
@Override
|
||||
public void performAction() {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
boolean response = MessageNotifyUtil.Message.confirm(Bundle.ResetWindowAction_confirm_text());
|
||||
if (response) {
|
||||
//adding the shutdown hook, closing the current case, and marking for restart can be re-ordered if slightly different behavior is desired
|
||||
Runtime.getRuntime().addShutdownHook(new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
FileUtils.deleteDirectory(new File(PlatformUtil.getUserConfigDirectory() + File.separator + WINDOWS2LOCAL));
|
||||
} catch (IOException ex) {
|
||||
//While we would like the user to be aware of this in the unlikely event that the directory can not be deleted
|
||||
//Because our deletion is being attempted in a shutdown hook I don't know that we can pop up UI elements during the shutdown proces
|
||||
logger.log(Level.SEVERE, "Unable to delete config directory, window locations will not be reset. To manually reset the windows please delete the following directory while the software is closed. " + PlatformUtil.getUserConfigDirectory() + File.separator + "Windows2Local", ex);
|
||||
}
|
||||
}
|
||||
});
|
||||
try {
|
||||
if (Case.isCaseOpen()) {
|
||||
String caseMetadataFilePath = Case.getCurrentCase().getMetadata().getFilePath().toString();
|
||||
File caseToOpenFile = new File(ResetWindowsAction.getCaseToReopenFilePath());
|
||||
Charset encoding = null; //prevents writeStringToFile from having ambiguous arguments
|
||||
FileUtils.writeStringToFile(caseToOpenFile, caseMetadataFilePath, encoding);
|
||||
Case.closeCurrentCase();
|
||||
}
|
||||
// The method markForRestart can not be undone once it is called.
|
||||
LifecycleManager.getDefault().markForRestart();
|
||||
//we need to call exit last
|
||||
LifecycleManager.getDefault().exit();
|
||||
} catch (CaseActionException ex) {
|
||||
logger.log(Level.WARNING, Bundle.ResetWindowAction_caseCloseFailure_text(), ex);
|
||||
MessageNotifyUtil.Message.show(Bundle.ResetWindowAction_caseCloseFailure_text(), MessageNotifyUtil.MessageType.ERROR);
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.WARNING, Bundle.ResetWindowAction_caseSaveMetadata_text(), ex);
|
||||
MessageNotifyUtil.Message.show(Bundle.ResetWindowAction_caseSaveMetadata_text(), MessageNotifyUtil.MessageType.ERROR);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static String getCaseToReopenFilePath(){
|
||||
return PlatformUtil.getUserConfigDirectory() + File.separator + CASE_TO_REOPEN_FILE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this action to be enabled/disabled
|
||||
*
|
||||
* @param value whether to enable this action or not
|
||||
*/
|
||||
@Override
|
||||
|
||||
public void setEnabled(boolean value) {
|
||||
super.setEnabled(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return DISPLAY_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HelpCtx getHelpCtx() {
|
||||
return HelpCtx.DEFAULT_HELP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean asynchronous() {
|
||||
return false; // run on edt
|
||||
}
|
||||
}
|
@ -290,7 +290,7 @@ class AddImageWizardAddingProgressPanel extends ShortcutWizardDescriptorPanel {
|
||||
*
|
||||
*
|
||||
* @param errorString the error string to be displayed
|
||||
* @param critical true if this is a critical error
|
||||
* @param critical true if this is a critical error
|
||||
*/
|
||||
void addErrors(String errorString, boolean critical) {
|
||||
getComponent().showErrors(errorString, critical);
|
||||
@ -325,10 +325,10 @@ class AddImageWizardAddingProgressPanel extends ShortcutWizardDescriptorPanel {
|
||||
* Starts the Data source processing by kicking off the selected
|
||||
* DataSourceProcessor
|
||||
*
|
||||
* @param dsp The data source processor providing configuration for how to
|
||||
* process the specific data source type.
|
||||
* @param dsp The data source processor providing configuration for
|
||||
* how to process the specific data source type.
|
||||
* @param selectedHost The host to which this data source belongs or null
|
||||
* for a default host.
|
||||
* for a default host.
|
||||
*/
|
||||
void startDataSourceProcessing(DataSourceProcessor dsp, Host selectedHost) {
|
||||
if (dsProcessor == null) { //this can only be run once
|
||||
@ -336,45 +336,44 @@ class AddImageWizardAddingProgressPanel extends ShortcutWizardDescriptorPanel {
|
||||
newContents.clear();
|
||||
cleanupTask = null;
|
||||
dsProcessor = dsp;
|
||||
|
||||
// Add a cleanup task to interrupt the background process if the
|
||||
// wizard exits while the background process is running.
|
||||
cleanupTask = addImageAction.new CleanupTask() {
|
||||
@Override
|
||||
void cleanup() throws Exception {
|
||||
WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
||||
cancelDataSourceProcessing(dataSourceId);
|
||||
cancelled = true;
|
||||
WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
|
||||
}
|
||||
};
|
||||
|
||||
cleanupTask.enable();
|
||||
|
||||
new Thread(() -> {
|
||||
// Add a cleanup task to interrupt the background process if the
|
||||
// wizard exits while the background process is running.
|
||||
cleanupTask = addImageAction.new CleanupTask() {
|
||||
@Override
|
||||
void cleanup() throws Exception {
|
||||
WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
||||
cancelDataSourceProcessing(dataSourceId);
|
||||
cancelled = true;
|
||||
WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
|
||||
}
|
||||
};
|
||||
|
||||
cleanupTask.enable();
|
||||
|
||||
try {
|
||||
Case.getCurrentCaseThrows().notifyAddingDataSource(dataSourceId);
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
Logger.getLogger(AddImageWizardAddingProgressVisual.class.getName()).log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS
|
||||
}
|
||||
}).start();
|
||||
DataSourceProcessorCallback cbObj = new DataSourceProcessorCallback() {
|
||||
@Override
|
||||
public void doneEDT(DataSourceProcessorCallback.DataSourceProcessorResult result, List<String> errList, List<Content> contents) {
|
||||
dataSourceProcessorDone(dataSourceId, result, errList, contents);
|
||||
|
||||
DataSourceProcessorCallback cbObj = new DataSourceProcessorCallback() {
|
||||
@Override
|
||||
public void doneEDT(DataSourceProcessorCallback.DataSourceProcessorResult result, List<String> errList, List<Content> contents) {
|
||||
dataSourceProcessorDone(dataSourceId, result, errList, contents);
|
||||
}
|
||||
};
|
||||
|
||||
// Kick off the DSProcessor
|
||||
if (dsProcessor.supportsIngestStream()) {
|
||||
// Set readyToIngest to false to prevent the wizard from starting ingest a second time.
|
||||
readyToIngest = false;
|
||||
dsProcessor.runWithIngestStream(selectedHost, ingestJobSettings, getDSPProgressMonitorImpl(), cbObj);
|
||||
} else {
|
||||
dsProcessor.run(selectedHost, getDSPProgressMonitorImpl(), cbObj);
|
||||
}
|
||||
};
|
||||
|
||||
}).start();
|
||||
setStateStarted();
|
||||
|
||||
// Kick off the DSProcessor
|
||||
if (dsProcessor.supportsIngestStream()) {
|
||||
// Set readyToIngest to false to prevent the wizard from starting ingest a second time.
|
||||
readyToIngest = false;
|
||||
dsProcessor.runWithIngestStream(selectedHost, ingestJobSettings, getDSPProgressMonitorImpl(), cbObj);
|
||||
} else {
|
||||
dsProcessor.run(selectedHost, getDSPProgressMonitorImpl(), cbObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,7 +79,7 @@ class AddImageWizardSelectHostVisual extends javax.swing.JPanel {
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 7;
|
||||
hash = 41 * hash + Objects.hashCode(this.host == null ? 0 : this.host.getId());
|
||||
hash = 41 * hash + Objects.hashCode(this.host == null ? 0 : this.host.getHostId());
|
||||
return hash;
|
||||
}
|
||||
|
||||
@ -96,8 +96,8 @@ class AddImageWizardSelectHostVisual extends javax.swing.JPanel {
|
||||
}
|
||||
final HostListItem other = (HostListItem) obj;
|
||||
if (!Objects.equals(
|
||||
this.host == null ? 0 : this.host.getId(),
|
||||
other.host == null ? 0 : other.host.getId())) {
|
||||
this.host == null ? 0 : this.host.getHostId(),
|
||||
other.host == null ? 0 : other.host.getHostId())) {
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -166,7 +166,7 @@ class AddImageWizardSelectHostVisual extends javax.swing.JPanel {
|
||||
if (specifyNewHostRadio.isSelected() && StringUtils.isNotEmpty(specifyNewHostTextField.getText())) {
|
||||
String newHostName = specifyNewHostTextField.getText();
|
||||
try {
|
||||
return Case.getCurrentCaseThrows().getSleuthkitCase().getHostManager().createHost(newHostName);
|
||||
return Case.getCurrentCaseThrows().getSleuthkitCase().getHostManager().newHost(newHostName);
|
||||
} catch (NoCurrentCaseException | TskCoreException ex) {
|
||||
logger.log(Level.WARNING, String.format("Unable to create host '%s'.", newHostName), ex);
|
||||
return null;
|
||||
@ -186,7 +186,7 @@ class AddImageWizardSelectHostVisual extends javax.swing.JPanel {
|
||||
*/
|
||||
private void loadHostData() {
|
||||
try {
|
||||
Collection<Host> hosts = Case.getCurrentCaseThrows().getSleuthkitCase().getHostManager().getHosts();
|
||||
Collection<Host> hosts = Case.getCurrentCaseThrows().getSleuthkitCase().getHostManager().getAllHosts();
|
||||
sanitizedHostSet = HostNameValidator.getSanitizedHostNames(hosts);
|
||||
|
||||
Vector<HostListItem> hostListItems = hosts.stream()
|
||||
|
@ -4,7 +4,6 @@ CTL_CaseCloseAct=Close Case
|
||||
CTL_CaseNewAction=New Case
|
||||
CTL_CaseDetailsAction=Case Details
|
||||
CTL_CaseDeleteAction=Delete Case
|
||||
Menu/Case/OpenRecentCase=Open Recent Case
|
||||
CTL_CaseDeleteAction=Delete Case
|
||||
OpenIDE-Module-Name=Case
|
||||
NewCaseVisualPanel1.caseNameLabel.text_1=Case Name:
|
||||
|
@ -128,6 +128,7 @@ CTL_CaseCloseAct=Close Case
|
||||
CTL_CaseNewAction=New Case
|
||||
CTL_CaseDetailsAction=Case Details
|
||||
CTL_CaseDeleteAction=Delete Case
|
||||
CTL_CaseDeleteAction=Delete Case
|
||||
CTL_CaseOpenAction=Open Case
|
||||
CTL_UnpackagePortableCaseAction=Unpack and Open Portable Case
|
||||
DeleteDataSourceAction.confirmationDialog.message=Are you sure you want to remove the selected data source from the case?\nNote that the case will be closed and re-opened during the removal.
|
||||
@ -186,8 +187,6 @@ LogicalEvidenceFilePanel.pathValidation.getOpenCase.Error=Warning: Exception whi
|
||||
LogicalEvidenceFilePanel.validatePanel.nonL01Error.text=Only files with the .l01 file extension are supported here.
|
||||
LogicalFilesDspPanel.subTypeComboBox.l01FileOption.text=Logical evidence file (L01)
|
||||
LogicalFilesDspPanel.subTypeComboBox.localFilesOption.text=Local files and folders
|
||||
Menu/Case/OpenRecentCase=Open Recent Case
|
||||
CTL_CaseDeleteAction=Delete Case
|
||||
OpenIDE-Module-Name=Case
|
||||
NewCaseVisualPanel1.caseNameLabel.text_1=Case Name:
|
||||
NewCaseVisualPanel1.caseDirLabel.text=Base Directory:
|
||||
@ -346,6 +345,12 @@ RecentCases.getName.text=Clear Recent Cases
|
||||
RecentItems.openRecentCase.msgDlg.text=Case {0} no longer exists.
|
||||
SelectDataSourceProcessorPanel.name.text=Select Data Source Type
|
||||
StartupWindow.title.text=Welcome
|
||||
# {0} - autFilePath
|
||||
StartupWindowProvider.openCase.cantOpen=Unable to open previously open case with metadata file: {0}
|
||||
# {0} - reOpenFilePath
|
||||
StartupWindowProvider.openCase.deleteOpenFailure=Unable to open or delete file containing path {0} to previously open case. The previous case will not be opened.
|
||||
# {0} - autFilePath
|
||||
StartupWindowProvider.openCase.noFile=Unable to open previously open case because metadata file not found at: {0}
|
||||
UnpackagePortableCaseDialog.title.text=Unpackage Portable Case
|
||||
UnpackagePortableCaseDialog.UnpackagePortableCaseDialog.extensions=Portable case package (.zip, .zip.001)
|
||||
UnpackagePortableCaseDialog.validatePaths.badExtension=File extension must be .zip or .zip.001
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2012-2020 Basis Technology Corp.
|
||||
* Copyright 2012-2021 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -29,6 +29,7 @@ import java.awt.event.ActionListener;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.beans.PropertyChangeSupport;
|
||||
import java.io.File;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
@ -62,6 +63,7 @@ import javax.annotation.concurrent.GuardedBy;
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.SwingUtilities;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.openide.util.Lookup;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
@ -87,9 +89,10 @@ import org.sleuthkit.autopsy.casemodule.events.HostsChangedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.events.HostsRemovedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.events.OsAccountAddedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.events.OsAccountChangedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.events.OsAccountDeletedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.events.PersonsAddedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.events.PersonsChangedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.events.PersonsRemovedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.events.PersonsDeletedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.events.ReportAddedEvent;
|
||||
import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData.CaseNodeDataException;
|
||||
import org.sleuthkit.autopsy.casemodule.multiusercases.CoordinationServiceUtils;
|
||||
@ -114,6 +117,7 @@ import org.sleuthkit.autopsy.coreutils.ThreadUtils;
|
||||
import org.sleuthkit.autopsy.coreutils.TimeZoneUtils;
|
||||
import org.sleuthkit.autopsy.coreutils.Version;
|
||||
import org.sleuthkit.autopsy.datamodel.hosts.OpenHostsAction;
|
||||
import org.sleuthkit.autopsy.directorytree.DirectoryTreeTopComponent;
|
||||
import org.sleuthkit.autopsy.events.AutopsyEvent;
|
||||
import org.sleuthkit.autopsy.events.AutopsyEventException;
|
||||
import org.sleuthkit.autopsy.events.AutopsyEventPublisher;
|
||||
@ -124,6 +128,7 @@ import org.sleuthkit.autopsy.ingest.IngestServices;
|
||||
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
|
||||
import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchService;
|
||||
import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchServiceException;
|
||||
import org.sleuthkit.autopsy.machinesettings.UserMachinePreferences;
|
||||
import org.sleuthkit.autopsy.progress.LoggingProgressIndicator;
|
||||
import org.sleuthkit.autopsy.progress.ModalDialogProgressIndicator;
|
||||
import org.sleuthkit.autopsy.progress.ProgressIndicator;
|
||||
@ -138,24 +143,16 @@ import org.sleuthkit.datamodel.ContentTag;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
import org.sleuthkit.datamodel.FileSystem;
|
||||
import org.sleuthkit.datamodel.Host;
|
||||
import org.sleuthkit.datamodel.HostManager.HostsCreationEvent;
|
||||
import org.sleuthkit.datamodel.HostManager.HostsUpdateEvent;
|
||||
import org.sleuthkit.datamodel.HostManager.HostsDeletionEvent;
|
||||
import org.sleuthkit.datamodel.Image;
|
||||
import org.sleuthkit.datamodel.OsAccount;
|
||||
import org.sleuthkit.datamodel.OsAccountManager;
|
||||
import org.sleuthkit.datamodel.OsAccountManager.OsAccountsCreationEvent;
|
||||
import org.sleuthkit.datamodel.OsAccountManager.OsAccountsUpdateEvent;
|
||||
import org.sleuthkit.datamodel.Person;
|
||||
import org.sleuthkit.datamodel.PersonManager.PersonsCreationEvent;
|
||||
import org.sleuthkit.datamodel.PersonManager.PersonsUpdateEvent;
|
||||
import org.sleuthkit.datamodel.PersonManager.PersonsDeletionEvent;
|
||||
import org.sleuthkit.datamodel.Report;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TimelineManager;
|
||||
import org.sleuthkit.datamodel.SleuthkitCaseAdminUtil;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskDataException;
|
||||
import org.sleuthkit.datamodel.TskEvent;
|
||||
import org.sleuthkit.datamodel.TskUnsupportedSchemaVersionException;
|
||||
|
||||
/**
|
||||
@ -163,9 +160,10 @@ import org.sleuthkit.datamodel.TskUnsupportedSchemaVersionException;
|
||||
*/
|
||||
public class Case {
|
||||
|
||||
private static final String CASE_TEMP_DIR = Case.class.getSimpleName();
|
||||
private static final int CASE_LOCK_TIMEOUT_MINS = 1;
|
||||
private static final int CASE_RESOURCES_LOCK_TIMEOUT_HOURS = 1;
|
||||
private static final String APP_NAME = UserPreferences.getAppName();
|
||||
private static final String TEMP_FOLDER = "Temp";
|
||||
private static final String SINGLE_USER_CASE_DB_NAME = "autopsy.db";
|
||||
private static final String EVENT_CHANNEL_NAME = "%s-Case-Events"; //NON-NLS
|
||||
private static final String CACHE_FOLDER = "Cache"; //NON-NLS
|
||||
@ -438,41 +436,38 @@ public class Case {
|
||||
*/
|
||||
OS_ACCOUNT_ADDED,
|
||||
/**
|
||||
* OSAccount associated with the current case has changed.
|
||||
* Call getOsAccount to get the changed account;
|
||||
* OSAccount associated with the current case has changed. Call
|
||||
* getOsAccount to get the changed account;
|
||||
*/
|
||||
OS_ACCOUNT_CHANGED,
|
||||
|
||||
/**
|
||||
* OSAccount associated with the current case has been deleted.
|
||||
*/
|
||||
OS_ACCOUNT_REMOVED,
|
||||
/**
|
||||
* Hosts associated with the current case added.
|
||||
*/
|
||||
HOSTS_ADDED,
|
||||
|
||||
/**
|
||||
* Hosts associated with the current case has changed.
|
||||
* Hosts associated with the current case has changed.
|
||||
*/
|
||||
HOSTS_CHANGED,
|
||||
|
||||
/**
|
||||
* Hosts associated with the current case has been deleted.
|
||||
* Hosts associated with the current case has been deleted.
|
||||
*/
|
||||
HOSTS_DELETED,
|
||||
|
||||
/**
|
||||
* Persons associated with the current case added.
|
||||
*/
|
||||
PERSONS_ADDED,
|
||||
|
||||
/**
|
||||
* Persons associated with the current case has changed.
|
||||
* Persons associated with the current case has changed.
|
||||
*/
|
||||
PERSONS_CHANGED,
|
||||
|
||||
/**
|
||||
* Persons associated with the current case has been deleted.
|
||||
* Persons associated with the current case has been deleted.
|
||||
*/
|
||||
PERSONS_DELETED
|
||||
;
|
||||
PERSONS_DELETED;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -505,90 +500,97 @@ public class Case {
|
||||
event.getArtifacts(artifactType)));
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void publishOsAccountAddedEvent(OsAccountsCreationEvent event) {
|
||||
for(OsAccount account: event.getOsAcounts()) {
|
||||
|
||||
@Subscribe
|
||||
public void publishOsAccountAddedEvent(TskEvent.OsAccountsAddedTskEvent event) {
|
||||
for (OsAccount account : event.getOsAcounts()) {
|
||||
eventPublisher.publish(new OsAccountAddedEvent(account));
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void publishOsAccountChangedEvent(OsAccountsUpdateEvent event) {
|
||||
for(OsAccount account: event.getOsAcounts()) {
|
||||
|
||||
@Subscribe
|
||||
public void publishOsAccountChangedEvent(TskEvent.OsAccountsChangedTskEvent event) {
|
||||
for (OsAccount account : event.getOsAcounts()) {
|
||||
eventPublisher.publish(new OsAccountChangedEvent(account));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Subscribe
|
||||
public void publishOsAccountDeletedEvent(TskEvent.OsAccountsDeletedTskEvent event) {
|
||||
for (Long accountId : event.getOsAcountObjectIds()) {
|
||||
eventPublisher.publish(new OsAccountDeletedEvent(accountId));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Publishes an autopsy event from the sleuthkit HostCreationEvent
|
||||
* Publishes an autopsy event from the sleuthkit HostAddedEvent
|
||||
* indicating that hosts have been created.
|
||||
*
|
||||
*
|
||||
* @param event The sleuthkit event for the creation of hosts.
|
||||
*/
|
||||
@Subscribe
|
||||
public void publishHostsAddedEvent(HostsCreationEvent event) {
|
||||
@Subscribe
|
||||
public void publishHostsAddedEvent(TskEvent.HostsAddedTskEvent event) {
|
||||
eventPublisher.publish(new HostsAddedEvent(
|
||||
event == null ? Collections.emptyList() : event.getHosts()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Publishes an autopsy event from the sleuthkit HostUpdateEvent
|
||||
* Publishes an autopsy event from the sleuthkit HostUpdateEvent
|
||||
* indicating that hosts have been updated.
|
||||
*
|
||||
*
|
||||
* @param event The sleuthkit event for the updating of hosts.
|
||||
*/
|
||||
@Subscribe
|
||||
public void publishHostsChangedEvent(HostsUpdateEvent event) {
|
||||
*/
|
||||
@Subscribe
|
||||
public void publishHostsChangedEvent(TskEvent.HostsChangedTskEvent event) {
|
||||
eventPublisher.publish(new HostsChangedEvent(
|
||||
event == null ? Collections.emptyList() : event.getHosts()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Publishes an autopsy event from the sleuthkit HostDeletedEvent
|
||||
* Publishes an autopsy event from the sleuthkit HostDeletedEvent
|
||||
* indicating that hosts have been deleted.
|
||||
*
|
||||
*
|
||||
* @param event The sleuthkit event for the deleting of hosts.
|
||||
*/
|
||||
@Subscribe
|
||||
public void publishHostsDeletedEvent(HostsDeletionEvent event) {
|
||||
*/
|
||||
@Subscribe
|
||||
public void publishHostsDeletedEvent(TskEvent.HostsDeletedTskEvent event) {
|
||||
eventPublisher.publish(new HostsRemovedEvent(
|
||||
event == null ? Collections.emptyList() : event.getHosts()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Publishes an autopsy event from the sleuthkit PersonCreationEvent
|
||||
* Publishes an autopsy event from the sleuthkit PersonAddedEvent
|
||||
* indicating that persons have been created.
|
||||
*
|
||||
*
|
||||
* @param event The sleuthkit event for the creation of persons.
|
||||
*/
|
||||
@Subscribe
|
||||
public void publishPersonsAddedEvent(PersonsCreationEvent event) {
|
||||
@Subscribe
|
||||
public void publishPersonsAddedEvent(TskEvent.PersonsAddedTskEvent event) {
|
||||
eventPublisher.publish(new PersonsAddedEvent(
|
||||
event == null ? Collections.emptyList() : event.getPersons()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Publishes an autopsy event from the sleuthkit PersonUpdateEvent
|
||||
* Publishes an autopsy event from the sleuthkit PersonChangedEvent
|
||||
* indicating that persons have been updated.
|
||||
*
|
||||
*
|
||||
* @param event The sleuthkit event for the updating of persons.
|
||||
*/
|
||||
@Subscribe
|
||||
public void publishPersonsChangedEvent(PersonsUpdateEvent event) {
|
||||
*/
|
||||
@Subscribe
|
||||
public void publishPersonsChangedEvent(TskEvent.PersonsChangedTskEvent event) {
|
||||
eventPublisher.publish(new PersonsChangedEvent(
|
||||
event == null ? Collections.emptyList() : event.getPersons()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Publishes an autopsy event from the sleuthkit PersonDeletedEvent
|
||||
* Publishes an autopsy event from the sleuthkit PersonDeletedEvent
|
||||
* indicating that persons have been deleted.
|
||||
*
|
||||
*
|
||||
* @param event The sleuthkit event for the deleting of persons.
|
||||
*/
|
||||
@Subscribe
|
||||
public void publishPersonsDeletedEvent(PersonsDeletionEvent event) {
|
||||
eventPublisher.publish(new PersonsRemovedEvent(
|
||||
*/
|
||||
@Subscribe
|
||||
public void publishPersonsDeletedEvent(TskEvent.PersonsDeletedTskEvent event) {
|
||||
eventPublisher.publish(new PersonsDeletedEvent(
|
||||
event == null ? Collections.emptyList() : event.getPersons()));
|
||||
}
|
||||
}
|
||||
@ -869,12 +871,12 @@ public class Case {
|
||||
eventPublisher.publishLocally(new AutopsyEvent(Events.CURRENT_CASE.toString(), closedCase, null));
|
||||
logger.log(Level.INFO, "Closing current case {0} ({1}) in {2}", new Object[]{closedCase.getDisplayName(), closedCase.getName(), closedCase.getCaseDirectory()}); //NON-NLS
|
||||
closedCase.doCloseCaseAction();
|
||||
currentCase = null;
|
||||
logger.log(Level.INFO, "Closed current case {0} ({1}) in {2}", new Object[]{closedCase.getDisplayName(), closedCase.getName(), closedCase.getCaseDirectory()}); //NON-NLS
|
||||
} catch (CaseActionException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Error closing current case %s (%s) in %s", closedCase.getDisplayName(), closedCase.getName(), closedCase.getCaseDirectory()), ex); //NON-NLS
|
||||
throw ex;
|
||||
} finally {
|
||||
currentCase = null;
|
||||
if (RuntimeProperties.runningWithGUI()) {
|
||||
updateGUIForCaseClosed();
|
||||
}
|
||||
@ -1213,9 +1215,7 @@ public class Case {
|
||||
/**
|
||||
* Update the GUI to to reflect the current case.
|
||||
*/
|
||||
private static void updateGUIForCaseOpened(Case newCurrentCase) {
|
||||
if (RuntimeProperties.runningWithGUI()) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
private static void updateGUIForCaseOpened(Case newCurrentCase) {
|
||||
/*
|
||||
* If the case database was upgraded for a new schema and a
|
||||
* backup database was created, notify the user.
|
||||
@ -1241,17 +1241,31 @@ public class Case {
|
||||
String path = entry.getValue();
|
||||
boolean fileExists = (new File(path).isFile() || DriveUtils.driveExists(path));
|
||||
if (!fileExists) {
|
||||
int response = JOptionPane.showConfirmDialog(
|
||||
mainFrame,
|
||||
NbBundle.getMessage(Case.class, "Case.checkImgExist.confDlg.doesntExist.msg", path),
|
||||
NbBundle.getMessage(Case.class, "Case.checkImgExist.confDlg.doesntExist.title"),
|
||||
JOptionPane.YES_NO_OPTION);
|
||||
if (response == JOptionPane.YES_OPTION) {
|
||||
MissingImageDialog.makeDialog(obj_id, caseDb);
|
||||
} else {
|
||||
logger.log(Level.SEVERE, "User proceeding with missing image files"); //NON-NLS
|
||||
try {
|
||||
// Using invokeAndWait means that the dialog will
|
||||
// open on the EDT but this thread will wait for an
|
||||
// answer. Using invokeLater would cause this loop to
|
||||
// end before all of the dialogs appeared.
|
||||
SwingUtilities.invokeAndWait(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
int response = JOptionPane.showConfirmDialog(
|
||||
mainFrame,
|
||||
NbBundle.getMessage(Case.class, "Case.checkImgExist.confDlg.doesntExist.msg", path),
|
||||
NbBundle.getMessage(Case.class, "Case.checkImgExist.confDlg.doesntExist.title"),
|
||||
JOptionPane.YES_NO_OPTION);
|
||||
if (response == JOptionPane.YES_OPTION) {
|
||||
MissingImageDialog.makeDialog(obj_id, caseDb);
|
||||
} else {
|
||||
logger.log(Level.SEVERE, "User proceeding with missing image files"); //NON-NLS
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
} catch (InterruptedException | InvocationTargetException ex) {
|
||||
logger.log(Level.SEVERE, "Failed to show missing image confirmation dialog", ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1269,14 +1283,16 @@ public class Case {
|
||||
CallableSystemAction.get(CommonAttributeSearchAction.class).setEnabled(true);
|
||||
CallableSystemAction.get(OpenOutputFolderAction.class).setEnabled(false);
|
||||
CallableSystemAction.get(OpenDiscoveryAction.class).setEnabled(true);
|
||||
|
||||
/*
|
||||
* Add the case to the recent cases tracker that supplies a list
|
||||
* of recent cases to the recent cases menu item and the
|
||||
* open/create case dialog.
|
||||
*/
|
||||
RecentCases.getInstance().addRecentCase(newCurrentCase.getDisplayName(), newCurrentCase.getMetadata().getFilePath().toString());
|
||||
|
||||
|
||||
/*
|
||||
* Add the case to the recent cases tracker that supplies a list
|
||||
* of recent cases to the recent cases menu item and the
|
||||
* open/create case dialog.
|
||||
*/
|
||||
RecentCases.getInstance().addRecentCase(newCurrentCase.getDisplayName(), newCurrentCase.getMetadata().getFilePath().toString());
|
||||
final boolean hasData = newCurrentCase.hasData();
|
||||
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
/*
|
||||
* Open the top components (windows within the main application
|
||||
* window).
|
||||
@ -1285,8 +1301,11 @@ public class Case {
|
||||
* opened via the DirectoryTreeTopComponent 'propertyChange()'
|
||||
* method on a DATA_SOURCE_ADDED event.
|
||||
*/
|
||||
if (newCurrentCase.hasData()) {
|
||||
if (hasData) {
|
||||
CoreComponentControl.openCoreWindows();
|
||||
} else {
|
||||
//ensure that the DirectoryTreeTopComponent is open so that it's listener can open the core windows including making it visible.
|
||||
DirectoryTreeTopComponent.findInstance();
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1296,7 +1315,6 @@ public class Case {
|
||||
*/
|
||||
mainFrame.setTitle(newCurrentCase.getDisplayName() + " - " + getNameForTitle());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1471,6 +1489,13 @@ public class Case {
|
||||
return hostPath.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A subdirectory of java.io.tmpdir.
|
||||
*/
|
||||
private Path getBaseSystemTempPath() {
|
||||
return Paths.get(System.getProperty("java.io.tmpdir"), APP_NAME, getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the full path to the temp directory for this case, creating it if it
|
||||
* does not exist.
|
||||
@ -1478,16 +1503,45 @@ public class Case {
|
||||
* @return The temp subdirectory path.
|
||||
*/
|
||||
public String getTempDirectory() {
|
||||
// get temp folder scoped to the combination of case name and timestamp
|
||||
// provided by getName()
|
||||
Path path = Paths.get(UserPreferences.getAppTempDirectory(), CASE_TEMP_DIR, getName());
|
||||
File f = path.toFile();
|
||||
// verify that the folder exists
|
||||
if (!f.exists()) {
|
||||
f.mkdirs();
|
||||
// NOTE: UserPreferences may also be affected by changes in this method.
|
||||
// See JIRA-7505 for more information.
|
||||
Path basePath = null;
|
||||
// get base temp path for the case based on user preference
|
||||
switch (UserMachinePreferences.getTempDirChoice()) {
|
||||
case CUSTOM:
|
||||
String customDirectory = UserMachinePreferences.getCustomTempDirectory();
|
||||
basePath = (StringUtils.isBlank(customDirectory))
|
||||
? null
|
||||
: Paths.get(customDirectory, APP_NAME, getName());
|
||||
break;
|
||||
case CASE:
|
||||
basePath = Paths.get(getCaseDirectory());
|
||||
break;
|
||||
case SYSTEM:
|
||||
default:
|
||||
// at this level, if the case directory is specified for a temp
|
||||
// directory, return the system temp directory instead.
|
||||
basePath = getBaseSystemTempPath();
|
||||
break;
|
||||
}
|
||||
|
||||
return path.toAbsolutePath().toString();
|
||||
basePath = basePath == null ? getBaseSystemTempPath() : basePath;
|
||||
|
||||
// get sub directories based on multi user vs. single user
|
||||
Path caseRelPath = (CaseType.MULTI_USER_CASE.equals(getCaseType()))
|
||||
? Paths.get(NetworkUtils.getLocalHostName(), TEMP_FOLDER)
|
||||
: Paths.get(TEMP_FOLDER);
|
||||
|
||||
File caseTempDir = basePath
|
||||
.resolve(caseRelPath)
|
||||
.toFile();
|
||||
|
||||
// ensure directory exists
|
||||
if (!caseTempDir.exists()) {
|
||||
caseTempDir.mkdirs();
|
||||
}
|
||||
|
||||
return caseTempDir.getAbsolutePath();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1789,7 +1843,7 @@ public class Case {
|
||||
public void notifyBlackBoardArtifactTagDeleted(BlackboardArtifactTag deletedTag) {
|
||||
eventPublisher.publish(new BlackBoardArtifactTagDeletedEvent(deletedTag));
|
||||
}
|
||||
|
||||
|
||||
public void notifyOsAccountAdded(OsAccount account) {
|
||||
eventPublisher.publish(new OsAccountAddedEvent(account));
|
||||
}
|
||||
@ -1797,9 +1851,14 @@ public class Case {
|
||||
public void notifyOsAccountChanged(OsAccount account) {
|
||||
eventPublisher.publish(new OsAccountChangedEvent(account));
|
||||
}
|
||||
|
||||
|
||||
public void notifyOsAccountRemoved(Long osAccountObjectId) {
|
||||
eventPublisher.publish(new OsAccountDeletedEvent(osAccountObjectId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify via an autopsy event that a host has been added.
|
||||
*
|
||||
* @param host The host that has been added.
|
||||
*/
|
||||
public void notifyHostAdded(Host host) {
|
||||
@ -1808,22 +1867,25 @@ public class Case {
|
||||
|
||||
/**
|
||||
* Notify via an autopsy event that a host has been changed.
|
||||
*
|
||||
* @param newValue The host that has been updated.
|
||||
*/
|
||||
public void notifyHostChanged(Host newValue) {
|
||||
eventPublisher.publish(new HostsChangedEvent(Collections.singletonList(newValue)));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Notify via an autopsy event that a host has been deleted.
|
||||
*
|
||||
* @param host The host that has been deleted.
|
||||
*/
|
||||
public void notifyHostDeleted(Host host) {
|
||||
eventPublisher.publish(new HostsRemovedEvent(Collections.singletonList(host)));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Notify via an autopsy event that a person has been added.
|
||||
*
|
||||
* @param person The person that has been added.
|
||||
*/
|
||||
public void notifyPersonAdded(Person person) {
|
||||
@ -1832,20 +1894,22 @@ public class Case {
|
||||
|
||||
/**
|
||||
* Notify via an autopsy event that a person has been changed.
|
||||
*
|
||||
* @param newValue The person that has been updated.
|
||||
*/
|
||||
public void notifyPersonChanged(Person newValue) {
|
||||
eventPublisher.publish(new PersonsChangedEvent(Collections.singletonList(newValue)));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Notify via an autopsy event that a person has been deleted.
|
||||
*
|
||||
* @param person The person that has been deleted.
|
||||
*/
|
||||
public void notifyPersonDeleted(Person person) {
|
||||
eventPublisher.publish(new PersonsRemovedEvent(Collections.singletonList(person)));
|
||||
eventPublisher.publish(new PersonsDeletedEvent(Collections.singletonList(person)));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a report to the case.
|
||||
*
|
||||
@ -1924,7 +1988,7 @@ public class Case {
|
||||
*
|
||||
* @return A CaseMetaData object.
|
||||
*/
|
||||
CaseMetadata getMetadata() {
|
||||
public CaseMetadata getMetadata() {
|
||||
return metadata;
|
||||
}
|
||||
|
||||
|
@ -218,7 +218,7 @@ public final class CaseMetadata {
|
||||
*
|
||||
* @return The path to the metadata file
|
||||
*/
|
||||
Path getFilePath() {
|
||||
public Path getFilePath() {
|
||||
return metadataFilePath;
|
||||
}
|
||||
|
||||
|
@ -35,22 +35,22 @@ import org.sleuthkit.autopsy.directorytree.DirectoryTreeTopComponent;
|
||||
* Read and update case preference file values.
|
||||
*/
|
||||
public final class CasePreferences {
|
||||
|
||||
|
||||
private static final String SETTINGS_FILE = "CasePreferences.properties"; //NON-NLS
|
||||
private static final String KEY_GROUP_BY_DATA_SOURCE = "groupByDataSource"; //NON-NLS
|
||||
private static final String VALUE_TRUE = "true"; //NON-NLS
|
||||
private static final String VALUE_FALSE = "false"; //NON-NLS
|
||||
|
||||
|
||||
private static final Logger logger = Logger.getLogger(CasePreferences.class.getName());
|
||||
|
||||
|
||||
private static Boolean groupItemsInTreeByDataSource = false;
|
||||
|
||||
|
||||
/**
|
||||
* Prevent instantiation.
|
||||
*/
|
||||
private CasePreferences() {
|
||||
}
|
||||
|
||||
|
||||
static {
|
||||
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), (PropertyChangeEvent evt) -> {
|
||||
if (evt.getNewValue() != null) {
|
||||
@ -66,25 +66,27 @@ public final class CasePreferences {
|
||||
logger.log(Level.SEVERE, "No current case open.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the 'groupItemsInTreeByDataSource' value. This can be true, false, or
|
||||
* null.
|
||||
*
|
||||
*
|
||||
* @return The value.
|
||||
*/
|
||||
public static Boolean getGroupItemsInTreeByDataSource() {
|
||||
return groupItemsInTreeByDataSource;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the 'groupItemsInTreeByDataSource' value to true or false.
|
||||
*
|
||||
*
|
||||
* @param value The value to use for the value change.
|
||||
*/
|
||||
public static void setGroupItemsInTreeByDataSource(boolean value) {
|
||||
groupItemsInTreeByDataSource = value;
|
||||
DirectoryTreeTopComponent.getDefault().refreshContentTreeSafe();
|
||||
if (Case.isCaseOpen()) {
|
||||
DirectoryTreeTopComponent.getDefault().refreshContentTreeSafe();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -120,7 +122,7 @@ public final class CasePreferences {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reset all values to their default states.
|
||||
*/
|
||||
|
@ -284,10 +284,10 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
|
||||
ingestStream = IngestManager.getInstance().openIngestStream(image, settings);
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Error starting ingest modules", ex);
|
||||
final List<String> errors = new ArrayList<>();
|
||||
errors.add(ex.getMessage());
|
||||
callBack.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errors, new ArrayList<>());
|
||||
return;
|
||||
// There was an error with ingest, but the data source has already been added
|
||||
// so proceed with the defaultIngestStream. Code in openIngestStream
|
||||
// should have caused a dialog to popup with the errors.
|
||||
ingestStream = new DefaultIngestStream();
|
||||
}
|
||||
|
||||
doAddImageProcess(deviceId, imagePath, sectorSize, timeZone, ignoreFatOrphanFiles, md5, sha1, sha256, progress, callBack);
|
||||
|
@ -55,10 +55,8 @@ class NewCaseWizardPanel2 implements WizardDescriptor.ValidatingPanel<WizardDesc
|
||||
public NewCaseVisualPanel2 getComponent() {
|
||||
if (component == null) {
|
||||
component = new NewCaseVisualPanel2();
|
||||
} else {
|
||||
component.refreshCaseDetailsFields();
|
||||
}
|
||||
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
@ -137,6 +135,7 @@ class NewCaseWizardPanel2 implements WizardDescriptor.ValidatingPanel<WizardDesc
|
||||
@Override
|
||||
public void readSettings(WizardDescriptor settings) {
|
||||
NewCaseVisualPanel2 panel = getComponent();
|
||||
panel.refreshCaseDetailsFields();
|
||||
try {
|
||||
String lastExaminerName = ModuleSettings.getConfigSetting(ModuleSettings.MAIN_SETTINGS, PROP_EXAMINER_NAME);
|
||||
String lastExaminerPhone = ModuleSettings.getConfigSetting(ModuleSettings.MAIN_SETTINGS, PROP_EXAMINER_PHONE);
|
||||
|
@ -58,7 +58,6 @@ final class OptionalCasePropertiesPanel extends javax.swing.JPanel {
|
||||
lbPointOfContactPhoneText.setVisible(false);
|
||||
lbPointOfContactEmailLabel.setVisible(false);
|
||||
lbPointOfContactEmailText.setVisible(false);
|
||||
setUpCaseDetailsFields();
|
||||
}
|
||||
|
||||
OptionalCasePropertiesPanel(boolean editCurrentCase) {
|
||||
|
@ -37,11 +37,8 @@ class SolrNotConfiguredDialog extends javax.swing.JDialog {
|
||||
SolrNotConfiguredDialog() {
|
||||
super((JFrame) WindowManager.getDefault().getMainWindow(), true);
|
||||
// Center the startup window.
|
||||
Dimension screenDimension = Toolkit.getDefaultToolkit().getScreenSize();
|
||||
int width = getSize().width;
|
||||
int height = getSize().height;
|
||||
setLocation((screenDimension.width - width) / 2, (screenDimension.height - height) / 2);
|
||||
initComponents();
|
||||
setLocationRelativeTo(WindowManager.getDefault().getMainWindow());
|
||||
setIconImage(ImageUtilities.loadImage("org/sleuthkit/autopsy/images/warning16.png", false));
|
||||
}
|
||||
|
||||
|
@ -18,12 +18,18 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.casemodule;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.logging.Level;
|
||||
import org.netbeans.spi.sendopts.OptionProcessor;
|
||||
import javax.swing.SwingUtilities;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.openide.util.Lookup;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.apputils.ResetWindowsAction;
|
||||
import org.sleuthkit.autopsy.commandlineingest.CommandLineIngestManager;
|
||||
import org.sleuthkit.autopsy.commandlineingest.CommandLineOpenCaseManager;
|
||||
import org.sleuthkit.autopsy.commandlineingest.CommandLineOptionProcessor;
|
||||
@ -61,15 +67,22 @@ public class StartupWindowProvider implements StartupWindowInterface {
|
||||
return instance;
|
||||
}
|
||||
|
||||
@NbBundle.Messages({
|
||||
"# {0} - autFilePath",
|
||||
"StartupWindowProvider.openCase.noFile=Unable to open previously open case because metadata file not found at: {0}",
|
||||
"# {0} - reOpenFilePath",
|
||||
"StartupWindowProvider.openCase.deleteOpenFailure=Unable to open or delete file containing path {0} to previously open case. The previous case will not be opened.",
|
||||
"# {0} - autFilePath",
|
||||
"StartupWindowProvider.openCase.cantOpen=Unable to open previously open case with metadata file: {0}"})
|
||||
private void init() {
|
||||
if (startupWindowToUse == null) {
|
||||
// first check whether we are running from command line
|
||||
if (isRunningFromCommandLine()) {
|
||||
|
||||
|
||||
String defaultArg = getDefaultArgument();
|
||||
if(defaultArg != null) {
|
||||
new CommandLineOpenCaseManager(defaultArg).start();
|
||||
return;
|
||||
if (defaultArg != null) {
|
||||
new CommandLineOpenCaseManager(defaultArg).start();
|
||||
return;
|
||||
} else {
|
||||
// Autopsy is running from command line
|
||||
logger.log(Level.INFO, "Running from command line"); //NON-NLS
|
||||
@ -83,36 +96,41 @@ public class StartupWindowProvider implements StartupWindowInterface {
|
||||
if (RuntimeProperties.runningWithGUI()) {
|
||||
checkSolr();
|
||||
}
|
||||
|
||||
//discover the registered windows
|
||||
Collection<? extends StartupWindowInterface> startupWindows
|
||||
= Lookup.getDefault().lookupAll(StartupWindowInterface.class);
|
||||
|
||||
int windowsCount = startupWindows.size();
|
||||
if (windowsCount == 1) {
|
||||
startupWindowToUse = startupWindows.iterator().next();
|
||||
logger.log(Level.INFO, "Will use the default startup window: " + startupWindowToUse.toString()); //NON-NLS
|
||||
} else if (windowsCount == 2) {
|
||||
//pick the non default one
|
||||
Iterator<? extends StartupWindowInterface> it = startupWindows.iterator();
|
||||
while (it.hasNext()) {
|
||||
StartupWindowInterface window = it.next();
|
||||
if (!org.sleuthkit.autopsy.casemodule.StartupWindow.class.isInstance(window)) {
|
||||
startupWindowToUse = window;
|
||||
logger.log(Level.INFO, "Will use the custom startup window: " + startupWindowToUse.toString()); //NON-NLS
|
||||
break;
|
||||
switch (windowsCount) {
|
||||
case 1:
|
||||
startupWindowToUse = startupWindows.iterator().next();
|
||||
logger.log(Level.INFO, "Will use the default startup window: {0}", startupWindowToUse.toString()); //NON-NLS
|
||||
break;
|
||||
case 2: {
|
||||
//pick the non default one
|
||||
Iterator<? extends StartupWindowInterface> it = startupWindows.iterator();
|
||||
while (it.hasNext()) {
|
||||
StartupWindowInterface window = it.next();
|
||||
if (!org.sleuthkit.autopsy.casemodule.StartupWindow.class.isInstance(window)) {
|
||||
startupWindowToUse = window;
|
||||
logger.log(Level.INFO, "Will use the custom startup window: {0}", startupWindowToUse.toString()); //NON-NLS
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// select first non-Autopsy start up window
|
||||
Iterator<? extends StartupWindowInterface> it = startupWindows.iterator();
|
||||
while (it.hasNext()) {
|
||||
StartupWindowInterface window = it.next();
|
||||
if (!window.getClass().getCanonicalName().startsWith("org.sleuthkit.autopsy")) {
|
||||
startupWindowToUse = window;
|
||||
logger.log(Level.INFO, "Will use the custom startup window: " + startupWindowToUse.toString()); //NON-NLS
|
||||
break;
|
||||
default: {
|
||||
// select first non-Autopsy start up window
|
||||
Iterator<? extends StartupWindowInterface> it = startupWindows.iterator();
|
||||
while (it.hasNext()) {
|
||||
StartupWindowInterface window = it.next();
|
||||
if (!window.getClass().getCanonicalName().startsWith("org.sleuthkit.autopsy")) {
|
||||
startupWindowToUse = window;
|
||||
logger.log(Level.INFO, "Will use the custom startup window: {0}", startupWindowToUse.toString()); //NON-NLS
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -121,6 +139,45 @@ public class StartupWindowProvider implements StartupWindowInterface {
|
||||
startupWindowToUse = new org.sleuthkit.autopsy.casemodule.StartupWindow();
|
||||
}
|
||||
}
|
||||
File openPreviousCaseFile = new File(ResetWindowsAction.getCaseToReopenFilePath());
|
||||
|
||||
if (openPreviousCaseFile.exists()) {
|
||||
//do actual opening on another thread
|
||||
new Thread(() -> {
|
||||
String caseFilePath = "";
|
||||
String unableToOpenMessage = null;
|
||||
try {
|
||||
//avoid readFileToString having ambiguous arguments
|
||||
Charset encoding = null;
|
||||
caseFilePath = FileUtils.readFileToString(openPreviousCaseFile, encoding);
|
||||
if (new File(caseFilePath).exists()) {
|
||||
FileUtils.forceDelete(openPreviousCaseFile);
|
||||
//close the startup window as we attempt to open the case
|
||||
close();
|
||||
Case.openAsCurrentCase(caseFilePath);
|
||||
|
||||
} else {
|
||||
unableToOpenMessage = Bundle.StartupWindowProvider_openCase_noFile(caseFilePath);
|
||||
logger.log(Level.WARNING, unableToOpenMessage);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
unableToOpenMessage = Bundle.StartupWindowProvider_openCase_deleteOpenFailure(ResetWindowsAction.getCaseToReopenFilePath());
|
||||
logger.log(Level.WARNING, unableToOpenMessage, ex);
|
||||
} catch (CaseActionException ex) {
|
||||
unableToOpenMessage = Bundle.StartupWindowProvider_openCase_cantOpen(caseFilePath);
|
||||
logger.log(Level.WARNING, unableToOpenMessage, ex);
|
||||
}
|
||||
|
||||
if (RuntimeProperties.runningWithGUI() && !StringUtils.isBlank(unableToOpenMessage)) {
|
||||
final String message = unableToOpenMessage;
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
MessageNotifyUtil.Message.warn(message);
|
||||
//the case was not opened restore the startup window
|
||||
open();
|
||||
});
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
}
|
||||
|
||||
private void checkSolr() {
|
||||
@ -147,9 +204,9 @@ public class StartupWindowProvider implements StartupWindowInterface {
|
||||
* @return True if running from command line, false otherwise
|
||||
*/
|
||||
private boolean isRunningFromCommandLine() {
|
||||
|
||||
|
||||
CommandLineOptionProcessor processor = Lookup.getDefault().lookup(CommandLineOptionProcessor.class);
|
||||
if(processor != null) {
|
||||
if (processor != null) {
|
||||
return processor.isRunFromCommandLine();
|
||||
}
|
||||
return false;
|
||||
@ -157,12 +214,12 @@ public class StartupWindowProvider implements StartupWindowInterface {
|
||||
|
||||
/**
|
||||
* Get the default argument from the CommandLineOptionProcessor.
|
||||
*
|
||||
* @return If set, the default argument otherwise null.
|
||||
*
|
||||
* @return If set, the default argument otherwise null.
|
||||
*/
|
||||
private String getDefaultArgument() {
|
||||
private String getDefaultArgument() {
|
||||
CommandLineOptionProcessor processor = Lookup.getDefault().lookup(CommandLineOptionProcessor.class);
|
||||
if(processor != null) {
|
||||
if (processor != null) {
|
||||
return processor.getDefaultArgument();
|
||||
}
|
||||
return null;
|
||||
|
@ -33,6 +33,8 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
*/
|
||||
public class HostsEvent extends TskDataModelChangeEvent<Host> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Retrieves a list of ids from a list of hosts.
|
||||
*
|
||||
@ -42,7 +44,7 @@ public class HostsEvent extends TskDataModelChangeEvent<Host> {
|
||||
private static List<Long> getIds(List<Host> hosts) {
|
||||
return getSafeList(hosts).stream()
|
||||
.filter(h -> h != null)
|
||||
.map(h -> h.getId()).collect(Collectors.toList());
|
||||
.map(h -> h.getHostId()).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -76,7 +78,7 @@ public class HostsEvent extends TskDataModelChangeEvent<Host> {
|
||||
continue;
|
||||
}
|
||||
|
||||
Optional<Host> thisHostOpt = hostManager.getHost(id);
|
||||
Optional<Host> thisHostOpt = hostManager.getHostById(id);
|
||||
thisHostOpt.ifPresent((h) -> toRet.add(h));
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.casemodule.events;
|
||||
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.events.AutopsyEvent;
|
||||
|
||||
/**
|
||||
* Event published when an OsAccount is deleted.
|
||||
*
|
||||
* oldValue will contain the objectId of the account that was removed. newValue
|
||||
* will be null.
|
||||
*/
|
||||
public final class OsAccountDeletedEvent extends AutopsyEvent {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public OsAccountDeletedEvent(Long osAccountObjectId) {
|
||||
super(Case.Events.OS_ACCOUNT_REMOVED.toString(), osAccountObjectId, null);
|
||||
}
|
||||
}
|
@ -56,7 +56,7 @@ class OsAccountEvent extends TskDataModelChangeEvent<OsAccount> {
|
||||
@Override
|
||||
protected List<OsAccount> getDataModelObjects(SleuthkitCase caseDb, List<Long> ids) throws TskCoreException {
|
||||
Long id = ids.get(0);
|
||||
OsAccount account = caseDb.getOsAccountManager().getOsAccount(id);
|
||||
OsAccount account = caseDb.getOsAccountManager().getOsAccountByObjectId(id);
|
||||
List<OsAccount> accounts = new ArrayList<>();
|
||||
accounts.add(account);
|
||||
return accounts;
|
||||
|
@ -25,7 +25,7 @@ import org.sleuthkit.datamodel.Person;
|
||||
/**
|
||||
* Event fired when persons are removed.
|
||||
*/
|
||||
public class PersonsRemovedEvent extends PersonsEvent {
|
||||
public class PersonsDeletedEvent extends PersonsEvent {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ -33,7 +33,7 @@ public class PersonsRemovedEvent extends PersonsEvent {
|
||||
* Main constructor.
|
||||
* @param dataModelObjects The list of persons that have been deleted.
|
||||
*/
|
||||
public PersonsRemovedEvent(List<Person> dataModelObjects) {
|
||||
public PersonsDeletedEvent(List<Person> dataModelObjects) {
|
||||
super(Case.Events.PERSONS_DELETED.name(), dataModelObjects);
|
||||
}
|
||||
}
|
@ -42,7 +42,7 @@ public class PersonsEvent extends TskDataModelChangeEvent<Person> {
|
||||
private static List<Long> getIds(List<Person> persons) {
|
||||
return getSafeList(persons).stream()
|
||||
.filter(h -> h != null)
|
||||
.map(h -> h.getId()).collect(Collectors.toList());
|
||||
.map(h -> h.getPersonId()).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -23,25 +23,4 @@
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JScrollPane" name="caseTableScrollPane">
|
||||
<Properties>
|
||||
<Property name="horizontalScrollBarPolicy" type="int" value="31"/>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[0, 5]"/>
|
||||
</Property>
|
||||
<Property name="opaque" type="boolean" value="false"/>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[500, 500]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
|
||||
<BorderConstraints direction="Center"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2017-2019 Basis Technology Corp.
|
||||
* Copyright 2017-2021 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -18,6 +18,7 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.casemodule.multiusercasesbrowser;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -68,8 +69,7 @@ public final class MultiUserCasesBrowserPanel extends javax.swing.JPanel impleme
|
||||
outlineView = new org.openide.explorer.view.OutlineView();
|
||||
outline = this.outlineView.getOutline();
|
||||
configureOutlineView();
|
||||
caseTableScrollPane.add(outlineView);
|
||||
caseTableScrollPane.setViewportView(outlineView);
|
||||
add(outlineView, BorderLayout.CENTER);
|
||||
this.setVisible(true);
|
||||
}
|
||||
|
||||
@ -146,20 +146,11 @@ public final class MultiUserCasesBrowserPanel extends javax.swing.JPanel impleme
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
caseTableScrollPane = new javax.swing.JScrollPane();
|
||||
|
||||
setMinimumSize(new java.awt.Dimension(0, 5));
|
||||
setPreferredSize(new java.awt.Dimension(5, 5));
|
||||
setLayout(new java.awt.BorderLayout());
|
||||
|
||||
caseTableScrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
|
||||
caseTableScrollPane.setMinimumSize(new java.awt.Dimension(0, 5));
|
||||
caseTableScrollPane.setOpaque(false);
|
||||
caseTableScrollPane.setPreferredSize(new java.awt.Dimension(500, 500));
|
||||
add(caseTableScrollPane, java.awt.BorderLayout.CENTER);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JScrollPane caseTableScrollPane;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ import org.sleuthkit.datamodel.TskException;
|
||||
* View correlation results from other cases
|
||||
*/
|
||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||
@ServiceProvider(service = DataContentViewer.class, position = 9)
|
||||
@ServiceProvider(service = DataContentViewer.class, position = 10)
|
||||
@Messages({"DataContentViewerOtherCases.title=Other Occurrences",
|
||||
"DataContentViewerOtherCases.toolTip=Displays instances of the selected file/artifact from other occurrences."})
|
||||
public final class DataContentViewerOtherCases extends JPanel implements DataContentViewer {
|
||||
|
@ -130,7 +130,7 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel {
|
||||
exportToCSVMenuItem.addActionListener(actList);
|
||||
showCaseDetailsMenuItem.addActionListener(actList);
|
||||
showCommonalityMenuItem.addActionListener(actList);
|
||||
|
||||
filesTable.setComponentPopupMenu(rightClickPopupMenu);
|
||||
// Configure column sorting.
|
||||
TableRowSorter<TableModel> sorter = new TableRowSorter<>(filesTable.getModel());
|
||||
filesTable.setRowSorter(sorter);
|
||||
@ -380,7 +380,6 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel {
|
||||
int totalCount = 0;
|
||||
Set<String> dataSources = new HashSet<>();
|
||||
if (CentralRepository.isEnabled()) {
|
||||
|
||||
try {
|
||||
List<CorrelationAttributeInstance> instances;
|
||||
instances = CentralRepository.getInstance().getArtifactInstancesByTypeValue(aType, value);
|
||||
@ -395,12 +394,12 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel {
|
||||
// - the data source device ID is different
|
||||
// - the file path is different
|
||||
if (artifactInstance.getCorrelationCase().getCaseUUID().equals(caseUUID)
|
||||
|| (!StringUtils.isBlank(dataSourceName) && artifactInstance.getCorrelationDataSource().getName().equals(dataSourceName))
|
||||
|| (!StringUtils.isBlank(deviceId) && artifactInstance.getCorrelationDataSource().getDeviceID().equals(deviceId))
|
||||
|| (file != null && artifactInstance.getFilePath().equalsIgnoreCase(file.getParentPath() + file.getName()))) {
|
||||
correlationAttributes.add(artifactInstance);
|
||||
&& (!StringUtils.isBlank(dataSourceName) && artifactInstance.getCorrelationDataSource().getName().equals(dataSourceName))
|
||||
&& (!StringUtils.isBlank(deviceId) && artifactInstance.getCorrelationDataSource().getDeviceID().equals(deviceId))
|
||||
&& (file != null && artifactInstance.getFilePath().equalsIgnoreCase(file.getParentPath() + file.getName()))) {
|
||||
continue;
|
||||
}
|
||||
correlationAttributes.add(artifactInstance);
|
||||
OtherOccurrenceNodeInstanceData newNode = new OtherOccurrenceNodeInstanceData(artifactInstance, aType, value);
|
||||
UniquePathKey uniquePathKey = new UniquePathKey(newNode);
|
||||
nodeDataMap.put(uniquePathKey, newNode);
|
||||
@ -510,9 +509,7 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel {
|
||||
* artifact. If the central repo is not enabled, this will only return files
|
||||
* from the current case with matching MD5 hashes.
|
||||
*
|
||||
* @param corAttr CorrelationAttribute to query for
|
||||
* @param dataSourceName Data source to filter results
|
||||
* @param deviceId Device Id to filter results
|
||||
* @param corAttr CorrelationAttribute to query for
|
||||
*
|
||||
* @return A collection of correlated artifact instances
|
||||
*/
|
||||
@ -535,9 +532,9 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel {
|
||||
// - the data source device ID is different
|
||||
// - the file path is different
|
||||
if (artifactInstance.getCorrelationCase().getCaseUUID().equals(caseUUID)
|
||||
|| (!StringUtils.isBlank(dataSourceName) && artifactInstance.getCorrelationDataSource().getName().equals(dataSourceName))
|
||||
|| (!StringUtils.isBlank(deviceId) && artifactInstance.getCorrelationDataSource().getDeviceID().equals(deviceId))
|
||||
|| (file != null && artifactInstance.getFilePath().equalsIgnoreCase(file.getParentPath() + file.getName()))) {
|
||||
&& (!StringUtils.isBlank(dataSourceName) && artifactInstance.getCorrelationDataSource().getName().equals(dataSourceName))
|
||||
&& (!StringUtils.isBlank(deviceId) && artifactInstance.getCorrelationDataSource().getDeviceID().equals(deviceId))
|
||||
&& (file != null && artifactInstance.getFilePath().equalsIgnoreCase(file.getParentPath() + file.getName()))) {
|
||||
continue;
|
||||
}
|
||||
OtherOccurrenceNodeInstanceData newNode = new OtherOccurrenceNodeInstanceData(artifactInstance, corAttr.getCorrelationType(), corAttr.getCorrelationValue());
|
||||
@ -618,6 +615,7 @@ public final class OtherOccurrencesPanel extends javax.swing.JPanel {
|
||||
*
|
||||
* @param corAttr The CorrelationAttribute containing the MD5 to search for
|
||||
* @param openCase The current case
|
||||
* @param file The current file.
|
||||
*
|
||||
* @return List of matching AbstractFile objects
|
||||
*
|
||||
|
@ -12,7 +12,7 @@ CentralRepoDbChoice.PostgreSQL.Text=Custom PostgreSQL
|
||||
CentralRepoDbChoice.PostgreSQL_Multiuser.Text=PostgreSQL using multi-user settings
|
||||
CentralRepoDbChoice.Sqlite.Text=SQLite
|
||||
CentralRepoDbManager.connectionErrorMsg.text=Failed to connect to central repository database.
|
||||
CentralRepositoryService.progressMsg.updatingSchema=Updating schema...
|
||||
CentralRepositoryService.progressMsg.updatingSchema=Checking for schema updates...
|
||||
CentralRepositoryService.progressMsg.waitingForListeners=Finishing adding data to central repository database....
|
||||
CentralRepositoryService.serviceName=Central Repository Service
|
||||
CorrelationAttributeInstance.invalidName.message=Invalid database table name. Name must start with a lowercase letter and can only contain lowercase letters, numbers, and '_'.
|
||||
|
@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.centralrepository.datamodel;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
@ -860,10 +861,10 @@ public interface CentralRepository {
|
||||
* Get account type by type name.
|
||||
*
|
||||
* @param accountTypeName account type name to look for
|
||||
* @return CR account type
|
||||
* @return CR account type (if found)
|
||||
* @throws CentralRepoException
|
||||
*/
|
||||
CentralRepoAccountType getAccountTypeByName(String accountTypeName) throws CentralRepoException;
|
||||
Optional<CentralRepoAccountType> getAccountTypeByName(String accountTypeName) throws CentralRepoException;
|
||||
|
||||
/**
|
||||
* Gets all account types.
|
||||
|
2
Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepositoryService.java
Normal file → Executable file
2
Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepositoryService.java
Normal file → Executable file
@ -47,7 +47,7 @@ public class CentralRepositoryService implements AutopsyService {
|
||||
}
|
||||
|
||||
@NbBundle.Messages({
|
||||
"CentralRepositoryService.progressMsg.updatingSchema=Updating schema..."
|
||||
"CentralRepositoryService.progressMsg.updatingSchema=Checking for schema updates..."
|
||||
})
|
||||
@Override
|
||||
public void openCaseResources(CaseContext context) throws AutopsyServiceException {
|
||||
|
@ -22,6 +22,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
@ -308,8 +309,12 @@ public class CorrelationAttributeUtil {
|
||||
if (Account.Type.DEVICE.getTypeName().equalsIgnoreCase(accountTypeStr) == false && predefinedAccountType != null) {
|
||||
|
||||
// Get the corresponding CentralRepoAccountType from the database.
|
||||
CentralRepoAccountType crAccountType = CentralRepository.getInstance().getAccountTypeByName(accountTypeStr);
|
||||
|
||||
Optional<CentralRepoAccountType> optCrAccountType = CentralRepository.getInstance().getAccountTypeByName(accountTypeStr);
|
||||
if (!optCrAccountType.isPresent()) {
|
||||
return;
|
||||
}
|
||||
CentralRepoAccountType crAccountType = optCrAccountType.get();
|
||||
|
||||
int corrTypeId = crAccountType.getCorrelationTypeId();
|
||||
CorrelationAttributeInstance.Type corrType = CentralRepository.getInstance().getCorrelationTypeById(corrTypeId);
|
||||
|
||||
|
@ -26,8 +26,10 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.sleuthkit.datamodel.Account;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* This class represents an association between a Persona and an Account.
|
||||
@ -206,10 +208,15 @@ public class PersonaAccount {
|
||||
);
|
||||
|
||||
// create account
|
||||
CentralRepoAccount.CentralRepoAccountType crAccountType = getCRInstance().getAccountTypeByName(rs.getString("type_name"));
|
||||
String accountTypeName = rs.getString("type_name");
|
||||
Optional<CentralRepoAccount.CentralRepoAccountType> optCrAccountType = getCRInstance().getAccountTypeByName(accountTypeName);
|
||||
if (! optCrAccountType.isPresent()) {
|
||||
// The CR account can not be null, so throw an exception
|
||||
throw new CentralRepoException("Account type with name '" + accountTypeName + "' not found in Central Repository");
|
||||
}
|
||||
CentralRepoAccount account = new CentralRepoAccount(
|
||||
rs.getInt("account_id"),
|
||||
crAccountType,
|
||||
optCrAccountType.get(),
|
||||
rs.getString("account_unique_identifier"));
|
||||
|
||||
// create persona account
|
||||
@ -389,10 +396,15 @@ public class PersonaAccount {
|
||||
while (rs.next()) {
|
||||
|
||||
// create account
|
||||
CentralRepoAccount.CentralRepoAccountType crAccountType = getCRInstance().getAccountTypeByName(rs.getString("type_name"));
|
||||
String accountTypeName = rs.getString("type_name");
|
||||
Optional<CentralRepoAccount.CentralRepoAccountType> optCrAccountType = getCRInstance().getAccountTypeByName(accountTypeName);
|
||||
if (! optCrAccountType.isPresent()) {
|
||||
// The CR account can not be null, so throw an exception
|
||||
throw new CentralRepoException("Account type with name '" + accountTypeName + "' not found in Central Repository");
|
||||
}
|
||||
CentralRepoAccount account = new CentralRepoAccount(
|
||||
rs.getInt("account_id"),
|
||||
crAccountType,
|
||||
optCrAccountType.get(),
|
||||
rs.getString("account_unique_identifier"));
|
||||
|
||||
accountsList.add(account);
|
||||
|
@ -37,6 +37,7 @@ import java.time.LocalDate;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@ -78,7 +79,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
|
||||
private static final int CASE_CACHE_TIMEOUT = 5;
|
||||
private static final int DATA_SOURCE_CACHE_TIMEOUT = 5;
|
||||
private static final int ACCOUNTS_CACHE_TIMEOUT = 5;
|
||||
private static final Cache<String, CentralRepoAccountType> accountTypesCache = CacheBuilder.newBuilder().build();
|
||||
private static final Cache<String, Optional<CentralRepoAccountType>> accountTypesCache = CacheBuilder.newBuilder().build();
|
||||
private static final Cache<Pair<CentralRepoAccountType, String>, CentralRepoAccount> accountsCache = CacheBuilder.newBuilder()
|
||||
.expireAfterWrite(ACCOUNTS_CACHE_TIMEOUT, TimeUnit.MINUTES).
|
||||
build();
|
||||
@ -1115,7 +1116,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CentralRepoAccountType getAccountTypeByName(String accountTypeName) throws CentralRepoException {
|
||||
public Optional<CentralRepoAccountType> getAccountTypeByName(String accountTypeName) throws CentralRepoException {
|
||||
try {
|
||||
return accountTypesCache.get(accountTypeName, () -> getCRAccountTypeFromDb(accountTypeName));
|
||||
} catch (CacheLoader.InvalidCacheLoadException | ExecutionException ex) {
|
||||
@ -1155,7 +1156,7 @@ abstract class RdbmsCentralRepo implements CentralRepository {
|
||||
*
|
||||
* @throws CentralRepoException
|
||||
*/
|
||||
private CentralRepoAccountType getCRAccountTypeFromDb(String accountTypeName) throws CentralRepoException {
|
||||
private Optional<CentralRepoAccountType> getCRAccountTypeFromDb(String accountTypeName) throws CentralRepoException {
|
||||
|
||||
String sql = "SELECT * FROM account_types WHERE type_name = ?";
|
||||
try (Connection conn = connect();
|
||||
@ -1166,10 +1167,11 @@ abstract class RdbmsCentralRepo implements CentralRepository {
|
||||
if (resultSet.next()) {
|
||||
Account.Type acctType = new Account.Type(accountTypeName, resultSet.getString("display_name"));
|
||||
CentralRepoAccountType crAccountType = new CentralRepoAccountType(resultSet.getInt("id"), acctType, resultSet.getInt("correlation_type_id"));
|
||||
accountTypesCache.put(accountTypeName, crAccountType);
|
||||
return crAccountType;
|
||||
accountTypesCache.put(accountTypeName, Optional.of(crAccountType));
|
||||
return Optional.of(crAccountType);
|
||||
} else {
|
||||
throw new CentralRepoException("Failed to find entry for account type = " + accountTypeName);
|
||||
accountTypesCache.put(accountTypeName, Optional.empty());
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
|
@ -22,6 +22,8 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
@ -41,7 +43,15 @@ public final class SqliteCentralRepoSettings implements CentralRepoDbConnectivit
|
||||
|
||||
public final static String DEFAULT_DBNAME = "central_repository.db"; // NON-NLS
|
||||
private final static Logger LOGGER = Logger.getLogger(SqliteCentralRepoSettings.class.getName());
|
||||
private final Path userConfigDir = Paths.get(PlatformUtil.getUserDirectory().getAbsolutePath());
|
||||
private final static String DEFAULT_DBDIRECTORY = PlatformUtil.getUserDirectory() + File.separator + "central_repository"; // NON-NLS
|
||||
|
||||
//property names
|
||||
private static final String PROFILE_NAME = "CentralRepository";
|
||||
private static final String DATABASE_NAME = "db.sqlite.dbName"; //NON-NLS
|
||||
private static final String DATABASE_PATH = "db.sqlite.dbDirectory"; //NON-NLS
|
||||
private static final String BULK_THRESHOLD = "db.sqlite.bulkThreshold"; //NON-NLS
|
||||
|
||||
private final static String JDBC_DRIVER = "org.sqlite.JDBC"; // NON-NLS
|
||||
private final static String JDBC_BASE_URI = "jdbc:sqlite:"; // NON-NLS
|
||||
private final static String VALIDATION_QUERY = "SELECT count(*) from sqlite_master"; // NON-NLS
|
||||
@ -56,18 +66,18 @@ public final class SqliteCentralRepoSettings implements CentralRepoDbConnectivit
|
||||
}
|
||||
|
||||
public void loadSettings() {
|
||||
dbName = ModuleSettings.getConfigSetting("CentralRepository", "db.sqlite.dbName"); // NON-NLS
|
||||
dbName = ModuleSettings.getConfigSetting(PROFILE_NAME, DATABASE_NAME); // NON-NLS
|
||||
if (dbName == null || dbName.isEmpty()) {
|
||||
dbName = DEFAULT_DBNAME;
|
||||
}
|
||||
|
||||
dbDirectory = ModuleSettings.getConfigSetting("CentralRepository", "db.sqlite.dbDirectory"); // NON-NLS
|
||||
dbDirectory = readDbPath(); // NON-NLS
|
||||
if (dbDirectory == null || dbDirectory.isEmpty()) {
|
||||
dbDirectory = DEFAULT_DBDIRECTORY;
|
||||
}
|
||||
|
||||
try {
|
||||
String bulkThresholdString = ModuleSettings.getConfigSetting("CentralRepository", "db.sqlite.bulkThreshold"); // NON-NLS
|
||||
String bulkThresholdString = ModuleSettings.getConfigSetting(PROFILE_NAME, BULK_THRESHOLD); // NON-NLS
|
||||
if (bulkThresholdString == null || bulkThresholdString.isEmpty()) {
|
||||
this.bulkThreshold = RdbmsCentralRepo.DEFAULT_BULK_THRESHHOLD;
|
||||
} else {
|
||||
@ -96,9 +106,64 @@ public final class SqliteCentralRepoSettings implements CentralRepoDbConnectivit
|
||||
public void saveSettings() {
|
||||
createDbDirectory();
|
||||
|
||||
ModuleSettings.setConfigSetting("CentralRepository", "db.sqlite.dbName", getDbName()); // NON-NLS
|
||||
ModuleSettings.setConfigSetting("CentralRepository", "db.sqlite.dbDirectory", getDbDirectory()); // NON-NLS
|
||||
ModuleSettings.setConfigSetting("CentralRepository", "db.sqlite.bulkThreshold", Integer.toString(getBulkThreshold())); // NON-NLS
|
||||
ModuleSettings.setConfigSetting(PROFILE_NAME, DATABASE_NAME, getDbName()); // NON-NLS
|
||||
saveDbPath(getDbDirectory()); // NON-NLS
|
||||
ModuleSettings.setConfigSetting(PROFILE_NAME, BULK_THRESHOLD, Integer.toString(getBulkThreshold())); // NON-NLS
|
||||
}
|
||||
|
||||
/**
|
||||
* Save CR database path. If the path is inside user directory (e.g.
|
||||
* "C:\Users\USER_NAME\AppData\Roaming\autopsy"), trim that off and save it
|
||||
* as a relative path (i.e it will not start with a “/” or drive letter). Otherwise,
|
||||
* full path is saved. See JIRA-7348.
|
||||
*
|
||||
* @param fullPath Full path to the SQLite db file.
|
||||
*/
|
||||
private void saveDbPath(String fullPath) {
|
||||
Path relativePath = Paths.get(fullPath);
|
||||
// check if the path is within user directory
|
||||
if (Paths.get(fullPath).startsWith(userConfigDir)) {
|
||||
// relativize the path
|
||||
relativePath = userConfigDir.relativize(relativePath);
|
||||
}
|
||||
// Use properties to persist the logo to use.
|
||||
ModuleSettings.setConfigSetting(PROFILE_NAME, DATABASE_PATH, relativePath.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Read CD database path from preferences file. Reverses the path relativization performed
|
||||
* in saveDbPath(). If the stored path starts with either “/” or drive letter,
|
||||
* it is a full path, and is returned to the caller. Otherwise, append current user
|
||||
* directory to the saved relative path. See JIRA-7348.
|
||||
*
|
||||
* @return Full path to the SQLite CR database file.
|
||||
*/
|
||||
private String readDbPath() {
|
||||
|
||||
String curPath = ModuleSettings.getConfigSetting(PROFILE_NAME, DATABASE_PATH);
|
||||
|
||||
|
||||
//if has been set, validate it's correct, if not set, return null
|
||||
if (curPath != null && !curPath.isEmpty()) {
|
||||
|
||||
// check if the path is an absolute path (starts with either drive letter or "/")
|
||||
Path driveLetterOrNetwork = Paths.get(curPath).getRoot();
|
||||
if (driveLetterOrNetwork != null) {
|
||||
// absolute path
|
||||
return curPath;
|
||||
}
|
||||
|
||||
// Path is a relative path. Reverse path relativization performed in saveDbPath()
|
||||
Path absolutePath = userConfigDir.resolve(curPath);
|
||||
curPath = absolutePath.toString();
|
||||
if (new File(curPath).canRead() == false) {
|
||||
//use default
|
||||
LOGGER.log(Level.INFO, "Path to SQLite Central Repository database is not valid: {0}", curPath); //NON-NLS
|
||||
curPath = null;
|
||||
}
|
||||
}
|
||||
|
||||
return curPath;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -252,9 +317,9 @@ public final class SqliteCentralRepoSettings implements CentralRepoDbConnectivit
|
||||
}
|
||||
|
||||
boolean isChanged() {
|
||||
String dbNameString = ModuleSettings.getConfigSetting("CentralRepository", "db.sqlite.dbName"); // NON-NLS
|
||||
String dbDirectoryString = ModuleSettings.getConfigSetting("CentralRepository", "db.sqlite.dbDirectory"); // NON-NLS
|
||||
String bulkThresholdString = ModuleSettings.getConfigSetting("CentralRepository", "db.sqlite.bulkThreshold"); // NON-NLS
|
||||
String dbNameString = ModuleSettings.getConfigSetting(PROFILE_NAME, DATABASE_NAME); // NON-NLS
|
||||
String dbDirectoryString = readDbPath(); // NON-NLS
|
||||
String bulkThresholdString = ModuleSettings.getConfigSetting(PROFILE_NAME, BULK_THRESHOLD); // NON-NLS
|
||||
|
||||
return !dbName.equals(dbNameString)
|
||||
|| !dbDirectory.equals(dbDirectoryString)
|
||||
|
@ -184,6 +184,7 @@ final public class FiltersPanel extends JPanel {
|
||||
applyFiltersButton.addActionListener(e -> applyFilters());
|
||||
refreshButton.addActionListener(e -> applyFilters());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validate that filters are in a consistent state and will result in some
|
||||
@ -220,13 +221,18 @@ final public class FiltersPanel extends JPanel {
|
||||
}
|
||||
|
||||
void initalizeFilters() {
|
||||
Runnable runnable = new Runnable() {
|
||||
|
||||
applyFiltersButton.setEnabled(false);
|
||||
refreshButton.setEnabled(true);
|
||||
needsRefreshLabel.setText("Loading filters...");
|
||||
needsRefreshLabel.setVisible(true);
|
||||
|
||||
(new Thread(new Runnable(){
|
||||
@Override
|
||||
public void run() {
|
||||
new FilterPanelRefresher(true, true).refresh();
|
||||
}
|
||||
};
|
||||
runnable.run();
|
||||
})).start();
|
||||
}
|
||||
|
||||
private void updateTimeZone() {
|
||||
@ -1152,6 +1158,8 @@ final public class FiltersPanel extends JPanel {
|
||||
if (!isEnabled()) {
|
||||
setEnabled(true);
|
||||
}
|
||||
|
||||
needsRefreshLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.needsRefreshLabel.text")); // NOI18N
|
||||
|
||||
validateFilters();
|
||||
|
||||
|
@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.communications.relationships;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import org.openide.nodes.AbstractNode;
|
||||
@ -75,9 +76,9 @@ final class CorrelationCaseChildNodeFactory extends ChildFactory<CorrelationCase
|
||||
|
||||
accounts.forEach((account) -> {
|
||||
try {
|
||||
CorrelationAttributeInstance.Type correlationType = getCorrelationType(account.getAccountType());
|
||||
if (correlationType != null) {
|
||||
List<CorrelationAttributeInstance> correlationInstances = dbInstance.getArtifactInstancesByTypeValue(correlationType, account.getTypeSpecificID());
|
||||
Optional<CorrelationAttributeInstance.Type> optCorrelationType = getCorrelationType(account.getAccountType());
|
||||
if (optCorrelationType.isPresent()) {
|
||||
List<CorrelationAttributeInstance> correlationInstances = dbInstance.getArtifactInstancesByTypeValue(optCorrelationType.get(), account.getTypeSpecificID());
|
||||
correlationInstances.forEach((correlationInstance) -> {
|
||||
CorrelationCase correlationCase = correlationInstance.getCorrelationCase();
|
||||
uniqueCaseMap.put(correlationCase.getCaseUUID(), correlationCase);
|
||||
@ -103,20 +104,22 @@ final class CorrelationCaseChildNodeFactory extends ChildFactory<CorrelationCase
|
||||
*
|
||||
* @param accountType Account type
|
||||
*
|
||||
* @return CorrelationAttributeInstance.Type for given account or null if
|
||||
* @return CorrelationAttributeInstance.Type for given account or empty if
|
||||
* there is no match
|
||||
*
|
||||
* @throws CentralRepoException
|
||||
*/
|
||||
private CorrelationAttributeInstance.Type getCorrelationType(Account.Type accountType) throws CentralRepoException {
|
||||
private Optional<CorrelationAttributeInstance.Type> getCorrelationType(Account.Type accountType) throws CentralRepoException {
|
||||
|
||||
String accountTypeStr = accountType.getTypeName();
|
||||
if (Account.Type.DEVICE.getTypeName().equalsIgnoreCase(accountTypeStr) == false) {
|
||||
CentralRepoAccount.CentralRepoAccountType crAccountType = CentralRepository.getInstance().getAccountTypeByName(accountTypeStr);
|
||||
int corrTypeId = crAccountType.getCorrelationTypeId();
|
||||
return CentralRepository.getInstance().getCorrelationTypeById(corrTypeId);
|
||||
Optional<CentralRepoAccount.CentralRepoAccountType> optCrAccountType = CentralRepository.getInstance().getAccountTypeByName(accountTypeStr);
|
||||
if (optCrAccountType.isPresent()) {
|
||||
int corrTypeId = optCrAccountType.get().getCorrelationTypeId();
|
||||
return Optional.of(CentralRepository.getInstance().getCorrelationTypeById(corrTypeId));
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -49,6 +49,7 @@ import org.openide.nodes.Children;
|
||||
import org.openide.nodes.Node;
|
||||
import org.openide.nodes.Node.Property;
|
||||
import org.openide.nodes.Node.PropertySet;
|
||||
import org.openide.util.Exceptions;
|
||||
import org.openide.util.Lookup;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.communications.ModifiableProxyLookup;
|
||||
@ -146,14 +147,23 @@ final class MessageViewer extends JPanel implements RelationshipsViewer {
|
||||
|
||||
@Override
|
||||
public void setSelectionInfo(SelectionInfo info) {
|
||||
currentSelectionInfo = info;
|
||||
if(currentSelectionInfo != null && currentSelectionInfo.equals(info)) {
|
||||
try {
|
||||
// Clear the currently selected thread so that clicks can
|
||||
// be registered.
|
||||
rootTablePane.getExplorerManager().setSelectedNodes(new Node[0]);
|
||||
} catch (PropertyVetoException ex) {
|
||||
logger.log(Level.WARNING, "Error clearing the selected node", ex);
|
||||
}
|
||||
} else {
|
||||
currentSelectionInfo = info;
|
||||
rootMessageFactory.refresh(info);
|
||||
}
|
||||
|
||||
currentPanel = rootTablePane;
|
||||
|
||||
CardLayout layout = (CardLayout) this.getLayout();
|
||||
layout.show(this, "threads");
|
||||
|
||||
rootMessageFactory.refresh(info);
|
||||
layout.show(this, "threads");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -192,8 +202,8 @@ final class MessageViewer extends JPanel implements RelationshipsViewer {
|
||||
if (isDescendingFrom(newFocusOwner, rootTablePane)) {
|
||||
proxyLookup.setNewLookups(createLookup(rootTablePane.getExplorerManager(), getActionMap()));
|
||||
} else if (isDescendingFrom(newFocusOwner, this)) {
|
||||
proxyLookup.setNewLookups(createLookup(currentPanel.getExplorerManager(), getActionMap()));
|
||||
}
|
||||
proxyLookup.setNewLookups(createLookup(threadMessagesPanel.getExplorerManager(), getActionMap()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -113,7 +113,7 @@ class MessagesPanel extends javax.swing.JPanel implements Lookup.Provider {
|
||||
outlineViewPanel.setTableColumnsWidth(5, 10, 10, 15, 50, 10);
|
||||
}
|
||||
|
||||
public MessagesPanel(ChildFactory<?> nodeFactory) {
|
||||
MessagesPanel(ChildFactory<?> nodeFactory) {
|
||||
this();
|
||||
setChildFactory(nodeFactory);
|
||||
}
|
||||
@ -122,6 +122,15 @@ class MessagesPanel extends javax.swing.JPanel implements Lookup.Provider {
|
||||
public Lookup getLookup() {
|
||||
return proxyLookup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the explorerManager for the table.
|
||||
*
|
||||
* @return The explorer manager for the table.
|
||||
*/
|
||||
ExplorerManager getExplorerManager() {
|
||||
return outlineViewPanel.getExplorerManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addNotify() {
|
||||
|
@ -21,10 +21,12 @@ package org.sleuthkit.autopsy.communications.relationships;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.SwingWorker;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.Persona;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.PersonaAccount;
|
||||
@ -61,8 +63,10 @@ class SummaryPanelWorker extends SwingWorker<SummaryPanelWorker.SummaryWorkerRes
|
||||
CentralRepoAccount crAccount = null;
|
||||
List<String> stringList = new ArrayList<>();
|
||||
List<AccountFileInstance> accountFileInstanceList = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().getAccountFileInstances(account);
|
||||
for (AccountFileInstance instance : accountFileInstanceList) {
|
||||
stringList.add(instance.getFile().getUniquePath());
|
||||
if (accountFileInstanceList != null) {
|
||||
for (AccountFileInstance instance : accountFileInstanceList) {
|
||||
stringList.add(instance.getFile().getUniquePath());
|
||||
}
|
||||
}
|
||||
|
||||
List<Persona> personaList = new ArrayList<>();
|
||||
@ -74,12 +78,15 @@ class SummaryPanelWorker extends SwingWorker<SummaryPanelWorker.SummaryWorkerRes
|
||||
personaList.add(pAccount.getPersona());
|
||||
}
|
||||
|
||||
try {
|
||||
crAccount = CentralRepository.getInstance().getAccount(CentralRepository.getInstance().getAccountTypeByName(account.getAccountType().getTypeName()), account.getTypeSpecificID());
|
||||
} catch (InvalidAccountIDException unused) {
|
||||
// This was probably caused to a phone number not making
|
||||
// threw the normalization.
|
||||
logger.log(Level.WARNING, String.format("Exception thrown from CR getAccount for account %s (%d)", account.getTypeSpecificID(), account.getAccountID()));
|
||||
Optional<CentralRepoAccount.CentralRepoAccountType> optCrAccountType = CentralRepository.getInstance().getAccountTypeByName(account.getAccountType().getTypeName());
|
||||
if (optCrAccountType.isPresent()) {
|
||||
try {
|
||||
crAccount = CentralRepository.getInstance().getAccount(optCrAccountType.get(), account.getTypeSpecificID());
|
||||
} catch (InvalidAccountIDException unused) {
|
||||
// This was probably caused to a phone number not making
|
||||
// threw the normalization.
|
||||
logger.log(Level.WARNING, String.format("Exception thrown from CR getAccount for account %s (%d)", account.getTypeSpecificID(), account.getAccountID()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -216,9 +216,11 @@ public class SummaryViewer extends javax.swing.JPanel implements RelationshipsVi
|
||||
|
||||
List<String> fileRefList = results.getPaths();
|
||||
|
||||
fileRefList.forEach(value -> {
|
||||
fileRefListModel.addElement(value);
|
||||
});
|
||||
if (fileRefList != null) {
|
||||
fileRefList.forEach(value -> {
|
||||
fileRefListModel.addElement(value);
|
||||
});
|
||||
}
|
||||
|
||||
CardLayout cardLayout = (CardLayout) fileRefPane.getLayout();
|
||||
cardLayout.show(fileRefPane, "listPanelCard");
|
||||
|
@ -61,7 +61,7 @@ import org.jsoup.nodes.Element;
|
||||
* Annotations view of file contents.
|
||||
*/
|
||||
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||
@ServiceProvider(service = DataContentViewer.class, position = 8)
|
||||
@ServiceProvider(service = DataContentViewer.class, position = 9)
|
||||
@Messages({
|
||||
"AnnotationsContentViewer.title=Annotations",
|
||||
"AnnotationsContentViewer.toolTip=Displays tags and comments associated with the selected content.",
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2018-2020 Basis Technology Corp.
|
||||
* Copyright 2018-2021 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -18,6 +18,7 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.contentviewers;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import java.awt.EventQueue;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.image.BufferedImage;
|
||||
@ -32,7 +33,6 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import static java.util.Objects.nonNull;
|
||||
import java.util.SortedSet;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
@ -70,7 +70,6 @@ import javax.swing.SwingWorker;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.controlsfx.control.MaskerPane;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.python.google.common.collect.Lists;
|
||||
import org.sleuthkit.autopsy.actions.GetTagNameAndCommentDialog;
|
||||
import org.sleuthkit.autopsy.actions.GetTagNameAndCommentDialog.TagNameAndComment;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
@ -110,10 +109,6 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Logger logger = Logger.getLogger(MediaViewImagePanel.class.getName());
|
||||
private static final SortedSet<String> supportedMimes = ImageUtils.getSupportedImageMimeTypes();
|
||||
private static final List<String> supportedExtensions = ImageUtils.getSupportedImageExtensions().stream()
|
||||
.map("."::concat) //NOI18N
|
||||
.collect(Collectors.toList());
|
||||
private static final double[] ZOOM_STEPS = {
|
||||
0.0625, 0.125, 0.25, 0.375, 0.5, 0.75,
|
||||
1, 1.5, 2, 2.5, 3, 4, 5, 6, 8, 10};
|
||||
@ -634,30 +629,25 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
|
||||
}
|
||||
|
||||
/**
|
||||
* @return supported mime types
|
||||
* Gets the list of supported MIME types.
|
||||
*
|
||||
* @return A list of the supported MIME types as Strings.
|
||||
*/
|
||||
@Override
|
||||
final public List<String> getSupportedMimeTypes() {
|
||||
return Collections.unmodifiableList(Lists.newArrayList(supportedMimes));
|
||||
return Collections.unmodifiableList(Lists.newArrayList(ImageUtils.getSupportedImageMimeTypes()));
|
||||
}
|
||||
|
||||
/**
|
||||
* returns supported extensions (each starting with .)
|
||||
* Returns supported extensions (each starting with .)
|
||||
*
|
||||
* @return
|
||||
* @return A unmodifiable list of image extensions as Strings.
|
||||
*/
|
||||
@Override
|
||||
final public List<String> getSupportedExtensions() {
|
||||
return getExtensions();
|
||||
}
|
||||
|
||||
/**
|
||||
* returns supported extensions (each starting with .)
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
final public List<String> getExtensions() {
|
||||
return Collections.unmodifiableList(supportedExtensions);
|
||||
return ImageUtils.getSupportedImageExtensions().stream()
|
||||
.map("."::concat) //NOI18N
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -32,6 +32,7 @@ import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.logging.Level;
|
||||
@ -630,11 +631,13 @@ public class ContactArtifactViewer extends javax.swing.JPanel implements Artifac
|
||||
|
||||
// make a list of all unique accounts for this contact
|
||||
if (!account.getAccountType().equals(Account.Type.DEVICE)) {
|
||||
CentralRepoAccount.CentralRepoAccountType crAccountType = CentralRepository.getInstance().getAccountTypeByName(account.getAccountType().getTypeName());
|
||||
CentralRepoAccount crAccount = CentralRepository.getInstance().getAccount(crAccountType, account.getTypeSpecificID());
|
||||
Optional<CentralRepoAccount.CentralRepoAccountType> optCrAccountType = CentralRepository.getInstance().getAccountTypeByName(account.getAccountType().getTypeName());
|
||||
if (optCrAccountType.isPresent()) {
|
||||
CentralRepoAccount crAccount = CentralRepository.getInstance().getAccount(optCrAccountType.get(), account.getTypeSpecificID());
|
||||
|
||||
if (crAccount != null && uniqueAccountsList.contains(crAccount) == false) {
|
||||
uniqueAccountsList.add(crAccount);
|
||||
if (crAccount != null && uniqueAccountsList.contains(crAccount) == false) {
|
||||
uniqueAccountsList.add(crAccount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -48,7 +48,7 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
* usage, if known.
|
||||
*
|
||||
*/
|
||||
@ServiceProvider(service = DataContentViewer.class, position = 7)
|
||||
@ServiceProvider(service = DataContentViewer.class, position = 8)
|
||||
public final class ContextViewer extends javax.swing.JPanel implements DataContentViewer {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
@ -1,3 +1,4 @@
|
||||
OsAccountDataPanel_administrator_title=Administrator
|
||||
OsAccountDataPanel_basic_address=Address
|
||||
OsAccountDataPanel_basic_admin=Administrator
|
||||
OsAccountDataPanel_basic_creationDate=Creation Date
|
||||
@ -5,11 +6,16 @@ OsAccountDataPanel_basic_fullname=Full Name
|
||||
OsAccountDataPanel_basic_login=Login
|
||||
OsAccountDataPanel_basic_title=Basic Properties
|
||||
OsAccountDataPanel_basic_type=Type
|
||||
OsAccountDataPanel_data_accessed_title=Last Login
|
||||
OsAccountDataPanel_host_count_title=Login Count
|
||||
# {0} - hostName
|
||||
OsAccountDataPanel_host_section_title={0} Details
|
||||
OsAccountDataPanel_realm_address=Address
|
||||
OsAccountDataPanel_realm_confidence=Confidence
|
||||
OsAccountDataPanel_realm_name=Name
|
||||
OsAccountDataPanel_realm_scope=Scope
|
||||
OsAccountDataPanel_realm_title=Realm Properties
|
||||
OsAccountDataPanel_realm_unknown=Unknown
|
||||
OsAccountViewer_title=Os Account
|
||||
OsAccountViewer_tooltip=Viewer for OS accounts related to the selected node.
|
||||
OsAccountViewer_title=OS Account
|
||||
OsAccountViewer_tooltip=Viewer for Operating System accounts related to the selected node.
|
||||
TableDataPanel.titleLabel.text=Title
|
||||
|
297
Core/src/org/sleuthkit/autopsy/contentviewers/osaccount/OsAccountDataPanel.java
Executable file → Normal file
297
Core/src/org/sleuthkit/autopsy/contentviewers/osaccount/OsAccountDataPanel.java
Executable file → Normal file
@ -18,6 +18,7 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.contentviewers.osaccount;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Font;
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.GridBagLayout;
|
||||
@ -25,16 +26,30 @@ import java.awt.Insets;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import static java.util.Locale.US;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javax.swing.Box;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.SwingWorker;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.contentviewers.osaccount.SectionData.RowData;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
import org.sleuthkit.datamodel.Host;
|
||||
import org.sleuthkit.datamodel.OsAccount;
|
||||
import org.sleuthkit.datamodel.OsAccount.OsAccountAttribute;
|
||||
import org.sleuthkit.datamodel.OsAccountInstance;
|
||||
import org.sleuthkit.datamodel.OsAccountManager;
|
||||
import org.sleuthkit.datamodel.OsAccountRealm;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
|
||||
/**
|
||||
* Panel for displaying the properties of an OsAccount.
|
||||
@ -42,12 +57,15 @@ import org.sleuthkit.datamodel.OsAccountRealm;
|
||||
public class OsAccountDataPanel extends JPanel {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
final private static Logger logger = Logger.getLogger(OsAccountDataPanel.class.getName());
|
||||
|
||||
private static final int KEY_COLUMN = 0;
|
||||
private static final int VALUE_COLUMN = 1;
|
||||
|
||||
|
||||
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("MMM dd yyyy", US);
|
||||
|
||||
private PanelDataFetcher dataFetcher = null;
|
||||
|
||||
// Panel constructor.
|
||||
OsAccountDataPanel() {
|
||||
initialize();
|
||||
@ -66,21 +84,38 @@ public class OsAccountDataPanel extends JPanel {
|
||||
* @param account OsAccount to display, if null is passed the panel will
|
||||
* appear blank.
|
||||
*/
|
||||
void setOsAccount(OsAccount account) {
|
||||
void setOsAccountId(Long osAccountId) {
|
||||
removeAll();
|
||||
revalidate();
|
||||
|
||||
if (account != null) {
|
||||
List<SectionData> data = new ArrayList<>();
|
||||
data.add(buildBasicProperties(account));
|
||||
if (osAccountId != null) {
|
||||
setLayout(new BorderLayout());
|
||||
add(new JLabel("Loading OsAccount Data..."), BorderLayout.NORTH);
|
||||
|
||||
OsAccountRealm realm = account.getRealm();
|
||||
if (realm != null) {
|
||||
data.add(buildRealmProperties(realm));
|
||||
if (dataFetcher != null && !dataFetcher.isDone()) {
|
||||
dataFetcher.cancel(true);
|
||||
}
|
||||
|
||||
addDataComponents(data);
|
||||
dataFetcher = new PanelDataFetcher(osAccountId);
|
||||
dataFetcher.execute();
|
||||
}
|
||||
}
|
||||
|
||||
void setOsAccount(OsAccount account) {
|
||||
removeAll();
|
||||
revalidate();
|
||||
|
||||
if (account != null) {
|
||||
setLayout(new BorderLayout());
|
||||
add(new JLabel("Loading OsAccount Data..."), BorderLayout.NORTH);
|
||||
|
||||
if (dataFetcher != null && !dataFetcher.isDone()) {
|
||||
dataFetcher.cancel(true);
|
||||
}
|
||||
|
||||
dataFetcher = new PanelDataFetcher(account);
|
||||
dataFetcher.execute();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -122,8 +157,7 @@ public class OsAccountDataPanel extends JPanel {
|
||||
"OsAccountDataPanel_basic_address=Address",
|
||||
"OsAccountDataPanel_basic_admin=Administrator",
|
||||
"OsAccountDataPanel_basic_type=Type",
|
||||
"OsAccountDataPanel_basic_creationDate=Creation Date",
|
||||
})
|
||||
"OsAccountDataPanel_basic_creationDate=Creation Date",})
|
||||
|
||||
/**
|
||||
* Returns the data for the Basic Properties section of the panel.
|
||||
@ -146,13 +180,14 @@ public class OsAccountDataPanel extends JPanel {
|
||||
data.addData(Bundle.OsAccountDataPanel_basic_address(),
|
||||
account.getName() == null || account.getName().isEmpty() ? "" : account.getName());
|
||||
|
||||
data.addData(Bundle.OsAccountDataPanel_basic_type(), account.getOsAccountType().getName());
|
||||
|
||||
data.addData(Bundle.OsAccountDataPanel_basic_type(),
|
||||
account.getOsAccountType().isPresent() ? account.getOsAccountType().get().getName() : "");
|
||||
|
||||
Optional<Long> crTime = account.getCreationTime();
|
||||
if(crTime.isPresent()) {
|
||||
if (crTime.isPresent()) {
|
||||
data.addData(Bundle.OsAccountDataPanel_basic_creationDate(), DATE_FORMAT.format(new Date(crTime.get() * 1000)));
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
data.addData(Bundle.OsAccountDataPanel_basic_creationDate(), "");
|
||||
}
|
||||
|
||||
@ -177,23 +212,54 @@ public class OsAccountDataPanel extends JPanel {
|
||||
private SectionData buildRealmProperties(OsAccountRealm realm) {
|
||||
SectionData data = new SectionData(Bundle.OsAccountDataPanel_realm_title());
|
||||
|
||||
Optional<String> optional = realm.getRealmName();
|
||||
data.addData(Bundle.OsAccountDataPanel_realm_name(),
|
||||
optional.isPresent() ? optional.get() : Bundle.OsAccountDataPanel_realm_unknown());
|
||||
String realmName = realm.getRealmNames().isEmpty() ? Bundle.OsAccountDataPanel_realm_unknown() : realm.getRealmNames().get(0);
|
||||
data.addData(Bundle.OsAccountDataPanel_realm_name(), realmName);
|
||||
|
||||
optional = realm.getRealmAddr();
|
||||
Optional<String> optional = realm.getRealmAddr();
|
||||
data.addData(Bundle.OsAccountDataPanel_realm_address(),
|
||||
optional.isPresent() ? optional.get() : "");
|
||||
|
||||
|
||||
data.addData(Bundle.OsAccountDataPanel_realm_scope(),
|
||||
realm.getScope().getName());
|
||||
|
||||
realm.getScope().getName());
|
||||
|
||||
data.addData(Bundle.OsAccountDataPanel_realm_confidence(),
|
||||
realm.getScopeConfidence().getName());
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@Messages({
|
||||
"# {0} - hostName",
|
||||
"OsAccountDataPanel_host_section_title={0} Details",
|
||||
"OsAccountDataPanel_host_count_title=Login Count",
|
||||
"OsAccountDataPanel_data_accessed_title=Last Login",
|
||||
"OsAccountDataPanel_administrator_title=Administrator"
|
||||
})
|
||||
private SectionData buildHostData(Host host, List<OsAccountAttribute> attributeList) {
|
||||
SectionData data = new SectionData(Bundle.OsAccountDataPanel_host_section_title(host.getName()));
|
||||
for (OsAccountAttribute attribute : attributeList) {
|
||||
String displayName = attribute.getAttributeType().getDisplayName();
|
||||
String value = attribute.getDisplayString();
|
||||
|
||||
if(attribute.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COUNT.getTypeID()) {
|
||||
displayName = Bundle.OsAccountDataPanel_host_count_title();
|
||||
} else if(attribute.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_IS_ADMIN.getTypeID()) {
|
||||
displayName = Bundle.OsAccountDataPanel_administrator_title();
|
||||
if(attribute.getValueInt() == 0) {
|
||||
value = "False";
|
||||
} else {
|
||||
value = "True";
|
||||
}
|
||||
} else if(attribute.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED.getTypeID()) {
|
||||
displayName = Bundle.OsAccountDataPanel_data_accessed_title();
|
||||
}
|
||||
|
||||
data.addData(displayName, value);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a section title to the panel with the given title and location.
|
||||
*
|
||||
@ -214,7 +280,7 @@ public class OsAccountDataPanel extends JPanel {
|
||||
* @param row The row in the layout.
|
||||
*/
|
||||
private void addPropertyName(String key, int row) {
|
||||
JLabel label = new JLabel(key);
|
||||
JLabel label = new JLabel(key + ":");
|
||||
add(label, getPropertyNameContraints(row));
|
||||
}
|
||||
|
||||
@ -291,4 +357,189 @@ public class OsAccountDataPanel extends JPanel {
|
||||
|
||||
return constraints;
|
||||
}
|
||||
|
||||
/**
|
||||
* A SwingWorker to gather the data for the content panel.
|
||||
*/
|
||||
private class PanelDataFetcher extends SwingWorker<WorkerResults, Void> {
|
||||
|
||||
private final Long accountId;
|
||||
private OsAccount account;
|
||||
|
||||
/**
|
||||
* Construct a new worker for the given account.
|
||||
*
|
||||
* @param account
|
||||
*/
|
||||
PanelDataFetcher(Long accountId) {
|
||||
this.accountId = accountId;
|
||||
this.account = null;
|
||||
}
|
||||
|
||||
PanelDataFetcher(OsAccount account) {
|
||||
this.account = account;
|
||||
this.accountId = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected WorkerResults doInBackground() throws Exception {
|
||||
Map<Host, List<OsAccountAttribute>> hostMap = new HashMap<>();
|
||||
Map<Host, DataSource> instanceMap = new HashMap<>();
|
||||
SleuthkitCase skCase = Case.getCurrentCase().getSleuthkitCase();
|
||||
OsAccountManager osAccountManager = skCase.getOsAccountManager();
|
||||
|
||||
if(account == null) {
|
||||
account = osAccountManager.getOsAccountByObjectId(accountId);
|
||||
}
|
||||
|
||||
OsAccountRealm realm = skCase.getOsAccountRealmManager().getRealmByRealmId(account.getRealmId());
|
||||
|
||||
List<Host> hosts = osAccountManager.getHosts(account);
|
||||
List<OsAccountAttribute> attributeList = account.getExtendedOsAccountAttributes();
|
||||
|
||||
if (attributeList != null) {
|
||||
if (hosts != null) {
|
||||
// Organize the attributes by hostId
|
||||
Map<Long, List<OsAccountAttribute>> idMap = new HashMap<>();
|
||||
for (OsAccountAttribute attribute : attributeList) {
|
||||
List<OsAccountAttribute> atList = null;
|
||||
Optional<Long> optionalId = attribute.getHostId();
|
||||
Long key = null;
|
||||
if (optionalId.isPresent()) {
|
||||
key = optionalId.get();
|
||||
}
|
||||
|
||||
atList = idMap.get(key);
|
||||
|
||||
if (atList == null) {
|
||||
atList = new ArrayList<>();
|
||||
idMap.put(key, atList);
|
||||
}
|
||||
|
||||
atList.add(attribute);
|
||||
}
|
||||
|
||||
// Add attribute lists to the hostMap
|
||||
for (Host host : hosts) {
|
||||
List<OsAccountAttribute> atList = idMap.get(host.getHostId());
|
||||
if (atList != null) {
|
||||
hostMap.put(host, atList);
|
||||
}
|
||||
|
||||
}
|
||||
List<OsAccountAttribute> atList = idMap.get(null);
|
||||
if (atList != null) {
|
||||
hostMap.put(null, atList);
|
||||
}
|
||||
|
||||
// Store both the host and the dataSource so that we get
|
||||
// all of the calls to the db done in the thread.
|
||||
for (OsAccountInstance instance : account.getOsAccountInstances()) {
|
||||
instanceMap.put(instance.getDataSource().getHost(), instance.getDataSource());
|
||||
}
|
||||
|
||||
} else {
|
||||
hostMap.put(null, attributeList);
|
||||
}
|
||||
}
|
||||
|
||||
return new WorkerResults(hostMap, instanceMap, realm);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done() {
|
||||
WorkerResults results = null;
|
||||
|
||||
try {
|
||||
if (this.isCancelled()) {
|
||||
return;
|
||||
} else {
|
||||
results = get();
|
||||
}
|
||||
} catch (ExecutionException | InterruptedException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Failed to retrieve data for OsAccount (%d)", account.getId()), ex);
|
||||
}
|
||||
|
||||
if (results != null) {
|
||||
removeAll();
|
||||
setLayout(new GridBagLayout());
|
||||
|
||||
List<SectionData> data = new ArrayList<>();
|
||||
data.add(buildBasicProperties(account));
|
||||
Map<Host, List<OsAccountAttribute>> hostDataMap = results.getAttributeMap();
|
||||
if (hostDataMap != null && !hostDataMap.isEmpty()) {
|
||||
hostDataMap.forEach((K, V) -> data.add(buildHostData(K, V)));
|
||||
}
|
||||
|
||||
OsAccountRealm realm = results.getRealm();
|
||||
if (realm != null) {
|
||||
data.add(buildRealmProperties(realm));
|
||||
}
|
||||
|
||||
// Removing the instance section for now. Leaving code here for
|
||||
// future use.
|
||||
// Map<Host, DataSource> instanceMap = results.getDataSourceMap();
|
||||
// if (!instanceMap.isEmpty()) {
|
||||
// SectionData instanceSection = new SectionData("Instances");
|
||||
// instanceMap.forEach((K, V) -> instanceSection.addData(K.getName(), V.getName()));
|
||||
//
|
||||
// data.add(instanceSection);
|
||||
// }
|
||||
|
||||
addDataComponents(data);
|
||||
|
||||
revalidate();
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class for PanelDataFetcher that wraps the returned data needed for
|
||||
* the panel.
|
||||
*/
|
||||
private final class WorkerResults {
|
||||
|
||||
private final Map<Host, List<OsAccountAttribute>> attributeMap;
|
||||
private final Map<Host, DataSource> instanceMap;
|
||||
private final OsAccountRealm realm;
|
||||
|
||||
/**
|
||||
* Construct a new WorkerResult object.
|
||||
*
|
||||
* @param attributeMap Maps the OsAccountAttributes to the host they
|
||||
* belong with.
|
||||
* @param instanceMap A map of data to display OsAccount instance
|
||||
* information.
|
||||
*/
|
||||
WorkerResults(Map<Host, List<OsAccountAttribute>> attributeMap, Map<Host, DataSource> instanceMap, OsAccountRealm realm) {
|
||||
this.attributeMap = attributeMap;
|
||||
this.instanceMap = instanceMap;
|
||||
this.realm = realm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a map of OsAccountAttributes that belong to a specific Host.
|
||||
* There maybe a null key in the map which represents properties that
|
||||
* are not host specific.
|
||||
*
|
||||
* @return OsAccountAttribute map.
|
||||
*/
|
||||
Map<Host, List<OsAccountAttribute>> getAttributeMap() {
|
||||
return attributeMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* A map of the instance data for the OsAccount.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Map<Host, DataSource> getDataSourceMap() {
|
||||
return instanceMap;
|
||||
}
|
||||
|
||||
OsAccountRealm getRealm() {
|
||||
return realm;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,14 +11,20 @@
|
||||
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
||||
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,44,0,0,1,-112"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JScrollPane" name="mainScrollPane">
|
||||
<Properties>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[200, 0]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
|
||||
<BorderConstraints direction="Center"/>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="-1" gridY="-1" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="1.0" weightY="1.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
|
@ -18,7 +18,10 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.contentviewers.osaccount;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Component;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.Optional;
|
||||
import java.util.logging.Level;
|
||||
import org.openide.nodes.Node;
|
||||
@ -34,7 +37,7 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
/**
|
||||
* DataContentViewer for OsAccounts.
|
||||
*/
|
||||
@ServiceProvider(service = DataContentViewer.class, position = 12)
|
||||
@ServiceProvider(service = DataContentViewer.class, position = 7)
|
||||
public class OsAccountViewer extends javax.swing.JPanel implements DataContentViewer {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
@ -53,26 +56,29 @@ public class OsAccountViewer extends javax.swing.JPanel implements DataContentVi
|
||||
|
||||
@Override
|
||||
public void setNode(Node node) {
|
||||
OsAccount osAccount = null;
|
||||
Long osAccountId = null;
|
||||
try {
|
||||
osAccount = node.getLookup().lookup(OsAccount.class);
|
||||
if (osAccount == null) {
|
||||
Optional<OsAccount> optional;
|
||||
AbstractFile file = node.getLookup().lookup(AbstractFile.class);
|
||||
if (file != null) {
|
||||
optional = file.getOsAccount();
|
||||
if (optional.isPresent()) {
|
||||
osAccount = optional.get();
|
||||
}
|
||||
OsAccount osAccount = node.getLookup().lookup(OsAccount.class);
|
||||
if (osAccount != null) {
|
||||
dataPanel.setOsAccount(osAccount);
|
||||
return;
|
||||
}
|
||||
|
||||
Optional<Long> optional;
|
||||
AbstractFile file = node.getLookup().lookup(AbstractFile.class);
|
||||
if (file != null) {
|
||||
optional = file.getOsAccountObjectId();
|
||||
if (optional.isPresent()) {
|
||||
osAccountId = optional.get();
|
||||
}
|
||||
}
|
||||
|
||||
if (osAccount == null) {
|
||||
if (osAccountId == null) {
|
||||
DataArtifact dataArtifact = node.getLookup().lookup(DataArtifact.class);
|
||||
if (dataArtifact != null) {
|
||||
Optional<OsAccount> optional = dataArtifact.getOsAccount();
|
||||
optional = dataArtifact.getOsAccountObjectId();
|
||||
if (optional.isPresent()) {
|
||||
osAccount = optional.get();
|
||||
osAccountId = optional.get();
|
||||
}
|
||||
}
|
||||
|
||||
@ -81,13 +87,13 @@ public class OsAccountViewer extends javax.swing.JPanel implements DataContentVi
|
||||
logger.log(Level.SEVERE, String.format("Failed to get OsAccount for node %s", node.getDisplayName()), ex);
|
||||
}
|
||||
|
||||
if (osAccount != null) {
|
||||
dataPanel.setOsAccount(osAccount);
|
||||
if (osAccountId != null) {
|
||||
dataPanel.setOsAccountId(osAccountId);
|
||||
}
|
||||
}
|
||||
|
||||
@Messages({
|
||||
"OsAccountViewer_title=Os Account"
|
||||
"OsAccountViewer_title=OS Account"
|
||||
})
|
||||
@Override
|
||||
public String getTitle() {
|
||||
@ -95,7 +101,7 @@ public class OsAccountViewer extends javax.swing.JPanel implements DataContentVi
|
||||
}
|
||||
|
||||
@Messages({
|
||||
"OsAccountViewer_tooltip=Viewer for OS accounts related to the selected node."
|
||||
"OsAccountViewer_tooltip=Viewer for Operating System accounts related to the selected node."
|
||||
})
|
||||
@Override
|
||||
public String getToolTip() {
|
||||
@ -125,8 +131,8 @@ public class OsAccountViewer extends javax.swing.JPanel implements DataContentVi
|
||||
|
||||
try {
|
||||
return osAccount != null
|
||||
|| (file != null && file.getOsAccount().isPresent())
|
||||
|| (dataArtifact != null && dataArtifact.getOsAccount().isPresent());
|
||||
|| (file != null && file.getOsAccountObjectId().isPresent())
|
||||
|| (dataArtifact != null && dataArtifact.getOsAccountObjectId().isPresent());
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Failed to determine if node %s is Supported for OsAccountViewer", node.getDisplayName()), ex);
|
||||
return false;
|
||||
@ -150,8 +156,14 @@ public class OsAccountViewer extends javax.swing.JPanel implements DataContentVi
|
||||
|
||||
mainScrollPane = new javax.swing.JScrollPane();
|
||||
|
||||
setLayout(new java.awt.BorderLayout());
|
||||
add(mainScrollPane, java.awt.BorderLayout.CENTER);
|
||||
setLayout(new java.awt.GridBagLayout());
|
||||
|
||||
mainScrollPane.setPreferredSize(new java.awt.Dimension(200, 0));
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
|
||||
gridBagConstraints.weightx = 1.0;
|
||||
gridBagConstraints.weighty = 1.0;
|
||||
add(mainScrollPane, gridBagConstraints);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
|
||||
|
@ -51,10 +51,10 @@ final class SectionData implements Iterable<SectionData.RowData<String, String>>
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new property name\property value pair.
|
||||
* Add a new property name/property value pair.
|
||||
*
|
||||
* @param key The property display name.
|
||||
* @param value The property value.
|
||||
* @param properytName The property display name.
|
||||
* @param propertyValue The property value.
|
||||
*/
|
||||
void addData(String properytName, String propertyValue) {
|
||||
data.add(new RowData<>(properytName, propertyValue));
|
||||
|
@ -27,3 +27,7 @@ ServicesMonitor.remoteKeywordSearch.displayName.text=Multi-user keyword search s
|
||||
ServicesMonitor.messaging.displayName.text=Messaging service
|
||||
ServicesMonitor.databaseConnectionInfo.error.msg=Error accessing case database connection info
|
||||
ServicesMonitor.messagingService.connErr.text=Error accessing messaging service connection info
|
||||
Actions/Case=Case
|
||||
Menu/Case=Case
|
||||
Toolbars/Case=Case
|
||||
Menu/Case/OpenRecentCase=Open Recent Case
|
@ -31,3 +31,7 @@ ServicesMonitor.remoteKeywordSearch.displayName.text=Multi-user keyword search s
|
||||
ServicesMonitor.messaging.displayName.text=Messaging service
|
||||
ServicesMonitor.databaseConnectionInfo.error.msg=Error accessing case database connection info
|
||||
ServicesMonitor.messagingService.connErr.text=Error accessing messaging service connection info
|
||||
Actions/Case=Case
|
||||
Menu/Case=Case
|
||||
Toolbars/Case=Case
|
||||
Menu/Case/OpenRecentCase=Open Recent Case
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-2018 Basis Technology Corp.
|
||||
* Copyright 2011-2021 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -50,6 +50,7 @@ import org.sleuthkit.autopsy.coreutils.ModuleSettings;
|
||||
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
|
||||
import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector;
|
||||
import org.sleuthkit.autopsy.python.JythonModuleLoader;
|
||||
import org.sleuthkit.autopsy.texttranslation.TextTranslationService;
|
||||
|
||||
/**
|
||||
* Wrapper over Installers in packages in Core module. This is the main
|
||||
@ -369,6 +370,7 @@ public class Installer extends ModuleInstall {
|
||||
}
|
||||
logger.log(Level.INFO, "Autopsy Core restore completed"); //NON-NLS
|
||||
preloadJython();
|
||||
preloadTranslationServices();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -376,7 +378,7 @@ public class Installer extends ModuleInstall {
|
||||
* because we encountered issues related to file locking when initialization
|
||||
* was performed closer to where the bindings are used. See JIRA-6528.
|
||||
*/
|
||||
private void initializeSevenZip() {
|
||||
private static void initializeSevenZip() {
|
||||
try {
|
||||
SevenZip.initSevenZipFromPlatformJAR();
|
||||
logger.log(Level.INFO, "7zip-java bindings loaded"); //NON-NLS
|
||||
@ -388,7 +390,7 @@ public class Installer extends ModuleInstall {
|
||||
/**
|
||||
* Runs an initial load of the Jython modules to speed up subsequent loads.
|
||||
*/
|
||||
private void preloadJython() {
|
||||
private static void preloadJython() {
|
||||
Runnable loader = () -> {
|
||||
try {
|
||||
JythonModuleLoader.getIngestModuleFactories();
|
||||
@ -402,6 +404,22 @@ public class Installer extends ModuleInstall {
|
||||
};
|
||||
new Thread(loader).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs an initial load of the translation services to speed up subsequent loads.
|
||||
*/
|
||||
private static void preloadTranslationServices() {
|
||||
Runnable loader = () -> {
|
||||
try {
|
||||
TextTranslationService.getInstance();
|
||||
} catch (Exception ex) {
|
||||
// This is a firewall exception to ensure that any possible exception caused
|
||||
// by this initial load of the translation modules are caught and logged.
|
||||
logger.log(Level.SEVERE, "There was an error while doing an initial load of translation services.", ex);
|
||||
}
|
||||
};
|
||||
new Thread(loader).start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate() throws IllegalStateException {
|
||||
|
@ -18,12 +18,14 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.core;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Paths;
|
||||
import org.sleuthkit.autopsy.coreutils.TextConverter;
|
||||
import java.util.prefs.BackingStoreException;
|
||||
import org.sleuthkit.autopsy.events.MessageServiceConnectionInfo;
|
||||
import java.util.prefs.PreferenceChangeListener;
|
||||
import java.util.prefs.Preferences;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.openide.util.NbPreferences;
|
||||
import org.python.icu.util.TimeZone;
|
||||
import org.sleuthkit.autopsy.machinesettings.UserMachinePreferences;
|
||||
@ -93,7 +95,8 @@ public final class UserPreferences {
|
||||
private static final String GEO_OSM_SERVER_ADDRESS = "GeolocationOsmServerAddress";
|
||||
private static final String GEO_MBTILES_FILE_PATH = "GeolcoationMBTilesFilePath";
|
||||
private static final String HEALTH_MONITOR_REPORT_PATH = "HealthMonitorReportPath";
|
||||
|
||||
private static final String TEMP_FOLDER = "Temp";
|
||||
|
||||
// Prevent instantiation.
|
||||
private UserPreferences() {
|
||||
}
|
||||
@ -348,27 +351,27 @@ public final class UserPreferences {
|
||||
public static void setIndexingServerPort(int port) {
|
||||
preferences.putInt(SOLR8_SERVER_PORT, port);
|
||||
}
|
||||
|
||||
|
||||
public static String getSolr4ServerHost() {
|
||||
return preferences.get(SOLR4_SERVER_HOST, "");
|
||||
}
|
||||
|
||||
public static void setSolr4ServerHost(String hostName) {
|
||||
preferences.put(SOLR4_SERVER_HOST, hostName);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static String getSolr4ServerPort() {
|
||||
return preferences.get(SOLR4_SERVER_PORT, "");
|
||||
}
|
||||
|
||||
public static void setSolr4ServerPort(String port) {
|
||||
preferences.put(SOLR4_SERVER_PORT, port);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static String getZkServerHost() {
|
||||
return preferences.get(ZK_SERVER_HOST, "");
|
||||
}
|
||||
|
||||
|
||||
public static void setZkServerHost(String hostName) {
|
||||
preferences.put(ZK_SERVER_HOST, hostName);
|
||||
}
|
||||
@ -380,7 +383,7 @@ public final class UserPreferences {
|
||||
public static void setZkServerPort(String port) {
|
||||
preferences.put(ZK_SERVER_PORT, port);
|
||||
}
|
||||
|
||||
|
||||
public static void setTextTranslatorName(String textTranslatorName) {
|
||||
preferences.put(TEXT_TRANSLATOR_NAME, textTranslatorName);
|
||||
}
|
||||
@ -388,14 +391,14 @@ public final class UserPreferences {
|
||||
public static String getTextTranslatorName() {
|
||||
return preferences.get(TEXT_TRANSLATOR_NAME, null);
|
||||
}
|
||||
|
||||
|
||||
public static void setUseOcrInTranslation(boolean enableOcr) {
|
||||
preferences.putBoolean(OCR_TRANSLATION_ENABLED, enableOcr);
|
||||
}
|
||||
|
||||
public static boolean getUseOcrInTranslation() {
|
||||
return preferences.getBoolean(OCR_TRANSLATION_ENABLED, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Persists message service connection info.
|
||||
@ -536,10 +539,11 @@ public final class UserPreferences {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the maximum JVM heap size (in MB) for the embedded Solr server. The returned value
|
||||
* depends on the platform (64bit vs 32bit).
|
||||
* Get the maximum JVM heap size (in MB) for the embedded Solr server. The
|
||||
* returned value depends on the platform (64bit vs 32bit).
|
||||
*
|
||||
* @return Saved value or default (2 GB for 64bit platforms, 512MB for 32bit)
|
||||
* @return Saved value or default (2 GB for 64bit platforms, 512MB for
|
||||
* 32bit)
|
||||
*/
|
||||
public static int getMaxSolrVMSize() {
|
||||
if (PlatformUtil.is64BitJVM()) {
|
||||
@ -594,20 +598,21 @@ public final class UserPreferences {
|
||||
public static String getExternalHexEditorPath() {
|
||||
return preferences.get(EXTERNAL_HEX_EDITOR_PATH, Paths.get("C:", "Program Files", "HxD", "HxD.exe").toString());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the geolocation tile server option.
|
||||
*
|
||||
* @param option
|
||||
*
|
||||
* @param option
|
||||
*/
|
||||
public static void setGeolocationTileOption(int option) {
|
||||
preferences.putInt(GEO_TILE_OPTION, option);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the Geolocation tile option. If not found, the value will
|
||||
* Retrieves the Geolocation tile option. If not found, the value will
|
||||
* default to 0.
|
||||
* @return
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static int getGeolocationtTileOption() {
|
||||
return preferences.getInt(GEO_TILE_OPTION, 0);
|
||||
@ -615,8 +620,8 @@ public final class UserPreferences {
|
||||
|
||||
/**
|
||||
* Sets the path to the OSM tile zip file.
|
||||
*
|
||||
* @param absolutePath
|
||||
*
|
||||
* @param absolutePath
|
||||
*/
|
||||
public static void setGeolocationOsmZipPath(String absolutePath) {
|
||||
preferences.put(GEO_OSM_TILE_ZIP_PATH, absolutePath);
|
||||
@ -625,7 +630,7 @@ public final class UserPreferences {
|
||||
/**
|
||||
* Retrieves the path for the OSM tile zip file or returns empty string if
|
||||
* none was found.
|
||||
*
|
||||
*
|
||||
* @return Path to zip file
|
||||
*/
|
||||
public static String getGeolocationOsmZipPath() {
|
||||
@ -633,9 +638,10 @@ public final class UserPreferences {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the address of geolocation window user defined OSM server data source.
|
||||
*
|
||||
* @param address
|
||||
* Sets the address of geolocation window user defined OSM server data
|
||||
* source.
|
||||
*
|
||||
* @param address
|
||||
*/
|
||||
public static void setGeolocationOsmServerAddress(String address) {
|
||||
preferences.put(GEO_OSM_SERVER_ADDRESS, address);
|
||||
@ -643,41 +649,72 @@ public final class UserPreferences {
|
||||
|
||||
/**
|
||||
* Retrieves the address to the OSM server or null if one was not found.
|
||||
*
|
||||
*
|
||||
* @return Address of OSM server
|
||||
*/
|
||||
public static String getGeolocationOsmServerAddress() {
|
||||
return preferences.get(GEO_OSM_SERVER_ADDRESS, "");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the path for Geolocation MBTiles data source file.
|
||||
*
|
||||
* @param absolutePath
|
||||
*
|
||||
* @param absolutePath
|
||||
*/
|
||||
public static void setGeolocationMBTilesFilePath(String absolutePath) {
|
||||
preferences.put(GEO_MBTILES_FILE_PATH, absolutePath);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the path for the Geolocation MBTiles data source file.
|
||||
*
|
||||
* @return Absolute path to MBTiles file or empty string if none was found.
|
||||
*
|
||||
* @return Absolute path to MBTiles file or empty string if none was found.
|
||||
*/
|
||||
public static String getGeolocationMBTilesFilePath() {
|
||||
return preferences.get(GEO_MBTILES_FILE_PATH, "");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the root application temp directory.
|
||||
*
|
||||
* @return A subdirectory of java.io.tmpdir.
|
||||
*/
|
||||
private static File getSystemTempDirFile() {
|
||||
return Paths.get(System.getProperty("java.io.tmpdir"), getAppName(), TEMP_FOLDER).toFile();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the application temp directory and ensures the directory
|
||||
* exists.
|
||||
*
|
||||
* @return The absolute path to the application temp directory.
|
||||
*/
|
||||
public static String getAppTempDirectory() {
|
||||
return Paths.get(UserMachinePreferences.getBaseTempDirectory(), getAppName())
|
||||
.toAbsolutePath().toString();
|
||||
// NOTE: If this code changes, Case.getTempDirectory() should likely be checked
|
||||
// as well. See JIRA 7505 for more information.
|
||||
File appTempDir = null;
|
||||
switch (UserMachinePreferences.getTempDirChoice()) {
|
||||
case CUSTOM:
|
||||
String customDirectory = UserMachinePreferences.getCustomTempDirectory();
|
||||
appTempDir = (StringUtils.isBlank(customDirectory))
|
||||
? null
|
||||
: Paths.get(customDirectory, getAppName(), TEMP_FOLDER).toFile();
|
||||
break;
|
||||
case SYSTEM:
|
||||
default:
|
||||
// at this level, if the case directory is specified for a temp
|
||||
// directory, return the system temp directory instead.
|
||||
appTempDir = getSystemTempDirFile();
|
||||
break;
|
||||
}
|
||||
|
||||
appTempDir = appTempDir == null ? getSystemTempDirFile() : appTempDir;
|
||||
|
||||
if (!appTempDir.exists()) {
|
||||
appTempDir.mkdirs();
|
||||
}
|
||||
|
||||
return appTempDir.getAbsolutePath();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the last used health monitor report path.
|
||||
*
|
||||
@ -690,9 +727,10 @@ public final class UserPreferences {
|
||||
/**
|
||||
* Gets the last used health monitor report path.
|
||||
*
|
||||
* @return Last used health monitor report path. Empty string if no value has been recorded.
|
||||
* @return Last used health monitor report path. Empty string if no value
|
||||
* has been recorded.
|
||||
*/
|
||||
public static String getHealthMonitorReportPath() {
|
||||
return preferences.get(HEALTH_MONITOR_REPORT_PATH, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,6 +40,7 @@
|
||||
====================================================== -->
|
||||
<folder name="Actions">
|
||||
<folder name="Case">
|
||||
<attr name="SystemFileSystem.localizingBundle" stringvalue="org.sleuthkit.autopsy.core.Bundle"/>
|
||||
<file name="org-sleuthkit-autopsy-casemodule-AddImageAction.instance"/>
|
||||
<file name="org-sleuthkit-autopsy-casemodule-CaseCloseAction.instance"/>
|
||||
<file name="org-sleuthkit-autopsy-casemodule-CaseNewAction.instance">
|
||||
@ -140,13 +141,14 @@
|
||||
<file name="Edit_hidden"/>
|
||||
<file name="File_hidden"/>
|
||||
<folder name="Case">
|
||||
<attr name="SystemFileSystem.localizingBundle" stringvalue="org.sleuthkit.autopsy.core.Bundle"/>
|
||||
<file name="org-sleuthkit-autopsy-casemodule-CaseNewAction.shadow">
|
||||
<attr name="originalFile" stringvalue="Actions/Case/org-sleuthkit-autopsy-casemodule-CaseNewAction.instance"/>
|
||||
<attr name="position" intvalue="100"/>
|
||||
</file>
|
||||
<folder name="Open Recent Case">
|
||||
<folder name="OpenRecentCase">
|
||||
<attr name="SystemFileSystem.localizingBundle" stringvalue="org.sleuthkit.autopsy.core.Bundle"/>
|
||||
<attr name="position" intvalue="101"/>
|
||||
<attr name="SystemFileSystem.localizingBundle" stringvalue="org.sleuthkit.autopsy.casemodule.Bundle"/>
|
||||
<file name="org-sleuthkit-autopsy-casemodule-RecentCasesAction.shadow">
|
||||
<attr name="originalFile" stringvalue="Actions/Case/org-sleuthkit-autopsy-casemodule-RecentCases.instance"/>
|
||||
</file>
|
||||
@ -248,18 +250,10 @@
|
||||
<file name="org-netbeans-core-windows-actions-ResetWindowsAction.shadow_hidden"/>
|
||||
<file name="testAction.shadow_hidden"/>
|
||||
<file name="org-netbeans-core-io-ui-IOWindowAction.shadow_hidden"/>
|
||||
<file name="org-sleuthkit-autopsy-menuactions-DataResultMenu-separatoBefore.instance">
|
||||
<attr name="instanceClass" stringvalue="javax.swing.JSeparator"/>
|
||||
<attr name="position" intvalue="75"/>
|
||||
</file>
|
||||
<file name="org-sleuthkit-autopsy-menuactions-DataResultMenu.instance">
|
||||
<attr name="originalFile" stringvalue="Actions/Tools/org-sleuthkit-autopsy-menuactions-DataResultMenu.instance"/>
|
||||
<attr name="position" intvalue="100"/>
|
||||
</file>
|
||||
<file name="org-sleuthkit-autopsy-menuactions-DataResultMenu-separatoAfter.instance">
|
||||
<attr name="instanceClass" stringvalue="javax.swing.JSeparator"/>
|
||||
<attr name="position" intvalue="125"/>
|
||||
</file>
|
||||
<file name="org-sleuthkit-autopsy-menuactions-DataContentMenu.instance">
|
||||
<attr name="originalFile" stringvalue="Actions/Tools/org-sleuthkit-autopsy-menuactions-DataContentMenu.instance"/>
|
||||
<attr name="position" intvalue="150"/>
|
||||
@ -281,7 +275,6 @@
|
||||
<folder name="Help">
|
||||
<file name="org-netbeans-core-actions-AboutAction.shadow_hidden"/>
|
||||
<file name="org-netbeans-modules-autoupdate-ui-actions-CheckForUpdatesAction.shadow_hidden"/>
|
||||
<attr name="master-help.xml/org-sleuthkit-autopsy-corecomponents-CustomAboutAction.shadow" boolvalue="true"/>
|
||||
</folder>
|
||||
</folder>
|
||||
|
||||
@ -378,6 +371,7 @@
|
||||
<file name="UndoRedo_hidden"/>
|
||||
<file name="File_hidden"/>
|
||||
<folder name="Case">
|
||||
<attr name="SystemFileSystem.localizingBundle" stringvalue="org.sleuthkit.autopsy.core.Bundle"/>
|
||||
<attr name="position" intvalue="90"/>
|
||||
<!--<file name="org-sleuthkit-autopsy-casemodule-AddImageAction.instance">
|
||||
<attr name="delegate" newvalue="org.sleuthkit.autopsy.casemodule.AddImageAction"/>
|
||||
|
@ -27,6 +27,7 @@ import org.openide.windows.TopComponent;
|
||||
import org.openide.windows.WindowManager;
|
||||
import org.sleuthkit.autopsy.corecomponents.DataContentTopComponent;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.directorytree.DirectoryTreeTopComponent;
|
||||
|
||||
/**
|
||||
* Responsible for opening and closing the core windows when a case is opened
|
||||
@ -49,7 +50,8 @@ final public class CoreComponentControl {
|
||||
* ({@link DataExplorer}, {@link DataResult}, and {@link DataContent})
|
||||
*/
|
||||
public static void openCoreWindows() {
|
||||
// TODO: there has to be a better way to do this.
|
||||
// preload UI components (JIRA-7345). This only takes place the first time Autopsy opens a case.
|
||||
DirectoryTreeTopComponent dtc = DirectoryTreeTopComponent.findInstance();
|
||||
|
||||
// find the data explorer top components
|
||||
Collection<? extends DataExplorer> dataExplorers = Lookup.getDefault().lookupAll(DataExplorer.class);
|
||||
|
@ -24,15 +24,14 @@ import org.openide.DialogDescriptor;
|
||||
import org.openide.DialogDisplayer;
|
||||
import org.openide.awt.ActionID;
|
||||
import org.openide.awt.ActionReference;
|
||||
import org.openide.awt.ActionRegistration;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
|
||||
/**
|
||||
* Action to open custom implementation of the "About" window from the Help
|
||||
* menu.
|
||||
*/
|
||||
@ActionID(id = "org.sleuthkit.autopsy.corecomponents.AboutWindowAction", category = "Help")
|
||||
@ActionRegistration(displayName = "#CTL_CustomAboutAction", iconInMenu = true, lazy = false)
|
||||
@ActionReference(path = "Menu/Help", position = 3000, separatorBefore = 2999)
|
||||
public class AboutWindowAction extends AboutAction {
|
||||
|
||||
|
@ -8,6 +8,8 @@
|
||||
</Component>
|
||||
<Component class="javax.swing.ButtonGroup" name="logoSourceButtonGroup">
|
||||
</Component>
|
||||
<Component class="javax.swing.ButtonGroup" name="tempDirChoiceGroup">
|
||||
</Component>
|
||||
</NonVisualComponents>
|
||||
<AuxValues>
|
||||
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
|
||||
@ -24,12 +26,12 @@
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="jScrollPane1" alignment="0" pref="648" max="32767" attributes="0"/>
|
||||
<Component id="jScrollPane1" alignment="0" pref="860" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="jScrollPane1" alignment="0" pref="382" max="32767" attributes="0"/>
|
||||
<Component id="jScrollPane1" alignment="0" pref="620" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
@ -242,7 +244,7 @@
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace min="-2" pref="23" max="-2" attributes="0"/>
|
||||
<Component id="memFieldValidationLabel" min="-2" pref="478" max="-2" attributes="0"/>
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
<EmptySpace pref="12" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace min="-2" pref="18" max="-2" attributes="0"/>
|
||||
@ -253,7 +255,24 @@
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
<Component id="restartNecessaryWarning" alignment="0" min="-2" pref="615" max="-2" attributes="0"/>
|
||||
<Group type="102" attributes="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="restartNecessaryWarning" alignment="0" min="-2" pref="615" max="-2" attributes="0"/>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="heapFileLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="heapFieldValidationLabel" min="-2" pref="478" max="-2" attributes="0"/>
|
||||
<Group type="102" attributes="0">
|
||||
<Component id="heapDumpFileField" min="-2" pref="415" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="heapDumpBrowseButton" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
@ -291,7 +310,15 @@
|
||||
<Component id="maxLogFileCount" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="logFileCount" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="heapFileLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="heapDumpFileField" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="heapDumpBrowseButton" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="heapFieldValidationLabel" min="-2" pref="16" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="restartNecessaryWarning" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
@ -412,13 +439,47 @@
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="heapFileLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.heapFileLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JTextField" name="heapDumpFileField">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.heapDumpFileField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="heapDumpBrowseButton">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.heapDumpBrowseButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="heapDumpBrowseButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="heapFieldValidationLabel">
|
||||
<Properties>
|
||||
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
|
||||
<Color blue="0" green="0" red="ff" type="rgb"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.heapFieldValidationLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Container class="javax.swing.JPanel" name="tempDirectoryPanel">
|
||||
<Properties>
|
||||
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
|
||||
<Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo">
|
||||
<TitledBorder title="Temp Directory">
|
||||
<TitledBorder title="Root Temp Directory">
|
||||
<ResourceString PropertyName="titleX" bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.tempDirectoryPanel.border.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</TitledBorder>
|
||||
</Border>
|
||||
@ -444,37 +505,53 @@
|
||||
<Group type="102" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="tempDirectoryWarningLabel" min="-2" pref="615" max="-2" attributes="0"/>
|
||||
<Group type="102" attributes="0">
|
||||
<Component id="tempDirectoryField" min="-2" pref="367" max="-2" attributes="0"/>
|
||||
<Component id="tempLocalRadio" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="tempCaseRadio" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="tempDirectoryWarningLabel" alignment="0" min="-2" pref="615" max="-2" attributes="0"/>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="tempCustomRadio" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="tempDirectoryBrowseButton" min="-2" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="tempOnCustomNoPath" min="-2" max="-2" attributes="0"/>
|
||||
<Group type="102" attributes="0">
|
||||
<Component id="tempCustomField" min="-2" pref="459" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="tempDirectoryBrowseButton" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
|
||||
<EmptySpace pref="164" 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="tempLocalRadio" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="tempCaseRadio" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="tempDirectoryField" alignment="3" max="32767" attributes="0"/>
|
||||
<Component id="tempCustomRadio" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="tempCustomField" alignment="3" max="32767" attributes="0"/>
|
||||
<Component id="tempDirectoryBrowseButton" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace type="separate" max="-2" attributes="0"/>
|
||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||
<Component id="tempOnCustomNoPath" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace type="unrelated" max="32767" attributes="0"/>
|
||||
<Component id="tempDirectoryWarningLabel" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="14" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JTextField" name="tempDirectoryField">
|
||||
<Component class="javax.swing.JTextField" name="tempCustomField">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.tempDirectoryField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.tempCustomField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
@ -498,6 +575,55 @@
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JRadioButton" name="tempLocalRadio">
|
||||
<Properties>
|
||||
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
|
||||
<ComponentRef name="tempDirChoiceGroup"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.tempLocalRadio.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="tempLocalRadioActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JRadioButton" name="tempCaseRadio">
|
||||
<Properties>
|
||||
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
|
||||
<ComponentRef name="tempDirChoiceGroup"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.tempCaseRadio.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="tempCaseRadioActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JRadioButton" name="tempCustomRadio">
|
||||
<Properties>
|
||||
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
|
||||
<ComponentRef name="tempDirChoiceGroup"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.tempCustomRadio.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="tempCustomRadioActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="tempOnCustomNoPath">
|
||||
<Properties>
|
||||
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||
<Connection code="java.awt.Color.RED" type="code"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.tempOnCustomNoPath.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Container class="javax.swing.JPanel" name="rdpPanel">
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-2019 Basis Technology Corp.
|
||||
* Copyright 2011-2021 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -29,8 +29,11 @@ import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.logging.Level;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JFileChooser;
|
||||
@ -38,6 +41,9 @@ import javax.swing.JOptionPane;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.tools.ant.types.Commandline;
|
||||
import org.netbeans.spi.options.OptionsPanelController;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
@ -47,9 +53,9 @@ import org.sleuthkit.autopsy.casemodule.GeneralFilter;
|
||||
import org.sleuthkit.autopsy.machinesettings.UserMachinePreferences;
|
||||
import org.sleuthkit.autopsy.machinesettings.UserMachinePreferencesException;
|
||||
import org.sleuthkit.autopsy.core.UserPreferences;
|
||||
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
|
||||
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
|
||||
import org.sleuthkit.autopsy.coreutils.Version;
|
||||
import org.sleuthkit.autopsy.machinesettings.UserMachinePreferences.TempDirChoice;
|
||||
import org.sleuthkit.autopsy.report.ReportBranding;
|
||||
|
||||
/**
|
||||
@ -75,9 +81,9 @@ import org.sleuthkit.autopsy.report.ReportBranding;
|
||||
final class AutopsyOptionsPanel extends javax.swing.JPanel {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final String DEFAULT_HEAP_DUMP_FILE_FIELD = "";
|
||||
private final JFileChooser logoFileChooser;
|
||||
private final JFileChooser tempDirChooser;
|
||||
private final TextFieldListener textFieldListener;
|
||||
private static final String ETC_FOLDER_NAME = "etc";
|
||||
private static final String CONFIG_FILE_EXTENSION = ".conf";
|
||||
private static final long ONE_BILLION = 1000000000L; //used to roughly convert system memory from bytes to gigabytes
|
||||
@ -86,6 +92,9 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
|
||||
private static final int MIN_MEMORY_IN_GB = 2; //the enforced minimum memory in gigabytes
|
||||
private static final Logger logger = Logger.getLogger(AutopsyOptionsPanel.class.getName());
|
||||
private String initialMemValue = Long.toString(Runtime.getRuntime().maxMemory() / ONE_BILLION);
|
||||
|
||||
private final ReportBranding reportBranding;
|
||||
private final JFileChooser heapFileChooser;
|
||||
|
||||
/**
|
||||
* Instantiate the Autopsy options panel.
|
||||
@ -101,13 +110,19 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
|
||||
tempDirChooser = new JFileChooser();
|
||||
tempDirChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
|
||||
tempDirChooser.setMultiSelectionEnabled(false);
|
||||
|
||||
heapFileChooser = new JFileChooser();
|
||||
heapFileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
|
||||
heapFileChooser.setMultiSelectionEnabled(false);
|
||||
|
||||
if (!PlatformUtil.is64BitJVM() || Version.getBuildType() == Version.Type.DEVELOPMENT) {
|
||||
if (!isJVMHeapSettingsCapable()) {
|
||||
//32 bit JVM has a max heap size of 1.4 gb to 4 gb depending on OS
|
||||
//So disabling the setting of heap size when the JVM is not 64 bit
|
||||
//Is the safest course of action
|
||||
//And the file won't exist in the install folder when running through netbeans
|
||||
memField.setEnabled(false);
|
||||
heapDumpFileField.setEnabled(false);
|
||||
heapDumpBrowseButton.setEnabled(false);
|
||||
solrMaxHeapSpinner.setEnabled(false);
|
||||
}
|
||||
systemMemoryTotal.setText(Long.toString(getSystemMemoryInGB()));
|
||||
@ -116,10 +131,20 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
|
||||
solrMaxHeapSpinner.setModel(new javax.swing.SpinnerNumberModel(UserPreferences.getMaxSolrVMSize(),
|
||||
JVM_MEMORY_STEP_SIZE_MB, ((int) getSystemMemoryInGB()) * MEGA_IN_GIGA, JVM_MEMORY_STEP_SIZE_MB));
|
||||
|
||||
textFieldListener = new TextFieldListener();
|
||||
agencyLogoPathField.getDocument().addDocumentListener(textFieldListener);
|
||||
tempDirectoryField.getDocument().addDocumentListener(textFieldListener);
|
||||
agencyLogoPathField.getDocument().addDocumentListener(new TextFieldListener(null));
|
||||
heapDumpFileField.getDocument().addDocumentListener(new TextFieldListener(this::isHeapPathValid));
|
||||
tempCustomField.getDocument().addDocumentListener(new TextFieldListener(this::evaluateTempDirState));
|
||||
logFileCount.setText(String.valueOf(UserPreferences.getLogFileCount()));
|
||||
|
||||
reportBranding = new ReportBranding();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the jvm runtime heap settings can effectively be changed.
|
||||
* @return Whether or not the jvm runtime heap settings can effectively be changed.
|
||||
*/
|
||||
private static boolean isJVMHeapSettingsCapable() {
|
||||
return PlatformUtil.is64BitJVM() && Version.getBuildType() != Version.Type.DEVELOPMENT;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -136,18 +161,23 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
|
||||
|
||||
/**
|
||||
* Gets the currently saved max java heap space in gigabytes.
|
||||
* @param The conf file memory value (i.e. 4G).
|
||||
*
|
||||
* @return @throws IOException when unable to get a valid setting
|
||||
* @return The value in gigabytes.
|
||||
* @throws IOException when unable to get a valid setting
|
||||
*/
|
||||
private long getCurrentJvmMaxMemoryInGB() throws IOException {
|
||||
String currentXmx = getCurrentXmxValue();
|
||||
private long getCurrentJvmMaxMemoryInGB(String confFileMemValue) throws IOException {
|
||||
char units = '-';
|
||||
Long value = 0L;
|
||||
if (currentXmx.length() > 1) {
|
||||
units = currentXmx.charAt(currentXmx.length() - 1);
|
||||
value = Long.parseLong(currentXmx.substring(0, currentXmx.length() - 1));
|
||||
if (confFileMemValue.length() > 1) {
|
||||
units = confFileMemValue.charAt(confFileMemValue.length() - 1);
|
||||
try {
|
||||
value = Long.parseLong(confFileMemValue.substring(0, confFileMemValue.length() - 1));
|
||||
} catch (NumberFormatException ex) {
|
||||
throw new IOException("Unable to properly parse memory number.", ex);
|
||||
}
|
||||
} else {
|
||||
throw new IOException("No memory setting found in String: " + currentXmx);
|
||||
throw new IOException("No memory setting found in String: " + confFileMemValue);
|
||||
}
|
||||
//some older .conf files might have the units as megabytes instead of gigabytes
|
||||
switch (units) {
|
||||
@ -162,33 +192,6 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The value currently saved in the conf file as the max java heap space
|
||||
* available to this application. Form will be an integer followed by a
|
||||
* character indicating units. Helper method for
|
||||
* getCurrentJvmMaxMemoryInGB()
|
||||
*
|
||||
* @return the saved value for the max java heap space
|
||||
*
|
||||
* @throws IOException if the conf file does not exist in either the user
|
||||
* directory or the install directory
|
||||
*/
|
||||
private String getCurrentXmxValue() throws IOException {
|
||||
String[] settings;
|
||||
String currentSetting = "";
|
||||
File userConfFile = getUserFolderConfFile();
|
||||
if (!userConfFile.exists()) {
|
||||
settings = getDefaultsFromFileContents(readConfFile(getInstallFolderConfFile()));
|
||||
} else {
|
||||
settings = getDefaultsFromFileContents(readConfFile(userConfFile));
|
||||
}
|
||||
for (String option : settings) {
|
||||
if (option.startsWith("-J-Xmx")) {
|
||||
currentSetting = option.replace("-J-Xmx", "").trim();
|
||||
}
|
||||
}
|
||||
return currentSetting;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the conf file from the install directory which stores the default
|
||||
@ -225,7 +228,61 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
|
||||
}
|
||||
return new File(userEtcFolder, confFileName);
|
||||
}
|
||||
|
||||
|
||||
private static final String JVM_SETTINGS_REGEX_PARAM = "options";
|
||||
private static final String JVM_SETTINGS_REGEX_STR = "^\\s*default_options\\s*=\\s*\"?(?<" + JVM_SETTINGS_REGEX_PARAM + ">.+?)\"?\\s*$";
|
||||
private static final Pattern JVM_SETTINGS_REGEX = Pattern.compile(JVM_SETTINGS_REGEX_STR);
|
||||
private static final String XMX_REGEX_PARAM = "mem";
|
||||
private static final String XMX_REGEX_STR = "^\\s*\\-J\\-Xmx(?<" + XMX_REGEX_PARAM + ">.+?)\\s*$";
|
||||
private static final Pattern XMX_REGEX = Pattern.compile(XMX_REGEX_STR);
|
||||
private static final String HEAP_DUMP_REGEX_PARAM = "path";
|
||||
private static final String HEAP_DUMP_REGEX_STR = "^\\s*\\-J\\-XX:HeapDumpPath=(\\\")?\\s*(?<" + HEAP_DUMP_REGEX_PARAM + ">.+?)\\s*(\\\")?$";
|
||||
private static final Pattern HEAP_DUMP_REGEX = Pattern.compile(HEAP_DUMP_REGEX_STR);
|
||||
|
||||
/**
|
||||
* Parse the autopsy conf file line. If the line is the default_options line,
|
||||
* then replaces with current memory and heap path value. Otherwise, returns
|
||||
* the line provided as parameter.
|
||||
*
|
||||
* @param line The line.
|
||||
* @param memText The text to add as an argument to be used as memory with -J-Xmx.
|
||||
* @param heapText The text to add as an argument to be used as the heap dump path with
|
||||
* -J-XX:HeapDumpPath.
|
||||
* @return The line modified to contain memory and heap arguments.
|
||||
*/
|
||||
private static String updateConfLine(String line, String memText, String heapText) {
|
||||
Matcher match = JVM_SETTINGS_REGEX.matcher(line);
|
||||
if (match.find()) {
|
||||
// split on command line arguments
|
||||
String[] parsedArgs = Commandline.translateCommandline(match.group(JVM_SETTINGS_REGEX_PARAM));
|
||||
|
||||
String memString = "-J-Xmx" + memText.replaceAll("[^\\d]", "") + "g";
|
||||
|
||||
// only add in heap path argument if a heap path is specified
|
||||
String heapString = null;
|
||||
if (StringUtils.isNotBlank(heapText)) {
|
||||
while (heapText.endsWith("\\") && heapText.length() > 0) {
|
||||
heapText = heapText.substring(0, heapText.length() - 1);
|
||||
}
|
||||
|
||||
heapString = String.format("-J-XX:HeapDumpPath=\"%s\"", heapText);
|
||||
}
|
||||
|
||||
Stream<String> argsNoMemHeap = Stream.of(parsedArgs)
|
||||
// remove saved version of memory and heap dump path
|
||||
.filter(s -> !s.matches(XMX_REGEX_STR) && !s.matches(HEAP_DUMP_REGEX_STR));
|
||||
|
||||
String newArgs = Stream.concat(argsNoMemHeap, Stream.of(memString, heapString))
|
||||
.filter(s -> s != null)
|
||||
.collect(Collectors.joining(" "));
|
||||
|
||||
return String.format("default_options=\"%s\"", newArgs);
|
||||
};
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Take the conf file in the install directory and save a copy of it to the
|
||||
* user directory. The copy will be modified to include the current memory
|
||||
@ -235,25 +292,124 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
|
||||
* install folders conf file
|
||||
*/
|
||||
private void writeEtcConfFile() throws IOException {
|
||||
StringBuilder content = new StringBuilder();
|
||||
List<String> confFile = readConfFile(getInstallFolderConfFile());
|
||||
for (String line : confFile) {
|
||||
if (line.contains("-J-Xmx")) {
|
||||
String[] splitLine = line.split(" ");
|
||||
StringJoiner modifiedLine = new StringJoiner(" ");
|
||||
for (String piece : splitLine) {
|
||||
if (piece.contains("-J-Xmx")) {
|
||||
piece = "-J-Xmx" + memField.getText() + "g";
|
||||
}
|
||||
modifiedLine.add(piece);
|
||||
}
|
||||
content.append(modifiedLine.toString());
|
||||
} else {
|
||||
content.append(line);
|
||||
}
|
||||
content.append("\n");
|
||||
String fileText = readConfFile(getInstallFolderConfFile()).stream()
|
||||
.map((line) -> updateConfLine(line, memField.getText(), heapDumpFileField.getText()))
|
||||
.collect(Collectors.joining("\n"));
|
||||
|
||||
FileUtils.writeStringToFile(getUserFolderConfFile(), fileText, "UTF-8");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Values for configuration located in the /etc/\*.conf file.
|
||||
*/
|
||||
private static class ConfValues {
|
||||
private final String XmxVal;
|
||||
private final String heapDumpPath;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
* @param XmxVal The heap memory size.
|
||||
* @param heapDumpPath The heap dump path.
|
||||
*/
|
||||
ConfValues(String XmxVal, String heapDumpPath) {
|
||||
this.XmxVal = XmxVal;
|
||||
this.heapDumpPath = heapDumpPath;
|
||||
}
|
||||
Files.write(getUserFolderConfFile().toPath(), content.toString().getBytes());
|
||||
|
||||
/**
|
||||
* Returns the heap memory value specified in the conf file.
|
||||
* @return The heap memory value specified in the conf file.
|
||||
*/
|
||||
String getXmxVal() {
|
||||
return XmxVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns path to the heap dump specified in the conf file.
|
||||
* @return Path to the heap dump specified in the conf file.
|
||||
*/
|
||||
String getHeapDumpPath() {
|
||||
return heapDumpPath;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the /etc/\*.conf file values pertinent to settings.
|
||||
* @return The conf file values.
|
||||
* @throws IOException
|
||||
*/
|
||||
private ConfValues getEtcConfValues() throws IOException {
|
||||
File userConfFile = getUserFolderConfFile();
|
||||
String[] args = userConfFile.exists() ?
|
||||
getDefaultsFromFileContents(readConfFile(userConfFile)) :
|
||||
getDefaultsFromFileContents(readConfFile(getInstallFolderConfFile()));
|
||||
|
||||
String heapFile = "";
|
||||
String memSize = "";
|
||||
|
||||
for (String arg : args) {
|
||||
Matcher memMatch = XMX_REGEX.matcher(arg);
|
||||
if (memMatch.find()) {
|
||||
memSize = memMatch.group(XMX_REGEX_PARAM);
|
||||
continue;
|
||||
}
|
||||
|
||||
Matcher heapFileMatch = HEAP_DUMP_REGEX.matcher(arg);
|
||||
if (heapFileMatch.find()) {
|
||||
heapFile = heapFileMatch.group(HEAP_DUMP_REGEX_PARAM);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return new ConfValues(memSize, heapFile);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Checks current heap path value to see if it is valid, and displays an error message if invalid.
|
||||
* @return True if the heap path is valid.
|
||||
*/
|
||||
@Messages({
|
||||
"AutopsyOptionsPanel_isHeapPathValid_selectValidDirectory=Please select an existing directory.",
|
||||
"AutopsyOptionsPanel_isHeapPathValid_developerMode=Cannot change heap dump path while in developer mode.",
|
||||
"AutopsyOptionsPanel_isHeapPathValid_not64BitMachine=Changing heap dump path settings only enabled for 64 bit version.",
|
||||
"AutopsyOPtionsPanel_isHeapPathValid_illegalCharacters=Please select a path with no quotes."
|
||||
})
|
||||
private boolean isHeapPathValid() {
|
||||
if (Version.getBuildType() == Version.Type.DEVELOPMENT) {
|
||||
heapFieldValidationLabel.setVisible(true);
|
||||
heapFieldValidationLabel.setText(Bundle.AutopsyOptionsPanel_isHeapPathValid_developerMode());
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!PlatformUtil.is64BitJVM()) {
|
||||
heapFieldValidationLabel.setVisible(true);
|
||||
heapFieldValidationLabel.setText(Bundle.AutopsyOptionsPanel_isHeapPathValid_not64BitMachine());
|
||||
return true;
|
||||
}
|
||||
|
||||
//allow blank field as the default will be used
|
||||
if (StringUtils.isNotBlank(heapDumpFileField.getText())) {
|
||||
String heapText = heapDumpFileField.getText().trim();
|
||||
if (heapText.contains("\"") || heapText.contains("'")) {
|
||||
heapFieldValidationLabel.setVisible(true);
|
||||
heapFieldValidationLabel.setText(Bundle.AutopsyOPtionsPanel_isHeapPathValid_illegalCharacters());
|
||||
return false;
|
||||
}
|
||||
|
||||
File curHeapFile = new File(heapText);
|
||||
if (!curHeapFile.exists() || !curHeapFile.isDirectory()) {
|
||||
heapFieldValidationLabel.setVisible(true);
|
||||
heapFieldValidationLabel.setText(Bundle.AutopsyOptionsPanel_isHeapPathValid_selectValidDirectory());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
heapFieldValidationLabel.setVisible(false);
|
||||
heapFieldValidationLabel.setText("");
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -291,51 +447,96 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
|
||||
* options is not present.
|
||||
*/
|
||||
private static String[] getDefaultsFromFileContents(List<String> list) {
|
||||
Optional<String> defaultSettings = list.stream().filter(line -> line.startsWith("default_options=")).findFirst();
|
||||
Optional<String> defaultSettings = list.stream()
|
||||
.filter(line -> line.matches(JVM_SETTINGS_REGEX_STR))
|
||||
.findFirst();
|
||||
|
||||
if (defaultSettings.isPresent()) {
|
||||
return defaultSettings.get().replace("default_options=", "").replaceAll("\"", "").split(" ");
|
||||
Matcher match = JVM_SETTINGS_REGEX.matcher(defaultSettings.get());
|
||||
if (match.find()) {
|
||||
return Commandline.translateCommandline(match.group(JVM_SETTINGS_REGEX_PARAM));
|
||||
}
|
||||
}
|
||||
|
||||
return new String[]{};
|
||||
}
|
||||
|
||||
private void evaluateTempDirState() {
|
||||
boolean caseOpen = Case.isCaseOpen();
|
||||
boolean customSelected = tempCustomRadio.isSelected();
|
||||
|
||||
tempDirectoryBrowseButton.setEnabled(!caseOpen && customSelected);
|
||||
tempCustomField.setEnabled(!caseOpen && customSelected);
|
||||
|
||||
tempOnCustomNoPath.setVisible(customSelected && StringUtils.isBlank(tempCustomField.getText()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the saved user preferences.
|
||||
*/
|
||||
void load() {
|
||||
String path = ModuleSettings.getConfigSetting(ReportBranding.MODULE_NAME, ReportBranding.AGENCY_LOGO_PATH_PROP);
|
||||
String path = reportBranding.getAgencyLogoPath();
|
||||
boolean useDefault = (path == null || path.isEmpty());
|
||||
defaultLogoRB.setSelected(useDefault);
|
||||
specifyLogoRB.setSelected(!useDefault);
|
||||
agencyLogoPathField.setEnabled(!useDefault);
|
||||
browseLogosButton.setEnabled(!useDefault);
|
||||
tempDirectoryField.setText(UserMachinePreferences.getBaseTempDirectory());
|
||||
|
||||
tempCustomField.setText(UserMachinePreferences.getCustomTempDirectory());
|
||||
switch (UserMachinePreferences.getTempDirChoice()) {
|
||||
case CASE:
|
||||
tempCaseRadio.setSelected(true);
|
||||
break;
|
||||
case CUSTOM:
|
||||
tempCustomRadio.setSelected(true);
|
||||
break;
|
||||
default:
|
||||
case SYSTEM:
|
||||
tempLocalRadio.setSelected(true);
|
||||
break;
|
||||
}
|
||||
|
||||
evaluateTempDirState();
|
||||
|
||||
logFileCount.setText(String.valueOf(UserPreferences.getLogFileCount()));
|
||||
solrMaxHeapSpinner.setValue(UserPreferences.getMaxSolrVMSize());
|
||||
tempDirectoryField.setText(UserMachinePreferences.getBaseTempDirectory());
|
||||
try {
|
||||
updateAgencyLogo(path);
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.WARNING, "Error loading image from previously saved agency logo path", ex);
|
||||
}
|
||||
if (memField.isEnabled()) {
|
||||
|
||||
boolean confLoaded = false;
|
||||
if (isJVMHeapSettingsCapable()) {
|
||||
try {
|
||||
initialMemValue = Long.toString(getCurrentJvmMaxMemoryInGB());
|
||||
ConfValues confValues = getEtcConfValues();
|
||||
heapDumpFileField.setText(confValues.getHeapDumpPath());
|
||||
initialMemValue = Long.toString(getCurrentJvmMaxMemoryInGB(confValues.getXmxVal()));
|
||||
confLoaded = true;
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.SEVERE, "Can't read current Jvm max memory setting from file", ex);
|
||||
memField.setEnabled(false);
|
||||
heapDumpFileField.setText(DEFAULT_HEAP_DUMP_FILE_FIELD);
|
||||
}
|
||||
memField.setText(initialMemValue);
|
||||
}
|
||||
|
||||
heapDumpBrowseButton.setEnabled(confLoaded);
|
||||
heapDumpFileField.setEnabled(confLoaded);
|
||||
|
||||
setTempDirEnabled();
|
||||
valid(); //ensure the error messages are up to date
|
||||
}
|
||||
|
||||
private void setTempDirEnabled() {
|
||||
boolean enabled = !Case.isCaseOpen();
|
||||
this.tempDirectoryBrowseButton.setEnabled(enabled);
|
||||
this.tempDirectoryField.setEnabled(enabled);
|
||||
|
||||
this.tempCaseRadio.setEnabled(enabled);
|
||||
this.tempCustomRadio.setEnabled(enabled);
|
||||
this.tempLocalRadio.setEnabled(enabled);
|
||||
|
||||
this.tempDirectoryWarningLabel.setVisible(!enabled);
|
||||
evaluateTempDirState();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -367,12 +568,14 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
|
||||
@Messages({
|
||||
"AutopsyOptionsPanel_storeTempDir_onError_title=Error Saving Temporary Directory",
|
||||
"# {0} - path",
|
||||
"AutopsyOptionsPanel_storeTempDir_onError_description=There was an error creating the temporary directory on the filesystem at: {0}.",})
|
||||
"AutopsyOptionsPanel_storeTempDir_onError_description=There was an error creating the temporary directory on the filesystem at: {0}.",
|
||||
"AutopsyOptionsPanel_storeTempDir_onChoiceError_title=Error Saving Temporary Directory Choice",
|
||||
"AutopsyOptionsPanel_storeTempDir_onChoiceError_description=There was an error updating temporary directory choice selection.",})
|
||||
private void storeTempDir() {
|
||||
String tempDirectoryPath = tempDirectoryField.getText();
|
||||
if (!UserMachinePreferences.getBaseTempDirectory().equals(tempDirectoryPath)) {
|
||||
String tempDirectoryPath = tempCustomField.getText();
|
||||
if (!UserMachinePreferences.getCustomTempDirectory().equals(tempDirectoryPath)) {
|
||||
try {
|
||||
UserMachinePreferences.setBaseTempDirectory(tempDirectoryPath);
|
||||
UserMachinePreferences.setCustomTempDirectory(tempDirectoryPath);
|
||||
} catch (UserMachinePreferencesException ex) {
|
||||
logger.log(Level.WARNING, "There was an error creating the temporary directory defined by the user: " + tempDirectoryPath, ex);
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
@ -383,6 +586,29 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
TempDirChoice choice;
|
||||
if (tempCaseRadio.isSelected()) {
|
||||
choice = TempDirChoice.CASE;
|
||||
} else if (tempCustomRadio.isSelected()) {
|
||||
choice = TempDirChoice.CUSTOM;
|
||||
} else {
|
||||
choice = TempDirChoice.SYSTEM;
|
||||
}
|
||||
|
||||
if (!choice.equals(UserMachinePreferences.getTempDirChoice())) {
|
||||
try {
|
||||
UserMachinePreferences.setTempDirChoice(choice);
|
||||
} catch (UserMachinePreferencesException ex) {
|
||||
logger.log(Level.WARNING, "There was an error updating choice to: " + choice.name(), ex);
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
JOptionPane.showMessageDialog(this,
|
||||
String.format("<html>%s</html>", Bundle.AutopsyOptionsPanel_storeTempDir_onChoiceError_description()),
|
||||
Bundle.AutopsyOptionsPanel_storeTempDir_onChoiceError_title(),
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -395,13 +621,13 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
|
||||
if (!agencyLogoPathField.getText().isEmpty()) {
|
||||
File file = new File(agencyLogoPathField.getText());
|
||||
if (file.exists()) {
|
||||
ModuleSettings.setConfigSetting(ReportBranding.MODULE_NAME, ReportBranding.AGENCY_LOGO_PATH_PROP, agencyLogoPathField.getText());
|
||||
reportBranding.setAgencyLogoPath(agencyLogoPathField.getText());
|
||||
}
|
||||
} else {
|
||||
ModuleSettings.setConfigSetting(ReportBranding.MODULE_NAME, ReportBranding.AGENCY_LOGO_PATH_PROP, "");
|
||||
reportBranding.setAgencyLogoPath("");
|
||||
}
|
||||
UserPreferences.setMaxSolrVMSize((int) solrMaxHeapSpinner.getValue());
|
||||
if (memField.isEnabled()) { //if the field could of been changed we need to try and save it
|
||||
if (isJVMHeapSettingsCapable()) { //if the field could of been changed we need to try and save it
|
||||
try {
|
||||
writeEtcConfFile();
|
||||
} catch (IOException ex) {
|
||||
@ -416,18 +642,12 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
|
||||
* @return True if valid; false otherwise.
|
||||
*/
|
||||
boolean valid() {
|
||||
boolean valid = true;
|
||||
if (!isAgencyLogoPathValid()) {
|
||||
valid = false;
|
||||
}
|
||||
if (!isMemFieldValid()) {
|
||||
valid = false;
|
||||
}
|
||||
if (!isLogNumFieldValid()) {
|
||||
valid = false;
|
||||
}
|
||||
|
||||
return valid;
|
||||
boolean agencyValid = isAgencyLogoPathValid();
|
||||
boolean memFieldValid = isMemFieldValid();
|
||||
boolean logNumValid = isLogNumFieldValid();
|
||||
boolean heapPathValid = isHeapPathValid();
|
||||
|
||||
return agencyValid && memFieldValid && logNumValid && heapPathValid;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -531,24 +751,44 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
|
||||
|
||||
/**
|
||||
* Listens for registered text fields that have changed and fires a
|
||||
* PropertyChangeEvent accordingly.
|
||||
* PropertyChangeEvent accordingly as well as firing an optional additional listener.
|
||||
*/
|
||||
private class TextFieldListener implements DocumentListener {
|
||||
private final Runnable onChange;
|
||||
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
* @param onChange Additional listener for change events.
|
||||
*/
|
||||
TextFieldListener(Runnable onChange) {
|
||||
this.onChange = onChange;
|
||||
}
|
||||
|
||||
private void baseOnChange() {
|
||||
if (onChange != null) {
|
||||
onChange.run();
|
||||
}
|
||||
|
||||
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e) {
|
||||
baseOnChange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
|
||||
baseOnChange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e) {
|
||||
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
baseOnChange();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -563,6 +803,7 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
|
||||
fileSelectionButtonGroup = new javax.swing.ButtonGroup();
|
||||
displayTimesButtonGroup = new javax.swing.ButtonGroup();
|
||||
logoSourceButtonGroup = new javax.swing.ButtonGroup();
|
||||
tempDirChoiceGroup = new javax.swing.ButtonGroup();
|
||||
jScrollPane1 = new javax.swing.JScrollPane();
|
||||
javax.swing.JPanel mainPanel = new javax.swing.JPanel();
|
||||
logoPanel = new javax.swing.JPanel();
|
||||
@ -588,10 +829,18 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
|
||||
maxMemoryUnitsLabel2 = new javax.swing.JLabel();
|
||||
solrMaxHeapSpinner = new javax.swing.JSpinner();
|
||||
solrJVMHeapWarning = new javax.swing.JLabel();
|
||||
heapFileLabel = new javax.swing.JLabel();
|
||||
heapDumpFileField = new javax.swing.JTextField();
|
||||
heapDumpBrowseButton = new javax.swing.JButton();
|
||||
heapFieldValidationLabel = new javax.swing.JLabel();
|
||||
tempDirectoryPanel = new javax.swing.JPanel();
|
||||
tempDirectoryField = new javax.swing.JTextField();
|
||||
tempCustomField = new javax.swing.JTextField();
|
||||
tempDirectoryBrowseButton = new javax.swing.JButton();
|
||||
tempDirectoryWarningLabel = new javax.swing.JLabel();
|
||||
tempLocalRadio = new javax.swing.JRadioButton();
|
||||
tempCaseRadio = new javax.swing.JRadioButton();
|
||||
tempCustomRadio = new javax.swing.JRadioButton();
|
||||
tempOnCustomNoPath = new javax.swing.JLabel();
|
||||
rdpPanel = new javax.swing.JPanel();
|
||||
javax.swing.JScrollPane sizingScrollPane = new javax.swing.JScrollPane();
|
||||
javax.swing.JTextPane sizingTextPane = new javax.swing.JTextPane();
|
||||
@ -734,6 +983,20 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(solrJVMHeapWarning, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.solrJVMHeapWarning.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(heapFileLabel, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.heapFileLabel.text")); // NOI18N
|
||||
|
||||
heapDumpFileField.setText(org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.heapDumpFileField.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(heapDumpBrowseButton, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.heapDumpBrowseButton.text")); // NOI18N
|
||||
heapDumpBrowseButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
heapDumpBrowseButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
heapFieldValidationLabel.setForeground(new java.awt.Color(255, 0, 0));
|
||||
org.openide.awt.Mnemonics.setLocalizedText(heapFieldValidationLabel, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.heapFieldValidationLabel.text")); // NOI18N
|
||||
|
||||
javax.swing.GroupLayout runtimePanelLayout = new javax.swing.GroupLayout(runtimePanel);
|
||||
runtimePanel.setLayout(runtimePanelLayout);
|
||||
runtimePanelLayout.setHorizontalGroup(
|
||||
@ -762,14 +1025,26 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
|
||||
.addGroup(runtimePanelLayout.createSequentialGroup()
|
||||
.addGap(23, 23, 23)
|
||||
.addComponent(memFieldValidationLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 478, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
.addContainerGap(12, Short.MAX_VALUE))
|
||||
.addGroup(runtimePanelLayout.createSequentialGroup()
|
||||
.addGap(18, 18, 18)
|
||||
.addComponent(solrJVMHeapWarning, javax.swing.GroupLayout.PREFERRED_SIZE, 331, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(44, 44, 44)
|
||||
.addComponent(logNumAlert)
|
||||
.addContainerGap())))
|
||||
.addComponent(restartNecessaryWarning, javax.swing.GroupLayout.PREFERRED_SIZE, 615, javax.swing.GroupLayout.PREFERRED_SIZE)))
|
||||
.addGroup(runtimePanelLayout.createSequentialGroup()
|
||||
.addGroup(runtimePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(restartNecessaryWarning, javax.swing.GroupLayout.PREFERRED_SIZE, 615, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGroup(runtimePanelLayout.createSequentialGroup()
|
||||
.addComponent(heapFileLabel)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(runtimePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(heapFieldValidationLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 478, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGroup(runtimePanelLayout.createSequentialGroup()
|
||||
.addComponent(heapDumpFileField, javax.swing.GroupLayout.PREFERRED_SIZE, 415, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(heapDumpBrowseButton)))))
|
||||
.addGap(0, 0, Short.MAX_VALUE))))
|
||||
);
|
||||
|
||||
runtimePanelLayout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {maxLogFileCount, maxMemoryLabel, totalMemoryLabel});
|
||||
@ -803,7 +1078,14 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
|
||||
.addGroup(runtimePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(maxLogFileCount)
|
||||
.addComponent(logFileCount, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(runtimePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(heapFileLabel)
|
||||
.addComponent(heapDumpFileField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(heapDumpBrowseButton))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(heapFieldValidationLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(restartNecessaryWarning)
|
||||
.addContainerGap())
|
||||
);
|
||||
@ -818,7 +1100,7 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
|
||||
tempDirectoryPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.tempDirectoryPanel.border.title"))); // NOI18N
|
||||
tempDirectoryPanel.setName(org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.tempDirectoryPanel.name")); // NOI18N
|
||||
|
||||
tempDirectoryField.setText(org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.tempDirectoryField.text")); // NOI18N
|
||||
tempCustomField.setText(org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.tempCustomField.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(tempDirectoryBrowseButton, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.tempDirectoryBrowseButton.text")); // NOI18N
|
||||
tempDirectoryBrowseButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
@ -830,6 +1112,33 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
|
||||
tempDirectoryWarningLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/warning16.png"))); // NOI18N
|
||||
org.openide.awt.Mnemonics.setLocalizedText(tempDirectoryWarningLabel, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.tempDirectoryWarningLabel.text")); // NOI18N
|
||||
|
||||
tempDirChoiceGroup.add(tempLocalRadio);
|
||||
org.openide.awt.Mnemonics.setLocalizedText(tempLocalRadio, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.tempLocalRadio.text")); // NOI18N
|
||||
tempLocalRadio.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
tempLocalRadioActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
tempDirChoiceGroup.add(tempCaseRadio);
|
||||
org.openide.awt.Mnemonics.setLocalizedText(tempCaseRadio, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.tempCaseRadio.text")); // NOI18N
|
||||
tempCaseRadio.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
tempCaseRadioActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
tempDirChoiceGroup.add(tempCustomRadio);
|
||||
org.openide.awt.Mnemonics.setLocalizedText(tempCustomRadio, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.tempCustomRadio.text")); // NOI18N
|
||||
tempCustomRadio.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
tempCustomRadioActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
tempOnCustomNoPath.setForeground(java.awt.Color.RED);
|
||||
org.openide.awt.Mnemonics.setLocalizedText(tempOnCustomNoPath, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.tempOnCustomNoPath.text")); // NOI18N
|
||||
|
||||
javax.swing.GroupLayout tempDirectoryPanelLayout = new javax.swing.GroupLayout(tempDirectoryPanel);
|
||||
tempDirectoryPanel.setLayout(tempDirectoryPanelLayout);
|
||||
tempDirectoryPanelLayout.setHorizontalGroup(
|
||||
@ -837,23 +1146,37 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
|
||||
.addGroup(tempDirectoryPanelLayout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(tempDirectoryPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(tempLocalRadio)
|
||||
.addComponent(tempCaseRadio)
|
||||
.addComponent(tempDirectoryWarningLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 615, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGroup(tempDirectoryPanelLayout.createSequentialGroup()
|
||||
.addComponent(tempDirectoryField, javax.swing.GroupLayout.PREFERRED_SIZE, 367, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(tempCustomRadio)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(tempDirectoryBrowseButton)))
|
||||
.addGap(0, 0, Short.MAX_VALUE))
|
||||
.addGroup(tempDirectoryPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(tempOnCustomNoPath)
|
||||
.addGroup(tempDirectoryPanelLayout.createSequentialGroup()
|
||||
.addComponent(tempCustomField, javax.swing.GroupLayout.PREFERRED_SIZE, 459, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(tempDirectoryBrowseButton)))))
|
||||
.addContainerGap(164, Short.MAX_VALUE))
|
||||
);
|
||||
tempDirectoryPanelLayout.setVerticalGroup(
|
||||
tempDirectoryPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(tempDirectoryPanelLayout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(tempLocalRadio)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(tempCaseRadio)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(tempDirectoryPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(tempDirectoryField)
|
||||
.addComponent(tempCustomRadio)
|
||||
.addComponent(tempCustomField)
|
||||
.addComponent(tempDirectoryBrowseButton))
|
||||
.addGap(18, 18, 18)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addComponent(tempOnCustomNoPath)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(tempDirectoryWarningLabel)
|
||||
.addContainerGap())
|
||||
.addGap(14, 14, 14))
|
||||
);
|
||||
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
@ -906,11 +1229,11 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 648, Short.MAX_VALUE)
|
||||
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 860, Short.MAX_VALUE)
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 382, Short.MAX_VALUE)
|
||||
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 620, Short.MAX_VALUE)
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
@ -927,7 +1250,7 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
|
||||
if (!f.exists() && !f.mkdirs()) {
|
||||
throw new InvalidPathException(specifiedPath, "Unable to create parent directories leading to " + specifiedPath);
|
||||
}
|
||||
tempDirectoryField.setText(specifiedPath);
|
||||
tempCustomField.setText(specifiedPath);
|
||||
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
|
||||
} catch (InvalidPathException ex) {
|
||||
logger.log(Level.WARNING, "Unable to create temporary directory in " + specifiedPath, ex);
|
||||
@ -971,7 +1294,7 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
|
||||
browseLogosButton.setEnabled(true);
|
||||
try {
|
||||
if (agencyLogoPathField.getText().isEmpty()) {
|
||||
String path = ModuleSettings.getConfigSetting(ReportBranding.MODULE_NAME, ReportBranding.AGENCY_LOGO_PATH_PROP);
|
||||
String path = reportBranding.getAgencyLogoPath();
|
||||
if (path != null && !path.isEmpty()) {
|
||||
updateAgencyLogo(path);
|
||||
}
|
||||
@ -1017,6 +1340,39 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
|
||||
}
|
||||
}//GEN-LAST:event_browseLogosButtonActionPerformed
|
||||
|
||||
private void tempLocalRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_tempLocalRadioActionPerformed
|
||||
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
|
||||
evaluateTempDirState();
|
||||
}//GEN-LAST:event_tempLocalRadioActionPerformed
|
||||
|
||||
private void tempCaseRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_tempCaseRadioActionPerformed
|
||||
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
|
||||
evaluateTempDirState();
|
||||
}//GEN-LAST:event_tempCaseRadioActionPerformed
|
||||
|
||||
private void tempCustomRadioActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_tempCustomRadioActionPerformed
|
||||
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
|
||||
evaluateTempDirState();
|
||||
}//GEN-LAST:event_tempCustomRadioActionPerformed
|
||||
|
||||
@Messages({
|
||||
"AutopsyOptionsPanel_heapDumpBrowseButtonActionPerformed_fileAlreadyExistsTitle=File Already Exists",
|
||||
"AutopsyOptionsPanel_heapDumpBrowseButtonActionPerformed_fileAlreadyExistsMessage=File already exists. Please select a new location."
|
||||
})
|
||||
private void heapDumpBrowseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_heapDumpBrowseButtonActionPerformed
|
||||
String oldHeapPath = heapDumpFileField.getText();
|
||||
if (!StringUtils.isBlank(oldHeapPath)) {
|
||||
heapFileChooser.setCurrentDirectory(new File(oldHeapPath));
|
||||
}
|
||||
|
||||
int returnState = heapFileChooser.showOpenDialog(this);
|
||||
if (returnState == JFileChooser.APPROVE_OPTION) {
|
||||
File selectedDirectory = heapFileChooser.getSelectedFile();
|
||||
heapDumpFileField.setText(selectedDirectory.getAbsolutePath());
|
||||
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
|
||||
}
|
||||
}//GEN-LAST:event_heapDumpBrowseButtonActionPerformed
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JTextField agencyLogoPathField;
|
||||
private javax.swing.JLabel agencyLogoPathFieldValidationLabel;
|
||||
@ -1025,6 +1381,10 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
|
||||
private javax.swing.JRadioButton defaultLogoRB;
|
||||
private javax.swing.ButtonGroup displayTimesButtonGroup;
|
||||
private javax.swing.ButtonGroup fileSelectionButtonGroup;
|
||||
private javax.swing.JButton heapDumpBrowseButton;
|
||||
private javax.swing.JTextField heapDumpFileField;
|
||||
private javax.swing.JLabel heapFieldValidationLabel;
|
||||
private javax.swing.JLabel heapFileLabel;
|
||||
private javax.swing.JScrollPane jScrollPane1;
|
||||
private javax.swing.JTextField logFileCount;
|
||||
private javax.swing.JTextField logNumAlert;
|
||||
@ -1045,10 +1405,15 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
|
||||
private javax.swing.JSpinner solrMaxHeapSpinner;
|
||||
private javax.swing.JRadioButton specifyLogoRB;
|
||||
private javax.swing.JLabel systemMemoryTotal;
|
||||
private javax.swing.JRadioButton tempCaseRadio;
|
||||
private javax.swing.JTextField tempCustomField;
|
||||
private javax.swing.JRadioButton tempCustomRadio;
|
||||
private javax.swing.ButtonGroup tempDirChoiceGroup;
|
||||
private javax.swing.JButton tempDirectoryBrowseButton;
|
||||
private javax.swing.JTextField tempDirectoryField;
|
||||
private javax.swing.JPanel tempDirectoryPanel;
|
||||
private javax.swing.JLabel tempDirectoryWarningLabel;
|
||||
private javax.swing.JRadioButton tempLocalRadio;
|
||||
private javax.swing.JLabel tempOnCustomNoPath;
|
||||
private javax.swing.JLabel totalMemoryLabel;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
CTL_DataContentAction=DataContent
|
||||
CTL_DataContentTopComponent=Data Content
|
||||
CTL_CustomAboutAction=About
|
||||
OptionsCategory_Name_General=Application
|
||||
OptionsCategory_Keywords_General=Autopsy Options
|
||||
HINT_DataContentTopComponent=This is a DataContent window
|
||||
@ -188,12 +187,11 @@ MultiUserSettingsPanel.restartRequiredLabel.text=Application restart required to
|
||||
MultiUserSettingsPanel.MustRestart=Autopsy must be restarted for new configuration to take effect
|
||||
MultiUserSettingsPanel.lbSolrNote1.text=Enter Solr 8 and/or Solr 4 server settings.
|
||||
MultiUserSettingsPanel.lbSolrNote2.text=New text indexing can only be done with Solr 8.
|
||||
AutopsyOptionsPanel.tempDirectoryField.text=
|
||||
AutopsyOptionsPanel.tempDirectoryBrowseButton.text=Browse
|
||||
AutopsyOptionsPanel.a.AccessibleContext.accessibleName=Temp Directory
|
||||
AutopsyOptionsPanel.tempDirectoryPanel.AccessibleContext.accessibleName=Temp Directory
|
||||
AutopsyOptionsPanel.tempDirectoryPanel.name=Temp Directory
|
||||
AutopsyOptionsPanel.tempDirectoryPanel.border.title=Temp Directory
|
||||
AutopsyOptionsPanel.tempDirectoryPanel.border.title=Root Temp Directory
|
||||
AutopsyOptionsPanel.tempDirectoryWarningLabel.text=Close the current case to change the temporary directory.
|
||||
AutopsyOptionsPanel.solrJVMHeapWarning.text=NOTE: Setting this too large may impact overall performance.
|
||||
AutopsyOptionsPanel.maxMemoryUnitsLabel2.text=MB
|
||||
@ -247,3 +245,12 @@ AutopsyOptionsPanel.agencyLogoPathField.text=
|
||||
AutopsyOptionsPanel.logoPanel.border.title=Logo
|
||||
ViewPreferencesPanel.radioGroupByPersonHost.text=Group by Person/Host
|
||||
ViewPreferencesPanel.radioGroupByDataType.text=Group by Data Type
|
||||
AutopsyOptionsPanel.tempLocalRadio.text=Local temp directory
|
||||
AutopsyOptionsPanel.tempCaseRadio.text=Temp folder in case directory
|
||||
AutopsyOptionsPanel.tempCustomRadio.text=Custom
|
||||
AutopsyOptionsPanel.tempCustomField.text=
|
||||
AutopsyOptionsPanel.tempOnCustomNoPath.text=Please select a path for the custom root temp directory.
|
||||
AutopsyOptionsPanel.heapDumpFileField.text=
|
||||
AutopsyOptionsPanel.heapDumpBrowseButton.text=Browse
|
||||
AutopsyOptionsPanel.heapFileLabel.text=Custom Heap Dump Location:
|
||||
AutopsyOptionsPanel.heapFieldValidationLabel.text=
|
||||
|
@ -12,6 +12,14 @@ AutopsyOptionsPanel.memFieldValidationLabel.noValueEntered.text=No value entered
|
||||
AutopsyOptionsPanel.memFieldValidationLabel.overMaxMemory.text=Value must be less than the total system memory of {0}GB
|
||||
# {0} - minimumMemory
|
||||
AutopsyOptionsPanel.memFieldValidationLabel.underMinMemory.text=Value must be at least {0}GB
|
||||
AutopsyOptionsPanel_heapDumpBrowseButtonActionPerformed_fileAlreadyExistsMessage=File already exists. Please select a new location.
|
||||
AutopsyOptionsPanel_heapDumpBrowseButtonActionPerformed_fileAlreadyExistsTitle=File Already Exists
|
||||
AutopsyOptionsPanel_isHeapPathValid_developerMode=Cannot change heap dump path while in developer mode.
|
||||
AutopsyOPtionsPanel_isHeapPathValid_illegalCharacters=Please select a path with no quotes.
|
||||
AutopsyOptionsPanel_isHeapPathValid_not64BitMachine=Changing heap dump path settings only enabled for 64 bit version.
|
||||
AutopsyOptionsPanel_isHeapPathValid_selectValidDirectory=Please select an existing directory.
|
||||
AutopsyOptionsPanel_storeTempDir_onChoiceError_description=There was an error updating temporary directory choice selection.
|
||||
AutopsyOptionsPanel_storeTempDir_onChoiceError_title=Error Saving Temporary Directory Choice
|
||||
# {0} - path
|
||||
AutopsyOptionsPanel_storeTempDir_onError_description=There was an error creating the temporary directory on the filesystem at: {0}.
|
||||
AutopsyOptionsPanel_storeTempDir_onError_title=Error Saving Temporary Directory
|
||||
@ -20,7 +28,6 @@ AutopsyOptionsPanel_tempDirectoryBrowseButtonActionPerformed_onInvalidPath_descr
|
||||
AutopsyOptionsPanel_tempDirectoryBrowseButtonActionPerformed_onInvalidPath_title=Path cannot be used
|
||||
CTL_DataContentAction=DataContent
|
||||
CTL_DataContentTopComponent=Data Content
|
||||
CTL_CustomAboutAction=About
|
||||
CTL_OfflineHelpAction=Offline Autopsy Documentation
|
||||
CTL_OnlineHelpAction=Online Autopsy Documentation
|
||||
DataContentViewerArtifact.failedToGetAttributes.message=Failed to get some or all attributes from case database
|
||||
@ -246,12 +253,11 @@ MultiUserSettingsPanel.restartRequiredLabel.text=Application restart required to
|
||||
MultiUserSettingsPanel.MustRestart=Autopsy must be restarted for new configuration to take effect
|
||||
MultiUserSettingsPanel.lbSolrNote1.text=Enter Solr 8 and/or Solr 4 server settings.
|
||||
MultiUserSettingsPanel.lbSolrNote2.text=New text indexing can only be done with Solr 8.
|
||||
AutopsyOptionsPanel.tempDirectoryField.text=
|
||||
AutopsyOptionsPanel.tempDirectoryBrowseButton.text=Browse
|
||||
AutopsyOptionsPanel.a.AccessibleContext.accessibleName=Temp Directory
|
||||
AutopsyOptionsPanel.tempDirectoryPanel.AccessibleContext.accessibleName=Temp Directory
|
||||
AutopsyOptionsPanel.tempDirectoryPanel.name=Temp Directory
|
||||
AutopsyOptionsPanel.tempDirectoryPanel.border.title=Temp Directory
|
||||
AutopsyOptionsPanel.tempDirectoryPanel.border.title=Root Temp Directory
|
||||
AutopsyOptionsPanel.tempDirectoryWarningLabel.text=Close the current case to change the temporary directory.
|
||||
AutopsyOptionsPanel.solrJVMHeapWarning.text=NOTE: Setting this too large may impact overall performance.
|
||||
AutopsyOptionsPanel.maxMemoryUnitsLabel2.text=MB
|
||||
@ -305,3 +311,12 @@ AutopsyOptionsPanel.agencyLogoPathField.text=
|
||||
AutopsyOptionsPanel.logoPanel.border.title=Logo
|
||||
ViewPreferencesPanel.radioGroupByPersonHost.text=Group by Person/Host
|
||||
ViewPreferencesPanel.radioGroupByDataType.text=Group by Data Type
|
||||
AutopsyOptionsPanel.tempLocalRadio.text=Local temp directory
|
||||
AutopsyOptionsPanel.tempCaseRadio.text=Temp folder in case directory
|
||||
AutopsyOptionsPanel.tempCustomRadio.text=Custom
|
||||
AutopsyOptionsPanel.tempCustomField.text=
|
||||
AutopsyOptionsPanel.tempOnCustomNoPath.text=Please select a path for the custom root temp directory.
|
||||
AutopsyOptionsPanel.heapDumpFileField.text=
|
||||
AutopsyOptionsPanel.heapDumpBrowseButton.text=Browse
|
||||
AutopsyOptionsPanel.heapFileLabel.text=Custom Heap Dump Location:
|
||||
AutopsyOptionsPanel.heapFieldValidationLabel.text=
|
||||
|
@ -24,6 +24,7 @@ import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.UIManager.LookAndFeelInfo;
|
||||
@ -75,6 +76,16 @@ public class Installer extends ModuleInstall {
|
||||
}
|
||||
|
||||
private void setLookAndFeel() {
|
||||
|
||||
ImageIcon questionIcon = new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/question_32.png"));
|
||||
ImageIcon warningIcon = new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/warning_32.png"));
|
||||
ImageIcon informationIcon = new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/information_32.png"));
|
||||
ImageIcon errorIcon = new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/error_32.png"));
|
||||
UIManager.put("OptionPane.errorIcon", errorIcon);
|
||||
UIManager.put("OptionPane.warningIcon", warningIcon);
|
||||
UIManager.put("OptionPane.questionIcon", questionIcon);
|
||||
UIManager.put("OptionPane.informationIcon", informationIcon);
|
||||
|
||||
if (System.getProperty("os.name").toLowerCase().contains("mac")) { //NON-NLS
|
||||
setUnixLookAndFeel();
|
||||
setModuleSettings("false");
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2017 Basis Technology Corp.
|
||||
* Copyright 2017-2021 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -18,8 +18,13 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.coreutils;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.ThreadInfo;
|
||||
import java.lang.management.ThreadMXBean;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/*
|
||||
* Concurrent programming utilities.
|
||||
@ -50,6 +55,36 @@ final public class ThreadUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the thread info for all live threads with stack trace and
|
||||
* synchronization information. Some threads included in the returned array
|
||||
* may have been terminated when this method returns.
|
||||
*
|
||||
* @return Thread dump of all live threads
|
||||
*/
|
||||
public static String generateThreadDump() {
|
||||
StringBuilder threadDump = new StringBuilder(System.lineSeparator());
|
||||
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
|
||||
ThreadInfo[] threadInfos = threadMXBean.getThreadInfo(threadMXBean.getAllThreadIds(), 100);
|
||||
for (ThreadInfo threadInfo : threadInfos) {
|
||||
threadDump.append(threadInfo.toString());
|
||||
threadDump.append("\n");
|
||||
}
|
||||
|
||||
long[] deadlockThreadIds = threadMXBean.findDeadlockedThreads();
|
||||
if (deadlockThreadIds != null) {
|
||||
threadDump.append("-------------------List of Deadlocked Thread IDs ---------------------");
|
||||
String idsList = (Arrays
|
||||
.stream(deadlockThreadIds)
|
||||
.boxed()
|
||||
.collect(Collectors.toList()))
|
||||
.stream().map(n -> String.valueOf(n))
|
||||
.collect(Collectors.joining("-", "{", "}"));
|
||||
threadDump.append(idsList);
|
||||
}
|
||||
return threadDump.toString();
|
||||
}
|
||||
|
||||
private ThreadUtils() {
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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.datamodel;
|
||||
|
||||
import org.openide.nodes.Children;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
|
||||
/**
|
||||
* Analysis Results node support.
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"AnalysisResults_name=Analysis Results",})
|
||||
public class AnalysisResults implements AutopsyVisitableItem {
|
||||
|
||||
/**
|
||||
* Returns the name of this node that is the key in the children object.
|
||||
*
|
||||
* @return The name of this node that is the key in the children object.
|
||||
*/
|
||||
public static String getName() {
|
||||
return Bundle.AnalysisResults_name();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parent node of all analysis results.
|
||||
*/
|
||||
static class RootNode extends Artifacts.BaseArtifactNode {
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param filteringDSObjId The data source object id for which results
|
||||
* should be filtered. If no filtering should
|
||||
* occur, this number should be less than or
|
||||
* equal to 0.
|
||||
*/
|
||||
RootNode(long filteringDSObjId) {
|
||||
super(Children.create(new Artifacts.TypeFactory(BlackboardArtifact.Category.ANALYSIS_RESULT, filteringDSObjId), true),
|
||||
"org/sleuthkit/autopsy/images/analysis_result.png",
|
||||
AnalysisResults.getName(),
|
||||
AnalysisResults.getName());
|
||||
}
|
||||
}
|
||||
|
||||
private final long datasourceObjId;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*/
|
||||
public AnalysisResults() {
|
||||
this(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param dsObjId The data source object id.
|
||||
*/
|
||||
public AnalysisResults(long dsObjId) {
|
||||
this.datasourceObjId = dsObjId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T accept(AutopsyItemVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not there is a data source object for which results
|
||||
* should be filtered.
|
||||
*
|
||||
* @return Whether or not there is a data source object for which results
|
||||
* should be filtered.
|
||||
*/
|
||||
Long getFilteringDataSourceObjId() {
|
||||
return datasourceObjId;
|
||||
}
|
||||
}
|
704
Core/src/org/sleuthkit/autopsy/datamodel/Artifacts.java
Normal file
704
Core/src/org/sleuthkit/autopsy/datamodel/Artifacts.java
Normal file
@ -0,0 +1,704 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-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.datamodel;
|
||||
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import org.openide.nodes.ChildFactory;
|
||||
import org.openide.nodes.Children;
|
||||
import org.openide.nodes.Node;
|
||||
import org.openide.nodes.Sheet;
|
||||
import org.openide.util.Lookup;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.lookup.Lookups;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datamodel.accounts.Accounts;
|
||||
import org.sleuthkit.autopsy.datamodel.utils.IconsUtil;
|
||||
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.autopsy.guiutils.RefreshThrottler;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact.Category;
|
||||
import org.python.google.common.collect.Sets;
|
||||
import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_ACCOUNT;
|
||||
import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_DATA_SOURCE_USAGE;
|
||||
import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_EMAIL_MSG;
|
||||
import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_HASHSET_HIT;
|
||||
import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT;
|
||||
import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT;
|
||||
import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_GEN_INFO;
|
||||
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE;
|
||||
import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_TL_EVENT;
|
||||
import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_ASSOCIATED_OBJECT;
|
||||
import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_KEYWORD_HIT;
|
||||
|
||||
/**
|
||||
* Classes for creating nodes for BlackboardArtifacts.
|
||||
*/
|
||||
public class Artifacts {
|
||||
|
||||
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST
|
||||
= EnumSet.of(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED);
|
||||
|
||||
/**
|
||||
* Base class for a parent node of artifacts.
|
||||
*/
|
||||
static class BaseArtifactNode extends DisplayableItemNode {
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param children The children of the node.
|
||||
* @param icon The icon for the node.
|
||||
* @param name The name identifier of the node.
|
||||
* @param displayName The display name for the node.
|
||||
*/
|
||||
BaseArtifactNode(Children children, String icon, String name, String displayName) {
|
||||
super(children, Lookups.singleton(name));
|
||||
super.setName(name);
|
||||
super.setDisplayName(displayName);
|
||||
this.setIconBaseWithExtension(icon); //NON-NLS
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLeafTypeNode() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Sheet createSheet() {
|
||||
Sheet sheet = super.createSheet();
|
||||
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
|
||||
if (sheetSet == null) {
|
||||
sheetSet = Sheet.createPropertiesSet();
|
||||
sheet.put(sheetSet);
|
||||
}
|
||||
|
||||
sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ExtractedContentNode.createSheet.name.name"),
|
||||
NbBundle.getMessage(this.getClass(), "ExtractedContentNode.createSheet.name.displayName"),
|
||||
NbBundle.getMessage(this.getClass(), "ExtractedContentNode.createSheet.name.desc"),
|
||||
super.getDisplayName()));
|
||||
return sheet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getItemType() {
|
||||
return getClass().getName();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A key to be used with the type factory.
|
||||
*/
|
||||
private static class TypeNodeKey {
|
||||
|
||||
private final UpdatableCountTypeNode node;
|
||||
private final Set<BlackboardArtifact.Type> applicableTypes;
|
||||
|
||||
/**
|
||||
* Constructor generating a generic TypeNode for a given artifact type.
|
||||
*
|
||||
* @param type The type for the key.
|
||||
* @param dsObjId The data source object id if filtering should occur.
|
||||
* If no filtering should occur, this number should be
|
||||
* less than or equal to 0.
|
||||
*/
|
||||
TypeNodeKey(BlackboardArtifact.Type type, long dsObjId) {
|
||||
this(new TypeNode(type, dsObjId), type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for any UpdatableCountTypeNode.
|
||||
*
|
||||
* @param typeNode The UpdatableCountTypeNode.
|
||||
* @param types The blackboard artifact types corresponding to this
|
||||
* node.
|
||||
*/
|
||||
TypeNodeKey(UpdatableCountTypeNode typeNode, BlackboardArtifact.Type... types) {
|
||||
this.node = typeNode;
|
||||
this.applicableTypes = Stream.of(types)
|
||||
.filter(t -> t != null)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the node associated with this key.
|
||||
*
|
||||
* @return The node associated with this key.
|
||||
*/
|
||||
UpdatableCountTypeNode getNode() {
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the blackboard artifact types associated with this key.
|
||||
*
|
||||
* @return The blackboard artifact types associated with this key.
|
||||
*/
|
||||
Set<BlackboardArtifact.Type> getApplicableTypes() {
|
||||
return applicableTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 3;
|
||||
hash = 61 * hash + Objects.hashCode(this.applicableTypes);
|
||||
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 TypeNodeKey other = (TypeNodeKey) obj;
|
||||
if (!Objects.equals(this.applicableTypes, other.applicableTypes)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory for showing a list of artifact types (i.e. all the data artifact
|
||||
* types).
|
||||
*/
|
||||
static class TypeFactory extends ChildFactory.Detachable<TypeNodeKey> implements RefreshThrottler.Refresher {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(TypeNode.class.getName());
|
||||
|
||||
/**
|
||||
* Types that should not be shown in the tree.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
private static final Set<BlackboardArtifact.Type> IGNORED_TYPES = Sets.newHashSet(
|
||||
// these are shown in other parts of the UI (and different node types)
|
||||
TSK_DATA_SOURCE_USAGE,
|
||||
TSK_GEN_INFO,
|
||||
new BlackboardArtifact.Type(TSK_DOWNLOAD_SOURCE),
|
||||
TSK_TL_EVENT,
|
||||
//This is not meant to be shown in the UI at all. It is more of a meta artifact.
|
||||
TSK_ASSOCIATED_OBJECT
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns a Children key to be use for a particular artifact type.
|
||||
*
|
||||
* @param type The artifact type.
|
||||
* @param skCase The relevant Sleuthkit case in order to create the
|
||||
* node.
|
||||
* @param dsObjId The data source object id to use for filtering. If id
|
||||
* is less than or equal to 0, no filtering will occur.
|
||||
*
|
||||
* @return The generated key.
|
||||
*/
|
||||
private static TypeNodeKey getTypeKey(BlackboardArtifact.Type type, SleuthkitCase skCase, long dsObjId) {
|
||||
int typeId = type.getTypeID();
|
||||
if (TSK_EMAIL_MSG.getTypeID() == typeId) {
|
||||
EmailExtracted.RootNode emailNode = new EmailExtracted(skCase, dsObjId).new RootNode();
|
||||
return new TypeNodeKey(emailNode, TSK_EMAIL_MSG);
|
||||
|
||||
} else if (TSK_ACCOUNT.getTypeID() == typeId) {
|
||||
Accounts.AccountsRootNode accountsNode = new Accounts(skCase, dsObjId).new AccountsRootNode();
|
||||
return new TypeNodeKey(accountsNode, TSK_ACCOUNT);
|
||||
|
||||
} else if (TSK_KEYWORD_HIT.getTypeID() == typeId) {
|
||||
KeywordHits.RootNode keywordsNode = new KeywordHits(skCase, dsObjId).new RootNode();
|
||||
return new TypeNodeKey(keywordsNode, TSK_KEYWORD_HIT);
|
||||
|
||||
} else if (TSK_INTERESTING_ARTIFACT_HIT.getTypeID() == typeId
|
||||
|| TSK_INTERESTING_FILE_HIT.getTypeID() == typeId) {
|
||||
|
||||
InterestingHits.RootNode interestingHitsNode = new InterestingHits(skCase, dsObjId).new RootNode();
|
||||
return new TypeNodeKey(interestingHitsNode,
|
||||
TSK_INTERESTING_ARTIFACT_HIT,
|
||||
TSK_INTERESTING_FILE_HIT);
|
||||
|
||||
} else if (TSK_HASHSET_HIT.getTypeID() == typeId) {
|
||||
HashsetHits.RootNode hashsetHits = new HashsetHits(skCase, dsObjId).new RootNode();
|
||||
return new TypeNodeKey(hashsetHits, TSK_HASHSET_HIT);
|
||||
|
||||
} else {
|
||||
return new TypeNodeKey(type, dsObjId);
|
||||
}
|
||||
}
|
||||
|
||||
// maps the artifact type to its child node
|
||||
private final Map<BlackboardArtifact.Type, TypeNodeKey> typeNodeMap = new HashMap<>();
|
||||
private final long filteringDSObjId;
|
||||
|
||||
/**
|
||||
* RefreshThrottler is used to limit the number of refreshes performed
|
||||
* when CONTENT_CHANGED and DATA_ADDED ingest module events are
|
||||
* received.
|
||||
*/
|
||||
private final RefreshThrottler refreshThrottler = new RefreshThrottler(this);
|
||||
private final Category category;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param category The category of types to be displayed.
|
||||
* @param filteringDSObjId The data source object id to use for
|
||||
* filtering. If id is less than or equal to 0,
|
||||
* no filtering will occur.
|
||||
*/
|
||||
TypeFactory(Category category, long filteringDSObjId) {
|
||||
super();
|
||||
this.filteringDSObjId = filteringDSObjId;
|
||||
this.category = category;
|
||||
}
|
||||
|
||||
private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> {
|
||||
String eventType = evt.getPropertyName();
|
||||
if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
|
||||
// case was closed. Remove listeners so that we don't get called with a stale case handle
|
||||
if (evt.getNewValue() == null) {
|
||||
removeNotify();
|
||||
}
|
||||
} else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
|
||||
|| eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
|
||||
/**
|
||||
* This is a stop gap measure until a different way of handling
|
||||
* the closing of cases is worked out. Currently, remote events
|
||||
* may be received for a case that is already closed.
|
||||
*/
|
||||
try {
|
||||
Case.getCurrentCaseThrows();
|
||||
refresh(false);
|
||||
} catch (NoCurrentCaseException notUsed) {
|
||||
/**
|
||||
* Case is closed, do nothing.
|
||||
*/
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
protected void addNotify() {
|
||||
refreshThrottler.registerForIngestModuleEvents();
|
||||
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl);
|
||||
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void removeNotify() {
|
||||
refreshThrottler.unregisterEventListener();
|
||||
IngestManager.getInstance().removeIngestJobEventListener(pcl);
|
||||
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
|
||||
typeNodeMap.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean createKeys(List<TypeNodeKey> list) {
|
||||
try {
|
||||
// Get all types in use
|
||||
SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
|
||||
List<BlackboardArtifact.Type> types = (this.filteringDSObjId > 0)
|
||||
? skCase.getBlackboard().getArtifactTypesInUse(this.filteringDSObjId)
|
||||
: skCase.getArtifactTypesInUse();
|
||||
|
||||
List<TypeNodeKey> allKeysSorted = types.stream()
|
||||
// filter types by category and ensure they are not in the list of ignored types
|
||||
.filter(tp -> category.equals(tp.getCategory()) && !IGNORED_TYPES.contains(tp))
|
||||
.map(tp -> {
|
||||
// if typeNodeMap already contains key, update the relevant node and return the node
|
||||
if (typeNodeMap.containsKey(tp)) {
|
||||
TypeNodeKey typeKey = typeNodeMap.get(tp);
|
||||
typeKey.getNode().updateDisplayName();
|
||||
return typeKey;
|
||||
} else {
|
||||
// if key is not in map, create the type key and add to map
|
||||
TypeNodeKey newTypeKey = getTypeKey(tp, skCase, filteringDSObjId);
|
||||
for (BlackboardArtifact.Type recordType : newTypeKey.getApplicableTypes()) {
|
||||
typeNodeMap.put(recordType, newTypeKey);
|
||||
}
|
||||
return newTypeKey;
|
||||
}
|
||||
})
|
||||
// ensure record is returned
|
||||
.filter(record -> record != null)
|
||||
// there are potentially multiple types that apply to the same node (i.e. Interesting Files / Artifacts)
|
||||
// ensure the keys are distinct
|
||||
.distinct()
|
||||
// sort by display name
|
||||
.sorted((a, b) -> {
|
||||
String aSafe = (a.getNode() == null || a.getNode().getDisplayName() == null) ? "" : a.getNode().getDisplayName();
|
||||
String bSafe = (b.getNode() == null || b.getNode().getDisplayName() == null) ? "" : b.getNode().getDisplayName();
|
||||
return aSafe.compareToIgnoreCase(bSafe);
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
list.addAll(allKeysSorted);
|
||||
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.WARNING, "Trying to access case when no case is open.", ex); //NON-NLS
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Error getting list of artifacts in use: " + ex.getLocalizedMessage()); //NON-NLS
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node createNodeForKey(TypeNodeKey key) {
|
||||
return key.getNode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh() {
|
||||
refresh(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRefreshRequired(PropertyChangeEvent evt) {
|
||||
String eventType = evt.getPropertyName();
|
||||
if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
|
||||
/**
|
||||
* This is a stop gap measure until a different way of handling
|
||||
* the closing of cases is worked out. Currently, remote events
|
||||
* may be received for a case that is already closed.
|
||||
*/
|
||||
try {
|
||||
Case.getCurrentCaseThrows();
|
||||
/**
|
||||
* Due to some unresolved issues with how cases are closed,
|
||||
* it is possible for the event to have a null oldValue if
|
||||
* the event is a remote event.
|
||||
*/
|
||||
final ModuleDataEvent event = (ModuleDataEvent) evt.getOldValue();
|
||||
if (null != event && category.equals(event.getBlackboardArtifactType().getCategory())
|
||||
&& !(IGNORED_TYPES.contains(event.getBlackboardArtifactType()))) {
|
||||
return true;
|
||||
}
|
||||
} catch (NoCurrentCaseException notUsed) {
|
||||
/**
|
||||
* Case is closed, do nothing.
|
||||
*/
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract class for type(s) nodes. This class allows for displaying a
|
||||
* count artifacts with the type(s) associated with this node.
|
||||
*/
|
||||
public static abstract class UpdatableCountTypeNode extends DisplayableItemNode {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(UpdatableCountTypeNode.class.getName());
|
||||
|
||||
private final Set<BlackboardArtifact.Type> types;
|
||||
private final long filteringDSObjId;
|
||||
private long childCount = 0;
|
||||
private final String baseName;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param children The Children to associated with this node.
|
||||
* @param lookup The Lookup to use with this name.
|
||||
* @param baseName The display name. The Node.displayName will
|
||||
* be of format "[baseName] ([count])".
|
||||
* @param filteringDSObjId The data source object id to use for
|
||||
* filtering. If id is less than or equal to 0,
|
||||
* no filtering will occur.
|
||||
* @param types The types associated with this type node.
|
||||
*/
|
||||
public UpdatableCountTypeNode(Children children, Lookup lookup, String baseName,
|
||||
long filteringDSObjId, BlackboardArtifact.Type... types) {
|
||||
|
||||
super(children, lookup);
|
||||
this.types = Stream.of(types).collect(Collectors.toSet());
|
||||
this.filteringDSObjId = filteringDSObjId;
|
||||
this.baseName = baseName;
|
||||
updateDisplayName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the count of artifacts associated with these type(s).
|
||||
*
|
||||
* @return The count of artifacts associated with these type(s).
|
||||
*/
|
||||
protected long getChildCount() {
|
||||
return this.childCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the count to be displayed from the case.
|
||||
*
|
||||
* @param skCase The relevant SleuthkitCase.
|
||||
*
|
||||
* @return The count to be displayed.
|
||||
*
|
||||
* @throws TskCoreException
|
||||
*/
|
||||
protected long fetchChildCount(SleuthkitCase skCase) throws TskCoreException {
|
||||
int count = 0;
|
||||
for (BlackboardArtifact.Type type : this.types) {
|
||||
if (filteringDSObjId > 0) {
|
||||
count += skCase.getBlackboard().getArtifactsCount(type.getTypeID(), filteringDSObjId);
|
||||
} else {
|
||||
count += skCase.getBlackboardArtifactsTypeCount(type.getTypeID());
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* When this method is called, the count to be displayed will be
|
||||
* updated.
|
||||
*/
|
||||
void updateDisplayName() {
|
||||
try {
|
||||
SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
|
||||
this.childCount = fetchChildCount(skCase);
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.WARNING, "Error fetching data when case closed.", ex);
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.WARNING, "Error getting child count", ex); //NON-NLS
|
||||
}
|
||||
super.setDisplayName(this.baseName + " \u200E(\u200E" + this.childCount + ")\u200E");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Default node encapsulating a blackboard artifact type. This is used on
|
||||
* the left-hand navigation side of the Autopsy UI as the parent node for
|
||||
* all of the artifacts of a given type. Its children will be
|
||||
* BlackboardArtifactNode objects.
|
||||
*/
|
||||
static class TypeNode extends UpdatableCountTypeNode {
|
||||
|
||||
private final BlackboardArtifact.Type type;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param type The blackboard artifact type for this node.
|
||||
* @param filteringDSObjId The data source object id to use for
|
||||
* filtering. If id is less than or equal to 0,
|
||||
* no filtering will occur.
|
||||
*/
|
||||
TypeNode(BlackboardArtifact.Type type, long filteringDSObjId) {
|
||||
super(Children.create(new ArtifactFactory(type, filteringDSObjId), true),
|
||||
Lookups.singleton(type.getDisplayName()),
|
||||
type.getDisplayName(),
|
||||
filteringDSObjId,
|
||||
type);
|
||||
|
||||
super.setName(type.getTypeName());
|
||||
this.type = type;
|
||||
String iconPath = IconsUtil.getIconFilePath(type.getTypeID());
|
||||
setIconBaseWithExtension(iconPath != null && iconPath.charAt(0) == '/' ? iconPath.substring(1) : iconPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Sheet createSheet() {
|
||||
Sheet sheet = super.createSheet();
|
||||
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
|
||||
if (sheetSet == null) {
|
||||
sheetSet = Sheet.createPropertiesSet();
|
||||
sheet.put(sheetSet);
|
||||
}
|
||||
|
||||
sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.artType.name"),
|
||||
NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.artType.displayName"),
|
||||
NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.artType.desc"),
|
||||
type.getDisplayName()));
|
||||
|
||||
sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.childCnt.name"),
|
||||
NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.childCnt.displayName"),
|
||||
NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.childCnt.desc"),
|
||||
getChildCount()));
|
||||
|
||||
return sheet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLeafTypeNode() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getItemType() {
|
||||
return getClass().getName() + type.getDisplayName();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates children for a given artifact type
|
||||
*/
|
||||
private static class ArtifactFactory extends BaseChildFactory<BlackboardArtifact> implements RefreshThrottler.Refresher {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(ArtifactFactory.class.getName());
|
||||
private final BlackboardArtifact.Type type;
|
||||
|
||||
/**
|
||||
* RefreshThrottler is used to limit the number of refreshes performed
|
||||
* when CONTENT_CHANGED and DATA_ADDED ingest module events are
|
||||
* received.
|
||||
*/
|
||||
private final RefreshThrottler refreshThrottler = new RefreshThrottler(this);
|
||||
private final long filteringDSObjId;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param type The blackboard artifact type for this node.
|
||||
* @param filteringDSObjId The data source object id to use for
|
||||
* filtering. If id is less than or equal to 0,
|
||||
* no filtering will occur.
|
||||
*/
|
||||
ArtifactFactory(BlackboardArtifact.Type type, long filteringDSObjId) {
|
||||
super(type.getTypeName());
|
||||
this.type = type;
|
||||
this.filteringDSObjId = filteringDSObjId;
|
||||
}
|
||||
|
||||
private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> {
|
||||
String eventType = evt.getPropertyName();
|
||||
if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
|
||||
|| eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
|
||||
/**
|
||||
* Checking for a current case is a stop gap measure until a
|
||||
* different way of handling the closing of cases is worked out.
|
||||
* Currently, remote events may be received for a case that is
|
||||
* already closed.
|
||||
*/
|
||||
try {
|
||||
Case.getCurrentCaseThrows();
|
||||
refresh(false);
|
||||
} catch (NoCurrentCaseException notUsed) {
|
||||
/**
|
||||
* Case is closed, do nothing.
|
||||
*/
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
protected void onAdd() {
|
||||
refreshThrottler.registerForIngestModuleEvents();
|
||||
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRemove() {
|
||||
refreshThrottler.unregisterEventListener();
|
||||
IngestManager.getInstance().removeIngestJobEventListener(pcl);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node createNodeForKey(BlackboardArtifact key) {
|
||||
return new BlackboardArtifactNode(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<BlackboardArtifact> makeKeys() {
|
||||
try {
|
||||
List<BlackboardArtifact> arts;
|
||||
arts = (filteringDSObjId > 0)
|
||||
? Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard().getArtifacts(type.getTypeID(), filteringDSObjId)
|
||||
: Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboardArtifacts(type.getTypeID());
|
||||
|
||||
for (BlackboardArtifact art : arts) {
|
||||
//Cache attributes while we are off the EDT.
|
||||
//See JIRA-5969
|
||||
art.getAttributes();
|
||||
}
|
||||
return arts;
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.WARNING, "Trying to access case when no case is open.", ex); //NON-NLS
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Couldn't get blackboard artifacts from database", ex); //NON-NLS
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh() {
|
||||
refresh(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRefreshRequired(PropertyChangeEvent evt) {
|
||||
String eventType = evt.getPropertyName();
|
||||
if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
|
||||
|
||||
/**
|
||||
* Checking for a current case is a stop gap measure until a
|
||||
* different way of handling the closing of cases is worked out.
|
||||
* Currently, remote events may be received for a case that is
|
||||
* already closed.
|
||||
*/
|
||||
try {
|
||||
Case.getCurrentCaseThrows();
|
||||
/**
|
||||
* Even with the check above, it is still possible that the
|
||||
* case will be closed in a different thread before this
|
||||
* code executes. If that happens, it is possible for the
|
||||
* event to have a null oldValue.
|
||||
*/
|
||||
final ModuleDataEvent event = (ModuleDataEvent) evt.getOldValue();
|
||||
if (null != event && event.getBlackboardArtifactType().equals(type)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
} catch (NoCurrentCaseException notUsed) {
|
||||
/**
|
||||
* Case is closed, do nothing.
|
||||
*/
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -53,8 +53,6 @@ public interface AutopsyItemVisitor<T> {
|
||||
|
||||
T visit(FileSize.FileSizeFilter fsf);
|
||||
|
||||
T visit(ExtractedContent ec);
|
||||
|
||||
T visit(KeywordHits kh);
|
||||
|
||||
T visit(HashsetHits hh);
|
||||
@ -63,8 +61,6 @@ public interface AutopsyItemVisitor<T> {
|
||||
|
||||
T visit(InterestingHits ih);
|
||||
|
||||
T visit(Results r);
|
||||
|
||||
T visit(Tags tagsNodeKey);
|
||||
|
||||
T visit(Reports reportsItem);
|
||||
@ -85,15 +81,15 @@ public interface AutopsyItemVisitor<T> {
|
||||
|
||||
T visit(DataSourcesByType aThis);
|
||||
|
||||
T visit(AnalysisResults aThis);
|
||||
|
||||
T visit(DataArtifacts aThis);
|
||||
|
||||
|
||||
static abstract public class Default<T> implements AutopsyItemVisitor<T> {
|
||||
|
||||
protected abstract T defaultVisit(AutopsyVisitableItem ec);
|
||||
|
||||
@Override
|
||||
public T visit(ExtractedContent ec) {
|
||||
return defaultVisit(ec);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T visit(FileTypesByExtension sf) {
|
||||
return defaultVisit(sf);
|
||||
@ -199,11 +195,6 @@ public interface AutopsyItemVisitor<T> {
|
||||
return defaultVisit(personGrouping);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T visit(Results r) {
|
||||
return defaultVisit(r);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T visit(FileTypes ft) {
|
||||
return defaultVisit(ft);
|
||||
@ -233,5 +224,15 @@ public interface AutopsyItemVisitor<T> {
|
||||
public T visit(DataSourcesByType dataSourceHosts) {
|
||||
return defaultVisit(dataSourceHosts);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T visit(DataArtifacts aThis) {
|
||||
return defaultVisit(aThis);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T visit(AnalysisResults aThis) {
|
||||
return defaultVisit(aThis);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -111,24 +111,27 @@ public final class AutopsyTreeChildFactory extends ChildFactory.Detachable<Objec
|
||||
if (CollectionUtils.isNotEmpty(personManager.getHostsForPerson(null))) {
|
||||
list.add(new PersonGrouping(null));
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
// otherwise, just show host level
|
||||
tskCase.getHostManager().getHosts().stream()
|
||||
tskCase.getHostManager().getAllHosts().stream()
|
||||
.map(HostGrouping::new)
|
||||
.sorted()
|
||||
.forEach(list::add);
|
||||
return true;
|
||||
|
||||
}
|
||||
list.add(new Reports());
|
||||
return true;
|
||||
} else {
|
||||
// data source by type view
|
||||
List<AutopsyVisitableItem> keys = new ArrayList<>(Arrays.asList(
|
||||
new DataSourcesByType(),
|
||||
new Views(tskCase),
|
||||
new Results(tskCase),
|
||||
new Views(Case.getCurrentCaseThrows().getSleuthkitCase()),
|
||||
new DataArtifacts(),
|
||||
new AnalysisResults(),
|
||||
new OsAccounts(Case.getCurrentCaseThrows().getSleuthkitCase()),
|
||||
new Tags(),
|
||||
new Reports()));
|
||||
new Reports()
|
||||
));
|
||||
|
||||
list.addAll(keys);
|
||||
}
|
||||
|
@ -171,7 +171,7 @@ TagNameNode.createSheet.name.displayName=Name
|
||||
TagsNode.displayName.text=Tags
|
||||
TagsNode.createSheet.name.name=Name
|
||||
TagsNode.createSheet.name.displayName=Name
|
||||
ViewsNode.name.text=Views
|
||||
ViewsNode.name.text=File Views
|
||||
ViewsNode.createSheet.name.name=Name
|
||||
ViewsNode.createSheet.name.displayName=Name
|
||||
ViewsNode.createSheet.name.desc=no description
|
||||
|
@ -39,6 +39,7 @@ AbstractAbstractFileNode.useridColLbl=UserID
|
||||
AbstractContentNode.nodescription=no description
|
||||
AbstractContentNode.valueLoading=value loading
|
||||
AbstractFsContentNode.noDesc.text=no description
|
||||
AnalysisResults_name=Analysis Results
|
||||
ArtifactStringContent.attrsTableHeader.sources=Source(s)
|
||||
ArtifactStringContent.attrsTableHeader.type=Type
|
||||
ArtifactStringContent.attrsTableHeader.value=Value
|
||||
@ -93,6 +94,7 @@ ContentTagNode.createSheet.artifactMD5.displayName=MD5 Hash
|
||||
ContentTagNode.createSheet.artifactMD5.name=MD5 Hash
|
||||
ContentTagNode.createSheet.origFileName=Original Name
|
||||
ContentTagNode.createSheet.userName.text=User Name
|
||||
DataArtifacts_name=Data Artifacts
|
||||
DataSourcesHostsNode_name=Data Sources
|
||||
DeletedContent.allDelFilter.text=All
|
||||
DeletedContent.createSheet.filterType.desc=no description
|
||||
@ -358,10 +360,6 @@ ReportNode.reportNameProperty.name=Report Name
|
||||
ReportNode.reportNameProperty.displayName=Report Name
|
||||
ReportNode.reportNameProperty.desc=Name of the report
|
||||
ReportsListNode.displayName=Reports
|
||||
ResultsNode.createSheet.name.desc=no description
|
||||
ResultsNode.createSheet.name.displayName=Name
|
||||
ResultsNode.createSheet.name.name=Name
|
||||
ResultsNode.name.text=Results
|
||||
SlackFileNode.getActions.viewInNewWin.text=View in New Window
|
||||
SlackFileNode.getActions.viewFileInDir.text=View File in Directory
|
||||
SpecialDirectoryNode.getActions.viewInNewWin.text=View in New Window
|
||||
@ -376,7 +374,11 @@ TagNode.propertySheet.origNameDisplayName=Original Name
|
||||
TagsNode.displayName.text=Tags
|
||||
TagsNode.createSheet.name.name=Name
|
||||
TagsNode.createSheet.name.displayName=Name
|
||||
ViewsNode.name.text=Views
|
||||
UnsupportedContentNode.createSheet.name.desc=no description
|
||||
UnsupportedContentNode.createSheet.name.displayName=Name
|
||||
UnsupportedContentNode.createSheet.name.name=Name
|
||||
UnsupportedContentNode.displayName=Unsupported Content
|
||||
ViewsNode.name.text=File Views
|
||||
ViewsNode.createSheet.name.name=Name
|
||||
ViewsNode.createSheet.name.displayName=Name
|
||||
ViewsNode.createSheet.name.desc=no description
|
||||
|
@ -18,6 +18,8 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.datamodel;
|
||||
|
||||
import org.sleuthkit.autopsy.datamodel.OsAccounts.OsAccountNode;
|
||||
|
||||
/**
|
||||
* Visitor Pattern interface that goes over Content nodes in the data source
|
||||
* area of the tree.
|
||||
@ -50,6 +52,9 @@ interface ContentNodeVisitor<T> {
|
||||
|
||||
T visit(BlackboardArtifactNode bban);
|
||||
|
||||
T visit(UnsupportedContentNode ucn);
|
||||
|
||||
T visit(OsAccountNode bban);
|
||||
|
||||
/**
|
||||
* Visitor with an implementable default behavior for all types. Override
|
||||
@ -122,5 +127,15 @@ interface ContentNodeVisitor<T> {
|
||||
public T visit(BlackboardArtifactNode bban) {
|
||||
return defaultVisit(bban);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T visit(UnsupportedContentNode ucn) {
|
||||
return defaultVisit(ucn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T visit(OsAccountNode bban) {
|
||||
return defaultVisit(bban);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ import org.sleuthkit.datamodel.Pool;
|
||||
import org.sleuthkit.datamodel.SlackFile;
|
||||
import org.sleuthkit.datamodel.SleuthkitItemVisitor;
|
||||
import org.sleuthkit.datamodel.SleuthkitVisitableItem;
|
||||
import org.sleuthkit.datamodel.UnsupportedContent;
|
||||
import org.sleuthkit.datamodel.VirtualDirectory;
|
||||
import org.sleuthkit.datamodel.Volume;
|
||||
|
||||
@ -99,6 +100,11 @@ public class CreateSleuthkitNodeVisitor extends SleuthkitItemVisitor.Default<Abs
|
||||
public AbstractContentNode<? extends Content> visit(BlackboardArtifact art) {
|
||||
return new BlackboardArtifactNode(art);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractContentNode<? extends Content> visit(UnsupportedContent uc) {
|
||||
return new UnsupportedContentNode(uc);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractContentNode<? extends Content> defaultVisit(SleuthkitVisitableItem di) {
|
||||
|
95
Core/src/org/sleuthkit/autopsy/datamodel/DataArtifacts.java
Normal file
95
Core/src/org/sleuthkit/autopsy/datamodel/DataArtifacts.java
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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.datamodel;
|
||||
|
||||
import org.openide.nodes.Children;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
|
||||
/**
|
||||
* Analysis Results node support.
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"DataArtifacts_name=Data Artifacts",})
|
||||
public class DataArtifacts implements AutopsyVisitableItem {
|
||||
|
||||
/**
|
||||
* Returns the name of this node that is the key in the children object.
|
||||
*
|
||||
* @return The name of this node that is the key in the children object.
|
||||
*/
|
||||
public static String getName() {
|
||||
return Bundle.DataArtifacts_name();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parent node of all data artifacts.
|
||||
*/
|
||||
static class RootNode extends Artifacts.BaseArtifactNode {
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param filteringDSObjId The data source object id for which results
|
||||
* should be filtered. If no filtering should
|
||||
* occur, this number should be less than or
|
||||
* equal to 0.
|
||||
*/
|
||||
RootNode(long filteringDSObjId) {
|
||||
super(Children.create(new Artifacts.TypeFactory(BlackboardArtifact.Category.DATA_ARTIFACT, filteringDSObjId), true),
|
||||
"org/sleuthkit/autopsy/images/extracted_content.png",
|
||||
DataArtifacts.getName(),
|
||||
DataArtifacts.getName());
|
||||
}
|
||||
}
|
||||
|
||||
private final long datasourceObjId;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*/
|
||||
public DataArtifacts() {
|
||||
this(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param dsObjId The data source object id.
|
||||
*/
|
||||
public DataArtifacts(long dsObjId) {
|
||||
this.datasourceObjId = dsObjId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T accept(AutopsyItemVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not there is a data source object for which results
|
||||
* should be filtered.
|
||||
*
|
||||
* @return Whether or not there is a data source object for which results
|
||||
* should be filtered.
|
||||
*/
|
||||
Long getFilteringDataSourceObjId() {
|
||||
return datasourceObjId;
|
||||
}
|
||||
}
|
@ -35,6 +35,7 @@ import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction;
|
||||
import org.sleuthkit.autopsy.actions.ReplaceBlackboardArtifactTagAction;
|
||||
import org.sleuthkit.autopsy.actions.ReplaceContentTagAction;
|
||||
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
|
||||
import org.sleuthkit.autopsy.datamodel.OsAccounts.OsAccountNode;
|
||||
import org.sleuthkit.autopsy.datamodel.Reports.ReportNode;
|
||||
import org.sleuthkit.autopsy.directorytree.ExportCSVAction;
|
||||
import org.sleuthkit.autopsy.directorytree.ExternalViewerAction;
|
||||
@ -53,6 +54,7 @@ import org.sleuthkit.datamodel.File;
|
||||
import org.sleuthkit.datamodel.LayoutFile;
|
||||
import org.sleuthkit.datamodel.LocalFile;
|
||||
import org.sleuthkit.datamodel.LocalDirectory;
|
||||
import org.sleuthkit.datamodel.OsAccount;
|
||||
import org.sleuthkit.datamodel.Report;
|
||||
import org.sleuthkit.datamodel.SlackFile;
|
||||
import org.sleuthkit.datamodel.VirtualDirectory;
|
||||
@ -446,6 +448,18 @@ public class DataModelActionsFactory {
|
||||
actionsList.addAll(ContextMenuExtensionPoint.getActions());
|
||||
return actionsList;
|
||||
}
|
||||
|
||||
public static List<Action> getActions(OsAccount osAccount) {
|
||||
List<Action> actionsList = new ArrayList<>();
|
||||
|
||||
OsAccountNode node = new OsAccountNode(osAccount);
|
||||
actionsList.add(null); // creates a menu separator
|
||||
actionsList.add(new NewWindowViewAction(VIEW_IN_NEW_WINDOW, node));
|
||||
actionsList.add(null);
|
||||
actionsList.add(ExportCSVAction.getInstance());
|
||||
actionsList.addAll(ContextMenuExtensionPoint.getActions());
|
||||
return actionsList;
|
||||
}
|
||||
|
||||
public static List<Action> getActions(Content content, boolean isArtifactSource) {
|
||||
if (content instanceof File) {
|
||||
@ -464,7 +478,9 @@ public class DataModelActionsFactory {
|
||||
return getActions((SlackFile) content, isArtifactSource);
|
||||
} else if (content instanceof Report) {
|
||||
return getActions((Report) content, isArtifactSource);
|
||||
} else {
|
||||
} else if (content instanceof OsAccount) {
|
||||
return getActions((OsAccount) content, isArtifactSource);
|
||||
}else {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
@ -30,24 +30,23 @@ import org.sleuthkit.datamodel.DataSource;
|
||||
import org.sleuthkit.datamodel.Image;
|
||||
import org.sleuthkit.datamodel.LocalFilesDataSource;
|
||||
|
||||
|
||||
/**
|
||||
* Data source grouping node - an optional grouping node in the data tree view
|
||||
*
|
||||
* Data source grouping node - an optional grouping node in the data tree view
|
||||
*
|
||||
*/
|
||||
class DataSourceGroupingNode extends DisplayableItemNode {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(DataSourceGroupingNode.class.getName());
|
||||
|
||||
/**
|
||||
* Creates a data source grouping node for the given data source.
|
||||
*
|
||||
* @param dataSource specifies the data source
|
||||
* Creates a data source grouping node for the given data source.
|
||||
*
|
||||
* @param dataSource specifies the data source
|
||||
*/
|
||||
DataSourceGroupingNode(DataSource dataSource) {
|
||||
|
||||
super (Optional.ofNullable(createDSGroupingNodeChildren(dataSource))
|
||||
.orElse(new RootContentChildren(Arrays.asList(Collections.EMPTY_LIST))),
|
||||
super(Optional.ofNullable(createDSGroupingNodeChildren(dataSource))
|
||||
.orElse(new RootContentChildren(Arrays.asList(Collections.EMPTY_LIST))),
|
||||
Lookups.singleton(dataSource));
|
||||
|
||||
if (dataSource instanceof Image) {
|
||||
@ -70,7 +69,7 @@ class DataSourceGroupingNode extends DisplayableItemNode {
|
||||
public boolean isLeafTypeNode() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private static RootContentChildren createDSGroupingNodeChildren(DataSource dataSource) {
|
||||
|
||||
long dsObjId = dataSource.getId();
|
||||
@ -78,11 +77,13 @@ class DataSourceGroupingNode extends DisplayableItemNode {
|
||||
return new RootContentChildren(Arrays.asList(
|
||||
new DataSources(dsObjId),
|
||||
new Views(Case.getCurrentCaseThrows().getSleuthkitCase(), dsObjId),
|
||||
new Results(Case.getCurrentCaseThrows().getSleuthkitCase(), dsObjId),
|
||||
new Tags(dsObjId) )
|
||||
|
||||
);
|
||||
|
||||
new DataArtifacts(dsObjId),
|
||||
new AnalysisResults(dsObjId),
|
||||
new OsAccounts(Case.getCurrentCaseThrows().getSleuthkitCase(), dsObjId),
|
||||
new Tags(dsObjId),
|
||||
new Reports()
|
||||
));
|
||||
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
logger.log(Level.SEVERE, "Error getting open case.", ex); //NON-NLS
|
||||
return null;
|
||||
|
@ -22,7 +22,9 @@ import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import org.openide.nodes.ChildFactory;
|
||||
import org.openide.nodes.Children;
|
||||
import org.openide.nodes.Node;
|
||||
@ -48,13 +50,23 @@ public class DataSourcesByTypeNode extends DisplayableItemNode {
|
||||
*/
|
||||
public static class DataSourcesByTypeChildren extends ChildFactory.Detachable<HostDataSources> {
|
||||
|
||||
private static final Set<Case.Events> UPDATE_EVTS = EnumSet.of(
|
||||
Case.Events.DATA_SOURCE_ADDED,
|
||||
Case.Events.HOSTS_ADDED,
|
||||
Case.Events.HOSTS_DELETED,
|
||||
Case.Events.HOSTS_CHANGED);
|
||||
|
||||
private static final Set<String> UPDATE_EVT_STRS = UPDATE_EVTS.stream()
|
||||
.map(evt -> evt.name())
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
private static final Logger logger = Logger.getLogger(DataSourcesByTypeChildren.class.getName());
|
||||
|
||||
private final PropertyChangeListener pcl = new PropertyChangeListener() {
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
String eventType = evt.getPropertyName();
|
||||
if (eventType.equals(Case.Events.DATA_SOURCE_ADDED.toString())) {
|
||||
if (UPDATE_EVT_STRS.contains(eventType)) {
|
||||
refresh(true);
|
||||
}
|
||||
}
|
||||
@ -62,18 +74,18 @@ public class DataSourcesByTypeNode extends DisplayableItemNode {
|
||||
|
||||
@Override
|
||||
protected void addNotify() {
|
||||
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED), pcl);
|
||||
Case.addEventTypeSubscriber(UPDATE_EVTS, pcl);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void removeNotify() {
|
||||
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED), pcl);
|
||||
Case.removeEventTypeSubscriber(UPDATE_EVTS, pcl);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean createKeys(List<HostDataSources> toPopulate) {
|
||||
try {
|
||||
Case.getCurrentCaseThrows().getSleuthkitCase().getHostManager().getHosts().stream()
|
||||
Case.getCurrentCaseThrows().getSleuthkitCase().getHostManager().getAllHosts().stream()
|
||||
.map(HostDataSources::new)
|
||||
.sorted()
|
||||
.forEach(toPopulate::add);
|
||||
@ -91,7 +103,7 @@ public class DataSourcesByTypeNode extends DisplayableItemNode {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static final String NAME = Bundle.DataSourcesHostsNode_name();
|
||||
|
||||
/**
|
||||
@ -100,7 +112,7 @@ public class DataSourcesByTypeNode extends DisplayableItemNode {
|
||||
public static String getNameIdentifier() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*/
|
||||
|
@ -88,16 +88,11 @@ public interface DisplayableItemNodeVisitor<T> {
|
||||
|
||||
T visit(RecentFilesFilterNode rffn);
|
||||
|
||||
/*
|
||||
* Extracted Results Area
|
||||
*/
|
||||
T visit(ResultsNode rn);
|
||||
|
||||
T visit(BlackboardArtifactNode ban);
|
||||
|
||||
T visit(ExtractedContent.TypeNode atn);
|
||||
T visit(Artifacts.TypeNode atn);
|
||||
|
||||
T visit(ExtractedContent.RootNode ecn);
|
||||
T visit(Artifacts.BaseArtifactNode ecn);
|
||||
|
||||
T visit(KeywordHits.RootNode khrn);
|
||||
|
||||
@ -202,6 +197,11 @@ public interface DisplayableItemNodeVisitor<T> {
|
||||
T visit(HostNode node);
|
||||
|
||||
T visit(DataSourcesByTypeNode node);
|
||||
|
||||
/*
|
||||
* Unsupported node
|
||||
*/
|
||||
T visit(UnsupportedContentNode ucn);
|
||||
|
||||
/**
|
||||
* Visitor with an implementable default behavior for all types. Override
|
||||
@ -296,12 +296,12 @@ public interface DisplayableItemNodeVisitor<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public T visit(ExtractedContent.TypeNode atn) {
|
||||
public T visit(Artifacts.TypeNode atn) {
|
||||
return defaultVisit(atn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T visit(ExtractedContent.RootNode ecn) {
|
||||
public T visit(Artifacts.BaseArtifactNode ecn) {
|
||||
return defaultVisit(ecn);
|
||||
}
|
||||
|
||||
@ -400,11 +400,6 @@ public interface DisplayableItemNodeVisitor<T> {
|
||||
return defaultVisit(dataSourceGroupingNode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T visit(ResultsNode rn) {
|
||||
return defaultVisit(rn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T visit(FileTypesNode ft) {
|
||||
return defaultVisit(ft);
|
||||
@ -574,5 +569,10 @@ public interface DisplayableItemNodeVisitor<T> {
|
||||
public T visit(PersonGroupingNode node) {
|
||||
return defaultVisit(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T visit(UnsupportedContentNode node) {
|
||||
return defaultVisit(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -44,10 +44,12 @@ import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_EMAIL_MSG;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.autopsy.datamodel.Artifacts.UpdatableCountTypeNode;
|
||||
|
||||
/**
|
||||
* Support for TSK_EMAIL_MSG nodes and displaying emails in the directory tree.
|
||||
@ -57,8 +59,7 @@ import org.sleuthkit.datamodel.TskCoreException;
|
||||
*/
|
||||
public class EmailExtracted implements AutopsyVisitableItem {
|
||||
|
||||
private static final String LABEL_NAME = BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getLabel();
|
||||
private static final String DISPLAY_NAME = BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getDisplayName();
|
||||
private static final String LABEL_NAME = BlackboardArtifact.Type.TSK_EMAIL_MSG.getTypeName();
|
||||
private static final Logger logger = Logger.getLogger(EmailExtracted.class.getName());
|
||||
private static final String MAIL_ACCOUNT = NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.mailAccount.text");
|
||||
private static final String MAIL_FOLDER = NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.mailFolder.text");
|
||||
@ -77,7 +78,7 @@ public class EmailExtracted implements AutopsyVisitableItem {
|
||||
*/
|
||||
public static final Map<String, String> parsePath(String path) {
|
||||
Map<String, String> parsed = new HashMap<>();
|
||||
String[] split = path.split(MAIL_PATH_SEPARATOR);
|
||||
String[] split = path == null ? new String[0] : path.split(MAIL_PATH_SEPARATOR);
|
||||
if (split.length < 4) {
|
||||
parsed.put(MAIL_ACCOUNT, NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.defaultAcct.text"));
|
||||
parsed.put(MAIL_FOLDER, NbBundle.getMessage(EmailExtracted.class, "EmailExtracted.defaultFolder.text"));
|
||||
@ -147,50 +148,52 @@ public class EmailExtracted implements AutopsyVisitableItem {
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public void update() {
|
||||
synchronized (accounts) {
|
||||
accounts.clear();
|
||||
}
|
||||
// clear cache if no case
|
||||
if (skCase == null) {
|
||||
synchronized (accounts) {
|
||||
accounts.clear();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int artId = BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID();
|
||||
// get artifact id and path (if present) of all email artifacts
|
||||
int emailArtifactId = BlackboardArtifact.Type.TSK_EMAIL_MSG.getTypeID();
|
||||
int pathAttrId = BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH.getTypeID();
|
||||
String query = "SELECT value_text,blackboard_attributes.artifact_id,attribute_type_id " //NON-NLS
|
||||
+ "FROM blackboard_attributes,blackboard_artifacts WHERE " //NON-NLS
|
||||
+ "attribute_type_id=" + pathAttrId //NON-NLS
|
||||
+ " AND blackboard_attributes.artifact_id=blackboard_artifacts.artifact_id" //NON-NLS
|
||||
+ " AND blackboard_artifacts.artifact_type_id=" + artId; //NON-NLS
|
||||
if (filteringDSObjId > 0) {
|
||||
query += " AND blackboard_artifacts.data_source_obj_id = " + filteringDSObjId;
|
||||
}
|
||||
|
||||
String query = "SELECT \n"
|
||||
+ " art.artifact_id AS artifact_id,\n"
|
||||
+ " (SELECT value_text FROM blackboard_attributes attr\n"
|
||||
+ " WHERE attr.artifact_id = art.artifact_id AND attr.attribute_type_id = " + pathAttrId + "\n"
|
||||
+ " LIMIT 1) AS value_text\n"
|
||||
+ "FROM \n"
|
||||
+ " blackboard_artifacts art\n"
|
||||
+ " WHERE art.artifact_type_id = " + emailArtifactId + "\n"
|
||||
+ ((filteringDSObjId > 0) ? " AND art.data_source_obj_id = " + filteringDSObjId : "");
|
||||
|
||||
// form hierarchy of account -> folder -> account id
|
||||
Map<String, Map<String, List<Long>>> newMapping = new HashMap<>();
|
||||
|
||||
try (CaseDbQuery dbQuery = skCase.executeQuery(query)) {
|
||||
ResultSet resultSet = dbQuery.getResultSet();
|
||||
synchronized (accounts) {
|
||||
while (resultSet.next()) {
|
||||
final String path = resultSet.getString("value_text"); //NON-NLS
|
||||
final long artifactId = resultSet.getLong("artifact_id"); //NON-NLS
|
||||
final Map<String, String> parsedPath = parsePath(path);
|
||||
final String account = parsedPath.get(MAIL_ACCOUNT);
|
||||
final String folder = parsedPath.get(MAIL_FOLDER);
|
||||
while (resultSet.next()) {
|
||||
Long artifactId = resultSet.getLong("artifact_id");
|
||||
Map<String, String> accountFolderMap = parsePath(resultSet.getString("value_text"));
|
||||
String account = accountFolderMap.get(MAIL_ACCOUNT);
|
||||
String folder = accountFolderMap.get(MAIL_FOLDER);
|
||||
|
||||
Map<String, List<Long>> folders = accounts.get(account);
|
||||
if (folders == null) {
|
||||
folders = new LinkedHashMap<>();
|
||||
accounts.put(account, folders);
|
||||
}
|
||||
List<Long> messages = folders.get(folder);
|
||||
if (messages == null) {
|
||||
messages = new ArrayList<>();
|
||||
folders.put(folder, messages);
|
||||
}
|
||||
messages.add(artifactId);
|
||||
}
|
||||
Map<String, List<Long>> folders = newMapping.computeIfAbsent(account, (str) -> new LinkedHashMap<>());
|
||||
List<Long> messages = folders.computeIfAbsent(folder, (str) -> new ArrayList<>());
|
||||
messages.add(artifactId);
|
||||
}
|
||||
} catch (TskCoreException | SQLException ex) {
|
||||
logger.log(Level.WARNING, "Cannot initialize email extraction: ", ex); //NON-NLS
|
||||
}
|
||||
|
||||
synchronized (accounts) {
|
||||
accounts.clear();
|
||||
accounts.putAll(newMapping);
|
||||
}
|
||||
|
||||
setChanged();
|
||||
notifyObservers();
|
||||
}
|
||||
@ -200,12 +203,16 @@ public class EmailExtracted implements AutopsyVisitableItem {
|
||||
* Mail root node grouping all mail accounts, supports account-> folder
|
||||
* structure
|
||||
*/
|
||||
public class RootNode extends DisplayableItemNode {
|
||||
public class RootNode extends UpdatableCountTypeNode {
|
||||
|
||||
public RootNode() {
|
||||
super(Children.create(new AccountFactory(), true), Lookups.singleton(DISPLAY_NAME));
|
||||
super(Children.create(new AccountFactory(), true),
|
||||
Lookups.singleton(TSK_EMAIL_MSG.getDisplayName()),
|
||||
TSK_EMAIL_MSG.getDisplayName(),
|
||||
filteringDSObjId,
|
||||
TSK_EMAIL_MSG);
|
||||
//super(Children.create(new AccountFactory(), true), Lookups.singleton(DISPLAY_NAME));
|
||||
super.setName(LABEL_NAME);
|
||||
super.setDisplayName(DISPLAY_NAME);
|
||||
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/mail-icon-16.png"); //NON-NLS
|
||||
emailResults.update();
|
||||
}
|
||||
@ -272,7 +279,7 @@ public class EmailExtracted implements AutopsyVisitableItem {
|
||||
* for the event to have a null oldValue.
|
||||
*/
|
||||
ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
|
||||
if (null != eventData && eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()) {
|
||||
if (null != eventData && eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.Type.TSK_EMAIL_MSG.getTypeID()) {
|
||||
emailResults.update();
|
||||
}
|
||||
} catch (NoCurrentCaseException notUsed) {
|
||||
|
@ -1,492 +0,0 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-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.datamodel;
|
||||
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import org.openide.nodes.ChildFactory;
|
||||
import org.openide.nodes.Children;
|
||||
import org.openide.nodes.Node;
|
||||
import org.openide.nodes.Sheet;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.lookup.Lookups;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datamodel.utils.IconsUtil;
|
||||
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
|
||||
import org.sleuthkit.datamodel.Blackboard;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT;
|
||||
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT;
|
||||
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_TL_EVENT;
|
||||
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_DATA_SOURCE_USAGE;
|
||||
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG;
|
||||
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_GEN_INFO;
|
||||
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT;
|
||||
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT;
|
||||
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT;
|
||||
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT;
|
||||
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.autopsy.guiutils.RefreshThrottler;
|
||||
|
||||
/**
|
||||
* Parent of the "extracted content" artifacts to be displayed in the tree.
|
||||
* Other artifacts are displayed under other more specific parents.
|
||||
*/
|
||||
public class ExtractedContent implements AutopsyVisitableItem {
|
||||
|
||||
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED);
|
||||
public static final String NAME = NbBundle.getMessage(RootNode.class, "ExtractedContentNode.name.text");
|
||||
private final long filteringDSObjId; // 0 if not filtering/grouping by data source
|
||||
private SleuthkitCase skCase; // set to null after case has been closed
|
||||
private Blackboard blackboard;
|
||||
|
||||
/**
|
||||
* Constructs extracted content object
|
||||
*
|
||||
* @param skCase Case DB
|
||||
*/
|
||||
public ExtractedContent(SleuthkitCase skCase) {
|
||||
this(skCase, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs extracted content object
|
||||
*
|
||||
* @param skCase Case DB
|
||||
* @param objId Object id of the parent datasource
|
||||
*/
|
||||
public ExtractedContent(SleuthkitCase skCase, long objId) {
|
||||
this.skCase = skCase;
|
||||
this.filteringDSObjId = objId;
|
||||
this.blackboard = skCase.getBlackboard();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T accept(AutopsyItemVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
public SleuthkitCase getSleuthkitCase() {
|
||||
return skCase;
|
||||
}
|
||||
|
||||
public class RootNode extends DisplayableItemNode {
|
||||
|
||||
public RootNode(SleuthkitCase skCase) {
|
||||
super(Children.create(new TypeFactory(), true), Lookups.singleton(NAME));
|
||||
super.setName(NAME);
|
||||
super.setDisplayName(NAME);
|
||||
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/extracted_content.png"); //NON-NLS
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLeafTypeNode() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Sheet createSheet() {
|
||||
Sheet sheet = super.createSheet();
|
||||
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
|
||||
if (sheetSet == null) {
|
||||
sheetSet = Sheet.createPropertiesSet();
|
||||
sheet.put(sheetSet);
|
||||
}
|
||||
|
||||
sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ExtractedContentNode.createSheet.name.name"),
|
||||
NbBundle.getMessage(this.getClass(), "ExtractedContentNode.createSheet.name.displayName"),
|
||||
NbBundle.getMessage(this.getClass(), "ExtractedContentNode.createSheet.name.desc"),
|
||||
NAME));
|
||||
return sheet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getItemType() {
|
||||
return getClass().getName();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the children for the ExtractedContent area of the results tree.
|
||||
* This area has all of the blackboard artifacts that are not displayed in a
|
||||
* more specific form elsewhere in the tree.
|
||||
*/
|
||||
private class TypeFactory extends ChildFactory.Detachable<BlackboardArtifact.Type> implements RefreshThrottler.Refresher {
|
||||
|
||||
private final ArrayList<BlackboardArtifact.Type> doNotShow = new ArrayList<>();
|
||||
// maps the artifact type to its child node
|
||||
private final HashMap<BlackboardArtifact.Type, TypeNode> typeNodeList = new HashMap<>();
|
||||
|
||||
/**
|
||||
* RefreshThrottler is used to limit the number of refreshes performed
|
||||
* when CONTENT_CHANGED and DATA_ADDED ingest module events are
|
||||
* received.
|
||||
*/
|
||||
private final RefreshThrottler refreshThrottler = new RefreshThrottler(this);
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
TypeFactory() {
|
||||
super();
|
||||
|
||||
// these are shown in other parts of the UI
|
||||
doNotShow.add(new BlackboardArtifact.Type(TSK_GEN_INFO));
|
||||
doNotShow.add(new BlackboardArtifact.Type(TSK_EMAIL_MSG));
|
||||
doNotShow.add(new BlackboardArtifact.Type(TSK_HASHSET_HIT));
|
||||
doNotShow.add(new BlackboardArtifact.Type(TSK_KEYWORD_HIT));
|
||||
doNotShow.add(new BlackboardArtifact.Type(TSK_INTERESTING_FILE_HIT));
|
||||
doNotShow.add(new BlackboardArtifact.Type(TSK_INTERESTING_ARTIFACT_HIT));
|
||||
doNotShow.add(new BlackboardArtifact.Type(TSK_ACCOUNT));
|
||||
doNotShow.add(new BlackboardArtifact.Type(TSK_DATA_SOURCE_USAGE));
|
||||
doNotShow.add(new BlackboardArtifact.Type(TSK_DOWNLOAD_SOURCE));
|
||||
doNotShow.add(new BlackboardArtifact.Type(TSK_TL_EVENT));
|
||||
|
||||
//This is not meant to be shown in the UI at all. It is more of a meta artifact.
|
||||
doNotShow.add(new BlackboardArtifact.Type(TSK_ASSOCIATED_OBJECT));
|
||||
}
|
||||
|
||||
private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> {
|
||||
String eventType = evt.getPropertyName();
|
||||
if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
|
||||
// case was closed. Remove listeners so that we don't get called with a stale case handle
|
||||
if (evt.getNewValue() == null) {
|
||||
removeNotify();
|
||||
skCase = null;
|
||||
}
|
||||
} else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
|
||||
|| eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
|
||||
/**
|
||||
* This is a stop gap measure until a different way of handling
|
||||
* the closing of cases is worked out. Currently, remote events
|
||||
* may be received for a case that is already closed.
|
||||
*/
|
||||
try {
|
||||
Case.getCurrentCaseThrows();
|
||||
refresh(false);
|
||||
} catch (NoCurrentCaseException notUsed) {
|
||||
/**
|
||||
* Case is closed, do nothing.
|
||||
*/
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
protected void addNotify() {
|
||||
refreshThrottler.registerForIngestModuleEvents();
|
||||
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl);
|
||||
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void removeNotify() {
|
||||
refreshThrottler.unregisterEventListener();
|
||||
IngestManager.getInstance().removeIngestJobEventListener(pcl);
|
||||
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), pcl);
|
||||
typeNodeList.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean createKeys(List<BlackboardArtifact.Type> list) {
|
||||
if (skCase != null) {
|
||||
try {
|
||||
List<BlackboardArtifact.Type> types = (filteringDSObjId > 0)
|
||||
? blackboard.getArtifactTypesInUse(filteringDSObjId)
|
||||
: skCase.getArtifactTypesInUse();
|
||||
|
||||
types.removeAll(doNotShow);
|
||||
Collections.sort(types,
|
||||
new Comparator<BlackboardArtifact.Type>() {
|
||||
@Override
|
||||
public int compare(BlackboardArtifact.Type a, BlackboardArtifact.Type b) {
|
||||
return a.getDisplayName().compareTo(b.getDisplayName());
|
||||
}
|
||||
});
|
||||
list.addAll(types);
|
||||
|
||||
// the create node method will get called only for new types
|
||||
// refresh the counts if we already created them from a previous update
|
||||
for (BlackboardArtifact.Type art : types) {
|
||||
TypeNode node = typeNodeList.get(art);
|
||||
if (node != null) {
|
||||
node.updateDisplayName();
|
||||
}
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
Logger.getLogger(TypeFactory.class.getName()).log(Level.SEVERE, "Error getting list of artifacts in use: " + ex.getLocalizedMessage()); //NON-NLS
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node createNodeForKey(BlackboardArtifact.Type key) {
|
||||
TypeNode node = new TypeNode(key);
|
||||
typeNodeList.put(key, node);
|
||||
return node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh() {
|
||||
refresh(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRefreshRequired(PropertyChangeEvent evt) {
|
||||
String eventType = evt.getPropertyName();
|
||||
if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
|
||||
/**
|
||||
* This is a stop gap measure until a different way of handling
|
||||
* the closing of cases is worked out. Currently, remote events
|
||||
* may be received for a case that is already closed.
|
||||
*/
|
||||
try {
|
||||
Case.getCurrentCaseThrows();
|
||||
/**
|
||||
* Due to some unresolved issues with how cases are closed,
|
||||
* it is possible for the event to have a null oldValue if
|
||||
* the event is a remote event.
|
||||
*/
|
||||
final ModuleDataEvent event = (ModuleDataEvent) evt.getOldValue();
|
||||
if (null != event && !(this.doNotShow.contains(event.getBlackboardArtifactType()))) {
|
||||
return true;
|
||||
}
|
||||
} catch (NoCurrentCaseException notUsed) {
|
||||
/**
|
||||
* Case is closed, do nothing.
|
||||
*/
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Node encapsulating blackboard artifact type. This is used on the
|
||||
* left-hand navigation side of the Autopsy UI as the parent node for all of
|
||||
* the artifacts of a given type. Its children will be
|
||||
* BlackboardArtifactNode objects.
|
||||
*/
|
||||
public class TypeNode extends DisplayableItemNode {
|
||||
|
||||
private final BlackboardArtifact.Type type;
|
||||
private long childCount = 0;
|
||||
|
||||
TypeNode(BlackboardArtifact.Type type) {
|
||||
super(Children.create(new ArtifactFactory(type), true), Lookups.singleton(type.getDisplayName()));
|
||||
super.setName(type.getTypeName());
|
||||
this.type = type;
|
||||
String iconPath = IconsUtil.getIconFilePath(type.getTypeID());
|
||||
setIconBaseWithExtension(iconPath != null && iconPath.charAt(0) == '/' ? iconPath.substring(1) : iconPath);
|
||||
updateDisplayName();
|
||||
}
|
||||
|
||||
final void updateDisplayName() {
|
||||
if (skCase == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE: This completely destroys our lazy-loading ideal
|
||||
// a performance increase might be had by adding a
|
||||
// "getBlackboardArtifactCount()" method to skCase
|
||||
try {
|
||||
this.childCount = (filteringDSObjId > 0)
|
||||
? blackboard.getArtifactsCount(type.getTypeID(), filteringDSObjId)
|
||||
: skCase.getBlackboardArtifactsTypeCount(type.getTypeID());
|
||||
} catch (TskCoreException ex) {
|
||||
Logger.getLogger(TypeNode.class.getName())
|
||||
.log(Level.WARNING, "Error getting child count", ex); //NON-NLS
|
||||
}
|
||||
super.setDisplayName(type.getDisplayName() + " \u200E(\u200E" + childCount + ")\u200E");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Sheet createSheet() {
|
||||
Sheet sheet = super.createSheet();
|
||||
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
|
||||
if (sheetSet == null) {
|
||||
sheetSet = Sheet.createPropertiesSet();
|
||||
sheet.put(sheetSet);
|
||||
}
|
||||
|
||||
sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.artType.name"),
|
||||
NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.artType.displayName"),
|
||||
NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.artType.desc"),
|
||||
type.getDisplayName()));
|
||||
|
||||
sheetSet.put(new NodeProperty<>(NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.childCnt.name"),
|
||||
NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.childCnt.displayName"),
|
||||
NbBundle.getMessage(this.getClass(), "ArtifactTypeNode.createSheet.childCnt.desc"),
|
||||
childCount));
|
||||
|
||||
return sheet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLeafTypeNode() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getItemType() {
|
||||
return getClass().getName() + type.getDisplayName();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates children for a given artifact type
|
||||
*/
|
||||
private class ArtifactFactory extends BaseChildFactory<BlackboardArtifact> implements RefreshThrottler.Refresher {
|
||||
|
||||
private final BlackboardArtifact.Type type;
|
||||
|
||||
/**
|
||||
* RefreshThrottler is used to limit the number of refreshes performed
|
||||
* when CONTENT_CHANGED and DATA_ADDED ingest module events are
|
||||
* received.
|
||||
*/
|
||||
private final RefreshThrottler refreshThrottler = new RefreshThrottler(this);
|
||||
|
||||
ArtifactFactory(BlackboardArtifact.Type type) {
|
||||
super(type.getTypeName());
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> {
|
||||
String eventType = evt.getPropertyName();
|
||||
if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
|
||||
|| eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
|
||||
/**
|
||||
* Checking for a current case is a stop gap measure until a
|
||||
* different way of handling the closing of cases is worked out.
|
||||
* Currently, remote events may be received for a case that is
|
||||
* already closed.
|
||||
*/
|
||||
try {
|
||||
Case.getCurrentCaseThrows();
|
||||
refresh(false);
|
||||
} catch (NoCurrentCaseException notUsed) {
|
||||
/**
|
||||
* Case is closed, do nothing.
|
||||
*/
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
protected void onAdd() {
|
||||
refreshThrottler.registerForIngestModuleEvents();
|
||||
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRemove() {
|
||||
refreshThrottler.unregisterEventListener();
|
||||
IngestManager.getInstance().removeIngestJobEventListener(pcl);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node createNodeForKey(BlackboardArtifact key) {
|
||||
return new BlackboardArtifactNode(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<BlackboardArtifact> makeKeys() {
|
||||
if (skCase != null) {
|
||||
try {
|
||||
List<BlackboardArtifact> arts;
|
||||
if (filteringDSObjId > 0) {
|
||||
arts = blackboard.getArtifacts(type.getTypeID(), filteringDSObjId);
|
||||
} else {
|
||||
arts = skCase.getBlackboardArtifacts(type.getTypeID());
|
||||
}
|
||||
for (BlackboardArtifact art : arts) {
|
||||
//Cache attributes while we are off the EDT.
|
||||
//See JIRA-5969
|
||||
art.getAttributes();
|
||||
}
|
||||
return arts;
|
||||
} catch (TskCoreException ex) {
|
||||
Logger.getLogger(ArtifactFactory.class.getName()).log(Level.SEVERE, "Couldn't get blackboard artifacts from database", ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh() {
|
||||
refresh(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRefreshRequired(PropertyChangeEvent evt) {
|
||||
String eventType = evt.getPropertyName();
|
||||
if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
|
||||
|
||||
/**
|
||||
* Checking for a current case is a stop gap measure until a
|
||||
* different way of handling the closing of cases is worked out.
|
||||
* Currently, remote events may be received for a case that is
|
||||
* already closed.
|
||||
*/
|
||||
try {
|
||||
Case.getCurrentCaseThrows();
|
||||
/**
|
||||
* Even with the check above, it is still possible that the
|
||||
* case will be closed in a different thread before this
|
||||
* code executes. If that happens, it is possible for the
|
||||
* event to have a null oldValue.
|
||||
*/
|
||||
final ModuleDataEvent event = (ModuleDataEvent) evt.getOldValue();
|
||||
if (null != event && event.getBlackboardArtifactType().equals(type)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
} catch (NoCurrentCaseException notUsed) {
|
||||
/**
|
||||
* Case is closed, do nothing.
|
||||
*/
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -34,6 +34,8 @@ import org.openide.nodes.Sheet;
|
||||
import org.openide.util.Lookup;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.lookup.Lookups;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.datamodel.AnalysisResult;
|
||||
import org.sleuthkit.datamodel.AnalysisResultAdded;
|
||||
@ -76,16 +78,10 @@ public final class FileTypes implements AutopsyVisitableItem {
|
||||
*/
|
||||
private boolean showCounts = true;
|
||||
|
||||
private final SleuthkitCase skCase;
|
||||
|
||||
private final long datasourceObjId;
|
||||
|
||||
FileTypes(SleuthkitCase skCase) {
|
||||
this(skCase, 0);
|
||||
}
|
||||
|
||||
FileTypes(SleuthkitCase skCase, long dsObjId) {
|
||||
this.skCase = skCase;
|
||||
|
||||
FileTypes(long dsObjId) {
|
||||
this.datasourceObjId = dsObjId;
|
||||
updateShowCounts();
|
||||
}
|
||||
@ -95,10 +91,6 @@ public final class FileTypes implements AutopsyVisitableItem {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
SleuthkitCase getSleuthkitCase() {
|
||||
return skCase;
|
||||
}
|
||||
|
||||
long filteringDataSourceObjId() {
|
||||
return this.datasourceObjId;
|
||||
}
|
||||
@ -112,10 +104,10 @@ public final class FileTypes implements AutopsyVisitableItem {
|
||||
*/
|
||||
if (showCounts) {
|
||||
try {
|
||||
if (skCase.countFilesWhere("1=1") > NODE_COUNT_FILE_TABLE_THRESHOLD) { //NON-NLS
|
||||
if (Case.getCurrentCaseThrows().getSleuthkitCase().countFilesWhere("1=1") > NODE_COUNT_FILE_TABLE_THRESHOLD) { //NON-NLS
|
||||
showCounts = false;
|
||||
}
|
||||
} catch (TskCoreException tskCoreException) {
|
||||
} catch (NoCurrentCaseException | TskCoreException tskCoreException) {
|
||||
showCounts = false;
|
||||
logger.log(Level.SEVERE, "Error counting files.", tskCoreException); //NON-NLS
|
||||
}
|
||||
@ -397,8 +389,8 @@ public final class FileTypes implements AutopsyVisitableItem {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataArtifact newDataArtifact(BlackboardArtifact.Type artifactType, Collection<BlackboardAttribute> attributesList, OsAccount osAccount) throws TskCoreException {
|
||||
return content.newDataArtifact(artifactType, attributesList, osAccount);
|
||||
public DataArtifact newDataArtifact(BlackboardArtifact.Type artifactType, Collection<BlackboardAttribute> attributesList, Long osAccountId) throws TskCoreException {
|
||||
return content.newDataArtifact(artifactType, attributesList, osAccountId);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -34,6 +34,7 @@ import org.openide.nodes.ChildFactory;
|
||||
import org.openide.nodes.Children;
|
||||
import org.openide.nodes.Node;
|
||||
import org.openide.nodes.Sheet;
|
||||
import org.openide.util.Exceptions;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.openide.util.lookup.Lookups;
|
||||
@ -58,16 +59,18 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
|
||||
private final static Logger logger = Logger.getLogger(FileTypesByExtension.class.getName());
|
||||
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED);
|
||||
private static final Set<IngestManager.IngestModuleEvent> INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestModuleEvent.CONTENT_CHANGED);
|
||||
private final SleuthkitCase skCase;
|
||||
private final FileTypes typesRoot;
|
||||
|
||||
public FileTypesByExtension(FileTypes typesRoot) {
|
||||
this.skCase = typesRoot.getSleuthkitCase();
|
||||
this.typesRoot = typesRoot;
|
||||
}
|
||||
|
||||
public SleuthkitCase getSleuthkitCase() {
|
||||
return this.skCase;
|
||||
try {
|
||||
return Case.getCurrentCaseThrows().getSleuthkitCase();
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -404,7 +407,11 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
|
||||
|
||||
@Override
|
||||
long calculateChildCount() throws TskCoreException {
|
||||
return skCase.countFilesWhere(createQuery(filter));
|
||||
try {
|
||||
return Case.getCurrentCaseThrows().getSleuthkitCase().countFilesWhere(createQuery(filter));
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
throw new TskCoreException("No open case.", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -63,7 +63,7 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
|
||||
|
||||
private final static Logger logger = Logger.getLogger(FileTypesByMimeType.class.getName());
|
||||
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED);
|
||||
private final SleuthkitCase skCase;
|
||||
|
||||
/**
|
||||
* The nodes of this tree will be determined dynamically by the mimetypes
|
||||
* which exist in the database. This hashmap will store them with the media
|
||||
@ -130,11 +130,8 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
|
||||
+ " GROUP BY mime_type";
|
||||
synchronized (existingMimeTypeCounts) {
|
||||
existingMimeTypeCounts.clear();
|
||||
|
||||
if (skCase == null) {
|
||||
return;
|
||||
}
|
||||
try (SleuthkitCase.CaseDbQuery dbQuery = skCase.executeQuery(query)) {
|
||||
try
|
||||
(SleuthkitCase.CaseDbQuery dbQuery = Case.getCurrentCaseThrows().getSleuthkitCase().executeQuery(query)) {
|
||||
ResultSet resultSet = dbQuery.getResultSet();
|
||||
while (resultSet.next()) {
|
||||
final String mime_type = resultSet.getString("mime_type"); //NON-NLS
|
||||
@ -149,7 +146,7 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (TskCoreException | SQLException ex) {
|
||||
} catch (NoCurrentCaseException | TskCoreException | SQLException ex) {
|
||||
logger.log(Level.SEVERE, "Unable to populate File Types by MIME Type tree view from DB: ", ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
@ -159,7 +156,6 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
|
||||
}
|
||||
|
||||
FileTypesByMimeType(FileTypes typesRoot) {
|
||||
this.skCase = typesRoot.getSleuthkitCase();
|
||||
this.typesRoot = typesRoot;
|
||||
this.pcl = (PropertyChangeEvent evt) -> {
|
||||
String eventType = evt.getPropertyName();
|
||||
@ -497,9 +493,10 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
|
||||
@Override
|
||||
protected List<FileTypesKey> makeKeys() {
|
||||
try {
|
||||
return skCase.findAllFilesWhere(createBaseWhereExpr() + " AND mime_type = '" + mimeType + "'")
|
||||
return Case.getCurrentCaseThrows().getSleuthkitCase()
|
||||
.findAllFilesWhere(createBaseWhereExpr() + " AND mime_type = '" + mimeType + "'")
|
||||
.stream().map(f -> new FileTypesKey(f)).collect(Collectors.toList()); //NON-NLS
|
||||
} catch (TskCoreException ex) {
|
||||
} catch (NoCurrentCaseException | TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Couldn't get search results", ex); //NON-NLS
|
||||
}
|
||||
return Collections.emptyList();
|
||||
|
@ -46,19 +46,20 @@ import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
|
||||
import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_HASHSET_HIT;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.autopsy.datamodel.Artifacts.UpdatableCountTypeNode;
|
||||
|
||||
/**
|
||||
* Hash set hits node support. Inner classes have all of the nodes in the tree.
|
||||
*/
|
||||
public class HashsetHits implements AutopsyVisitableItem {
|
||||
|
||||
private static final String HASHSET_HITS = BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getLabel();
|
||||
private static final String DISPLAY_NAME = BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getDisplayName();
|
||||
private static final String HASHSET_HITS = BlackboardArtifact.Type.TSK_HASHSET_HIT.getTypeName();
|
||||
private static final String DISPLAY_NAME = BlackboardArtifact.Type.TSK_HASHSET_HIT.getDisplayName();
|
||||
private static final Logger logger = Logger.getLogger(HashsetHits.class.getName());
|
||||
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED);
|
||||
private static final Set<IngestManager.IngestModuleEvent> INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestModuleEvent.DATA_ADDED);
|
||||
@ -134,7 +135,7 @@ public class HashsetHits implements AutopsyVisitableItem {
|
||||
}
|
||||
|
||||
int setNameId = ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID();
|
||||
int artId = ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID();
|
||||
int artId = TSK_HASHSET_HIT.getTypeID();
|
||||
String query = "SELECT value_text,blackboard_attributes.artifact_id,attribute_type_id " //NON-NLS
|
||||
+ "FROM blackboard_attributes,blackboard_artifacts WHERE " //NON-NLS
|
||||
+ "attribute_type_id=" + setNameId //NON-NLS
|
||||
@ -168,10 +169,15 @@ public class HashsetHits implements AutopsyVisitableItem {
|
||||
/**
|
||||
* Top-level node for all hash sets
|
||||
*/
|
||||
public class RootNode extends DisplayableItemNode {
|
||||
public class RootNode extends UpdatableCountTypeNode {
|
||||
|
||||
public RootNode() {
|
||||
super(Children.create(new HashsetNameFactory(), true), Lookups.singleton(DISPLAY_NAME));
|
||||
super(Children.create(new HashsetNameFactory(), true),
|
||||
Lookups.singleton(DISPLAY_NAME),
|
||||
DISPLAY_NAME,
|
||||
filteringDSObjId,
|
||||
TSK_HASHSET_HIT);
|
||||
|
||||
super.setName(HASHSET_HITS);
|
||||
super.setDisplayName(DISPLAY_NAME);
|
||||
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/hashset_hits.png"); //NON-NLS
|
||||
@ -239,7 +245,7 @@ public class HashsetHits implements AutopsyVisitableItem {
|
||||
* oldValue if the event is a remote event.
|
||||
*/
|
||||
ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
|
||||
if (null != eventData && eventData.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID()) {
|
||||
if (null != eventData && eventData.getBlackboardArtifactType().getTypeID() == TSK_HASHSET_HIT.getTypeID()) {
|
||||
hashsetResults.update();
|
||||
}
|
||||
} catch (NoCurrentCaseException notUsed) {
|
||||
|
@ -51,7 +51,7 @@ public class HostDataSources implements AutopsyVisitableItem, Comparable<HostDat
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(this.host == null ? 0 : this.host.getId());
|
||||
return Objects.hashCode(this.host == null ? 0 : this.host.getHostId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -66,8 +66,8 @@ public class HostDataSources implements AutopsyVisitableItem, Comparable<HostDat
|
||||
return false;
|
||||
}
|
||||
final HostDataSources other = (HostDataSources) obj;
|
||||
long thisId = (this.getHost() == null) ? 0 : this.getHost().getId();
|
||||
long otherId = (other.getHost() == null) ? 0 : other.getHost().getId();
|
||||
long thisId = (this.getHost() == null) ? 0 : this.getHost().getHostId();
|
||||
long otherId = (other.getHost() == null) ? 0 : other.getHost().getHostId();
|
||||
return thisId == otherId;
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,7 @@ public class HostGrouping implements AutopsyVisitableItem, Comparable<HostGroupi
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(this.host == null ? 0 : this.host.getId());
|
||||
return Objects.hashCode(this.host == null ? 0 : this.host.getHostId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -66,8 +66,8 @@ public class HostGrouping implements AutopsyVisitableItem, Comparable<HostGroupi
|
||||
return false;
|
||||
}
|
||||
final HostGrouping other = (HostGrouping) obj;
|
||||
long thisId = (this.getHost() == null) ? 0 : this.getHost().getId();
|
||||
long otherId = (other.getHost() == null) ? 0 : other.getHost().getId();
|
||||
long thisId = (this.getHost() == null) ? 0 : this.getHost().getHostId();
|
||||
long otherId = (other.getHost() == null) ? 0 : other.getHost().getHostId();
|
||||
return thisId == otherId;
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.datamodel;
|
||||
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
@ -40,6 +41,7 @@ import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.casemodule.events.HostsChangedEvent;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datamodel.hosts.AssociatePersonsMenuAction;
|
||||
import org.sleuthkit.autopsy.datamodel.hosts.MergeHostMenuAction;
|
||||
import org.sleuthkit.autopsy.datamodel.hosts.RemoveParentPersonAction;
|
||||
import org.sleuthkit.datamodel.DataSource;
|
||||
import org.sleuthkit.datamodel.Host;
|
||||
@ -76,13 +78,16 @@ public class HostNode extends DisplayableItemNode {
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener for handling DATA_SOURCE_ADDED events.
|
||||
* Listener for handling DATA_SOURCE_ADDED / HOST_DELETED events.
|
||||
* A host may have been deleted as part of a merge, which means its data sources could
|
||||
* have moved to a different host requiring a refresh.
|
||||
*/
|
||||
private final PropertyChangeListener dataSourceAddedPcl = new PropertyChangeListener() {
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
String eventType = evt.getPropertyName();
|
||||
if (eventType.equals(Case.Events.DATA_SOURCE_ADDED.toString())) {
|
||||
if (eventType.equals(Case.Events.DATA_SOURCE_ADDED.toString())
|
||||
|| eventType.equals(Case.Events.HOSTS_DELETED.toString())) {
|
||||
refresh(true);
|
||||
}
|
||||
}
|
||||
@ -90,12 +95,12 @@ public class HostNode extends DisplayableItemNode {
|
||||
|
||||
@Override
|
||||
protected void addNotify() {
|
||||
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED), dataSourceAddedPcl);
|
||||
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED, Case.Events.HOSTS_DELETED), dataSourceAddedPcl);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void removeNotify() {
|
||||
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED), dataSourceAddedPcl);
|
||||
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED, Case.Events.HOSTS_DELETED), dataSourceAddedPcl);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -174,7 +179,7 @@ public class HostNode extends DisplayableItemNode {
|
||||
String eventType = evt.getPropertyName();
|
||||
if (hostId != null && eventType.equals(Case.Events.HOSTS_CHANGED.toString()) && evt instanceof HostsChangedEvent) {
|
||||
((HostsChangedEvent) evt).getNewValue().stream()
|
||||
.filter(h -> h != null && h.getId() == hostId)
|
||||
.filter(h -> h != null && h.getHostId() == hostId)
|
||||
.findFirst()
|
||||
.ifPresent((newHost) -> {
|
||||
setName(newHost.getName());
|
||||
@ -240,7 +245,7 @@ public class HostNode extends DisplayableItemNode {
|
||||
super(children,
|
||||
host == null ? Lookups.fixed(displayName) : Lookups.fixed(host, displayName));
|
||||
|
||||
hostId = host == null ? null : host.getId();
|
||||
hostId = host == null ? null : host.getHostId();
|
||||
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.HOSTS_CHANGED),
|
||||
WeakListeners.propertyChange(hostChangePcl, this));
|
||||
super.setName(displayName);
|
||||
@ -287,12 +292,15 @@ public class HostNode extends DisplayableItemNode {
|
||||
"HostNode_actions_removeFromPerson=Remove from person ({0})"})
|
||||
public Action[] getActions(boolean context) {
|
||||
|
||||
Optional<Person> parent = Optional.empty();
|
||||
|
||||
List<Action> actionsList = new ArrayList<>();
|
||||
|
||||
// if there is a host, then provide actions
|
||||
if (this.host != null) {
|
||||
|
||||
// Add the appropriate Person action
|
||||
Optional<Person> parent;
|
||||
try {
|
||||
parent = Case.getCurrentCaseThrows().getSleuthkitCase().getHostManager().getPerson(this.host);
|
||||
parent = Case.getCurrentCaseThrows().getSleuthkitCase().getPersonManager().getPerson(this.host);
|
||||
} catch (NoCurrentCaseException | TskCoreException ex) {
|
||||
logger.log(Level.WARNING, String.format("Error fetching parent person of host: %s", this.host.getName() == null ? "<null>" : this.host.getName()), ex);
|
||||
return new Action[0];
|
||||
@ -300,17 +308,14 @@ public class HostNode extends DisplayableItemNode {
|
||||
|
||||
// if there is a parent, only give option to remove parent person.
|
||||
if (parent.isPresent()) {
|
||||
return new Action[]{
|
||||
new RemoveParentPersonAction(this.host, parent.get()),
|
||||
null
|
||||
};
|
||||
actionsList.add(new RemoveParentPersonAction(this.host, parent.get()));
|
||||
} else {
|
||||
return new Action[]{
|
||||
new AssociatePersonsMenuAction(this.host),
|
||||
null
|
||||
};
|
||||
actionsList.add(new AssociatePersonsMenuAction(this.host));
|
||||
}
|
||||
|
||||
// Add option to merge hosts
|
||||
actionsList.add(new MergeHostMenuAction(this.host));
|
||||
}
|
||||
return new Action[0];
|
||||
return actionsList.toArray(new Action[actionsList.size()]);
|
||||
}
|
||||
}
|
||||
|
@ -50,6 +50,7 @@ import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.autopsy.datamodel.Artifacts.UpdatableCountTypeNode;
|
||||
|
||||
public class InterestingHits implements AutopsyVisitableItem {
|
||||
|
||||
@ -110,8 +111,8 @@ public class InterestingHits implements AutopsyVisitableItem {
|
||||
synchronized (interestingItemsMap) {
|
||||
interestingItemsMap.clear();
|
||||
}
|
||||
loadArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT);
|
||||
loadArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT);
|
||||
loadArtifacts(BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT);
|
||||
loadArtifacts(BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT);
|
||||
setChanged();
|
||||
notifyObservers();
|
||||
}
|
||||
@ -121,7 +122,7 @@ public class InterestingHits implements AutopsyVisitableItem {
|
||||
* the interestingItemsMap
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
private void loadArtifacts(BlackboardArtifact.ARTIFACT_TYPE artType) {
|
||||
private void loadArtifacts(BlackboardArtifact.Type artType) {
|
||||
if (skCase == null) {
|
||||
return;
|
||||
}
|
||||
@ -145,8 +146,8 @@ public class InterestingHits implements AutopsyVisitableItem {
|
||||
long artifactId = resultSet.getLong("artifact_id"); //NON-NLS
|
||||
if (!interestingItemsMap.containsKey(value)) {
|
||||
interestingItemsMap.put(value, new LinkedHashMap<>());
|
||||
interestingItemsMap.get(value).put(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getDisplayName(), new HashSet<>());
|
||||
interestingItemsMap.get(value).put(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getDisplayName(), new HashSet<>());
|
||||
interestingItemsMap.get(value).put(BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT.getDisplayName(), new HashSet<>());
|
||||
interestingItemsMap.get(value).put(BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT.getDisplayName(), new HashSet<>());
|
||||
}
|
||||
interestingItemsMap.get(value).get(artType.getDisplayName()).add(artifactId);
|
||||
}
|
||||
@ -165,12 +166,16 @@ public class InterestingHits implements AutopsyVisitableItem {
|
||||
/**
|
||||
* Node for the interesting items
|
||||
*/
|
||||
public class RootNode extends DisplayableItemNode {
|
||||
public class RootNode extends UpdatableCountTypeNode {
|
||||
|
||||
public RootNode() {
|
||||
super(Children.create(new SetNameFactory(), true), Lookups.singleton(DISPLAY_NAME));
|
||||
super(Children.create(new SetNameFactory(), true),
|
||||
Lookups.singleton(DISPLAY_NAME),
|
||||
DISPLAY_NAME,
|
||||
filteringDSObjId,
|
||||
BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT,
|
||||
BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT);
|
||||
super.setName(INTERESTING_ITEMS);
|
||||
super.setDisplayName(DISPLAY_NAME);
|
||||
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/interesting_item.png"); //NON-NLS
|
||||
}
|
||||
|
||||
@ -232,8 +237,8 @@ public class InterestingHits implements AutopsyVisitableItem {
|
||||
* event to have a null oldValue.
|
||||
*/
|
||||
ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
|
||||
if (null != eventData && (eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()
|
||||
|| eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID())) {
|
||||
if (null != eventData && (eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()
|
||||
|| eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT.getTypeID())) {
|
||||
interestingResults.update();
|
||||
}
|
||||
} catch (NoCurrentCaseException notUsed) {
|
||||
@ -314,8 +319,8 @@ public class InterestingHits implements AutopsyVisitableItem {
|
||||
}
|
||||
|
||||
private void updateDisplayName() {
|
||||
int sizeOfSet = interestingResults.getArtifactIds(setName, BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getDisplayName()).size()
|
||||
+ interestingResults.getArtifactIds(setName, BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getDisplayName()).size();
|
||||
int sizeOfSet = interestingResults.getArtifactIds(setName, BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT.getDisplayName()).size()
|
||||
+ interestingResults.getArtifactIds(setName, BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT.getDisplayName()).size();
|
||||
super.setDisplayName(setName + " (" + sizeOfSet + ")");
|
||||
}
|
||||
|
||||
@ -374,8 +379,8 @@ public class InterestingHits implements AutopsyVisitableItem {
|
||||
|
||||
@Override
|
||||
protected boolean createKeys(List<String> list) {
|
||||
list.add(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getDisplayName());
|
||||
list.add(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getDisplayName());
|
||||
list.add(BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT.getDisplayName());
|
||||
list.add(BlackboardArtifact.Type.TSK_INTERESTING_ARTIFACT_HIT.getDisplayName());
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -400,8 +405,8 @@ public class InterestingHits implements AutopsyVisitableItem {
|
||||
this.typeName = typeName;
|
||||
this.setName = setName;
|
||||
/**
|
||||
* We use the combination of setName and typeName as the name of
|
||||
* the node to ensure that nodes have a unique name. This comes into
|
||||
* We use the combination of setName and typeName as the name of the
|
||||
* node to ensure that nodes have a unique name. This comes into
|
||||
* play when associating paging state with the node.
|
||||
*/
|
||||
super.setName(setName + "_" + typeName);
|
||||
@ -462,9 +467,9 @@ public class InterestingHits implements AutopsyVisitableItem {
|
||||
|
||||
private HitFactory(String setName, String typeName) {
|
||||
/**
|
||||
* The node name passed to the parent constructor must be the
|
||||
* same as the name set in the InterestingItemTypeNode constructor,
|
||||
* i.e. setName underscore typeName
|
||||
* The node name passed to the parent constructor must be the same
|
||||
* as the name set in the InterestingItemTypeNode constructor, i.e.
|
||||
* setName underscore typeName
|
||||
*/
|
||||
super(setName + "_" + typeName);
|
||||
this.setName = setName;
|
||||
|
@ -55,6 +55,8 @@ import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_KEYWORD_HIT;
|
||||
import org.sleuthkit.autopsy.datamodel.Artifacts.UpdatableCountTypeNode;
|
||||
|
||||
/**
|
||||
* Keyword hits node support
|
||||
@ -71,7 +73,7 @@ public class KeywordHits implements AutopsyVisitableItem {
|
||||
@NbBundle.Messages("KeywordHits.singleRegexSearch.text=Single Regular Expression Search")
|
||||
private static final String SIMPLE_REGEX_SEARCH = KeywordHits_singleRegexSearch_text();
|
||||
|
||||
public static final String NAME = BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getLabel();
|
||||
public static final String NAME = BlackboardArtifact.Type.TSK_KEYWORD_HIT.getTypeName();
|
||||
|
||||
private SleuthkitCase skCase;
|
||||
private final KeywordResults keywordResults;
|
||||
@ -93,7 +95,7 @@ public class KeywordHits implements AutopsyVisitableItem {
|
||||
+ "blackboard_attributes.attribute_type_id "//NON-NLS
|
||||
+ "FROM blackboard_attributes, blackboard_artifacts "//NON-NLS
|
||||
+ "WHERE blackboard_attributes.artifact_id = blackboard_artifacts.artifact_id "//NON-NLS
|
||||
+ " AND blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID() //NON-NLS
|
||||
+ " AND blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.Type.TSK_KEYWORD_HIT.getTypeID() //NON-NLS
|
||||
+ " AND (attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID()//NON-NLS
|
||||
+ " OR attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD.getTypeID()//NON-NLS
|
||||
+ " OR attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE.getTypeID()//NON-NLS
|
||||
@ -375,12 +377,16 @@ public class KeywordHits implements AutopsyVisitableItem {
|
||||
}
|
||||
|
||||
// Created by CreateAutopsyNodeVisitor
|
||||
public class RootNode extends DisplayableItemNode {
|
||||
public class RootNode extends UpdatableCountTypeNode {
|
||||
|
||||
public RootNode() {
|
||||
super(Children.create(new ListFactory(), true), Lookups.singleton(KEYWORD_HITS));
|
||||
super(Children.create(new ListFactory(), true),
|
||||
Lookups.singleton(KEYWORD_HITS),
|
||||
KEYWORD_HITS,
|
||||
filteringDSObjId,
|
||||
TSK_KEYWORD_HIT);
|
||||
|
||||
super.setName(NAME);
|
||||
super.setDisplayName(KEYWORD_HITS);
|
||||
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/keyword_hits.png"); //NON-NLS
|
||||
}
|
||||
|
||||
@ -464,7 +470,7 @@ public class KeywordHits implements AutopsyVisitableItem {
|
||||
* for the event to have a null oldValue.
|
||||
*/
|
||||
ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
|
||||
if (null != eventData && eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()) {
|
||||
if (null != eventData && eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.Type.TSK_KEYWORD_HIT.getTypeID()) {
|
||||
keywordResults.update();
|
||||
}
|
||||
} catch (NoCurrentCaseException notUsed) {
|
||||
|
@ -20,25 +20,36 @@ package org.sleuthkit.autopsy.datamodel;
|
||||
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.Action;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.openide.nodes.ChildFactory;
|
||||
import org.openide.nodes.Children;
|
||||
import org.openide.nodes.Node;
|
||||
import org.openide.nodes.Sheet;
|
||||
import org.openide.util.Exceptions;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.openide.util.lookup.Lookups;
|
||||
import org.openide.util.WeakListeners;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.events.OsAccountChangedEvent;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
|
||||
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import static org.sleuthkit.autopsy.datamodel.AbstractContentNode.backgroundTasksPool;
|
||||
import org.sleuthkit.autopsy.events.AutopsyEvent;
|
||||
import org.sleuthkit.datamodel.Host;
|
||||
import org.sleuthkit.datamodel.OsAccount;
|
||||
import org.sleuthkit.datamodel.OsAccountRealm;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.Tag;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskDataException;
|
||||
|
||||
@ -50,8 +61,9 @@ public final class OsAccounts implements AutopsyVisitableItem {
|
||||
private static final Logger logger = Logger.getLogger(OsAccounts.class.getName());
|
||||
private static final String ICON_PATH = "org/sleuthkit/autopsy/images/os-account.png";
|
||||
private static final SimpleDateFormat DATE_FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
|
||||
private static final String REALM_DATA_AVAILABLE_EVENT = "REALM_DATA_AVAILABLE_EVENT";
|
||||
|
||||
private final SleuthkitCase skCase;
|
||||
private SleuthkitCase skCase;
|
||||
private final long filteringDSObjId;
|
||||
|
||||
public OsAccounts(SleuthkitCase skCase) {
|
||||
@ -111,34 +123,47 @@ public final class OsAccounts implements AutopsyVisitableItem {
|
||||
private final PropertyChangeListener listener = new PropertyChangeListener() {
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
refresh(true);
|
||||
String eventType = evt.getPropertyName();
|
||||
if (eventType.equals(Case.Events.OS_ACCOUNT_ADDED.toString())
|
||||
|| eventType.equals(Case.Events.OS_ACCOUNT_REMOVED.toString())) {
|
||||
refresh(true);
|
||||
} else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
|
||||
// case was closed. Remove listeners so that we don't get called with a stale case handle
|
||||
if (evt.getNewValue() == null) {
|
||||
removeNotify();
|
||||
skCase = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@Override
|
||||
protected void addNotify() {
|
||||
Case.addEventTypeSubscriber(Collections.singleton(Case.Events.OS_ACCOUNT_ADDED), listener);
|
||||
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.OS_ACCOUNT_ADDED, Case.Events.OS_ACCOUNT_REMOVED), listener);
|
||||
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), listener);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void removeNotify() {
|
||||
Case.removeEventTypeSubscriber(Collections.singleton(Case.Events.OS_ACCOUNT_ADDED), listener);
|
||||
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), listener);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected boolean createKeys(List<OsAccount> list) {
|
||||
try {
|
||||
if (filteringDSObjId == 0) {
|
||||
list.addAll(skCase.getOsAccountManager().getAccounts());
|
||||
} else {
|
||||
Host host = skCase.getHostManager().getHost(skCase.getDataSource(filteringDSObjId));
|
||||
list.addAll(skCase.getOsAccountManager().getAccounts(host));
|
||||
if (skCase != null) {
|
||||
try {
|
||||
if (filteringDSObjId == 0) {
|
||||
list.addAll(skCase.getOsAccountManager().getOsAccounts());
|
||||
} else {
|
||||
Host host = skCase.getHostManager().getHostByDataSource(skCase.getDataSource(filteringDSObjId));
|
||||
list.addAll(skCase.getOsAccountManager().getOsAccounts(host));
|
||||
}
|
||||
} catch (TskCoreException | TskDataException ex) {
|
||||
logger.log(Level.SEVERE, "Unable to retrieve list of OsAccounts for case", ex);
|
||||
return false;
|
||||
}
|
||||
} catch (TskCoreException | TskDataException ex) {
|
||||
logger.log(Level.SEVERE, "Unable to retrieve list of OsAccounts for case", ex);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -151,35 +176,52 @@ public final class OsAccounts implements AutopsyVisitableItem {
|
||||
/**
|
||||
* An OsAccount leaf Node.
|
||||
*/
|
||||
public static final class OsAccountNode extends DisplayableItemNode {
|
||||
public static final class OsAccountNode extends AbstractContentNode<OsAccount> {
|
||||
|
||||
private OsAccount account;
|
||||
|
||||
|
||||
private final PropertyChangeListener listener = new PropertyChangeListener() {
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
if(((OsAccountChangedEvent)evt).getOsAccount().getId() == account.getId()) {
|
||||
// Update the account node to the new one
|
||||
account = ((OsAccountChangedEvent)evt).getOsAccount();
|
||||
updateSheet();
|
||||
if (evt.getPropertyName().equals(Case.Events.OS_ACCOUNT_CHANGED.name())) {
|
||||
if (((OsAccountChangedEvent) evt).getOsAccount().getId() == account.getId()) {
|
||||
// Update the account node to the new one
|
||||
account = ((OsAccountChangedEvent) evt).getOsAccount();
|
||||
updateSheet();
|
||||
}
|
||||
} else if (evt.getPropertyName().equals(REALM_DATA_AVAILABLE_EVENT)) {
|
||||
OsAccountRealm realm = (OsAccountRealm) evt.getNewValue();
|
||||
|
||||
// Currently only 0 or 1 names are supported, this will need
|
||||
// to be modified if that changes.
|
||||
List<String> realmNames = realm.getRealmNames();
|
||||
if (!realmNames.isEmpty()) {
|
||||
updateSheet(new NodeProperty<>(
|
||||
Bundle.OsAccounts_accountRealmNameProperty_name(),
|
||||
Bundle.OsAccounts_accountRealmNameProperty_displayName(),
|
||||
Bundle.OsAccounts_accountRealmNameProperty_desc(),
|
||||
""));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final PropertyChangeListener weakListener = WeakListeners.propertyChange(listener, null);
|
||||
|
||||
/**
|
||||
* Constructs a new OsAccountNode.
|
||||
*
|
||||
* @param account Node object.
|
||||
*/
|
||||
OsAccountNode(OsAccount account) {
|
||||
super(Children.LEAF, Lookups.fixed(account));
|
||||
super(account);
|
||||
this.account = account;
|
||||
|
||||
setName(account.getName());
|
||||
setDisplayName(account.getName());
|
||||
setIconBaseWithExtension(ICON_PATH);
|
||||
|
||||
Case.addEventTypeSubscriber(Collections.singleton(Case.Events.OS_ACCOUNT_CHANGED), listener);
|
||||
|
||||
Case.addEventTypeSubscriber(Collections.singleton(Case.Events.OS_ACCOUNT_CHANGED), weakListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -196,7 +238,16 @@ public final class OsAccounts implements AutopsyVisitableItem {
|
||||
public String getItemType() {
|
||||
return getClass().getName();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the OsAccount associated with this node.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
OsAccount getOsAccount() {
|
||||
return account;
|
||||
}
|
||||
|
||||
@Messages({
|
||||
"OsAccounts_accountNameProperty_name=Name",
|
||||
"OsAccounts_accountNameProperty_displayName=Name",
|
||||
@ -211,13 +262,13 @@ public final class OsAccounts implements AutopsyVisitableItem {
|
||||
"OsAccounts_loginNameProperty_displayName=Login Name",
|
||||
"OsAccounts_loginNameProperty_desc=Os Account login name"
|
||||
})
|
||||
|
||||
|
||||
/**
|
||||
* Refreshes this node's property sheet.
|
||||
*/
|
||||
void updateSheet() {
|
||||
this.setSheet(createSheet());
|
||||
}
|
||||
* Refreshes this node's property sheet.
|
||||
*/
|
||||
void updateSheet() {
|
||||
this.setSheet(createSheet());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Sheet createSheet() {
|
||||
@ -240,13 +291,13 @@ public final class OsAccounts implements AutopsyVisitableItem {
|
||||
Bundle.OsAccounts_loginNameProperty_displayName(),
|
||||
Bundle.OsAccounts_loginNameProperty_desc(),
|
||||
optional.isPresent() ? optional.get() : ""));
|
||||
|
||||
optional = account.getRealm().getRealmName();
|
||||
// Fill with empty string, fetch on background task.
|
||||
String realmName = "";
|
||||
propertiesSet.put(new NodeProperty<>(
|
||||
Bundle.OsAccounts_accountRealmNameProperty_name(),
|
||||
Bundle.OsAccounts_accountRealmNameProperty_displayName(),
|
||||
Bundle.OsAccounts_accountRealmNameProperty_desc(),
|
||||
optional.isPresent() ? optional.get() : ""));
|
||||
realmName));
|
||||
|
||||
Optional<Long> creationTimeValue = account.getCreationTime();
|
||||
String timeDisplayStr
|
||||
@ -258,7 +309,91 @@ public final class OsAccounts implements AutopsyVisitableItem {
|
||||
Bundle.OsAccounts_createdTimeProperty_desc(),
|
||||
timeDisplayStr));
|
||||
|
||||
backgroundTasksPool.submit(new GetOsAccountRealmTask(new WeakReference<>(this), weakListener));
|
||||
|
||||
return sheet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Action[] getActions(boolean popup) {
|
||||
List<Action> actionsList = new ArrayList<>();
|
||||
actionsList.addAll(Arrays.asList(super.getActions(popup)));
|
||||
actionsList.addAll(DataModelActionsFactory.getActions(account));
|
||||
|
||||
return actionsList.toArray(new Action[actionsList.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Tag> getAllTagsFromDatabase() {
|
||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CorrelationAttributeInstance getCorrelationAttributeInstance() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Pair<DataResultViewerTable.Score, String> getScorePropertyAndDescription(List<Tag> tags) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DataResultViewerTable.HasCommentStatus getCommentProperty(List<Tag> tags, CorrelationAttributeInstance attribute) {
|
||||
return DataResultViewerTable.HasCommentStatus.NO_COMMENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Pair<Long, String> getCountPropertyAndDescription(CorrelationAttributeInstance.Type attributeType, String attributeValue, String defaultDescription) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T accept(ContentNodeVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Task for grabbing the osAccount realm.
|
||||
*/
|
||||
static class GetOsAccountRealmTask implements Runnable {
|
||||
|
||||
private final WeakReference<OsAccountNode> weakNodeRef;
|
||||
private final PropertyChangeListener listener;
|
||||
|
||||
/**
|
||||
* Construct a new task.
|
||||
*
|
||||
* @param weakContentRef
|
||||
* @param listener
|
||||
*/
|
||||
GetOsAccountRealmTask(WeakReference<OsAccountNode> weakContentRef, PropertyChangeListener listener) {
|
||||
this.weakNodeRef = weakContentRef;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
OsAccountNode node = weakNodeRef.get();
|
||||
if (node == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
long realmId = node.getOsAccount().getRealmId();
|
||||
OsAccountRealm realm = Case.getCurrentCase().getSleuthkitCase().getOsAccountRealmManager().getRealmByRealmId(realmId);
|
||||
|
||||
if (listener != null && realm != null) {
|
||||
listener.propertyChange(new PropertyChangeEvent(
|
||||
AutopsyEvent.SourceType.LOCAL.toString(),
|
||||
REALM_DATA_AVAILABLE_EVENT,
|
||||
null, realm));
|
||||
}
|
||||
|
||||
} catch (TskCoreException ex) {
|
||||
Exceptions.printStackTrace(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ public class PersonGrouping implements AutopsyVisitableItem, Comparable<PersonGr
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(this.person == null ? 0 : this.person.getId());
|
||||
return Objects.hashCode(this.person == null ? 0 : this.person.getPersonId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -67,8 +67,8 @@ public class PersonGrouping implements AutopsyVisitableItem, Comparable<PersonGr
|
||||
return false;
|
||||
}
|
||||
final PersonGrouping other = (PersonGrouping) obj;
|
||||
long thisId = (this.getPerson() == null) ? 0 : this.getPerson().getId();
|
||||
long otherId = (other.getPerson() == null) ? 0 : other.getPerson().getId();
|
||||
long thisId = (this.getPerson() == null) ? 0 : this.getPerson().getPersonId();
|
||||
long otherId = (other.getPerson() == null) ? 0 : other.getPerson().getPersonId();
|
||||
return thisId == otherId;
|
||||
}
|
||||
|
||||
|
@ -147,7 +147,7 @@ public class PersonGroupingNode extends DisplayableItemNode {
|
||||
String eventType = evt.getPropertyName();
|
||||
if (personId != null && eventType.equals(Case.Events.PERSONS_CHANGED.toString()) && evt instanceof PersonsChangedEvent) {
|
||||
((PersonsChangedEvent) evt).getNewValue().stream()
|
||||
.filter(p -> p != null && p.getId() == personId)
|
||||
.filter(p -> p != null && p.getPersonId() == personId)
|
||||
.findFirst()
|
||||
.ifPresent((newPerson) -> {
|
||||
setName(newPerson.getName());
|
||||
@ -191,7 +191,7 @@ public class PersonGroupingNode extends DisplayableItemNode {
|
||||
super.setDisplayName(displayName);
|
||||
this.setIconBaseWithExtension(ICON_PATH);
|
||||
this.person = person;
|
||||
this.personId = person == null ? null : person.getId();
|
||||
this.personId = person == null ? null : person.getPersonId();
|
||||
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.PERSONS_CHANGED),
|
||||
WeakListeners.propertyChange(personChangePcl, this));
|
||||
}
|
||||
|
@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.datamodel;
|
||||
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
|
||||
/**
|
||||
* Results node support
|
||||
*/
|
||||
public class Results implements AutopsyVisitableItem {
|
||||
|
||||
private SleuthkitCase skCase;
|
||||
private final long datasourceObjId;
|
||||
|
||||
public Results(SleuthkitCase skCase) {
|
||||
this(skCase, 0);
|
||||
}
|
||||
|
||||
public Results(SleuthkitCase skCase, long dsObjId) {
|
||||
this.skCase = skCase;
|
||||
this.datasourceObjId = dsObjId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T accept(AutopsyItemVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
public SleuthkitCase getSleuthkitCase() {
|
||||
return skCase;
|
||||
}
|
||||
|
||||
long filteringDataSourceObjId() {
|
||||
return datasourceObjId;
|
||||
}
|
||||
}
|
@ -1,97 +0,0 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-2018 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.datamodel;
|
||||
|
||||
import org.sleuthkit.autopsy.datamodel.accounts.Accounts;
|
||||
import java.util.Arrays;
|
||||
import org.openide.nodes.Sheet;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.lookup.Lookups;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
|
||||
/**
|
||||
* Node for the results section of the tree.
|
||||
*/
|
||||
public class ResultsNode extends DisplayableItemNode {
|
||||
|
||||
@NbBundle.Messages("ResultsNode.name.text=Results")
|
||||
private static final String NAME = Bundle.ResultsNode_name_text();
|
||||
|
||||
public static String getNameIdentifier() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
public ResultsNode(SleuthkitCase sleuthkitCase) {
|
||||
this(sleuthkitCase, 0);
|
||||
}
|
||||
|
||||
public ResultsNode(SleuthkitCase sleuthkitCase, long dsObjId) {
|
||||
super(
|
||||
|
||||
new RootContentChildren(Arrays.asList(
|
||||
new ExtractedContent(sleuthkitCase, dsObjId ),
|
||||
new KeywordHits(sleuthkitCase, dsObjId),
|
||||
new HashsetHits(sleuthkitCase, dsObjId),
|
||||
new EmailExtracted(sleuthkitCase, dsObjId),
|
||||
new InterestingHits(sleuthkitCase, dsObjId ),
|
||||
new Accounts(sleuthkitCase, dsObjId),
|
||||
new OsAccounts(sleuthkitCase, dsObjId))
|
||||
),
|
||||
Lookups.singleton(NAME));
|
||||
setName(NAME);
|
||||
setDisplayName(NAME);
|
||||
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/results.png"); //NON-NLS
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLeafTypeNode() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NbBundle.Messages({
|
||||
"ResultsNode.createSheet.name.name=Name",
|
||||
"ResultsNode.createSheet.name.displayName=Name",
|
||||
"ResultsNode.createSheet.name.desc=no description"})
|
||||
protected Sheet createSheet() {
|
||||
Sheet sheet = super.createSheet();
|
||||
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
|
||||
if (sheetSet == null) {
|
||||
sheetSet = Sheet.createPropertiesSet();
|
||||
sheet.put(sheetSet);
|
||||
}
|
||||
|
||||
sheetSet.put(new NodeProperty<>(Bundle.ResultsNode_createSheet_name_name(),
|
||||
Bundle.ResultsNode_createSheet_name_displayName(),
|
||||
Bundle.ResultsNode_createSheet_name_desc(),
|
||||
NAME
|
||||
));
|
||||
return sheet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getItemType() {
|
||||
return getClass().getName();
|
||||
}
|
||||
}
|
@ -83,11 +83,6 @@ public class RootContentChildren extends Children.Keys<Object> {
|
||||
*/
|
||||
static class CreateAutopsyNodeVisitor extends AutopsyItemVisitor.Default<AbstractNode> {
|
||||
|
||||
@Override
|
||||
public ExtractedContent.RootNode visit(ExtractedContent ec) {
|
||||
return ec.new RootNode(ec.getSleuthkitCase());
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractNode visit(FileTypesByExtension sf) {
|
||||
return sf.new FileTypesByExtNode(sf.getSleuthkitCase(), null);
|
||||
@ -148,11 +143,6 @@ public class RootContentChildren extends Children.Keys<Object> {
|
||||
return new ViewsNode(v.getSleuthkitCase(), v.filteringDataSourceObjId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractNode visit(Results results) {
|
||||
return new ResultsNode(results.getSleuthkitCase(), results.filteringDataSourceObjId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractNode visit(FileTypes ft) {
|
||||
return ft.new FileTypesNode();
|
||||
@ -167,7 +157,7 @@ public class RootContentChildren extends Children.Keys<Object> {
|
||||
public AbstractNode visit(Accounts accountsItem) {
|
||||
return accountsItem.new AccountsRootNode();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public AbstractNode visit(OsAccounts osAccountsItem) {
|
||||
return osAccountsItem.new OsAccountListNode();
|
||||
@ -204,5 +194,17 @@ public class RootContentChildren extends Children.Keys<Object> {
|
||||
public AbstractNode visit(DataSourcesByType dataSourceHosts) {
|
||||
return new DataSourcesByTypeNode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractNode visit(AnalysisResults analysisResults) {
|
||||
return new AnalysisResults.RootNode(
|
||||
analysisResults.getFilteringDataSourceObjId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractNode visit(DataArtifacts dataArtifacts) {
|
||||
return new DataArtifacts.RootNode(
|
||||
dataArtifacts.getFilteringDataSourceObjId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,186 @@
|
||||
/*
|
||||
* 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.datamodel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.swing.Action;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.openide.nodes.Sheet;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
|
||||
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
|
||||
import static org.sleuthkit.autopsy.datamodel.AbstractContentNode.NO_DESCR;
|
||||
import org.sleuthkit.datamodel.UnsupportedContent;
|
||||
import org.sleuthkit.datamodel.Tag;
|
||||
|
||||
/**
|
||||
* This class is used to represent the "Node" for an unsupported content object.
|
||||
*/
|
||||
public class UnsupportedContentNode extends AbstractContentNode<UnsupportedContent> {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param unsupportedContent underlying Content instance
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"UnsupportedContentNode.displayName=Unsupported Content",
|
||||
})
|
||||
public UnsupportedContentNode(UnsupportedContent unsupportedContent) {
|
||||
super(unsupportedContent);
|
||||
|
||||
// set name, display name, and icon
|
||||
this.setDisplayName(Bundle.UnsupportedContentNode_displayName());
|
||||
|
||||
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/file-icon.png"); //NON-NLS
|
||||
}
|
||||
|
||||
/**
|
||||
* Right click action for UnsupportedContentNode node
|
||||
*
|
||||
* @param popup
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public Action[] getActions(boolean popup) {
|
||||
List<Action> actionsList = new ArrayList<>();
|
||||
|
||||
for (Action a : super.getActions(true)) {
|
||||
actionsList.add(a);
|
||||
}
|
||||
|
||||
return actionsList.toArray(new Action[actionsList.size()]);
|
||||
|
||||
}
|
||||
|
||||
@NbBundle.Messages({
|
||||
"UnsupportedContentNode.createSheet.name.name=Name",
|
||||
"UnsupportedContentNode.createSheet.name.displayName=Name",
|
||||
"UnsupportedContentNode.createSheet.name.desc=no description",
|
||||
})
|
||||
@Override
|
||||
protected Sheet createSheet() {
|
||||
Sheet sheet = super.createSheet();
|
||||
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
|
||||
if (sheetSet == null) {
|
||||
sheetSet = Sheet.createPropertiesSet();
|
||||
sheet.put(sheetSet);
|
||||
}
|
||||
|
||||
sheetSet.put(new NodeProperty<>(Bundle.UnsupportedContentNode_createSheet_name_name(),
|
||||
Bundle.UnsupportedContentNode_createSheet_name_displayName(),
|
||||
Bundle.UnsupportedContentNode_createSheet_name_desc(),
|
||||
this.getDisplayName()));
|
||||
|
||||
return sheet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T accept(ContentNodeVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLeafTypeNode() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getItemType() {
|
||||
return getClass().getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads and returns a list of all tags associated with this content node.
|
||||
*
|
||||
* Null implementation of an abstract method.
|
||||
*
|
||||
* @return list of tags associated with the node.
|
||||
*/
|
||||
@Override
|
||||
protected List<Tag> getAllTagsFromDatabase() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns correlation attribute instance for the underlying content of the
|
||||
* node.
|
||||
*
|
||||
* Null implementation of an abstract method.
|
||||
*
|
||||
* @return correlation attribute instance for the underlying content of the
|
||||
* node.
|
||||
*/
|
||||
@Override
|
||||
protected CorrelationAttributeInstance getCorrelationAttributeInstance() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Score property for the node.
|
||||
*
|
||||
* Null implementation of an abstract method.
|
||||
*
|
||||
* @param tags list of tags.
|
||||
*
|
||||
* @return Score property for the underlying content of the node.
|
||||
*/
|
||||
@Override
|
||||
protected Pair<DataResultViewerTable.Score, String> getScorePropertyAndDescription(List<Tag> tags) {
|
||||
return Pair.of(DataResultViewerTable.Score.NO_SCORE, NO_DESCR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns comment property for the node.
|
||||
*
|
||||
* Null implementation of an abstract method.
|
||||
*
|
||||
* @param tags list of tags
|
||||
* @param attribute correlation attribute instance
|
||||
*
|
||||
* @return Comment property for the underlying content of the node.
|
||||
*/
|
||||
@Override
|
||||
protected DataResultViewerTable.HasCommentStatus getCommentProperty(List<Tag> tags, CorrelationAttributeInstance attribute) {
|
||||
return DataResultViewerTable.HasCommentStatus.NO_COMMENT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns occurrences/count property for the node.
|
||||
*
|
||||
* Null implementation of an abstract method.
|
||||
*
|
||||
* @param attributeType the type of the attribute to count
|
||||
* @param attributeValue the value of the attribute to coun
|
||||
* @param defaultDescription a description to use when none is determined by
|
||||
* the getCountPropertyAndDescription method
|
||||
*
|
||||
* @return count property for the underlying content of the node.
|
||||
*/
|
||||
@Override
|
||||
protected Pair<Long, String> getCountPropertyAndDescription(CorrelationAttributeInstance.Type attributeType, String attributeValue, String defaultDescription) {
|
||||
return Pair.of(-1L, NO_DESCR);
|
||||
}
|
||||
}
|
@ -41,7 +41,7 @@ public class ViewsNode extends DisplayableItemNode {
|
||||
|
||||
super(
|
||||
new RootContentChildren(Arrays.asList(
|
||||
new FileTypes(sleuthkitCase, dsObjId),
|
||||
new FileTypes(dsObjId),
|
||||
// June '15: Recent Files was removed because it was not useful w/out filtering
|
||||
// add it back in if we can filter the results to a more managable size.
|
||||
// new RecentFiles(sleuthkitCase),
|
||||
|
@ -69,6 +69,7 @@ import org.sleuthkit.autopsy.datamodel.CreditCards;
|
||||
import org.sleuthkit.autopsy.datamodel.DataModelActionsFactory;
|
||||
import org.sleuthkit.autopsy.datamodel.DisplayableItemNode;
|
||||
import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor;
|
||||
import org.sleuthkit.autopsy.datamodel.Artifacts.UpdatableCountTypeNode;
|
||||
import org.sleuthkit.autopsy.datamodel.NodeProperty;
|
||||
import org.sleuthkit.autopsy.directorytree.DirectoryTreeTopComponent;
|
||||
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||
@ -76,7 +77,8 @@ import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.Account;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact.Type;
|
||||
import static org.sleuthkit.datamodel.BlackboardArtifact.Type.TSK_ACCOUNT;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
@ -93,7 +95,8 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
private static final String ICON_BASE_PATH = "/org/sleuthkit/autopsy/images/"; //NON-NLS
|
||||
private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.COMPLETED, IngestManager.IngestJobEvent.CANCELLED);
|
||||
private static final Set<IngestManager.IngestModuleEvent> INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestModuleEvent.DATA_ADDED);
|
||||
|
||||
private static final String DISPLAY_NAME = Bundle.Accounts_RootNode_displayName();
|
||||
|
||||
@NbBundle.Messages("AccountsRootNode.name=Accounts")
|
||||
final public static String NAME = Bundle.AccountsRootNode_name();
|
||||
|
||||
@ -109,7 +112,7 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
|
||||
private final RejectAccounts rejectActionInstance;
|
||||
private final ApproveAccounts approveActionInstance;
|
||||
|
||||
|
||||
// tracks the number of each account type found
|
||||
private final AccountTypeResults accountTypeResults;
|
||||
|
||||
@ -131,7 +134,7 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
public Accounts(SleuthkitCase skCase, long objId) {
|
||||
this.skCase = skCase;
|
||||
this.filteringDSObjId = objId;
|
||||
|
||||
|
||||
this.rejectActionInstance = new RejectAccounts();
|
||||
this.approveActionInstance = new ApproveAccounts();
|
||||
this.accountTypeResults = new AccountTypeResults();
|
||||
@ -231,12 +234,16 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
* Top-level node for the accounts tree
|
||||
*/
|
||||
@NbBundle.Messages({"Accounts.RootNode.displayName=Accounts"})
|
||||
final public class AccountsRootNode extends DisplayableItemNode {
|
||||
final public class AccountsRootNode extends UpdatableCountTypeNode {
|
||||
|
||||
public AccountsRootNode() {
|
||||
super(Children.create(new AccountTypeFactory(), true), Lookups.singleton(Accounts.this));
|
||||
super(Children.create(new AccountTypeFactory(), true),
|
||||
Lookups.singleton(Accounts.this),
|
||||
DISPLAY_NAME,
|
||||
filteringDSObjId,
|
||||
TSK_ACCOUNT);
|
||||
|
||||
setName(Accounts.NAME);
|
||||
setDisplayName(Bundle.Accounts_RootNode_displayName());
|
||||
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/accounts.png"); //NON-NLS
|
||||
}
|
||||
|
||||
@ -254,37 +261,71 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
public String getItemType() {
|
||||
return getClass().getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long fetchChildCount(SleuthkitCase skCase) throws TskCoreException {
|
||||
long count = 0;
|
||||
String dataSourceFilterClause = (filteringDSObjId > 0)
|
||||
? " AND " + filteringDSObjId + " IN (SELECT art.data_source_obj_id FROM blackboard_artifacts art WHERE art.artifact_id = attr.artifact_id)"
|
||||
: "";
|
||||
|
||||
String accountTypesInUseQuery
|
||||
= "SELECT COUNT(attr.value_text) AS count"
|
||||
+ " FROM blackboard_attributes attr"
|
||||
+ " WHERE attr.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID()
|
||||
+ " AND attr.artifact_type_id = " + BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID()
|
||||
+ dataSourceFilterClause
|
||||
+ " GROUP BY attr.value_text";
|
||||
|
||||
try (SleuthkitCase.CaseDbQuery executeQuery = skCase.executeQuery(accountTypesInUseQuery);
|
||||
ResultSet resultSet = executeQuery.getResultSet()) {
|
||||
|
||||
if (resultSet.next()) {
|
||||
count = resultSet.getLong("count");
|
||||
}
|
||||
|
||||
} catch (TskCoreException | SQLException ex) {
|
||||
LOGGER.log(Level.SEVERE, "Error querying for count of all account types", ex);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tracks the account types and the number of account types found.
|
||||
*/
|
||||
private class AccountTypeResults {
|
||||
|
||||
private final Map<String, Long> counts = new HashMap<>();
|
||||
|
||||
|
||||
AccountTypeResults() {
|
||||
update();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Given the type name of the Account.Type, provides the count of those type.
|
||||
* @param accountType The type name of the Account.Type.
|
||||
* @return The number of results found for the given account type.
|
||||
* Given the type name of the Account.Type, provides the count of those
|
||||
* type.
|
||||
*
|
||||
* @param accountType The type name of the Account.Type.
|
||||
*
|
||||
* @return The number of results found for the given account type.
|
||||
*/
|
||||
Long getCount(String accountType) {
|
||||
return counts.get(accountType);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves an alphabetically organized list of all the account types.
|
||||
* @return An alphabetically organized list of all the account types.
|
||||
*
|
||||
* @return An alphabetically organized list of all the account types.
|
||||
*/
|
||||
List<String> getTypes() {
|
||||
List<String> types = new ArrayList<>(counts.keySet());
|
||||
Collections.sort(types);
|
||||
return types;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Queries the database and updates the counts for each account type.
|
||||
*/
|
||||
@ -293,14 +334,14 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
= "SELECT blackboard_attributes.value_text as account_type, COUNT(*) as count "
|
||||
+ " FROM blackboard_artifacts " //NON-NLS
|
||||
+ " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
|
||||
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
|
||||
+ " WHERE blackboard_artifacts.artifact_type_id = " + TSK_ACCOUNT.getTypeID() //NON-NLS
|
||||
+ " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS
|
||||
+ getFilterByDataSourceClause()
|
||||
+ " GROUP BY blackboard_attributes.value_text ";
|
||||
|
||||
try (SleuthkitCase.CaseDbQuery executeQuery = skCase.executeQuery(accountTypesInUseQuery);
|
||||
ResultSet resultSet = executeQuery.getResultSet()) {
|
||||
|
||||
|
||||
counts.clear();
|
||||
while (resultSet.next()) {
|
||||
String accountType = resultSet.getString("account_type");
|
||||
@ -317,7 +358,7 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
* Creates child nodes for each account type in the db.
|
||||
*/
|
||||
private class AccountTypeFactory extends ObservingChildren<String> {
|
||||
|
||||
|
||||
/*
|
||||
* The pcl is in this class because it has the easiest mechanisms to add
|
||||
* and remove itself during its life cycles.
|
||||
@ -343,7 +384,7 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
*/
|
||||
ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
|
||||
if (null != eventData
|
||||
&& eventData.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) {
|
||||
&& eventData.getBlackboardArtifactType().getTypeID() == Type.TSK_ACCOUNT.getTypeID()) {
|
||||
accountTypeResults.update();
|
||||
reviewStatusBus.post(eventData);
|
||||
}
|
||||
@ -391,12 +432,14 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
list.addAll(accountTypeResults.getTypes());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Registers the given node with the reviewStatusBus and returns
|
||||
* the node wrapped in an array.
|
||||
* @param node The node to be wrapped.
|
||||
* @return The array containing this node.
|
||||
* Registers the given node with the reviewStatusBus and returns the
|
||||
* node wrapped in an array.
|
||||
*
|
||||
* @param node The node to be wrapped.
|
||||
*
|
||||
* @return The array containing this node.
|
||||
*/
|
||||
private Node[] getNodeArr(Node node) {
|
||||
reviewStatusBus.register(node);
|
||||
@ -476,7 +519,7 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
*/
|
||||
ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
|
||||
if (null != eventData
|
||||
&& eventData.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) {
|
||||
&& eventData.getBlackboardArtifactType().getTypeID() == Type.TSK_ACCOUNT.getTypeID()) {
|
||||
reviewStatusBus.post(eventData);
|
||||
}
|
||||
} catch (NoCurrentCaseException notUsed) {
|
||||
@ -529,7 +572,7 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
= "SELECT blackboard_artifacts.artifact_id " //NON-NLS
|
||||
+ " FROM blackboard_artifacts " //NON-NLS
|
||||
+ " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
|
||||
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
|
||||
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID() //NON-NLS
|
||||
+ " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS
|
||||
+ " AND blackboard_attributes.value_text = '" + accountType.getTypeName() + "'" //NON-NLS
|
||||
+ getFilterByDataSourceClause()
|
||||
@ -576,13 +619,15 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
* no special behavior.
|
||||
*/
|
||||
final public class DefaultAccountTypeNode extends DisplayableItemNode {
|
||||
|
||||
private final Account.Type accountType;
|
||||
|
||||
|
||||
private DefaultAccountTypeNode(Account.Type accountType) {
|
||||
super(Children.create(new DefaultAccountFactory(accountType), true), Lookups.singleton(accountType));
|
||||
this.accountType = accountType;
|
||||
String iconPath = getIconFilePath(accountType);
|
||||
this.setIconBaseWithExtension(iconPath != null && iconPath.charAt(0) == '/' ? iconPath.substring(1) : iconPath); //NON-NLS
|
||||
setName(accountType.getTypeName());
|
||||
updateName();
|
||||
}
|
||||
|
||||
@ -600,8 +645,7 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
public String getItemType() {
|
||||
return getClass().getName();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Subscribe
|
||||
void handleReviewStatusChange(ReviewStatusChangeEvent event) {
|
||||
updateName();
|
||||
@ -611,12 +655,13 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
void handleDataAdded(ModuleDataEvent event) {
|
||||
updateName();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the latest counts for the account type and then updates the name.
|
||||
* Gets the latest counts for the account type and then updates the
|
||||
* name.
|
||||
*/
|
||||
public void updateName() {
|
||||
setName(String.format("%s (%d)", accountType.getDisplayName(), accountTypeResults.getCount(accountType.getTypeName())));
|
||||
setDisplayName(String.format("%s (%d)", accountType.getDisplayName(), accountTypeResults.getCount(accountType.getTypeName())));
|
||||
}
|
||||
}
|
||||
|
||||
@ -651,7 +696,7 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
*/
|
||||
ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
|
||||
if (null != eventData
|
||||
&& eventData.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) {
|
||||
&& eventData.getBlackboardArtifactType().getTypeID() == Type.TSK_ACCOUNT.getTypeID()) {
|
||||
reviewStatusBus.post(eventData);
|
||||
}
|
||||
} catch (NoCurrentCaseException notUsed) {
|
||||
@ -747,14 +792,15 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
setName(Account.Type.CREDIT_CARD.getDisplayName());
|
||||
this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/credit-cards.png"); //NON-NLS
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the latest counts for the account type and then updates the name.
|
||||
* Gets the latest counts for the account type and then updates the
|
||||
* name.
|
||||
*/
|
||||
public void updateName() {
|
||||
setName(String.format("%s (%d)", Account.Type.CREDIT_CARD.getDisplayName(), accountTypeResults.getCount(Account.Type.CREDIT_CARD.getTypeName())));
|
||||
}
|
||||
|
||||
|
||||
@Subscribe
|
||||
void handleReviewStatusChange(ReviewStatusChangeEvent event) {
|
||||
updateName();
|
||||
@ -804,7 +850,7 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
*/
|
||||
ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
|
||||
if (null != eventData
|
||||
&& eventData.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) {
|
||||
&& eventData.getBlackboardArtifactType().getTypeID() == Type.TSK_ACCOUNT.getTypeID()) {
|
||||
reviewStatusBus.post(eventData);
|
||||
}
|
||||
} catch (NoCurrentCaseException notUsed) {
|
||||
@ -882,7 +928,7 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
+ " LEFT JOIN blackboard_attributes as account_type ON blackboard_artifacts.artifact_id = account_type.artifact_id " //NON-NLS
|
||||
+ " AND account_type.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS
|
||||
+ " AND account_type.value_text = '" + Account.Type.CREDIT_CARD.getTypeName() + "'" //NON-NLS
|
||||
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
|
||||
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID() //NON-NLS
|
||||
+ getFilterByDataSourceClause()
|
||||
+ getRejectedArtifactFilterClause()
|
||||
+ " GROUP BY blackboard_artifacts.obj_id, solr_document_id " //NON-NLS
|
||||
@ -951,7 +997,7 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
+ " LEFT JOIN blackboard_attributes as account_type ON blackboard_artifacts.artifact_id = account_type.artifact_id " //NON-NLS
|
||||
+ " AND account_type.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS
|
||||
+ " AND account_type.value_text = '" + Account.Type.CREDIT_CARD.getTypeName() + "'" //NON-NLS
|
||||
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
|
||||
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID() //NON-NLS
|
||||
+ getFilterByDataSourceClause()
|
||||
+ getRejectedArtifactFilterClause()
|
||||
+ " GROUP BY blackboard_artifacts.obj_id, solr_attribute.value_text ) AS foo";
|
||||
@ -1019,7 +1065,7 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
*/
|
||||
ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
|
||||
if (null != eventData
|
||||
&& eventData.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) {
|
||||
&& eventData.getBlackboardArtifactType().getTypeID() == Type.TSK_ACCOUNT.getTypeID()) {
|
||||
reviewStatusBus.post(eventData);
|
||||
}
|
||||
} catch (NoCurrentCaseException notUsed) { //NOPMD empy catch clause
|
||||
@ -1087,7 +1133,7 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
+ " COUNT(blackboard_artifacts.artifact_id) AS count " //NON-NLS
|
||||
+ " FROM blackboard_artifacts " //NON-NLS
|
||||
+ " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id" //NON-NLS
|
||||
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
|
||||
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID() //NON-NLS
|
||||
+ " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS
|
||||
+ getFilterByDataSourceClause()
|
||||
+ getRejectedArtifactFilterClause()
|
||||
@ -1154,7 +1200,7 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
= "SELECT count(distinct SUBSTR(blackboard_attributes.value_text,1,8)) AS BINs " //NON-NLS
|
||||
+ " FROM blackboard_artifacts " //NON-NLS
|
||||
+ " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id" //NON-NLS
|
||||
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
|
||||
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID() //NON-NLS
|
||||
+ " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS
|
||||
+ getFilterByDataSourceClause()
|
||||
+ getRejectedArtifactFilterClause(); //NON-NLS
|
||||
@ -1449,7 +1495,7 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
= "SELECT blackboard_artifacts.artifact_id " //NON-NLS
|
||||
+ " FROM blackboard_artifacts " //NON-NLS
|
||||
+ " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
|
||||
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
|
||||
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID() //NON-NLS
|
||||
+ " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS
|
||||
+ " AND blackboard_attributes.value_text >= '" + bin.getBINStart() + "' AND blackboard_attributes.value_text < '" + (bin.getBINEnd() + 1) + "'" //NON-NLS
|
||||
+ getFilterByDataSourceClause()
|
||||
@ -1523,7 +1569,7 @@ final public class Accounts implements AutopsyVisitableItem {
|
||||
= "SELECT count(blackboard_artifacts.artifact_id ) AS count" //NON-NLS
|
||||
+ " FROM blackboard_artifacts " //NON-NLS
|
||||
+ " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS
|
||||
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS
|
||||
+ " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.Type.TSK_ACCOUNT.getTypeID() //NON-NLS
|
||||
+ " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CARD_NUMBER.getTypeID() //NON-NLS
|
||||
+ " AND blackboard_attributes.value_text >= '" + bin.getBINStart() + "' AND blackboard_attributes.value_text < '" + (bin.getBINEnd() + 1) + "'" //NON-NLS
|
||||
+ getFilterByDataSourceClause()
|
||||
|
@ -65,8 +65,8 @@ public class AssociateNewPersonAction extends AbstractAction {
|
||||
try {
|
||||
newPersonName = getAddDialogName();
|
||||
if (StringUtils.isNotBlank(newPersonName)) {
|
||||
Person person = Case.getCurrentCaseThrows().getSleuthkitCase().getPersonManager().createPerson(newPersonName);
|
||||
Case.getCurrentCaseThrows().getSleuthkitCase().getHostManager().setPerson(host, person);
|
||||
Person person = Case.getCurrentCaseThrows().getSleuthkitCase().getPersonManager().newPerson(newPersonName);
|
||||
Case.getCurrentCaseThrows().getSleuthkitCase().getPersonManager().setPerson(host, person);
|
||||
}
|
||||
} catch (NoCurrentCaseException | TskCoreException ex) {
|
||||
String hostName = this.host == null || this.host.getName() == null ? "" : this.host.getName();
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user